Skip to content

Commit e950977

Browse files
committed
Major refactoring of views.py and other files thanks to pylint use and google python development guide
1 parent 3c28bd5 commit e950977

File tree

12 files changed

+144
-47
lines changed

12 files changed

+144
-47
lines changed
410 Bytes
Binary file not shown.
354 Bytes
Binary file not shown.
54 Bytes
Binary file not shown.
1.66 KB
Binary file not shown.

App/models.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
from django.db import models
22
from django.conf import settings
3+
from django.dispatch import receiver
4+
from django.db.models.signals import post_delete
35

46

57
class FileModel(models.Model):
68
file = models.FileField(null=True, blank=True)
79
timestamp = models.DateTimeField(auto_now_add=True)
810
path = models.FilePathField(path=settings.MEDIA_ROOT, default=settings.MEDIA_ROOT)
11+
12+
13+
@receiver(post_delete, sender=FileModel)
14+
def submission_delete(sender, instance, **kwargs):
15+
"""
16+
This function is used to delete attachments when a file object is deleted.
17+
Django does not do this automatically.
18+
"""
19+
instance.file.delete(False)

App/templates/index.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ <h2>Deep Learning to predict human emotions.</h2>
1919
</div>
2020
<div class="container" id="Container1">
2121
<div class="row">
22-
<div class="col-sm-6 text-center" id="FileUpload">
22+
<div class="col-sm-4 text-center" id="FileUpload">
2323
<a href="{% url 'upload_file' %}" class="btn btn-primary">Upload your audio file</a>
2424
</div>
25-
<div class="col-sm-6 text-center" id="makePredictions">
25+
<div class="col-sm-4 text-center" id="FileDelete">
26+
<a href="{% url 'file_delete' %}" class="btn btn-primary">Delete a file from the server</a>
27+
</div>
28+
<div class="col-sm-4 text-center" id="makePredictions">
2629
<a href="{% url 'file_select' %}" class="btn btn-primary">Make your prediction</a>
2730
</div>
2831
</div>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{% extends "index.html" %}
2+
3+
{% block content %}
4+
<form action="/App/delete/" method="post" enctype="multipart/form-data">
5+
{% csrf_token %}
6+
{{ form.as_p }}
7+
<p>Please select one file at a time from the list below to delete it from the server.</p>
8+
<p>The list includes the files already available on the server.</p>
9+
{% for myfile in filename %}
10+
<input type="checkbox" name="file_name" value="{{ myfile }}">{{ myfile }}<br>
11+
{% endfor %}
12+
<br>
13+
<button type="submit" class="btn btn-primary">Delete</button>
14+
</form>
15+
{% endblock %}

App/tests.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
"""
2+
tests.py includes all the test of the application.
3+
"""
4+
15
import os
2-
from App.views import Predict
6+
37
from django.test import TestCase
48
from rest_framework.request import Request
59
from rest_framework.test import APIRequestFactory
610
from rest_framework.parsers import MultiPartParser, FormParser
711
from django.core.files.uploadedfile import SimpleUploadedFile
812

13+
from App.views import Predict
914
from App.forms import FileForm
1015

1116

@@ -90,6 +95,13 @@ def testselectfile(self):
9095
file_name_dir = 'App/templates/select_file_predictions.html'
9196
assert os.path.isfile(file_name_dir)
9297

98+
def testdeletefile(self):
99+
"""
100+
Ensure a select_file_predictions template exists
101+
"""
102+
file_name_dir = 'App/templates/select_file_deletion.html'
103+
assert os.path.isfile(file_name_dir)
104+
93105

94106
class TestForm(TestCase):
95107

App/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
from App.views import Predict, FileView
1+
from App.views import Predict, FileView, FileDeleteView
22
from django.conf.urls import url
33

44
app_name = 'App'
55

66
urlpatterns = [
77
url(r'^predict/$', Predict.as_view(), name="APIpredict"),
88
url(r'^upload/$', FileView.as_view(), name='APIupload'),
9+
url(r'^delete/$', FileDeleteView.as_view(), name='APIupload'),
910
]
1011

