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 8fa877f..2778999 100644 Binary files a/PlanktonDetector/db.sqlite3 and b/PlanktonDetector/db.sqlite3 differ 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