Skip to content

Commit 27c2acf

Browse files
metpdaxtens
authored andcommitted
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 <[email protected]> [dja: address some review comments from Stephen, add an admin view, move to using Events, misc tidy-ups.] Signed-off-by: Daniel Axtens <[email protected]>
1 parent 00808ed commit 27c2acf

File tree

7 files changed

+159
-2
lines changed

7 files changed

+159
-2
lines changed

patchwork/admin.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from patchwork.models import CoverLetter
1515
from patchwork.models import DelegationRule
1616
from patchwork.models import Patch
17+
from patchwork.models import PatchRelation
1718
from patchwork.models import Person
1819
from patchwork.models import Project
1920
from patchwork.models import Series
@@ -174,3 +175,10 @@ class TagAdmin(admin.ModelAdmin):
174175

175176

176177
admin.site.register(Tag, TagAdmin)
178+
179+
180+
class PatchRelationAdmin(admin.ModelAdmin):
181+
model = PatchRelation
182+
183+
184+
admin.site.register(PatchRelation, PatchRelationAdmin)

patchwork/api/event.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class EventSerializer(ModelSerializer):
4242
'current_state'],
4343
Event.CATEGORY_PATCH_DELEGATED: ['patch', 'previous_delegate',
4444
'current_delegate'],
45+
Event.CATEGORY_PATCH_RELATION_CHANGED: ['patch', 'previous_relation',
46+
'current_relation'],
4547
Event.CATEGORY_CHECK_CREATED: ['patch', 'created_check'],
4648
Event.CATEGORY_SERIES_CREATED: ['series'],
4749
Event.CATEGORY_SERIES_COMPLETED: ['series'],
@@ -68,7 +70,8 @@ class Meta:
6870
model = Event
6971
fields = ('id', 'category', 'project', 'date', 'actor', 'patch',
7072
'series', 'cover', 'previous_state', 'current_state',
71-
'previous_delegate', 'current_delegate', 'created_check')
73+
'previous_delegate', 'current_delegate', 'created_check',
74+
'previous_relation', 'current_relation',)
7275
read_only_fields = fields
7376
versioned_fields = {
7477
'1.2': ('actor', ),
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('patchwork', '0039_unique_series_references'),
12+
]
13+
14+
operations = [
15+
migrations.CreateModel(
16+
name='PatchRelation',
17+
fields=[
18+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19+
],
20+
),
21+
migrations.AlterField(
22+
model_name='event',
23+
name='category',
24+
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),
25+
),
26+
migrations.AddField(
27+
model_name='event',
28+
name='current_relation',
29+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='patchwork.PatchRelation'),
30+
),
31+
migrations.AddField(
32+
model_name='event',
33+
name='previous_relation',
34+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='patchwork.PatchRelation'),
35+
),
36+
migrations.AddField(
37+
model_name='patch',
38+
name='related',
39+
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'),
40+
),
41+
]

patchwork/models.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,12 @@ class Patch(Submission):
449449
default=None, null=True,
450450
help_text='The number assigned to this patch in the series')
451451

452+
# related patches metadata
453+
454+
related = models.ForeignKey(
455+
'PatchRelation', null=True, blank=True, on_delete=models.SET_NULL,
456+
related_name='patches', related_query_name='patch')
457+
452458
objects = PatchManager()
453459

454460
@staticmethod
@@ -865,6 +871,19 @@ class Meta:
865871
ordering = ['order']
866872

867873

874+
@python_2_unicode_compatible
875+
class PatchRelation(models.Model):
876+
877+
def __str__(self):
878+
patches = self.patches.all()
879+
if not patches:
880+
return '<Empty>'
881+
name = ', '.join(patch.name for patch in patches[:10])
882+
if len(name) > 60:
883+
name = name[:60] + '...'
884+
return name
885+
886+
868887
@python_2_unicode_compatible
869888
class Check(models.Model):
870889

@@ -930,6 +949,7 @@ class Event(models.Model):
930949
CATEGORY_PATCH_COMPLETED = 'patch-completed'
931950
CATEGORY_PATCH_STATE_CHANGED = 'patch-state-changed'
932951
CATEGORY_PATCH_DELEGATED = 'patch-delegated'
952+
CATEGORY_PATCH_RELATION_CHANGED = 'patch-relation-changed'
933953
CATEGORY_CHECK_CREATED = 'check-created'
934954
CATEGORY_SERIES_CREATED = 'series-created'
935955
CATEGORY_SERIES_COMPLETED = 'series-completed'
@@ -939,6 +959,7 @@ class Event(models.Model):
939959
(CATEGORY_PATCH_COMPLETED, 'Patch Completed'),
940960
(CATEGORY_PATCH_STATE_CHANGED, 'Patch State Changed'),
941961
(CATEGORY_PATCH_DELEGATED, 'Patch Delegate Changed'),
962+
(CATEGORY_PATCH_RELATION_CHANGED, 'Patch Relation Changed'),
942963
(CATEGORY_CHECK_CREATED, 'Check Created'),
943964
(CATEGORY_SERIES_CREATED, 'Series Created'),
944965
(CATEGORY_SERIES_COMPLETED, 'Series Completed'),
@@ -954,7 +975,7 @@ class Event(models.Model):
954975
# event metadata
955976

