From fcc1461bdb5d52108d3ea3764400d1fa0e5bfaa2 Mon Sep 17 00:00:00 2001 From: gorkemarslan Date: Thu, 11 Nov 2021 18:40:57 +0300 Subject: [PATCH 1/8] Update accounts/test_api.py --- project/accounts/tests/test_api.py | 142 +++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 6 deletions(-) diff --git a/project/accounts/tests/test_api.py b/project/accounts/tests/test_api.py index 1558996f8..b8efaa1d9 100644 --- a/project/accounts/tests/test_api.py +++ b/project/accounts/tests/test_api.py @@ -1,4 +1,6 @@ import json +from PIL import Image +from django.core.files.temp import NamedTemporaryFile from django.contrib.auth import get_user_model from django.test import TestCase from rest_framework.test import APIClient @@ -155,8 +157,8 @@ def test_existing_user_account_data(self): self.assertEqual(response.status_code, 200) self.assertEqual(content["username"], self.user.username) - def test_nonexisting_user_account_data(self): - """Whether retrieving a nonexisting user raises 404""" + def test_nonexistent_user_account_data(self): + """Whether retrieving a nonexistent user raises 404""" self.client.login(username="newuser", password="password123") response = self.client.get(reverse("get_user", args=["newuser" + "not_exist"])) @@ -177,8 +179,8 @@ def test_existing_user_profile_data(self): self.assertEqual(response.status_code, 200) self.assertEqual(content["username"], self.user.username) - def test_nonexisting_user_profile_data(self): - """Whether retrieving a nonexisting user profile raises 404""" + def test_nonexistent_user_profile_data(self): + """Whether retrieving a nonexistent user profile raises 404""" self.client.login(username="newuser", password="password123") response = self.client.get( @@ -207,8 +209,8 @@ def test_existing_user_profile_data(self): self.assertEqual(content["username"], self.superuser.username) self.assertTrue(content["follow_state"]) - def test_nonexisting_user_profile_data(self): - """Whether retrieving a nonexisting user card raises 404""" + def test_nonexistent_user_profile_data(self): + """Whether retrieving a nonexistent user card raises 404""" self.client.login(username="newuser", password="password123") response = self.client.get(reverse("get_card", args=["newuser" + "not_exist"])) @@ -231,3 +233,131 @@ def test_first_name_last_name_about_fields_can_be_editable(self): self.assertEqual(self.user.profile.first_name, "First") self.assertEqual(self.user.profile.last_name, "Last") self.assertEqual(self.user.profile.about_me, "About me") + + +class UploadProfileImage(BaseTestCase): + """A class to test upload_profile_image function""" + + def setUp(self) -> None: + super(UploadProfileImage, self).setUp() + self.client.login(username="newuser", password="password123") + self.url = reverse("upload_profile") + self.image = Image.new("RGB", size=(5, 5), color=(0, 0, 0)) + self.file = NamedTemporaryFile(suffix=".jpg") + self.image.save(self.file) + + def tearDown(self) -> None: + for profile in Profile.objects.all(): + profile.profile_image.delete() + profile.profile_image_thumb.delete() + + def test_upload_profile_image(self): + """Whether upload_profile_image function works as expected""" + + with open(self.file.name, "rb") as image_file: + data = {"profile_image": image_file} + response = self.client.post(self.url, data=data) + content = json.loads(response.content) + self.user.profile.refresh_from_db() + self.assertEqual( + content.get("profile_image"), self.user.profile.profile_image_url + ) + self.assertNotEqual( + self.user.profile.profile_image_url, "/static/img/no_image_md.png" + ) + self.assertNotEqual( + self.user.profile.profile_image_thumb_url, "/static/img/no_image_md.png" + ) + + def test_upload_profile_image_with_invalid_extension(self): + """Whether upload_profile_image raises an error when non-image file is past""" + + image = Image.new("RGB", size=(5, 5), color=(0, 0, 0)) + file = NamedTemporaryFile(suffix=".pdf") + image.save(file) + + with open(file.name, "rb") as image_file: + data = {"profile_image": image_file} + response = self.client.post(self.url, data=data) + content = json.loads(response.content) + self.assertEqual(content["error"], "FORM_ERROR") + self.user.profile.refresh_from_db() + self.assertEqual( + self.user.profile.profile_image_url, "/static/img/no_image_md.png" + ) + self.assertEqual( + self.user.profile.profile_image_thumb_url, "/static/img/no_image_md.png" + ) + + +class RequestFollowTests(BaseTestCase): + """A class to test request_follow function""" + + def test_request_follow(self): + """Whether request_follow function works as expected""" + + self.client.login(username="newuser", password="password123") + number_of_followings = self.user.profile.following.count() + data = {"target": self.user2.username} + response = self.client.post(reverse("follow_user"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 200) + self.assertEqual(content.get("result").get("username"), self.user2.username) + self.assertTrue(content.get("result").get("follow_status")) + self.assertEqual(self.user.profile.following.count(), number_of_followings + 1) + + def test_user_cannot_follow_itself(self): + """Whether a user trying to follow itself raises an error""" + + self.client.login(username="newuser", password="password123") + data = {"target": self.user.username} + response = self.client.post(reverse("follow_user"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 400) + self.assertIn("You cannot follow yourself", content.get("error")) + + def test_following_nonexistent_user_gives_error(self): + """Whether following a nonexistent user raises an error""" + + self.client.login(username="newuser", password="password123") + data = {"target": "nonexistent"} + response = self.client.post(reverse("follow_user"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 400) + self.assertIn("not found", content.get("error")) + + +class RequestUnfollowTests(BaseTestCase): + """A class to test request_unfollow function""" + + def test_request_unfollow(self): + """Whether request_unfollow function works as expected""" + + self.client.login(username="newuser", password="password123") + number_of_followings = self.user.profile.following.count() + data = {"target": self.superuser.username} + response = self.client.post(reverse("unfollow_user"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 200) + self.assertEqual(content.get("result"), "Success") + self.assertEqual(self.user.profile.following.count(), number_of_followings - 1) + + def test_username_for_unfollowing_cannot_be_empty(self): + """Whether unfollowing an empty username raises an error""" + + self.client.login(username="newuser", password="password123") + data = {"target": ""} + response = self.client.post(reverse("unfollow_user"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 400) + self.assertEqual(content.get("error"), "username cannot be empty") + + def test_unfollowing_nonexistent_user_gives_error(self): + """Whether unfollowing an nonexistent user raises an error""" + + self.client.login(username="newuser", password="password123") + data = {"target": "nonexistent"} + response = self.client.post(reverse("unfollow_user"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 400) + self.assertIn("not found", content.get("error")) From 9770759583d1e8b07c6194bab85e9baa1c184cff Mon Sep 17 00:00:00 2001 From: gorkemarslan Date: Thu, 11 Nov 2021 18:43:18 +0300 Subject: [PATCH 2/8] Update threads/test_api.py --- project/threads/tests/test_api.py | 181 +++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 1 deletion(-) diff --git a/project/threads/tests/test_api.py b/project/threads/tests/test_api.py index 15c00f57f..c03efe3f5 100644 --- a/project/threads/tests/test_api.py +++ b/project/threads/tests/test_api.py @@ -1,10 +1,14 @@ +import os import json +from PIL import Image +from django.conf import settings +from django.core.files.temp import NamedTemporaryFile from django.contrib.auth import get_user_model from django.test import TestCase from rest_framework.test import APIClient from django.urls import reverse from categories.models import Category -from threads.models import Civi, Thread +from threads.models import Civi, CiviImage, Thread class BaseTestCase(TestCase): @@ -81,6 +85,27 @@ def test_get_thread(self): self.assertEqual(content["category"]["name"], "NewCategory") +class CreateCiviTests(BaseTestCase): + """A class to test create_civi function""" + + def test_create_new_civi(self): + """Whether a new civi is created as expected""" + + new_civi_data = { + "thread_id": self.thread.pk, + "title": "New Civi", + "body": "New Body", + "c_type": self.category.name, + } + self.client.login(username="newuser", password="password123") + response = self.client.post(reverse("new civi"), data=new_civi_data) + content = json.loads(response.content) + self.assertEqual(content["data"]["thread_id"], self.thread.id) + self.assertEqual(content["data"]["title"], "New Civi") + self.assertEqual(content["data"]["body"], "New Body") + self.assertEqual(content["data"]["author"]["username"], self.user.username) + + class EditThreadTests(BaseTestCase): """A class to test edit_thread function""" @@ -100,3 +125,157 @@ def test_edit_thread(self): self.assertEqual(self.thread.title, "Edited Title") self.assertEqual(self.thread.summary, "Edited summary") self.assertEqual(int(content["data"]["thread_id"]), self.thread.id) + + def test_edit_nonexistent_thread(self): + """Whether editing a nonexistent thread gives 400 HTTP Status Code""" + + self.client.login(username="newuser", password="password123") + data = { + "thread_id": 12345, + "title": "Edited Title", + "summary": "Edited summary", + } + response = self.client.post(reverse("edit thread"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 400) + self.assertIn("does not exist", content["error"]) + + def test_edit_thread_with_missing_id_field(self): + """Whether editing a thread with_missing id field gives 400 HTTP Status Code""" + + self.client.login(username="newuser", password="password123") + data = { + "title": "Edited Title", + "summary": "Edited summary", + } + response = self.client.post(reverse("edit thread"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 400) + self.assertEqual(content["error"], "Invalid Thread Reference") + + +class EditCiviTests(BaseTestCase): + """A class to test edit_civi function""" + + def test_edit_civi(self): + """Whether a civi is edited as expected""" + + self.client.login(username="newuser", password="password123") + data = { + "civi_id": self.civi.id, + "title": "Edited Civi", + "body": "Edited body", + } + response = self.client.post(reverse("edit civi"), data=data) + content = json.loads(response.content) + self.civi.refresh_from_db() + self.assertEqual(response.status_code, 200) + self.assertEqual(self.civi.title, "Edited Civi") + self.assertEqual(self.civi.body, "Edited body") + self.assertEqual(content.get("thread_id"), 1) + self.assertEqual(content.get("title"), "Edited Civi") + + def test_edit_nonexistent_civi(self): + """Whether editing a nonexistent civi gives 400 HTTP Status Code""" + + self.client.login(username="newuser", password="password123") + data = { + "civi_id": 12345, + "title": "Edited Civi", + "body": "Edited body", + } + response = self.client.post(reverse("edit civi"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 400) + self.assertIn("does not exist", content["error"]) + + +class DeleteCiviTests(BaseTestCase): + """A class to test delete_civi function""" + + def test_delete_civi(self): + """Whether a civi is deleted as expected""" + + number_of_civis = Civi.objects.count() + self.client.login(username="newuser", password="password123") + data = {"civi_id": self.civi.id} + response = self.client.post(reverse("delete civi"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 200) + self.assertEqual(content.get("result"), "Success") + self.assertEqual(Civi.objects.count(), number_of_civis - 1) + + def test_only_civi_authors_have_right_to_delete(self): + """Whether only owner of a civi can delete the civi""" + + self.client.login(username="newuser2", password="password123") + data = {"civi_id": self.civi.id} + response = self.client.post(reverse("delete civi"), data=data) + content = json.loads(response.content) + self.assertEqual(response.status_code, 400) + self.assertEqual(content.get("error"), "No Edit Rights") + + +class UploadThreadImageTests(BaseTestCase): + """A class to test upload_thread_image function""" + + def setUp(self) -> None: + super(UploadThreadImageTests, self).setUp() + self.client.login(username="newuser", password="password123") + self.url = reverse("upload image") + self.image = Image.new("RGB", size=(5, 5), color=(0, 0, 0)) + self.file = NamedTemporaryFile(suffix=".jpg") + self.image.save(self.file) + + def tearDown(self) -> None: + """Delete test thread images from the file system""" + + for thread in Thread.objects.all(): + thread.image.delete() + + def test_upload_thread_image(self): + """Whether a thread image is uploaded as expected""" + + with open(self.file.name, "rb") as image_file: + data = {"thread_id": self.thread.id, "attachment_image": image_file} + response = self.client.post(self.url, data=data) + content = json.loads(response.content) + self.thread.refresh_from_db() + self.assertEqual(content.get("image"), self.thread.image_url) + self.assertNotEqual(self.thread.image_url, "/static/img/no_image_md.png") + + +class UploadCiviImageTests(BaseTestCase): + """A class to test upload_civi_image function""" + + def setUp(self) -> None: + super(UploadCiviImageTests, self).setUp() + self.client.login(username="newuser", password="password123") + self.url = reverse("upload images") + self.image = Image.new("RGB", size=(5, 5), color=(0, 0, 0)) + self.file = NamedTemporaryFile(suffix=".jpg") + self.image.save(self.file) + + def tearDown(self) -> None: + """Delete test civi images from the file system""" + + for civi_image in CiviImage.objects.all(): + image_path = os.path.join(settings.BASE_DIR, civi_image.image_url[1:]) + if os.path.isfile(image_path): + os.remove(image_path) + + def test_upload_civi_image(self): + """Whether a civi image is uploaded as expected""" + + with open(self.file.name, "rb") as image_file: + data = {"civi_id": self.civi.id, "attachment_image": image_file} + response = self.client.post(self.url, data=data) + content = json.loads(response.content) + self.thread.refresh_from_db() + self.assertEqual( + content.get("attachments")[0].get("image_url"), + self.civi.images.first().image_url, + ) + self.assertNotEqual( + self.civi.images.first().image_url, "/static/img/no_image_md.png" + ) From 6fe2f5474e59c4f5fdcaf4f25e8ecfb9e708827f Mon Sep 17 00:00:00 2001 From: gorkemarslan Date: Thu, 11 Nov 2021 18:44:01 +0300 Subject: [PATCH 3/8] Update accounts/test_views.py --- project/accounts/tests/test_views.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/project/accounts/tests/test_views.py b/project/accounts/tests/test_views.py index 67560ff8c..fa9d692f3 100644 --- a/project/accounts/tests/test_views.py +++ b/project/accounts/tests/test_views.py @@ -13,7 +13,6 @@ def setUp(self) -> None: self.user = get_user_model().objects.create_user( username="newuser", email="test@test.com", password="password123" ) - self.profile, created = Profile.objects.update_or_create(user=self.user) class LoginViewTests(BaseTestCase): @@ -118,9 +117,9 @@ class SettingsViewTests(BaseTestCase): def setUp(self) -> None: super(SettingsViewTests, self).setUp() - self.profile.first_name = "Gorkem" - self.profile.last_name = "Arslan" - self.profile.save() + self.user.profile.first_name = "Gorkem" + self.user.profile.last_name = "Arslan" + self.user.profile.save() self.client.login(username=self.user.username, password="password123") self.url = reverse("accounts_settings") self.response = self.client.get(self.url) @@ -196,3 +195,24 @@ def test_invalid_action_link(self): self.assertFalse(self.profile.is_verified) self.assertTemplateUsed(response, "general_message.html") self.assertContains(response, "Email Verification Error") + + +class UserProfileView(BaseTestCase): + """A class to test user profile view""" + + def setUp(self) -> None: + super(UserProfileView, self).setUp() + self.user.profile.first_name = "First" + self.user.profile.last_name = "Last" + self.user.profile.about_me = "About" + self.user.profile.save() + + def test_get_user_profile(self): + """Whether user_profile function works as expected""" + + self.client.login(username="newuser", password="password123") + response = self.client.get(reverse("profile", args=["newuser"])) + self.assertEqual(response.status_code, 200) + self.assertContains(response, self.user.username) + self.assertContains(response, self.user.email) + self.assertTemplateUsed(response, "account.html") From 436eda05739d0539648cd414616b34ef2e0eb997 Mon Sep 17 00:00:00 2001 From: gorkemarslan Date: Thu, 11 Nov 2021 18:53:33 +0300 Subject: [PATCH 4/8] Update threads/test_views.py --- project/threads/tests/test_views.py | 179 ++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 project/threads/tests/test_views.py diff --git a/project/threads/tests/test_views.py b/project/threads/tests/test_views.py new file mode 100644 index 000000000..3c3ac003f --- /dev/null +++ b/project/threads/tests/test_views.py @@ -0,0 +1,179 @@ +import json +from django.contrib.auth import get_user_model +from django.test import TestCase +from rest_framework.test import APIClient +from django.urls import reverse +from categories.models import Category +from threads.models import Civi, Thread + + +class BaseTestCase(TestCase): + """Base test class to set up test cases""" + + def setUp(self) -> None: + self.user = get_user_model().objects.create_user( + username="newuser", email="test@test.com", password="password123" + ) + self.user2 = get_user_model().objects.create_user( + username="newuser2", email="test2@test.com", password="password123" + ) + self.superuser = get_user_model().objects.create_superuser( + username="superuser", email="superuser@su.com", password="superpassword123" + ) + self.user.profile.first_name = "First" + self.user.profile.last_name = "Last" + self.user.profile.about_me = "About Me" + self.user.profile.save() + self.user.profile.following.add(self.superuser.profile) + self.user.profile.followers.add(self.superuser.profile, self.user2.profile) + self.category = Category.objects.create(name="NewCategory") + self.user.profile.categories.add(self.category) + self.thread = Thread.objects.create( + author=self.user, + title="Thread", + summary="summary", + category=self.category, + is_draft=False, + ) + self.draft_thread = Thread.objects.create( + author=self.user, + title="Draft Thread", + summary="draft_summary", + category=self.category, + ) + self.civi = Civi.objects.create( + author=self.user, thread=self.thread, title="Civi", body="body" + ) + + def tearDown(self) -> None: + self.client.logout() + + +class ThreadViewSetTests(BaseTestCase): + """A class to test ThreadViewSet""" + + def setUp(self) -> None: + super(ThreadViewSetTests, self).setUp() + self.client = APIClient() + + def test_anonymous_user_can_list_threads(self): + """Whether unauthenticated users cannot list profiles""" + + response = self.client.get(reverse("thread-list")) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data), 1) + + def test_authenticated_users_can_list_threads(self): + """Whether authenticated users can only get their own profiles""" + + self.client.login(username="newuser", password="password123") + response = self.client.get(reverse("thread-list")) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data), 1) + + def test_retrieving_thread_detail(self): + """Whether thread:id is retrieved""" + + url = reverse("thread-detail", args=["1"]) + response = self.client.get(url) + content = json.loads(response.content) + self.assertEqual(response.status_code, 200) + self.assertEqual(content.get("title"), "Thread") + + def test_retrieving_civis(self): + """Whether civi:id is retrieved""" + + response = self.client.get(reverse("thread-civis", args=["1"])) + content = json.loads(response.content) + self.assertEqual(response.status_code, 200) + self.assertEqual(content[0].get("title"), "Civi") + + def test_list_all_threads_via_all(self): + """Whether all threads are listed""" + + response = self.client.get(reverse("thread-all")) + content = json.loads(response.content) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(content), 2) + + def test_users_can_only_get_their_draft_threads(self): + """Whether users can only get their draft threads""" + + self.client.login(username=self.user.username, password="password123") + response = self.client.get(reverse("thread-drafts")) + content = json.loads(response.content) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(content), 1) + self.assertEqual(content[0].get("title"), "Draft Thread") + + def test_users_cannot_get_draft_threads_of_another_users(self): + """Whether users cannot get other users' draft threads""" + + self.client.login(username=self.user2.username, password="password123") + response = self.client.get(reverse("thread-drafts")) + content = json.loads(response.content) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(content), 0) + + +class BaseViewTests(BaseTestCase): + """A class to test base_view function""" + + def setUp(self) -> None: + super(BaseViewTests, self).setUp() + self.client.login(username=self.user.username, password="password123") + self.url = reverse("base") + self.response = self.client.get(self.url) + + def test_anonymous_users_are_redirected_to_landing_page(self): + """Whether unauthenticated users are redirected to the landing page""" + + self.client.logout() + self.response = self.client.get(self.url) + self.assertEqual(self.response.status_code, 200) + self.assertTemplateUsed(self.response, "base.html") + self.assertTemplateUsed(self.response, "landing.html") + self.assertTemplateUsed(self.response, "static_nav.html") + self.assertTemplateUsed(self.response, "static_footer.html") + self.assertContains(self.response, "Why CiviWiki?") + self.assertNotContains(self.response, "Wrong Content!") + + def test_authenticated_users_are_redirected_to_feed_page(self): + """Whether authenticated users are redirected to the feed page""" + + self.assertEqual(self.response.status_code, 200) + self.assertTemplateUsed(self.response, "base.html") + self.assertTemplateUsed(self.response, "feed.html") + self.assertTemplateUsed(self.response, "global_nav.html") + self.assertTemplateUsed(self.response, "static_footer.html") + self.assertContains(self.response, "Trending Issues") + self.assertNotContains(self.response, "Wrong Content!") + + +class IssueThreadTests(BaseTestCase): + """A class to test issue_thread function""" + + def setUp(self) -> None: + super(IssueThreadTests, self).setUp() + self.client.login(username=self.user.username, password="password123") + + def test_nonexistent_threads_redirect_to_404_page(self): + """Whether getting nonexistent threads redirects to 404 page""" + + url = reverse("issue_thread", args=["12345"]) + response = self.client.get(url) + self.assertEqual(response.status_code, 404) + + def test_thread_having_none_id_redirects_to_404_page(self): + """Whether getting threads with empty id field redirects to 404 page""" + + url = reverse("issue_thread", args=None) + response = self.client.get(url) + self.assertRedirects(response, "/404", status_code=302, target_status_code=404) + + def test_existing_threads(self): + """Whether existing threads are retrieved as expected""" + + url = reverse("issue_thread", args=["1"]) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) From d6161e9562ecbcd7130f7ab6be9ceaba07f24f8a Mon Sep 17 00:00:00 2001 From: gorkemarslan Date: Thu, 11 Nov 2021 18:54:14 +0300 Subject: [PATCH 5/8] Use Class Based Views --- project/threads/urls/urls.py | 9 +++++---- project/threads/views.py | 21 +++++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/project/threads/urls/urls.py b/project/threads/urls/urls.py index 92af0e504..ab823a2ed 100644 --- a/project/threads/urls/urls.py +++ b/project/threads/urls/urls.py @@ -5,10 +5,11 @@ urlpatterns = [ path("thread//csv/", views.civi2csv, name="civi2csv"), path("thread//", views.issue_thread, name="issue_thread"), - path("about/", views.about_view, name="about"), - path("support_us/", views.support_us_view, name="support_us"), - path("howitworks/", views.how_it_works_view, name="how_it_works"), - path("declaration/", views.declaration, name="declaration"), + path("thread/", views.issue_thread, name="issue_thread"), + path("about/", views.AboutView.as_view(), name="about"), + path("support_us/", views.SupportUsView.as_view(), name="support_us"), + path("howitworks/", views.HowItWorksView.as_view(), name="how_it_works"), + path("declaration/", views.DeclarationView.as_view(), name="declaration"), path("create-group/", views.create_group, name="create_group"), path("", views.base_view, name="base"), ] diff --git a/project/threads/views.py b/project/threads/views.py index e49f6fc15..94d9fe352 100644 --- a/project/threads/views.py +++ b/project/threads/views.py @@ -7,6 +7,7 @@ from django.db.models import F from django.http import HttpResponse, HttpResponseRedirect from django.template.response import TemplateResponse +from django.views.generic import TemplateView from django.views.decorators.csrf import csrf_exempt from rest_framework.decorators import action from rest_framework.response import Response @@ -261,21 +262,21 @@ def create_group(request): return TemplateResponse(request, "newgroup.html", {}) -def declaration(request): - return TemplateResponse(request, "declaration.html", {}) +class DeclarationView(TemplateView): + template_name = "declaration.html" -def landing_view(request): - return TemplateResponse(request, "landing.html", {}) +class LandingView(TemplateView): + template_name = "landing.html" -def how_it_works_view(request): - return TemplateResponse(request, "how_it_works.html", {}) +class HowItWorksView(TemplateView): + template_name = "how_it_works.html" -def about_view(request): - return TemplateResponse(request, "about.html", {}) +class AboutView(TemplateView): + template_name = "about.html" -def support_us_view(request): - return TemplateResponse(request, "support_us.html", {}) +class SupportUsView(TemplateView): + template_name = "support_us.html" From d85e3b794835dcb58fee5ffbac16977cfca682c0 Mon Sep 17 00:00:00 2001 From: gorkemarslan Date: Thu, 11 Nov 2021 19:05:34 +0300 Subject: [PATCH 6/8] Fix errors --- project/threads/api.py | 14 ++++++++++---- project/threads/serializers.py | 2 +- project/threads/views.py | 8 ++++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/project/threads/api.py b/project/threads/api.py index 335305964..5500b35a2 100644 --- a/project/threads/api.py +++ b/project/threads/api.py @@ -311,11 +311,11 @@ def edit_civi(request): body = request.POST.get("body", "") civi_type = request.POST.get("type", "") - c = Civi.objects.get(id=civi_id) - if request.user.username != c.author.username: - return HttpResponseBadRequest(reason="No Edit Rights") - try: + c = Civi.objects.get(id=civi_id) + if request.user.username != c.author.username: + return HttpResponseBadRequest(reason="No Edit Rights") + c.title = title c.body = body c.c_type = civi_type @@ -335,6 +335,12 @@ def edit_civi(request): civi_image.delete() return JsonResponse(c.dict_with_score(request.user.id)) + + except Civi.DoesNotExist: + return JsonResponse( + {"error": f"Civi with id:{civi_id} does not exist"}, + status=400, + ) except Exception as e: return HttpResponseServerError(reason=str(e)) diff --git a/project/threads/serializers.py b/project/threads/serializers.py index ea0b785f8..a2c222790 100644 --- a/project/threads/serializers.py +++ b/project/threads/serializers.py @@ -61,7 +61,7 @@ def get_score(self, obj): else: return 0 - if user.is_anonymous(): + if not user.is_authenticated: return 0 else: return obj.score(user.id) diff --git a/project/threads/views.py b/project/threads/views.py index 94d9fe352..1e33dcc25 100644 --- a/project/threads/views.py +++ b/project/threads/views.py @@ -6,6 +6,7 @@ from core.custom_decorators import full_profile, login_required from django.db.models import F from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import get_object_or_404 from django.template.response import TemplateResponse from django.views.generic import TemplateView from django.views.decorators.csrf import csrf_exempt @@ -89,8 +90,7 @@ def drafts(self, request): Gets the drafts of the current authenticated user /threads/drafts """ - account = get_account(username=self.request.user) - draft_threads = Thread.objects.filter(author=account, is_draft=True) + draft_threads = Thread.objects.filter(author=self.request.user, is_draft=True) serializer = ThreadListSerializer( draft_threads, many=True, context={"request": request} ) @@ -121,7 +121,7 @@ def images(self, request, pk=None): def base_view(request): if not request.user.is_authenticated: return TemplateResponse(request, "landing.html", {}) - # return HttpResponseRedirect("/") + Profile_filter = Profile.objects.get(user=request.user) if "login_user_image" not in request.session.keys(): request.session["login_user_image"] = Profile_filter.profile_image_thumb_url @@ -189,7 +189,7 @@ def issue_thread(request, thread_id=None): if not thread_id: return HttpResponseRedirect("/404") - Thread_filter = Thread.objects.get(id=thread_id) + Thread_filter = get_object_or_404(Thread, pk=thread_id) c_qs = Civi.objects.filter(thread_id=thread_id).exclude(c_type="response") c_scored = [c.dict_with_score(request.user.id) for c in c_qs] civis = sorted(c_scored, key=lambda c: c["score"], reverse=True) From 5e8cf64b227eb09eb1803a64f26bf92a7ad70e51 Mon Sep 17 00:00:00 2001 From: gorkemarslan Date: Thu, 11 Nov 2021 19:07:29 +0300 Subject: [PATCH 7/8] Return JsonResponse instead of HttpResponse for API views --- project/accounts/api.py | 12 +++++++----- project/threads/api.py | 13 ++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/project/accounts/api.py b/project/accounts/api.py index 3a8b25af0..819ae7300 100644 --- a/project/accounts/api.py +++ b/project/accounts/api.py @@ -387,8 +387,10 @@ def request_follow(request): ) return JsonResponse({"result": data}) - except get_user_model().DoesNotExist as e: - return HttpResponseBadRequest(reason=str(e)) + except get_user_model().DoesNotExist: + return JsonResponse( + {"error": f"User with username {target_username} not found"}, status=400 + ) except Exception as e: return HttpResponseServerError(reason=str(e)) @@ -421,11 +423,11 @@ def request_unfollow(request): target_account.followers.remove(account) target_account.save() return JsonResponse({"result": "Success"}) - return HttpResponseBadRequest(reason="username cannot be empty") + return JsonResponse({"error": "username cannot be empty"}, status=400) except get_user_model().DoesNotExist: - return HttpResponseBadRequest( - reason=f"User with username {username} does not exist" + return JsonResponse( + {"error": f"User with username {username} not found"}, status=400 ) except Exception as e: return HttpResponseServerError(reason=str(e)) diff --git a/project/threads/api.py b/project/threads/api.py index 5500b35a2..064ecb87e 100644 --- a/project/threads/api.py +++ b/project/threads/api.py @@ -11,7 +11,6 @@ from django.contrib.auth import get_user_model from django.http import ( JsonResponse, - HttpResponse, HttpResponseServerError, HttpResponseForbidden, HttpResponseBadRequest, @@ -352,12 +351,11 @@ def delete_civi(request): c = Civi.objects.get(id=civi_id) if request.user.username != c.author.username: - return HttpResponseBadRequest(reason="No Edit Rights") + return JsonResponse({"error": "No Edit Rights"}, status=400) try: c.delete() - - return HttpResponse("Success") + return JsonResponse({"result": "Success"}) except Exception as e: return HttpResponseServerError(reason=str(e)) @@ -374,7 +372,7 @@ def edit_thread(request): is_draft = request.POST.get("is_draft", True) if not thread_id: - return HttpResponseBadRequest(reason="Invalid Thread Reference") + return JsonResponse({"error": "Invalid Thread Reference"}, status=400) # for some reason this is not cast to boolean in the request if is_draft == "false": @@ -397,8 +395,9 @@ def edit_thread(request): req_edit_thread.save() except Thread.DoesNotExist: - return HttpResponseServerError( - reason=f"Thread with id:{thread_id} does not exist" + return JsonResponse( + {"error": f"Thread with id:{thread_id} does not exist"}, + status=400, ) except Exception as e: return HttpResponseServerError(reason=str(e)) From 245024bc2a4a0da0a3322ec76597ba8dbba8ff0a Mon Sep 17 00:00:00 2001 From: gorkemarslan Date: Thu, 11 Nov 2021 19:08:06 +0300 Subject: [PATCH 8/8] Update accounts/api.py --- project/accounts/api.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/project/accounts/api.py b/project/accounts/api.py index 819ae7300..817cc9f22 100644 --- a/project/accounts/api.py +++ b/project/accounts/api.py @@ -286,26 +286,27 @@ def edit_user(request): @login_required def upload_profile_image(request): """This function is used to allow users to upload profile photos""" + if request.method == "POST": form = UpdateProfileImage(request.POST, request.FILES) if form.is_valid(): try: - account = Profile.objects.get(user=request.user) + profile = Profile.objects.get(user=request.user) # Clean up previous image - account.profile_image.delete() + profile.profile_image.delete() # Upload new image and set as profile picture - account.profile_image = form.clean_profile_image() + profile.profile_image = form.clean_profile_image() try: - account.save() + profile.save() except Exception as e: response = {"message": str(e), "error": "MODEL_SAVE_ERROR"} return JsonResponse(response, status=400) - request.session["login_user_image"] = account.profile_image_thumb_url + request.session["login_user_image"] = profile.profile_image_thumb_url - response = {"profile_image": account.profile_image_url} + response = {"profile_image": profile.profile_image_url} return JsonResponse(response, status=200) except get_user_model().DoesNotExist: @@ -328,6 +329,7 @@ def upload_profile_image(request): @login_required def clear_profile_image(request): """This function is used to delete a profile image""" + if request.method == "POST": try: account = Profile.objects.get(user=request.user) @@ -363,12 +365,15 @@ def request_follow(request): :return: (200, okay, list of friend information) (400, bad lookup) (500, error) """ - if request.user.username == request.POST.get("target", -1): - return HttpResponseBadRequest(reason="You cannot follow yourself, silly!") + + target_username = request.POST.get("target", -1) + if request.user.username == target_username: + response = {"error": "You cannot follow yourself, silly!"} + return JsonResponse(response, status=400) try: account = Profile.objects.get(user=request.user) - target = get_user_model().objects.get(username=request.POST.get("target", -1)) + target = get_user_model().objects.get(username=target_username) target_account = Profile.objects.get(user=target) account.following.add(target_account)