@@ -11,7 +11,6 @@ from django.db.models import Prefetch
from patchwork.models import Bundle
from patchwork.models import Check
from patchwork.models import Comment
-from patchwork.models import CoverLetter
from patchwork.models import DelegationRule
from patchwork.models import Patch
from patchwork.models import Person
@@ -83,8 +82,6 @@ class SubmissionAdmin(admin.ModelAdmin):
admin.site.register(Submission, SubmissionAdmin)
-admin.site.register(CoverLetter, SubmissionAdmin)
-
class PatchAdmin(admin.ModelAdmin):
list_display = ('name', 'submitter', 'project', 'state', 'date',
@@ -15,7 +15,7 @@ from patchwork.api.filters import CoverLetterFilterSet
from patchwork.api.embedded import PersonSerializer
from patchwork.api.embedded import ProjectSerializer
from patchwork.api.embedded import SeriesSerializer
-from patchwork.models import CoverLetter
+from patchwork.models import Submission
class CoverLetterListSerializer(BaseHyperlinkedModelSerializer):
@@ -24,7 +24,7 @@ class CoverLetterListSerializer(BaseHyperlinkedModelSerializer):
project = ProjectSerializer(read_only=True)
submitter = PersonSerializer(read_only=True)
mbox = SerializerMethodField()
- series = SeriesSerializer(read_only=True)
+ series = SeriesSerializer(source='cl_series', read_only=True)
comments = SerializerMethodField()
def get_web_url(self, instance):
@@ -49,7 +49,7 @@ class CoverLetterListSerializer(BaseHyperlinkedModelSerializer):
return data
class Meta:
- model = CoverLetter
+ model = Submission
fields = ('id', 'url', 'web_url', 'project', 'msgid',
'list_archive_url', 'date', 'name', 'submitter', 'mbox',
'series', 'comments')
@@ -82,7 +82,7 @@ class CoverLetterDetailSerializer(CoverLetterListSerializer):
return headers
class Meta:
- model = CoverLetter
+ model = Submission
fields = CoverLetterListSerializer.Meta.fields + (
'headers', 'content')
read_only_fields = fields
@@ -100,8 +100,8 @@ class CoverLetterList(ListAPIView):
ordering = 'id'
def get_queryset(self):
- return CoverLetter.objects.all()\
- .select_related('project', 'submitter', 'series')\
+ return Submission.objects.filter(new_diff__isnull=True, new_pull_url__isnull=True)\
+ .select_related('project', 'submitter', 'cl_series')\
.defer('content', 'headers')
@@ -111,5 +111,5 @@ class CoverLetterDetail(RetrieveAPIView):
serializer_class = CoverLetterDetailSerializer
def get_queryset(self):
- return CoverLetter.objects.all()\
- .select_related('project', 'submitter', 'series')
+ return Submission.objects.filter(new_diff__isnull=True, new_pull_url__isnull=True)\
+ .select_related('project', 'submitter', 'cl_series')
@@ -107,7 +107,7 @@ class CoverLetterSerializer(SerializedRelatedField):
class _Serializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer):
class Meta:
- model = models.CoverLetter
+ model = models.Submission
fields = ('id', 'url', 'web_url', 'msgid', 'list_archive_url',
'date', 'name', 'mbox')
read_only_fields = fields
@@ -15,13 +15,13 @@ from django.forms.widgets import MultipleHiddenInput
from patchwork.compat import NAME_FIELD
from patchwork.models import Bundle
from patchwork.models import Check
-from patchwork.models import CoverLetter
from patchwork.models import Event
from patchwork.models import Patch
from patchwork.models import Person
from patchwork.models import Project
from patchwork.models import Series
from patchwork.models import State
+from patchwork.models import Submission
# custom fields, filters
@@ -161,8 +161,13 @@ class CoverLetterFilterSet(TimestampMixin, FilterSet):
widget=MultipleHiddenInput)
submitter = PersonFilter(queryset=Person.objects.all())
+ @property
+ def qs(self):
+ parent = super(CoverLetterFilterSet, self).qs
+ return parent.filter(new_diff__isnull=True, new_pull_url__isnull=True)
+
class Meta:
- model = CoverLetter
+ model = Submission
fields = ('project', 'series', 'submitter')
@@ -191,6 +196,8 @@ class CheckFilterSet(TimestampMixin, FilterSet):
model = Check
fields = ('user', 'state', 'context')
+def get_coverletter_qs(request):
+ return Submission.objects.all().filter(new_diff__isnull=True, new_pull_url__isnull=True)
class EventFilterSet(TimestampMixin, FilterSet):
@@ -203,7 +210,7 @@ class EventFilterSet(TimestampMixin, FilterSet):
widget=MultipleHiddenInput)
patch = BaseFilter(queryset=Patch.objects.all(),
widget=MultipleHiddenInput)
- cover = BaseFilter(queryset=CoverLetter.objects.all(),
+ cover = BaseFilter(queryset=get_coverletter_qs,
widget=MultipleHiddenInput)
class Meta:
@@ -32,7 +32,7 @@ class Command(BaseCommand):
def handle(self, *args, **options):
results = {
models.Patch: 0,
- models.CoverLetter: 0,
+ models.Submission: 0,
models.Comment: 0,
}
duplicates = 0
@@ -118,7 +118,7 @@ class Command(BaseCommand):
' %(errors)4d errors\n'
'Total: %(new)s new entries' % {
'total': count,
- 'covers': results[models.CoverLetter],
+ 'covers': results[models.Submission],
'patches': results[models.Patch],
'comments': results[models.Comment],
'duplicates': duplicates,
new file mode 100644
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.24 on 2019-09-17 17:56
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('patchwork', '0040_drop_old_diff_pull_url'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='event',
+ name='cover',
+ field=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.Submission'),
+ ),
+ migrations.AlterField(
+ model_name='series',
+ name='cover_letter',
+ field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cl_series', to='patchwork.Submission'),
+ ),
+ migrations.DeleteModel(
+ name='CoverLetter',
+ ),
+ ]
@@ -389,6 +389,12 @@ class Submission(FilenameMixin, EmailMixin, models.Model):
def __str__(self):
return self.name
+ def get_absolute_url(self):
+ return reverse('cover-detail', kwargs={'cover_id': self.id})
+
+ def get_mbox_url(self):
+ return reverse('cover-mbox', kwargs={'cover_id': self.id})
+
class Meta:
ordering = ['date']
unique_together = [('msgid', 'project')]
@@ -402,15 +408,6 @@ class Submission(FilenameMixin, EmailMixin, models.Model):
]
-class CoverLetter(Submission):
-
- def get_absolute_url(self):
- return reverse('cover-detail', kwargs={'cover_id': self.id})
-
- def get_mbox_url(self):
- return reverse('cover-mbox', kwargs={'cover_id': self.id})
-
-
@python_2_unicode_compatible
class Patch(Submission):
# patch metadata
@@ -739,7 +736,7 @@ class Series(FilenameMixin, models.Model):
blank=True, on_delete=models.CASCADE)
# content
- cover_letter = models.OneToOneField(CoverLetter, related_name='series',
+ cover_letter = models.OneToOneField(Submission, related_name='cl_series',
null=True,
on_delete=models.CASCADE)
@@ -1030,7 +1027,7 @@ class Event(models.Model):
on_delete=models.CASCADE,
help_text='The series that this event was created for.')
cover = models.ForeignKey(
- CoverLetter, related_name='+', null=True, blank=True,
+ Submission, related_name='+', null=True, blank=True,
on_delete=models.CASCADE,
help_text='The cover letter that this event was created for.')
@@ -19,7 +19,6 @@ from django.db.utils import IntegrityError
from django.utils import six
from patchwork.models import Comment
-from patchwork.models import CoverLetter
from patchwork.models import DelegationRule
from patchwork.models import get_default_initial_patch_state
from patchwork.models import Patch
@@ -1088,11 +1087,11 @@ def parse_mail(mail, list_id=None):
if not is_comment:
if not refs == []:
try:
- CoverLetter.objects.all().get(name=name)
- except CoverLetter.DoesNotExist:
+ Submission.objects.get(name=name, new_diff__isnull=True, new_pull_url__isnull=True)
+ except Submission.DoesNotExist:
# if no match, this is a new cover letter
is_cover_letter = True
- except CoverLetter.MultipleObjectsReturned:
+ except Submission.MultipleObjectsReturned:
# if multiple cover letters are found, just ignore
pass
else:
@@ -1135,7 +1134,7 @@ def parse_mail(mail, list_id=None):
" in project %s!" % (msgid, project.name))
try:
- cover_letter = CoverLetter.objects.create(
+ cover_letter = Submission.objects.create(
msgid=msgid,
project=project,
name=name[:255],
@@ -10,11 +10,11 @@ from django.db.models.signals import pre_save
from django.dispatch import receiver
from patchwork.models import Check
-from patchwork.models import CoverLetter
from patchwork.models import Event
from patchwork.models import Patch
from patchwork.models import PatchChangeNotification
from patchwork.models import Series
+from patchwork.models import Submission
@receiver(pre_save, sender=Patch)
@@ -54,7 +54,7 @@ def patch_change_callback(sender, instance, raw, **kwargs):
notification.save()
-@receiver(post_save, sender=CoverLetter)
+@receiver(post_save, sender=Submission)
def create_cover_created_event(sender, instance, created, raw, **kwargs):
def create_event(cover):
@@ -68,12 +68,12 @@ function toggle_div(link_id, headers_id)
</div>
</td>
</tr>
-{% if submission.series %}
+{% if series %}
<tr>
<th>Series</th>
<td>
- <a href="{% url 'patch-list' project_id=project.linkname %}?series={{ submission.series.id }}">
- {{ submission.series }}
+ <a href="{% url 'patch-list' project_id=project.linkname %}?series={{ series.id }}">
+ {{ series }}
</a>
</td>
</tr>
@@ -85,7 +85,7 @@ function toggle_div(link_id, headers_id)
>show</a>
<div id="patchrelations" class="patchrelations" style="display:none;">
<ul>
- {% with submission.series.cover_letter as cover %}
+ {% with series.cover_letter as cover %}
<li>
{% if cover %}
{% if cover == submission %}
@@ -98,7 +98,7 @@ function toggle_div(link_id, headers_id)
{% endif %}
</li>
{% endwith %}
- {% for sibling in submission.series.patches.all %}
+ {% for sibling in series.patches.all %}
<li>
{% if sibling == submission %}
{{ sibling.name|default:"[no subject]"|truncatechars:100 }}
@@ -44,10 +44,9 @@ class TestCoverLetterAPI(utils.APITestCase):
self.assertEqual(cover_obj.submitter.id,
cover_json['submitter']['id'])
-
- if hasattr(cover_obj, 'series'):
+ if hasattr(cover_obj, 'cl_series'):
self.assertEqual(1, len(cover_json['series']))
- self.assertEqual(cover_obj.series.id,
+ self.assertEqual(cover_obj.cl_series.id,
cover_json['series'][0]['id'])
else:
self.assertEqual([], cover_json['series'])
@@ -36,7 +36,7 @@ class _BaseTestCase(TestCase):
mbox = mailbox.mbox(os.path.join(TEST_SERIES_DIR, name), create=False)
for msg in mbox:
obj = parser.parse_mail(msg, project.listid)
- if type(obj) == models.CoverLetter:
+ if type(obj) == models.Submission:
results[0].append(obj)
elif type(obj) == models.Patch:
results[1].append(obj)
@@ -75,14 +75,14 @@ class _BaseTestCase(TestCase):
patches_ = patches[start_idx:end_idx]
for patch in patches_:
- self.assertEqual(patch.series, series[idx])
-
# TODO(stephenfin): Rework this function into two different
# functions - we're clearly not always testing patches here
if isinstance(patch, models.Patch):
+ self.assertEqual(patch.series, series[idx])
self.assertEqual(series[idx].patches.get(id=patch.id),
patch)
else:
+ self.assertEqual(patch.cl_series, series[idx])
self.assertEqual(series[idx].cover_letter, patch)
start_idx = end_idx
@@ -660,13 +660,13 @@ class SeriesNameTestCase(TestCase):
cover = self._parse_mail(mbox[0])
cover_name = 'A sample series'
- self.assertEqual(cover.series.name, cover_name)
+ self.assertEqual(cover.cl_series.name, cover_name)
self._parse_mail(mbox[1])
- self.assertEqual(cover.series.name, cover_name)
+ self.assertEqual(cover.cl_series.name, cover_name)
self._parse_mail(mbox[2])
- self.assertEqual(cover.series.name, cover_name)
+ self.assertEqual(cover.cl_series.name, cover_name)
mbox.close()
@@ -714,7 +714,7 @@ class SeriesNameTestCase(TestCase):
self.assertEqual(patch.series.name, patch.name)
cover = self._parse_mail(mbox[2])
- self.assertEqual(cover.series.name, 'A sample series')
+ self.assertEqual(cover.cl_series.name, 'A sample series')
mbox.close()
@@ -14,13 +14,13 @@ from django.contrib.auth.models import User
from patchwork.models import Bundle
from patchwork.models import Check
from patchwork.models import Comment
-from patchwork.models import CoverLetter
from patchwork.models import Patch
from patchwork.models import Person
from patchwork.models import Project
from patchwork.models import Series
from patchwork.models import SeriesReference
from patchwork.models import State
+from patchwork.models import Submission
from patchwork.tests import TEST_PATCH_DIR
SAMPLE_DIFF = """--- /dev/null 2011-01-01 00:00:00.000000000 +0800
@@ -202,7 +202,7 @@ def create_patch(**kwargs):
def create_cover(**kwargs):
"""Create 'CoverLetter' object."""
- num = CoverLetter.objects.count()
+ num = Submission.objects.count() - Patch.objects.count()
# NOTE(stephenfin): Despite first appearances, passing 'series' to the
# 'create' function doesn't actually cause the relationship to be created.
@@ -230,7 +230,7 @@ def create_cover(**kwargs):
}
values.update(kwargs)
- cover = CoverLetter.objects.create(**values)
+ cover = Submission.objects.create(**values)
if series:
series.add_cover_letter(cover)
@@ -10,7 +10,7 @@ from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django.urls import reverse
-from patchwork.models import CoverLetter
+from patchwork.models import Patch
from patchwork.models import Submission
from patchwork.views.utils import cover_to_mbox
@@ -18,13 +18,14 @@ from patchwork.views.utils import cover_to_mbox
def cover_detail(request, cover_id):
# redirect to patches where necessary
try:
- cover = get_object_or_404(CoverLetter, id=cover_id)
- except Http404 as exc:
- submissions = Submission.objects.filter(id=cover_id)
- if submissions:
+ patch = get_object_or_404(Patch, id=cover_id)
+ if patch:
return HttpResponseRedirect(
reverse('patch-detail', kwargs={'patch_id': cover_id}))
- raise exc
+ except Http404:
+ pass
+
+ cover = get_object_or_404(Submission, id=cover_id)
context = {
'submission': cover,
@@ -36,12 +37,22 @@ def cover_detail(request, cover_id):
comments = comments.only('submitter', 'date', 'id', 'content',
'submission')
context['comments'] = comments
+ context['series'] = cover.cl_series
return render(request, 'patchwork/submission.html', context)
def cover_mbox(request, cover_id):
- cover = get_object_or_404(CoverLetter, id=cover_id)
+ # check if we should redirect to patch
+ try:
+ patch = get_object_or_404(Patch, id=cover_id)
+ if patch:
+ return HttpResponseRedirect(
+ reverse('patch-mbox', kwargs={'patch_id': cover_id}))
+ except Http404:
+ pass
+
+ cover = get_object_or_404(Submission, id=cover_id)
response = HttpResponse(content_type='text/plain')
response.write(cover_to_mbox(cover))
@@ -110,6 +110,7 @@ def patch_detail(request, patch_id):
context['patchform'] = form
context['createbundleform'] = createbundleform
context['project'] = patch.project
+ context['series'] = patch.series
return render(request, 'patchwork/submission.html', context)
A patch must have a diff or a pull_url. If a Submission does not have a diff or a pull_url, it must be a CoverLetter. This is enough to get rid of the CoverLetter model. We do have some work to do around the interactions with Series, which is currently super ugly, but it's good enough for a proof of concept. Signed-off-by: Daniel Axtens <dja@axtens.net> --- patchwork/admin.py | 3 -- patchwork/api/cover.py | 16 +++++----- patchwork/api/embedded.py | 2 +- patchwork/api/filters.py | 13 +++++++-- patchwork/management/commands/parsearchive.py | 4 +-- patchwork/migrations/0041_drop_coverletter.py | 29 +++++++++++++++++++ patchwork/models.py | 19 +++++------- patchwork/parser.py | 9 +++--- patchwork/signals.py | 4 +-- patchwork/templates/patchwork/submission.html | 10 +++---- patchwork/tests/api/test_cover.py | 5 ++-- patchwork/tests/test_series.py | 14 ++++----- patchwork/tests/utils.py | 6 ++-- patchwork/views/cover.py | 25 +++++++++++----- patchwork/views/patch.py | 1 + 15 files changed, 100 insertions(+), 60 deletions(-) create mode 100644 patchwork/migrations/0041_drop_coverletter.py