From patchwork Fri Oct 9 10:39:55 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Damien Lespiau X-Patchwork-Id: 528173 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42F9E1402B3 for ; Fri, 9 Oct 2015 21:41:26 +1100 (AEDT) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 28F991A039F for ; Fri, 9 Oct 2015 21:41:26 +1100 (AEDT) X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Received: from mga03.intel.com (mga03.intel.com [134.134.136.65]) by lists.ozlabs.org (Postfix) with ESMTP id 821381A03C2 for ; Fri, 9 Oct 2015 21:40:11 +1100 (AEDT) Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by orsmga103.jf.intel.com with ESMTP; 09 Oct 2015 03:40:11 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.17,657,1437462000"; d="scan'208";a="823048693" Received: from mschrift-mobl.amr.corp.intel.com (HELO strange.amr.corp.intel.com) ([10.252.207.4]) by fmsmga002.fm.intel.com with ESMTP; 09 Oct 2015 03:40:09 -0700 From: Damien Lespiau To: patchwork@lists.ozlabs.org Subject: [PATCH 08/15] series: Create Series objects when parsing mails Date: Fri, 9 Oct 2015 11:39:55 +0100 Message-Id: <1444387202-25735-9-git-send-email-damien.lespiau@intel.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1444387202-25735-1-git-send-email-damien.lespiau@intel.com> References: <1444387202-25735-1-git-send-email-damien.lespiau@intel.com> X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" This commit only adds basic support for the initial series submission. Series with or without cover letters are supported. When sent without a cover letter, the series is named "Untitled series". It's planned to let the user chose a more appropriate series title through the web UI at a later point. Single patches are treated as a Series of 1 patch, named with the subject of that patch. Signed-off-by: Damien Lespiau Acked-by: Stephen Finucane --- patchwork/bin/parsemail.py | 86 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/patchwork/bin/parsemail.py b/patchwork/bin/parsemail.py index 8bbb7ee..1e7e083 100755 --- a/patchwork/bin/parsemail.py +++ b/patchwork/bin/parsemail.py @@ -31,8 +31,8 @@ from email.utils import parsedate_tz, mktime_tz import logging from patchwork.parser import parse_patch -from patchwork.models import Patch, Project, Person, Comment, State, \ - get_default_initial_patch_state +from patchwork.models import Patch, Project, Person, Comment, State, Series, \ + SeriesRevision, get_default_initial_patch_state import django from django.conf import settings from django.contrib.auth.models import User @@ -159,6 +159,9 @@ class MailContent: def __init__(self): self.patch = None self.comment = None + self.series = None + self.revision = None + self.patch_order = 1 # place of the patch in the series def build_references_list(mail): # construct a list of possible reply message ids @@ -245,9 +248,36 @@ def find_content(project, mail): ret = MailContent() + (name, prefixes) = clean_subject(mail.get('Subject'), [project.linkname]) + (x, n) = parse_series_marker(prefixes) + refs = build_references_list(mail) + is_root = refs == [] + is_cover_letter = is_root and x == 0 + + if is_cover_letter or patchbuf: + msgid = mail.get('Message-Id').strip() + + # Series get a generic name when they don't start by a cover letter or + # when they haven't received the root message yet. Except when it's + # only 1 patch, then the series takes the patch subject as name. + series_name = None + if is_cover_letter or n is None: + series_name = strip_prefixes(name) + + (ret.series, ret.revision) = find_series_for_mail(project, series_name, + msgid, refs) + ret.series.n_patches = n or 1 + + date = mail_date(mail) + if not ret.series.submitted or date < ret.series.submitted: + ret.series.submitted = date + + if is_cover_letter: + ret.revision.cover_letter = clean_content(commentbuf) + return ret + if pullurl or patchbuf: - (name, prefixes) = clean_subject(mail.get('Subject'), - [project.linkname]) + ret.patch_order = x or 1 ret.patch = Patch(name = name, pull_url = pullurl, content = patchbuf, date = mail_date(mail), headers = mail_headers(mail)) @@ -260,7 +290,6 @@ def find_content(project, mail): headers = mail_headers(mail)) else: - refs = build_references_list(mail) cpatch = find_patch_for_comment(project, refs) if not cpatch: return ret @@ -268,8 +297,35 @@ def find_content(project, mail): content = clean_content(commentbuf), headers = mail_headers(mail)) + # make sure we always have a valid (series,revision) tuple if we have a + # patch. We don't consider pull requests a series. + if ret.patch and not pullurl and (not ret.series or not ret.revision): + raise Exception("Could not find series for: %s" % name) + return ret +# The complexity here is because patches can be received out of order: +# If we receive a patch, part of series, before the root message, we create a +# placeholder series that will be updated once we receive the root message. +def find_series_for_mail(project, name, msgid, refs): + if refs == []: + root_msgid = msgid + else: + root_msgid = refs[-1] + + try: + revision = SeriesRevision.objects.get(root_msgid = root_msgid) + series = revision.series + if name: + series.name = name + except SeriesRevision.DoesNotExist: + if not name: + name = "Untitled series" + series = Series(name=name) + revision = SeriesRevision(root_msgid = root_msgid) + + return (series, revision) + def find_patch_for_comment(project, refs): for ref in refs: patch = None @@ -344,6 +400,10 @@ def clean_subject(subject, drop_prefixes = None): return (subject, prefixes) +prefixes_re = re.compile('^\[[^\]]*\]\s*') +def strip_prefixes(subject): + return prefixes_re.sub('', subject) + sig_re = re.compile('^(-- |_+)\n.*', re.S | re.M) def clean_content(str): """ Try to remove signature (-- ) and list footer (_____) cruft """ @@ -398,6 +458,20 @@ def parse_mail(mail): return 0 patch = content.patch comment = content.comment + series = content.series + revision = content.revision + + if series: + if save_required: + author.save() + save_required = False + series.project = project + series.submitter = author + series.save() + + if revision: + revision.series = series + revision.save() if patch: # we delay the saving until we know we have a patch. @@ -411,6 +485,8 @@ def parse_mail(mail): patch.delegate = get_delegate( mail.get('X-Patchwork-Delegate', '').strip()) patch.save() + if revision: + revision.add_patch(patch, content.patch_order) if comment: if save_required: