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

Target

Select target project
  • kj2-drupisz/desd
1 result
Show changes
Commits on Source (36)
Showing
with 1685 additions and 181 deletions
......@@ -7,4 +7,7 @@ myproject/myproject/__pycache__/__init__.cpython-312.pyc
myproject/myproject/__pycache__/settings.cpython-312.pyc
myproject/myproject/__pycache__/urls.cpython-312.pyc
.venv/
myproject/debug.log
\ No newline at end of file
myproject/debug.log
./myproject/debug.log
debug.log
desd.code-workspace
......@@ -27,6 +27,9 @@ ENV PYTHONUNBUFFERED 1
# Set work directory
WORKDIR /usr/src/app
COPY ./wait-for-it.sh /wait-for-it.sh
RUN chmod +x /wait-for-it.sh
# Install Python dependencies
COPY ./requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt
......@@ -47,3 +50,4 @@ RUN python manage.py collectstatic --noinput
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
This diff is collapsed.
......@@ -14,7 +14,8 @@ services:
- "5432:5432"
web:
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:
- .:/usr/src/app
- static_volume:/usr/src/app/static
......@@ -40,10 +41,9 @@ services:
tensorflow_serving:
image: tensorflow/serving
ports:
- "8501:8501"
volumes:
- ./models:/models
- ./monitoring_config.txt:/etc/tensorflow_serving/monitoring_config.txt
environment:
- MODEL_NAME=instrument_model
- MODEL_BASE_PATH=/models/instrument_model
......@@ -51,6 +51,12 @@ services:
- --model_base_path=/models/instrument_model
- --rest_api_port=8501
- --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:
static_volume:
......
......@@ -6,5 +6,6 @@ python manage.py makemigrations
echo "Applying migrations"
python manage.py migrate
python manage.py make_users
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
......@@ -8,6 +10,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
......@@ -18,4 +21,30 @@ class Migration(migrations.Migration):
('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.contrib.auth import get_user_model
from django.contrib.auth.models import User, Group, Permission
......@@ -6,34 +7,6 @@ from enum import Enum
from django.dispatch import receiver
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):
USER_TYPES = (
(0, 'Basic User'),
......@@ -58,61 +31,22 @@ class UserTokenCount(models.Model):
def __str__(self):
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):
# """
# * Bill/receipts Model
# """
# user_id = models.ForeignKey("User", on_delete=models.CASCADE)
# date = models.DateTimeField()
# paid = models.BooleanField(default=False)
# class Files(models.Model):
# """
# * Uploaded files
# """
# date = models.DateTimeField()
# data = models.CharField(max_length=2000)
# uploader = models.ForeignKey("User", on_delete=models.CASCADE)
class Action(Enum):
UPLOAD_FILE = "{username} has successfully uploaded a file."
LOGIN = "{username} has logged in to their account."
REGISTER = "{username} has registered for a new account."
PAYMENT_SUCCESSFUL = "{username} has successfully made a payment."
GENERATE_FINANCIAL_STATEMENT = "{username} has generated a financial statement."
CHANGE_MLA = "{username} has changed their maximum loss amount (MLA)."
RUN_ALGORITHM = "{username} has run an algorithm."
FEEDBACK_SUBMITTED = "{username} has submitted feedback."
INVALID_FILE = "{username} uploaded an invalid file that cannot be processed."
INVALID_PASSWORD = "{username} has entered an invalid password."
USER_DOES_NOT_EXIST = "The user {username} does not exist in the system."
DOWNLOAD_BREAKDOWN = "{username} has downloaded a breakdown of their data."
UNKNOWN = "An unknown error has occurred for user {username}."
class Audio(models.Model):
file = models.FileField('audio', upload_to='audio')
......@@ -121,16 +55,5 @@ class Log(models.Model):
date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
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
from django.urls import reverse
from django.shortcuts import redirect, render
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
def create_payment(request):
......@@ -14,7 +14,7 @@ def create_payment(request):
paypalrestsdk.configure({
"mode": settings.PAYPAL_MODE,
"client_id": settings.PAYPAL_CLIENT_ID,
"client_secret": settings.PAYPAL_CLIENT_SECRET
"client_secret": settings.PAYPAL_CLIENT_SECRET
})
# Create payment object
......@@ -45,7 +45,7 @@ def create_payment(request):
}]
})
# Successfully communicated with API
# Successfully communicated with API
if payment.create():
print("Payment created successfully!")
# get url for payment approval
......@@ -70,7 +70,7 @@ def execute_payment(request):
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
return redirect('handler404')
# configure API
paypalrestsdk.configure({
"mode": settings.PAYPAL_MODE,
......@@ -88,15 +88,17 @@ def execute_payment(request):
# Allocate some tokens
user = request.user
tokens_purchased = 1
# increment user_tokens
# commit changes
# 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
#
add_tokens(user, tokens_purchased)
return redirect('success')
# i.e. £9.99 payment for one token or £24.99 for
#
if request.user.is_authenticated:
add_tokens(user, tokens_purchased)
return redirect('success')
else:
return redirect('handler404')
else:
#TODO: Change this to a more appropriate error message
print("exiting at the end of execute_payment()")
......@@ -112,4 +114,6 @@ def payment_cancelled(request):
return render(request, 'payment_cancelled.html')
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')
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
from .models import Profile, UserTokenCount
@receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
UserTokenCount.objects.get_or_create(user=instance)
Profile.objects.get_or_create(user=instance)
instance.profile.save()
\ No newline at end of file
......@@ -9,6 +9,13 @@
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}" />
{% 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>
<body>
......@@ -86,7 +93,15 @@
</nav>
{% endblock navbar %}
<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 scripts %} {% endblock scripts %}
</div>
<!-- <footer class="bg-white dark:bg-gray-900 fixed bottom-0 w-full"> -->
......
......@@ -51,30 +51,71 @@
{% if predictions %}
<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>
<ul id="predictionList" class="space-y-2">
{% for prediction in predictions %}
<li class="bg-gray-100 dark:bg-gray-800 px-4 py-2 rounded-md" style="white-space: pre-line;">{{ prediction|safe }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<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>
<form method="post" action="{% url 'submit_feedback' %}">
{% csrf_token %}
<input type="hidden" name="file_name" value="{{ file_name }}">
<ul id="predictionList" class="space-y-2">
{% for prediction in predictions %}
<li class="bg-gray-100 dark:bg-gray-800 px-4 py-2 rounded-md" style="white-space: pre-line;">
{{ 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>
{% endblock content %}
{% block scripts %}
<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) {
var file = event.target.files[0];
if (file) {
wavesurfer.loadBlob(file);
document.getElementById('player').classList.remove('hidden');
wavesurfer.on('ready', function () {
document.getElementById('playButton').disabled = false;
});
}
var file = event.target.files[0];
if (file) {
wavesurfer.loadBlob(file);
document.getElementById('player').classList.remove('hidden');
wavesurfer.on('ready', function () {
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() {
var form = document.getElementById('uploadForm');
......@@ -166,10 +207,25 @@ function loadAudioFile(event) {
document.getElementById('uploadForm').addEventListener('reset', function() {
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 %}