def create_tag_comment(self, patch, tagtype=None): comment = Comment(patch=patch, msgid=str(datetime.datetime.now()), submitter=defaults.patch_author_person, content=self.create_tag(tagtype)) comment.save() return comment
def create_comment(**kwargs): """Create 'Comment' object.""" values = { 'submitter': create_person(), 'submission': create_patch(), 'msgid': make_msgid(), 'content': SAMPLE_CONTENT, } values.update(kwargs) comment = Comment(**values) comment.save() return comment
def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project = defaults.project, msgid = 'p1', name = 'testpatch', submitter = self.person, content = '') self.patch.save() self.txt = 'some comment\n---\n some/file | 1 +\n' comment = Comment(patch = self.patch, msgid = 'p1', submitter = self.person, content = self.txt) comment.save()
def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, diff='', content='comment 1 text\nAcked-by: 1\n') self.patch.save() comment = Comment(submission=self.patch, msgid='p2', submitter=self.person, content='comment 2 text\nAcked-by: 2\n') comment.save()
def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, content='') self.patch.save() self.txt = 'some comment\n---\n some/file | 1 +\n' comment = Comment(patch=self.patch, msgid='p1', submitter=self.person, content=self.txt) comment.save()
def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, content='') self.patch.save() comment = Comment(patch=self.patch, msgid='p1', submitter=self.person, content='comment 1 text\nAcked-by: 1\n---\nupdate\n') comment.save() comment = Comment(patch=self.patch, msgid='p2', submitter=self.person, content='comment 2 text\nAcked-by: 2\n') comment.save()
def parse_mail(mail): # some basic sanity checks if 'From' not in mail: LOGGER.debug("Ignoring mail due to missing 'From'") return 1 if 'Subject' not in mail: LOGGER.debug("Ignoring mail due to missing 'Subject'") return 1 if 'Message-Id' not in mail: LOGGER.debug("Ignoring mail due to missing 'Message-Id'") return 1 hint = mail.get('X-Patchwork-Hint', '').lower() if hint == 'ignore': LOGGER.debug("Ignoring mail due to 'ignore' hint") return 0 project = find_project(mail) if project is None: LOGGER.error('Failed to find a project for mail') return 1 (author, save_required) = find_author(mail) content = find_content(project, mail) if not content: return 0 patch = content.patch comment = content.comment series = content.series revision = content.revision msgid = (content.msgid or mail.get('Message-Id').strip()) series_revision_complete.connect(on_revision_complete) if series: if save_required: author.save() save_required = False series.project = project series.submitter = author series.save() LOGGER.debug('Series saved') if revision: revision.series = series revision.save() LOGGER.debug('Revision saved') if patch: delegate = get_delegate(mail.get('X-Patchwork-Delegate', '').strip()) if not delegate: delegate = auto_delegate(project, content.filenames) # we delay the saving until we know we have a patch. if save_required: author.save() save_required = False patch.submitter = author patch.msgid = msgid patch.project = project patch.state = get_state(mail.get('X-Patchwork-State', '').strip()) patch.delegate = delegate patch.save() if revision: revision.add_patch(patch, content.patch_order) LOGGER.debug('Patch saved') if comment: if save_required: author.save() # we defer this assignment until we know that we have a saved patch if patch: comment.patch = patch comment.submitter = author comment.msgid = msgid comment.save() LOGGER.debug('Comment saved') # if comment's author has project-maintainer permissions, # parse comment content and process the status-change command # if it is found if author.user and project in \ (author.user).profile.maintainer_projects.all(): cmd = None comment_re = re.compile('^\[Patchwork-Status:\s*(Under Review|\ Rejected|RFC|Not Applicable|Changes Requested|Awaiting Upstream|Superseded|\ Deferred)\]$', re.M | re.I) # if multiple valid status-change commands are found, use last one for match in comment_re.finditer(comment.content): cmd = re.sub(r'(\[Patchwork-Status:\s*)(.*)(\])', r'\2', "{}".format(match.group(0))) if cmd is not None: new_state = State.objects.get(name=cmd.title()) mod_patch = Patch.objects.get(pk=comment.patch.pk) if new_state and mod_patch.state != new_state: mod_patch.state = new_state mod_patch.save() cmd_message = 'This is a system generated Comment: Patch \ %s status was updated to "%s" due to request in comment.' % ( comment.patch.pk, cmd) cmd_msgid = "%s: System generated by comment %s" % ( datetime.datetime.now().strftime("\ %d%b%Y.%H:%M:%S.%f"), comment.pk) new_comment = Comment(pk=None, patch=comment.patch, content=cmd_message, date=datetime.datetime.now(), submitter=comment.submitter, msgid=cmd_msgid) new_comment.save() series_revision_complete.disconnect(on_revision_complete) return 0
def create_tag_comment(self, patch, tagtype = None): comment = Comment(patch=patch, msgid=str(datetime.datetime.now()), submitter=defaults.patch_author_person, content=self.create_tag(tagtype)) comment.save() return comment
def parse_mail(mail, list_id=None): """Parse a mail and add to the database. Args: mail (`mbox.Mail`): Mail to parse and add. list_id (str): Mailing list ID Returns: None """ # some basic sanity checks if 'From' not in mail: raise ValueError("Missing 'From' header") if 'Subject' not in mail: raise ValueError("Missing 'Subject' header") if 'Message-Id' not in mail: raise ValueError("Missing 'Message-Id' header") hint = clean_header(mail.get('X-Patchwork-Hint', '')) if hint and hint.lower() == 'ignore': logger.debug("Ignoring email due to 'ignore' hint") return if list_id: project = find_project_by_id(list_id) else: project = find_project_by_header(mail) if project is None: logger.error('Failed to find a project for email') return # parse metadata msgid = clean_header(mail.get('Message-Id')) if not msgid: raise ValueError("Broken 'Message-Id' header") msgid = msgid[:255] author = find_author(mail) subject = mail.get('Subject') name, prefixes = clean_subject(subject, [project.linkname]) is_comment = subject_check(subject) x, n = parse_series_marker(prefixes) version = parse_version(name, prefixes) refs = find_references(mail) date = find_date(mail) headers = find_headers(mail) # parse content if not is_comment: diff, message = find_patch_content(mail) else: diff, message = find_comment_content(mail) if not (diff or message): return # nothing to work with pull_url = parse_pull_request(message) # build objects if not is_comment and (diff or pull_url): # patches or pull requests # we delay the saving until we know we have a patch. author.save() delegate = find_delegate_by_header(mail) if not delegate and diff: filenames = find_filenames(diff) delegate = find_delegate_by_filename(project, filenames) # if we don't have a series marker, we will never have an existing # series to match against. series = None if n: series = find_series(project, mail) else: x = n = 1 # We will create a new series if: # - there is no existing series to assign this patch to, or # - there is an existing series, but it already has a patch with this # number in it if not series or ( SeriesPatch.objects.filter(series=series, number=x).count()): series = Series(project=project, date=date, submitter=author, version=version, total=n) series.save() # NOTE(stephenfin) We must save references for series. We # do this to handle the case where a later patch is # received first. Without storing references, it would not # be possible to identify the relationship between patches # as the earlier patch does not reference the later one. for ref in refs + [msgid]: ref = ref[:255] # we don't want duplicates try: # we could have a ref to a previous series. (For # example, a series sent in reply to another # series.) That should not create a series ref # for this series, so check for the msg-id only, # not the msg-id/series pair. SeriesReference.objects.get(msgid=ref, series__project=project) except SeriesReference.DoesNotExist: SeriesReference.objects.create(series=series, msgid=ref) patch = Patch( msgid=msgid, project=project, name=name[:255], date=date, headers=headers, submitter=author, content=message, diff=diff, pull_url=pull_url, delegate=delegate, state=find_state(mail)) patch.save() logger.debug('Patch saved') # add to a series if we have found one, and we have a numbered # patch. Don't add unnumbered patches (for example diffs sent # in reply, or just messages with random refs/in-reply-tos) if series and x: series.add_patch(patch, x) return patch elif x == 0: # (potential) cover letters # if refs are empty, it's implicitly a cover letter. If not, # however, we need to see if a match already exists and, if # not, assume that it is indeed a new cover letter is_cover_letter = False if not is_comment: if not refs == []: try: CoverLetter.objects.all().get(name=name) except CoverLetter.DoesNotExist: # if no match, this is a new cover letter is_cover_letter = True except CoverLetter.MultipleObjectsReturned: # if multiple cover letters are found, just ignore pass else: is_cover_letter = True if is_cover_letter: author.save() # we don't use 'find_series' here as a cover letter will # always be the first item in a thread, thus the references # could only point to a different series or unrelated # message try: series = SeriesReference.objects.get( msgid=msgid, series__project=project).series except SeriesReference.DoesNotExist: series = None if not series: series = Series(project=project, date=date, submitter=author, version=version, total=n) series.save() # we don't save the in-reply-to or references fields # for a cover letter, as they can't refer to the same # series SeriesReference.objects.get_or_create(series=series, msgid=msgid) cover_letter = CoverLetter( msgid=msgid, project=project, name=name[:255], date=date, headers=headers, submitter=author, content=message) cover_letter.save() logger.debug('Cover letter saved') series.add_cover_letter(cover_letter) return cover_letter # comments # we only save comments if we have the parent email submission = find_submission_for_comment(project, refs) if not submission: return author.save() comment = Comment( submission=submission, msgid=msgid, date=date, headers=headers, submitter=author, content=message) comment.save() logger.debug('Comment saved') return comment
def parse_mail(mail, list_id=None): """Parse a mail and add to the database. Args: mail (`mbox.Mail`): Mail to parse and add. list_id (str): Mailing list ID Returns: patch/cover letter/comment Or None if nothing is found in the mail or X-P-H: ignore or project not found Raises: ValueError if there is an error in parsing or a duplicate mail Other truly unexpected issues may bubble up from the DB. """ # some basic sanity checks if 'From' not in mail: raise ValueError("Missing 'From' header") if 'Subject' not in mail: raise ValueError("Missing 'Subject' header") if 'Message-Id' not in mail: raise ValueError("Missing 'Message-Id' header") hint = clean_header(mail.get('X-Patchwork-Hint', '')) if hint and hint.lower() == 'ignore': logger.debug("Ignoring email due to 'ignore' hint") return project = find_project(mail, list_id) if project is None: logger.error('Failed to find a project for email') return # parse metadata msgid = clean_header(mail.get('Message-Id')) if not msgid: raise ValueError("Broken 'Message-Id' header") msgid = msgid[:255] subject = mail.get('Subject') name, prefixes = clean_subject(subject, [project.linkname]) is_comment = subject_check(subject) x, n = parse_series_marker(prefixes) version = parse_version(name, prefixes) refs = find_references(mail) date = find_date(mail) headers = find_headers(mail) # parse content if not is_comment: diff, message = find_patch_content(mail) else: diff, message = find_comment_content(mail) if not (diff or message): return # nothing to work with pull_url = parse_pull_request(message) # build objects if not is_comment and (diff or pull_url): # patches or pull requests # we delay the saving until we know we have a patch. author = get_or_create_author(mail) delegate = find_delegate_by_header(mail) if not delegate and diff: filenames = find_filenames(diff) delegate = find_delegate_by_filename(project, filenames) try: patch = Patch.objects.create(msgid=msgid, project=project, patch_project=project, name=name[:255], date=date, headers=headers, submitter=author, content=message, diff=diff, pull_url=pull_url, delegate=delegate, state=find_state(mail)) logger.debug('Patch saved') except IntegrityError: logger.error("Duplicate mail for message ID %s" % msgid) return None # if we don't have a series marker, we will never have an existing # series to match against. series = None if n: series = find_series(project, mail, author) else: x = n = 1 # We will create a new series if: # - there is no existing series to assign this patch to, or # - there is an existing series, but it already has a patch with this # number in it if not series or (SeriesPatch.objects.filter(series=series, number=x).count()): series = Series(project=project, date=date, submitter=author, version=version, total=n) series.save() # NOTE(stephenfin) We must save references for series. We # do this to handle the case where a later patch is # received first. Without storing references, it would not # be possible to identify the relationship between patches # as the earlier patch does not reference the later one. for ref in refs + [msgid]: ref = ref[:255] # we don't want duplicates try: # we could have a ref to a previous series. (For # example, a series sent in reply to another # series.) That should not create a series ref # for this series, so check for the msg-id only, # not the msg-id/series pair. SeriesReference.objects.get(msgid=ref, series__project=project) except SeriesReference.DoesNotExist: SeriesReference.objects.create(series=series, msgid=ref) except SeriesReference.MultipleObjectsReturned: logger.error("Multiple SeriesReferences for %s" " in project %s!" % (ref, project.name)) # add to a series if we have found one, and we have a numbered # patch. Don't add unnumbered patches (for example diffs sent # in reply, or just messages with random refs/in-reply-tos) if series and x: series.add_patch(patch, x) return patch elif x == 0: # (potential) cover letters # if refs are empty, it's implicitly a cover letter. If not, # however, we need to see if a match already exists and, if # not, assume that it is indeed a new cover letter is_cover_letter = False if not is_comment: if not refs == []: try: CoverLetter.objects.all().get(name=name) except CoverLetter.DoesNotExist: # if no match, this is a new cover letter is_cover_letter = True except CoverLetter.MultipleObjectsReturned: # if multiple cover letters are found, just ignore pass else: is_cover_letter = True if is_cover_letter: author = get_or_create_author(mail) # we don't use 'find_series' here as a cover letter will # always be the first item in a thread, thus the references # could only point to a different series or unrelated # message try: series = SeriesReference.objects.get( msgid=msgid, series__project=project).series except SeriesReference.DoesNotExist: series = None except SeriesReference.MultipleObjectsReturned: logger.error("Multiple SeriesReferences for %s" " in project %s!" % (msgid, project.name)) series = SeriesReference.objects.filter( msgid=msgid, series__project=project).first().series if not series: series = Series(project=project, date=date, submitter=author, version=version, total=n) series.save() # we don't save the in-reply-to or references fields # for a cover letter, as they can't refer to the same # series try: SeriesReference.objects.get_or_create(series=series, msgid=msgid) except SeriesReference.MultipleObjectsReturned: logger.error("Multiple SeriesReferences for %s" " in project %s!" % (msgid, project.name)) cover_letter = CoverLetter(msgid=msgid, project=project, name=name[:255], date=date, headers=headers, submitter=author, content=message) cover_letter.save() logger.debug('Cover letter saved') series.add_cover_letter(cover_letter) return cover_letter # comments # we only save comments if we have the parent email submission = find_submission_for_comment(project, refs) if not submission: return author = get_or_create_author(mail) comment = Comment(submission=submission, msgid=msgid, date=date, headers=headers, submitter=author, content=message) comment.save() logger.debug('Comment saved') return comment
def parse_mail(mail, list_id=None): """Parse a mail and add to the database. Args: mail (`mbox.Mail`): Mail to parse and add. list_id (str): Mailing list ID Returns: None """ # some basic sanity checks if 'From' not in mail: raise ValueError("Missing 'From' header") if 'Subject' not in mail: raise ValueError("Missing 'Subject' header") if 'Message-Id' not in mail: raise ValueError("Missing 'Message-Id' header") hint = mail.get('X-Patchwork-Hint', '').lower() if hint == 'ignore': LOGGER.debug("Ignoring email due to 'ignore' hint") return if list_id: project = find_project_by_id(list_id) else: project = find_project_by_header(mail) if project is None: LOGGER.error('Failed to find a project for email') return # parse content diff, message = find_content(project, mail) if not (diff or message): return # nothing to work with msgid = mail.get('Message-Id').strip() author = find_author(mail) name, prefixes = clean_subject(mail.get('Subject'), [project.linkname]) x, n = parse_series_marker(prefixes) refs = find_references(mail) date = find_date(mail) headers = find_headers(mail) pull_url = find_pull_request(message) # build objects if diff or pull_url: # patches or pull requests # we delay the saving until we know we have a patch. author.save() delegate = find_delegate(mail) if not delegate and diff: filenames = patch_get_filenames(diff) delegate = auto_delegate(project, filenames) patch = Patch( msgid=msgid, project=project, name=name, date=date, headers=headers, submitter=author, content=message, diff=diff, pull_url=pull_url, delegate=delegate, state=find_state(mail)) patch.save() LOGGER.debug('Patch saved') return patch elif x == 0: # (potential) cover letters # if refs are empty, it's implicitly a cover letter. If not, # however, we need to see if a match already exists and, if # not, assume that it is indeed a new cover letter is_cover_letter = False if not refs == []: try: CoverLetter.objects.all().get(name=name) except CoverLetter.DoesNotExist: # no match => new cover is_cover_letter = True else: is_cover_letter = True if is_cover_letter: author.save() cover_letter = CoverLetter( msgid=msgid, project=project, name=name, date=date, headers=headers, submitter=author, content=message) cover_letter.save() LOGGER.debug('Cover letter saved') return cover_letter # comments # we only save comments if we have the parent email submission = find_submission_for_comment(project, refs) if not submission: return author.save() comment = Comment( submission=submission, msgid=msgid, date=date, headers=headers, submitter=author, content=message) comment.save() LOGGER.debug('Comment saved') return comment
def parse_mail(mail, list_id=None): """Parse a mail and add to the database. Args: mail (`mbox.Mail`): Mail to parse and add. list_id (str): Mailing list ID Returns: None """ # some basic sanity checks if 'From' not in mail: raise ValueError("Missing 'From' header") if 'Subject' not in mail: raise ValueError("Missing 'Subject' header") if 'Message-Id' not in mail: raise ValueError("Missing 'Message-Id' header") hint = mail.get('X-Patchwork-Hint', '').lower() if hint == 'ignore': logger.debug("Ignoring email due to 'ignore' hint") return if list_id: project = find_project_by_id(list_id) else: project = find_project_by_header(mail) if project is None: logger.error('Failed to find a project for email') return # parse content diff, message = find_content(project, mail) if not (diff or message): return # nothing to work with msgid = mail.get('Message-Id').strip() author = find_author(mail) subject = mail.get('Subject') name, prefixes = clean_subject(subject, [project.linkname]) is_comment = subject_check(subject) x, n = parse_series_marker(prefixes) refs = find_references(mail) date = find_date(mail) headers = find_headers(mail) pull_url = parse_pull_request(message) # build objects if not is_comment and (diff or pull_url): # patches or pull requests # we delay the saving until we know we have a patch. author.save() delegate = find_delegate(mail) if not delegate and diff: filenames = find_filenames(diff) delegate = auto_delegate(project, filenames) patch = Patch(msgid=msgid, project=project, name=name, date=date, headers=headers, submitter=author, content=message, diff=diff, pull_url=pull_url, delegate=delegate, state=find_state(mail)) patch.save() logger.debug('Patch saved') return patch elif x == 0: # (potential) cover letters # if refs are empty, it's implicitly a cover letter. If not, # however, we need to see if a match already exists and, if # not, assume that it is indeed a new cover letter is_cover_letter = False if not is_comment: if not refs == []: try: CoverLetter.objects.all().get(name=name) except CoverLetter.DoesNotExist: # if no match, this is a new cover letter is_cover_letter = True except CoverLetter.MultipleObjectsReturned: # if multiple cover letters are found, just ignore pass else: is_cover_letter = True if is_cover_letter: author.save() cover_letter = CoverLetter(msgid=msgid, project=project, name=name, date=date, headers=headers, submitter=author, content=message) cover_letter.save() logger.debug('Cover letter saved') return cover_letter # comments # we only save comments if we have the parent email submission = find_submission_for_comment(project, refs) if not submission: return if is_comment and diff: message += diff author.save() comment = Comment(submission=submission, msgid=msgid, date=date, headers=headers, submitter=author, content=message) comment.save() logger.debug('Comment saved') return comment
def parse_mail(mail, list_id=None): """Parse a mail and add to the database. Args: mail (`mbox.Mail`): Mail to parse and add. list_id (str): Mailing list ID Returns: None """ # some basic sanity checks if 'From' not in mail: raise ValueError("Missing 'From' header") if 'Subject' not in mail: raise ValueError("Missing 'Subject' header") if 'Message-Id' not in mail: raise ValueError("Missing 'Message-Id' header") hint = mail.get('X-Patchwork-Hint', '').lower() if hint == 'ignore': logger.debug("Ignoring email due to 'ignore' hint") return if list_id: project = find_project_by_id(list_id) else: project = find_project_by_header(mail) if project is None: logger.error('Failed to find a project for email') return # parse content diff, message = find_content(project, mail) if not (diff or message): return # nothing to work with msgid = mail.get('Message-Id').strip() author = find_author(mail) subject = mail.get('Subject') name, prefixes = clean_subject(subject, [project.linkname]) is_comment = subject_check(subject) x, n = parse_series_marker(prefixes) version = parse_version(name, prefixes) refs = find_references(mail) date = find_date(mail) headers = find_headers(mail) pull_url = parse_pull_request(message) # build objects if not is_comment and (diff or pull_url): # patches or pull requests # we delay the saving until we know we have a patch. author.save() delegate = find_delegate(mail) if not delegate and diff: filenames = find_filenames(diff) delegate = auto_delegate(project, filenames) series = find_series(mail) # We will create a new series if: # - we have a patch number (x of n), and # - either: # * there is no series, or # * the version doesn't match # * we have a patch with this number already if n and ( (not series) or (series.version != version) or (SeriesPatch.objects.filter(series=series, number=x).count())): series = Series(date=date, submitter=author, version=version, total=n) series.save() # NOTE(stephenfin) We must save references for series. We # do this to handle the case where a later patch is # received first. Without storing references, it would not # be possible to identify the relationship between patches # as the earlier patch does not reference the later one. for ref in refs + [msgid]: # we don't want duplicates try: # we could have a ref to a previous series. (For # example, a series sent in reply to another # series.) That should not create a series ref # for this series, so check for the msg-id only, # not the msg-id/series pair. SeriesReference.objects.get(msgid=ref) except SeriesReference.DoesNotExist: SeriesReference.objects.create(series=series, msgid=ref) patch = Patch(msgid=msgid, project=project, name=name, date=date, headers=headers, submitter=author, content=message, diff=diff, pull_url=pull_url, delegate=delegate, state=find_state(mail)) patch.save() logger.debug('Patch saved') # add to a series if we have found one, and we have a numbered # patch. Don't add unnumbered patches (for example diffs sent # in reply, or just messages with random refs/in-reply-tos) if series and x: series.add_patch(patch, x) return patch elif x == 0: # (potential) cover letters # if refs are empty, it's implicitly a cover letter. If not, # however, we need to see if a match already exists and, if # not, assume that it is indeed a new cover letter is_cover_letter = False if not is_comment: if not refs == []: try: CoverLetter.objects.all().get(name=name) except CoverLetter.DoesNotExist: # if no match, this is a new cover letter is_cover_letter = True except CoverLetter.MultipleObjectsReturned: # if multiple cover letters are found, just ignore pass else: is_cover_letter = True if is_cover_letter: author.save() # we don't use 'find_series' here as a cover letter will # always be the first item in a thread, thus the references # could only point to a different series or unrelated # message try: series = SeriesReference.objects.get(msgid=msgid).series except SeriesReference.DoesNotExist: series = None if not series: series = Series(date=date, submitter=author, version=version, total=n) series.save() # we don't save the in-reply-to or references fields # for a cover letter, as they can't refer to the same # series SeriesReference.objects.get_or_create(series=series, msgid=msgid) cover_letter = CoverLetter(msgid=msgid, project=project, name=name, date=date, headers=headers, submitter=author, content=message) cover_letter.save() logger.debug('Cover letter saved') series.add_cover_letter(cover_letter) return cover_letter # comments # we only save comments if we have the parent email submission = find_submission_for_comment(project, refs) if not submission: return if is_comment and diff: message += diff author.save() comment = Comment(submission=submission, msgid=msgid, date=date, headers=headers, submitter=author, content=message) comment.save() logger.debug('Comment saved') return comment
def parse_mail(mail): # some basic sanity checks if 'From' not in mail: LOGGER.debug("Ignoring mail due to missing 'From'") return 1 if 'Subject' not in mail: LOGGER.debug("Ignoring mail due to missing 'Subject'") return 1 if 'Message-Id' not in mail: LOGGER.debug("Ignoring mail due to missing 'Message-Id'") return 1 hint = mail.get('X-Patchwork-Hint', '').lower() if hint == 'ignore': LOGGER.debug("Ignoring mail due to 'ignore' hint") return 0 project = find_project(mail) if project is None: LOGGER.error('Failed to find a project for mail') return 1 (author, save_required) = find_author(mail) content = find_content(project, mail) if not content: return 0 patch = content.patch comment = content.comment series = content.series revision = content.revision msgid = (content.msgid or mail.get('Message-Id').strip()) series_revision_complete.connect(on_revision_complete) if series: if save_required: author.save() save_required = False series.project = project series.submitter = author series.save() LOGGER.debug('Series saved') if revision: revision.series = series revision.save() LOGGER.debug('Revision saved') if patch: delegate = get_delegate(mail.get('X-Patchwork-Delegate', '').strip()) if not delegate: delegate = auto_delegate(project, content.filenames) # we delay the saving until we know we have a patch. if save_required: author.save() save_required = False patch.submitter = author patch.msgid = msgid patch.project = project patch.state = get_state(mail.get('X-Patchwork-State', '').strip()) patch.delegate = delegate patch.save() if revision: revision.add_patch(patch, content.patch_order) LOGGER.debug('Patch saved') if comment: if save_required: author.save() # we defer this assignment until we know that we have a saved patch if patch: comment.patch = patch comment.submitter = author comment.msgid = msgid comment.save() LOGGER.debug('Comment saved') # look for a status-change command string in the comment cmd = None comment_re = re.compile( '^(?:\n|\r\n?)\[Patchwork-Status:\s*\ (Under Review|Rejected|RFC|Not Applicable|Changes Requested|Awaiting \ Upstream|Superseded|Deferred)\](?:\n|\r\n?)$', re.M | re.I) # if multiple valid status-change commands are found, use last one for match in comment_re.finditer(comment.content): cmd = re.sub( r'((?:\n|\r\n?)\[Patchwork-Status:\s*)(.*)(\](?:\n|\r\n?))', r'\2', "{}".format(match.group(0))) # if a status-change command string is found, see if comment's author # has either project-maintainer permissions or is the series submitter # and process the command if true. if cmd is not None: refs = build_references_list(mail) if not refs == []: if not series: series = SeriesRevision.objects.filter( series__project=project, root_msgid=refs[-1]).reverse()[0].series if (author.user and project in (author.user).profile.maintainer_projects.all()) or ( author and author == series.submitter): new_state = State.objects.get(name=cmd.title()) mod_patch = Patch.objects.get(pk=comment.patch.pk) if new_state and mod_patch.state != new_state: mod_patch.state = new_state mod_patch.save() cmd_message = 'This is a system generated Comment: \ Patch %s status was updated to "%s"\ndue to request in comment body.' % ( comment.patch.pk, cmd) cmd_msgid = "%s: System generated by comment %s" % ( datetime.datetime.now().strftime("\ %d%b%Y.%H:%M:%S.%f"), comment.pk) new_comment = Comment(pk=None, patch=comment.patch, content=cmd_message, date=datetime.datetime.now(), submitter=comment.submitter, msgid=cmd_msgid) new_comment.save() else: # notify that a patch-status change was attempted without # apropriate submitter/maintainer permissions cmd_message = 'This is a system generated Comment: \ A command to change a patch-status through\nemail was detected in comment, \ but the sender email does not belong either to\na project maintainer or to \ the series submitter, the only approved users for\nthis function. \ Ensure you are using an approved email address when submitting.' cmd_msgid = "%s: System generated by comment %s" % ( datetime.datetime.now().strftime("\ %d%b%Y.%H:%M:%S.%f"), comment.pk) new_comment = Comment(pk=None, patch=comment.patch, content=cmd_message, date=datetime.datetime.now(), submitter=comment.submitter, msgid=cmd_msgid) new_comment.save() series_revision_complete.disconnect(on_revision_complete) return 0