956977
category = models.CharField(
957-
max_length=20,
978+
max_length=25,
958979
choices=CATEGORY_CHOICES,
959980
db_index=True,
960981
help_text='The category of the event.')
@@ -1002,6 +1023,15 @@ class Event(models.Model):
10021023
User, related_name='+', null=True, blank=True,
10031024
on_delete=models.CASCADE)
10041025

1026+
# fields for 'patch-relation-changed-changed' events
1027+
1028+
previous_relation = models.ForeignKey(
1029+
PatchRelation, related_name='+', null=True, blank=True,
1030+
on_delete=models.CASCADE)
1031+
current_relation = models.ForeignKey(
1032+
PatchRelation, related_name='+', null=True, blank=True,
1033+
on_delete=models.CASCADE)
1034+
10051035
# fields or 'patch-check-created' events
10061036

10071037
created_check = models.ForeignKey(

patchwork/signals.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,30 @@ def create_event(patch, before, after):
134134
create_event(instance, orig_patch.delegate, instance.delegate)
135135

136136

137+
@receiver(pre_save, sender=Patch)
138+
def create_patch_relation_changed_event(sender, instance, raw, **kwargs):
139+
140+
def create_event(patch, before, after):
141+
return Event.objects.create(
142+
category=Event.CATEGORY_PATCH_RELATION_CHANGED,
143+
project=patch.project,
144+
actor=getattr(patch, '_edited_by', None),
145+
patch=patch,
146+
previous_relation=before,
147+
current_relation=after)
148+
149+
# don't trigger for items loaded from fixtures or new items
150+
if raw or not instance.pk:
151+
return
152+
153+
orig_patch = Patch.objects.get(pk=instance.pk)
154+
155+
if orig_patch.related == instance.related:
156+
return
157+
158+
create_event(instance, orig_patch.related, instance.related)
159+
160+
137161
@receiver(pre_save, sender=Patch)
138162
def create_patch_completed_event(sender, instance, raw, **kwargs):
139163

patchwork/templates/patchwork/submission.html

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,43 @@ <h1>{{ submission.name }}</h1>
110110
</td>
111111
</tr>
112112
{% endif %}
113+
{% if submission.related %}
114+
<tr>
115+
<th>Related</th>
116+
<td>
117+
<a id="togglerelated"
118+
href="javascript:toggle_div('togglerelated', 'related')"
119+
>show</a>
120+
<div id="related" class="submissionlist" style="display:none;">
121+
<ul>
122+
{% for sibling in related_same_project %}
123+
<li>
124+
{% if sibling.id != submission.id %}
125+
<a href="{% url 'patch-detail' project_id=project.linkname msgid=sibling.url_msgid %}">
126+
{{ sibling.name|default:"[no subject]"|truncatechars:100 }}
127+
</a>
128+
{% endif %}
129+
</li>
130+
{% endfor %}
131+
{% if related_different_project %}
132+
<a id="togglerelatedoutside"
133+
href="javascript:toggle_div('togglerelatedoutside', 'relatedoutside', 'show from other projects')"
134+
>show from other projects</a>
135+
<div id="relatedoutside" class="submissionlist" style="display:none;">
136+
{% for sibling in related_outside %}
137+
<li>
138+
<a href="{% url 'patch-detail' project_id=sibling.project.linkname msgid=sibling.url_msgid %}">
139+
{{ sibling.name|default:"[no subject]"|truncatechars:100 }}
140+
</a> (in {{ sibling.project }})
141+
</li>
142+
{% endfor %}
143+
</div>
144+
{% endif %}
145+
</ul>
146+
</div>
147+
</td>
148+
</tr>
149+
{% endif %}
113150
</table>
114151

115152
<div class="patchforms">

patchwork/views/patch.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,26 @@ def patch_detail(request, project_id, msgid):
110110
comments = comments.only('submitter', 'date', 'id', 'content',
111111
'submission')
112112

113+
if patch.related:
114+
related_same_project = patch.related.patches.only(
115+
'name', 'msgid', 'project', 'related')
116+
# avoid a second trip out to the db for info we already have
117+
related_different_project = [
118+
related_patch for related_patch in related_same_project
119+
if related_patch.project_id != patch.project_id
120+
]
121+
else:
122+
related_same_project = []
123+
related_different_project = []
124+
113125
context['comments'] = comments
114126
context['checks'] = patch.check_set.all().select_related('user')
115127
context['submission'] = patch
116128
context['patchform'] = form
117129
context['createbundleform'] = createbundleform
118130
context['project'] = patch.project
131+
context['related_same_project'] = related_same_project
132+
context['related_different_project'] = related_different_project
119133

120134
return render(request, 'patchwork/submission.html', context)
121135

0 commit comments

Comments
 (0)