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 (2)
Showing
with 328 additions and 80 deletions
......@@ -19,6 +19,7 @@ services:
volumes:
- .:/usr/src/app
- static_volume:/usr/src/app/static
- ./models:/usr/src/app/models
depends_on:
- db
- tensorflow_serving
......
؋סȭʏ Ԛʘ(Ǜ 2
\ No newline at end of file
No preview for this file type
No preview for this file type
No preview for this file type
# Generated by Django 5.0.1 on 2024-04-30 23:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='ModelConfig',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('selected_model_version', models.CharField(default='2', max_length=255)),
],
),
]
# Generated by Django 5.0.1 on 2024-05-01 00:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('myapp', '0002_modelconfig'),
]
operations = [
migrations.CreateModel(
name='ModelPerformanceMetrics',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('request_count', models.IntegerField(default=0)),
('request_latency_sum', models.FloatField(default=0)),
('request_latency_count', models.IntegerField(default=0)),
('runtime_latency_sum', models.FloatField(default=0)),
('runtime_latency_count', models.IntegerField(default=0)),
('model_load_latency', models.FloatField(default=0)),
],
),
]
# Generated by Django 5.0.1 on 2024-05-01 00:39
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('myapp', '0003_modelperformancemetrics'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Payment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.DecimalField(decimal_places=2, max_digits=10)),
('payment_id', models.CharField(max_length=255)),
('payer_id', models.CharField(max_length=255)),
('date', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
......@@ -57,3 +57,43 @@ class Log(models.Model):
log = models.JSONField()
feedback = models.BooleanField(null=True)
class ModelConfig(models.Model):
selected_model_version = models.CharField(max_length=255, default='2')
def save(self, *args, **kwargs):
# Ensure there's only one instance of ModelConfig
self.pk = 1
super().save(*args, **kwargs)
@classmethod
def load(cls):
obj, _ = cls.objects.get_or_create(pk=1)
return obj
class ModelPerformanceMetrics(models.Model):
request_count = models.IntegerField(default=0)
request_latency_sum = models.FloatField(default=0)
request_latency_count = models.IntegerField(default=0)
runtime_latency_sum = models.FloatField(default=0)
runtime_latency_count = models.IntegerField(default=0)
model_load_latency = models.FloatField(default=0)
def reset_metrics(self):
self.request_count = 0
self.request_latency_sum = 0
self.request_latency_count = 0
self.runtime_latency_sum = 0
self.runtime_latency_count = 0
self.model_load_latency = 0
self.save(force_update=True)
class Payment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
amount = models.DecimalField(max_digits=10, decimal_places=2)
payment_id = models.CharField(max_length=255)
payer_id = models.CharField(max_length=255)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'Payment by {self.user.username} - Amount: {self.amount}'
......@@ -5,7 +5,7 @@ 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 Action, UserTokenCount
from .models import Action, UserTokenCount, Payment
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
......@@ -60,18 +60,61 @@ def create_payment(request):
# Execute a successful payment
# def execute_payment(request):
# # Get payment id and payer id
# payment_id = request.GET.get('paymentId')
# payer_id = request.GET.get('PayerID')
# #If neither ID, error, restart
# if not payment_id or not payer_id:
# 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,
# "client_id": settings.PAYPAL_CLIENT_ID,
# "client_secret": settings.PAYPAL_CLIENT_SECRET
# })
# # Check we've got a successful payment
# payment = paypalrestsdk.Payment.find(payment_id)
# # If it we do an the payer IDs match
# if payment.execute({"payer_id": payer_id}):
# print("Payment executed successfully!")
# # 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
# #
# 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()")
# return redirect('handler404')
def execute_payment(request):
# Get payment id and payer id
payment_id = request.GET.get('paymentId')
payer_id = request.GET.get('PayerID')
#If neither ID, error, restart
# If neither ID, error, restart
if not payment_id or not payer_id:
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
print("No payment")
return redirect('handler404')
# configure API
# Configure API
paypalrestsdk.configure({
"mode": settings.PAYPAL_MODE,
"client_id": settings.PAYPAL_CLIENT_ID,
......@@ -81,7 +124,7 @@ def execute_payment(request):
# Check we've got a successful payment
payment = paypalrestsdk.Payment.find(payment_id)
# If it we do an the payer IDs match
# If it we do and the payer IDs match
if payment.execute({"payer_id": payer_id}):
print("Payment executed successfully!")
......@@ -89,22 +132,24 @@ def execute_payment(request):
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
#
if request.user.is_authenticated:
add_tokens(user, tokens_purchased)
# Save payment details to the database
Payment.objects.create(
user=user,
amount=payment.transactions[0].amount.total,
payment_id=payment_id,
payer_id=payer_id
)
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()")
print("Payment execution failed")
return redirect('handler404')
def add_tokens(user, tokens):
token_count_instance, created = UserTokenCount.objects.get_or_create(user=user)
token_count_instance.token_count += tokens
......
<!-- model_performance.html -->
{% extends "_base.html" %}
{% block content %}
<div class="container mx-auto px-4">
<h1 class="text-2xl font-bold mb-4">Model Performance</h1>
{% if metrics %}
<h1 class="text-2xl font-bold mb-4">Model Performance</h1>
{% if metrics %}
<div class="bg-white shadow-md rounded p-6">
<p><strong>Request Count:</strong> {{ metrics.request_count }}</p>
<p><strong>Average Request Latency:</strong> {{ metrics.avg_request_latency|floatformat:6 }} seconds</p>
<p><strong>Average Runtime Latency:</strong> {{ metrics.avg_runtime_latency|floatformat:6 }} seconds</p>
<p><strong>Model Load Latency:</strong> {{ metrics.model_load_latency|floatformat:6 }} seconds</p>
<p><strong>Request Count:</strong> {{ metrics.request_count }}</p>
<p><strong>Average Request Latency:</strong> {{ metrics.avg_request_latency|floatformat:6 }} seconds</p>
<p><strong>Average Runtime Latency:</strong> {{ metrics.avg_runtime_latency|floatformat:6 }} seconds</p>
<p><strong>Model Load Latency:</strong> {{ metrics.model_load_latency|floatformat:6 }} seconds</p>
</div>
{% else %}
{% else %}
<p>Failed to retrieve model performance metrics.</p>
{% endif %}
{% endif %}
<form method="post" action="{% url 'model_performance' %}" class="mt-4">
{% csrf_token %}
<button type="submit" name="reset_metrics" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Reset Metrics
</button>
</form>
</div>
{% endblock content %}
\ No newline at end of file
{% extends '_base.html' %}
{% block content %}
<h1>Model Version Selection</h1>
<form method="post">
{% csrf_token %}
<label for="model_version">Select Model Version:</label>
<select name="model_version" id="model_version">
{% for version in model_versions %}
<option value="{{ version }}" {% if selected_model_version == version %}selected{% endif %}>{{ version }}</option>
{% endfor %}
</select>
<button type="submit">Save</button>
</form>
{% endblock %}
\ No newline at end of file
{% extends "_base.html" %} {% block content %}
<!-- user_page.html -->
{% extends "_base.html" %}
{% block content %}
<div
class="grid grid-cols-1 px-4 pt-6 xl:grid-cols-3 xl:gap-4 dark:bg-grey-300"
>
......@@ -33,7 +35,13 @@
<h3 class="mb-4 text-xl font-semibold dark:text-white">Change your password</h3>
<a href="{% url 'password_change' %}" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Change Password</a>
</div>
{% if user_profile.user_type == 2 or user.is_superuser %}
<div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800">
<h3 class="mb-4 text-xl font-semibold dark:text-white">Model Selection</h3>
<a href="{% url 'model_selection' %}" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Select Model</a>
</div>
{% endif %}
{% if user_profile.user_type == 3 or user.is_superuser or user_profile.user_type == 1%}
<div
......
from django.urls import path
from .views import InstrumentDetectionView, ModelPerformanceView, index, log_fileupload, users, maintenance, \
from .views import InstrumentDetectionView, ModelPerformanceView, ModelSelectionView, index, log_fileupload, users, maintenance, \
handler404, handler500, terms_conditions, privacy_policy, pricing, generate_pdf, admin_table,\
change_user_type, submit_feedback
from .payments import create_payment, execute_payment, payment_cancelled, payment_success
......@@ -26,6 +26,7 @@ urlpatterns = [
path('generate_pdf/', generate_pdf, name='generate_pdf'),
path('instrument_detection/', InstrumentDetectionView.as_view(), name='instrument_detection'),
path('model_performance/', ModelPerformanceView.as_view(), name='model_performance'),
path('model_selection/', ModelSelectionView.as_view(), name='model_selection'),
path('password_change/', auth_views.PasswordChangeView.as_view(template_name='password_change_form.html'), name='password_change'),
path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(template_name='password_change_done.html'), name='password_change_done'),
......
# views.py
import os
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
......@@ -12,7 +14,7 @@ import json
from datetime import datetime
from .forms import InstrumentDetectionForm
from .models import Log, Action, User, UserTokenCount, Profile
from .models import Log, Action, User, UserTokenCount, Profile, ModelConfig, ModelPerformanceMetrics
from django.http import JsonResponse
from django.db import connection
......@@ -26,8 +28,8 @@ import requests
# Authentication Imports
from django.urls import reverse_lazy
from django.views import generic
from .models import Profile
from django.views import View, generic
from .models import Profile, ModelConfig
from .forms import UserRegisterForm, LoginAuthenticationForm
from django.contrib.auth.views import LoginView
from django.views.decorators.csrf import csrf_exempt
......@@ -332,42 +334,50 @@ class InstrumentDetectionView(APIView):
else: return super().dispatch(request, *args, **kwargs)
def post(self, request):
# Get the user's token count
user_token_count = UserTokenCount.objects.get(user=request.user)
# Decrease the user's token count by one
user_token_count.token_count -= 1
user_token_count.save()
serializer = InstrumentDetectionSerializer(data=request.data)
if serializer.is_valid():
audio_file = serializer.validated_data['audio_file']
# Save the uploaded file temporarily
# with open('temp_audio.wav', 'wb') as f:
# f.write(audio_file.read())
# Preprocess the audio file
preprocessed_data = preprocess_audio_for_inference(audio_file)
# Prepare data for TensorFlow Serving
data = json.dumps({"signature_name": "serving_default", "instances": [window.tolist() for window in preprocessed_data]})
# Send request to TensorFlow Serving
url = 'http://tensorflow_serving:8501/v1/models/instrument_model/versions/2:predict'
headers = {"Content-Type": "application/json"}
response = requests.post(url, data=data, headers=headers)
# Process the response
if response.status_code == 200:
raw_predictions = response.json()['predictions']
# Convert raw prediction numbers into percentages
formatted_predictions = self.format_predictions(raw_predictions)
return Response({"predictions": formatted_predictions}, status=status.HTTP_200_OK)
else:
return Response({"error": "Failed to get predictions"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
try:
model_config = ModelConfig.load()
selected_model_version = model_config.selected_model_version
# Get the user's token count
user_token_count = UserTokenCount.objects.get(user=request.user)
# Decrease the user's token count by one
user_token_count.token_count -= 1
user_token_count.save()
serializer = InstrumentDetectionSerializer(data=request.data)
if serializer.is_valid():
audio_file = serializer.validated_data['audio_file']
# Save the uploaded file temporarily
# with open('temp_audio.wav', 'wb') as f:
# f.write(audio_file.read())
# Preprocess the audio file
preprocessed_data = preprocess_audio_for_inference(audio_file)
# Prepare data for TensorFlow Serving
data = json.dumps({"signature_name": "serving_default", "instances": [window.tolist() for window in preprocessed_data]})
# Send request to TensorFlow Serving
# url = 'http://tensorflow_serving:8501/v1/models/instrument_model/versions/2:predict'
url = f'http://tensorflow_serving:8501/v1/models/instrument_model/versions/{selected_model_version}:predict'
headers = {"Content-Type": "application/json"}
response = requests.post(url, data=data, headers=headers)
# Process the response
if response.status_code == 200:
print('Predictions received')
raw_predictions = response.json()['predictions']
# Convert raw prediction numbers into percentages
formatted_predictions = self.format_predictions(raw_predictions)
return Response({"predictions": formatted_predictions}, status=status.HTTP_200_OK)
else:
return Response({"error": "Failed to get predictions"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
except Exception as e:
logger.error(f"An error occurred: {e}")
messages.error(request, 'An error occurred: {e}')
return redirect('index')
def format_predictions(self, predictions):
......@@ -395,10 +405,10 @@ class ModelPerformanceView(UserPassesTestMixin, TemplateView):
def dispatch(self, request, *args, **kwargs):
if request.user.is_anonymous:
messages.info(request, 'Must be logged in as an ML Engineer or Admin to access this page.')
messages.info(request, 'Must be logged in as an ML Engineer or Superuser to access this page.')
return redirect('login')
elif request.user.profile.user_type != 2 and not request.user.is_superuser:
messages.info(request, 'Must be logged in as an ML Engineer or Admin to access this page.')
messages.info(request, 'Must be logged in as an ML Engineer or Superuser to access this page.')
return redirect('users')
else:
return super().dispatch(request, *args, **kwargs)
......@@ -423,21 +433,73 @@ class ModelPerformanceView(UserPassesTestMixin, TemplateView):
runtime_latency_count = re.search(r':tensorflow:serving:runtime_latency_count{model_name="instrument_model",API="Predict",runtime="TF1"} (\d+)', metrics_data)
model_load_latency = re.search(r':tensorflow:cc:saved_model:load_latency{model_path="/models/instrument_model/2"} (\d+)', metrics_data)
# Get or create the ModelPerformanceMetrics instance
metrics, _ = ModelPerformanceMetrics.objects.get_or_create(pk=1)
# Update the metrics in the database
metrics.request_count = int(request_count.group(1)) if request_count else 0
metrics.request_latency_sum = float(request_latency_sum.group(1)) if request_latency_sum else 0
metrics.request_latency_count = int(request_latency_count.group(1)) if request_latency_count else 0
metrics.runtime_latency_sum = float(runtime_latency_sum.group(1)) if runtime_latency_sum else 0
metrics.runtime_latency_count = int(runtime_latency_count.group(1)) if runtime_latency_count else 0
metrics.model_load_latency = float(model_load_latency.group(1)) if model_load_latency else 0
metrics.save()
# Calculate average latencies in seconds
avg_request_latency = float(request_latency_sum.group(1)) / float(request_latency_count.group(1)) / 1e6 if request_latency_sum and request_latency_count else None
avg_runtime_latency = float(runtime_latency_sum.group(1)) / float(runtime_latency_count.group(1)) / 1e6 if runtime_latency_sum and runtime_latency_count else None
model_load_latency_seconds = float(model_load_latency.group(1)) / 1e6 if model_load_latency else None
avg_request_latency = metrics.request_latency_sum / metrics.request_latency_count / 1e6 if metrics.request_latency_count else None
avg_runtime_latency = metrics.runtime_latency_sum / metrics.runtime_latency_count / 1e6 if metrics.runtime_latency_count else None
context['metrics'] = {
'request_count': request_count.group(1) if request_count else None,
'request_count': metrics.request_count,
'avg_request_latency': avg_request_latency,
'avg_runtime_latency': avg_runtime_latency,
'model_load_latency': model_load_latency_seconds
'model_load_latency': metrics.model_load_latency / 1e6
}
else:
context['metrics'] = None
return context
def post(self, request, *args, **kwargs):
if 'reset_metrics' in request.POST:
metrics = ModelPerformanceMetrics.objects.get(pk=1)
metrics.reset_metrics()
metrics.save()
return redirect('users')
else:
messages.error(request, 'Invalid request')
return redirect('users')
class ModelSelectionView(UserPassesTestMixin, View):
def dispatch(self, request, *args, **kwargs):
if request.user.is_anonymous:
messages.info(request, 'Must be logged in as an ML Engineer or Superuser to access this page.')
return redirect('login')
elif request.user.profile.user_type != 2 and not request.user.is_superuser:
messages.info(request, 'Must be logged in as an ML Engineer or Superuser to access this page.')
return redirect('users')
else:
return super().dispatch(request, *args, **kwargs)
def test_func(self):
return self.request.user.is_authenticated and self.request.user.profile.user_type == 2
def get(self, request):
model_versions = os.listdir('models/instrument_model')
model_config = ModelConfig.load()
context = {
'model_versions': model_versions,
'selected_model_version': model_config.selected_model_version,
}
return render(request, 'model_selection.html', context)
def post(self, request):
selected_model_version = request.POST.get('model_version')
model_config = ModelConfig.load()
model_config.selected_model_version = selected_model_version
model_config.save()
return redirect('model_selection')
def change_user_type(request, user_id):
if request.method == 'POST':
......@@ -449,7 +511,4 @@ def change_user_type(request, user_id):
def user_has_credits():
has_credits = False
return has_credits