diff --git a/myproject/myapp/models.py b/myproject/myapp/models.py index 900085ca0dbfecf5621a2d1b8796c8bb8842cac2..326123a737d7ef7aca00446c56a78ff39684017b 100644 --- a/myproject/myapp/models.py +++ b/myproject/myapp/models.py @@ -1,7 +1,8 @@ +# models.py from django.db import models -#from django.contrib.auth import get_user_model -from django.contrib.auth.models import User#, Group, Permission -#from django.contrib.contenttypes.models import ContentType +from django.contrib.auth import get_user_model +from django.contrib.auth.models import User, Group, Permission +from django.contrib.contenttypes.models import ContentType from enum import Enum from django.dispatch import receiver from django.db.models.signals import post_save @@ -20,6 +21,7 @@ class Profile(models.Model): def __str__(self): return f'{self.user.username} Profile' + # Model to hold the user token count class UserTokenCount(models.Model): # User @@ -29,28 +31,22 @@ class UserTokenCount(models.Model): def __str__(self): return f'{self.user.username}\'s token count: {self.token_count}' - -# Automatically create a UserTokenCount and a account type Profile table 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." + 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') @@ -59,3 +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) + diff --git a/myproject/myapp/payments.py b/myproject/myapp/payments.py index 1fed0f117c95e23275876c5a118c4893e3bb7957..c99f04c4c4c3d6879c72cb2ffaca3993e743e753 100644 --- a/myproject/myapp/payments.py +++ b/myproject/myapp/payments.py @@ -6,9 +6,11 @@ from django.urls import reverse from django.shortcuts import redirect, render from django.urls import reverse from .models import UserTokenCount +from rest_framework.response import Response +from rest_framework import status # Create a payment that can be made via the PayPal API -def create_payment(request): +def create_payment(request, purchase_type): # Configure PayPal SDK paypalrestsdk.configure({ "mode": settings.PAYPAL_MODE, @@ -16,6 +18,26 @@ def create_payment(request): "client_secret": settings.PAYPAL_CLIENT_SECRET }) + # We need to check what kind of payment it is first, how many tokens are being bought? + if purchase_type == "single": + name = "Single purchase" + sku = "sku01" + quantity = 1 + price = "9.99" + description = "Single purchase of 1 token" + + elif purchase_type == "bulk": + name = "Bulk purchase" + sku = "sku02" + quantity = 10 + price = "44.99" + description = "Bulk purchase of 10 tokens" + else: + return JsonResponse({"error": "Invalid purchase type"}) + + # Pass the quantity to the session to later change the payment execution type + request.session['purchase_quantity'] = quantity + # Create payment object payment = paypalrestsdk.Payment({ "intent": "sale", @@ -29,18 +51,18 @@ def create_payment(request): "transactions" : [{ "item_list" : { "items" : [{ - "name": "Test item", - "sku": "test item", - "price": "9.99", + "name": name, + "sku": sku, + "price": price, "currency": "GBP", "quantity": 1, }] }, "amount" : { - "total": "9.99", + "total": price, "currency": "GBP" }, - "description": "Test payment description" + "description": description }] }) @@ -66,9 +88,8 @@ def execute_payment(request): #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') + print("no payment id or payer_id") + return Response({"error": "Error: No payment id or payer id was found."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) # configure API paypalrestsdk.configure({ @@ -83,28 +104,22 @@ def execute_payment(request): # If it we do an the payer IDs match if payment.execute({"payer_id": payer_id}): print("Payment executed successfully!") + print(f"Payment: {payment}") # 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) + tokens_purchased = request.session.get("purchase_quantity") + + add_tokens(request.user, tokens_purchased) return redirect('success') else: - #TODO: Change this to a more appropriate error message - print("exiting at the end of execute_payment()") - return redirect('handler404') + print("exiting at the end of execute_payment(), incorrect payer id") + return Response({"error": "Error: Payment failed to execute, incorrect payer id"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) def add_tokens(user, tokens): token_count_instance, created = UserTokenCount.objects.get_or_create(user=user) - token_count_instance.token_count += tokens + token_count_instance.token_count += int(tokens) token_count_instance.save() def payment_cancelled(request): diff --git a/myproject/myapp/templates/payment_success.html b/myproject/myapp/templates/payment_success.html index 8496abe27218036de5f59812760e16c1b43ba7c9..51a411039e2529af3f8d003f8490d8531ffcc352 100644 --- a/myproject/myapp/templates/payment_success.html +++ b/myproject/myapp/templates/payment_success.html @@ -1,10 +1,14 @@ {% extends "_base.html" %}{% block content %} -<div class="grid grid-cols-1 gap-16 items-center py-8 px-4 mx-auto w-70 lg:grid"> - <div class="font-light text-gray-500 sm:text-lg dark:text-gray-400"> - <h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">Success!</h2> - <p class="mb-4">Your payment was successful and you have been credited with 1 token.</p> +<div class="flex mt-6"> + <div class="grid grid-cols-1 gap-12 items-center py-8 px-4 mx-auto w-70 lg:grid"> + <div class="font-light text-gray-500 sm:text-lg dark:text-gray-400 text-center"> + <h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">Transaction completed</h2> + <p class=" mt-6">Your payment was successful.</p> + </div> + <button class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-200 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-900 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex justify-center text-center"><a href="{% url 'users' %}">Return</a></button> </div> </div> -<button class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-200 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-900 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex justify-center text-center"><a href="{% url 'users' %}">Return</a></button> + + {% endblock content%} diff --git a/myproject/myapp/templates/pricing.html b/myproject/myapp/templates/pricing.html index 78b60c2de6bfdf0b8bc7348ba8439a66e76fbcc0..7f72582cc008f5f724d4beb40dab2c2ae18c1b11 100644 --- a/myproject/myapp/templates/pricing.html +++ b/myproject/myapp/templates/pricing.html @@ -1,7 +1,7 @@ {% extends "_base.html" %}{% block content %} <div class="grid grid-cols-1 gap-16 items-center py-8 px-4 mx-auto w-70 lg:grid"> - <div class="font-light text-gray-500 sm:text-lg dark:text-gray-400"> + <div class="font-light text-gray-500 sm:text-lg dark:text-gray-400 my-10"> <h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">An Intelligent System for Instrument Detection</h2> <p class="mb-4">*placeholder input* We present to you a intelligent system for instrument detection. Using audio processing techinques and a convolutionsal neural network we are able to classify instruments used in a song. other exciting words that might catch peoples attention and make them use our product. @@ -38,62 +38,35 @@ <span class="text-base font-normal leading-tight text-gray-500 ms-3">Email support</span> </li> </ul> - <button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-200 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-900 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex justify-center w-full text-center"><a href="{% url 'create_payment' %}">Purchase via PayPal</a></button> + <button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-200 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-900 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex justify-center w-full text-center"><a href="{% url 'create_payment' 'single' %}">Purchase via PayPal</a></button> </div> <div class="w-full mx-auto max-w-sm p-4 bg-white border border-gray-200 rounded-lg shadow sm:p-8 dark:bg-gray-800 dark:border-gray-700"> <h5 class="mb-4 text-xl font-medium text-gray-500 dark:text-gray-400">Bulk Purchase</h5> <div class="flex items-baseline text-gray-900 dark:text-white"> - <span class="text-3xl font-semibold">£</span> - <span class="text-5xl font-extrabold tracking-tight">49</span> - <span class="ms-1 text-xl font-normal text-gray-500 dark:text-gray-400">/month</span> + <span class="text-5xl font-extrabold tracking-tight">£44.99</span> </div> <ul role="list" class="space-y-5 my-7"> <li class="flex items-center"> <svg class="flex-shrink-0 w-4 h-4 text-blue-700 dark:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"/> </svg> - <span class="text-base font-normal leading-tight text-gray-500 dark:text-gray-400 ms-3">2 team members</span> + <span class="text-base font-normal leading-tight text-gray-500 dark:text-gray-400 ms-3">10 tokens</span> </li> - <li class="flex"> - <svg class="flex-shrink-0 w-4 h-4 text-blue-700 dark:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> - <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"/> - </svg> - <span class="text-base font-normal leading-tight text-gray-500 dark:text-gray-400 ms-3">20GB Cloud storage</span> - </li> - <li class="flex decoration-gray-500"> - <svg class="flex-shrink-0 w-4 h-4 text-blue-700 dark:text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> - <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"/> - </svg> - <span class="text-base font-normal leading-tight text-gray-500 ms-3">100 File Uploads</span> - <span class="ms-1 text-sm font-normal text-gray-500 dark:text-gray-400">/month</span> - </li> - <li class="flex"> - <svg class="flex-shrink-0 w-4 h-4 text-blue-700 dark:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> - <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"/> - </svg> - <span class="text-base font-normal leading-tight text-gray-500 dark:text-gray-400 ms-3">Integration help</span> - </li> - <li class="flex decoration-gray-500"> - <svg class="flex-shrink-0 w-4 h-4 text-blue-700 dark:text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> + <li class="flex decoration-gray-500"> + <svg class="flex-shrink-0 w-4 h-4 text-gray-400 dark:text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"/> </svg> <span class="text-base font-normal leading-tight text-gray-500 ms-3">API Access</span> </li> - <li class="flex decoration-gray-500"> - <svg class="flex-shrink-0 w-4 h-4 text-blue-700 dark:text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> - <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"/> - </svg> - <span class="text-base font-normal leading-tight text-gray-500 ms-3">Complete documentation</span> - </li> - <li class="flex decoration-gray-500"> - <svg class="flex-shrink-0 w-4 h-4 text-blue-700 dark:text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> + <li class="flex decoration-gray-500"> + <svg class="flex-shrink-0 w-4 h-4 text-gray-400 dark:text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"/> </svg> - <span class="text-base font-normal leading-tight text-gray-500 ms-3">24×7 phone & email support</span> + <span class="text-base font-normal leading-tight text-gray-500 ms-3">Email support</span> </li> </ul> - <button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-200 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-900 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex justify-center w-full text-center">Choose plan</button> + <button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-200 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-900 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex justify-center w-full text-center"><a href="{% url 'create_payment' 'bulk' %}">Purchase via PayPal</a></button> </div> </div> diff --git a/myproject/myapp/urls.py b/myproject/myapp/urls.py index f7c169ca586946c0e567e5743886c6cf0d40c6c3..b07b3678265f4c764f99a0a02be4846ec0768622 100644 --- a/myproject/myapp/urls.py +++ b/myproject/myapp/urls.py @@ -38,7 +38,7 @@ urlpatterns = [ path('register/', RegisterView.as_view(), name='register'), path('user_logout/', auth_views.LogoutView.as_view(next_page='index'), name='user_logout'), # Payment - path('payment/create/', create_payment, name='create_payment'), + path('payment/create/<str:purchase_type>', create_payment, name='create_payment'), path('payment/execute/', execute_payment, name='execute_payment'), path('payment/cancel/', payment_cancelled, name='payment_cancelled'), path('payment_success/', payment_success, name='success') diff --git a/myproject/myapp/views.py b/myproject/myapp/views.py index a04cd1bbaca8bd0515bf55761b3ff87d3d048318..d7354e9b7cdb803af11d3b5672fcbadfdc6e1258 100644 --- a/myproject/myapp/views.py +++ b/myproject/myapp/views.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import User from django.contrib import messages from django.http import HttpResponse from django.utils import timezone -from django.shortcuts import render, redirect +from django.shortcuts import render, redirect, get_object_or_404 from django.template import RequestContext import logging from reportlab.pdfgen import canvas @@ -12,7 +12,7 @@ import json from datetime import datetime from .forms import InstrumentDetectionForm -from .models import Log, Action, User, UserTokenCount +from .models import Log, Action, User, UserTokenCount, Profile from django.http import JsonResponse from django.db import connection @@ -30,20 +30,27 @@ from django.views import generic from .models import Profile from .forms import UserRegisterForm, LoginAuthenticationForm from django.contrib.auth.views import LoginView +from django.views.decorators.csrf import csrf_exempt + +from django.contrib.auth.mixins import UserPassesTestMixin +from django.views.generic import TemplateView +import re logger = logging.getLogger(__name__) -def get_log_data(action, status='success', file=None, description=None): +def get_log_data(user, action, status='success', file=None, description=None, feedback=None): log_data = { - 'action': action.value, + 'username': user.username, + 'action': action.value.format(username=user.username), 'status': status, 'file': file, 'description': description, + 'feedback': feedback, # Add the feedback field } return log_data def create_log(user, log_data): - Log.objects.create(user=user, log=log_data) + Log.objects.create(user=user, log=log_data, feedback=log_data.get('feedback')) def handling_music_file(request): if request.method == 'POST': @@ -52,16 +59,54 @@ def handling_music_file(request): 'action': 'File uploaded', 'file': request.FILES['audio_file'].name, } - log_data = get_log_data(Action.UPLOAD_FILE, 'success', file=request.FILES['audio_file'].name) + log_data = get_log_data(request.user ,Action.UPLOAD_FILE, 'success', file=request.FILES['audio_file'].name) create_log(request.user if request.user.is_authenticated else None, log_data) return HttpResponse('File uploaded successfully!',log_data) - log_data = get_log_data(Action.invalid_file, 'error') + log_data = get_log_data(request.user ,Action.INVALID_FILE, 'error') create_log(None, log_data) return HttpResponse('File invalid',log_data) +@csrf_exempt +def log_fileupload(request): + if request.method == 'POST': + data = json.loads(request.body) + status = data.get('status') + file = data.get('file') + + if request.user.is_authenticated: + log_data = get_log_data(request.user, Action.UPLOAD_FILE, status, file) + create_log(request.user, log_data) + + return JsonResponse({'message': 'Log created successfully'}, status=201) + + return JsonResponse({'error': 'Invalid request'}, status=400) + +def submit_feedback(request): + if request.method == 'POST' and request.user.is_authenticated: + prediction = request.POST.get('prediction') + liked = request.POST.get('feedback') == 'true' + file_name = request.POST.get('file_name') # Get the filename from the form data + + # Create log data using the get_log_data function + log_data = get_log_data( + user=request.user, + action=Action.FEEDBACK_SUBMITTED, + status='success', + file=file_name, # Use the filename obtained from the form + description=prediction, + feedback=liked + ) + + # Create the Log entry using the create_log function + create_log(request.user, log_data) + + return redirect('index') + + return redirect('index') + def admin_table(request): # Execute the query and fetch all rows - query = """SELECT date, user, log FROM myapp_log ORDER BY date DESC""" + query = """SELECT date, log, user_id, feedback FROM myapp_log ORDER BY date DESC""" with connection.cursor() as cursor: cursor.execute(query) rows = cursor.fetchall() @@ -70,21 +115,24 @@ def admin_table(request): data = [] for row in rows: # Parse the JSON string into a dictionary - log = json.loads(row[2]) - # Create a dictionary with the date, user, and JSON fields + log = json.loads(row[1]) + # Get the user object based on the user_id + user_id = row[2] + # Get the feedback value + feedback = row[3] + # Create a dictionary with the date, user, JSON fields, and feedback date = row[0].strftime('%Y-%m-%d %H:%M:%S') - entry = {'date': date, 'user': row[1], 'file': log['file'], 'action': log['action'], 'status': log['status']} + entry = {'date': date, 'user': user_id, 'file': log['file'], 'action': log['action'], 'status': log['status'], + 'description': log['description'], 'feedback': feedback} data.append(entry) # Return the data as a JSON response return JsonResponse({'data': data}, safe=False) - def user_table(request): - # Execute the query and fetch all rows - query = """SELECT date, user, log FROM myapp_log ORDER BY date DESC""" + user_id = request.user.id # Only display user logs code below - # query = """SELECT date, user, log FROM myapp_log WHERE user = '{}' ORDER BY date DESC""".format(request.user) + query = """SELECT date, log, user_id, feedback FROM myapp_log WHERE user_id = {} ORDER BY date DESC""".format(user_id) with connection.cursor() as cursor: cursor.execute(query) rows = cursor.fetchall() @@ -93,10 +141,15 @@ def user_table(request): data = [] for row in rows: # Parse the JSON string into a dictionary - log = json.loads(row[2]) - # Create a dictionary with the date, user, and JSON fields + log = json.loads(row[1]) + # Get the user object based on the user_id + user_id = row[2] + # Get the feedback value + feedback = row[3] + # Create a dictionary with the date, user, JSON fields, and feedback date = row[0].strftime('%Y-%m-%d %H:%M:%S') - entry = {'date': date, 'user': row[1], 'file': log['file'], 'action': log['action'], 'status': log['status']} + entry = {'date': date, 'user': user_id, 'file': log['file'], 'action': log['action'], 'status': log['status'], + 'description': log['description'], 'feedback': feedback} data.append(entry) # Return the data as a JSON response @@ -104,32 +157,35 @@ def user_table(request): def index(request): # Initialize default context - context = {'form': InstrumentDetectionForm(), - 'predictions': [], - 'file_name': None - } - + context = {'form': InstrumentDetectionForm(), 'predictions': [], 'file_name': None} + # Handle authenticated users if request.user.is_authenticated: token_count = UserTokenCount.objects.get(user=request.user).token_count context['token_count'] = token_count + if request.method == 'POST': form = InstrumentDetectionForm(request.POST, request.FILES) if form.is_valid() and 'audio_file' in request.FILES: uploaded_file = request.FILES['audio_file'] context['file_name'] = uploaded_file.name + # Make a request to the InstrumentDetectionView to get the predictions view = InstrumentDetectionView().as_view() response = view(request) + # Ensure there's a response and it contains predictions before updating context if response and hasattr(response, 'data') and 'predictions' in response.data: context['predictions'] = response.data['predictions'] + + if request.user.is_authenticated: + feedback = request.POST.get('feedback') # Get the user's feedback from the form + predictions_string = ', '.join(response.data['predictions']) + log_data = get_log_data(request.user, Action.RUN_ALGORITHM, 'success', file=uploaded_file.name, + description=predictions_string, feedback=feedback) + create_log(request.user, log_data) else: context['form'] = form - # For GET requests or if form is not valid, render the page with the default or updated context - return render(request, 'index1.html', context) - - # Handle unauthenticated users else: if request.method == 'POST': # Assuming you want to do something specific with the file if it's uploaded @@ -137,33 +193,41 @@ def index(request): if uploaded_file: # Process the uploaded file as needed pass - # For now, just render the main page again, potentially after handling the uploaded file - # For GET requests or if POST doesn't involve a file, just show the main page + + # For GET requests or if form is not valid, render the page with the default or updated context + if request.user.is_authenticated: + return render(request, 'index1.html', context) + else: return render(request, 'index2.html') - + def users(request): - # Make a request to the admin_table view to get the data - context = {} - data_admin = admin_table(request) - data_user = user_table(request) - admin_dict = json.loads(data_admin.content) - user_dict = json.loads(data_user.content) - token_count = UserTokenCount.objects.get(user=request.user).token_count - user_profile = request.user.profile - user = request.user - # Pass the data as a context variable to the template - # !!! ADMIN DATA ONLY DISPLAYED AND GET IF USER IS ADMIN !!! - context['admin_data'] = admin_dict['data'] - context['user_data'] = user_dict['data'] - context['token_count'] = token_count - context['user_profile'] = user_profile - context['user'] = user - - return render(request, 'user_page.html', context) + if request.user.is_authenticated: + # Make a request to the admin_table view to get the data + context = {} + data_admin = admin_table(request) + data_user = user_table(request) + admin_dict = json.loads(data_admin.content) + user_dict = json.loads(data_user.content) + token_count = UserTokenCount.objects.get(user=request.user).token_count + user_profile = request.user.profile + user = request.user + all_user_profiles = Profile.objects.all() # Retrieve all Profile objects + + # Pass the data as a context variable to the template + # !!! ADMIN DATA ONLY DISPLAYED AND GET IF USER IS ADMIN !!! + context['admin_data'] = admin_dict['data'] + context['user_data'] = user_dict['data'] + context['token_count'] = token_count + context['user_profile'] = user_profile + context['user'] = user + context['all_user_profiles'] = all_user_profiles # Add all_user_profiles to the context + + return render(request, 'user_page.html', context) + return redirect('login') def handler404(request, *args, **kwargs): response = render(request, '404.html', {}) @@ -178,60 +242,43 @@ def handler500(request, *args, **kwargs): def maintenance(request): return render(request, 'maintenance.html') -# def user_login(request): -# if request.method == 'POST': -# form = LoginForm(request.POST) - -# if form.is_valid(): -# username = form.cleaned_data.get('username') -# password = form.cleaned_data.get('password') - -# user = authenticate(request, username=username, password=password) # Passing request along with username and password - -# if user: -# login(request, user=user) # Passing request along with user -# return redirect('users') -# else: -# messages.error(request, 'Invalid username or password.') -# else: -# pass - -# else: -# form = LoginForm() -# return render(request, 'login.html', {'form': form}) - - -# def register(request): -# if request.method == 'POST': -# form = CustomRegistrationForm(request.POST) -# if form.is_valid(): -# form.save() -# return redirect('user_login') -# else: -# form = CustomRegistrationForm() - -# return render(request, 'register.html', {'form': form}) - -# def user_logout(request): -# logout(request) -# return redirect('user_login') - # Authentication class RegisterView(generic.CreateView): form_class = UserRegisterForm - success_url = reverse_lazy('login') + success_url = reverse_lazy('index') template_name = 'registration/register.html' def form_valid(self, form): response = super().form_valid(form) - Profile.objects.create(user=self.object, user_type=0) # Default user type as Basic User + user = self.object # Grab the user instance + + # Ensure the user is active; this line might be redundant if you're sure users are active by default + user.is_active = True + user.save() + + # Check if the Profile exists, and if not, create it + if not Profile.objects.filter(user=user).exists(): + Profile.objects.create(user=user, user_type=0) # Default user type as Basic User + + # Log the user in + login(self.request, user) + return response - + class CustomLoginView(LoginView): authentication_form = LoginAuthenticationForm - template_name = 'registration/login.html' + template_name = 'registration/login.html' + + def form_valid(self, form): + # Create log if user is authenticated + login(self.request, form.get_user()) + + log_data = get_log_data(form.get_user(), Action.LOGIN, 'success') + create_log(form.get_user(), log_data) + + return super().form_valid(form) def terms_conditions(request): @@ -258,25 +305,36 @@ def generate_pdf(request): # Running the audio file through the model class InstrumentDetectionView(APIView): def post(self, request): + # Get the user's token count + user_token_count = UserTokenCount.objects.get(user=request.user) + + # Check if the user has more than one token + if user_token_count.token_count < 1: + return Response({'error': 'Insufficient tokens'}, status=status.HTTP_403_FORBIDDEN) + + # 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'] @@ -285,32 +343,80 @@ class InstrumentDetectionView(APIView): 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) - - def convert_to_percentages(self, predictions): - # Assuming predictions is a list of lists - percentage_predictions = [] - for prediction in predictions: - total = sum(prediction) - # Convert each number to a percentage of the total, rounded to 2 decimal places - percentages = [round((number / total) * 100, 2) for number in prediction] - percentage_predictions.append(percentages) - return percentage_predictions - + + def format_predictions(self, predictions): instruments = ['Guitar', 'Drum', 'Violin', 'Piano'] - formatted_predictions = [] + instrument_windows = {instrument: [] for instrument in instruments} + for window_index, prediction in enumerate(predictions, start=1): - formatted_window = f"<strong>Window {window_index}</strong><br>" - formatted_scores = "<br>".join([f"{instruments[i]} - {score:.2f}" for i, score in enumerate(prediction)]) - formatted_predictions.append(f"{formatted_window}{formatted_scores}") + highest_score_index = prediction.index(max(prediction)) + highest_score_instrument = instruments[highest_score_index] + instrument_windows[highest_score_instrument].append(window_index) + + formatted_predictions = [] + for instrument, windows in instrument_windows.items(): + if windows: + window_list = ', '.join(map(str, windows)) + formatted_predictions.append(f"{instrument} - Windows: {window_list}") + return formatted_predictions + + + + +class ModelPerformanceView(UserPassesTestMixin, TemplateView): + template_name = 'model_performance.html' + def test_func(self): + return self.request.user.is_authenticated and (self.request.user.is_superuser or self.request.user.profile.user_type == 2) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + metrics_url = 'http://tensorflow_serving:8501/monitoring/prometheus/metrics' + response = requests.get(metrics_url) + + if response.status_code == 200: + metrics_data = response.text + + # Extract metrics using regular expressions + request_count = re.search(r':tensorflow:serving:request_count{model_name="instrument_model",status="OK"} (\d+)', metrics_data) + request_latency_sum = re.search(r':tensorflow:serving:request_latency_sum{model_name="instrument_model",API="predict",entrypoint="REST"} ([\d\.e\+]+)', metrics_data) + request_latency_count = re.search(r':tensorflow:serving:request_latency_count{model_name="instrument_model",API="predict",entrypoint="REST"} (\d+)', metrics_data) + runtime_latency_sum = re.search(r':tensorflow:serving:runtime_latency_sum{model_name="instrument_model",API="Predict",runtime="TF1"} ([\d\.e\+]+)', metrics_data) + 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) + + # 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 + + context['metrics'] = { + 'request_count': request_count.group(1) if request_count else None, + 'avg_request_latency': avg_request_latency, + 'avg_runtime_latency': avg_runtime_latency, + 'model_load_latency': model_load_latency_seconds + } + else: + context['metrics'] = None + + return context + +def change_user_type(request, user_id): + if request.method == 'POST': + user_type = request.POST.get('user_type') + user_profile = get_object_or_404(Profile, user__id=user_id) # Get the user profile + user_profile.user_type = user_type + user_profile.save() + return redirect('users') # Redirect to the users page def user_has_credits(): has_credits = False - return has_credits \ No newline at end of file + return has_credits