[PATCH v3 4/9] models: Add 'Event' model
Stephen Finucane
stephen at that.guru
Wed Feb 8 09:12:39 AEDT 2017
Events record Patch-related things like initial creation, state
transitions, delegation assigning etc.
Signed-off-by: Stephen Finucane <stephen at that.guru>
---
Changes since v2:
- Add database indexes to speed up queries on these oft queried fields
- Add new events
- series-completed
- Rename some events
- patch-check-added -> check-added
- patch-dependencies-met -> patch-completed
Changes since v1:
- Add new events
- series-created
- cover-created
These require additional fields on the model
- Rename some events to present a clear parent-child hierarchy
---
patchwork/migrations/0017_add_event_model.py | 38 +++++++++++
patchwork/models.py | 96 +++++++++++++++++++++++++++-
2 files changed, 132 insertions(+), 2 deletions(-)
create mode 100644 patchwork/migrations/0017_add_event_model.py
diff --git a/patchwork/migrations/0017_add_event_model.py b/patchwork/migrations/0017_add_event_model.py
new file mode 100644
index 0000000..db10713
--- /dev/null
+++ b/patchwork/migrations/0017_add_event_model.py
@@ -0,0 +1,38 @@
+# -*- 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', '0016_series_project'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Event',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('category', models.CharField(choices=[(b'cover-created', b'Cover Letter Created'), (b'patch-created', b'Patch Created'), (b'patch-completed', b'Patch Completed'), (b'patch-state-changed', b'Patch State Changed'), (b'patch-delegated', b'Patch Delegate Changed'), (b'check-created', b'Check Created'), (b'series-created', b'Series Created'), (b'series-completed', b'Series Completed')], db_index=True, help_text=b'The category of the event.', max_length=20)),
+ ('date', models.DateTimeField(default=datetime.datetime.now, help_text=b'The time this event was created.')),
+ ('cover', models.ForeignKey(blank=True, help_text=b'The cover letter that this event was created for.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='patchwork.CoverLetter')),
+ ('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(blank=True, help_text=b'The patch that this event was created for.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', 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')),
+ ('series', models.ForeignKey(blank=True, help_text=b'The series that this event was created for.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='patchwork.Series')),
+ ],
+ options={
+ 'ordering': ['-date'],
+ },
+ ),
+ ]
diff --git a/patchwork/models.py b/patchwork/models.py
index cf363f0..d9f7bec 100644
--- a/patchwork/models.py
+++ b/patchwork/models.py
@@ -20,14 +20,15 @@
from __future__ import absolute_import
-from collections import Counter, OrderedDict
+from collections import Counter
+from collections import OrderedDict
import datetime
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.urlresolvers import reverse
from django.db import models
@@ -793,6 +794,97 @@ 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. Generic foreign keys
+ are another solution, but using these will result in a lot of massaging
+ should we wish to add support for an 'expand' paramter in the REST API in
+ the future. Refer to https://code.djangoproject.com/ticket/24272 for more
+ information.
+ """
+ CATEGORY_COVER_CREATED = 'cover-created'
+ CATEGORY_PATCH_CREATED = 'patch-created'
+ CATEGORY_PATCH_COMPLETED = 'patch-completed'
+ CATEGORY_PATCH_STATE_CHANGED = 'patch-state-changed'
+ CATEGORY_PATCH_DELEGATED = 'patch-delegated'
+ CATEGORY_CHECK_CREATED = 'check-created'
+ CATEGORY_SERIES_CREATED = 'series-created'
+ CATEGORY_SERIES_COMPLETED = 'series-completed'
+ CATEGORY_CHOICES = (
+ (CATEGORY_COVER_CREATED, 'Cover Letter Created'),
+ (CATEGORY_PATCH_CREATED, 'Patch Created'),
+ (CATEGORY_PATCH_COMPLETED, 'Patch Completed'),
+ (CATEGORY_PATCH_STATE_CHANGED, 'Patch State Changed'),
+ (CATEGORY_PATCH_DELEGATED, 'Patch Delegate Changed'),
+ (CATEGORY_CHECK_CREATED, 'Check Created'),
+ (CATEGORY_SERIES_CREATED, 'Series Created'),
+ (CATEGORY_SERIES_COMPLETED, 'Series Completed'),
+ )
+
+ # parents
+
+ project = models.ForeignKey(
+ Project, related_name='+', db_index=True,
+ help_text='The project that the events belongs to.')
+
+ # event metadata
+
+ category = models.CharField(
+ max_length=20,
+ choices=CATEGORY_CHOICES,
+ db_index=True,
+ help_text='The category of the event.')
+ date = models.DateTimeField(
+ default=datetime.datetime.now,
+ help_text='The time this event was created.')
+
+ # event object
+
+ # only one of the below should be used, depending on which category was
+ # used
+
+ patch = models.ForeignKey(
+ Patch, related_name='+', null=True, blank=True,
+ help_text='The patch that this event was created for.')
+ series = models.ForeignKey(
+ Series, related_name='+', null=True, blank=True,
+ help_text='The series that this event was created for.')
+ cover = models.ForeignKey(
+ CoverLetter, related_name='+', null=True, blank=True,
+ help_text='The cover letter that this event was created for.')
+
+ # fields for 'patch-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 'patch-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 or 'patch-check-created' events
+
+ created_check = models.ForeignKey(
+ Check, related_name='+', null=True, blank=True)
+
+ # TODO(stephenfin): Validate that the correct fields are being set by way
+ # of a 'clean' method
+
+ 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