Hi Don,

>> I suppose to put a finer point on it - what is your usecase here? What
>> are you trying to achieve, and can we help you do that in a way that
>> requires smaller changes to Patchwork, and is less fragile? (For example
>> this is going to break if someone mis-spells the keyword you're looking
>> for in the subject_match.)
>
> So here is our use case.  Internally at Red Hat we use one mailing list to
> post all of our kernel patches.  However, being a distro company, patches
> can be applied to either RHEL-6,7,X.  For the last 15 years we have always
> used the method:
>
> [RHEL-7 PATCH] instead of [PATCH].

Ah yes. I work for Canonical (although I do Patchwork in a private
capacity only) and our kernel team does something very similar.

> The project inside the []s has been what we filter through our regex.  Is it
> prone to human typos?  Yes.  Most folks have stuck this in the .git/config
> subjectprefix option.  That limited the typos. It isn't perfect.
>
> We have been using a hacked up PatchWork1 for the last 7 or so years.  This
> is one of those features we need (or something to solve our problem) if we
> want to migrate to a PatchWork2 instance.
>
> I hope that adds a little bit of context on our thinking.  Thoughts?

That's a compelling use-case and I'm happy to look at supporting it. I
will review the patch more closely in the coming days.

Regards,
Daniel

> Cheers,
> Don
>
>> 
>> Regards,
>> Daniel
>> 
>> >
>> > Signed-off-by: Veronika Kabatova <vkaba...@redhat.com>
>> > ---
>> >  docs/api.yaml                                      |  3 +++
>> >  docs/deployment/management.rst                     |  4 +--
>> >  patchwork/api/project.py                           |  5 ++--
>> >  .../0021_add_subject_match_to_project.py           | 29 
>> > ++++++++++++++++++++++
>> >  patchwork/models.py                                |  9 ++++++-
>> >  patchwork/parser.py                                | 19 +++++++++++++-
>> >  .../notes/list-filtering-4643d98b4064367a.yaml     | 10 ++++++++
>> >  7 files changed, 73 insertions(+), 6 deletions(-)
>> >  create mode 100644 
>> > patchwork/migrations/0021_add_subject_match_to_project.py
>> >  create mode 100644 releasenotes/notes/list-filtering-4643d98b4064367a.yaml
>> >
>> > diff --git a/docs/api.yaml b/docs/api.yaml
>> > index 3e79f0b..3373226 100644
>> > --- a/docs/api.yaml
>> > +++ b/docs/api.yaml
>> > @@ -374,6 +374,9 @@ definitions:
>> >        list_id:
>> >          type: string
>> >          description: Mailing list identifier for project.
>> > +      subject_match:
>> > +        type: string
>> > +        description: Regex used for email filtering.
>> >        list_email:
>> >          type: string
>> >          description: Mailing list email address for project.
>> > diff --git a/docs/deployment/management.rst 
>> > b/docs/deployment/management.rst
>> > index c50b7b6..870d7ee 100644
>> > --- a/docs/deployment/management.rst
>> > +++ b/docs/deployment/management.rst
>> > @@ -63,7 +63,7 @@ due to, for example, an outage.
>> >  .. option:: --list-id <list-id>
>> >  
>> >     mailing list ID. If not supplied, this will be extracted from the mail
>> > -   headers.
>> > +   headers. Don't use this option if you require filtering based on 
>> > subjects.
>> >  
>> >  .. option:: infile
>> >  
>> > @@ -88,7 +88,7 @@ the :ref:`deployment installation guide 
>> > <deployment-parsemail>`.
>> >  .. option:: --list-id <list-id>
>> >  
>> >     mailing list ID. If not supplied, this will be extracted from the mail
>> > -   headers.
>> > +   headers. Don't use this option if you require filtering based on 
>> > subjects.
>> >  
>> >  .. option:: infile
>> >  
>> > diff --git a/patchwork/api/project.py b/patchwork/api/project.py
>> > index 446c473..597f605 100644
>> > --- a/patchwork/api/project.py
>> > +++ b/patchwork/api/project.py
>> > @@ -39,8 +39,9 @@ class ProjectSerializer(HyperlinkedModelSerializer):
>> >      class Meta:
>> >          model = Project
>> >          fields = ('id', 'url', 'name', 'link_name', 'list_id', 
>> > 'list_email',
>> > -                  'web_url', 'scm_url', 'webscm_url', 'maintainers')
>> > -        read_only_fields = ('name', 'maintainers')
>> > +                  'web_url', 'scm_url', 'webscm_url', 'maintainers',
>> > +                  'subject_match')
>> > +        read_only_fields = ('name', 'maintainers', 'subject_match')
>> >          extra_kwargs = {
>> >              'url': {'view_name': 'api-project-detail'},
>> >          }
>> > diff --git a/patchwork/migrations/0021_add_subject_match_to_project.py 
>> > b/patchwork/migrations/0021_add_subject_match_to_project.py
>> > new file mode 100644
>> > index 0000000..6066266
>> > --- /dev/null
>> > +++ b/patchwork/migrations/0021_add_subject_match_to_project.py
>> > @@ -0,0 +1,29 @@
>> > +# -*- coding: utf-8 -*-
>> > +# Generated by Django 1.10.8 on 2018-01-19 18:16
>> > +from __future__ import unicode_literals
>> > +
>> > +from django.db import migrations, models
>> > +
>> > +
>> > +class Migration(migrations.Migration):
>> > +
>> > +    dependencies = [
>> > +        ('patchwork', '0020_tag_show_column'),
>> > +    ]
>> > +
>> > +    operations = [
>> > +        migrations.AddField(
>> > +            model_name='project',
>> > +            name='subject_match',
>> > +            field=models.CharField(blank=True, default=b'', 
>> > help_text=b'Regex to match the subject against if only part of emails sent 
>> > to the list belongs to this project. Will be used with IGNORECASE and 
>> > MULTILINE flags. If rules for more projects match the first one returned 
>> > from DB is chosen.', max_length=64),
>> > +        ),
>> > +        migrations.AlterField(
>> > +            model_name='project',
>> > +            name='listid',
>> > +            field=models.CharField(max_length=255),
>> > +        ),
>> > +        migrations.AlterUniqueTogether(
>> > +            name='project',
>> > +            unique_together=set([('listid', 'subject_match')]),
>> > +        ),
>> > +    ]
>> > diff --git a/patchwork/models.py b/patchwork/models.py
>> > index 11886f1..907707f 100644
>> > --- a/patchwork/models.py
>> > +++ b/patchwork/models.py
>> > @@ -71,8 +71,14 @@ class Project(models.Model):
>> >  
>> >      linkname = models.CharField(max_length=255, unique=True)
>> >      name = models.CharField(max_length=255, unique=True)
>> > -    listid = models.CharField(max_length=255, unique=True)
>> > +    listid = models.CharField(max_length=255)
>> >      listemail = models.CharField(max_length=200)
>> > +    subject_match = models.CharField(
>> > +        max_length=64, blank=True, default='', help_text='Regex to match 
>> > the '
>> > +        'subject against if only part of emails sent to the list belongs 
>> > to '
>> > +        'this project. Will be used with IGNORECASE and MULTILINE flags. 
>> > If '
>> > +        'rules for more projects match the first one returned from DB is '
>> > +        'chosen.')
>> >  
>> >      # url metadata
>> >  
>> > @@ -100,6 +106,7 @@ class Project(models.Model):
>> >          return self.name
>> >  
>> >      class Meta:
>> > +        unique_together = (('listid', 'subject_match'),)
>> >          ordering = ['linkname']
>> >  
>> >  
>> > diff --git a/patchwork/parser.py b/patchwork/parser.py
>> > index ac7dc5f..015f709 100644
>> > --- a/patchwork/parser.py
>> > +++ b/patchwork/parser.py
>> > @@ -157,9 +157,25 @@ def find_project_by_id(list_id):
>> >          project = Project.objects.get(listid=list_id)
>> >      except Project.DoesNotExist:
>> >          logger.debug("'%s' if not a valid project list-id", list_id)
>> > +    except Project.MultipleObjectsReturned:
>> > +        logger.debug("Multiple projects with list-id '%s' found", list_id)
>> >      return project
>> >  
>> >  
>> > +def find_project_by_id_and_subject(list_id, subject):
>> > +    """Find a `project` object based on `list_id` and subject prefix 
>> > match."""
>> > +    projects = Project.objects.filter(listid=list_id)
>> > +    for project in projects:
>> > +        if (not project.subject_match or
>> > +                re.search(project.subject_match, subject,
>> > +                          re.MULTILINE | re.IGNORECASE)):
>> > +            return project
>> > +
>> > +    logger.debug("No project to match email with list-id '%s', subject 
>> > '%s' "
>> > +                 "found", list_id, subject)
>> > +    return None
>> > +
>> > +
>> >  def find_project_by_header(mail):
>> >      project = None
>> >      listid_res = [re.compile(r'.*<([^>]+)>.*', re.S),
>> > @@ -181,7 +197,8 @@ def find_project_by_header(mail):
>> >  
>> >              listid = match.group(1)
>> >  
>> > -            project = find_project_by_id(listid)
>> > +            project = find_project_by_id_and_subject(listid,
>> > +                                                     mail.get('Subject'))
>> >              if project:
>> >                  break
>> >  
>> > diff --git a/releasenotes/notes/list-filtering-4643d98b4064367a.yaml 
>> > b/releasenotes/notes/list-filtering-4643d98b4064367a.yaml
>> > new file mode 100644
>> > index 0000000..21c1680
>> > --- /dev/null
>> > +++ b/releasenotes/notes/list-filtering-4643d98b4064367a.yaml
>> > @@ -0,0 +1,10 @@
>> > +---
>> > +features:
>> > +  - |
>> > +    Allow list filtering into multiple projects (and email dropping) 
>> > based on
>> > +    subject prefixes. Disabled by default, enable by specifying a regular
>> > +    expression which needs to be matched in the subject on a per-project 
>> > basis
>> > +    (field `subject_match`).
>> > +api:
>> > +  - |
>> > +    Expose `subject_match` in REST API.
>> > -- 
>> > 2.13.6
>> >
>> > _______________________________________________
>> > Patchwork mailing list
>> > Patchwork@lists.ozlabs.org
>> > https://lists.ozlabs.org/listinfo/patchwork
>> _______________________________________________
>> Patchwork mailing list
>> Patchwork@lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/patchwork
_______________________________________________
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork

Reply via email to