1112

App/views.py

Lines changed: 93 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1+
"""
2+
views.py includes the main business logic of the application.
3+
Its role is to manage file upload, deletion and predictions.
4+
"""
5+
16
import os
7+
from os import listdir
8+
from os.path import join
9+
from os.path import isfile
10+
211
import keras
312
import librosa
413
import numpy as np
5-
from os import listdir
614
import tensorflow as tf
7-
from App.models import FileModel
8-
from rest_framework import views
915
from django.conf import settings
10-
from os.path import isfile, join
11-
from rest_framework import status
12-
from App.serialize import FileSerializer
13-
from rest_framework.response import Response
1416
from django.views.generic import TemplateView
1517
from django.views.generic.edit import CreateView
18+
from rest_framework import views
19+
from rest_framework import status
20+
from rest_framework import generics
21+
from rest_framework.parsers import FormParser
22+
from rest_framework.parsers import MultiPartParser
23+
from rest_framework.response import Response
1624
from rest_framework.renderers import TemplateHTMLRenderer
17-
from rest_framework.parsers import MultiPartParser, FormParser
25+
26+
from App.models import FileModel
27+
from App.serialize import FileSerializer
1828

1929

2030
class IndexView(TemplateView):
@@ -48,7 +58,29 @@ class SelectPredFileView(TemplateView):
4858
The server will return the predictions.
4959
"""
5060

51-
template_name = "select_file_predictions.html"
61+
template_name = 'select_file_predictions.html'
62+
parser_classes = FormParser
63+
queryset = FileModel.objects.all()
64+
65+
def get_context_data(self, **kwargs):
66+
"""
67+
This function is used to render the list of file in the MEDIA_ROOT in the html template.
68+
"""
69+
context = super().get_context_data(**kwargs)
70+
media_path = settings.MEDIA_ROOT
71+
myfiles = [f for f in listdir(media_path) if isfile(join(media_path, f))]
72+
context['filename'] = myfiles
73+
return context
74+
75+
76+
class SelectFileDelView(TemplateView):
77+
"""
78+
This view is used to select a file from the list of files in the server.
79+
After the selection, it will send the file to the server.
80+
The server will delete the file.
81+
"""
82+
template_name = 'select_file_deletion.html'
83+
5284
parser_classes = FormParser
5385
queryset = FileModel.objects.all()
5486

@@ -65,8 +97,8 @@ def get_context_data(self, **kwargs):
6597

6698
class FileView(views.APIView):
6799
"""
68-
This class contains the method to upload and delete a file interacting directly with the API.
69-
POST and DELETE request are accepted.
100+
This class contains the method to upload a file interacting directly with the API.
101+
POST requests are accepted.
70102
"""
71103
parser_classes = (MultiPartParser, FormParser)
72104
queryset = FileModel.objects.all()
@@ -79,34 +111,52 @@ def upload(self, request):
79111
if file_serializer.is_valid():
80112
# TODO: implement a check to see if the file is already on the server
81113
file_serializer.save()
82-
return Response(file_serializer.data, status=status.HTTP_201_CREATED)
114+
response = Response(file_serializer.data, status=status.HTTP_201_CREATED)
83115
else:
84-
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
116+
response = Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
85117

86-
def delete(self, request):
87-
# TODO: Implement
88-
raise NotImplementedError
118+
return response
119+
120+
121+
class FileDeleteView(generics.RetrieveDestroyAPIView):
122+
"""
123+
This class contains the method to delete a file interacting directly with the API.
124+
DELETE requests are accepted.
125+
"""
126+
127+
parser_classes = FormParser
128+
queryset = FileModel.objects.all()
129+
130+
def post(self, request):
131+
"""
132+
This method will be used to delete files from the server.
133+
"""
134+
# TODO: complete, not working (error: type "object" is not iterable)
135+
filename = request.POST.getlist('file_name').pop()
136+
getfile = FileModel.objects.filter(name__in=filename)
137+
getfile.file.delete()
138+
return getfile
89139

90140

91141
class Predict(views.APIView):
92142
"""
93143
This class is used to making predictions.
94144
95145
Example of input:
96-
{"filename": "01-01-01-01-01-01-01.wav"}
146+
{'filename': '01-01-01-01-01-01-01.wav'}
97147
98148
Example of output:
99-
[["neutral"]]
149+
[['neutral']]
100150
"""
101151

102152
template_name = 'index.html'
153+
# Removing this shows the API view to the user instead of the template.
103154
renderer_classes = [TemplateHTMLRenderer]
104155

105156
def __init__(self, **kwargs):
106157
super().__init__(**kwargs)
107158
modelname = 'Emotion_Voice_Detection_Model.h5'
108-
global graph
109-
graph = tf.get_default_graph()
159+
self.graph = tf.get_default_graph()
110160
self.loaded_model = keras.models.load_model(os.path.join(settings.MODEL_ROOT, modelname))
111161
self.predictions = []
112162

@@ -118,26 +168,28 @@ def file_elaboration(self, filepath):
118168
"""
119169
data, sampling_rate = librosa.load(filepath)
120170
try:
121-
mfccs = np.mean(librosa.feature.mfcc(y=data, sr=sampling_rate, n_mfcc=40).T, axis=0)
122-
x = np.expand_dims(mfccs, axis=2)
123-
x = np.expand_dims(x, axis=0)
124-
numpred = self.loaded_model.predict_classes(x)
171+
mfccs = np.mean(librosa.feature.mfcc(y=data, sr=sampling_rate,
172+
n_mfcc=40).T, axis=0)
173+
training_data = np.expand_dims(mfccs, axis=2)
174+
training_data_expanded = np.expand_dims(training_data, axis=0)
175+
numpred = self.loaded_model.predict_classes(training_data_expanded)
125176
self.predictions.append([self.classtoemotion(numpred)])
126177
return self.predictions
127-
except Exception as err:
178+
except ValueError as err:
128179
return Response(str(err), status=status.HTTP_400_BAD_REQUEST)
129180

