[PATCH 13/14] series: New series with similar titles as previous ones are new revisions

Damien Lespiau damien.lespiau at intel.com
Wed Oct 21 08:59:42 AEDT 2015


There's another way than the one already implemented to update a series:
resend another full thread with some of the patches changed.

Because those two mail threads are separate, the only way is to try and
match the subject of their cover letters.

v2: Rebase on top of single patch update changes
v3: Rebase on top of the SERIES_DEFAULT_NAME change

Signed-off-by: Damien Lespiau <damien.lespiau at intel.com>
---
 patchwork/bin/parsemail.py     | 46 +++++++++++++++++++++++++-
 patchwork/tests/test_series.py | 73 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 117 insertions(+), 2 deletions(-)

diff --git a/patchwork/bin/parsemail.py b/patchwork/bin/parsemail.py
index 44bcb1c..efdcbb0 100755
--- a/patchwork/bin/parsemail.py
+++ b/patchwork/bin/parsemail.py
@@ -34,8 +34,11 @@ import logging
 from patchwork.parser import parse_patch
 from patchwork.models import Patch, Project, Person, Comment, State, Series, \
         SeriesRevision, SeriesRevisionPatch, get_default_initial_patch_state, \
-        SERIES_DEFAULT_NAME, get_default_initial_patch_state
+        get_default_initial_patch_state, series_revision_complete, \
+        SERIES_DEFAULT_NAME
 import django
+from django import dispatch
+from django.db.models import Q
 from django.conf import settings
 from django.contrib.auth.models import User
 from django.core.exceptions import MultipleObjectsReturned
@@ -559,6 +562,43 @@ def get_delegate(delegate_email):
             pass
     return None
 
+series_name_re = re.compile('[, \(]*(v|take)[\) 0-9]+$', re.I)
+def clean_series_name(str):
+    """Try to remove 'v2' and 'take 28' markers in cover letters subjects"""
+    str = series_name_re.sub('', str)
+    return str.strip()
+
+def on_revision_complete(sender, revision, **kwargs):
+    # Brand new series (revision.version == 1) may be updates to a Series
+    # previously posted. Hook the SeriesRevision to the previous series then.
+    if revision.version != 1:
+        return
+
+    new_series = revision.series
+    if new_series.name == SERIES_DEFAULT_NAME:
+        return
+
+    name = clean_series_name(new_series.name)
+    previous_series = Series.objects.filter(Q(project=new_series.project),
+                                            Q(name__iexact=name) &
+                                            ~Q(pk=new_series.pk))
+    if len(previous_series) != 1:
+        return
+
+
+    previous_series = previous_series[0]
+    new_revision = previous_series.latest_revision().duplicate_meta()
+    new_revision.root_msgid = revision.root_msgid
+    new_revision.cover_letter = revision.cover_letter
+    new_revision.save()
+    i = 1
+    for patch in revision.ordered_patches():
+        new_revision.add_patch(patch, i)
+        i += 1
+
+    revision.delete()
+    new_series.delete()
+
 def parse_mail(mail):
 
     # some basic sanity checks
@@ -592,6 +632,8 @@ def parse_mail(mail):
     series = content.series
     revision = content.revision
 
+    series_revision_complete.connect(on_revision_complete)
+
     if series:
         if save_required:
             author.save()
@@ -629,6 +671,8 @@ def parse_mail(mail):
         comment.msgid = msgid
         comment.save()
 
+    series_revision_complete.disconnect(on_revision_complete)
+
     return 0
 
 extra_error_message = '''
diff --git a/patchwork/tests/test_series.py b/patchwork/tests/test_series.py
index a277df4..373ef1b 100644
--- a/patchwork/tests/test_series.py
+++ b/patchwork/tests/test_series.py
@@ -25,7 +25,8 @@ from patchwork.models import Patch, Series, SeriesRevision, Project, \
 from patchwork.tests.utils import read_mail
 from patchwork.tests.utils import defaults, read_mail, TestSeries
 
-from patchwork.bin.parsemail import parse_mail, build_references_list
+from patchwork.bin.parsemail import parse_mail, build_references_list, \
+                                    clean_series_name
 
 class SeriesTest(TestCase):
     fixtures = ['default_states']
@@ -457,3 +458,73 @@ class SinglePatchUpdatesVariousCornerCasesTest(TestCase):
         self.assertEqual(len(patches), 2)
         self.assertEqual(patches[0].name, '[1/2] ' + defaults.patch_name)
         self.assertEqual(patches[1].name, '[v2] ' + defaults.patch_name)
+
+#
+# New version of a full series (separate mail thread)
+#
+
+class FullSeriesUpdateTest(GeneratedSeriesTest):
+
+    def check_revision(self, series, revision, mails):
+        n_patches = len(mails)
+        if self.has_cover_letter:
+            n_patches -= 1
+
+        self.assertEquals(revision.series_id, series.id)
+        self.assertEquals(revision.root_msgid, mails[0].get('Message-Id'))
+        self.assertEquals(revision.patches.count(), n_patches)
+
+        i = 1 if self.has_cover_letter else 0
+        for patch in revision.ordered_patches():
+            patch_mail = mails[i]
+            self.assertEquals(patch.msgid, patch_mail.get('Message-Id'))
+            i += 1
+
+    def check(self, series1_mails, series2_mails):
+        self.assertEquals(Series.objects.count(), 1)
+        series = Series.objects.all()[0]
+        self.assertEquals(series.version, 2)
+
+        revisions = SeriesRevision.objects.all()
+        self.assertEquals(revisions.count(), 2)
+
+        self.check_revision(series, revisions[0], series1_mails)
+        self.check_revision(series, revisions[1], series2_mails)
+
+    def _set_cover_letter_subject(self, mail, n_patches, subject):
+        del mail['Subject']
+        mail['Subject'] = '[PATCH 0/%d] %s' % (n_patches, subject)
+
+    def _test_internal(self, n_patches, subjects):
+
+        (series1, series1_mails) = self._create_series(n_patches)
+        self._set_cover_letter_subject(series1_mails[0], n_patches, subjects[0])
+        self.series_name = subjects[0]
+        series1.insert(series1_mails)
+        self.commonInsertionChecks()
+
+        (series2,series2_mails) = self._create_series(n_patches)
+        self._set_cover_letter_subject(series2_mails[0], n_patches, subjects[1])
+        series2.insert(series2_mails)
+
+        self.check(series1_mails, series2_mails)
+
+    def testCleanSeriesName(self):
+        cases = (
+            ('Awesome series', 'Awesome series'),
+            ('Awesome series', 'Awesome series v2'),
+            ('Awesome series', 'Awesome series V2'),
+            ('Awesome series', 'Awesome series, v3'),
+            ('Awesome series', 'Awesome series (v4)'),
+            ('Awesome series', 'Awesome series (take 5)'),
+            ('Awesome series', 'Awesome series (Take 5)'),
+            ('Awesome series', 'Awesome series, take 6'),
+        )
+        for case in cases:
+            self.assertEquals(clean_series_name(case[1]), case[0])
+
+    def testNewSeries(self):
+        self._test_internal(3, ('Awesome series', 'Awesome series (v4)'))
+
+    def testNewSeriesIgnoreCase(self):
+        self._test_internal(3, ('Awesome series', 'awesome series (V4)'))
-- 
2.4.3



More information about the Patchwork mailing list