diff --git a/README.md b/README.md index 8b2e2f90e..817dcf89e 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,9 @@ This codebase was originally forked from [mysociety/yournextrepresentative](http://github.com/mysociety/yournextrepresentative) We no longer track the upstream but we thank [mySociety](http://mysociety.org/) for their work on the project which we have been able to build on. + +# API Versions + +v0.9 is legacy code and is now frozen. v1.0 is currently in alpha. We plan on publishing a v1 API once we have some more feedback from users and we think it’s stable enough. + + \ No newline at end of file diff --git a/ynr/apps/api/next/filters.py b/ynr/apps/api/next/filters.py new file mode 100644 index 000000000..0a029d19b --- /dev/null +++ b/ynr/apps/api/next/filters.py @@ -0,0 +1,10 @@ +from django_filters import filterset, filters + + +class LastUpdatedMixin(filterset.FilterSet): + last_updated = filters.IsoDateTimeFilter( + field_name="modified", + lookup_expr="gt", + label="Last updated", + help_text="An ISO datetime", + ) diff --git a/ynr/apps/api/next/serializers.py b/ynr/apps/api/next/serializers.py index 85b3cc03f..46660a798 100644 --- a/ynr/apps/api/next/serializers.py +++ b/ynr/apps/api/next/serializers.py @@ -23,10 +23,11 @@ class OrganizationSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = popolo_models.Organization - fields = ("url", "name", "slug") + fields = ("url", "name", "slug", "last_updated", "created") url = serializers.HyperlinkedIdentityField( view_name="organization-detail", lookup_field="slug", lookup_url_kwarg="slug", ) + last_updated = serializers.DateTimeField(source="modified") diff --git a/ynr/apps/api/next/views.py b/ynr/apps/api/next/views.py index 287b5d392..f6a668064 100644 --- a/ynr/apps/api/next/views.py +++ b/ynr/apps/api/next/views.py @@ -3,10 +3,11 @@ from rest_framework import pagination, viewsets import candidates.api.next.serializers -from api.next import serializers +import api.next.serializers from candidates import models as extra_models -from candidates.filters import LoggedActionAPIFilter from popolo.models import Organization +from candidates.filters import LoggedActionAPIFilter +from popolo.api.next.filters import OrganizationFilter def parse_date(date_text): @@ -33,8 +34,9 @@ class OrganizationViewSet(viewsets.ReadOnlyModelViewSet): .exclude(classification="Party") ) lookup_field = "slug" - serializer_class = serializers.OrganizationSerializer + serializer_class = api.next.serializers.OrganizationSerializer pagination_class = ResultsSetPagination + filterset_class = OrganizationFilter class LoggedActionViewSet(viewsets.ReadOnlyModelViewSet): diff --git a/ynr/apps/api/templates/api/next_home.html b/ynr/apps/api/templates/api/next_home.html index 83c7474eb..5fd52409b 100644 --- a/ynr/apps/api/templates/api/next_home.html +++ b/ynr/apps/api/templates/api/next_home.html @@ -1,5 +1,5 @@ {% extends "api/api-base.html" %} -{% load staticfiles %} +{% load static %} {% load api_docs %} {% load markdown_deux_tags %} {% block api_page_title %} diff --git a/ynr/apps/api/v09/serializers.py b/ynr/apps/api/v09/serializers.py index df44223a8..d1d8faa19 100644 --- a/ynr/apps/api/v09/serializers.py +++ b/ynr/apps/api/v09/serializers.py @@ -1,3 +1,4 @@ +# v0.9 is legacy code from rest_framework import serializers from rest_framework.fields import JSONField from sorl_thumbnail_serializer.fields import HyperlinkedSorlImageField diff --git a/ynr/apps/api/v09/views.py b/ynr/apps/api/v09/views.py index 54d9ef613..891d4f0d3 100644 --- a/ynr/apps/api/v09/views.py +++ b/ynr/apps/api/v09/views.py @@ -1,3 +1,4 @@ +# v0.9 is legacy code import json import subprocess import sys @@ -222,7 +223,7 @@ def get_queryset(self): if date_qs: date = parser.parse(date_qs) queryset = queryset.filter( - Q(updated_at__gte=date) | Q(memberships__updated_at__gte=date) + Q(modified__gte=date) | Q(memberships__modified__gte=date) ) return queryset diff --git a/ynr/apps/candidates/migrations/0070_auto_20210617_1506.py b/ynr/apps/candidates/migrations/0070_auto_20210617_1506.py new file mode 100644 index 000000000..e8a89a2ea --- /dev/null +++ b/ynr/apps/candidates/migrations/0070_auto_20210617_1506.py @@ -0,0 +1,37 @@ +# Generated by Django 2.2.20 on 2021-06-17 14:06 + +from django.db import migrations +import django.utils.timezone +import django_extensions.db.fields + + +class Migration(migrations.Migration): + + dependencies = [("candidates", "0069_ballot_voting_system")] + + operations = [ + migrations.AlterModelOptions( + name="ballot", + options={ + "get_latest_by": "modified", + "ordering": ("-modified", "-created"), + }, + ), + migrations.AddField( + model_name="ballot", + name="created", + field=django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, + default=django.utils.timezone.now, + verbose_name="created", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="ballot", + name="modified", + field=django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name="modified" + ), + ), + ] diff --git a/ynr/apps/candidates/migrations/0071_add_created_data.py b/ynr/apps/candidates/migrations/0071_add_created_data.py new file mode 100644 index 000000000..0ad1548e8 --- /dev/null +++ b/ynr/apps/candidates/migrations/0071_add_created_data.py @@ -0,0 +1,25 @@ +# Generated by Django 2.2.20 on 2021-06-17 14:08 + +from django.db import migrations + +from elections.helpers import four_weeks_before_election_date + + +def add_created_date(apps, schema_editor): + Ballot = apps.get_model("candidates", "Ballot") + for ballot in Ballot.objects.select_related("election").iterator(): + ballot.created = four_weeks_before_election_date( + election=ballot.election + ) + ballot.save() + + +class Migration(migrations.Migration): + + dependencies = [("candidates", "0070_auto_20210617_1506")] + + operations = [ + migrations.RunPython( + code=add_created_date, reverse_code=migrations.RunPython.noop + ) + ] diff --git a/ynr/apps/candidates/models/popolo_extra.py b/ynr/apps/candidates/models/popolo_extra.py index 69bc8143a..025b32b60 100644 --- a/ynr/apps/candidates/models/popolo_extra.py +++ b/ynr/apps/candidates/models/popolo_extra.py @@ -8,6 +8,7 @@ from django.utils import timezone from django.utils.html import mark_safe from django.utils.functional import cached_property +from django_extensions.db.models import TimeStampedModel from candidates.models.auth import TRUSTED_TO_LOCK_GROUP_NAME from elections.models import Election @@ -148,7 +149,7 @@ def no_results(self): ).distinct() -class Ballot(models.Model): +class Ballot(TimeStampedModel, models.Model): VOTING_SYSTEM_FPTP = "FPTP" diff --git a/ynr/apps/elections/helpers.py b/ynr/apps/elections/helpers.py index 0fd2fffb3..7616ab115 100644 --- a/ynr/apps/elections/helpers.py +++ b/ynr/apps/elections/helpers.py @@ -1,4 +1,5 @@ from functools import update_wrapper +from django.utils import timezone from candidates.models import Ballot @@ -33,3 +34,19 @@ def __call__(self, request, *args, **kwargs): self.__name__ = self.__qualname__ = view.__name__ return view(request, *args, **kwargs) + + +def four_weeks_before_election_date(election): + """ + Takes an election object and a datetime object four weeks prior to the + election date. + Used in data migrations where we want to set a realistic created timestamp + on old objects. + """ + election_date = election.election_date - timezone.timedelta(weeks=4) + return timezone.datetime( + election_date.year, + election_date.month, + election_date.day, + tzinfo=timezone.utc, + ) diff --git a/ynr/apps/elections/tests/test_helpers.py b/ynr/apps/elections/tests/test_helpers.py new file mode 100644 index 000000000..6e2d20f20 --- /dev/null +++ b/ynr/apps/elections/tests/test_helpers.py @@ -0,0 +1,12 @@ +from django.utils import timezone + +from elections.models import Election +from elections import helpers + + +class TestHelpers: + def test_four_weeks_before_election_date(self): + election = Election(election_date=timezone.datetime(2021, 5, 6).date()) + expected = timezone.datetime(2021, 4, 8, 0, 0, 0, tzinfo=timezone.utc) + result = helpers.four_weeks_before_election_date(election=election) + assert result == expected diff --git a/ynr/apps/parties/api/next/api_views.py b/ynr/apps/parties/api/next/api_views.py index e6462319f..b4d36a3c5 100644 --- a/ynr/apps/parties/api/next/api_views.py +++ b/ynr/apps/parties/api/next/api_views.py @@ -5,6 +5,7 @@ from django.views.decorators.cache import cache_page from django.views import View from rest_framework import viewsets +from parties.api.next.filters import PartyFilter from api.helpers import DefaultPageNumberPagination @@ -25,6 +26,7 @@ class PartyViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = PartySerializer lookup_field = "ec_id" pagination_class = DefaultPageNumberPagination + filterset_class = PartyFilter def retrieve(self, request, *args, **kwargs): """ diff --git a/ynr/apps/parties/api/next/filters.py b/ynr/apps/parties/api/next/filters.py new file mode 100644 index 000000000..c10208de8 --- /dev/null +++ b/ynr/apps/parties/api/next/filters.py @@ -0,0 +1,8 @@ +from parties.models import Party +from api.next.filters import LastUpdatedMixin + + +class PartyFilter(LastUpdatedMixin): + class Meta: + model = Party + fields = ["last_updated"] diff --git a/ynr/apps/parties/api/next/serializers.py b/ynr/apps/parties/api/next/serializers.py index e7819e851..274bcc2e9 100644 --- a/ynr/apps/parties/api/next/serializers.py +++ b/ynr/apps/parties/api/next/serializers.py @@ -51,6 +51,8 @@ class Meta: "emblems", "descriptions", "legacy_slug", + "created", + "last_updated", ) swagger_schema_fields = {"description": model.__doc__} @@ -60,6 +62,7 @@ class Meta: default_emblem = DefaultPartyEmblemSerializer(read_only=True) emblems = PartyEmblemSerializer(many=True, read_only=True) descriptions = PartyDescriptionSerializer(many=True, read_only=True) + last_updated = serializers.DateTimeField(source="modified") class PartyRegisterSerializer(serializers.Serializer): diff --git a/ynr/apps/people/api/next/api_views.py b/ynr/apps/people/api/next/api_views.py index 67ff3dfc7..971fa1c46 100644 --- a/ynr/apps/people/api/next/api_views.py +++ b/ynr/apps/people/api/next/api_views.py @@ -10,6 +10,7 @@ import people.api.next.serializers from api.next.views import ResultsSetPagination +from people.api.next.filters import PersonFilter from candidates import models as extra_models from candidates.api.next.serializers import LoggedActionSerializer from people.models import Person, PersonImage @@ -39,7 +40,7 @@ def get_queryset(self): if date_qs: date = parser.parse(date_qs) queryset = queryset.filter( - Q(updated_at__gte=date) | Q(memberships__updated_at__gte=date) + Q(modified__gte=date) | Q(memberships__modified__gte=date) ) return queryset @@ -88,6 +89,7 @@ def versions(self, request, pk=None, **kwargs): serializer_class = people.api.next.serializers.PersonSerializer pagination_class = ResultsSetPagination + filterset_class = PersonFilter class PersonRedirectViewSet(viewsets.ReadOnlyModelViewSet): diff --git a/ynr/apps/people/api/next/filters.py b/ynr/apps/people/api/next/filters.py new file mode 100644 index 000000000..64d51be28 --- /dev/null +++ b/ynr/apps/people/api/next/filters.py @@ -0,0 +1,8 @@ +from people.models import Person +from api.next.filters import LastUpdatedMixin + + +class PersonFilter(LastUpdatedMixin): + class Meta: + model = Person + fields = ["last_updated"] diff --git a/ynr/apps/people/api/next/serializers.py b/ynr/apps/people/api/next/serializers.py index 3f222fd7f..183da0f45 100644 --- a/ynr/apps/people/api/next/serializers.py +++ b/ynr/apps/people/api/next/serializers.py @@ -54,6 +54,8 @@ class Meta: model = popolo_models.ContactDetail fields = ("contact_type", "label", "note", "value") + last_updated = serializers.DateTimeField(source="modified") + class SourceSerializer(serializers.ModelSerializer): class Meta: @@ -83,6 +85,7 @@ class Meta: "url", "versions_url", "history_url", + "created", "last_updated", "honorific_prefix", "name", @@ -107,7 +110,7 @@ class Meta: history_url = serializers.HyperlinkedIdentityField( view_name="person-history" ) - last_updated = serializers.DateTimeField(source="updated_at") + last_updated = serializers.DateTimeField(source="modified") identifiers = PersonIdentifierSerializer( many=True, read_only=True, source="tmp_person_identifiers" ) diff --git a/ynr/apps/people/merging.py b/ynr/apps/people/merging.py index bc4893e5a..ff394f4b4 100644 --- a/ynr/apps/people/merging.py +++ b/ynr/apps/people/merging.py @@ -93,8 +93,8 @@ class PersonMerger: ("duplicate_suggestion_other_person", "merge_duplicate_suggestion"), # Discarded ("id", "discard_data"), - ("created_at", "discard_data"), - ("updated_at", "discard_data"), + ("created", "discard_data"), + ("modified", "discard_data"), ("edit_limitations", "discard_data"), ("sources", "discard_data"), ) diff --git a/ynr/apps/people/migrations/0030_auto_20210616_1642.py b/ynr/apps/people/migrations/0030_auto_20210616_1642.py new file mode 100644 index 000000000..c45356362 --- /dev/null +++ b/ynr/apps/people/migrations/0030_auto_20210616_1642.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.20 on 2021-06-16 15:42 + +from django.db import migrations +import django.utils.timezone +import django_extensions.db.fields + + +class Migration(migrations.Migration): + + dependencies = [("people", "0029_default_versions_to_list")] + + operations = [ + migrations.AddField( + model_name="person", + name="created", + field=django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, + default=django.utils.timezone.now, + verbose_name="created", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="person", + name="modified", + field=django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name="modified" + ), + ), + ] diff --git a/ynr/apps/people/migrations/0031_copy_created.py b/ynr/apps/people/migrations/0031_copy_created.py new file mode 100644 index 000000000..05700baa6 --- /dev/null +++ b/ynr/apps/people/migrations/0031_copy_created.py @@ -0,0 +1,26 @@ +# Generated by Django 2.2.20 on 2021-06-16 15:59 + +from django.db import migrations + + +def copy_created_at(apps, schema_editor): + Person = apps.get_model("people", "Person") + for person in Person.objects.iterator(): + person.created = person.created_at + person.save() + + +def copy_created(apps, schema_editor): + Person = apps.get_model("people", "Person") + for person in Person.objects.iterator(): + person.created_at = person.created + person.save() + + +class Migration(migrations.Migration): + + dependencies = [("people", "0030_auto_20210616_1642")] + + operations = [ + migrations.RunPython(code=copy_created_at, reverse_code=copy_created) + ] diff --git a/ynr/apps/people/migrations/0032_auto_20210616_1700.py b/ynr/apps/people/migrations/0032_auto_20210616_1700.py new file mode 100644 index 000000000..c12d01d17 --- /dev/null +++ b/ynr/apps/people/migrations/0032_auto_20210616_1700.py @@ -0,0 +1,13 @@ +# Generated by Django 2.2.20 on 2021-06-16 16:00 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [("people", "0031_copy_created")] + + operations = [ + migrations.RemoveField(model_name="person", name="created_at"), + migrations.RemoveField(model_name="person", name="updated_at"), + ] diff --git a/ynr/apps/people/models.py b/ynr/apps/people/models.py index b7b3a47c1..aaf75aade 100644 --- a/ynr/apps/people/models.py +++ b/ynr/apps/people/models.py @@ -32,7 +32,6 @@ PersonImageManager, PersonQuerySet, ) -from popolo.behaviors.models import Timestampable from popolo.models import Membership, VersionNotFound @@ -181,7 +180,7 @@ def get_value_html(self): return format_html(text, self.value) -class Person(Timestampable, models.Model): +class Person(TimeStampedModel, models.Model): """ A real person, alive or dead see schema at http://popoloproject.com/schemas/person.json# diff --git a/ynr/apps/popolo/api/next/filters.py b/ynr/apps/popolo/api/next/filters.py new file mode 100644 index 000000000..27b8e083c --- /dev/null +++ b/ynr/apps/popolo/api/next/filters.py @@ -0,0 +1,8 @@ +from popolo.models import Organization +from api.next.filters import LastUpdatedMixin + + +class OrganizationFilter(LastUpdatedMixin): + class Meta: + model = Organization + fields = ["last_updated"] diff --git a/ynr/apps/popolo/api/next/serializers.py b/ynr/apps/popolo/api/next/serializers.py index 38346f78e..196e5a30b 100644 --- a/ynr/apps/popolo/api/next/serializers.py +++ b/ynr/apps/popolo/api/next/serializers.py @@ -23,11 +23,12 @@ class MinimalPostSerializer(serializers.ModelSerializer): class Meta: model = popolo_models.Post ref_name = None # Tells swagger that this is always embedded - fields = ("id", "label", "slug") + fields = ("id", "label", "slug", "created", "last_updated") id = serializers.ReadOnlyField(source="identifier") slug = serializers.ReadOnlyField() label = serializers.ReadOnlyField() + last_updated = serializers.DateTimeField(source="modified") class BallotOnCandidacySerializer(serializers.HyperlinkedModelSerializer): @@ -58,6 +59,7 @@ class Meta: "party", "party_name", "party_description_text", + "created", ] CANDIDACY_ON_BALLOT_FIELDS = BASE_CANDIDACY_FIELDS + ["person", "result"] diff --git a/ynr/apps/popolo/behaviors/models.py b/ynr/apps/popolo/behaviors/models.py index 60173b1ff..19ec6b606 100644 --- a/ynr/apps/popolo/behaviors/models.py +++ b/ynr/apps/popolo/behaviors/models.py @@ -5,7 +5,6 @@ from django.core.exceptions import ValidationError from django.core.validators import RegexValidator from django.db import models -from model_utils.fields import AutoCreatedField, AutoLastModifiedField __author__ = "guglielmo" @@ -74,16 +73,3 @@ class Dateframeable(models.Model): class Meta: abstract = True - - -class Timestampable(models.Model): - """ - An abstract base class model that provides self-updating - ``created`` and ``modified`` fields. - """ - - created_at = AutoCreatedField("creation time") - updated_at = AutoLastModifiedField("last modification time") - - class Meta: - abstract = True diff --git a/ynr/apps/popolo/behaviors/tests/test_behaviors.py b/ynr/apps/popolo/behaviors/tests/test_behaviors.py index 729b4512a..9efbe0c85 100644 --- a/ynr/apps/popolo/behaviors/tests/test_behaviors.py +++ b/ynr/apps/popolo/behaviors/tests/test_behaviors.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from time import sleep class BehaviorTestCaseMixin(object): @@ -107,40 +106,3 @@ def test_querysets_filters(self): 1, "One future object should have been fetched", ) - - -class TimestampableTests(BehaviorTestCaseMixin): - """ - Timestampable tests. - - Tests whether objects are assigned timestamps at creation time, and - whether a successive modification changes the update timestamp only. - """ - - def test_new_instance_has_equal_timestamps(self): - """Object is assigned timestamps when created""" - obj = self.create_instance() - self.assertIsNotNone(obj.created_at) - self.assertIsNotNone(obj.updated_at) - - # created_at and updated_at are actually different, but still within 2 millisec - # that's because of the pre-save signal validation - self.assertTrue( - (obj.updated_at - obj.created_at) < timedelta(microseconds=10000) - ) - - def test_updated_instance_has_different_timestamps(self): - """Modified object has different created_at and updated_at timestamps """ - obj = self.create_instance() - creation_ts = obj.created_at - update_ts = obj.updated_at - # save object after 30K microsecs and check again - sleep(0.03) - obj.save() - self.assertEqual(obj.created_at, creation_ts) - self.assertNotEqual(obj.updated_at, update_ts) - - # created_at and updated_at are actually different, well outside 10 millisecs - self.assertFalse( - (obj.updated_at - obj.created_at) < timedelta(microseconds=10000) - ) diff --git a/ynr/apps/popolo/migrations/0039_auto_20210616_1642.py b/ynr/apps/popolo/migrations/0039_auto_20210616_1642.py new file mode 100644 index 000000000..8bbbd6108 --- /dev/null +++ b/ynr/apps/popolo/migrations/0039_auto_20210616_1642.py @@ -0,0 +1,85 @@ +# Generated by Django 2.2.20 on 2021-06-16 15:42 + +from django.db import migrations +import django.utils.timezone +import model_utils.fields + + +class Migration(migrations.Migration): + + dependencies = [("popolo", "0038_auto_20210401_0811")] + + operations = [ + migrations.AddField( + model_name="contactdetail", + name="created", + field=model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + migrations.AddField( + model_name="contactdetail", + name="modified", + field=model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="modified", + ), + ), + migrations.AddField( + model_name="membership", + name="created", + field=model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + migrations.AddField( + model_name="membership", + name="modified", + field=model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="modified", + ), + ), + migrations.AddField( + model_name="organization", + name="created", + field=model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + migrations.AddField( + model_name="organization", + name="modified", + field=model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="modified", + ), + ), + migrations.AddField( + model_name="post", + name="created", + field=model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + migrations.AddField( + model_name="post", + name="modified", + field=model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="modified", + ), + ), + ] diff --git a/ynr/apps/popolo/migrations/0040_copy_created.py b/ynr/apps/popolo/migrations/0040_copy_created.py new file mode 100644 index 000000000..2458749c2 --- /dev/null +++ b/ynr/apps/popolo/migrations/0040_copy_created.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.20 on 2021-06-16 15:48 + +from django.db import migrations + +MODELS_TO_UPDATE = ["ContactDetail", "Membership", "Organization", "Post"] + + +def copy_created_at(apps, schema_editor): + for ModelClass in MODELS_TO_UPDATE: + ModelClass = apps.get_model("popolo", ModelClass) + for obj in ModelClass.objects.iterator(): + obj.created = obj.created_at + obj.save() + + +def copy_created(apps, schema_editor): + for ModelClass in MODELS_TO_UPDATE: + ModelClass = apps.get_model("popolo", ModelClass) + for obj in ModelClass.objects.iterator(): + obj.created_at = obj.created + obj.save() + + +class Migration(migrations.Migration): + + dependencies = [("popolo", "0039_auto_20210616_1642")] + + operations = [ + migrations.RunPython(code=copy_created_at, reverse_code=copy_created) + ] diff --git a/ynr/apps/popolo/migrations/0041_auto_20210616_1705.py b/ynr/apps/popolo/migrations/0041_auto_20210616_1705.py new file mode 100644 index 000000000..b1cfff087 --- /dev/null +++ b/ynr/apps/popolo/migrations/0041_auto_20210616_1705.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.20 on 2021-06-16 16:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [("popolo", "0040_copy_created")] + + operations = [ + migrations.RemoveField(model_name="contactdetail", name="created_at"), + migrations.RemoveField(model_name="contactdetail", name="updated_at"), + migrations.RemoveField(model_name="membership", name="created_at"), + migrations.RemoveField(model_name="membership", name="updated_at"), + migrations.RemoveField(model_name="organization", name="created_at"), + migrations.RemoveField(model_name="organization", name="updated_at"), + migrations.RemoveField(model_name="post", name="created_at"), + migrations.RemoveField(model_name="post", name="updated_at"), + ] diff --git a/ynr/apps/popolo/models.py b/ynr/apps/popolo/models.py index 46fec2388..8828dc318 100644 --- a/ynr/apps/popolo/models.py +++ b/ynr/apps/popolo/models.py @@ -12,7 +12,7 @@ from ynr_refactoring.settings import PersonIdentifierFields -from .behaviors.models import Dateframeable, GenericRelatable, Timestampable +from .behaviors.models import Dateframeable, GenericRelatable from .querysets import ( ContactDetailQuerySet, MembershipQuerySet, @@ -30,7 +30,7 @@ class NotStandingValidationError(ValueError): pass -class Organization(Dateframeable, Timestampable, models.Model): +class Organization(Dateframeable, TimeStampedModel, models.Model): """ A group with a common purpose or reason for existence that goes beyond the set of people belonging to it see schema at @@ -161,7 +161,7 @@ def __str__(self): return self.name -class Post(Dateframeable, Timestampable, models.Model): +class Post(Dateframeable, TimeStampedModel, models.Model): """ A position that exists independent of the person holding it see schema at http://popoloproject.com/schemas/json# @@ -265,7 +265,7 @@ class PostIdentifier(TimeStampedModel): label = models.CharField(max_length=255, blank=True) -class Membership(Dateframeable, Timestampable, models.Model): +class Membership(Dateframeable, TimeStampedModel, models.Model): """ A relationship between a person and an organization see schema at http://popoloproject.com/schemas/membership.json# @@ -429,7 +429,7 @@ def dict_for_csv(self, redirects=None): class ContactDetail( - Timestampable, Dateframeable, GenericRelatable, models.Model + TimeStampedModel, Dateframeable, GenericRelatable, models.Model ): """ A means of contacting an entity diff --git a/ynr/apps/popolo/tests/test_models.py b/ynr/apps/popolo/tests/test_models.py index a6cf59e55..94359ae08 100644 --- a/ynr/apps/popolo/tests/test_models.py +++ b/ynr/apps/popolo/tests/test_models.py @@ -8,16 +8,13 @@ from slugify import slugify from people.models import Person -from popolo.behaviors.tests.test_behaviors import ( - DateframeableTests, - TimestampableTests, -) +from popolo.behaviors.tests.test_behaviors import DateframeableTests from popolo.models import Organization faker = Factory.create("it_IT") # a factory to create fake names for tests -class PersonTestCase(TimestampableTests, TestCase): +class PersonTestCase(TestCase): model = Person object_name = "person" @@ -27,7 +24,7 @@ def create_instance(self, **kwargs): return Person.objects.create(**kwargs) -class OrganizationTestCase(DateframeableTests, TimestampableTests, TestCase): +class OrganizationTestCase(DateframeableTests, TestCase): model = Organization object_name = "organization" diff --git a/ynr/apps/uk_results/api/next/filters.py b/ynr/apps/uk_results/api/next/filters.py index ef8996dfa..184546a2a 100644 --- a/ynr/apps/uk_results/api/next/filters.py +++ b/ynr/apps/uk_results/api/next/filters.py @@ -36,7 +36,7 @@ class Meta: fields = ["election_date", "election_id", "last_updated"] last_updated = filters.DateTimeFilter( - field_name="updated_at", + field_name="modified", lookup_expr="gt", label="Last updated", help_text="An ISO datetime",