Skip to content

Commit

Permalink
Merge pull request #134 from maykinmedia/feature/filter-on-date
Browse files Browse the repository at this point in the history
specify record to display for objects
  • Loading branch information
annashamray committed Jan 13, 2021
2 parents 675b088 + fd2b73a commit 977ebaf
Show file tree
Hide file tree
Showing 15 changed files with 504 additions and 249 deletions.
42 changes: 41 additions & 1 deletion src/objects/api/filters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from django import forms
from django.utils.translation import ugettext_lazy as _

from django_filters import filters
from rest_framework.serializers import ValidationError
from vng_api_common.filtersets import FilterSet
from vng_api_common.utils import get_help_text

Expand All @@ -11,10 +13,41 @@
from .validators import validate_data_attrs


class ObjectFilterForm(forms.Form):
def clean(self):
cleaned_data = super().clean()
date = cleaned_data.get("date")
registration_date = cleaned_data.get("registrationDate")

if date and registration_date:
raise ValidationError(
_(
"'date' and 'registrationDate' parameters can't be used in the same request"
),
code="invalid-date-query-params",
)

return cleaned_data


class ObjectFilterSet(FilterSet):
type = filters.CharFilter(
field_name="object_type", help_text=get_help_text("core.Object", "object_type")
)
date = filters.DateFilter(
method="filter_date",
help_text=_(
"Display record data for the specified formal date, i.e. the specified "
"date would be between `startAt` and `endAt` attributes. The default value is today"
),
)
registrationDate = filters.DateFilter(
method="filter_registration_date",
help_text=_(
"Display record data for the specified registration date, i.e. the specified "
"date would be between `registrationAt` attributes of different records"
),
)
data_attrs = filters.CharFilter(
method="filter_data_attrs",
validators=[validate_data_attrs],
Expand All @@ -40,7 +73,8 @@ class ObjectFilterSet(FilterSet):

class Meta:
model = Object
fields = ("type", "data_attrs")
fields = ("type", "data_attrs", "date", "registrationDate")
form = ObjectFilterForm

def filter_data_attrs(self, queryset, name, value):
parts = value.split(",")
Expand All @@ -65,3 +99,9 @@ def filter_data_attrs(self, queryset, name, value):
)

return queryset

def filter_date(self, queryset, name, value):
return queryset.filter_for_date(value)

def filter_registration_date(self, queryset, name, value):
return queryset.filter_for_registration_date(value)
43 changes: 31 additions & 12 deletions src/objects/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,18 @@ class Meta:
"typeVersion",
"data",
"geometry",
"startDate",
"endDate",
"registrationDate",
"startAt",
"endAt",
"registrationAt",
"correctionFor",
"correctedBy",
)
extra_kwargs = {
"uuid": {"read_only": True},
"typeVersion": {"source": "version"},
"startDate": {"source": "start_date"},
"endDate": {"source": "end_date", "read_only": True},
"registrationDate": {"source": "registration_date", "read_only": True},
"startAt": {"source": "start_at"},
"endAt": {"source": "end_at", "read_only": True},
"registrationAt": {"source": "registration_at", "read_only": True},
}


Expand All @@ -62,18 +62,18 @@ class Meta:
"typeVersion",
"data",
"geometry",
"startDate",
"endDate",
"registrationDate",
"startAt",
"endAt",
"registrationAt",
"correctionFor",
"correctedBy",
)
extra_kwargs = {
"uuid": {"read_only": True},
"typeVersion": {"source": "version"},
"startDate": {"source": "start_date"},
"endDate": {"source": "end_date", "read_only": True},
"registrationDate": {"source": "registration_date", "read_only": True},
"startAt": {"source": "start_at"},
"endAt": {"source": "end_at", "read_only": True},
"registrationAt": {"source": "registration_at", "read_only": True},
}


Expand All @@ -89,6 +89,25 @@ class Meta:
}
validators = [JsonSchemaValidator(), CorrectionValidator()]

def to_representation(self, instance):
ret = super().to_representation(instance)

actual_date = self.context["request"].query_params.get("date", None)
registration_date = self.context["request"].query_params.get(
"registrationDate", None
)

if not actual_date and not registration_date:
return ret

record = (
instance.get_actual_record(actual_date)
if actual_date
else instance.get_registration_record(registration_date)
)
ret["record"] = self.fields["record"].to_representation(record)
return ret

@transaction.atomic
def create(self, validated_data):
record_data = validated_data.pop("current_record")
Expand Down
9 changes: 8 additions & 1 deletion src/objects/api/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import datetime

from django.db import models

from drf_yasg.utils import swagger_auto_schema
Expand Down Expand Up @@ -55,7 +57,12 @@ def get_queryset(self):
if self.action not in ("list", "search"):
return base

return base.filter_for_date().filter_for_user(self.request.user)
date = self.request.query_params.get("date", None)
# default filtering on current day
if not date:
base = base.filter_for_date(datetime.date.today())

return base.filter_for_user(self.request.user)

@swagger_auto_schema(responses={"200": HistoryRecordSerializer(many=True)})
@action(detail=True, methods=["get"], serializer_class=HistoryRecordSerializer)
Expand Down
6 changes: 3 additions & 3 deletions src/objects/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
class ObjectRecordInline(admin.TabularInline):
model = ObjectRecord
extra = 1
readonly_fields = ("uuid", "registration_date", "end_date", "get_corrected_by")
readonly_fields = ("uuid", "registration_at", "end_at", "get_corrected_by")
search_fields = ("uuid",)
fields = (
"version",
"data",
"geometry",
"start_date",
"end_date",
"start_at",
"end_at",
"registration_date",
"get_corrected_by",
"correct",
Expand Down
26 changes: 26 additions & 0 deletions src/objects/core/migrations/0016_auto_20210111_1649.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 2.2.12 on 2021-01-11 15:49

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("core", "0015_objectrecord_geometry"),
]