130181
def post(self, request):
131182
"""
132-
This method is used to making predictions on audio files previously loaded with FileView.post
183+
This method is used to making predictions on audio files
184+
loaded with FileView.post
133185
"""
134-
with graph.as_default():
186+
with self.graph.as_default():
135187
filename = request.POST.getlist('file_name').pop()
136188
filepath = str(os.path.join(settings.MEDIA_ROOT, filename))
137189
predictions = self.file_elaboration(filepath)
138190
try:
139191
return Response({'predictions': predictions.pop()})
140-
except Exception as err:
192+
except ValueError as err:
141193
return Response(str(err), status=status.HTTP_400_BAD_REQUEST)
142194

143195
return Response(predictions, status=status.HTTP_200_OK)
@@ -150,20 +202,20 @@ def classtoemotion(pred):
150202
::output:: A string label
151203
152204
Example:
153-
>>> classtoemotion(0) == neutral
205+
classtoemotion(0) == neutral
154206
"""
155207

156-
label_conversion = {"0": "neutral",
157-
"1": "calm",
158-
"2": "happy",
159-
"3": "sad",
160-
"4": "angry",
161-
"5": "fearful",
162-
"6": "disgust",
163-
"7": "surprised"}
208+
label_conversion = {'0': 'neutral',
209+
'1': 'calm',
210+
'2': 'happy',
211+
'3': 'sad',
212+
'4': 'angry',
213+
'5': 'fearful',
214+
'6': 'disgust',
215+
'7': 'surprised'}
164216

165-
for key in label_conversion.keys():
217+
for key, value in label_conversion.items():
166218
if int(key) == pred:
167-
return label_conversion[key]
168-
else:
169-
continue
219+
label = value
220+
221+
return label

0 commit comments

Comments
 (0)