diff options
-rw-r--r-- | patchwork/admin.py | 8 | ||||
-rw-r--r-- | patchwork/api/event.py | 5 | ||||
-rw-r--r-- | patchwork/migrations/0040_add_related_patches.py | 41 | ||||
-rw-r--r-- | patchwork/models.py | 32 | ||||
-rw-r--r-- | patchwork/signals.py | 24 | ||||
-rw-r--r-- | patchwork/templates/patchwork/submission.html | 37 | ||||
-rw-r--r-- | patchwork/views/patch.py | 14 |
7 files changed, 159 insertions, 2 deletions
diff --git a/patchwork/admin.py b/patchwork/admin.py index f9a94c6..c3d4524 100644 --- a/patchwork/admin.py +++ b/patchwork/admin.py @@ -14,6 +14,7 @@ from patchwork.models import Comment from patchwork.models import CoverLetter from patchwork.models import DelegationRule from patchwork.models import Patch +from patchwork.models import PatchRelation from patchwork.models import Person from patchwork.models import Project from patchwork.models import Series @@ -174,3 +175,10 @@ class TagAdmin(admin.ModelAdmin): admin.site.register(Tag, TagAdmin) + + +class PatchRelationAdmin(admin.ModelAdmin): + model = PatchRelation + + +admin.site.register(PatchRelation, PatchRelationAdmin) diff --git a/patchwork/api/event.py b/patchwork/api/event.py index fdff6a4..44c3452 100644 --- a/patchwork/api/event.py +++ b/patchwork/api/event.py @@ -42,6 +42,8 @@ class EventSerializer(ModelSerializer): 'current_state'], Event.CATEGORY_PATCH_DELEGATED: ['patch', 'previous_delegate', 'current_delegate'], + Event.CATEGORY_PATCH_RELATION_CHANGED: ['patch', 'previous_relation', + 'current_relation'], Event.CATEGORY_CHECK_CREATED: ['patch', 'created_check'], Event.CATEGORY_SERIES_CREATED: ['series'], Event.CATEGORY_SERIES_COMPLETED: ['series'], @@ -68,7 +70,8 @@ class EventSerializer(ModelSerializer): model = Event fields = ('id', 'category', 'project', 'date', 'actor', 'patch', 'series', 'cover', 'previous_state', 'current_state', - 'previous_delegate', 'current_delegate', 'created_check') + 'previous_delegate', 'current_delegate', 'created_check', + 'previous_relation', 'current_relation',) read_only_fields = fields versioned_fields = { '1.2': ('actor', ), diff --git a/patchwork/migrations/0040_add_related_patches.py b/patchwork/migrations/0040_add_related_patches.py new file mode 100644 index 0000000..fc2994d --- /dev/null +++ b/patchwork/migrations/0040_add_related_patches.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('patchwork', '0039_unique_series_references'), + ] + + operations = [ + migrations.CreateModel( + name='PatchRelation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + migrations.AlterField( + model_name='event', + name='category', + field=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'patch-relation-changed', b'Patch Relation 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=25), + ), + migrations.AddField( + model_name='event', + name='current_relation', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='patchwork.PatchRelation'), + ), + migrations.AddField( + model_name='event', + name='previous_relation', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='patchwork.PatchRelation'), + ), + migrations.AddField( + model_name='patch', + name='related', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patches', related_query_name='patch', to='patchwork.PatchRelation'), + ), + ] diff --git a/patchwork/models.py b/patchwork/models.py index 08ee341..e295e17 100644 --- a/patchwork/models.py +++ b/patchwork/models.py @@ -449,6 +449,12 @@ class Patch(Submission): default=None, null=True, help_text='The number assigned to this patch in the series') + # related patches metadata + + related = models.ForeignKey( + 'PatchRelation', null=True, blank=True, on_delete=models.SET_NULL, + related_name='patches', related_query_name='patch') + objects = PatchManager() @staticmethod @@ -866,6 +872,19 @@ class BundlePatch(models.Model): @python_2_unicode_compatible +class PatchRelation(models.Model): + + def __str__(self): + patches = self.patches.all() + if not patches: + return '<Empty>' + name = ', '.join(patch.name for patch in patches[:10]) + if len(name) > 60: + name = name[:60] + '...' + return name + + +@python_2_unicode_compatible class Check(models.Model): """Check for a patch. @@ -930,6 +949,7 @@ class Event(models.Model): CATEGORY_PATCH_COMPLETED = 'patch-completed' CATEGORY_PATCH_STATE_CHANGED = 'patch-state-changed' CATEGORY_PATCH_DELEGATED = 'patch-delegated' + CATEGORY_PATCH_RELATION_CHANGED = 'patch-relation-changed' CATEGORY_CHECK_CREATED = 'check-created' CATEGORY_SERIES_CREATED = 'series-created' CATEGORY_SERIES_COMPLETED = 'series-completed' @@ -939,6 +959,7 @@ class Event(models.Model): (CATEGORY_PATCH_COMPLETED, 'Patch Completed'), (CATEGORY_PATCH_STATE_CHANGED, 'Patch State Changed'), (CATEGORY_PATCH_DELEGATED, 'Patch Delegate Changed'), + (CATEGORY_PATCH_RELATION_CHANGED, 'Patch Relation Changed'), (CATEGORY_CHECK_CREATED, 'Check Created'), (CATEGORY_SERIES_CREATED, 'Series Created'), (CATEGORY_SERIES_COMPLETED, 'Series Completed'), @@ -954,7 +975,7 @@ class Event(models.Model): # event metadata category = models.CharField( - max_length=20, + max_length=25, choices=CATEGORY_CHOICES, db_index=True, help_text='The category of the event.') @@ -1002,6 +1023,15 @@ class Event(models.Model): User, related_name='+', null=True, blank=True, on_delete=models.CASCADE) + # fields for 'patch-relation-changed-changed' events + + previous_relation = models.ForeignKey( + PatchRelation, related_name='+', null=True, blank=True, + on_delete=models.CASCADE) + current_relation = models.ForeignKey( + PatchRelation, related_name='+', null=True, blank=True, + on_delete=models.CASCADE) + # fields or 'patch-check-created' events created_check = models.ForeignKey( diff --git a/patchwork/signals.py b/patchwork/signals.py index 73ddfa5..3a2f0fb 100644 --- a/patchwork/signals.py +++ b/patchwork/signals.py @@ -135,6 +135,30 @@ def create_patch_delegated_event(sender, instance, raw, **kwargs): @receiver(pre_save, sender=Patch) +def create_patch_relation_changed_event(sender, instance, raw, **kwargs): + + def create_event(patch, before, after): + return Event.objects.create( + category=Event.CATEGORY_PATCH_RELATION_CHANGED, + project=patch.project, + actor=getattr(patch, '_edited_by', None), + patch=patch, + previous_relation=before, + current_relation=after) + + # don't trigger for items loaded from fixtures or new items + if raw or not instance.pk: + return + + orig_patch = Patch.objects.get(pk=instance.pk) + + if orig_patch.related == instance.related: + return + + create_event(instance, orig_patch.related, instance.related) + + +@receiver(pre_save, sender=Patch) def create_patch_completed_event(sender, instance, raw, **kwargs): def create_event(patch): diff --git a/patchwork/templates/patchwork/submission.html b/patchwork/templates/patchwork/submission.html index 77a2711..978559b 100644 --- a/patchwork/templates/patchwork/submission.html +++ b/patchwork/templates/patchwork/submission.html @@ -110,6 +110,43 @@ function toggle_div(link_id, headers_id, label_show, label_hide) </td> </tr> {% endif %} +{% if submission.related %} + <tr> + <th>Related</th> + <td> + <a id="togglerelated" + href="javascript:toggle_div('togglerelated', 'related')" + >show</a> + <div id="related" class="submissionlist" style="display:none;"> + <ul> + {% for sibling in related_same_project %} + <li> + {% if sibling.id != submission.id %} + <a href="{% url 'patch-detail' project_id=project.linkname msgid=sibling.url_msgid %}"> + {{ sibling.name|default:"[no subject]"|truncatechars:100 }} + </a> + {% endif %} + </li> + {% endfor %} + {% if related_different_project %} + <a id="togglerelatedoutside" + href="javascript:toggle_div('togglerelatedoutside', 'relatedoutside', 'show from other projects')" + >show from other projects</a> + <div id="relatedoutside" class="submissionlist" style="display:none;"> + {% for sibling in related_outside %} + <li> + <a href="{% url 'patch-detail' project_id=sibling.project.linkname msgid=sibling.url_msgid %}"> + {{ sibling.name|default:"[no subject]"|truncatechars:100 }} + </a> (in {{ sibling.project }}) + </li> + {% endfor %} + </div> + {% endif %} + </ul> + </div> + </td> + </tr> +{% endif %} </table> <div class="patchforms"> diff --git a/patchwork/views/patch.py b/patchwork/views/patch.py index b368cfa..470ad19 100644 --- a/patchwork/views/patch.py +++ b/patchwork/views/patch.py @@ -110,12 +110,26 @@ def patch_detail(request, project_id, msgid): comments = comments.only('submitter', 'date', 'id', 'content', 'submission') + if patch.related: + related_same_project = patch.related.patches.only( + 'name', 'msgid', 'project', 'related') + # avoid a second trip out to the db for info we already have + related_different_project = [ + related_patch for related_patch in related_same_project + if related_patch.project_id != patch.project_id + ] + else: + related_same_project = [] + related_different_project = [] + context['comments'] = comments context['checks'] = patch.check_set.all().select_related('user') context['submission'] = patch context['patchform'] = form context['createbundleform'] = createbundleform context['project'] = patch.project + context['related_same_project'] = related_same_project + context['related_different_project'] = related_different_project return render(request, 'patchwork/submission.html', context) |