From 3880930f4dfec700465941037e6bd149ee87f638 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Fri, 15 Mar 2024 17:11:25 -0400 Subject: [PATCH 01/15] Add drafting to sublet model --- backend/sublet/models.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/sublet/models.py b/backend/sublet/models.py index e391053a..31011663 100644 --- a/backend/sublet/models.py +++ b/backend/sublet/models.py @@ -33,6 +33,7 @@ class Sublet(models.Model): sublettees = models.ManyToManyField( User, through=Offer, related_name="sublets_offered", blank=True ) + is_draft = models.BooleanField(default=True) favorites = models.ManyToManyField(User, related_name="sublets_favorited", blank=True) amenities = models.ManyToManyField(Amenity, blank=True) @@ -42,12 +43,12 @@ class Sublet(models.Model): baths = models.DecimalField(max_digits=3, decimal_places=1, null=True, blank=True) description = models.TextField(null=True, blank=True) external_link = models.URLField(max_length=255, null=True, blank=True) - price = models.IntegerField() + price = models.IntegerField(null=True, blank=True) negotiable = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) - expires_at = models.DateTimeField() - start_date = models.DateField() - end_date = models.DateField() + expires_at = models.DateTimeField(null=True, blank=True) + start_date = models.DateField(null=True, blank=True) + end_date = models.DateField(null=True, blank=True) def __str__(self): return f"{self.title} by {self.subletter}" From 0c233302cedf4a125b8d574cc696049990ac2c5b Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Fri, 15 Mar 2024 17:42:07 -0400 Subject: [PATCH 02/15] Add draft field validation --- backend/sublet/serializers.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index 549e9e2b..d1a3b16e 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -61,6 +61,22 @@ class SubletSerializer(serializers.ModelSerializer): many=True, queryset=Amenity.objects.all(), required=False ) + def validate_not_draft(self, validated_data): + if not validated_data["is_draft"]: + # check that certain fields are there + fields = [ + "title", + "address", + "price", + "negotiable", + "start_date", + "end_date", + "expires_at", + ] + + if bad_fields := [field for field in fields if not validated_data[field]]: + raise serializers.ValidationError(f"{', '.join(bad_fields)} are required to publish sublet.") + class Meta: model = Sublet read_only_fields = [ @@ -72,7 +88,7 @@ class Meta: ] fields = [ "id", - "subletter", + "is_draft", "amenities", "title", "address", @@ -93,6 +109,7 @@ class Meta: def create(self, validated_data): validated_data["subletter"] = self.context["request"].user + self.validate_not_draft(validated_data) instance = super().create(validated_data) instance.save() return instance @@ -104,6 +121,7 @@ def update(self, instance, validated_data): self.context["request"].user == instance.subletter or self.context["request"].user.is_superuser ): + self.validate_not_draft(validated_data) instance = super().update(instance, validated_data) instance.save() return instance From beaf30d270cf12d9cd5912ee03d916b03a3e5909 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Sun, 17 Mar 2024 13:34:26 -0400 Subject: [PATCH 03/15] Fix validations for sublet draft status --- ...is_draft_alter_sublet_end_date_and_more.py | 38 +++++++++++++++++++ backend/sublet/serializers.py | 8 ++-- 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 backend/sublet/migrations/0005_sublet_is_draft_alter_sublet_end_date_and_more.py diff --git a/backend/sublet/migrations/0005_sublet_is_draft_alter_sublet_end_date_and_more.py b/backend/sublet/migrations/0005_sublet_is_draft_alter_sublet_end_date_and_more.py new file mode 100644 index 00000000..d6c32e65 --- /dev/null +++ b/backend/sublet/migrations/0005_sublet_is_draft_alter_sublet_end_date_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.0.2 on 2024-03-15 22:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("sublet", "0004_alter_sublet_external_link"), + ] + + operations = [ + migrations.AddField( + model_name="sublet", + name="is_draft", + field=models.BooleanField(default=True), + ), + migrations.AlterField( + model_name="sublet", + name="end_date", + field=models.DateField(blank=True, null=True), + ), + migrations.AlterField( + model_name="sublet", + name="expires_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AlterField( + model_name="sublet", + name="price", + field=models.IntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name="sublet", + name="start_date", + field=models.DateField(blank=True, null=True), + ), + ] diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index d1a3b16e..47e6bd4c 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -1,6 +1,5 @@ from phonenumber_field.serializerfields import PhoneNumberField from rest_framework import serializers - from sublet.models import Amenity, Offer, Sublet, SubletImage @@ -75,7 +74,9 @@ def validate_not_draft(self, validated_data): ] if bad_fields := [field for field in fields if not validated_data[field]]: - raise serializers.ValidationError(f"{', '.join(bad_fields)} are required to publish sublet.") + raise serializers.ValidationError( + f"{', '.join(bad_fields)} are required to publish sublet." + ) class Meta: model = Sublet @@ -121,7 +122,8 @@ def update(self, instance, validated_data): self.context["request"].user == instance.subletter or self.context["request"].user.is_superuser ): - self.validate_not_draft(validated_data) + if instance.is_draft: + self.validate_not_draft(validated_data) instance = super().update(instance, validated_data) instance.save() return instance From 671e10abe6027d6b9324618fe3492a928ea9fb09 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Sun, 17 Mar 2024 13:36:37 -0400 Subject: [PATCH 04/15] Lint --- ...et_is_draft_alter_sublet_end_date_and_more.py | 16 ++++------------ backend/sublet/serializers.py | 1 + 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/backend/sublet/migrations/0005_sublet_is_draft_alter_sublet_end_date_and_more.py b/backend/sublet/migrations/0005_sublet_is_draft_alter_sublet_end_date_and_more.py index d6c32e65..ef9d9068 100644 --- a/backend/sublet/migrations/0005_sublet_is_draft_alter_sublet_end_date_and_more.py +++ b/backend/sublet/migrations/0005_sublet_is_draft_alter_sublet_end_date_and_more.py @@ -11,14 +11,10 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( - model_name="sublet", - name="is_draft", - field=models.BooleanField(default=True), + model_name="sublet", name="is_draft", field=models.BooleanField(default=True), ), migrations.AlterField( - model_name="sublet", - name="end_date", - field=models.DateField(blank=True, null=True), + model_name="sublet", name="end_date", field=models.DateField(blank=True, null=True), ), migrations.AlterField( model_name="sublet", @@ -26,13 +22,9 @@ class Migration(migrations.Migration): field=models.DateTimeField(blank=True, null=True), ), migrations.AlterField( - model_name="sublet", - name="price", - field=models.IntegerField(blank=True, null=True), + model_name="sublet", name="price", field=models.IntegerField(blank=True, null=True), ), migrations.AlterField( - model_name="sublet", - name="start_date", - field=models.DateField(blank=True, null=True), + model_name="sublet", name="start_date", field=models.DateField(blank=True, null=True), ), ] diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index 47e6bd4c..69efec09 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -1,5 +1,6 @@ from phonenumber_field.serializerfields import PhoneNumberField from rest_framework import serializers + from sublet.models import Amenity, Offer, Sublet, SubletImage From af0f24789999592bd2118fe87e1f27af4d500a23 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Sun, 17 Mar 2024 13:39:55 -0400 Subject: [PATCH 05/15] Fix draft status implementation bug --- backend/sublet/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index 69efec09..976200d1 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -62,7 +62,7 @@ class SubletSerializer(serializers.ModelSerializer): ) def validate_not_draft(self, validated_data): - if not validated_data["is_draft"]: + if "is_draft" in validated_data and not validated_data["is_draft"]: # check that certain fields are there fields = [ "title", From 89b0d6a8aee6c043a9db50d65a9b65c9488a4f34 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Sun, 17 Mar 2024 21:39:34 -0400 Subject: [PATCH 06/15] Fix incorrect logic on draft validation --- backend/sublet/serializers.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index 976200d1..d814e7a5 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -61,7 +61,7 @@ class SubletSerializer(serializers.ModelSerializer): many=True, queryset=Amenity.objects.all(), required=False ) - def validate_not_draft(self, validated_data): + def validate_not_draft(self, validated_data, instance=None): if "is_draft" in validated_data and not validated_data["is_draft"]: # check that certain fields are there fields = [ @@ -73,8 +73,19 @@ def validate_not_draft(self, validated_data): "end_date", "expires_at", ] - - if bad_fields := [field for field in fields if not validated_data[field]]: + if bad_fields := [ + field + for field in fields + if (field in validated_data and not validated_data[field]) + or ( + field not in validated_data + and ( + instance is None + or not hasattr(instance, field) + or not getattr(instance, field) + ) + ) + ]: raise serializers.ValidationError( f"{', '.join(bad_fields)} are required to publish sublet." ) From c07867185636831bafbc256452518ed07c555fb6 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Sun, 17 Mar 2024 21:42:01 -0400 Subject: [PATCH 07/15] Minor fix to validation --- backend/sublet/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index d814e7a5..6bd96276 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -135,7 +135,7 @@ def update(self, instance, validated_data): or self.context["request"].user.is_superuser ): if instance.is_draft: - self.validate_not_draft(validated_data) + self.validate_not_draft(validated_data, instance) instance = super().update(instance, validated_data) instance.save() return instance From 3f3ca47ccfa2bf17fa49e8b6a2705738e28f2e7a Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Sun, 24 Mar 2024 00:34:55 -0400 Subject: [PATCH 08/15] Migrate drafting logic to use is_published instead --- ...ove_sublet_is_draft_sublet_is_published.py | 17 +++++ backend/sublet/models.py | 2 +- backend/sublet/serializers.py | 66 +++++++++---------- backend/tests/sublet/test_sublets.py | 6 ++ 4 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 backend/sublet/migrations/0006_remove_sublet_is_draft_sublet_is_published.py diff --git a/backend/sublet/migrations/0006_remove_sublet_is_draft_sublet_is_published.py b/backend/sublet/migrations/0006_remove_sublet_is_draft_sublet_is_published.py new file mode 100644 index 00000000..4e9b0bf5 --- /dev/null +++ b/backend/sublet/migrations/0006_remove_sublet_is_draft_sublet_is_published.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.2 on 2024-03-22 21:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("sublet", "0005_sublet_is_draft_alter_sublet_end_date_and_more"), + ] + + operations = [ + migrations.RemoveField(model_name="sublet", name="is_draft",), + migrations.AddField( + model_name="sublet", name="is_published", field=models.BooleanField(default=False), + ), + ] diff --git a/backend/sublet/models.py b/backend/sublet/models.py index 31011663..2364cbb6 100644 --- a/backend/sublet/models.py +++ b/backend/sublet/models.py @@ -33,7 +33,7 @@ class Sublet(models.Model): sublettees = models.ManyToManyField( User, through=Offer, related_name="sublets_offered", blank=True ) - is_draft = models.BooleanField(default=True) + is_published = models.BooleanField(default=False) favorites = models.ManyToManyField(User, related_name="sublets_favorited", blank=True) amenities = models.ManyToManyField(Amenity, blank=True) diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index 6bd96276..0fd61c52 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -61,34 +61,30 @@ class SubletSerializer(serializers.ModelSerializer): many=True, queryset=Amenity.objects.all(), required=False ) - def validate_not_draft(self, validated_data, instance=None): - if "is_draft" in validated_data and not validated_data["is_draft"]: - # check that certain fields are there - fields = [ - "title", - "address", - "price", - "negotiable", - "start_date", - "end_date", - "expires_at", - ] - if bad_fields := [ - field - for field in fields - if (field in validated_data and not validated_data[field]) - or ( - field not in validated_data - and ( - instance is None - or not hasattr(instance, field) - or not getattr(instance, field) - ) - ) - ]: - raise serializers.ValidationError( - f"{', '.join(bad_fields)} are required to publish sublet." - ) + def validate_publish(self, validated_data, instance=None): + fields = [ + "title", + "address", + "price", + "negotiable", + "start_date", + "end_date", + "expires_at", + ] + + def field_bad(field, validated_data, instance): + if field in validated_data: + if not validated_data[field]: + return True + else: + if not instance or not hasattr(instance, field) or not getattr(instance, field): + return True + return False + + if bad_fields := [field for field in fields if field_bad(field, validated_data, instance)]: + raise serializers.ValidationError( + f"{', '.join(bad_fields)} are required to publish sublet." + ) class Meta: model = Sublet @@ -101,7 +97,7 @@ class Meta: ] fields = [ "id", - "is_draft", + "is_published", "amenities", "title", "address", @@ -122,7 +118,8 @@ class Meta: def create(self, validated_data): validated_data["subletter"] = self.context["request"].user - self.validate_not_draft(validated_data) + if validated_data["is_published"]: + self.validate_publish(validated_data) instance = super().create(validated_data) instance.save() return instance @@ -134,8 +131,10 @@ def update(self, instance, validated_data): self.context["request"].user == instance.subletter or self.context["request"].user.is_superuser ): - if instance.is_draft: - self.validate_not_draft(validated_data, instance) + if ("is_published" in validated_data and validated_data["is_published"]) or ( + "is_published" not in validated_data and instance.is_published + ): + self.validate_publish(validated_data, instance) instance = super().update(instance, validated_data) instance.save() return instance @@ -164,7 +163,7 @@ class Meta: read_only_fields = ["id", "created_at", "subletter", "sublettees"] fields = [ "id", - "subletter", + "is_published", "amenities", "title", "address", @@ -192,6 +191,7 @@ class Meta: model = Sublet fields = [ "id", + "is_published", "subletter", "amenities", "title", diff --git a/backend/tests/sublet/test_sublets.py b/backend/tests/sublet/test_sublets.py index 38199c85..d3d383ce 100644 --- a/backend/tests/sublet/test_sublets.py +++ b/backend/tests/sublet/test_sublets.py @@ -49,9 +49,11 @@ def test_create_sublet(self): "start_date": "3000-04-09", "end_date": "3000-08-07", "amenities": ["Amenity1", "Amenity2"], + "is_published": True, } response = self.client.post("/sublet/properties/", payload) res_json = json.loads(response.content) + print(res_json) match_keys = [ "title", "address", @@ -65,6 +67,7 @@ def test_create_sublet(self): "start_date", "end_date", "amenities", + "is_published", ] [self.assertEqual(payload[key], res_json[key]) for key in match_keys] self.assertIn("id", res_json) @@ -99,6 +102,9 @@ def test_update_sublet(self): self.assertEqual("New Title", Sublet.objects.get(id=old_id).title) self.assertEqual("New Title", res_json["title"]) self.assertEqual(1, len(res_json["amenities"])) + payload["address"] = "" + payload["is_published"] = True + self.client.patch(f"/sublet/properties/{str(old_id)}/", payload) def test_browse_sublets(self): response = self.client.get("/sublet/properties/") From 2e3439d28890fdb50d2570276399a747633324c3 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Sun, 24 Mar 2024 01:41:40 -0400 Subject: [PATCH 09/15] Update testing code to be compatible with PyCharm Django test runner + coverage --- backend/sublet/serializers.py | 3 +++ backend/tests/portal/test_polls.py | 34 ++++++++++++++-------------- backend/tests/portal/test_posts.py | 12 +++++----- backend/tests/sublet/test_sublets.py | 1 - backend/utils/__init__.py | 0 5 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 backend/utils/__init__.py diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index 0fd61c52..904b8af7 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -163,6 +163,8 @@ class Meta: read_only_fields = ["id", "created_at", "subletter", "sublettees"] fields = [ "id", + "subletter", + "sublettees", "is_published", "amenities", "title", @@ -173,6 +175,7 @@ class Meta: "external_link", "price", "negotiable", + "created_at", "start_date", "end_date", "expires_at", diff --git a/backend/tests/portal/test_polls.py b/backend/tests/portal/test_polls.py index 71c1a33c..23995ffd 100644 --- a/backend/tests/portal/test_polls.py +++ b/backend/tests/portal/test_polls.py @@ -97,7 +97,7 @@ def setUp(self): poll.save() poll_1 = Poll.objects.get(question="How is your day") - self.id = poll_1.id + self.poll_id = poll_1.id @mock.patch("portal.serializers.get_user_clubs", mock_get_user_clubs) def test_create_poll(self): @@ -122,11 +122,11 @@ def test_update_poll(self): payload = { "question": "New question", } - response = self.client.patch(f"/portal/polls/{self.id}/", payload) + response = self.client.patch(f"/portal/polls/{self.poll_id}/", payload) res_json = json.loads(response.content) # asserts that the update worked - self.assertEqual(self.id, res_json["id"]) - self.assertEqual("New question", Poll.objects.get(id=self.id).question) + self.assertEqual(self.poll_id, res_json["id"]) + self.assertEqual("New question", Poll.objects.get(id=self.poll_id).question) @mock.patch("portal.serializers.get_user_clubs", mock_get_user_clubs) @mock.patch("portal.logic.get_user_info", mock_get_user_info) @@ -158,14 +158,14 @@ def test_null_user_info_browse(self): @mock.patch("portal.permissions.get_user_clubs", mock_get_user_clubs) @mock.patch("portal.logic.get_user_info", mock_get_user_info) def test_create_option(self): - payload_1 = {"poll": self.id, "choice": "yes!"} - payload_2 = {"poll": self.id, "choice": "no!"} + payload_1 = {"poll": self.poll_id, "choice": "yes!"} + payload_2 = {"poll": self.poll_id, "choice": "no!"} self.client.post("/portal/options/", payload_1) self.client.post("/portal/options/", payload_2) self.assertEqual(2, PollOption.objects.all().count()) # asserts options were created and were placed to right poll for poll_option in PollOption.objects.all(): - self.assertEqual(Poll.objects.get(id=self.id), poll_option.poll) + self.assertEqual(Poll.objects.get(id=self.poll_id), poll_option.poll) response = self.client.post("/portal/polls/browse/", {"id_hash": 1}) res_json = json.loads(response.content) self.assertEqual(2, len(res_json[0]["options"])) @@ -173,11 +173,11 @@ def test_create_option(self): @mock.patch("portal.permissions.get_user_clubs", mock_get_user_clubs) @mock.patch("portal.views.get_user_clubs", mock_get_user_clubs) def test_update_option(self): - payload_1 = {"poll": self.id, "choice": "yes!"} + payload_1 = {"poll": self.poll_id, "choice": "yes!"} response = self.client.post("/portal/options/", payload_1) res_json = json.loads(response.content) self.assertEqual("yes!", PollOption.objects.get(id=res_json["id"]).choice) - payload_2 = {"poll": self.id, "choice": "no!"} + payload_2 = {"poll": self.poll_id, "choice": "no!"} # checks that poll's option was changed self.client.patch(f'/portal/options/{res_json["id"]}/', payload_2) self.assertEqual("no!", PollOption.objects.get(id=res_json["id"]).choice) @@ -200,11 +200,11 @@ def test_review_poll(self): @mock.patch("portal.permissions.get_user_clubs", mock_get_user_clubs) @mock.patch("portal.logic.get_user_info", mock_get_user_info) def test_more_than_five_options(self): - payload_1 = {"poll": self.id, "choice": "1"} - payload_2 = {"poll": self.id, "choice": "2"} - payload_3 = {"poll": self.id, "choice": "3"} - payload_4 = {"poll": self.id, "choice": "4"} - payload_5 = {"poll": self.id, "choice": "5"} + payload_1 = {"poll": self.poll_id, "choice": "1"} + payload_2 = {"poll": self.poll_id, "choice": "2"} + payload_3 = {"poll": self.poll_id, "choice": "3"} + payload_4 = {"poll": self.poll_id, "choice": "4"} + payload_5 = {"poll": self.poll_id, "choice": "5"} self.client.post("/portal/options/", payload_1) self.client.post("/portal/options/", payload_2) self.client.post("/portal/options/", payload_3) @@ -213,17 +213,17 @@ def test_more_than_five_options(self): self.assertEqual(5, PollOption.objects.all().count()) # asserts options were created and were placed to right poll for poll_option in PollOption.objects.all(): - self.assertEqual(Poll.objects.get(id=self.id), poll_option.poll) + self.assertEqual(Poll.objects.get(id=self.poll_id), poll_option.poll) response = self.client.post("/portal/polls/browse/", {"id_hash": 1}) res_json = json.loads(response.content) self.assertEqual(5, len(res_json[0]["options"])) # adding more than 5 options to same poll should not be allowed - payload_6 = {"poll": self.id, "choice": "6"} + payload_6 = {"poll": self.poll_id, "choice": "6"} response = self.client.post("/portal/options/", payload_6) self.assertEqual(5, PollOption.objects.all().count()) def test_option_vote_view(self): - response = self.client.get(f"/portal/polls/{self.id}/option_view/") + response = self.client.get(f"/portal/polls/{self.poll_id}/option_view/") res_json = json.loads(response.content) self.assertEqual("pennlabs", res_json["club_code"]) # test that options key is in response diff --git a/backend/tests/portal/test_posts.py b/backend/tests/portal/test_posts.py index a4dd9d5a..ef3f9d48 100644 --- a/backend/tests/portal/test_posts.py +++ b/backend/tests/portal/test_posts.py @@ -58,7 +58,7 @@ def setUp(self): post_1 = Post.objects.all().first() post_1.status = Post.STATUS_APPROVED post_1.save() - self.id = post_1.id + self.post_id = post_1.id @mock.patch("portal.serializers.get_user_clubs", mock_get_user_clubs) def test_create_post(self): @@ -100,10 +100,10 @@ def test_fail_post(self): @mock.patch("portal.permissions.get_user_clubs", mock_get_user_clubs) def test_update_post(self): payload = {"title": "New Test Title 3"} - response = self.client.patch(f"/portal/posts/{self.id}/", payload) + response = self.client.patch(f"/portal/posts/{self.post_id}/", payload) res_json = json.loads(response.content) - self.assertEqual(self.id, res_json["id"]) - self.assertEqual("New Test Title 3", Post.objects.get(id=self.id).title) + self.assertEqual(self.post_id, res_json["id"]) + self.assertEqual("New Test Title 3", Post.objects.get(id=self.post_id).title) # since the user is not an admin, approved should be set to false after update self.assertEqual(Post.STATUS_DRAFT, res_json["status"]) @@ -113,9 +113,9 @@ def test_update_post_admin(self): admin = User.objects.create_superuser("admin@upenn.edu", "admin", "admin") self.client.force_authenticate(user=admin) payload = {"title": "New Test Title 3"} - response = self.client.patch(f"/portal/posts/{self.id}/", payload) + response = self.client.patch(f"/portal/posts/{self.post_id}/", payload) res_json = json.loads(response.content) - self.assertEqual(self.id, res_json["id"]) + self.assertEqual(self.post_id, res_json["id"]) self.assertEqual(Post.STATUS_APPROVED, res_json["status"]) @mock.patch("portal.serializers.get_user_clubs", mock_get_user_clubs) diff --git a/backend/tests/sublet/test_sublets.py b/backend/tests/sublet/test_sublets.py index d3d383ce..fa4f6491 100644 --- a/backend/tests/sublet/test_sublets.py +++ b/backend/tests/sublet/test_sublets.py @@ -53,7 +53,6 @@ def test_create_sublet(self): } response = self.client.post("/sublet/properties/", payload) res_json = json.loads(response.content) - print(res_json) match_keys = [ "title", "address", diff --git a/backend/utils/__init__.py b/backend/utils/__init__.py new file mode 100644 index 00000000..e69de29b From 8e0d48d82f41065e5aa9681c1fe90b6f36cb0752 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Sun, 24 Mar 2024 01:57:22 -0400 Subject: [PATCH 10/15] Add additional testing for sublet drafting --- backend/sublet/serializers.py | 2 +- backend/tests/sublet/test_sublets.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index 904b8af7..c8d036db 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -83,7 +83,7 @@ def field_bad(field, validated_data, instance): if bad_fields := [field for field in fields if field_bad(field, validated_data, instance)]: raise serializers.ValidationError( - f"{', '.join(bad_fields)} are required to publish sublet." + f"fields: {', '.join(bad_fields)} are required to publish sublet." ) class Meta: diff --git a/backend/tests/sublet/test_sublets.py b/backend/tests/sublet/test_sublets.py index fa4f6491..d69ba4d3 100644 --- a/backend/tests/sublet/test_sublets.py +++ b/backend/tests/sublet/test_sublets.py @@ -93,7 +93,7 @@ def test_update_sublet(self): response = self.client.post("/sublet/properties/", payload) old_id = json.loads(response.content)["id"] # Update the sublet using the serializer - data = {"title": "New Title", "beds": 3, "amenities": ["Amenity1"]} + data = {"title": "New Title", "beds": 3, "amenities": ["Amenity1"], "address": ""} response = self.client.patch(f"/sublet/properties/{str(old_id)}/", data) res_json = json.loads(response.content) self.assertEqual(3, res_json["beds"]) @@ -103,7 +103,13 @@ def test_update_sublet(self): self.assertEqual(1, len(res_json["amenities"])) payload["address"] = "" payload["is_published"] = True - self.client.patch(f"/sublet/properties/{str(old_id)}/", payload) + response = self.client.patch(f"/sublet/properties/{str(old_id)}/", payload) + self.assertEqual(400, response.status_code) + payload["is_published"] = False + response = self.client.patch(f"/sublet/properties/{str(old_id)}/", payload) + self.assertEqual(200, response.status_code) + response = self.client.patch(f"/sublet/properties/{str(old_id)}/", {"is_published": True}) + self.assertEqual(400, response.status_code) def test_browse_sublets(self): response = self.client.get("/sublet/properties/") From d79127cdb50c07f1f18fd6771ac5f4037a4f1538 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Sun, 24 Mar 2024 13:48:02 -0400 Subject: [PATCH 11/15] Clean up draft validation logic --- backend/sublet/serializers.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index c8d036db..72b9ad0f 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -72,16 +72,16 @@ def validate_publish(self, validated_data, instance=None): "expires_at", ] - def field_bad(field, validated_data, instance): - if field in validated_data: - if not validated_data[field]: - return True - else: - if not instance or not hasattr(instance, field) or not getattr(instance, field): - return True - return False - - if bad_fields := [field for field in fields if field_bad(field, validated_data, instance)]: + newest_fields = [ + (field, validated_data[field]) + if field in validated_data + else (field, getattr(instance, field)) + if instance and hasattr(instance, field) + else (field, None) + for field in fields + ] + + if bad_fields := [field[0] for field in newest_fields if not field[1]]: raise serializers.ValidationError( f"fields: {', '.join(bad_fields)} are required to publish sublet." ) From bdc4f4e2c14b04c14fb69dcf42cbc7b921700b90 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Sun, 24 Mar 2024 13:50:29 -0400 Subject: [PATCH 12/15] Update sublet viewing to only show sublets that are published when not browsing user's own sublets --- backend/sublet/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/sublet/views.py b/backend/sublet/views.py index 38623631..4545fb14 100644 --- a/backend/sublet/views.py +++ b/backend/sublet/views.py @@ -148,6 +148,7 @@ def list(self, request, *args, **kwargs): queryset = queryset.filter(subletter=request.user) else: queryset = queryset.filter(expires_at__gte=timezone.now()) + queryset = queryset.filter(is_published=True) if title: queryset = queryset.filter(title__icontains=title) if address: From 2cfb9dce4a9b0e0848f2817d0d1751af6911979f Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Wed, 27 Mar 2024 12:20:44 -0400 Subject: [PATCH 13/15] Fix tests --- backend/tests/sublet/test_sublets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/tests/sublet/test_sublets.py b/backend/tests/sublet/test_sublets.py index d69ba4d3..4556672a 100644 --- a/backend/tests/sublet/test_sublets.py +++ b/backend/tests/sublet/test_sublets.py @@ -128,6 +128,7 @@ def test_browse_sublets(self): "start_date": "3000-04-09", "end_date": "3000-08-07", "amenities": ["Amenity1", "Amenity2"], + "is_published": True, } response = self.client.post("/sublet/properties/", payload) old_id = json.loads(response.content)["id"] @@ -154,6 +155,7 @@ def test_browse_filtered(self): "start_date": "3000-04-09", "end_date": "3000-08-07", "amenities": ["Amenity1", "Amenity2"], + "is_published": True, } response = self.client.post("/sublet/properties/", payload) old_id = json.loads(response.content)["id"] From 49793f91496d42d43c6cef0dd8dbbdbb4e5e00d6 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Wed, 27 Mar 2024 12:43:56 -0400 Subject: [PATCH 14/15] Slightly clean up draft validation logic --- backend/sublet/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index 72b9ad0f..4b5c9b35 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -75,8 +75,8 @@ def validate_publish(self, validated_data, instance=None): newest_fields = [ (field, validated_data[field]) if field in validated_data - else (field, getattr(instance, field)) - if instance and hasattr(instance, field) + else (field, getattr(instance, field, None)) + if instance else (field, None) for field in fields ] From 88598615708c6711c2fbbfffd900f5eec187e589 Mon Sep 17 00:00:00 2001 From: Jesse Zong Date: Wed, 27 Mar 2024 22:38:15 -0400 Subject: [PATCH 15/15] Clean up draft validation logic further --- backend/sublet/serializers.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/sublet/serializers.py b/backend/sublet/serializers.py index 4b5c9b35..30d2e949 100644 --- a/backend/sublet/serializers.py +++ b/backend/sublet/serializers.py @@ -73,11 +73,16 @@ def validate_publish(self, validated_data, instance=None): ] newest_fields = [ - (field, validated_data[field]) - if field in validated_data - else (field, getattr(instance, field, None)) - if instance - else (field, None) + ( + field, + ( + validated_data[field] + if field in validated_data + else getattr(instance, field, None) + if instance + else None + ), + ) for field in fields ]