From 309b36ad395dca4bf6de70986df64b0df4d3f5e6 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 3 Feb 2025 10:26:44 +0000 Subject: [PATCH 1/7] Fix issue #184: Add timestamps to models V2 --- ...eated_at_collection_updated_at_and_more.py | 67 +++++++++++++++++++ photo/models.py | 11 +++ 2 files changed, 78 insertions(+) create mode 100644 photo/migrations/0005_collection_created_at_collection_updated_at_and_more.py diff --git a/photo/migrations/0005_collection_created_at_collection_updated_at_and_more.py b/photo/migrations/0005_collection_created_at_collection_updated_at_and_more.py new file mode 100644 index 0000000..7886fb5 --- /dev/null +++ b/photo/migrations/0005_collection_created_at_collection_updated_at_and_more.py @@ -0,0 +1,67 @@ +# Generated by Django 4.2.8 on 2025-02-03 10:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("photo", "0004_alter_contest_prize"), + ] + + operations = [ + migrations.AddField( + model_name="collection", + name="created_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="collection", + name="updated_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="contest", + name="created_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="contest", + name="updated_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="contestsubmission", + name="created_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="contestsubmission", + name="updated_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="picture", + name="created_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="picture", + name="updated_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="picturecomment", + name="updated_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="user", + name="created_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="user", + name="updated_at", + field=models.DateTimeField(blank=True, null=True), + ), + ] diff --git a/photo/models.py b/photo/models.py index 85d4a52..c5bfc42 100644 --- a/photo/models.py +++ b/photo/models.py @@ -48,6 +48,8 @@ def create_superuser(self, email, password=None, **kwargs): class SoftDeleteModel(models.Model): is_deleted = models.BooleanField(default=False) + created_at = models.DateTimeField(null=True, blank=True) + updated_at = models.DateTimeField(null=True, blank=True) objects = SoftDeleteManager() all_objects = models.Manager() @@ -60,6 +62,12 @@ def restore(self): self.is_deleted = False self.save() + def save(self, *args, **kwargs): + if not self.created_at: + self.created_at = timezone.now() + self.updated_at = timezone.now() + super().save(*args, **kwargs) + class Meta: abstract = True @@ -106,6 +114,9 @@ def validate_profile_picture(self): def save(self, *args, **kwargs): self.validate_profile_picture() + if not self.created_at: + self.created_at = timezone.now() + self.updated_at = timezone.now() super(User, self).save(*args, **kwargs) From 0d9d0160e86bc23bf963bc7a04ecd4db752f951c Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 3 Feb 2025 10:33:00 +0000 Subject: [PATCH 2/7] Fix pr #185: Fix issue #184: Add timestamps to models V2 --- photo/tests/test_queries/test_picture_comment.py | 10 +++------- photo/types.py | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/photo/tests/test_queries/test_picture_comment.py b/photo/tests/test_queries/test_picture_comment.py index 12ed20c..9336f3e 100644 --- a/photo/tests/test_queries/test_picture_comment.py +++ b/photo/tests/test_queries/test_picture_comment.py @@ -22,13 +22,9 @@ def test_query_success(self): self.assertEqual(result.errors, None) self.assertEqual(len(result.data["picture_comments"]), self.batch_size) - self.assertEqual( - sorted( - [key for key in result.data["picture_comments"][0].keys()] - + ["is_deleted"] - ), - sorted([field.name for field in PictureComment._meta.fields]), - ) + expected_fields = sorted([field.name for field in PictureComment._meta.fields]) + actual_fields = sorted([key for key in result.data["picture_comments"][0].keys()] + ["is_deleted"]) + self.assertEqual(actual_fields, expected_fields) def test_filter_by_id(self): picture_comment = PictureCommentFactory.create() diff --git a/photo/types.py b/photo/types.py index e63affc..afb976b 100644 --- a/photo/types.py +++ b/photo/types.py @@ -41,6 +41,7 @@ class PictureCommentType: picture: "PictureType" text: str created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.django.type(Collection) From 33281aee88b447e4cdd5c6c1d0fd08428940791d Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 3 Feb 2025 10:55:59 +0000 Subject: [PATCH 3/7] Fix pr #185: Fix issue #184: Add timestamps to models V2 --- photo/types.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/photo/types.py b/photo/types.py index afb976b..4bd28f3 100644 --- a/photo/types.py +++ b/photo/types.py @@ -23,6 +23,8 @@ class UserType: profile_picture: "PictureType" profile_picture_updated_at: strawberry.auto user_handle: str + created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.django.type(Picture) @@ -32,6 +34,8 @@ class PictureType: name: str file: str likes: List[UserType] + created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.django.type(PictureComment) @@ -50,6 +54,8 @@ class CollectionType: name: str user: "UserType" pictures: List[PictureType] + created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.django.type(Contest) @@ -68,6 +74,8 @@ class ContestType: winners: List[UserType] created_by: "UserType" status: str + created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.field def status(self) -> str: @@ -93,6 +101,8 @@ class ContestSubmissionType: picture: PictureType submission_date: strawberry.auto votes: List[UserType] + created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.type @@ -100,6 +110,8 @@ class AddVoteMutationResponse: success: bool results: ContestSubmissionType | None errors: str + created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.type @@ -107,6 +119,8 @@ class CreatePictureMutationResponse: success: bool results: PictureType errors: str + created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.type @@ -114,6 +128,8 @@ class CreateContestSubmissiomMutationResponse: success: bool results: ContestSubmissionType errors: str + created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.type @@ -121,6 +137,8 @@ class AddLikeMutationResponse: success: bool results: PictureType errors: str + created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.type @@ -128,6 +146,8 @@ class CollectionAddPictureMutationResponse: success: bool results: CollectionType errors: str + created_at: strawberry.auto + updated_at: strawberry.auto @strawberry.type @@ -135,3 +155,5 @@ class CloseContestMutationResponse: success: bool results: ContestType errors: str + created_at: strawberry.auto + updated_at: strawberry.auto From 746cad9c6b1bf6451e5bbb9c20478a5f900b93f8 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 3 Feb 2025 11:03:35 +0000 Subject: [PATCH 4/7] Fix pr #185: Fix issue #184: Add timestamps to models V2 --- photo/tests/test_queries/graphql_queries.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/photo/tests/test_queries/graphql_queries.py b/photo/tests/test_queries/graphql_queries.py index 3a1875f..5b8e05d 100644 --- a/photo/tests/test_queries/graphql_queries.py +++ b/photo/tests/test_queries/graphql_queries.py @@ -11,6 +11,8 @@ pictures { id } + created_at + updated_at } } """ @@ -55,6 +57,8 @@ id } status + created_at + updated_at } } """ @@ -131,6 +135,8 @@ votes { email } + created_at + updated_at } } """ @@ -169,6 +175,8 @@ likes { email } + created_at + updated_at } } """ @@ -200,6 +208,7 @@ } text created_at + updated_at } } """ From b39737746485765f322095bda7e0726f46d138f7 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 3 Feb 2025 12:14:37 +0000 Subject: [PATCH 5/7] Fix pr #185: Fix issue #184: Add timestamps to models V2 --- photo/types.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/photo/types.py b/photo/types.py index 4bd28f3..9f83605 100644 --- a/photo/types.py +++ b/photo/types.py @@ -110,8 +110,6 @@ class AddVoteMutationResponse: success: bool results: ContestSubmissionType | None errors: str - created_at: strawberry.auto - updated_at: strawberry.auto @strawberry.type @@ -119,8 +117,6 @@ class CreatePictureMutationResponse: success: bool results: PictureType errors: str - created_at: strawberry.auto - updated_at: strawberry.auto @strawberry.type @@ -128,8 +124,6 @@ class CreateContestSubmissiomMutationResponse: success: bool results: ContestSubmissionType errors: str - created_at: strawberry.auto - updated_at: strawberry.auto @strawberry.type @@ -137,8 +131,6 @@ class AddLikeMutationResponse: success: bool results: PictureType errors: str - created_at: strawberry.auto - updated_at: strawberry.auto @strawberry.type @@ -146,8 +138,6 @@ class CollectionAddPictureMutationResponse: success: bool results: CollectionType errors: str - created_at: strawberry.auto - updated_at: strawberry.auto @strawberry.type @@ -155,5 +145,3 @@ class CloseContestMutationResponse: success: bool results: ContestType errors: str - created_at: strawberry.auto - updated_at: strawberry.auto From f8b3bc400b08229e2be728b7bafd566409b38f03 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 3 Feb 2025 15:35:13 +0000 Subject: [PATCH 6/7] Fix pr #185: Fix issue #184: Add timestamps to models V2 --- photo/tests/test_database/test_timestamps.py | 152 +++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 photo/tests/test_database/test_timestamps.py diff --git a/photo/tests/test_database/test_timestamps.py b/photo/tests/test_database/test_timestamps.py new file mode 100644 index 0000000..439718d --- /dev/null +++ b/photo/tests/test_database/test_timestamps.py @@ -0,0 +1,152 @@ +from django.test import TransactionTestCase +from django.utils import timezone +from datetime import timedelta + +from photo.models import Collection, Contest, ContestSubmission, Picture, PictureComment, User +from photo.tests.factories import ( + CollectionFactory, + ContestFactory, + ContestSubmissionFactory, + PictureFactory, + PictureCommentFactory, + UserFactory, +) + + +class TimestampFieldsTest(TransactionTestCase): + def setUp(self): + self.user = UserFactory() + self.picture = PictureFactory(user=self.user) + self.collection = CollectionFactory(user=self.user) + self.contest = ContestFactory(created_by=self.user) + self.submission = ContestSubmissionFactory( + contest=self.contest, + picture=self.picture + ) + self.comment = PictureCommentFactory( + user=self.user, + picture=self.picture + ) + + def test_created_at_on_creation(self): + """Test that created_at is set on object creation""" + now = timezone.now() + + # Test for each model + self.assertIsNotNone(self.user.created_at) + self.assertLess(self.user.created_at, now) + + self.assertIsNotNone(self.picture.created_at) + self.assertLess(self.picture.created_at, now) + + self.assertIsNotNone(self.collection.created_at) + self.assertLess(self.collection.created_at, now) + + self.assertIsNotNone(self.contest.created_at) + self.assertLess(self.contest.created_at, now) + + self.assertIsNotNone(self.submission.created_at) + self.assertLess(self.submission.created_at, now) + + self.assertIsNotNone(self.comment.created_at) + self.assertLess(self.comment.created_at, now) + + def test_updated_at_on_creation(self): + """Test that updated_at is set on object creation""" + now = timezone.now() + + # Test for each model + self.assertIsNotNone(self.user.updated_at) + self.assertLess(self.user.updated_at, now) + + self.assertIsNotNone(self.picture.updated_at) + self.assertLess(self.picture.updated_at, now) + + self.assertIsNotNone(self.collection.updated_at) + self.assertLess(self.collection.updated_at, now) + + self.assertIsNotNone(self.contest.updated_at) + self.assertLess(self.contest.updated_at, now) + + self.assertIsNotNone(self.submission.updated_at) + self.assertLess(self.submission.updated_at, now) + + self.assertIsNotNone(self.comment.updated_at) + self.assertLess(self.comment.updated_at, now) + + def test_updated_at_on_update(self): + """Test that updated_at is updated when object is modified""" + # Store initial timestamps + user_updated = self.user.updated_at + picture_updated = self.picture.updated_at + collection_updated = self.collection.updated_at + contest_updated = self.contest.updated_at + submission_updated = self.submission.updated_at + comment_updated = self.comment.updated_at + + # Wait a bit to ensure timestamps will be different + timezone.sleep(timedelta(milliseconds=10)) + + # Update each object + self.user.name_first = "Updated" + self.user.save() + + self.picture.name = "Updated Picture" + self.picture.save() + + self.collection.name = "Updated Collection" + self.collection.save() + + self.contest.title = "Updated Contest" + self.contest.save() + + self.submission.submission_date = timezone.now() + self.submission.save() + + self.comment.text = "Updated Comment" + self.comment.save() + + # Verify updated_at was changed + self.assertGreater(self.user.updated_at, user_updated) + self.assertGreater(self.picture.updated_at, picture_updated) + self.assertGreater(self.collection.updated_at, collection_updated) + self.assertGreater(self.contest.updated_at, contest_updated) + self.assertGreater(self.submission.updated_at, submission_updated) + self.assertGreater(self.comment.updated_at, comment_updated) + + def test_created_at_unchanged_on_update(self): + """Test that created_at remains unchanged when object is modified""" + # Store initial timestamps + user_created = self.user.created_at + picture_created = self.picture.created_at + collection_created = self.collection.created_at + contest_created = self.contest.created_at + submission_created = self.submission.created_at + comment_created = self.comment.created_at + + # Update each object + self.user.name_first = "Updated" + self.user.save() + + self.picture.name = "Updated Picture" + self.picture.save() + + self.collection.name = "Updated Collection" + self.collection.save() + + self.contest.title = "Updated Contest" + self.contest.save() + + self.submission.submission_date = timezone.now() + self.submission.save() + + self.comment.text = "Updated Comment" + self.comment.save() + + # Verify created_at was not changed + self.assertEqual(self.user.created_at, user_created) + self.assertEqual(self.picture.created_at, picture_created) + self.assertEqual(self.collection.created_at, collection_created) + self.assertEqual(self.contest.created_at, contest_created) + self.assertEqual(self.submission.created_at, submission_created) + self.assertEqual(self.comment.created_at, comment_created) From 6d6bbcced89ef34f27a51ad57359d32c34de5cc8 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 3 Feb 2025 15:42:06 +0000 Subject: [PATCH 7/7] Fix pr #185: Fix issue #184: Add timestamps to models V2 --- photo/tests/test_database/test_timestamps.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/photo/tests/test_database/test_timestamps.py b/photo/tests/test_database/test_timestamps.py index 439718d..17a6c3b 100644 --- a/photo/tests/test_database/test_timestamps.py +++ b/photo/tests/test_database/test_timestamps.py @@ -1,6 +1,7 @@ from django.test import TransactionTestCase from django.utils import timezone from datetime import timedelta +import time from photo.models import Collection, Contest, ContestSubmission, Picture, PictureComment, User from photo.tests.factories import ( @@ -85,7 +86,7 @@ def test_updated_at_on_update(self): comment_updated = self.comment.updated_at # Wait a bit to ensure timestamps will be different - timezone.sleep(timedelta(milliseconds=10)) + time.sleep(0.01) # 10 milliseconds # Update each object self.user.name_first = "Updated"