From c6ba3dd6b01cfa40f137fe61cbdbc83ff6ed45c8 Mon Sep 17 00:00:00 2001 From: Piotr Szkudlarek Date: Thu, 4 Jan 2024 21:33:13 +0100 Subject: [PATCH] Add button for exporting results, fix history --- ..._predictedimage_date_predicted_and_more.py | 46 ++++++++++ ..._rename_predicitonbatch_predictionbatch.py | 18 ++++ PlanktonDetector/DetectionApp/models.py | 12 +-- PlanktonDetector/DetectionApp/urls.py | 1 + PlanktonDetector/DetectionApp/utils.py | 6 +- PlanktonDetector/DetectionApp/views.py | 38 +++++--- PlanktonDetector/db.sqlite3 | Bin 393216 -> 462848 bytes .../static/Community/css/post_details.css | 86 +++++++++++++++++- .../DetectionApp/css/detection_detail.css | 7 -- .../static/DetectionApp/css/history.css | 2 +- .../static/DetectionApp/css/results.css | 16 +++- .../templates/detection_detail.html | 7 -- PlanktonDetector/templates/history.html | 7 +- PlanktonDetector/templates/post_details.html | 22 +++-- PlanktonDetector/templates/results.html | 5 +- PlanktonDetector/templates/upload.html | 10 ++ 16 files changed, 229 insertions(+), 54 deletions(-) create mode 100644 PlanktonDetector/DetectionApp/migrations/0012_remove_predictedimage_date_predicted_and_more.py create mode 100644 PlanktonDetector/DetectionApp/migrations/0013_rename_predicitonbatch_predictionbatch.py delete mode 100644 PlanktonDetector/static/DetectionApp/css/detection_detail.css delete mode 100644 PlanktonDetector/templates/detection_detail.html diff --git a/PlanktonDetector/DetectionApp/migrations/0012_remove_predictedimage_date_predicted_and_more.py b/PlanktonDetector/DetectionApp/migrations/0012_remove_predictedimage_date_predicted_and_more.py new file mode 100644 index 0000000..6d2c72e --- /dev/null +++ b/PlanktonDetector/DetectionApp/migrations/0012_remove_predictedimage_date_predicted_and_more.py @@ -0,0 +1,46 @@ +# Generated by Django 4.2.7 on 2024-01-04 19:37 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("DetectionApp", "0011_remove_predictedimage_class_name_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="predictedimage", + name="date_predicted", + ), + migrations.RemoveField( + model_name="uploadimage", + name="owner", + ), + migrations.CreateModel( + name="PredicitonBatch", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("date_predicted", models.DateTimeField(auto_now_add=True)), + ("images", models.ManyToManyField(to="DetectionApp.predictedimage")), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/PlanktonDetector/DetectionApp/migrations/0013_rename_predicitonbatch_predictionbatch.py b/PlanktonDetector/DetectionApp/migrations/0013_rename_predicitonbatch_predictionbatch.py new file mode 100644 index 0000000..707f011 --- /dev/null +++ b/PlanktonDetector/DetectionApp/migrations/0013_rename_predicitonbatch_predictionbatch.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-01-04 19:49 + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("DetectionApp", "0012_remove_predictedimage_date_predicted_and_more"), + ] + + operations = [ + migrations.RenameModel( + old_name="PredicitonBatch", + new_name="PredictionBatch", + ), + ] diff --git a/PlanktonDetector/DetectionApp/models.py b/PlanktonDetector/DetectionApp/models.py index a1a6b39..91444d2 100644 --- a/PlanktonDetector/DetectionApp/models.py +++ b/PlanktonDetector/DetectionApp/models.py @@ -12,18 +12,12 @@ class UploadImage(models.Model): validators=[FileExtensionValidator(allowed_extensions=["jpg", "png"])], ) predicted_image_url = models.CharField(max_length=500) - owner = models.ForeignKey(User, on_delete=models.CASCADE) class PredictedImage(models.Model): original_image = models.OneToOneField(UploadImage, on_delete=models.CASCADE) image = models.ImageField(upload_to=f"plankton/%Y/%m/%d/{original_image.name}") prediction_data = models.JSONField() - date_predicted = models.DateTimeField(auto_now_add=True) - - @property - def get_owner(self): - return self.original_image.owner @property def get_original_image(self): @@ -34,3 +28,9 @@ class PredictedImage(models.Model): for pred in results: pred["confidence"] = round(pred["confidence"] * 100, 2) return results + + +class PredictionBatch(models.Model): + images = models.ManyToManyField(PredictedImage) + date_predicted = models.DateTimeField(auto_now_add=True) + owner = models.ForeignKey(User, on_delete=models.CASCADE) diff --git a/PlanktonDetector/DetectionApp/urls.py b/PlanktonDetector/DetectionApp/urls.py index 7cc7390..eca106e 100644 --- a/PlanktonDetector/DetectionApp/urls.py +++ b/PlanktonDetector/DetectionApp/urls.py @@ -10,4 +10,5 @@ urlpatterns = [ path( "detection/", views.DetectionDetails.as_view(), name="detection-details" ), + path("export_pred/", views.download_pred_res, name="download_predicitons"), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/PlanktonDetector/DetectionApp/utils.py b/PlanktonDetector/DetectionApp/utils.py index 94ded7f..7eb3789 100644 --- a/PlanktonDetector/DetectionApp/utils.py +++ b/PlanktonDetector/DetectionApp/utils.py @@ -16,6 +16,6 @@ def predict_image(image): name=f"{image.image.name}_predicted", imgsz=[640, 640], ) - for r in results: - x = r.tojson() - return x + for result in results: + metrics = result.tojson() + return metrics diff --git a/PlanktonDetector/DetectionApp/views.py b/PlanktonDetector/DetectionApp/views.py index 76f0fb8..24f6d73 100644 --- a/PlanktonDetector/DetectionApp/views.py +++ b/PlanktonDetector/DetectionApp/views.py @@ -1,15 +1,15 @@ from typing import Any from django.db.models.query import QuerySet -from django.shortcuts import render, redirect +from django.http import HttpResponse +from django.shortcuts import render from django.views import View from .forms import DetectForm from django.views.generic import ListView, DetailView from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator -from .models import UploadImage, PredictedImage +from .models import PredictionBatch, UploadImage, PredictedImage from .utils import predict_image from django.contrib.auth.mixins import LoginRequiredMixin -import json class DetectView(View): @@ -26,11 +26,11 @@ class DetectView(View): files = request.FILES.getlist("image") predictions = [] if form.is_valid(): + pred_batch = PredictionBatch.objects.create(owner=request.user) for f in files: image = UploadImage( image=f, ) - image.owner = request.user image.save() prediciton_results = predict_image(image) image.predicted_image_url = ( @@ -43,7 +43,7 @@ class DetectView(View): predicted_image = PredictedImage.objects.create( original_image=image, image=image.predicted_image_url, - prediction_data={"data": "no results"}, + prediction_data={"data": "no predicitions"}, ) else: predicted_image = PredictedImage.objects.create( @@ -52,12 +52,14 @@ class DetectView(View): prediction_data=results_metrics, ) predictions.append(predicted_image) + pred_batch.images.add(*predictions) + pred_batch.save() return render( request, "results.html", { "img_saved": True, - "img_url": predictions, + "img": pred_batch, }, ) else: @@ -65,18 +67,28 @@ class DetectView(View): class ListHistory(LoginRequiredMixin, ListView): - model = PredictedImage - queryset = PredictedImage.objects.all() + model = PredictionBatch + queryset = PredictionBatch.objects.all() template_name = "history.html" paginate_by = 3 def get_queryset(self) -> QuerySet[Any]: - queryset = PredictedImage.objects.filter( - original_image__owner=self.request.user - ).order_by("-date_predicted") + queryset = PredictionBatch.objects.filter(owner=self.request.user).order_by( + "-date_predicted" + ) return queryset class DetectionDetails(LoginRequiredMixin, DetailView): - model = PredictedImage - template_name = "detection_detail.html" + model = PredictionBatch + template_name = "results.html" + context_object_name = "img" + + +def download_pred_res(request, pk): + pred_batch = PredictionBatch.objects.get(pk=pk) + response = HttpResponse(content_type="text/plain") + response["Content-Disposition"] = "attachment; filename=predictions.txt" + for img in pred_batch.images.all(): + response.write(img.prediction_data) + return response diff --git a/PlanktonDetector/db.sqlite3 b/PlanktonDetector/db.sqlite3 index 8fa877f4a83c1f5b6c70516b3d24caebff0133d0..27789999260170529776b753b3836717390a9bca 100644 GIT binary patch delta 37686 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 diff --git a/PlanktonDetector/static/Community/css/post_details.css b/PlanktonDetector/static/Community/css/post_details.css index a7ed936..f2d3589 100644 --- a/PlanktonDetector/static/Community/css/post_details.css +++ b/PlanktonDetector/static/Community/css/post_details.css @@ -1,3 +1,85 @@ -.class{ +body, html{ + height: unset; +} + +.page-container{ + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + align-items: center; + overflow: hidden; +} + +.post{ display:flex; -} \ No newline at end of file + margin-top:10px; + flex-direction: column; + justify-content:center; + align-items: flex-start; + border: 3px solid lightgray; + width: 80%; + padding: 15px; + border-radius: 5%; +} + +.title-content{ + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; +} + +#title{ + font-size: 40px; + color: black; + font-weight: bold; +} + +#content{ + font-size: 20px; + color:black; +} + +.author{ + display: flex; + justify-content: flex-end; + align-items: center; + width: 100%; +} + +#author{ + font-size: 15px; +} + +.comments{ + display: flex; + flex-direction:column; + justify-content: space-between; + align-items: center; + width: 80%; + margin-bottom: 25px; +} + +#comment-label{ + font-size: 30px; + align-self: flex-start; + margin-left: 10%; + color:black; + font-weight: bold; +} + +.single-comment{ + display: flex; + flex-direction: column; + width: 90%; + border-bottom: 3px solid lightgray; + margin-top:10px; +} + +#comment-author{ + display: flex; + width: 100%; + justify-content: flex-end; +} + diff --git a/PlanktonDetector/static/DetectionApp/css/detection_detail.css b/PlanktonDetector/static/DetectionApp/css/detection_detail.css deleted file mode 100644 index 440f7ed..0000000 --- a/PlanktonDetector/static/DetectionApp/css/detection_detail.css +++ /dev/null @@ -1,7 +0,0 @@ -.history_list{ - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - height: 100%; -} diff --git a/PlanktonDetector/static/DetectionApp/css/history.css b/PlanktonDetector/static/DetectionApp/css/history.css index 3af0310..5fd10a0 100644 --- a/PlanktonDetector/static/DetectionApp/css/history.css +++ b/PlanktonDetector/static/DetectionApp/css/history.css @@ -24,7 +24,7 @@ ul{ display:flex; width: 100%; flex-direction: row; - align-items: flex-start; + align-items: center; justify-content: space-evenly; margin-bottom: 25px; } diff --git a/PlanktonDetector/static/DetectionApp/css/results.css b/PlanktonDetector/static/DetectionApp/css/results.css index bf6a92f..0c466e9 100644 --- a/PlanktonDetector/static/DetectionApp/css/results.css +++ b/PlanktonDetector/static/DetectionApp/css/results.css @@ -19,7 +19,7 @@ form{ border: 2px grey solid; padding: 0px 0px 40px 0px; margin-bottom: 5px; - overflow-y: scroll; + overflow-y: auto; } @@ -43,10 +43,9 @@ form{ .upload_field { margin: auto; display: flex; - flex-direction: row; + flex-direction: column; justify-content: right; width:645px; - height: 465px; } @@ -84,4 +83,15 @@ form{ font-size: large; text-decoration:solid; color: black; +} + +#download_json{ + background-color: chartreuse; + margin: 30px auto auto auto; + height: 50px; + width: 60%; + border-radius: 25px; + border: 2px white; + box-shadow: 0 0 0 4px white; + font-size: 25px; } \ No newline at end of file diff --git a/PlanktonDetector/templates/detection_detail.html b/PlanktonDetector/templates/detection_detail.html deleted file mode 100644 index f32f824..0000000 --- a/PlanktonDetector/templates/detection_detail.html +++ /dev/null @@ -1,7 +0,0 @@ -{%extends 'base.html'%} -{%load static%} -{%block extracss%} - -{%endblock extracss%} -{%block content%} -{%endblock content%} \ No newline at end of file diff --git a/PlanktonDetector/templates/history.html b/PlanktonDetector/templates/history.html index 91e312e..5f610ae 100644 --- a/PlanktonDetector/templates/history.html +++ b/PlanktonDetector/templates/history.html @@ -6,14 +6,15 @@ {%block content%}

