Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Sublet Drafting #271

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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),
),
]
Original file line number Diff line number Diff line change
@@ -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),
),
]
9 changes: 5 additions & 4 deletions backend/sublet/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Sublet(models.Model):
sublettees = models.ManyToManyField(
User, through=Offer, related_name="sublets_offered", blank=True
)
is_published = models.BooleanField(default=False)
favorites = models.ManyToManyField(User, related_name="sublets_favorited", blank=True)
amenities = models.ManyToManyField(Amenity, blank=True)

Expand All @@ -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}"
Expand Down
42 changes: 41 additions & 1 deletion backend/sublet/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,36 @@ class SubletSerializer(serializers.ModelSerializer):
many=True, queryset=Amenity.objects.all(), required=False
)

def validate_publish(self, validated_data, instance=None):
fields = [
"title",
"address",
"price",
"negotiable",
"start_date",
"end_date",
"expires_at",
]

newest_fields = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, a lot simpler and readable! :D

(
field,
(
validated_data[field]
if field in validated_data
else getattr(instance, field, None)
if instance
else None
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically i think u can also do validated_data.get(field) or getattr(instance, field, None)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Yep, confirmed! I think I like this better can we perhaps change it to that

)
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."
)

class Meta:
model = Sublet
read_only_fields = [
Expand All @@ -72,7 +102,7 @@ class Meta:
]
fields = [
"id",
"subletter",
"is_published",
"amenities",
"title",
"address",
Expand All @@ -93,6 +123,8 @@ class Meta:

def create(self, validated_data):
validated_data["subletter"] = self.context["request"].user
if validated_data["is_published"]:
self.validate_publish(validated_data)
instance = super().create(validated_data)
instance.save()
return instance
Expand All @@ -104,6 +136,10 @@ def update(self, instance, validated_data):
self.context["request"].user == instance.subletter
or self.context["request"].user.is_superuser
):
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
Expand Down Expand Up @@ -133,6 +169,8 @@ class Meta:
fields = [
"id",
"subletter",
"sublettees",
"is_published",
"amenities",
"title",
"address",
Expand All @@ -142,6 +180,7 @@ class Meta:
"external_link",
"price",
"negotiable",
"created_at",
"start_date",
"end_date",
"expires_at",
Expand All @@ -160,6 +199,7 @@ class Meta:
model = Sublet
fields = [
"id",
"is_published",
"subletter",
"amenities",
"title",
Expand Down
1 change: 1 addition & 0 deletions backend/sublet/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

if title:
queryset = queryset.filter(title__icontains=title)
if address:
Expand Down
34 changes: 17 additions & 17 deletions backend/tests/portal/test_polls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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)
Expand Down Expand Up @@ -158,26 +158,26 @@ 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"]))

@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)
Expand All @@ -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)
Expand All @@ -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
Expand Down
12 changes: 6 additions & 6 deletions backend/tests/portal/test_posts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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"])

Expand All @@ -113,9 +113,9 @@ def test_update_post_admin(self):
admin = User.objects.create_superuser("[email protected]", "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)
Expand Down
15 changes: 14 additions & 1 deletion backend/tests/sublet/test_sublets.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ 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)
Expand All @@ -65,6 +66,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)
Expand All @@ -91,14 +93,23 @@ 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"])
self.assertEqual(old_id, Sublet.objects.all().last().id)
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
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/")
Expand All @@ -117,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"]
Expand All @@ -143,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"]
Expand Down
Empty file added backend/utils/__init__.py
Empty file.
Loading