From d23cdff3afa9b32cc3915809d3550558847e1352 Mon Sep 17 00:00:00 2001 From: Piotr Szkudlarek Date: Sat, 6 Jan 2024 14:14:15 +0100 Subject: [PATCH] Add post and comment creation functionality --- PlanktonDetector/Community/froms.py | 18 +++++++ .../migrations/0003_comment_date_added.py | 21 ++++++++ PlanktonDetector/Community/models.py | 5 ++ PlanktonDetector/Community/urls.py | 1 + PlanktonDetector/Community/views.py | 46 +++++++++++++++++- PlanktonDetector/DetectionApp/urls.py | 2 +- PlanktonDetector/UserManagement/views.py | 16 ++++-- PlanktonDetector/db.sqlite3 | Bin 462848 -> 479232 bytes .../static/Community/css/add_post.css | 0 .../static/Community/css/list_posts.css | 13 +++++ .../static/Community/css/post_details.css | 43 ++++++++++++++-- .../static/DetectionApp/css/results.css | 34 ++++++++----- .../static/DetectionApp/css/upload.css | 8 ++- PlanktonDetector/templates/add_post.html | 12 +++++ PlanktonDetector/templates/base.html | 4 +- PlanktonDetector/templates/list_posts.html | 22 ++++++++- PlanktonDetector/templates/post_details.html | 16 ++++-- PlanktonDetector/templates/results.html | 6 +-- PlanktonDetector/templates/upload.html | 4 +- 19 files changed, 235 insertions(+), 36 deletions(-) create mode 100644 PlanktonDetector/Community/froms.py create mode 100644 PlanktonDetector/Community/migrations/0003_comment_date_added.py create mode 100644 PlanktonDetector/static/Community/css/add_post.css create mode 100644 PlanktonDetector/templates/add_post.html diff --git a/PlanktonDetector/Community/froms.py b/PlanktonDetector/Community/froms.py new file mode 100644 index 0000000..5cc041d --- /dev/null +++ b/PlanktonDetector/Community/froms.py @@ -0,0 +1,18 @@ +from django import forms +from .models import Post, Comment + + +class PostForm(forms.ModelForm): + class Meta: + model = Post + fields = ["title", "content"] + + +class CommentForm(forms.ModelForm): + class Meta: + model = Comment + fields = ["content"] + labels = { + "content": "Comment", + } + widgets = {"content": forms.Textarea(attrs={"rows": 3})} diff --git a/PlanktonDetector/Community/migrations/0003_comment_date_added.py b/PlanktonDetector/Community/migrations/0003_comment_date_added.py new file mode 100644 index 0000000..46f0226 --- /dev/null +++ b/PlanktonDetector/Community/migrations/0003_comment_date_added.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.7 on 2024-01-05 19:44 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + dependencies = [ + ("Community", "0002_post_date_pub"), + ] + + operations = [ + migrations.AddField( + model_name="comment", + name="date_added", + field=models.DateField( + auto_now_add=True, default=django.utils.timezone.now + ), + preserve_default=False, + ), + ] diff --git a/PlanktonDetector/Community/models.py b/PlanktonDetector/Community/models.py index aed7013..fa94610 100644 --- a/PlanktonDetector/Community/models.py +++ b/PlanktonDetector/Community/models.py @@ -10,8 +10,13 @@ class Post(models.Model): author = models.ForeignKey(User, on_delete=models.CASCADE, null=False) date_pub = models.DateField(auto_now_add=True) + @property + def sorted_comments(self): + return self.comment_set.all().order_by("-date_added") + 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) + date_added = models.DateField(auto_now_add=True) diff --git a/PlanktonDetector/Community/urls.py b/PlanktonDetector/Community/urls.py index c31225f..c95a7df 100644 --- a/PlanktonDetector/Community/urls.py +++ b/PlanktonDetector/Community/urls.py @@ -6,4 +6,5 @@ from . import views urlpatterns = [ path("posts/", views.ListPosts.as_view(), name="posts"), path("posts/", views.PostDetails.as_view(), name="post-details"), + path("posts/create/", views.AddPost.as_view(), name="add_post"), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/PlanktonDetector/Community/views.py b/PlanktonDetector/Community/views.py index 99ce625..2bfd4a6 100644 --- a/PlanktonDetector/Community/views.py +++ b/PlanktonDetector/Community/views.py @@ -1,6 +1,12 @@ -from django.shortcuts import render +from typing import Any +from django.db.models.query import QuerySet +from django.http import HttpResponse +from django.shortcuts import redirect, render from django.views.generic import ListView, DetailView +from django.views.generic.edit import FormView, FormMixin from .models import Post, Comment +from .froms import PostForm, CommentForm + # Create your views here. @@ -8,8 +14,44 @@ from .models import Post, Comment class ListPosts(ListView): model = Post template_name = "list_posts.html" + paginate_by = 3 + + def get_queryset(self) -> QuerySet[Any]: + queryset = Post.objects.all().order_by("-date_pub") + return queryset -class PostDetails(DetailView): +class PostDetails(DetailView, FormMixin): model = Post template_name = "post_details.html" + form_class = CommentForm + + # def get_queryset(self) -> QuerySet[Any]: + # queryset = Post.objects.get(pk=self.request.GET.get("pk")) + + def post(self, request, *args, **kwargs): + self.object = self.get_object() + form = self.get_form() + if form.is_valid(): + return self.form_valid(form) + else: + return self.form_invalid(form) + + def form_valid(self, form) -> HttpResponse: + post = self.get_object() + new_comment = form.save(commit=False) + new_comment.author = self.request.user + new_comment.post = post + new_comment.save() + return redirect("post-details", pk=post.id) + + +class AddPost(FormView): + template_name = "add_post.html" + form_class = PostForm + + def form_valid(self, form) -> HttpResponse: + new_post = form.save(commit=False) + new_post.author = self.request.user + new_post.save() + return redirect("post-details", pk=new_post.id) diff --git a/PlanktonDetector/DetectionApp/urls.py b/PlanktonDetector/DetectionApp/urls.py index eca106e..b69b15c 100644 --- a/PlanktonDetector/DetectionApp/urls.py +++ b/PlanktonDetector/DetectionApp/urls.py @@ -10,5 +10,5 @@ urlpatterns = [ path( "detection/", views.DetectionDetails.as_view(), name="detection-details" ), - path("export_pred/", views.download_pred_res, name="download_predicitons"), + path("export_pred/", views.download_pred_res, name="download_predicitions"), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/PlanktonDetector/UserManagement/views.py b/PlanktonDetector/UserManagement/views.py index 94df782..04e1091 100644 --- a/PlanktonDetector/UserManagement/views.py +++ b/PlanktonDetector/UserManagement/views.py @@ -1,8 +1,10 @@ from django.shortcuts import render, redirect from django.contrib.auth.forms import AuthenticationForm, UserCreationForm from django.contrib.auth import authenticate, login, logout +from django.urls import reverse_lazy from django.views import View + class LoginView(View): form_class = AuthenticationForm template_name = "login_signup.html" @@ -12,6 +14,7 @@ class LoginView(View): return render(request, self.template_name, {"form": form}) def post(self, request, *args, **kwargs): + redirection_path = request.GET.get("next") form = self.form_class(data=request.POST) if form.is_valid(): user = authenticate( @@ -20,7 +23,10 @@ class LoginView(View): ) if user is not None: login(request, user) - return redirect("detect") + if redirection_path is not None: + return redirect(redirection_path) + else: + return redirect("detect/") return render(request, self.template_name, {"form": form}) @@ -37,10 +43,14 @@ class SignupView(View): if form.is_valid(): user = form.save() login(request, user) - return redirect("detect") + return redirect("detect/") return render(request, self.template_name, {"form": form, "signup": True}) def logout_view(request): + redirection_path = request.GET.get("next") logout(request) - return redirect("detect") + if redirection_path is not None: + return redirect(redirection_path) + else: + return redirect("detect/") diff --git a/PlanktonDetector/db.sqlite3 b/PlanktonDetector/db.sqlite3 index 27789999260170529776b753b3836717390a9bca..30fa830db9f8ab8baf1ffb705c26af8a4a1167b7 100644 GIT binary patch delta 6254 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 a|6T&Ua(@0L&|Bu`2hiIAxih?Rm;VRqRcRFf delta 1162 zcmaize@t6d6vyAax9`0^;Js4_AYmJ6?U*b;%cBJ<$%un1ASV7HZZGrAMNKtin-O`qVs2uh*&<8GjEotxKim@C7R@F~bkQu)#d+IkvP2SJ za_=ACd+s@(^S$Sdr8kVF*T255a0|zALu~!~C6Cp((3X!v(?D#(iY8iGGphAnA+u6i zEfs)6SyKK~W|W{3%oNUf6fNke;{D1m+-6O+R%$a28!FM395&*IC$zhcO)6e3lF4S& z8XFu;M*9<~?s)8_1THBh&WE%fcDgT`5a1M7eypWb>vpD*?Offmx6o}>8=Bo}v+8ox zyIqa$y3CE3-D(l?YdGZ_?iMvR(lZ>1#0Fk=?GN|& zc|57!{%9&O@+9;>wRLK(>UoH$L4Dlg8n^Sgs#=F(%^Y~^Rbmmq&cSuC z!*LjdCxJ4j05)zUo#_9_jR7=kZ?F=3#~2S;FfO z@d!RDF5@B07#0cjUBwH}krFtmi*u-&1%`p1^pc2=_?4tCrcu_vJ2FkaMy75s&83TR zQ&u}8Q`aHWl`F(A&oZC+NqR1Bx?iCAtefcU9{%aC$|bmzMTo&4@Efc$yKl15z6#55 ziIp*ce(4pO(xiw#sEgkTl`Lg|rw6|mUMVjU`D>hVT&~DJo98lL6a)MDrMmS diff --git a/PlanktonDetector/static/Community/css/add_post.css b/PlanktonDetector/static/Community/css/add_post.css new file mode 100644 index 0000000..e69de29 diff --git a/PlanktonDetector/static/Community/css/list_posts.css b/PlanktonDetector/static/Community/css/list_posts.css index 17adc82..274e4e6 100644 --- a/PlanktonDetector/static/Community/css/list_posts.css +++ b/PlanktonDetector/static/Community/css/list_posts.css @@ -27,6 +27,7 @@ ul{ align-items: flex-start; justify-content: space-between; margin-bottom: 25px; + box-shadow: 0 2px lightgray; } .date_author{ @@ -35,4 +36,16 @@ ul{ flex-direction: row; align-items: flex-start; justify-content: space-between; +} + +#add_post{ + text-align: center; + padding: 1px 6px 1px 6px; + width: 10%; + background-color: chartreuse; + border-radius: 25px; + border: 2px black; + box-shadow: 0 0 0 2px black; + font-size: 20px; + font-family: Arial; } \ No newline at end of file diff --git a/PlanktonDetector/static/Community/css/post_details.css b/PlanktonDetector/static/Community/css/post_details.css index f2d3589..3a95ff6 100644 --- a/PlanktonDetector/static/Community/css/post_details.css +++ b/PlanktonDetector/static/Community/css/post_details.css @@ -77,9 +77,46 @@ body, html{ margin-top:10px; } -#comment-author{ +#add_comment{ display: flex; - width: 100%; - justify-content: flex-end; + flex-direction: row; + width: 55%; + justify-content: space-evenly; + align-items: center; + margin-top:30px; } +#comment_form{ + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; +} + +#comment_form textarea{ + height: auto; +} + +#comment_form label{ + font-size: 20px; +} + +.date_author{ + display: flex; + width: 100%; + flex-direction: row; + align-items: flex-start; + justify-content: space-between; +} + +#submit{ + background-color: chartreuse; + border-radius: 25px; + border: 2px solid black; + font-size: 20px; + text-decoration: none; + color:black; + font-family: Arial; + text-align: center; +} \ No newline at end of file diff --git a/PlanktonDetector/static/DetectionApp/css/results.css b/PlanktonDetector/static/DetectionApp/css/results.css index 0c466e9..6511018 100644 --- a/PlanktonDetector/static/DetectionApp/css/results.css +++ b/PlanktonDetector/static/DetectionApp/css/results.css @@ -29,17 +29,6 @@ form{ margin: 10px 0px 15px 0px; } -#submit{ - background-color: chartreuse; - margin-top: auto; - height: 50px; - width: 80%; - border-radius: 25px; - border: 2px white; - box-shadow: 0 0 0 4px white; - font-size: 25px; -} - .upload_field { margin: auto; display: flex; @@ -91,7 +80,26 @@ form{ height: 50px; width: 60%; border-radius: 25px; - border: 2px white; - box-shadow: 0 0 0 4px white; + border: 2px solid black; font-size: 25px; + text-decoration: none; + color:black; + font-family: Arial; + text-align: center; + line-height:45px; +} + +#submit{ + background-color: chartreuse; + margin-top: auto; + height: 50px; + width: 80%; + border-radius: 25px; + border: 2px solid white; + font-size: 25px; + text-decoration: none; + color:black; + font-family: Arial; + text-align: center; + line-height:45px; } \ No newline at end of file diff --git a/PlanktonDetector/static/DetectionApp/css/upload.css b/PlanktonDetector/static/DetectionApp/css/upload.css index 35e4672..55ef641 100644 --- a/PlanktonDetector/static/DetectionApp/css/upload.css +++ b/PlanktonDetector/static/DetectionApp/css/upload.css @@ -34,9 +34,13 @@ form{ height: 50px; width: 80%; border-radius: 25px; - border: 2px white; - box-shadow: 0 0 0 4px white; + border: 2px solid white; font-size: 25px; + text-decoration: none; + color:black; + font-family: Arial; + text-align: center; + line-height:45px; } .upload_field { diff --git a/PlanktonDetector/templates/add_post.html b/PlanktonDetector/templates/add_post.html new file mode 100644 index 0000000..69e7ac2 --- /dev/null +++ b/PlanktonDetector/templates/add_post.html @@ -0,0 +1,12 @@ +{%extends 'base.html'%} +{%load static%} +{%block extracss%} + +{%endblock extracss%} +{%block content%} +
+ {% csrf_token %} + {{ form.as_p }} + +
+{%endblock content%} \ No newline at end of file diff --git a/PlanktonDetector/templates/base.html b/PlanktonDetector/templates/base.html index a5764e0..42ece1c 100644 --- a/PlanktonDetector/templates/base.html +++ b/PlanktonDetector/templates/base.html @@ -18,9 +18,9 @@
Predict {% if user.is_authenticated %} History - Logout {{user}} + Logout {{user}} {%else%} - Login + Login {%endif%} {%block content%}{%endblock content%} diff --git a/PlanktonDetector/templates/list_posts.html b/PlanktonDetector/templates/list_posts.html index 5ca2d15..62187b8 100644 --- a/PlanktonDetector/templates/list_posts.html +++ b/PlanktonDetector/templates/list_posts.html @@ -6,7 +6,10 @@ {%block content%}

Posts

- {% for post in object_list %} +{% if user.is_authenticated %} +Add new post +{%endif%} + {% for post in page_obj %}

{{ post.title }}

@@ -17,5 +20,22 @@
{% endfor %} +
{%endblock content%} \ No newline at end of file diff --git a/PlanktonDetector/templates/post_details.html b/PlanktonDetector/templates/post_details.html index 8d4451a..e1a02af 100644 --- a/PlanktonDetector/templates/post_details.html +++ b/PlanktonDetector/templates/post_details.html @@ -14,12 +14,22 @@

{{object.author}}

+
+
+ {%csrf_token%} + {{form}} + +
+

Comments:

- {%for comments in object.comment_set.all%} + {%for comment in object.sorted_comments%}
-

{{comments.content}}

-

{{comments.author}}

+

{{comment.content}}

+
+

{{comment.date_added}}

+

{{comment.author}}

+
{%endfor%}
diff --git a/PlanktonDetector/templates/results.html b/PlanktonDetector/templates/results.html index 658bc77..994c90d 100644 --- a/PlanktonDetector/templates/results.html +++ b/PlanktonDetector/templates/results.html @@ -9,7 +9,7 @@
{% if not user.is_authenticated %}

Please login to see results

- + Login {% else %}

Results:

{%for img in img.images.all %} @@ -20,7 +20,7 @@
{%endfor%} {%endfor%} - + Submit again {%endif%}
@@ -29,7 +29,7 @@ upload_image {%endfor%} - + Export predictions