From e146f16f56e822d863c3bef40c7c29e05b109897 Mon Sep 17 00:00:00 2001 From: Piotr Szkudlarek Date: Wed, 13 Dec 2023 02:11:29 +0100 Subject: [PATCH] Add history, community, modify login --- PlanktonDetector/Community/__init__.py | 0 PlanktonDetector/Community/admin.py | 6 ++ PlanktonDetector/Community/apps.py | 6 ++ .../Community/migrations/0001_initial.py | 67 +++++++++++++ .../migrations/0002_post_date_pub.py | 21 ++++ .../Community/migrations/__init__.py | 0 PlanktonDetector/Community/models.py | 17 ++++ PlanktonDetector/Community/tests.py | 3 + PlanktonDetector/Community/urls.py | 9 ++ PlanktonDetector/Community/views.py | 14 +++ PlanktonDetector/DetectionApp/admin.py | 2 + PlanktonDetector/DetectionApp/forms.py | 12 ++- .../migrations/0003_detectimage_owner.py | 25 +++++ .../0004_detectimage_predicted_image_url.py | 18 ++++ .../0005_detectimage_date_predicted.py | 21 ++++ .../migrations/0006_predictedimage.py | 37 +++++++ ...ove_detectimage_date_predicted_and_more.py | 25 +++++ .../migrations/0008_predictedimage_owner.py | 25 +++++ .../0009_remove_predictedimage_owner.py | 16 +++ .../0010_rename_detectimage_uploadimage.py | 18 ++++ PlanktonDetector/DetectionApp/models.py | 21 +++- PlanktonDetector/DetectionApp/urls.py | 7 +- PlanktonDetector/DetectionApp/utils.py | 20 ++++ PlanktonDetector/DetectionApp/views.py | 92 +++++++----------- PlanktonDetector/PlanktonDetector/settings.py | 1 + PlanktonDetector/PlanktonDetector/urls.py | 8 +- PlanktonDetector/UserManagement/__init__.py | 0 PlanktonDetector/UserManagement/admin.py | 3 + PlanktonDetector/UserManagement/apps.py | 6 ++ .../UserManagement/migrations/__init__.py | 0 PlanktonDetector/UserManagement/models.py | 3 + PlanktonDetector/UserManagement/tests.py | 3 + PlanktonDetector/UserManagement/urls.py | 8 ++ PlanktonDetector/UserManagement/views.py | 46 +++++++++ PlanktonDetector/db.sqlite3 | Bin 143360 -> 192512 bytes .../static/Community/css/list_posts.css | 38 ++++++++ .../static/DetectionApp/css/base.css | 4 + .../DetectionApp/css/detection_detail.css | 7 ++ .../static/DetectionApp/css/history.css | 38 ++++++++ .../static/DetectionApp/css/upload.css | 1 + PlanktonDetector/templates/base.html | 3 +- .../templates/detection_detail.html | 7 ++ PlanktonDetector/templates/history.html | 40 ++++++++ PlanktonDetector/templates/list_posts.html | 21 ++++ PlanktonDetector/templates/upload.html | 11 ++- 45 files changed, 659 insertions(+), 71 deletions(-) create mode 100644 PlanktonDetector/Community/__init__.py create mode 100644 PlanktonDetector/Community/admin.py create mode 100644 PlanktonDetector/Community/apps.py create mode 100644 PlanktonDetector/Community/migrations/0001_initial.py create mode 100644 PlanktonDetector/Community/migrations/0002_post_date_pub.py create mode 100644 PlanktonDetector/Community/migrations/__init__.py create mode 100644 PlanktonDetector/Community/models.py create mode 100644 PlanktonDetector/Community/tests.py create mode 100644 PlanktonDetector/Community/urls.py create mode 100644 PlanktonDetector/Community/views.py create mode 100644 PlanktonDetector/DetectionApp/migrations/0003_detectimage_owner.py create mode 100644 PlanktonDetector/DetectionApp/migrations/0004_detectimage_predicted_image_url.py create mode 100644 PlanktonDetector/DetectionApp/migrations/0005_detectimage_date_predicted.py create mode 100644 PlanktonDetector/DetectionApp/migrations/0006_predictedimage.py create mode 100644 PlanktonDetector/DetectionApp/migrations/0007_remove_detectimage_date_predicted_and_more.py create mode 100644 PlanktonDetector/DetectionApp/migrations/0008_predictedimage_owner.py create mode 100644 PlanktonDetector/DetectionApp/migrations/0009_remove_predictedimage_owner.py create mode 100644 PlanktonDetector/DetectionApp/migrations/0010_rename_detectimage_uploadimage.py create mode 100644 PlanktonDetector/DetectionApp/utils.py create mode 100644 PlanktonDetector/UserManagement/__init__.py create mode 100644 PlanktonDetector/UserManagement/admin.py create mode 100644 PlanktonDetector/UserManagement/apps.py create mode 100644 PlanktonDetector/UserManagement/migrations/__init__.py create mode 100644 PlanktonDetector/UserManagement/models.py create mode 100644 PlanktonDetector/UserManagement/tests.py create mode 100644 PlanktonDetector/UserManagement/urls.py create mode 100644 PlanktonDetector/UserManagement/views.py create mode 100644 PlanktonDetector/static/Community/css/list_posts.css create mode 100644 PlanktonDetector/static/DetectionApp/css/detection_detail.css create mode 100644 PlanktonDetector/static/DetectionApp/css/history.css create mode 100644 PlanktonDetector/templates/detection_detail.html create mode 100644 PlanktonDetector/templates/history.html create mode 100644 PlanktonDetector/templates/list_posts.html diff --git a/PlanktonDetector/Community/__init__.py b/PlanktonDetector/Community/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PlanktonDetector/Community/admin.py b/PlanktonDetector/Community/admin.py new file mode 100644 index 0000000..2d5acdf --- /dev/null +++ b/PlanktonDetector/Community/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin +from . import models + +# Register your models here. +admin.site.register(models.Comment) +admin.site.register(models.Post) diff --git a/PlanktonDetector/Community/apps.py b/PlanktonDetector/Community/apps.py new file mode 100644 index 0000000..52470e6 --- /dev/null +++ b/PlanktonDetector/Community/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CommunityConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "Community" diff --git a/PlanktonDetector/Community/migrations/0001_initial.py b/PlanktonDetector/Community/migrations/0001_initial.py new file mode 100644 index 0000000..13bdbfe --- /dev/null +++ b/PlanktonDetector/Community/migrations/0001_initial.py @@ -0,0 +1,67 @@ +# Generated by Django 4.2.7 on 2023-12-11 15:42 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Post", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=500)), + ("content", models.TextField()), + ( + "author", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.CreateModel( + name="Comment", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("content", models.TextField()), + ( + "author", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "post", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="Community.post" + ), + ), + ], + ), + ] diff --git a/PlanktonDetector/Community/migrations/0002_post_date_pub.py b/PlanktonDetector/Community/migrations/0002_post_date_pub.py new file mode 100644 index 0000000..7abfcf9 --- /dev/null +++ b/PlanktonDetector/Community/migrations/0002_post_date_pub.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.7 on 2023-12-11 16:58 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + dependencies = [ + ("Community", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="post", + name="date_pub", + field=models.DateField( + auto_now_add=True, default=django.utils.timezone.now + ), + preserve_default=False, + ), + ] diff --git a/PlanktonDetector/Community/migrations/__init__.py b/PlanktonDetector/Community/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PlanktonDetector/Community/models.py b/PlanktonDetector/Community/models.py new file mode 100644 index 0000000..aed7013 --- /dev/null +++ b/PlanktonDetector/Community/models.py @@ -0,0 +1,17 @@ +from django.db import models +from django.contrib.auth.models import User + +# Create your models here. + + +class Post(models.Model): + title = models.CharField(max_length=500) + content = models.TextField() + author = models.ForeignKey(User, on_delete=models.CASCADE, null=False) + date_pub = models.DateField(auto_now_add=True) + + +class Comment(models.Model): + author = models.ForeignKey(User, on_delete=models.CASCADE, null=False) + content = models.TextField() + post = models.ForeignKey(Post, on_delete=models.CASCADE) diff --git a/PlanktonDetector/Community/tests.py b/PlanktonDetector/Community/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/PlanktonDetector/Community/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/PlanktonDetector/Community/urls.py b/PlanktonDetector/Community/urls.py new file mode 100644 index 0000000..c31225f --- /dev/null +++ b/PlanktonDetector/Community/urls.py @@ -0,0 +1,9 @@ +from django.urls import path +from django.conf import settings +from django.conf.urls.static import static +from . import views + +urlpatterns = [ + path("posts/", views.ListPosts.as_view(), name="posts"), + path("posts/", views.PostDetails.as_view(), name="post-details"), +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/PlanktonDetector/Community/views.py b/PlanktonDetector/Community/views.py new file mode 100644 index 0000000..50fb78a --- /dev/null +++ b/PlanktonDetector/Community/views.py @@ -0,0 +1,14 @@ +from django.shortcuts import render +from django.views.generic import ListView, DetailView +from .models import Post, Comment + +# Create your views here. + + +class ListPosts(ListView): + model = Post + template_name = "list_posts.html" + + +class PostDetails(DetailView): + model = Post diff --git a/PlanktonDetector/DetectionApp/admin.py b/PlanktonDetector/DetectionApp/admin.py index 8c38f3f..b5e0d05 100644 --- a/PlanktonDetector/DetectionApp/admin.py +++ b/PlanktonDetector/DetectionApp/admin.py @@ -1,3 +1,5 @@ from django.contrib import admin +from . import models # Register your models here. +admin.site.register(models.UploadImage) diff --git a/PlanktonDetector/DetectionApp/forms.py b/PlanktonDetector/DetectionApp/forms.py index 0729d02..c2c108b 100644 --- a/PlanktonDetector/DetectionApp/forms.py +++ b/PlanktonDetector/DetectionApp/forms.py @@ -1,9 +1,11 @@ from django import forms -from .models import DetectImage +from .models import UploadImage + class DetectForm(forms.ModelForm): - class Meta: - model = DetectImage - fields = "__all__" - labels={"image":""} \ No newline at end of file + model = UploadImage + fields = [ + "image", + ] + labels = {"image": ""} diff --git a/PlanktonDetector/DetectionApp/migrations/0003_detectimage_owner.py b/PlanktonDetector/DetectionApp/migrations/0003_detectimage_owner.py new file mode 100644 index 0000000..69b6a90 --- /dev/null +++ b/PlanktonDetector/DetectionApp/migrations/0003_detectimage_owner.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.7 on 2023-12-12 16:31 + +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", "0002_alter_detectimage_image"), + ] + + operations = [ + migrations.AddField( + model_name="detectimage", + name="owner", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + preserve_default=False, + ), + ] diff --git a/PlanktonDetector/DetectionApp/migrations/0004_detectimage_predicted_image_url.py b/PlanktonDetector/DetectionApp/migrations/0004_detectimage_predicted_image_url.py new file mode 100644 index 0000000..4a2d9b2 --- /dev/null +++ b/PlanktonDetector/DetectionApp/migrations/0004_detectimage_predicted_image_url.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2023-12-12 21:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("DetectionApp", "0003_detectimage_owner"), + ] + + operations = [ + migrations.AddField( + model_name="detectimage", + name="predicted_image_url", + field=models.CharField(default="url", max_length=500), + preserve_default=False, + ), + ] diff --git a/PlanktonDetector/DetectionApp/migrations/0005_detectimage_date_predicted.py b/PlanktonDetector/DetectionApp/migrations/0005_detectimage_date_predicted.py new file mode 100644 index 0000000..f085876 --- /dev/null +++ b/PlanktonDetector/DetectionApp/migrations/0005_detectimage_date_predicted.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.7 on 2023-12-12 21:21 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + dependencies = [ + ("DetectionApp", "0004_detectimage_predicted_image_url"), + ] + + operations = [ + migrations.AddField( + model_name="detectimage", + name="date_predicted", + field=models.DateTimeField( + auto_now_add=True, default=django.utils.timezone.now + ), + preserve_default=False, + ), + ] diff --git a/PlanktonDetector/DetectionApp/migrations/0006_predictedimage.py b/PlanktonDetector/DetectionApp/migrations/0006_predictedimage.py new file mode 100644 index 0000000..b81225d --- /dev/null +++ b/PlanktonDetector/DetectionApp/migrations/0006_predictedimage.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.7 on 2023-12-12 22:46 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("DetectionApp", "0005_detectimage_date_predicted"), + ] + + operations = [ + migrations.CreateModel( + name="PredictedImage", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("image", models.ImageField(upload_to="plankton/%Y/%m/%d/None")), + ("confidence", models.FloatField()), + ("class_name", models.CharField(max_length=100)), + ( + "original_image", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to="DetectionApp.detectimage", + ), + ), + ], + ), + ] diff --git a/PlanktonDetector/DetectionApp/migrations/0007_remove_detectimage_date_predicted_and_more.py b/PlanktonDetector/DetectionApp/migrations/0007_remove_detectimage_date_predicted_and_more.py new file mode 100644 index 0000000..5dd279d --- /dev/null +++ b/PlanktonDetector/DetectionApp/migrations/0007_remove_detectimage_date_predicted_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.7 on 2023-12-13 00:25 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + dependencies = [ + ("DetectionApp", "0006_predictedimage"), + ] + + operations = [ + migrations.RemoveField( + model_name="detectimage", + name="date_predicted", + ), + migrations.AddField( + model_name="predictedimage", + name="date_predicted", + field=models.DateTimeField( + auto_now_add=True, default=django.utils.timezone.now + ), + preserve_default=False, + ), + ] diff --git a/PlanktonDetector/DetectionApp/migrations/0008_predictedimage_owner.py b/PlanktonDetector/DetectionApp/migrations/0008_predictedimage_owner.py new file mode 100644 index 0000000..1b76f24 --- /dev/null +++ b/PlanktonDetector/DetectionApp/migrations/0008_predictedimage_owner.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.7 on 2023-12-13 00:29 + +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", "0007_remove_detectimage_date_predicted_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="predictedimage", + name="owner", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + preserve_default=False, + ), + ] diff --git a/PlanktonDetector/DetectionApp/migrations/0009_remove_predictedimage_owner.py b/PlanktonDetector/DetectionApp/migrations/0009_remove_predictedimage_owner.py new file mode 100644 index 0000000..6f199d4 --- /dev/null +++ b/PlanktonDetector/DetectionApp/migrations/0009_remove_predictedimage_owner.py @@ -0,0 +1,16 @@ +# Generated by Django 4.2.7 on 2023-12-13 00:31 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("DetectionApp", "0008_predictedimage_owner"), + ] + + operations = [ + migrations.RemoveField( + model_name="predictedimage", + name="owner", + ), + ] diff --git a/PlanktonDetector/DetectionApp/migrations/0010_rename_detectimage_uploadimage.py b/PlanktonDetector/DetectionApp/migrations/0010_rename_detectimage_uploadimage.py new file mode 100644 index 0000000..22dca81 --- /dev/null +++ b/PlanktonDetector/DetectionApp/migrations/0010_rename_detectimage_uploadimage.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2023-12-13 01:08 + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("DetectionApp", "0009_remove_predictedimage_owner"), + ] + + operations = [ + migrations.RenameModel( + old_name="DetectImage", + new_name="UploadImage", + ), + ] diff --git a/PlanktonDetector/DetectionApp/models.py b/PlanktonDetector/DetectionApp/models.py index 371f9f5..4518c20 100644 --- a/PlanktonDetector/DetectionApp/models.py +++ b/PlanktonDetector/DetectionApp/models.py @@ -1,11 +1,30 @@ from django.db import models from django.core.validators import FileExtensionValidator +from django.contrib.auth.models import User # Create your models here. -class DetectImage(models.Model): +class UploadImage(models.Model): image = models.ImageField( upload_to="plankton/%Y/%m/%d/", 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}") + confidence = models.FloatField() + class_name = models.CharField(max_length=100) + date_predicted = models.DateTimeField(auto_now_add=True) + + @property + def get_owner(self): + return self.original_image.owner + + @property + def get_original_image(self): + return self.original_image.image diff --git a/PlanktonDetector/DetectionApp/urls.py b/PlanktonDetector/DetectionApp/urls.py index 160f192..7cc7390 100644 --- a/PlanktonDetector/DetectionApp/urls.py +++ b/PlanktonDetector/DetectionApp/urls.py @@ -6,7 +6,8 @@ from django.contrib.auth import urls urlpatterns = [ path("detect/", views.DetectView.as_view(), name="detect"), - path("login/", views.LoginView.as_view(), name="login"), - path("logout/", views.logout_view, name="logout"), - path("register/", views.SignupView.as_view(), name="signup"), + path("history/", views.ListHistory.as_view(), name="history"), + path( + "detection/", views.DetectionDetails.as_view(), name="detection-details" + ), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/PlanktonDetector/DetectionApp/utils.py b/PlanktonDetector/DetectionApp/utils.py new file mode 100644 index 0000000..be0decc --- /dev/null +++ b/PlanktonDetector/DetectionApp/utils.py @@ -0,0 +1,20 @@ +import json +from django.core.files import File +from PIL import Image +from io import BytesIO +from django.conf import settings +from ultralytics import YOLO + +MODEL = YOLO("best.pt") + + +def predict_image(image): + results = MODEL.predict( + image.image.path, + save=True, + project=settings.MEDIA_ROOT, + name=f"{image.image.name}_predicted", + imgsz=[640, 640], + ) + x = json.loads(results[0].tojson()) + return x diff --git a/PlanktonDetector/DetectionApp/views.py b/PlanktonDetector/DetectionApp/views.py index eb2bcf5..c8cd5ce 100644 --- a/PlanktonDetector/DetectionApp/views.py +++ b/PlanktonDetector/DetectionApp/views.py @@ -1,13 +1,14 @@ +from typing import Any +from django.db.models.query import QuerySet from django.shortcuts import render, redirect from django.views import View from .forms import DetectForm -from django.conf import settings -from ultralytics import YOLO -from django.contrib.auth.forms import AuthenticationForm, UserCreationForm -from django.contrib.auth import authenticate, login, logout - -# Create your views here. -MODEL = YOLO("best.pt") +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 .utils import predict_image +from django.contrib.auth.mixins import LoginRequiredMixin class DetectView(View): @@ -18,68 +19,49 @@ class DetectView(View): form = self.form_class() return render(request, "upload.html", {"form": form}) + @method_decorator(login_required) def post(self, request, *args, **kwargs): form = self.form_class(request.POST, request.FILES) if form.is_valid(): - image = form.save() - MODEL.predict( - image.image.path, - save=True, - project=settings.MEDIA_ROOT, - name=f"{image.image.name}_predicted", - imgsz=[640, 640], + image = form.save(commit=False) + image.owner = request.user + image.save() + prediciton_results = predict_image(image) + image.predicted_image_url = ( + f"{image.image.name}_predicted/{image.image.name.split('/')[-1]}" + ) + image.save() + PredictedImage.objects.create( + original_image=image, + image=image.predicted_image_url, + confidence=prediciton_results[0]["confidence"], + class_name=prediciton_results[0]["name"], ) - saved = form.is_valid() return render( request, "upload.html", { - "img_saved": saved, - "img_path": f"{image.image.name}_predicted/{image.image.name.split('/')[-1]}", + "img_saved": True, + "img_url": image.predicted_image_url, }, ) else: return render(request, "upload.html", {"form": form}) -class LoginView(View): - form_class = AuthenticationForm - template_name = "login_signup.html" +class ListHistory(LoginRequiredMixin, ListView): + model = PredictedImage + queryset = PredictedImage.objects.all() + template_name = "history.html" + paginate_by = 4 - def get(self, request, *args, **kwargs): - form = self.form_class() - return render(request, self.template_name, {"form": form}) - - def post(self, request, *args, **kwargs): - form = self.form_class(data=request.POST) - if form.is_valid(): - user = authenticate( - username=form.cleaned_data["username"], - password=form.cleaned_data["password"], - ) - if user is not None: - login(request, user) - return redirect("detect") - return render(request, self.template_name, {"form": form}) + def get_queryset(self) -> QuerySet[Any]: + queryset = PredictedImage.objects.filter( + original_image__owner=self.request.user + ).order_by("-date_predicted") + return queryset -class SignupView(View): - form_class = UserCreationForm - template_name = "login_signup.html" - - def get(self, request, *args, **kwargs): - form = self.form_class() - return render(request, self.template_name, {"form": form, "signup": True}) - - def post(self, request, *args, **kwargs): - form = self.form_class(data=request.POST) - if form.is_valid(): - user = form.save() - login(request, user) - return redirect("detect") - return render(request, self.template_name, {"form": form, "signup": True}) - - -def logout_view(request): - logout(request) - return redirect("detect") +class DetectionDetails(LoginRequiredMixin, DetailView): + model = PredictedImage + template_name = "detection_detail.html" diff --git a/PlanktonDetector/PlanktonDetector/settings.py b/PlanktonDetector/PlanktonDetector/settings.py index 66a1b06..37a3d56 100644 --- a/PlanktonDetector/PlanktonDetector/settings.py +++ b/PlanktonDetector/PlanktonDetector/settings.py @@ -39,6 +39,7 @@ INSTALLED_APPS = [ "django.contrib.messages", "django.contrib.staticfiles", "DetectionApp", + "Community", ] MIDDLEWARE = [ diff --git a/PlanktonDetector/PlanktonDetector/urls.py b/PlanktonDetector/PlanktonDetector/urls.py index c1e0330..33e39cb 100644 --- a/PlanktonDetector/PlanktonDetector/urls.py +++ b/PlanktonDetector/PlanktonDetector/urls.py @@ -16,8 +16,12 @@ Including another URLconf """ from django.contrib import admin from django.urls import path, include +from django.conf import settings +from django.conf.urls.static import static urlpatterns = [ path("admin/", admin.site.urls), - path("", include("DetectionApp.urls")) -] + path("", include("DetectionApp.urls")), + path("accounts/", include("UserManagement.urls")), + path("community/", include("Community.urls")), +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/PlanktonDetector/UserManagement/__init__.py b/PlanktonDetector/UserManagement/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PlanktonDetector/UserManagement/admin.py b/PlanktonDetector/UserManagement/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/PlanktonDetector/UserManagement/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/PlanktonDetector/UserManagement/apps.py b/PlanktonDetector/UserManagement/apps.py new file mode 100644 index 0000000..d7c949f --- /dev/null +++ b/PlanktonDetector/UserManagement/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UsermanagementConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "UserManagement" diff --git a/PlanktonDetector/UserManagement/migrations/__init__.py b/PlanktonDetector/UserManagement/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PlanktonDetector/UserManagement/models.py b/PlanktonDetector/UserManagement/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/PlanktonDetector/UserManagement/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/PlanktonDetector/UserManagement/tests.py b/PlanktonDetector/UserManagement/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/PlanktonDetector/UserManagement/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/PlanktonDetector/UserManagement/urls.py b/PlanktonDetector/UserManagement/urls.py new file mode 100644 index 0000000..b1a3ee4 --- /dev/null +++ b/PlanktonDetector/UserManagement/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path("login/", views.LoginView.as_view(), name="login"), + path("logout/", views.logout_view, name="logout"), + path("register/", views.SignupView.as_view(), name="signup"), +] diff --git a/PlanktonDetector/UserManagement/views.py b/PlanktonDetector/UserManagement/views.py new file mode 100644 index 0000000..94df782 --- /dev/null +++ b/PlanktonDetector/UserManagement/views.py @@ -0,0 +1,46 @@ +from django.shortcuts import render, redirect +from django.contrib.auth.forms import AuthenticationForm, UserCreationForm +from django.contrib.auth import authenticate, login, logout +from django.views import View + +class LoginView(View): + form_class = AuthenticationForm + template_name = "login_signup.html" + + def get(self, request, *args, **kwargs): + form = self.form_class() + return render(request, self.template_name, {"form": form}) + + def post(self, request, *args, **kwargs): + form = self.form_class(data=request.POST) + if form.is_valid(): + user = authenticate( + username=form.cleaned_data["username"], + password=form.cleaned_data["password"], + ) + if user is not None: + login(request, user) + return redirect("detect") + return render(request, self.template_name, {"form": form}) + + +class SignupView(View): + form_class = UserCreationForm + template_name = "login_signup.html" + + def get(self, request, *args, **kwargs): + form = self.form_class() + return render(request, self.template_name, {"form": form, "signup": True}) + + def post(self, request, *args, **kwargs): + form = self.form_class(data=request.POST) + if form.is_valid(): + user = form.save() + login(request, user) + return redirect("detect") + return render(request, self.template_name, {"form": form, "signup": True}) + + +def logout_view(request): + logout(request) + return redirect("detect") diff --git a/PlanktonDetector/db.sqlite3 b/PlanktonDetector/db.sqlite3 index 6eb5dc89679c3b195a10e33419106bb32ea85911..8352d6e6a878afa119e94aca5e05de48b592a742 100644 GIT binary patch literal 192512 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 delta 6240 zcmbVQeQXN;Y-PMj=$4UIr z*lyxX=<10IsleJa6eGkoG*Hn5(-bU-phc?KBpA}hsF=ps&@%oROadwa+8@ul0(zR%E-5t)^d4WZ&Aauc`koA|oac;_t*Ci95t);_Jjuh>6v7 z7h!KwqNjj8kO)jALeYpd7GwRnFJW&maA6OI9tYlJVw#Nw=EI?QoX0fbwP=eRJ`B!B zlQ9HY`JR?B$o5Fk4!M8E8wo~PuRk1$uyfJiUVv)B#=~$ZIPVp##OoEjk1`^HWYuH? zv3$eT(1aan+vY~jiu-)WTUu`gEPF zVYUOL;~C%t2iJaO2PaSoSYJ(l0M_2=2W|lVu$+E#?NbL()N^PrQjeTOh#7SZ|C#D< zijNhYwJ*#6BCnHv!W#-pvgjmwPjUrZlh}}d^T4f7+Ca-1PMrejuOz&_xxh|Cywmd* z+i17d-O}CcwstwYn_G9nWp?!fL+0R*B~6)c2ElhSzG?6a=}A)B;5wFR34whX+cY?o zX$%2fhMWc8szaYc>b9H|{>wa^0S1i!hh9!=)`mjh6|naDEa=46Tz+6dGjG_yo0(G% zaA&Qj4_pU8kp=6Yxo+OT>SV%b{eyUq_#JVVc#*h9gjdrZVqc>SLyoi+I_!rfow@pZ z@XSxwv1Sw_uCB=*0G1HU9$L0GX0Cn)o>3Rfr#9l@+U|Rj2L#vJFHER@8fD|i{i0E7Gwxx1Wh2P*ewHW@*a6kPMp|j*Po!$2GA?km*B_WR(IulQPjJ zgBcQgQG~0OxoH|Qd#X2?BN`|pBz6}Oxm>i2ShTdBrE~_PfrLzZ5wi?&dz7|vL{2Qh zdP*mlv~-CNji|G^F!i*KO`0i_F=d4c)=COo$><6ab4i=K%i)C*mP!($3d7zhYIf8I z6;78?;KWNzaKcCX4Do@uAi;dOTn`s*G*HG*?vK6r(d3NY?FijE|b^(01oz z+0F?-;z?1Waa*Zz#WM>sPgKF&M$A`?Sv=hBHn*4*!@+Rp7n$xsS2wh6dMw)o;WE_| zg_)(Nje4Vo6GqoqJ#(|n1z)@Qg+EP{E4bWnN?Wzy(L)-)RL zwg(*?)X{%_onY4j6&`X6GMzepJ$p=VxSTrzT}PuxSd2a#h{omu9oC3H% zXS%GTAxTpmiHoymNP~aGWrnPd>R55slkzQ*VJ9R#SuH6x+Z}h&K1e#ABXLFBo=7T^ z4wJoSAs1D9wWgN(gQ3XTMZwhZV>=9ykaVtaU2^4Y*vC> z?_^>VXP{3}NA4pxE z5S+FgDpcS$(^*Nv$&95u;|?fsuu!6Eg-B0tidh(h3I{e6it4|T6O_y5nDrQ-MAPST zB4m!ottq&xTN*Kh6FGZUaw6^<*HKYe0haxr%ZWjf_VvVs?VSrUPvmQ%QYjP+C``b< zs%;k(5j$n4ZQf8!P+%HRPUPq;7Jq?)z-<2likvH%kvuUZT)bHw8#G!;DmV>m=oqj@HaX{+bjM%?wC}2b9UGf4_?bSaOO#-R{f|#uUU1hI+=*ukZx zu5R5D#MW+Lx3B*NP7Im5jGlxIg5?#2-9f8)+T?*)*$&uzVNRr?V?)Ea!j$sorLtA5 zbH;|uW_ZgyT7x2EViiBi_n!E!(Hvi#f&;A>ia5k@IS1fywW6CCuHeqQ6+IM^Y=X99 zHX;!)Z9r^edIgb#(A)X_gghKsW^>y{2x*q=z{u=(o5AmQ3;w#i#jVf&gSZrTYiOiS$o& zB!7A_&^ot<8 diff --git a/PlanktonDetector/static/Community/css/list_posts.css b/PlanktonDetector/static/Community/css/list_posts.css new file mode 100644 index 0000000..17adc82 --- /dev/null +++ b/PlanktonDetector/static/Community/css/list_posts.css @@ -0,0 +1,38 @@ +.post_list{ + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 100%; +} + +h2{ + margin-bottom:25px; +} + +ul{ + list-style-type: none; +} + +.post_list a{ + text-decoration: none; + width: 100%; + color: black; +} + +.post_details{ + display:flex; + width: 50%; + flex-direction: column; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 25px; +} + +.date_author{ + display: flex; + width: 100%; + flex-direction: row; + align-items: flex-start; + justify-content: space-between; +} \ No newline at end of file diff --git a/PlanktonDetector/static/DetectionApp/css/base.css b/PlanktonDetector/static/DetectionApp/css/base.css index e665d26..bf17a48 100644 --- a/PlanktonDetector/static/DetectionApp/css/base.css +++ b/PlanktonDetector/static/DetectionApp/css/base.css @@ -28,6 +28,10 @@ body, html{ .footer{ display:flex; + position:fixed; + bottom: 0; + right: 0; + left:0; flex-direction: row; justify-content: center; align-items: center; diff --git a/PlanktonDetector/static/DetectionApp/css/detection_detail.css b/PlanktonDetector/static/DetectionApp/css/detection_detail.css new file mode 100644 index 0000000..440f7ed --- /dev/null +++ b/PlanktonDetector/static/DetectionApp/css/detection_detail.css @@ -0,0 +1,7 @@ +.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 new file mode 100644 index 0000000..3af0310 --- /dev/null +++ b/PlanktonDetector/static/DetectionApp/css/history.css @@ -0,0 +1,38 @@ +.history_list{ + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 100%; +} + +h2{ + margin-bottom:25px; +} + +ul{ + list-style-type: none; +} + +.history_list a{ + text-decoration: none; + width: 50%; + color: black; +} + +.history_details{ + display:flex; + width: 100%; + flex-direction: row; + align-items: flex-start; + justify-content: space-evenly; + margin-bottom: 25px; +} + +.date_author{ + display: flex; + width: 100%; + flex-direction: column; + align-items: flex-end; + justify-content: space-between; +} diff --git a/PlanktonDetector/static/DetectionApp/css/upload.css b/PlanktonDetector/static/DetectionApp/css/upload.css index efd0898..7b492bf 100644 --- a/PlanktonDetector/static/DetectionApp/css/upload.css +++ b/PlanktonDetector/static/DetectionApp/css/upload.css @@ -17,6 +17,7 @@ form{ background-color: lightgray; border: grey solid; border-width: 2px; + margin-bottom: 59px; } diff --git a/PlanktonDetector/templates/base.html b/PlanktonDetector/templates/base.html index 8ddf9b5..34a976b 100644 --- a/PlanktonDetector/templates/base.html +++ b/PlanktonDetector/templates/base.html @@ -14,9 +14,10 @@
- Placeholder + Community Predict {% if user.is_authenticated %} + History Logout {{user}} {%else%} Login diff --git a/PlanktonDetector/templates/detection_detail.html b/PlanktonDetector/templates/detection_detail.html new file mode 100644 index 0000000..f32f824 --- /dev/null +++ b/PlanktonDetector/templates/detection_detail.html @@ -0,0 +1,7 @@ +{%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 new file mode 100644 index 0000000..91e312e --- /dev/null +++ b/PlanktonDetector/templates/history.html @@ -0,0 +1,40 @@ +{%extends 'base.html'%} +{%load static%} +{%block extracss%} + +{%endblock extracss%} +{%block content%} +
+

History

+ {% for detection in page_obj %} + + {% endfor %} + +
+{%endblock content%} \ No newline at end of file diff --git a/PlanktonDetector/templates/list_posts.html b/PlanktonDetector/templates/list_posts.html new file mode 100644 index 0000000..5ca2d15 --- /dev/null +++ b/PlanktonDetector/templates/list_posts.html @@ -0,0 +1,21 @@ +{%extends 'base.html'%} +{%load static%} +{%block extracss%} + +{%endblock extracss%} +{%block content%} +
+

Posts

+ {% for post in object_list %} +
+ +

{{ post.title }}

+
+

{{post.date_pub}}

+

{{post.author.username}}

+
+
+
+ {% endfor %} +
+{%endblock content%} \ No newline at end of file diff --git a/PlanktonDetector/templates/upload.html b/PlanktonDetector/templates/upload.html index ddd1f57..2a564a1 100644 --- a/PlanktonDetector/templates/upload.html +++ b/PlanktonDetector/templates/upload.html @@ -7,7 +7,10 @@
{% csrf_token %}
- {% if img_saved is None %} + {% if not user.is_authenticated %} +

Please login to submit image

+ + {% elif img_saved is None %}

Choose image for analysis

{% else %} @@ -18,10 +21,10 @@