Events record Patch-related things like initial creation, state transitions, delegation assigning etc.
Signed-off-by: Stephen Finucane <[email protected]> --- 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 _______________________________________________ Patchwork mailing list [email protected] https://lists.ozlabs.org/listinfo/patchwork
