diff mbox

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

Message ID 1445378383-16977-14-git-send-email-damien.lespiau@intel.com
State Rejected
Headers show

Commit Message

Damien Lespiau Oct. 20, 2015, 9:59 p.m. UTC
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@intel.com>
---
 patchwork/bin/parsemail.py     | 46 +++++++++++++++++++++++++-
 patchwork/tests/test_series.py | 73 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 117 insertions(+), 2 deletions(-)
diff mbox

Patch

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)'))