From f56a70fd4c706066011edc2e7e8681c7cae61cd4 Mon Sep 17 00:00:00 2001 From: Thomas Ngulube <47449914+Porcupine1@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:07:17 -0400 Subject: [PATCH 1/8] add club diff feature --- backend/clubs/models.py | 25 +++++++++++++++++++++++++ backend/clubs/views.py | 11 +++++++++++ 2 files changed, 36 insertions(+) diff --git a/backend/clubs/models.py b/backend/clubs/models.py index daf7f5590..01d124243 100644 --- a/backend/clubs/models.py +++ b/backend/clubs/models.py @@ -698,6 +698,31 @@ def send_approval_email(self, request=None, change=False): reply_to=settings.OSA_EMAILS + [settings.BRANDING_SITE_EMAIL], ) + def get_latest_and_latest_approved_versions(self): + """ + Returns the latest and latest approved versions of club key fields + [name, description, image] contents that trigger reapproval. + """ + latest_approved_version = ( + self.history.filter(approved=True).order_by("-history_date").first() + ) + latest_version = self.history.order_by("-history_date").first() + + if ( + not latest_version + or not latest_approved_version + or (latest_approved_version == latest_version) + ): + return {} + + diff = latest_version.diff_against(latest_approved_version) + + return { + change.field: {"old": change.old, "new": change.new} + for change in diff.changes + if change.field in ["name", "description", "image"] + } + class Meta: ordering = ["name"] permissions = [ diff --git a/backend/clubs/views.py b/backend/clubs/views.py index 7d1b53afd..4042fa4e7 100644 --- a/backend/clubs/views.py +++ b/backend/clubs/views.py @@ -2058,6 +2058,17 @@ def perform_destroy(self, instance): context=context, ) + @action(detail=False, methods=["GET"]) + def pending_clubs(self, request, *args, **kwargs): + """ + Return old and new data for clubs that are pending approval. + """ + pending_clubs = Club.objects.filter(approved=None, active=True) + + return Response( + [club.get_latest_and_latest_approved_versions() for club in pending_clubs] + ) + @action(detail=False, methods=["GET"]) def fields(self, request, *args, **kwargs): """ From c89fc81cb38d42c360bc6c1997df738da6d9d200 Mon Sep 17 00:00:00 2001 From: Thomas Ngulube <47449914+Porcupine1@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:54:35 -0400 Subject: [PATCH 2/8] add YAML doc --- backend/clubs/views.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/backend/clubs/views.py b/backend/clubs/views.py index 4042fa4e7..f0293af54 100644 --- a/backend/clubs/views.py +++ b/backend/clubs/views.py @@ -2062,6 +2062,47 @@ def perform_destroy(self, instance): def pending_clubs(self, request, *args, **kwargs): """ Return old and new data for clubs that are pending approval. + --- + responses: + "200": + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: object + description: Changes in the name field + properties: + old: + type: string + description: Old name of the club + new: + type: string + description: New name of the club + description: + type: object + description: Changes in the club description + properties: + old: + type: string + description: Old description of the club + new: + type: string + description: New description of the club + image: + type: object + description: Changes in the image of the club + properties: + old: + type: string + description: Old image URL of the club + new: + type: string + description: New image URL of the club + --- """ pending_clubs = Club.objects.filter(approved=None, active=True) From 7dd0ad12ab7389ab978e6bbba79fa85969a9408c Mon Sep 17 00:00:00 2001 From: Thomas Ngulube <47449914+Porcupine1@users.noreply.github.com> Date: Fri, 5 Jul 2024 22:41:35 -0400 Subject: [PATCH 3/8] address nits + add tests --- backend/clubs/models.py | 32 ++++++++------ backend/clubs/views.py | 72 ++++++++++++++++++------------- backend/tests/clubs/test_views.py | 60 ++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 43 deletions(-) diff --git a/backend/clubs/models.py b/backend/clubs/models.py index 01d124243..ddaecd858 100644 --- a/backend/clubs/models.py +++ b/backend/clubs/models.py @@ -708,20 +708,26 @@ def get_latest_and_latest_approved_versions(self): ) latest_version = self.history.order_by("-history_date").first() - if ( - not latest_version - or not latest_approved_version - or (latest_approved_version == latest_version) - ): - return {} - - diff = latest_version.diff_against(latest_approved_version) + # if this is the first time the club is being approved + if not latest_approved_version: + return { + self.code: { + field: {"old": None, "new": getattr(latest_version, field)} + for field in ["name", "description", "image"] + } + } - return { - change.field: {"old": change.old, "new": change.new} - for change in diff.changes - if change.field in ["name", "description", "image"] - } + # if the current version is not approvedthe and it has been approved before + if not self.approved and latest_approved_version: + return { + self.code: { + field: { + "old": getattr(latest_approved_version, field), + "new": getattr(latest_version, field), + } + for field in ["name", "description", "image"] + } + } class Meta: ordering = ["name"] diff --git a/backend/clubs/views.py b/backend/clubs/views.py index f0293af54..f438530dd 100644 --- a/backend/clubs/views.py +++ b/backend/clubs/views.py @@ -2059,7 +2059,7 @@ def perform_destroy(self, instance): ) @action(detail=False, methods=["GET"]) - def pending_clubs(self, request, *args, **kwargs): + def pending_clubs_old_new_data(self, request, *args, **kwargs): """ Return old and new data for clubs that are pending approval. --- @@ -2072,36 +2072,48 @@ def pending_clubs(self, request, *args, **kwargs): items: type: object properties: - name: - type: object - description: Changes in the name field - properties: - old: - type: string - description: Old name of the club - new: - type: string - description: New name of the club - description: - type: object - description: Changes in the club description - properties: - old: - type: string - description: Old description of the club - new: - type: string - description: New description of the club - image: - type: object - description: Changes in the image of the club + id: + type: object + description: club id properties: - old: - type: string - description: Old image URL of the club - new: - type: string - description: New image URL of the club + name: + type: object + description: Changes in the name field + properties: + old: + type: string + description: Old name of the + club + new: + type: string + description: New name of the + club + description: + type: object + description: Changes in the club + description + properties: + old: + type: string + description: Old description of + the club + new: + type: string + description: New description of + the club + image: + type: object + description: Changes in the image of the + club + properties: + old: + type: string + description: Old image URL of + the club + new: + type: string + description: New image URL of + the club --- """ pending_clubs = Club.objects.filter(approved=None, active=True) diff --git a/backend/tests/clubs/test_views.py b/backend/tests/clubs/test_views.py index 0750c611b..20ce0d13d 100644 --- a/backend/tests/clubs/test_views.py +++ b/backend/tests/clubs/test_views.py @@ -1306,6 +1306,66 @@ def test_club_list_filter(self): codes = [club["code"] for club in data] self.assertEqual(set(codes), set(query["results"]), (query, resp.content)) + def test_pending_clubs_old_new_data(self): + """ + Test that pending_clubs_old_new returns the correct data for new and clubs + seeking reapproval + """ + + # create club that requires approval + new_club = Club.objects.create( + code="new-club", + name="New Club", + description="We're the spirits of open source.", + approved=None, + active=True, + email="example@example.com", + ) + + # add officer to club + Membership.objects.create( + person=self.user4, club=new_club, role=Membership.ROLE_OFFICER + ) + + # login to officer user + self.client.login(username=self.user4.username, password="test") + + # New club should not have any old data + resp = self.client.get(reverse("clubs-pending-clubs-old-new-data")) + data = json.loads(resp.content.decode("utf-8")) + self.assertEqual(data[0]["new-club"]["name"]["new"], "New Club") + self.assertEqual(data[0]["new-club"]["name"]["old"], None) + + # approve club + new_club.approved = True + new_club.save(update_fields=["approved"]) + self.assertTrue(new_club.approved) + + # Make a change that edit description (requires reapproval) + resp = self.client.patch( + reverse("clubs-detail", args=(new_club.code,)), + {"description": "We are open source, expect us."}, + content_type="application/json", + ) + # Ensure change successful + self.assertIn(resp.status_code, [200, 201], resp.content) + + # Ensure club is marked as not approved + new_club.refresh_from_db() + self.assertFalse(new_club.approved) + + # Should now have old and new data + resp = self.client.get(reverse("clubs-pending-clubs-old-new-data")) + new_data = json.loads(resp.content.decode("utf-8")) + self.assertEqual( + new_data[0]["new-club"]["description"]["new"], + "We are open source, expect us.", + ) + self.assertEqual( + new_data[0]["new-club"]["description"]["old"], + "We're the spirits of open source.", + ) + def test_club_modify_wrong_auth(self): """ Outsiders should not be able to modify a club. From d82c835c9a693e4deca65488276969f20091abe7 Mon Sep 17 00:00:00 2001 From: Thomas Ngulube <47449914+Porcupine1@users.noreply.github.com> Date: Fri, 5 Jul 2024 23:26:05 -0400 Subject: [PATCH 4/8] address yaml error --- backend/clubs/views.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/backend/clubs/views.py b/backend/clubs/views.py index f438530dd..98ae5df12 100644 --- a/backend/clubs/views.py +++ b/backend/clubs/views.py @@ -2072,9 +2072,9 @@ def pending_clubs_old_new_data(self, request, *args, **kwargs): items: type: object properties: - id: - type: object - description: club id + code: + type: object + description: club code properties: name: type: object @@ -2082,38 +2082,38 @@ def pending_clubs_old_new_data(self, request, *args, **kwargs): properties: old: type: string - description: Old name of the - club + description: > + Old name of the club new: type: string - description: New name of the - club + description: > + New name of the club description: type: object - description: Changes in the club - description + description: > + Changes in the club description properties: old: type: string - description: Old description of - the club + description: > + Old description of the club new: type: string - description: New description of - the club + description: > + New description of the club image: type: object - description: Changes in the image of the - club + description: > + Changes in the image of the club properties: old: type: string - description: Old image URL of - the club + description: > + Old image URL of the club new: type: string - description: New image URL of - the club + description: > + New image URL of the club --- """ pending_clubs = Club.objects.filter(approved=None, active=True) From 01ccd5f833af1dbbeb15753d7f1f6212d0b014d7 Mon Sep 17 00:00:00 2001 From: Thomas Ngulube <47449914+Porcupine1@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:26:39 -0400 Subject: [PATCH 5/8] address requested changes --- backend/clubs/models.py | 31 -------- backend/clubs/views.py | 128 ++++++++++++++++++------------ backend/tests/clubs/test_views.py | 17 ++-- 3 files changed, 84 insertions(+), 92 deletions(-) diff --git a/backend/clubs/models.py b/backend/clubs/models.py index ddaecd858..daf7f5590 100644 --- a/backend/clubs/models.py +++ b/backend/clubs/models.py @@ -698,37 +698,6 @@ def send_approval_email(self, request=None, change=False): reply_to=settings.OSA_EMAILS + [settings.BRANDING_SITE_EMAIL], ) - def get_latest_and_latest_approved_versions(self): - """ - Returns the latest and latest approved versions of club key fields - [name, description, image] contents that trigger reapproval. - """ - latest_approved_version = ( - self.history.filter(approved=True).order_by("-history_date").first() - ) - latest_version = self.history.order_by("-history_date").first() - - # if this is the first time the club is being approved - if not latest_approved_version: - return { - self.code: { - field: {"old": None, "new": getattr(latest_version, field)} - for field in ["name", "description", "image"] - } - } - - # if the current version is not approvedthe and it has been approved before - if not self.approved and latest_approved_version: - return { - self.code: { - field: { - "old": getattr(latest_approved_version, field), - "new": getattr(latest_version, field), - } - for field in ["name", "description", "image"] - } - } - class Meta: ordering = ["name"] permissions = [ diff --git a/backend/clubs/views.py b/backend/clubs/views.py index 98ae5df12..ea38bd441 100644 --- a/backend/clubs/views.py +++ b/backend/clubs/views.py @@ -2058,69 +2058,93 @@ def perform_destroy(self, instance): context=context, ) - @action(detail=False, methods=["GET"]) - def pending_clubs_old_new_data(self, request, *args, **kwargs): + @action(detail=True, methods=["GET"]) + def diff(self, request, *args, **kwargs): """ - Return old and new data for clubs that are pending approval. + Return old and new data for a club that is pending approval. --- responses: "200": content: application/json: schema: - type: array - items: - type: object - properties: - code: - type: object - description: club code - properties: - name: - type: object - description: Changes in the name field - properties: - old: - type: string - description: > - Old name of the club - new: - type: string - description: > - New name of the club - description: - type: object - description: > - Changes in the club description - properties: - old: - type: string - description: > - Old description of the club - new: - type: string - description: > - New description of the club - image: - type: object - description: > - Changes in the image of the club - properties: - old: - type: string - description: > - Old image URL of the club - new: - type: string - description: > - New image URL of the club + type: object + properties: + club_code: + type: object + description: club code + properties: + name: + type: object + description: Changes in the name field + properties: + old: + type: string + description: > + Old name of the club + new: + type: string + description: > + New name of the club + description: + type: object + description: > + Changes in the club description + properties: + old: + type: string + description: > + Old description of the club + new: + type: string + description: > + New description of the club + image: + type: object + description: > + Changes in the image of the club + properties: + old: + type: string + description: > + Old image URL of the club + new: + type: string + description: > + New image URL of the club --- """ - pending_clubs = Club.objects.filter(approved=None, active=True) + club = self.get_object() - return Response( - [club.get_latest_and_latest_approved_versions() for club in pending_clubs] + latest_approved_version = ( + club.history.filter(approved=True).order_by("-history_date").first() ) + latest_version = club.history.order_by("-history_date").first() + + # if this is the first time the club is being approved + if not latest_approved_version: + return Response( + { + club.code: { + field: {"old": None, "new": getattr(latest_version, field)} + for field in ["name", "description", "image"] + } + } + ) + + # if the current version is not approvedthe and it has been approved before + if not club.approved and latest_approved_version: + return Response( + { + club.code: { + field: { + "old": getattr(latest_approved_version, field), + "new": getattr(latest_version, field), + } + for field in ["name", "description", "image"] + } + } + ) @action(detail=False, methods=["GET"]) def fields(self, request, *args, **kwargs): diff --git a/backend/tests/clubs/test_views.py b/backend/tests/clubs/test_views.py index 20ce0d13d..5f9a7230a 100644 --- a/backend/tests/clubs/test_views.py +++ b/backend/tests/clubs/test_views.py @@ -1306,10 +1306,9 @@ def test_club_list_filter(self): codes = [club["code"] for club in data] self.assertEqual(set(codes), set(query["results"]), (query, resp.content)) - def test_pending_clubs_old_new_data(self): + def test_diff(self): """ - Test that pending_clubs_old_new returns the correct data for new and clubs - seeking reapproval + Test that diff returns the correct old and new club for a club in approval queue """ # create club that requires approval @@ -1331,10 +1330,10 @@ def test_pending_clubs_old_new_data(self): self.client.login(username=self.user4.username, password="test") # New club should not have any old data - resp = self.client.get(reverse("clubs-pending-clubs-old-new-data")) + resp = self.client.get(reverse("clubs-diff", args=(new_club.code,))) data = json.loads(resp.content.decode("utf-8")) - self.assertEqual(data[0]["new-club"]["name"]["new"], "New Club") - self.assertEqual(data[0]["new-club"]["name"]["old"], None) + self.assertEqual(data["new-club"]["name"]["new"], "New Club") + self.assertEqual(data["new-club"]["name"]["old"], None) # approve club new_club.approved = True @@ -1355,14 +1354,14 @@ def test_pending_clubs_old_new_data(self): self.assertFalse(new_club.approved) # Should now have old and new data - resp = self.client.get(reverse("clubs-pending-clubs-old-new-data")) + resp = self.client.get(reverse("clubs-diff", args=(new_club.code,))) new_data = json.loads(resp.content.decode("utf-8")) self.assertEqual( - new_data[0]["new-club"]["description"]["new"], + new_data["new-club"]["description"]["new"], "We are open source, expect us.", ) self.assertEqual( - new_data[0]["new-club"]["description"]["old"], + new_data["new-club"]["description"]["old"], "We're the spirits of open source.", ) From 2ec546e2f9dc5b422337d262255749168b0ec886 Mon Sep 17 00:00:00 2001 From: Thomas Ngulube <47449914+Porcupine1@users.noreply.github.com> Date: Sun, 13 Oct 2024 05:16:10 -0400 Subject: [PATCH 6/8] add detail and list diff --- backend/clubs/views.py | 145 +++++++++++++++++++++++--- backend/tests/clubs/test_views.py | 168 ++++++++++++++++++++++++++++-- 2 files changed, 289 insertions(+), 24 deletions(-) diff --git a/backend/clubs/views.py b/backend/clubs/views.py index ea38bd441..51d6bfa52 100644 --- a/backend/clubs/views.py +++ b/backend/clubs/views.py @@ -47,8 +47,10 @@ ExpressionWrapper, F, Max, + OuterRef, Prefetch, Q, + Subquery, TextField, Value, When, @@ -2059,7 +2061,7 @@ def perform_destroy(self, instance): ) @action(detail=True, methods=["GET"]) - def diff(self, request, *args, **kwargs): + def club_detail_diff(self, request, *args, **kwargs): """ Return old and new data for a club that is pending approval. --- @@ -2117,28 +2119,30 @@ def diff(self, request, *args, **kwargs): club = self.get_object() latest_approved_version = ( - club.history.filter(approved=True).order_by("-history_date").first() + club.history.filter(approved=True) + .only("name", "description", "image") + .order_by("-history_date") + .first() ) - latest_version = club.history.order_by("-history_date").first() - # if this is the first time the club is being approved - if not latest_approved_version: - return Response( - { - club.code: { - field: {"old": None, "new": getattr(latest_version, field)} - for field in ["name", "description", "image"] - } - } - ) + latest_version = ( + club.history.only("name", "description", "image") + .order_by("-history_date") + .first() + ) - # if the current version is not approvedthe and it has been approved before - if not club.approved and latest_approved_version: + # if the current version is not approved, return a diff + # if the club has never be approved, for each field, it's old data is None + if not club.approved: return Response( { club.code: { field: { - "old": getattr(latest_approved_version, field), + "old": ( + getattr(latest_approved_version, field) + if latest_approved_version + else None + ), "new": getattr(latest_version, field), } for field in ["name", "description", "image"] @@ -2146,6 +2150,115 @@ def diff(self, request, *args, **kwargs): } ) + # if the current version is approved, no diff is returned + return Response({}) + + @action(detail=False, methods=["GET"]) + def club_list_diff(self, request, *args, **kwargs): + """ + Return old and new data for clubs that are pending approval. + --- + responses: + "200": + content: + application/json: + schema: + type: object + items: + type: object + properties: + code: + type: object + description: club code + properties: + name: + type: object + description: Changes in the name field + properties: + old: + type: string + description: > + Old name of the club + new: + type: string + description: > + New name of the club + description: + type: object + description: > + Changes in the club description + properties: + old: + type: string + description: > + Old description of the club + new: + type: string + description: > + New description of the club + image: + type: object + description: > + Changes in the image of the club + properties: + old: + type: string + description: > + Old image URL of the club + new: + type: string + description: > + New image URL of the club + --- + """ + pending_clubs = Club.objects.filter(approved=None, active=True).only( + "code", "name", "description", "image" + ) + + # write subqueries + latest_versions_subquery = ( + Club.history.filter(code=OuterRef("code")) + .order_by("-history_date") + .values("code")[:1] + ) + latest_approved_versions_subquery = ( + Club.history.filter(code=OuterRef("code"), approved=True) + .order_by("-history_date") + .values("code")[:1] + ) + + # get the latest versions of each club + latest_versions = Club.history.filter( + code__in=Subquery(latest_versions_subquery) + ).only("code", "name", "description", "image") + + # get the latest approved versions of each club + latest_approved_versions = Club.history.filter( + code__in=Subquery(latest_approved_versions_subquery), approved=True + ).only("code", "name", "description", "image") + + latest_versions_dict = {version.code: version for version in latest_versions} + latest_approved_versions_dict = { + version.code: version for version in latest_approved_versions + } + + return Response( + { + club.code: { + field: { + "old": ( + getattr(latest_approved_versions_dict[club.code], field) + if club.code in latest_approved_versions_dict + else None + ), + "new": getattr(latest_versions_dict[club.code], field), + } + for field in ["name", "description", "image"] + } + for club in pending_clubs + } + ) + @action(detail=False, methods=["GET"]) def fields(self, request, *args, **kwargs): """ diff --git a/backend/tests/clubs/test_views.py b/backend/tests/clubs/test_views.py index 5f9a7230a..a82a2ae77 100644 --- a/backend/tests/clubs/test_views.py +++ b/backend/tests/clubs/test_views.py @@ -1306,7 +1306,7 @@ def test_club_list_filter(self): codes = [club["code"] for club in data] self.assertEqual(set(codes), set(query["results"]), (query, resp.content)) - def test_diff(self): + def test_club_detail_diff(self): """ Test that diff returns the correct old and new club for a club in approval queue """ @@ -1330,7 +1330,7 @@ def test_diff(self): self.client.login(username=self.user4.username, password="test") # New club should not have any old data - resp = self.client.get(reverse("clubs-diff", args=(new_club.code,))) + resp = self.client.get(reverse("clubs-club-detail-diff", args=(new_club.code,))) data = json.loads(resp.content.decode("utf-8")) self.assertEqual(data["new-club"]["name"]["new"], "New Club") self.assertEqual(data["new-club"]["name"]["old"], None) @@ -1340,30 +1340,182 @@ def test_diff(self): new_club.save(update_fields=["approved"]) self.assertTrue(new_club.approved) - # Make a change that edit description (requires reapproval) - resp = self.client.patch( + # make multiple changes to club + resp1 = self.client.patch( reverse("clubs-detail", args=(new_club.code,)), {"description": "We are open source, expect us."}, content_type="application/json", ) # Ensure change successful - self.assertIn(resp.status_code, [200, 201], resp.content) + self.assertIn(resp1.status_code, [200, 201], resp1.content) + + resp1 = self.client.patch( + reverse("clubs-detail", args=(new_club.code,)), + {"name": "New Club 2"}, + content_type="application/json", + ) + # Ensure change successful + self.assertIn(resp1.status_code, [200, 201], resp1.content) + + resp1 = self.client.patch( + reverse("clubs-detail", args=(new_club.code,)), + {"description": "Expect us. Expect us."}, + content_type="application/json", + ) + # Ensure change successful + self.assertIn(resp1.status_code, [200, 201], resp1.content) # Ensure club is marked as not approved new_club.refresh_from_db() self.assertFalse(new_club.approved) # Should now have old and new data - resp = self.client.get(reverse("clubs-diff", args=(new_club.code,))) - new_data = json.loads(resp.content.decode("utf-8")) + resp2 = self.client.get( + reverse("clubs-club-detail-diff", args=(new_club.code,)) + ) + new_data = json.loads(resp2.content.decode("utf-8")) self.assertEqual( new_data["new-club"]["description"]["new"], - "We are open source, expect us.", + "Expect us. Expect us.", ) self.assertEqual( new_data["new-club"]["description"]["old"], "We're the spirits of open source.", ) + self.assertEqual( + new_data["new-club"]["name"]["new"], + "New Club 2", + ) + self.assertEqual( + new_data["new-club"]["name"]["old"], + "New Club", + ) + + # attempt to get of approved club + new_club.approved = True + new_club.save(update_fields=["approved"]) + self.assertTrue(new_club.approved) + resp3 = self.client.get( + reverse("clubs-club-detail-diff", args=(new_club.code,)) + ) + new_data1 = json.loads(resp3.content.decode("utf-8")) + self.assertEquals(new_data1, {}) + + def test_club_list_diff(self): + """ + Test that diff returns correct old and new data for all clubs pending approval. + """ + + # Create first club that requires approval + club1 = Club.objects.create( + code="club1", + name="Club 1", + description="This is the first club.", + approved=None, + active=True, + ) + + # Create second club that requires approval + club2 = Club.objects.create( + code="club2", + name="Club 2", + description="This is the second club.", + approved=None, + active=True, + ) + + # Create third club that is already approved + Club.objects.create( + code="club3", + name="Club 3", + description="This is the third club.", + approved=True, + active=True, + ) + + # Add officer to club 1 + Membership.objects.create( + person=self.user1, club=club1, role=Membership.ROLE_OFFICER + ) + + # Add officer to club 1 + Membership.objects.create( + person=self.user1, club=club2, role=Membership.ROLE_OFFICER + ) + + # officer login + self.client.login(username=self.user1.username, password="test") + + # Check that new clubs 1 and 2 have no old data + resp = self.client.get(reverse("clubs-club-list-diff")) + data = json.loads(resp.content.decode("utf-8")) + + # Check club1 + self.assertEqual(data["club1"]["name"]["new"], "Club 1") + self.assertEqual(data["club1"]["name"]["old"], None) + self.assertEqual(data["club1"]["description"]["new"], "This is the first club.") + self.assertEqual(data["club1"]["description"]["old"], None) + + # Check club2 + self.assertEqual(data["club2"]["name"]["new"], "Club 2") + self.assertEqual(data["club2"]["name"]["old"], None) + self.assertEqual( + data["club2"]["description"]["new"], "This is the second club." + ) + self.assertEqual(data["club2"]["description"]["old"], None) + + # club 3 should not be included, since currently approved + self.assertNotIn("club3", data) + + # Approve club1 + club1.approved = True + club1.save(update_fields=["approved"]) + self.assertTrue(club1.approved) + + # Make changes to club1 + resp1 = self.client.patch( + reverse("clubs-detail", args=(club1.code,)), + {"description": "updated description."}, + content_type="application/json", + ) + # Ensure change successful + self.assertIn(resp1.status_code, [200, 201], resp1.content) + + # Club1 should now require reapproval and diff should have old and new data + club1.refresh_from_db() + self.assertFalse(club1.approved) + + # Check diff again + resp2 = self.client.get(reverse("clubs-club-list-diff")) + new_data = json.loads(resp2.content.decode("utf-8")) + + # should not be equal since hasn't been approved + self.assertNotEqual( + new_data["club1"]["description"]["new"], "updated description." + ) + + # should has same data as before + self.assertEqual(data["club1"]["name"]["new"], "Club 1") + self.assertEqual(data["club1"]["name"]["old"], None) + self.assertEqual(data["club1"]["description"]["new"], "This is the first club.") + self.assertEqual(data["club1"]["description"]["old"], None) + + # Check that club2 remains in the pending list with no changes + self.assertEqual(new_data["club2"]["name"]["new"], "Club 2") + self.assertEqual(new_data["club2"]["name"]["old"], None) + + # Club3 should still not be included + self.assertNotIn("club3", new_data) + + # Approve club1 to remove from pending list + club1.approved = True + club1.save(update_fields=["approved"]) + resp3 = self.client.get(reverse("clubs-club-list-diff")) + new_data2 = json.loads(resp3.content.decode("utf-8")) + + # Check that club1 is no longer in the pending list + self.assertNotIn("club1", new_data2) + self.assertIn("club2", new_data2) def test_club_modify_wrong_auth(self): """ From 1c14e0a0d74b1a5460c9da48cb403248d0182c7b Mon Sep 17 00:00:00 2001 From: Thomas Ngulube <47449914+Porcupine1@users.noreply.github.com> Date: Sun, 13 Oct 2024 05:30:10 -0400 Subject: [PATCH 7/8] small changes --- backend/clubs/views.py | 96 +++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/backend/clubs/views.py b/backend/clubs/views.py index 51d6bfa52..d9aa2edf4 100644 --- a/backend/clubs/views.py +++ b/backend/clubs/views.py @@ -2159,56 +2159,54 @@ def club_list_diff(self, request, *args, **kwargs): Return old and new data for clubs that are pending approval. --- responses: - "200": - content: - application/json: - schema: - type: object - items: + "200": + content: + application/json: + schema: type: object - properties: - code: - type: object - description: club code - properties: - name: - type: object - description: Changes in the name field - properties: - old: - type: string - description: > - Old name of the club - new: - type: string - description: > - New name of the club - description: - type: object - description: > - Changes in the club description - properties: - old: - type: string - description: > - Old description of the club - new: - type: string - description: > - New description of the club - image: - type: object - description: > - Changes in the image of the club - properties: - old: - type: string - description: > - Old image URL of the club - new: - type: string - description: > - New image URL of the club + description: > + "Response object where the keys are club codes" + additionalProperties: + type: object + description: > + "Details of the changes for a particular club" + properties: + name: + type: object + description: "Changes in the name field" + properties: + old: + type: string + description: "Old name of the club" + new: + type: string + description: "New name of the club" + description: + type: object + description: > + "Changes in the club description" + properties: + old: + type: string + description: > + "Old description of the club" + new: + type: string + description: > + "New description of the club" + image: + type: object + description: > + "Changes in the image of the club" + properties: + old: + type: string + description: > + "Old image URL of the club" + new: + type: string + description: > + "New image URL of the club" --- """ pending_clubs = Club.objects.filter(approved=None, active=True).only( From 1724ec791a5ca4bbd2cf94ef8479087089b6a5ac Mon Sep 17 00:00:00 2001 From: Thomas Ngulube <47449914+Porcupine1@users.noreply.github.com> Date: Sun, 13 Oct 2024 05:38:40 -0400 Subject: [PATCH 8/8] small changes --- backend/clubs/views.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/backend/clubs/views.py b/backend/clubs/views.py index d9aa2edf4..4ee45c0f0 100644 --- a/backend/clubs/views.py +++ b/backend/clubs/views.py @@ -2159,54 +2159,54 @@ def club_list_diff(self, request, *args, **kwargs): Return old and new data for clubs that are pending approval. --- responses: - "200": - content: - application/json: - schema: - type: object - description: > - "Response object where the keys are club codes" - additionalProperties: + "200": + content: + application/json: + schema: + type: object + properties: + club_code: type: object - description: > - "Details of the changes for a particular club" + description: club code properties: name: type: object - description: "Changes in the name field" + description: Changes in the name field properties: old: type: string - description: "Old name of the club" + description: > + Old name of the club new: type: string - description: "New name of the club" + description: > + New name of the club description: type: object description: > - "Changes in the club description" + Changes in the club description properties: old: type: string description: > - "Old description of the club" + Old description of the club new: type: string description: > - "New description of the club" + New description of the club image: type: object description: > - "Changes in the image of the club" + Changes in the image of the club properties: old: type: string description: > - "Old image URL of the club" + Old image URL of the club new: type: string description: > - "New image URL of the club" + New image URL of the club --- """ pending_clubs = Club.objects.filter(approved=None, active=True).only(