From 9c052e82e65fcc62a9045c21e07153c4eb1e218e Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Wed, 17 Jul 2024 19:57:16 +0200 Subject: [PATCH] Track events so that we can generate update digests later Refs #8. --- projects/admin.py | 11 ++++ projects/migrations/0004_event.py | 97 +++++++++++++++++++++++++++++++ projects/models.py | 62 ++++++++++++++++++++ projects/views.py | 27 ++++++++- 4 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 projects/migrations/0004_event.py diff --git a/projects/admin.py b/projects/admin.py index beb9795..0118b7e 100644 --- a/projects/admin.py +++ b/projects/admin.py @@ -31,3 +31,14 @@ class CatalogAdmin(admin.ModelAdmin): list_filter = ["project"] readonly_fields = ["pofile"] ordering = ["project", *models.Catalog._meta.ordering] + + +@admin.register(models.Event) +class EventAdmin(admin.ModelAdmin): + list_display = ["created_at", "user", "action", "project_string", "catalog_string"] + + def has_add_permission(self, request, obj=None): + return False + + has_change_permission = has_add_permission + has_delete_permission = has_add_permission diff --git a/projects/migrations/0004_event.py b/projects/migrations/0004_event.py new file mode 100644 index 0000000..2bcd34b --- /dev/null +++ b/projects/migrations/0004_event.py @@ -0,0 +1,97 @@ +# Generated by Django 5.1b1 on 2024-07-17 17:54 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("projects", "0003_project__email_domains"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Event", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "created_at", + models.DateTimeField(auto_now_add=True, verbose_name="created at"), + ), + ( + "action", + models.CharField( + choices=[ + ("catalog-created", "created catalog"), + ("catalog-updated", "updated catalog"), + ("catalog-submitted", "submitted catalog"), + ("catalog-replaced", "replaced catalog"), + ("catalog-deleted", "deleted catalog"), + ], + max_length=20, + verbose_name="action", + ), + ), + ( + "user_string", + models.CharField(max_length=1000, verbose_name="user string"), + ), + ( + "project_string", + models.CharField(max_length=1000, verbose_name="project string"), + ), + ( + "catalog_string", + models.CharField(max_length=1000, verbose_name="catalog string"), + ), + ( + "catalog", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="events", + to="projects.catalog", + verbose_name="catalog", + ), + ), + ( + "project", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="events", + to="projects.project", + verbose_name="project", + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="events", + to=settings.AUTH_USER_MODEL, + verbose_name="user", + ), + ), + ], + options={ + "verbose_name": "event", + "verbose_name_plural": "events", + "ordering": ["-created_at"], + }, + ), + ] diff --git a/projects/models.py b/projects/models.py index 6d0835f..f0e1d2d 100644 --- a/projects/models.py +++ b/projects/models.py @@ -166,3 +166,65 @@ def get_absolute_url(self): @cached_property def po(self): return polib.pofile(self.pofile, wrapwidth=0) + + +class Event(models.Model): + class Action(models.TextChoices): + CATALOG_CREATED = "catalog-created", _("created catalog") + CATALOG_UPDATED = "catalog-updated", _("updated catalog") + CATALOG_SUBMITTED = "catalog-submitted", _("submitted catalog") + CATALOG_REPLACED = "catalog-replaced", _("replaced catalog") + CATALOG_DELETED = "catalog-deleted", _("deleted catalog") + + created_at = models.DateTimeField(_("created at"), auto_now_add=True) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name="events", + verbose_name=_("user"), + ) + action = models.CharField(_("action"), max_length=20, choices=Action) + + project = models.ForeignKey( + Project, + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name="events", + verbose_name=_("project"), + ) + catalog = models.ForeignKey( + Catalog, + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name="events", + verbose_name=_("catalog"), + ) + + user_string = models.CharField(_("user string"), max_length=1000) + project_string = models.CharField(_("project string"), max_length=1000) + catalog_string = models.CharField(_("catalog string"), max_length=1000) + + class Meta: + ordering = ["-created_at"] + verbose_name = _("event") + verbose_name_plural = _("events") + + def __str__(self): + return self.get_action_display() + + def save(self, *args, **kwargs): + if not self.project and self.catalog: + self.project = self.catalog.project + if not self.user_string and self.user: + self.user_string = str(self.user) + if not self.project_string and self.project: + self.project_string = str(self.project) + if not self.catalog_string and self.catalog: + self.catalog_string = str(self.catalog) + super().save(*args, **kwargs) + + save.alters_data = True diff --git a/projects/views.py b/projects/views.py index de5ceb1..c2b0b12 100644 --- a/projects/views.py +++ b/projects/views.py @@ -11,7 +11,7 @@ from form_rendering import adapt_rendering from projects import translators from projects.forms import EntriesForm, FilterForm, SuggestForm -from projects.models import Catalog, Project +from projects.models import Catalog, Event, Project ENTRIES_PER_PAGE = 20 @@ -76,6 +76,12 @@ def catalog(request, project, language_code, domain): if form.is_valid(): form.update(catalog, request=request) + Event.objects.create( + user=request.user, + action=Event.Action.CATALOG_UPDATED, + catalog=catalog, + ) + return http.HttpResponseRedirect( query_string( None, @@ -163,12 +169,31 @@ def pofile(request, project, language_code, domain): catalog.po.merge(new) catalog.pofile = str(catalog.po) catalog.save() + + Event.objects.create( + user=request.user, + action=( + Event.Action.CATALOG_CREATED + if created + else Event.Action.CATALOG_REPLACED + if request.method == "PUT" + else Event.Action.CATALOG_UPDATED + ), + catalog=catalog, + ) + return http.HttpResponse(status=202) # Accepted if request.method == "DELETE": if catalog := project.catalogs.filter( language_code=language_code, domain=domain ).first(): + Event.objects.create( + user=request.user, + action=Event.Action.CATALOG_DELETED, + catalog=catalog, + ) + catalog.delete() return http.HttpResponse("", status=204) # No content return http.HttpResponseNotFound()