History

- {% for detection in page_obj %} +{% for detection in page_obj %}
- upload_image + upload_image

{{detection.date_predicted.date}}

-

{{detection.get_owner}}

+

{{detection.owner}}

+

Images uploaded: {{detection.images.all.count}}

diff --git a/PlanktonDetector/templates/post_details.html b/PlanktonDetector/templates/post_details.html index c0fb22b..8d4451a 100644 --- a/PlanktonDetector/templates/post_details.html +++ b/PlanktonDetector/templates/post_details.html @@ -1,19 +1,27 @@ {%extends 'base.html'%} {%load static%} {%block extracss%} - + {%endblock extracss%} {%block content%} +
-

{{object.title}}

-

{{object.content}}

-

{{object.author}}

+
+

{{object.title}}

+

{{object.content}}

+
+

{{object.author}}

+
+
+

Comments:

{%for comments in object.comment_set.all%} -

{{comments.author}}

-

{{comments.content}}

-

{{comments.post}}

+
+

{{comments.content}}

+

{{comments.author}}

+
{%endfor%}
+
{%endblock content%} \ No newline at end of file diff --git a/PlanktonDetector/templates/results.html b/PlanktonDetector/templates/results.html index 8f5a0fc..658bc77 100644 --- a/PlanktonDetector/templates/results.html +++ b/PlanktonDetector/templates/results.html @@ -12,7 +12,7 @@ {% else %}

Results:

- {%for img in img_url%} + {%for img in img.images.all %}

Image {{forloop.counter}}

{%for detection in img.get_prediction_data%}
@@ -25,10 +25,11 @@
+
{%endblock content%} \ No newline at end of file