operations = [
migrations.RenameField(
model_name="objectrecord", old_name="end_date", new_name="end_at"
),
migrations.RenameField(
model_name="objectrecord",
old_name="registration_date",
new_name="registration_at",
),
migrations.RenameField(
model_name="objectrecord",
old_name="start_date",
new_name="start_at",
),
]
54 changes: 54 additions & 0 deletions src/objects/core/migrations/0017_auto_20210111_1653.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Generated by Django 2.2.12 on 2021-01-11 15:53

import datetime
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("core", "0016_auto_20210111_1649"),
]

operations = [
migrations.AlterField(
model_name="objectrecord",
name="correct",
field=models.OneToOneField(
blank=True,
help_text="Object record which corrects the current record",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="corrected",
to="core.ObjectRecord",
verbose_name="correction for",
),
),
migrations.AlterField(
model_name="objectrecord",
name="end_at",
field=models.DateField(
help_text="Legal end date of the object record",
null=True,
verbose_name="end at",
),
),
migrations.AlterField(
model_name="objectrecord",
name="registration_at",
field=models.DateField(
default=datetime.date.today,
help_text="The date when the record was registered in the system",
verbose_name="registration at",
),
),
migrations.AlterField(
model_name="objectrecord",
name="start_at",
field=models.DateField(
help_text="Legal start date of the object record",
verbose_name="start at",
),
),
]
43 changes: 26 additions & 17 deletions src/objects/core/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import datetime
import uuid
from datetime import date

from django.contrib.gis.db.models import GeometryField
from django.contrib.postgres.fields import JSONField
Expand All @@ -20,20 +20,29 @@ class Object(models.Model):

objects = ObjectQuerySet.as_manager()

@property
def current_record(self):
today = date.today()
def get_actual_record(self, date):
return (
self.records.filter(start_date__lte=today)
.filter(models.Q(end_date__gte=today) | models.Q(end_date__isnull=True))
self.records.filter(start_at__lte=date)
.filter(models.Q(end_at__gte=date) | models.Q(end_at__isnull=True))
.order_by("-pk")
.first()
# TODO: pk should prolly be index once added.
)

def get_registration_record(self, date):
return (
self.records.filter(registration_at__lte=date)
.order_by("-registration_at")
.first()
)

@property
def current_record(self):
return self.get_actual_record(datetime.date.today())

@property
def last_record(self):
return self.records.order_by("-start_date", "-id").first()
return self.records.order_by("-start_at", "-id").first()


class ObjectRecord(models.Model):
Expand All @@ -49,15 +58,15 @@ class ObjectRecord(models.Model):
data = JSONField(
_("data"), help_text=_("Object data, based on OBJECTTYPE"), default=dict
)
start_date = models.DateField(
_("start date"), help_text=_("Legal start date of the object record")
start_at = models.DateField(
_("start at"), help_text=_("Legal start date of the object record")
)
end_date = models.DateField(
_("end date"), null=True, help_text=_("Legal end date of the object record")
end_at = models.DateField(
_("end at"), null=True, help_text=_("Legal end date of the object record")
)
registration_date = models.DateField(
_("registration date"),
default=date.today,
registration_at = models.DateField(
_("registration at"),
default=datetime.date.today,
help_text=_("The date when the record was registered in the system"),
)
correct = models.OneToOneField(
Expand All @@ -79,7 +88,7 @@ class ObjectRecord(models.Model):
)

def __str__(self):
return f"{self.version} ({self.start_date})"
return f"{self.version} ({self.start_at})"

def clean(self):
super().clean()
Expand All @@ -90,9 +99,9 @@ def save(self, *args, **kwargs):
if not self.id and self.object.last_record:
# self.index = self.object.last_record.index + 1

# add end_date to previous record
# add end_at to previous record
previous_record = self.object.last_record
previous_record.end_date = self.start_date
previous_record.end_at = self.start_at
previous_record.save()

super().save(*args, **kwargs)
12 changes: 7 additions & 5 deletions src/objects/core/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ def filter_for_user(self, user):
allowed_object_types = user.object_permissions.values("object_type")
return self.filter(object_type__in=models.Subquery(allowed_object_types))

def filter_for_date(self, date=None):
actual_date = date or datetime.date.today()
def filter_for_date(self, date):
return (
self.filter(records__start_date__lte=actual_date)
self.filter(records__start_at__lte=date)
.filter(
models.Q(records__end_date__gte=actual_date)
| models.Q(records__end_date__isnull=True)
models.Q(records__end_at__gte=date)
| models.Q(records__end_at__isnull=True)
)
.distinct()
)

def filter_for_registration_date(self, date):
return self.filter(records__registration_at__lte=date).distinct()
2 changes: 1 addition & 1 deletion src/objects/core/tests/factores.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ObjectRecordFactory(factory.django.DjangoModelFactory):
object = factory.SubFactory(ObjectFactory)
version = factory.Sequence(lambda n: n)
data = factory.Sequence(lambda n: {"some_field": n})
start_date = date.today()
start_at = date.today()

class Meta:
model = ObjectRecord

0 comments on commit 977ebaf

Please sign in to comment.