Skip to content

Commit

Permalink
add change history
Browse files Browse the repository at this point in the history
  • Loading branch information
saxix committed Nov 18, 2024
1 parent 167743d commit 3f75b2a
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 11 deletions.
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ license = {text = "MIT"}
dependencies = [
"celery>=5.4.0",
"deepdiff>=8.0.1",
"deprecation>=2.1.0",
"dictdiffer>=0.9.0",
"django-admin-extra-buttons>=1.6.0",
"django-adminactions>=2.3.0",
Expand All @@ -28,6 +29,7 @@ dependencies = [
"django-jsoneditor>=0.2.4",
"django-mptt>=0.16.0",
"django-regex>=0.5.0",
"django-reversion>=5.1.0",
"django-select2",
"django-smart-admin>=2.6.0",
"django-smart-env>=0.1.0",
Expand Down
1 change: 1 addition & 0 deletions src/country_workspace/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"debug_toolbar",
# "country_workspace.admin_site.apps.AdminConfig",
"flags",
"reversion",
"tailwind",
"social_django",
"admin_extra_buttons",
Expand Down
44 changes: 33 additions & 11 deletions src/country_workspace/models/base.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, Optional

from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext as _

import dictdiffer
import reversion

from country_workspace.cache.manager import cache_manager
from country_workspace.state import state

if TYPE_CHECKING:
from hope_flex_fields.models import DataChecker
Expand Down Expand Up @@ -58,16 +62,12 @@ def __str__(self) -> str:
return self.name or "%s %s" % (self._meta.verbose_name, self.id)

def save(self, *args, force_insert=False, force_update=False, using=None, update_fields=None):
# current = self.__class__.objects.get(pk=self.pk).flex_fields
# if current != self.flex_fields:
# self.history.append({"current": current,
# "date": str(timezone.now()),
# "user": state.request.user.username
# })

super().save(
*args, force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields
)
with reversion.create_revision():
super().save(
*args, force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields
)
if state.request:
reversion.set_user(state.request.user)

def checker(self) -> "DataChecker":
raise NotImplementedError
Expand All @@ -82,6 +82,28 @@ def validate_with_checker(self) -> bool:
self.save(update_fields=["last_checked", "errors"])
return not bool(errors)

def last_changes(self) -> "Any":
from reversion.models import Version

last_version = Version.objects.get_for_object(self).latest("-pk")
stored_status = last_version.field_dict["flex_fields"]
current_status = self.flex_fields
return list(dictdiffer.diff(stored_status, current_status))

def diff(self, first: Optional[int] = None, second: Optional[int] = None) -> "Any":
from reversion.models import Version

qs = Version.objects.get_for_object(self).order_by("pk")
versions = list(qs.values_list("pk", flat=True))
if first is None:
first = len(versions) - 1
if second is None:
second = first - 1
v1, v2 = list(qs.filter(pk__in=[versions[first], versions[second]]))
status1 = v1.field_dict["flex_fields"]
status2 = v2.field_dict["flex_fields"]
return list(dictdiffer.diff(status2, status1))


class BaseModel(models.Model):
last_modified = models.DateTimeField(auto_now=True, editable=False)
Expand Down
3 changes: 3 additions & 0 deletions src/country_workspace/models/household.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

from django.db import models

import reversion

from .base import BaseModel, Validable

if TYPE_CHECKING:
Expand All @@ -12,6 +14,7 @@
from .program import Program


@reversion.register()
class Household(Validable, BaseModel):
system_fields = models.JSONField(default=dict, blank=True)

Expand Down
2 changes: 2 additions & 0 deletions src/country_workspace/models/individual.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from django.db import models
from django.utils.functional import cached_property

import reversion
from hope_flex_fields.models import DataChecker

from .base import BaseModel, Validable
from .household import Household


@reversion.register()
class Individual(Validable, BaseModel):
household = models.ForeignKey(Household, on_delete=models.CASCADE, null=True, blank=True, related_name="members")
system_fields = models.JSONField(default=dict, blank=True)
Expand Down
3 changes: 3 additions & 0 deletions src/country_workspace/workspaces/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.db import models
from django.utils.functional import cached_property

import reversion
from hope_flex_fields.models import DataChecker

from country_workspace.models import AsyncJob, Batch, Household, Individual, Office, Program
Expand All @@ -17,6 +18,7 @@ class Meta:
verbose_name_plural = "Country Batches"


@reversion.register()
class CountryHousehold(Household):
class Meta:
proxy = True
Expand All @@ -32,6 +34,7 @@ def country_office(self) -> "DataChecker":
return self.batch.program.country_office


@reversion.register()
class CountryIndividual(Individual):
class Meta:
proxy = True
Expand Down
36 changes: 36 additions & 0 deletions tests/models/test_m_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import pytest

from country_workspace.models import Household
from country_workspace.workspaces.models import CountryHousehold


@pytest.fixture()
def household() -> "CountryHousehold":
from testutils.factories.household import CountryHouseholdFactory, get_hh_fields

ff = get_hh_fields(None)
ff["size"] = 99
return CountryHouseholdFactory(flex_fields=ff)


def test_last_changes(household: "Household"):
household.flex_fields["size"] = 2
household.save()
household.refresh_from_db()
assert household.flex_fields["size"] == 2
assert household.last_changes() == [("change", "size", (99, 2))]


def test_diff(household: "Household"):
household.flex_fields["size"] = 11
household.save()
household.flex_fields["size"] = 12
household.save()
household.flex_fields["size"] = 13
household.save()
household.refresh_from_db()
assert household.diff() == [("change", "size", (13, 12))]
assert household.diff(-1) == [("change", "size", (13, 12))]
assert household.diff(-1, -2) == [("change", "size", (13, 12))]
assert household.diff(-2, -3) == [("change", "size", (12, 11))]
assert household.diff(-3, -4) == [("change", "size", (11, 99))]
28 changes: 28 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3f75b2a

Please sign in to comment.