[PATCH 2/4] models: Add 'Event' model

Stephen Finucane stephen at that.guru
Sat Jan 7 10:40:25 AEDT 2017


Events record Patch-related things like initial creation, state
transitions, delegation assigning etc.

Signed-off-by: Stephen Finucane <stephen at that.guru>
---
I don't currently expose any Series events as we don't have a way of
representing the project. Once this changes, I can update accordingly.
---
 patchwork/migrations/0016_event.py | 37 +++++++++++++++++++
 patchwork/models.py                | 75 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 111 insertions(+), 1 deletion(-)
 create mode 100644 patchwork/migrations/0016_event.py

diff --git a/patchwork/migrations/0016_event.py b/patchwork/migrations/0016_event.py
new file mode 100644
index 0000000..40becbb
--- /dev/null
+++ b/patchwork/migrations/0016_event.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import datetime
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('patchwork', '0015_add_series_models'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Event',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('category', models.SmallIntegerField(choices=[(0, b'patch-created'), (1, b'patch-dependencies-met'), (2, b'state-changed'), (3, b'delegate-changed'), (4, b'check-added')], help_text=b'The category of the event.')),
+                ('public', models.BooleanField(default=True, help_text=b'The visibilty of this event to unauthenticated users.')),
+                ('date', models.DateTimeField(default=datetime.datetime.now, help_text=b'The time this event was created.')),
+                ('created_check', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='patchwork.Check')),
+                ('current_delegate', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
+                ('current_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='patchwork.State')),
+                ('patch', models.ForeignKey(help_text=b'The patch that the event was raised against.', on_delete=django.db.models.deletion.CASCADE, related_name='events', related_query_name='event', to='patchwork.Patch')),
+                ('previous_delegate', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
+                ('previous_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='patchwork.State')),
+                ('project', models.ForeignKey(help_text=b'The project that the events belongs to.', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='patchwork.Project')),
+            ],
+            options={
+                'ordering': ['-date'],
+            },
+        ),
+    ]
diff --git a/patchwork/models.py b/patchwork/models.py
index cdb4e5f..45183f6 100644
--- a/patchwork/models.py
+++ b/patchwork/models.py
@@ -26,9 +26,10 @@ import random
 import re
 
 import django
-from django.contrib.auth.models import User
 from django.conf import settings
+from django.contrib.auth.models import User
 from django.contrib.sites.models import Site
+from django.core.exceptions import ValidationError
 from django.core.urlresolvers import reverse
 from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
@@ -786,6 +787,78 @@ class Check(models.Model):
         return '%s (%s)' % (self.context, self.get_state_display())
 
 
+class Event(models.Model):
+    """An event raised against a patch.
+
+    Events are created whenever certain attributes of a patch are
+    changed.
+
+    This model makes extensive use of nullification of fields. This is more
+    performant than a solution using concrete subclasses while still providing
+    the integrity promises that foreign keys provide.
+    """
+    CATEGORY_PATCH_CREATED = 0
+    CATEGORY_DEPENDENCIES_MET = 1
+    CATEGORY_STATE_CHANGED = 2
+    CATEGORY_DELEGATE_CHANGED = 3
+    CATEGORY_CHECK_ADDED = 4
+    CATEGORY_CHOICES = (
+        (CATEGORY_PATCH_CREATED, 'patch-created'),
+        (CATEGORY_DEPENDENCIES_MET, 'patch-dependencies-met'),
+        (CATEGORY_STATE_CHANGED, 'state-changed'),
+        (CATEGORY_DELEGATE_CHANGED, 'delegate-changed'),
+        (CATEGORY_CHECK_ADDED, 'check-added'),
+    )
+
+    project = models.ForeignKey(
+        Project, related_name='+',
+        help_text='The project that the events belongs to.')
+    patch = models.ForeignKey(
+        Patch, related_name='events', related_query_name='event',
+        help_text='The patch that the event was raised against.')
+    category = models.SmallIntegerField(
+        choices=CATEGORY_CHOICES,
+        help_text='The category of the event.')
+    public = models.BooleanField(
+        default=True,
+        help_text='The visibilty of this event to unauthenticated users.')
+    date = models.DateTimeField(
+        default=datetime.datetime.now,
+        help_text='The time this event was created.')
+
+    # fields for 'state-changed' events
+    previous_state = models.ForeignKey(
+        State, related_name='+', null=True, blank=True)
+    current_state = models.ForeignKey(
+        State, related_name='+', null=True, blank=True)
+
+    # fields for 'delegate-changed' events
+    previous_delegate = models.ForeignKey(
+        User, related_name='+', null=True, blank=True)
+    current_delegate = models.ForeignKey(
+        User, related_name='+', null=True, blank=True)
+
+    # fields for 'check-created' events
+    created_check = models.ForeignKey(
+        Check, related_name='+', null=True, blank=True)
+
+    def clean(self):
+        if self.category == self.PATCH_STATE_CHANGED and not (
+                self.previous_state and self.current_state):
+            raise ValidationError('State change events must record the '
+                                  'previous and current states')
+        elif self.category == self.PATCH_DELEGATE_CHANGED and not (
+                self.previous_delegate or self.current_delegate):
+            raise ValidationError('Delegate change events must record the '
+                                  'previous or current delegates')
+        elif self.category == self.CHECK_ADDED and not self.created_check:
+            raise ValidationError('Check created events must record the '
+                                  'created check')
+
+    class Meta:
+        ordering = ['-date']
+
+
 class EmailConfirmation(models.Model):
     validity = datetime.timedelta(days=settings.CONFIRMATION_VALIDITY_DAYS)
     type = models.CharField(max_length=20, choices=[
-- 
2.9.3



More information about the Patchwork mailing list