aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMete Polat <metepolat2000@gmail.com>2020-02-27 23:29:31 +0000
committerDaniel Axtens <dja@axtens.net>2020-03-16 10:05:51 +1100
commit27c2acf56cd30e77c932a1dde87b6fc1de8eeb2c (patch)
treef73e8dd6d047c6ac1c310b427445f7fd679d0852
parent00808edcfe99689324dc6b28ef1b70dd4d7ab224 (diff)
downloadpatchwork-27c2acf56cd30e77c932a1dde87b6fc1de8eeb2c.tar
patchwork-27c2acf56cd30e77c932a1dde87b6fc1de8eeb2c.tar.gz
models, templates: Add patch relations
Introduces the ability to add relations between patches. Relations are displayed in the details page of a patch under 'Related'. Related patches located in another projects can be viewed as well. Changes to relations are tracked in events. Currently the display of this is very bare in the API but that will be fixed in a subsequent patch: this is the minimum required to avoid throwing errors when you view the events feed. Signed-off-by: Mete Polat <metepolat2000@gmail.com> [dja: address some review comments from Stephen, add an admin view, move to using Events, misc tidy-ups.] Signed-off-by: Daniel Axtens <dja@axtens.net>
-rw-r--r--patchwork/admin.py8
-rw-r--r--patchwork/api/event.py5
-rw-r--r--patchwork/migrations/0040_add_related_patches.py41
-rw-r--r--patchwork/models.py32
-rw-r--r--patchwork/signals.py24
-rw-r--r--patchwork/templates/patchwork/submission.html37
-rw-r--r--patchwork/views/patch.py14
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)