Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • kj2-drupisz/desd
1 result
Select Git revision
Show changes
Commits on Source (36)
Showing
with 1685 additions and 181 deletions
...@@ -7,4 +7,7 @@ myproject/myproject/__pycache__/__init__.cpython-312.pyc ...@@ -7,4 +7,7 @@ myproject/myproject/__pycache__/__init__.cpython-312.pyc
myproject/myproject/__pycache__/settings.cpython-312.pyc myproject/myproject/__pycache__/settings.cpython-312.pyc
myproject/myproject/__pycache__/urls.cpython-312.pyc myproject/myproject/__pycache__/urls.cpython-312.pyc
.venv/ .venv/
myproject/debug.log myproject/debug.log
\ No newline at end of file ./myproject/debug.log
debug.log
desd.code-workspace
...@@ -27,6 +27,9 @@ ENV PYTHONUNBUFFERED 1 ...@@ -27,6 +27,9 @@ ENV PYTHONUNBUFFERED 1
# Set work directory # Set work directory
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY ./wait-for-it.sh /wait-for-it.sh
RUN chmod +x /wait-for-it.sh
# Install Python dependencies # Install Python dependencies
COPY ./requirements.txt . COPY ./requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt RUN pip install --upgrade pip && pip install -r requirements.txt
...@@ -47,3 +50,4 @@ RUN python manage.py collectstatic --noinput ...@@ -47,3 +50,4 @@ RUN python manage.py collectstatic --noinput
COPY entrypoint.sh /entrypoint.sh COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh RUN chmod +x /entrypoint.sh
This diff is collapsed.
...@@ -14,7 +14,8 @@ services: ...@@ -14,7 +14,8 @@ services:
- "5432:5432" - "5432:5432"
web: web:
build: . build: .
command: /bin/sh -c "/entrypoint.sh" #command: /bin/sh -c "/entrypoint.sh"
command: ["/wait-for-it.sh", "db:5432", "--", "/bin/sh", "/entrypoint.sh"]
volumes: volumes:
- .:/usr/src/app - .:/usr/src/app
- static_volume:/usr/src/app/static - static_volume:/usr/src/app/static
...@@ -40,10 +41,9 @@ services: ...@@ -40,10 +41,9 @@ services:
tensorflow_serving: tensorflow_serving:
image: tensorflow/serving image: tensorflow/serving
ports:
- "8501:8501"
volumes: volumes:
- ./models:/models - ./models:/models
- ./monitoring_config.txt:/etc/tensorflow_serving/monitoring_config.txt
environment: environment:
- MODEL_NAME=instrument_model - MODEL_NAME=instrument_model
- MODEL_BASE_PATH=/models/instrument_model - MODEL_BASE_PATH=/models/instrument_model
...@@ -51,6 +51,12 @@ services: ...@@ -51,6 +51,12 @@ services:
- --model_base_path=/models/instrument_model - --model_base_path=/models/instrument_model
- --rest_api_port=8501 - --rest_api_port=8501
- --model_name=instrument_model - --model_name=instrument_model
- --monitoring_config_file=/etc/tensorflow_serving/monitoring_config.txt
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
volumes: volumes:
static_volume: static_volume:
......
...@@ -6,5 +6,6 @@ python manage.py makemigrations ...@@ -6,5 +6,6 @@ python manage.py makemigrations
echo "Applying migrations" echo "Applying migrations"
python manage.py migrate python manage.py migrate
python manage.py make_users
python manage.py runserver 0.0.0.0:8000 python manage.py runserver 0.0.0.0:8000
\ No newline at end of file
prometheus_config {
enable: true,
path: "/monitoring/prometheus/metrics"
}
\ No newline at end of file
No preview for this file type
No preview for this file type
# __init__.py
\ No newline at end of file
# app/management/commands/create_users.py
from django.core.management.base import BaseCommand
from django.contrib.auth.hashers import make_password
from django.contrib.auth import get_user_model
class Command(BaseCommand):
def handle(self, *args, **options):
Profile = get_user_model()
if not Profile.objects.filter(username="superuser").exists():
Profile.objects.create_superuser("superuser", password="placeholder")
if not Profile.objects.filter(username="admin").exists():
Profile.objects.create_user("admin", password="placeholder")
if not Profile.objects.filter(username="base_user").exists():
Profile.objects.create_user("base_user", password="placeholder")
if not Profile.objects.filter(username="ml_engineer").exists():
Profile.objects.create_user("ml_engineer", password="placeholder")
if not Profile.objects.filter(username="accountant").exists():
Profile.objects.create_user("accountant", password="placeholder")
\ No newline at end of file
# Generated by Django 5.0.1 on 2024-03-04 13:30 # Generated by Django 5.0.1 on 2024-04-29 02:34
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
...@@ -8,6 +10,7 @@ class Migration(migrations.Migration): ...@@ -8,6 +10,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
...@@ -18,4 +21,30 @@ class Migration(migrations.Migration): ...@@ -18,4 +21,30 @@ class Migration(migrations.Migration):
('file', models.FileField(upload_to='audio', verbose_name='audio')), ('file', models.FileField(upload_to='audio', verbose_name='audio')),
], ],
), ),
migrations.CreateModel(
name='Log',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateTimeField(auto_now_add=True)),
('log', models.JSONField()),
('feedback', models.BooleanField(null=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Profile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user_type', models.IntegerField(choices=[(0, 'Basic User'), (1, 'Admin'), (2, 'ML Engineer'), (3, 'Accountant')], default=0)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='UserTokenCount',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('token_count', models.IntegerField(default=0)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
] ]
# Generated by Django 5.0.1 on 2024-03-11 06:11
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Log',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateTimeField(auto_now_add=True)),
('log', models.JSONField()),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
]
# Generated by Django 5.0.1 on 2024-03-25 00:43
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('myapp', '0002_log'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Profile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user_type', models.IntegerField(choices=[(0, 'Basic User'), (1, 'Admin'), (2, 'ML Engineer'), (3, 'Accountant')], default=0)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
File deleted
File deleted
# models.py
from django.db import models from django.db import models
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import User, Group, Permission from django.contrib.auth.models import User, Group, Permission
...@@ -6,34 +7,6 @@ from enum import Enum ...@@ -6,34 +7,6 @@ from enum import Enum
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.signals import post_save from django.db.models.signals import post_save
# class UserTypes(User):
# USER_TYPE_CHOICES = (
# 0, 'Basic User',
# 1, 'Admin',
# 2, 'ML Engineer',
# 3, 'Accountant'
# )
# usertype = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES) # should we declare default=0 here?
# group_names = ['Basic User', 'Admin', 'ML Engineer', 'Accountant']
# for group_name in group_names:
# Group.objects.get_or_create(name=group_name)
# # assign group permissions
# content_type = ContentType.objects.get_for_model(UserTypes)
# permission = Permission.objects.create(codename='can_view_user',
# name='Can View User',
# content_type=content_type)
# group = Group.objects.get(name='Admin')
# group.permissions.add(permission)
# User = get_user_model()
# user = User.objects.create_user('username', 'email', 'password')
# # names are not necessary - reduces gdpr concerns aswell
class Profile(models.Model): class Profile(models.Model):
USER_TYPES = ( USER_TYPES = (
(0, 'Basic User'), (0, 'Basic User'),
...@@ -58,61 +31,22 @@ class UserTokenCount(models.Model): ...@@ -58,61 +31,22 @@ class UserTokenCount(models.Model):
def __str__(self): def __str__(self):
return f'{self.user.username}\'s token count: {self.token_count}' return f'{self.user.username}\'s token count: {self.token_count}'
# Automatically create a UserTokenCount entry for each user on user creation
@receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
UserTokenCount.objects.get_or_create(user=instance)
Profile.objects.get_or_create(user=instance)
# instance.profile.save()
class Action(Enum):
UPLOAD_FILE = "The user has successfully uploaded a file."
LOGIN = "The user has logged in to their account."
REGISTER = "The user has registered for a new account."
PAYMENT_SUCCESSFUL = "The user has successfully made a payment."
GENERATE_FINANCIAL_STATEMENT = "The user has generated a financial statement."
CHANGE_MLA = "The user has changed their maximum loss amount (MLA)."
RUN_ALGORITHM = "The user has run an algorithm."
INVALID_FILE = "The uploaded file is invalid and cannot be processed."
INVALID_PASSWORD = "The user has entered an invalid password."
USER_DOES_NOT_EXIST = "The user does not exist in the system."
DOWNLOAD_BREAKDOWN = "The user has downloaded a breakdown of their data."
UNKNOWN = "An unknown error has occurred."
# class Logs(models.Model):
# """
# * Logs model
# """
# user_id = models.ForeignKey("User", on_delete=models.CASCADE)
# error_id = models.IntegerField()
# date = models.DateTimeField()
# class Feedback(models.Model):
# """
# * Feedback Model
# """
# user_id = models.ForeignKey("User", on_delete=models.CASCADE)
# content = models.CharField(max_length=2000)
# date = models.DateTimeField()
# class Bills(models.Model): class Action(Enum):
# """ UPLOAD_FILE = "{username} has successfully uploaded a file."
# * Bill/receipts Model LOGIN = "{username} has logged in to their account."
# """ REGISTER = "{username} has registered for a new account."
# user_id = models.ForeignKey("User", on_delete=models.CASCADE) PAYMENT_SUCCESSFUL = "{username} has successfully made a payment."
# date = models.DateTimeField() GENERATE_FINANCIAL_STATEMENT = "{username} has generated a financial statement."
# paid = models.BooleanField(default=False) CHANGE_MLA = "{username} has changed their maximum loss amount (MLA)."
RUN_ALGORITHM = "{username} has run an algorithm."
FEEDBACK_SUBMITTED = "{username} has submitted feedback."
# class Files(models.Model): INVALID_FILE = "{username} uploaded an invalid file that cannot be processed."
# """ INVALID_PASSWORD = "{username} has entered an invalid password."
# * Uploaded files USER_DOES_NOT_EXIST = "The user {username} does not exist in the system."
# """ DOWNLOAD_BREAKDOWN = "{username} has downloaded a breakdown of their data."
# date = models.DateTimeField() UNKNOWN = "An unknown error has occurred for user {username}."
# data = models.CharField(max_length=2000)
# uploader = models.ForeignKey("User", on_delete=models.CASCADE)
class Audio(models.Model): class Audio(models.Model):
file = models.FileField('audio', upload_to='audio') file = models.FileField('audio', upload_to='audio')
...@@ -121,16 +55,5 @@ class Log(models.Model): ...@@ -121,16 +55,5 @@ class Log(models.Model):
date = models.DateTimeField(auto_now_add=True) date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
log = models.JSONField() log = models.JSONField()
feedback = models.BooleanField(null=True)
# # LOGIN
# log_data = get_log_data(Action.LOGIN, 'success', user=request.user.username)
# create_log(log_data)
# # REGISTER
# log_data = get_log_data(Action.REGISTER, 'success', user=request.user.username)
# create_log(log_data)
# # INVALID_PASSWORD
# log_data = get_log_data(Action.INVALID_PASSWORD, 'error', user=request.user.username)
# create_log(log_data)
# # GENERATE_FINANCIAL_STATEMENT
# log_data = get_log_data(Action.GENERATE_FINANCIAL_STATEMENT, 'success', user=request.user.username)
# create_log(log_data)
...@@ -5,8 +5,8 @@ from django.shortcuts import redirect, render ...@@ -5,8 +5,8 @@ from django.shortcuts import redirect, render
from django.urls import reverse from django.urls import reverse
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.urls import reverse from django.urls import reverse
from .models import UserTokenCount from .models import Action, UserTokenCount
from .views import get_log_data, create_log
# Create a payment that can be made via the PayPal API # Create a payment that can be made via the PayPal API
# Create a payment that can be made via the PayPal API # Create a payment that can be made via the PayPal API
def create_payment(request): def create_payment(request):
...@@ -14,7 +14,7 @@ def create_payment(request): ...@@ -14,7 +14,7 @@ def create_payment(request):
paypalrestsdk.configure({ paypalrestsdk.configure({
"mode": settings.PAYPAL_MODE, "mode": settings.PAYPAL_MODE,
"client_id": settings.PAYPAL_CLIENT_ID, "client_id": settings.PAYPAL_CLIENT_ID,
"client_secret": settings.PAYPAL_CLIENT_SECRET "client_secret": settings.PAYPAL_CLIENT_SECRET
}) })
# Create payment object # Create payment object
...@@ -45,7 +45,7 @@ def create_payment(request): ...@@ -45,7 +45,7 @@ def create_payment(request):
}] }]
}) })
# Successfully communicated with API # Successfully communicated with API
if payment.create(): if payment.create():
print("Payment created successfully!") print("Payment created successfully!")
# get url for payment approval # get url for payment approval
...@@ -70,7 +70,7 @@ def execute_payment(request): ...@@ -70,7 +70,7 @@ def execute_payment(request):
print("no payment") print("no payment")
#TODO: Change this to a more appropriate path, maybe a generic error page that takes a string:Error to display in a template #TODO: Change this to a more appropriate path, maybe a generic error page that takes a string:Error to display in a template
return redirect('handler404') return redirect('handler404')
# configure API # configure API
paypalrestsdk.configure({ paypalrestsdk.configure({
"mode": settings.PAYPAL_MODE, "mode": settings.PAYPAL_MODE,
...@@ -88,15 +88,17 @@ def execute_payment(request): ...@@ -88,15 +88,17 @@ def execute_payment(request):
# Allocate some tokens # Allocate some tokens
user = request.user user = request.user
tokens_purchased = 1 tokens_purchased = 1
# increment user_tokens # increment user_tokens
# commit changes # commit changes
# TODO: Change something here such that the token amount added depends on a detail of the transaction, # TODO: Change something here such that the token amount added depends on a detail of the transaction,
# i.e. £9.99 payment for one token or £24.99 for # i.e. £9.99 payment for one token or £24.99 for
# #
add_tokens(user, tokens_purchased) if request.user.is_authenticated:
add_tokens(user, tokens_purchased)
return redirect('success') return redirect('success')
else:
return redirect('handler404')
else: else:
#TODO: Change this to a more appropriate error message #TODO: Change this to a more appropriate error message
print("exiting at the end of execute_payment()") print("exiting at the end of execute_payment()")
...@@ -112,4 +114,6 @@ def payment_cancelled(request): ...@@ -112,4 +114,6 @@ def payment_cancelled(request):
return render(request, 'payment_cancelled.html') return render(request, 'payment_cancelled.html')
def payment_success(request): def payment_success(request):
log_data = get_log_data(request.user, Action.PAYMENT_SUCCESSFUL, 'success')
create_log(request.user, log_data)
return render(request,'payment_success.html') return render(request,'payment_success.html')
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.dispatch import receiver from django.dispatch import receiver
from .models import Profile from .models import Profile, UserTokenCount
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs): def create_or_update_user_profile(sender, instance, created, **kwargs):
if created: if created:
Profile.objects.create(user=instance) UserTokenCount.objects.get_or_create(user=instance)
instance.profile.save() Profile.objects.get_or_create(user=instance)
instance.profile.save()
\ No newline at end of file
...@@ -9,6 +9,13 @@ ...@@ -9,6 +9,13 @@
{% compress css %} {% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}" /> <link rel="stylesheet" href="{% static 'src/output.css' %}" />
{% endcompress %} {% endcompress %}
<style>
/* Ensure the modal body takes up all available space minus a margin, allowing for scrolling */
.modal-body {
max-height: calc(100vh - 4rem); /* Adjust the 4rem to increase/decrease the top and bottom margin */
overflow-y: auto; /* Enable vertical scrolling */
}
</style>
</head> </head>
<body> <body>
...@@ -86,7 +93,15 @@ ...@@ -86,7 +93,15 @@
</nav> </nav>
{% endblock navbar %} {% endblock navbar %}
<div class="container mx-auto mt-6"> <div class="container mx-auto mt-6">
{% if messages %}
<div class="space-y-4">
{% for message in messages %}
<div class="{% if message.tags %}alert-{{ message.tags }}{% endif %}">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
{% block content %} {% endblock content %} {% block content %} {% endblock content %}
{% block scripts %} {% endblock scripts %}
</div> </div>
<!-- <footer class="bg-white dark:bg-gray-900 fixed bottom-0 w-full"> --> <!-- <footer class="bg-white dark:bg-gray-900 fixed bottom-0 w-full"> -->
......
...@@ -51,30 +51,71 @@ ...@@ -51,30 +51,71 @@
{% if predictions %} {% if predictions %}
<div id="predictions" class="py-8 px-4 mx-auto max-w-screen-xl"> <div id="predictions" class="py-8 px-4 mx-auto max-w-screen-xl">
<h3 class="text-2xl font-bold mb-4">{{ file_name }} Predictions:</h3> <h3 class="text-2xl font-bold mb-4">{{ file_name }} Predictions:</h3>
<ul id="predictionList" class="space-y-2"> <form method="post" action="{% url 'submit_feedback' %}">
{% for prediction in predictions %} {% csrf_token %}
<li class="bg-gray-100 dark:bg-gray-800 px-4 py-2 rounded-md" style="white-space: pre-line;">{{ prediction|safe }}</li> <input type="hidden" name="file_name" value="{{ file_name }}">
{% endfor %} <ul id="predictionList" class="space-y-2">
</ul> {% for prediction in predictions %}
</div> <li class="bg-gray-100 dark:bg-gray-800 px-4 py-2 rounded-md" style="white-space: pre-line;">
{% endif %} {{ prediction|safe }}
<input type="hidden" name="prediction" value="{{ prediction }}">
<div class="mt-2">
<label>
<input type="radio" name="feedback" value="true" required>
Like
</label>
<label class="ml-4">
<input type="radio" name="feedback" value="false">
Dislike
</label>
</div>
</li>
{% endfor %}
</ul>
<button type="submit" class="mt-4 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Submit Feedback
</button>
</form>
</div>
{% endif %}
</section> </section>
{% endblock content %}
{% block scripts %}
<script> <script>
function submitFeedback(prediction, liked) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/submit_feedback', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('prediction=' + encodeURIComponent(prediction) + '&liked=' + liked);
}
function loadAudioFile(event) { function loadAudioFile(event) {
var file = event.target.files[0]; var file = event.target.files[0];
if (file) { if (file) {
wavesurfer.loadBlob(file); wavesurfer.loadBlob(file);
document.getElementById('player').classList.remove('hidden'); document.getElementById('player').classList.remove('hidden');
wavesurfer.on('ready', function () { wavesurfer.on('ready', function () {
document.getElementById('playButton').disabled = false; document.getElementById('playButton').disabled = false;
});
} // After player is loaded, make an AJAX request to create a log
var xhr = new XMLHttpRequest();
xhr.open('POST', '/log_fileupload', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
'action': 'UPLOAD',
'status': 'success',
'file': file.name,
'description': 'File uploaded and player loaded'
}));
});
} }
}
function submitForm() { function submitForm() {
var form = document.getElementById('uploadForm'); var form = document.getElementById('uploadForm');
...@@ -166,10 +207,25 @@ function loadAudioFile(event) { ...@@ -166,10 +207,25 @@ function loadAudioFile(event) {
document.getElementById('uploadForm').addEventListener('reset', function() { document.getElementById('uploadForm').addEventListener('reset', function() {
clearFormAndPredictions(); clearFormAndPredictions();
}); });
}
var likeBtns = document.querySelectorAll('.like-btn');
var dislikeBtns = document.querySelectorAll('.dislike-btn');
</script> likeBtns.forEach(function(btn) {
btn.addEventListener('click', function() {
var prediction = this.dataset.prediction;
submitFeedback(prediction, true);
});
});
dislikeBtns.forEach(function(btn) {
btn.addEventListener('click', function() {
var prediction = this.dataset.prediction;
submitFeedback(prediction, false);
});
});
};
{% endblock content %} </script>
{% endblock scripts %}