Stepping down as a Patchwork maintainer

2022-06-29 Thread Daniel Axtens
Hi all,

I think it's time for me to officially step down from Patchwork maintainership.

I've been in a new job for 3 months now, and this company doesn't use
mailing lists for development. So I'm no longer using Patchwork as a
patch submitter, and the low volume of patches to the Patchwork mailing
list meant I never used it very much as a maintainer either. I think
that someone who doesn't use a piece of software probably shouldn't be a
maintainer of that software. So, with some sadness, it's time for me to
relinquish the role.

Additionally, the change in job means I no longer have any excuses to
use work time for Patchwork, and my free time continues to be very
limited. I think it's better for everyone if I don't stay in a role that
I'm not meaningfully able to keep up with.

I'll do my best to reply to Patchwork mail where I'm personally listed
in the To:, but I'm probably going to (continue to) be pretty bad at
responding to mail to the list generally. I'm happy to keep my commit
bit for as long as Stephen and any future maintainers deem appropriate.

Stephen, thanks for inviting me to the maintainership and for the trust
you've placed in me. I wish you all the best for the future of
Patchwork!

To our long-suffering Patchwork administrators at kernel.org and
ozlabs.org, and to the smaller ones around the web - thank you for
trusting us and apologies for the regular migration dramas! Thanks
especially to jk for kicking off the patchwork project. Finally, to all
our contributors: thank you, you have in a very real way made Patchwork
what it is today.

Kind regards,
Daniel
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: Testing, please ignore

2022-03-28 Thread Daniel Axtens
Stephen Finucane  writes:

> I've seen reports that the mailing list is rejecting emails. Let's see if 
> that's
> true for everyone. Please ignore.

Just the moderation queue getting overfull and me missing emails about
people who post before subscribing :/

Kind regards,
Daniel

>
> Stephen
> ___
> 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


Re: [PATCH] patch-list: Link to delegate's assigned patches

2022-03-28 Thread Daniel Axtens
Sean Anderson  writes:

> Hi Stephen,
>
> On 11/29/21 12:58 PM, Stephen Finucane wrote:
>> On Mon, 2021-11-22 at 18:20 -0500, Sean Anderson wrote:
>>> This adds a link to all patches currently delegated to a user. This can
>>> be much easier than going through the delegate filter, especially if you
>>> already have a patch delegated to that user. This also replaces username
>>> text with their real name or email (if they are populated). This can
>>> help submitters figure out who their patches are assigned to (in cases
>>> where the username and real name of the delegate significantly diverge).
>> 
>> Thanks for the patch. Some comments below. If you can address these, I'd be
>> happy to apply this.
>> 
>> Stephen
>> 
>> PS: This didn't appear on Patchwork. I've no idea why...
>
> Hm, looks like my original message isn't in the archive. I've just subscribed
> to the list, so perhaps that will fix it.

Yes, that was it, I've just found the message in the moderation queue
now. Apologies.

Kind regards,
Daniel
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH 1/2] REST: Don't error if a versioned field we would remove is absent

2021-10-28 Thread Daniel Axtens
Konstantin Ryabitsev  writes:

> On Wed, Oct 27, 2021 at 05:00:34PM +0100, Stephen Finucane wrote:
>> v2.2.6 is available now. Let me know if there are any issues.
>
> Thanks!
>
>> Somewhat related: are there any large blockers preventing you moving to 
>> v3.0.x?
>> Is it the major version bump or removal of Python 2.7 support that's 
>> preventing
>> the upgrade, or is there something else? In particular, is there anything 
>> that
>> we could address? I'd like to get v3.1.0 out before the end of year but I'm
>> slightly concerned that no one (?) has rolled forward to v3.0.x yet :D
>
> The only concern, really, is the data migration and the potential for
> regressions. There are several subsystems now that are reliant on the CI
> aspects of patchwork.kernel.org to track their work, so I'm trying to be
> careful not to disrupt their work. I know that there are some cool new
> features in 3.x that would warrant migrating to it sooner than later -- just
> trying to plan out when is the best time to do it.
>
> On the upside, we're on the cusp of moving the web ui parts to a containerized
> deployment, so perhaps I can trial out the migration to 3.0 as part of that
> work.

I'd probably hold off until I can get some of the pieces to make the
migration more friendly in. I've tried to take on some of your feedback
so it does things like migrate data in chunks rather than all in one
giant statement...

but up to you, of course! Who knows when I will get to them, work has
just decided that we're going to bring forward a feature we had decided
to defer so I am Busy!

Kind regards,
Daniel

>
> -K
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH] README: Add Discord badge

2021-09-06 Thread Daniel Axtens
I tweaked the URL: it was printing the online count from another
server. That required changing a setting on the PW discord, which is now
also done.

Applied.

Stephen Finucane  writes:

> Add a Discord badge that can be used to connect to the server. Link
> taken from daxtens' email to the list about the server [1].
>
> [1] https://lists.ozlabs.org/pipermail/patchwork/2021-September/007226.html
>
> Signed-off-by: Stephen Finucane 
> Cc: Daniel Axtens 
> ---
>  README.rst | 4 
>  1 file changed, 4 insertions(+)
>
> diff --git README.rst README.rst
> index 7af45ce7..8d73ad78 100644
> --- README.rst
> +++ README.rst
> @@ -18,6 +18,10 @@ Patchwork
> :target: http://patchwork.readthedocs.io/en/latest/?badge=latest
> :alt: Documentation Status
>  
> +.. image:: 
> https://img.shields.io/discord/591914197219016707.svg?label==discord=ff=7389D8=6A7EC2
> +   :target: https://discord.gg/hGWjXVTAbB
> +   :alt: Discord
> +
>  **Patchwork** is a patch tracking system for community-based projects. It is
>  intended to make the patch management process easier for both the project's
>  contributors and maintainers, leaving time for the more important (and more
> -- 
> 2.31.1
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: PaReD: a patch relations detector for patchwork

2021-09-05 Thread Daniel Axtens
Hi Lukas,

>> It currently detects mails with identical subjects (after prefixes are
>> removed) within a 180 day window. This is not a very sophisticated
>> matching system, but given that it's an API client and not in the core,
>> I'm much happier to experiment and build up sophistication as and when
>> it's needed.
>>
>
> Simplicity is certainly valuable.
>
> Ralf and I envisioned the much more sophisticated algorithm for
> similar patch detection (from pasta, https://github.com/lfd/PaStA)
> integrated into a workflow with patchwork.

Yes, PaStA was definitely something I had in mind when I wrote
this. The capitalisation in PaReD is a small homage to PaStA.

I have also been pointed at this abandoned attempt to get similar
functionality into Gerrit: 
https://gerrit-review.googlesource.com/c/gerrit/+/91253

I am very open to moving in that direction if it turns out that more
detection at that level of sophistication is required to get acceptable
accuracy.

> Daniel, you have seen the small steps we have taken:
>
> - Mete (an intern at BMW, my employer at the time) implemented the
> "related patches" feature for patchwork in 2019.
> - Rohit (a Google Summer of Code student in 2020, mentored by Ralf and
> me) implemented an "export, compute, import" toolchain between
> patchwork and pasta, some more details are described in
> https://github.com/lfd/PaStA/blob/master/documentation/pasta-patchwork.md.
>
> Unfortunately, IMHO, we hit two challenging implementation tasks with this 
> work:
> 1. Performance issue computing relations with pasta
> 2. The lack of being able to limit the computation to new incoming
> patches: pasta was designed as an run-once off-line analysis tool, not
> as an continuously running online analysis; changing that is possible,
> but touches on various internal aspects throughout the whole tool.
>
> At that point, we have not continued the work yet and I personally
> believe that exploring simpler solutions than the complex pasta
> heuristics is worth a try (even if just to save power consumption of
> servers in the long run...).
>
> For completeness, I need to mention that Konstantin's b4 tool also
> detects the "latest patch series" when you ask it to pick a patch
> series from a kernel mailing list. I do not know how it determines
> that (and I hope that Konstantin can comment here), but it is probably
> also a simple heuristics searching for similar/same subject lines of
> the patch series cover letter. It would be nice if that functionality
> could be invoked as some kind of library function/separate client tool
> for patchwork as well.
>
> I hope that others can also come up with simple PaReD variants, such
> as parsing lore.kernel.org Links in the 'patch comment section' (so
> below the "---"), as once named the best way for developers to refer
> to previous versions in a ksummit-discuss email thread. I always hope
> that once a tool provides a significant benefit for tracking and
> managing previous versions, more developers pick up the needed
> conventions that patches would need to follow to benefit from such a
> tool.

I hope so too! I have been pleased by the proliferation of checks across
kernel.org's patchwork; I hope this will be the next thing to spread!

>> You can get the code at https://github.com/daxtens/pw-pared . I'm using
>> the same license as Patchwork, for a number of reasons, but in part
>> because we may one day want to migrate the functionality into the
>> patchwork core. Patches are welcome.
>>
>> You can see some examples of where PaReD has set up meaningful relations
>> at:
>>
>>  - 
>> https://patchwork.ozlabs.org/project/linuxppc-dev/patch/20210802073929.907431-2-kj...@linux.ibm.com/
>>  - 
>> https://patchwork.ozlabs.org/project/patchwork/patch/20210823182833.3976100-6-ra...@google.com/
>>
>> Some very obvious things that doing this has exposed:
>>
>>  - the relations display should show the status of each related patch
>>(e.g. New, Superseded, Accepted)
>>
>>  - Series relations would make a lot of sense - probably even more sense
>>from a human point of view - and we should probably build those at
>>some point.
>>
>
> Agree. This is something Ralf, Mete, Rohit and I discussed as well.
>
> Extending a patch relation to a patch series relation is conceptually simple:
>
> If two patch series S1 and S2 with patches p1, ..., pn in series S1
> and patches r1, ..., rm in series S2 share a critical amount of
> related patches, i.e., for a large set of pairs of indices (i, j) in
> I: pi and rj are related to each other, then the series S1 and S2 are
> related to each other. Further, one could come up with a separate
> similarity relation among cover letters, and weigh that into the
> measure for related patch series. Fine-tune the weights and
> thresholds, evaluate it on a representative dataset and you are
> done...Conceptually clear, but this involves quite some work.

As with patch relations, I think we'd want to start with the

Re: [PATCH] models: Add Series.is_open

2021-09-04 Thread Daniel Axtens
Stephen Finucane  writes:

> Start making series more useful by tracking whether the state is open,
> meaning one or more patches are in a state with action_required=True, or
> not. This means that the 'git-pw series list' command will only list
> series that are actually actionable.
>
> Signed-off-by: Stephen Finucane 
> ---
> Open questions:
> - Is there a more efficient way to do the migration than I've done?
> - Should we expose an 'is_open' boolean attribute of a 'state' (open,
>   closed) attribute for the series views?
> - Is there a race condition possible in how I've implemented the series
>   state checks?

AIUI, series.is_open is a derived property (or I suppose in db terms a
'view') of series.patches.

Is the cost of computing it 'live' so bad that we need to cache it in
the db? (a 'materialised view'[1])

Is there any chance we could just always compute on demand? I'm trying
to think of all the ways we expose series:

 - on the patch list page, we already also fetch all the patches

 - on the API /series/ page, we also fetch all the patches even for just
   the list view.

so it seems to me we probably shouldn't suffer a performance hit for
doing it live.


[1] https://www.datacamp.com/community/tutorials/materialized-views-postgresql
and noting that materialised views are not supported natively in mysql -
but you've basically done https://linuxhint.com/materialized-views-mysql
with the refresh logic in the application layer rather than a stored
procedure.

> diff --git patchwork/models.py patchwork/models.py
> index 58e4c51e..7441510b 100644
> --- patchwork/models.py
> +++ patchwork/models.py
> @@ -14,6 +14,7 @@ from django.conf import settings
>  from django.contrib.auth.models import User
>  from django.core.exceptions import ValidationError
>  from django.db import models
> +from django.db import transaction
>  from django.urls import reverse
>  from django.utils.functional import cached_property
>  from django.core.validators import validate_unicode_slug
> @@ -494,6 +495,19 @@ class Patch(SubmissionMixin):
>  for tag in tags:
>  self._set_tag(tag, counter[tag])
>  
> +def refresh_series_state(self):
> +if not self.series:
> +return
> +
> +# If any of the patches in the series is in an "action required" 
> state,
> +# then the series is still open
> +# TODO(stephenfin): Can this still race?
> +with transaction.atomic():
> +self.series.is_open = self.series.patches.filter(
> +state__action_required=True
> +).exists()
> +self.series.save()
> +

AIUI based some experimentation and my experience dealing with our
series race conditions in 2018, this can race and transactions won't
save you here.  The transaction will roll back all the changes if any
statement fails (e.g. if you had an integrityerror from a foreign key),
and other users won't see intermediate writes from your
transaction. What it won't do is preserve the data dependency you're
creating in the way a traditional mutex would.

Basically, consider a concurrent parsemail and an API client changing a
patch state and refreshing series state on the same series:


 | parsemail| API client|
 
 |  | series.patch[2].save()| 
 |  | BEGIN TRANSACTION |
 |  | is_open = SELECT(...) = False |
 | series.patch[3].save()   |   |
 | BEGIN TRANSACTION|   |
 | is_open = SELECT(...) = True |   |
 | UPDATE series... |   |
 | COMMIT TRANSACTION   |   |
 |  | UPDATE series...  |
 |  | COMMIT TRANSACTION|

both transactions can complete successfully and the end result is
wrong. (You can play with this sort of thing with two concurrent
dbshells both updating a commit_ref in a transaction, which I found very
helpful in getting the details right.)

Not materialising the view avoids persisting a result where we've lost
the race.

Kind regards,
Daniel


>  def save(self, *args, **kwargs):
>  if not hasattr(self, 'state') or not self.state:
>  self.state = get_default_initial_patch_state()
> @@ -503,6 +517,7 @@ class Patch(SubmissionMixin):
>  
>  super(Patch, self).save(**kwargs)
>  
> +self.refresh_series_state()
>  self.refresh_tag_counts()
>  
>  def is_editable(self, user):
> @@ -772,6 +787,11 @@ class Series(FilenameMixin, models.Model):
>'by the subject prefix(es)')
>  total = models.IntegerField(help_text='Number of patches in series as '
>  

Patchwork Discord

2021-09-02 Thread Daniel Axtens
Hi all,

Recently I set up a Discord 'server' for patchwork discussions.

We used it a bit during Raxel's internship at Google and it was quite
helpful to have a slightly lower latency and less formal place to
chat. I'd like to welcome anyone else interested in patchwork or related
projects (git-pw, snowpatch etc) to join.

https://discord.gg/hGWjXVTAbB

It's not intended to displace the mailing list. We have and will
continue to encourage significant topics to be discussed on the mailing
list. The discord is intended to supplement the list in much the same
way that IRC does. You can very much continue to be a valuable part of
the patchwork project just by participating on the mailing list.

Discord also doesn't replace the IRC channel. (I didn't hang out there
before, but I think Stephen did.)

[Why Discord? Isn't that less free than IRC? something something FOSS?!
This is a fair point. But I'm already using Slack, Discord, Element,
Keybase and I've recently started needing to use Zulip too. I don't want
to add _another_ one, especially since patchwork is already a side
project and this is not the sole or even primary communications platform
for Patchwork. And also - last I checked - I need to pay IRCCloud if I
want to get a mobile app for IRC and that's a bummer.]

Kind regards,
Daniel
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


PaReD: a patch relations detector for patchwork

2021-09-02 Thread Daniel Axtens
Hi all,

I have written the simplest patch relation detector that might possibly
work as an API client. It is running against the patchwork and
linuxppc-dev projects on patchwork.ozlabs.org.

It currently detects mails with identical subjects (after prefixes are
removed) within a 180 day window. This is not a very sophisticated
matching system, but given that it's an API client and not in the core,
I'm much happier to experiment and build up sophistication as and when
it's needed.

You can get the code at https://github.com/daxtens/pw-pared . I'm using
the same license as Patchwork, for a number of reasons, but in part
because we may one day want to migrate the functionality into the
patchwork core. Patches are welcome.

You can see some examples of where PaReD has set up meaningful relations
at:

 - 
https://patchwork.ozlabs.org/project/linuxppc-dev/patch/20210802073929.907431-2-kj...@linux.ibm.com/
 - 
https://patchwork.ozlabs.org/project/patchwork/patch/20210823182833.3976100-6-ra...@google.com/

Some very obvious things that doing this has exposed:

 - the relations display should show the status of each related patch
   (e.g. New, Superseded, Accepted)

 - Series relations would make a lot of sense - probably even more sense
   from a human point of view - and we should probably build those at
   some point.

 - PaReD requires an API token for a maintainer account (much like for
   pushing checks) which is annoying and one day we should sort out
   fine-grained permissions.

Ask your patchwork instance admin if a maintainer account for PaReD is
right for you!

Kind regards,
Daniel

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH 4/4] tests: Tweak comment API tests

2021-09-01 Thread Daniel Axtens
Not really something for this exact patch, but I do wonder as API
versions proliferate if we should have infrastructure to test something
on all supported API versions.

[Perhaps it's my lack of a formal CS education showing here but I feel
like there's probably some software design pattern for this, and I have
a weird intuition that I'm describing a Factory, but the wikipedia
article left me more confused than enlightened.]

Anyway, patch itself LGTM. I'm trying not to have PW take over my whole work
day so I haven't done a detailed review.

Kind regards,
Daniel

Stephen Finucane  writes:

> We were missing tests for 'GET /patch/{patch_id}/comment' (list patch
> comments) and 'GET /cover/{cover_id}/comment' (list cover comments) when
> using API version 1.2. In addition, we had effectively duplicated tests
> by including explicit tests for API 1.3. These are unnecessary since we
> default to testing against the latest version. Address both issues.
>
> Signed-off-by: Stephen Finucane 
> ---
>  patchwork/tests/api/test_comment.py | 40 -
>  1 file changed, 22 insertions(+), 18 deletions(-)
>
> diff --git patchwork/tests/api/test_comment.py 
> patchwork/tests/api/test_comment.py
> index 74acf447..5c035e82 100644
> --- patchwork/tests/api/test_comment.py
> +++ patchwork/tests/api/test_comment.py
> @@ -69,6 +69,17 @@ class TestCoverComments(utils.APITestCase):
>  self.assertEqual(1, len(resp.data))
>  self.assertSerialized(comment, resp.data[0])
>  self.assertIn('list_archive_url', resp.data[0])
> +self.assertIn('addressed', resp.data[0])
> +
> +def test_list_version_1_2(self):
> +"""List cover letter comments using API v1.2."""
> +create_cover_comment(cover=self.cover)
> +
> +resp = self.client.get(self.api_url(self.cover, version='1.2'))
> +self.assertEqual(status.HTTP_200_OK, resp.status_code)
> +self.assertEqual(1, len(resp.data))
> +self.assertIn('list_archive_url', resp.data[0])
> +self.assertNotIn('addressed', resp.data[0])
>  
>  def test_list_version_1_1(self):
>  """List cover letter comments using API v1.1."""
> @@ -110,15 +121,6 @@ class TestCoverComments(utils.APITestCase):
>  self.assertEqual(status.HTTP_200_OK, resp.status_code)
>  self.assertSerialized(comment, resp.data)
>  
> -def test_detail_version_1_3(self):
> -"""Show a cover letter comment using API v1.3."""
> -comment = create_cover_comment(cover=self.cover)
> -
> -resp = self.client.get(
> -self.api_url(self.cover, version='1.3', item=comment))
> -self.assertEqual(status.HTTP_200_OK, resp.status_code)
> -self.assertSerialized(comment, resp.data)
> -
>  def test_detail_version_1_2(self):
>  """Show a cover letter comment using API v1.2."""
>  comment = create_cover_comment(cover=self.cover)
> @@ -292,6 +294,17 @@ class TestPatchComments(utils.APITestCase):
>  self.assertEqual(1, len(resp.data))
>  self.assertSerialized(comment, resp.data[0])
>  self.assertIn('list_archive_url', resp.data[0])
> +self.assertIn('addressed', resp.data[0])
> +
> +def test_list_version_1_2(self):
> +"""List patch comments using API v1.2."""
> +create_patch_comment(patch=self.patch)
> +
> +resp = self.client.get(self.api_url(self.patch, version='1.2'))
> +self.assertEqual(status.HTTP_200_OK, resp.status_code)
> +self.assertEqual(1, len(resp.data))
> +self.assertIn('list_archive_url', resp.data[0])
> +self.assertNotIn('addressed', resp.data[0])
>  
>  def test_list_version_1_1(self):
>  """List patch comments using API v1.1."""
> @@ -333,15 +346,6 @@ class TestPatchComments(utils.APITestCase):
>  self.assertEqual(status.HTTP_200_OK, resp.status_code)
>  self.assertSerialized(comment, resp.data)
>  
> -def test_detail_version_1_3(self):
> -"""Show a patch comment using API v1.3."""
> -comment = create_patch_comment(patch=self.patch)
> -
> -resp = self.client.get(
> -self.api_url(self.patch, version='1.3', item=comment))
> -self.assertEqual(status.HTTP_200_OK, resp.status_code)
> -self.assertSerialized(comment, resp.data)
> -
>  def test_detail_version_1_2(self):
>  """Show a patch comment using API v1.2."""
>  comment = create_patch_comment(patch=self.patch)
> -- 
> 2.31.1
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH 2/4] docs: Document the address/unaddressed comment feature

2021-09-01 Thread Daniel Axtens
LGTMed-by: Daniel Axtens 

I haven't looked at it quite enough to give it a proper Review but I
don't have any obvious objections.

Kind regards,
Daniel

Stephen Finucane  writes:

> Signed-off-by: Stephen Finucane 
> ---
>  docs/usage/overview.rst | 28 +++-
>  1 file changed, 23 insertions(+), 5 deletions(-)
>
> diff --git docs/usage/overview.rst docs/usage/overview.rst
> index 273c792a..a58acfa5 100644
> --- docs/usage/overview.rst
> +++ docs/usage/overview.rst
> @@ -71,11 +71,11 @@ Cover Letters
>  ~
>  
>  Cover letters provide a way to offer a "big picture" overview of a series of
> -patches. When using Git, these mails can be recognised by way of their `0/N`
> -subject prefix, e.g. `[00/11] A sample series`. Like patches, Patchwork 
> stores
> -not only the various aspects of the cover letter itself, such as the name and
> -body of the cover letter, but also various metadata associated with the email
> -that the cover letter was parsed from.
> +patches. When using Git, these mails can be recognised by way of their 
> ``0/N``
> +subject prefix, e.g. ``[00/11] A sample series``. Like patches, Patchwork
> +stores not only the various aspects of the cover letter itself, such as the
> +name and body of the cover letter, but also various metadata associated with
> +the email that the cover letter was parsed from.
>  
>  
>  Comments
> @@ -181,6 +181,24 @@ system to test patches. Checks have a number of fields 
> associated with them:
> to Patchwork.
>  
>  
> +Comment Metadata
> +
> +
> +Like patches, Patchwork allows users to store various bits of metadata 
> against
> +comments.
> +
> +Action required
> +~~~
> +
> +.. versionadded:: 3.1.0
> +
> +Patchwork allows users to set an "action required" flag on patch and cover
> +letter comments. This flag can be set by maintainers or by the users 
> submitting
> +the cover letters. Once the submitter has provided the required information,
> +either the submitter or a maintainer can mark the comment as "addressed". 
> This
> +provides a more granular way of tracking work items than patch states.
> +
> +
>  Collections
>  ---
>  
> -- 
> 2.31.1
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH 1/4] Make addressed/unaddressed workflow opt-in

2021-09-01 Thread Daniel Axtens
Stephen Finucane  writes:

> On Sun, 2021-08-29 at 23:04 +1000, Daniel Axtens wrote:
>> Stephen Finucane  writes:
>> 
>> > On Fri, 2021-08-27 at 13:50 +1000, Daniel Axtens wrote:
>> > > Stephen Finucane  writes:
>> > > 
>> > > > The current workflow for the address/unaddressed attribute of comments
>> > > > sets all comments to unaddressed by default. This is unintuitive, as it
>> > > > assumes that all comments are actionable items. It also imposes a
>> > > > massive burden on maintainers, who will need to manually sift through
>> > > > every single comment received to a list and manually set the
>> > > > non-actionable items as "addressed".
>> > > 
>> > > I agree that not every email is an actionable item.
>> > > 
>> > > I'm not convinced it's a burden on maintainers specifically. The comment
>> > > can also be marked as addressed by the patch submitter. Also,
>> > > maintainers (and everyone else) are free to ignore the field (and every
>> > > other piece of data stored on patchwork).
>> > > 
>> > > In general I do think 'unaddressed by default' is a good behaviour. But,
>> > > I agree that we can improve the current behaviour.
>> > > 
>> > > I think it makes sense to have it as null for every old patch. So if you
>> > > migrate, old patch comments are neither addressed nor
>> > > unaddressed. That's something I didn't consider sufficiently earlier on.
>> > > 
>> > > I think it also makes sense for patches that add 'Acked-by:',
>> > > 'Reviewed-by:' or 'Tested-by:' to be considered automatically addressed.
>> > > 
>> > > But I worry that saying that everything is automatically neither means
>> > > that a patch sumbitter could very easily forget to do that and then we
>> > > risk losing the value that the feature is supposed to add.
>> > 
>> > Right, but if as you've said this is a feature intended for submitters 
>> > rather
>> > than maintainers, then surely we can assume that they will set the flag as
>> > necessary since they'll ultimately benefit from it? I get that nudges (in 
>> > the
>> > psychology sense) are a thing but we shouldn't have to "force" people to 
>> > use
>> > this feature by turning it on for every single non-code submission they 
>> > make to
>> > a list and not providing a way to opt out of it. That's not cool and I 
>> > don't
>> > think it's all that productive either. Until we have sufficiently advanced 
>> > AI/ML
>> > to detect actionable comments, simply encouraging submitters to use this 
>> > tool as
>> > a way to manually track action items (rather than scribbling them in a 
>> > diary or
>> > whatever) seems more than okay.
>> 
>> Maybe? Easy to miss an actionable comment if they're not automatically
>> marked, I'd think.
>> 
>> Anyway, I feel like we could go back and forth on this a bit, so maybe
>> we should try to explore and see if there's a bigger set of potential
>> solutions that might make both of us happier...
>> 
>> How does this strike you?
>> 
>>  a) all old mail gets the NULL value.
>
> Yes, please.
>
>> and
>> 
>>  b) Projects get a switch to enable/disable the feature. If you're a
>> maintainer and you think these fields are more trouble than they're
>> worth, ask your PW admin to make them disappear.
>
> I'm on the fence about this one. As noted in my earlier email, I don't think
> allowing maintainers to entirely disable this user-centric feature is a good
> precedent. Also, it doesn't seem entirely necessary given the below.
>
>> 
>> and
>> 
>>  b) Users get a switch - maybe with "all automatically unaddressed",
>> "NULL until manually marked" and "don't show me any of this ever"
>> options (obviously with better names)
>
> So to rephrase, this would be a user preference checkbox to allow users to
> automatically have any submitted comments marked as unaddressed, yes? If we 
> can
> have that default to False (i.e. opt-in) then yes, this would work for me. The
> "don't show me any of this option" could/should probably be implemented as a
> separate thing though I could live without this if the addressed/unaddressed
> thing is something a contributor is opting into and is therefore likely to
> actually tend to.
>
> Want me to submit a f

Re: [PATCH 3/4] parser: Add 'X-Patchwork-Action-Required' header

2021-09-01 Thread Daniel Axtens
Stephen Finucane  writes:

> On Fri, 2021-08-27 at 13:51 +1000, Daniel Axtens wrote:
>> Stephen Finucane  writes:
>> 
>> > Allow submitters to indicate that their comment is something that needs
>> > to be addressed.
>> 
>> Hmm, do we have any evidence that any of our existing mail headers are
>> used by anything?
>
> I've no idea. A quick scan through an old Patchwork archive mbox I have 
> locally
> suggests no but I can't speak for other lists. That said, part of the issue 
> here
> could simply be lack of awareness of the feature as much as anything else.
> Perhaps we should try to make this feature more prominent in the docs?
>
>> Also, I'm not confident I know how to set a header on a comment and I
>> write my email in emacs, notoriously one of the more configurable
>> platforms for any given task.
>
> Evolution does make it pretty easy [1], as does mutt [1]. We could add these
> links to the docs also, if it would help? I'll admit I haven't used either
> myself though since I'm happy enough with 'git-pw' for my day-to-day work.
>
> Stephen
>
> [1] 
> https://help.gnome.org/users/evolution/stable//mail-composer-custom-header-lines
> [2] http://www.mutt.org/doc/manual/#ex-my-hdr

Under the proposed model where the patch submitter gets to decide about
the display of un/addressed, does it make sense to have email header
support?

The email header support moves the un/addressed setting to the comment
submitter which sits oddly with it being usually owned by the patch
submitter.

Kind regards,
Daniel
>
>> 
>> > 
>> > Some minors issues are addressed in the docs while we're here.
>> > 
>> > Signed-off-by: Stephen Finucane 
>> > ---
>> >  docs/usage/headers.rst | 17 ---
>> >  docs/usage/overview.rst|  7 +++
>> >  patchwork/parser.py| 10 +++-
>> >  patchwork/tests/test_parser.py | 89 --
>> >  4 files changed, 111 insertions(+), 12 deletions(-)
>> > 
>> > diff --git docs/usage/headers.rst docs/usage/headers.rst
>> > index 26e97c0a..b2b4b2d6 100644
>> > --- docs/usage/headers.rst
>> > +++ docs/usage/headers.rst
>> > @@ -5,8 +5,7 @@ Patchwork provides a number of special email headers to 
>> > control how a patch is
>> >  handled when it is received. The examples provided below use 
>> > `git-send-email`,
>> >  but custom headers can also be set when using tools like `mutt`.
>> >  
>> > -`X-Patchwork-Hint`
>> > -
>> > +``X-Patchwork-Hint``
>> >Valid values: ignore
>> >  
>> >When set, this header will ensure the provided email is not parsed
>> > @@ -16,8 +15,7 @@ but custom headers can also be set when using tools like 
>> > `mutt`.
>> >  
>> >   $ git send-email --add-header="X-Patchwork-Hint: ignore" master
>> >  
>> > -`X-Patchwork-Delegate`
>> > -
>> > +``X-Patchwork-Delegate``
>> >Valid values: An email address associated with a Patchwork user
>> >  
>> >If set and valid, the user corresponding to the provided email address 
>> > will
>> > @@ -28,8 +26,7 @@ but custom headers can also be set when using tools like 
>> > `mutt`.
>> >  
>> >   $ git send-email --add-header="X-Patchwork-Delegate: 
>> > a...@example.com" master
>> >  
>> > -`X-Patchwork-State`
>> > -
>> > +``X-Patchwork-State``
>> >Valid values: Varies between deployments. This can usually be one of
>> >"Accepted", "Rejected", "RFC" or "Awaiting Upstream", among others.
>> >  
>> > @@ -39,3 +36,11 @@ but custom headers can also be set when using tools 
>> > like `mutt`.
>> >.. code-block:: shell
>> >  
>> >   $ git send-email --add-header="X-Patchwork-State: RFC" master
>> > +
>> > +``X-Patchwork-Action-Required``
>> > +  Valid values: 
>> > +
>> > +  When set on a reply to an existing cover letter or patch, this header 
>> > will
>> > +  mark the comment as "unaddressed". The comment can then be addressed 
>> > using
>> > +  the API or web UI. For more details, refer to
>> > +  :ref:`overview-comment-metadata`.
>> > diff --git docs/usage/overview.rst docs/usage/overview.rst
>> > index a58acfa5..297569ec 100644
>> > --- docs/usage/overview.rst
>> > +++ docs/usage/overview.rst
>> > @@ -181,6 +181,8 @@ sys

Re: [PATCH 1/3] Allow a project to restrict submitter state changes

2021-09-01 Thread Daniel Axtens
Daniel Axtens  writes:

> Stephen Finucane  writes:
>
>> On Fri, 2021-08-06 at 11:39 +1000, Daniel Axtens wrote:
>>> Stephen Finucane  writes:
>>> 
>>> > On Tue, 2021-08-03 at 01:27 +1000, Daniel Axtens wrote:
>>> > > In discussions about how to make patchwork more user-friendly and
>>> > > suitable for more projects, we realised that perhaps the current
>>> > > ability for submitters to change their patch state to any value
>>> > > isn't the most appropriate setting for all maintainers, especially
>>> > > in light of increasing automation.
>>> > > 
>>> > > Allow a project to stop a submitter from changing the state of
>>> > > their patches. This is not the default but can be set by a patchwork
>>> > > administrator.
>>> > 
>>> > Couple of comments below. Unfortunately two of them are of the "I don't 
>>> > know
>>> > about this so can you investigate?" variety. I can help resolve them if
>>> > necessary but I'm hoping you've already done said investigation :-)
>>> > 
>>> > Stephen
>>> > 
>>> > > Signed-off-by: Daniel Axtens 
>>> > > ---
>>> > >  ...45_project_submitter_state_change_rules.py | 24 +
>>> > >  patchwork/models.py | 36 +++
>>> > >  patchwork/views/__init__.py | 8 +
>>> > >  patchwork/views/patch.py | 14 ++--
>>> > >  4 files changed, 80 insertions(+), 2 deletions(-)
>>> > >  create mode 100644 
>>> > > patchwork/migrations/0045_project_submitter_state_change_rules.py
>>> > > 
>>> > > diff --git 
>>> > > a/patchwork/migrations/0045_project_submitter_state_change_rules.py 
>>> > > b/patchwork/migrations/0045_project_submitter_state_change_rules.py
>>> > > new file mode 100644
>>> > > index ..9d0b2892bd5c
>>> > > --- /dev/null
>>> > > +++ b/patchwork/migrations/0045_project_submitter_state_change_rules.py
>>> > > @@ -0,0 +1,24 @@
>>> > > +# Generated by Django 3.1.12 on 2021-08-03 00:32
>>> > > +
>>> > > +from django.db import migrations, models
>>> > > +
>>> > > +
>>> > > +class Migration(migrations.Migration):
>>> > > +
>>> > > + dependencies = [
>>> > > + ('patchwork', '0044_add_project_linkname_validation'),
>>> > > + ]
>>> > > +
>>> > > + operations = [
>>> > > + migrations.AddField(
>>> > > + model_name='project',
>>> > > + name='submitter_state_change_rules',
>>> > > + field=models.SmallIntegerField(
>>> > > + choices=[
>>> > > + (0, 'Submitters may not change patch states'),
>>> > > + (1, 'Submitters may set any patch state')],
>>> > > + default=1,
>>> > > + help_text='What state changes can patch submitters make?'
>>> > > + ' Does not affect maintainers.'),
>>> > > + ),
>>> > 
>>> > This feels like a BooleanField rather than a SmallIntegerField (even if 
>>> > they
>>> > resolve to the same thing on e.g. MySQL 5.x, iirc). afaict, you can pass 
>>> > the
>>> > 'choices' argument for BooleanField too [1]. Any chance we could change 
>>> > this?
>>> > 
>>> > [1] https://code.djangoproject.com/ticket/9640
>>> 
>>> I picked this because I want to add another mode that permits submitters
>>> to change states but restricts the states that submitters can change
>>> between (so for example allowing them to move around the {New, RFC,
>>> Superseded, Not Applicable, Changes Requested} group but not to mark
>>> their own patches as Accepted). And I don't want to do a migration then
>>> if I can avoid it :)
>>
>> Is that necessary? What's the use case? fwiw, I'd like to get rid of State
>> objects entirely. I've a long-term goal of being able to mark a Series as 
>> open
>> or closed and make Series more of a first class citizen ('git-pw series 
>> list' is
>> effectively useless right now), but doing so requires Patches to also have a
>> boolean open/closed state (assuming we don't want the series and patch 
>> states to
>> be totally disconnected and for users to be forced to manually manage series
>> states, that is). I've been envis

Re: [PATCH] mailmap: update email

2021-08-31 Thread Daniel Axtens
Raxel Gutierrez  writes:

> From: raxelg 
>
> Update Google email used during internship to personal email.
>
> Signed-off-by: Raxel Gutierrez 

Applied. Welcome back, it's great to have you around!

> ---
>  .mailmap | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/.mailmap b/.mailmap
> index 00de968..40138c5 100644
> --- a/.mailmap
> +++ b/.mailmap
> @@ -1,3 +1,4 @@
> + 
>   
>   
>   
> -- 
> 2.30.1 (Apple Git-130)
>
> ___
> 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


Re: [PATCH 4/4] RFC: migrations: 0043: Perform ALTER TABLE in one big batch

2021-08-31 Thread Daniel Axtens
Daniel Axtens  writes:

> This saves us doing a whole bunch of rewrites sequentially - instead
> we just do one big rewrite.
>
> Currently breaks postgres and sqlite, which is annoying because they
> don't need this to go faster. OTOH it makes mysql a **lot** faster - from
> around 220s to ~45s, and that will scale with table size.
>
> Mostly, however, this is RFC because I am not sure about the random-ish
> strings in the middle of the constraint names. I took them from the
> `sqlmigrate` output but I don't know if they're random or special.

This is a doozy.

The random strings are created by names_digest in django. It's the first
8 hex digits of an md5 digest. The inputs to the md5 digest are a bit
fiddly to determine.

The name of the constraint is created by _fk_constraint_name in
django.db.backends.base.schema.BaseDatabaseSchemaEditor.
(AFAICT it is not overloaded.)

_fk_constraint_name is this:

def _fk_constraint_name(self, model, field, suffix):
def create_fk_name(*args, **kwargs):
return self.quote_name(self._create_index_name(*args, **kwargs))

return ForeignKeyName(
model._meta.db_table,
[field.column],
split_identifier(field.target_field.model._meta.db_table)[1],
[field.target_field.column],
suffix,
create_fk_name,
)

Ignoring ForeignKeyName for now, we can infer that create_fk_name will
get called and will call _create_index_name. That does this:

def _create_index_name(self, table_name, column_names, suffix=""):
"""
Generate a unique name for an index/unique constraint.

The name is divided into 3 parts: the table name, the column names,
and a unique digest and suffix.
"""
_, table_name = split_identifier(table_name)
hash_suffix_part = '%s%s' % (names_digest(table_name, *column_names, 
length=8), suffix)
max_length = self.connection.ops.max_name_length() or 200
# If everything fits into max_length, use that name.
index_name = '%s_%s_%s' % (table_name, '_'.join(column_names), 
hash_suffix_part)
if len(index_name) <= max_length:
return index_name
# Shorten a long suffix.
if len(hash_suffix_part) > max_length / 3:
hash_suffix_part = hash_suffix_part[:max_length // 3]
other_length = (max_length - len(hash_suffix_part)) // 2 - 1
index_name = '%s_%s_%s' % (
table_name[:other_length],
'_'.join(column_names)[:other_length],
hash_suffix_part,
)
# Prepend D if needed to prevent the name from starting with an
# underscore or a number (not permitted on Oracle).
if index_name[0] == "_" or index_name[0].isdigit():
index_name = "D%s" % index_name[:-1]
return index_name

So we call names_digest() on the table_name and the column_names.

So I feel fairly safe to say that it's going to be static and based on
the md5 of the table and related columns. I'll try to patch my local
django install and see what's getting passed to names_digest to confirm,
but we're _probably_ right to just use the names based on the SQL from
sqlmigrate.

Anyway I am trying to not spend all my nights coding so I will leave it
here for now and return to it later.

Kind regards,
Daniel

>
> Thoughts?
>
> Signed-off-by: Daniel Axtens 
> ---
>  .../migrations/0043_merge_patch_submission.py | 195 +++---
>  1 file changed, 117 insertions(+), 78 deletions(-)
>
> diff --git a/patchwork/migrations/0043_merge_patch_submission.py 
> b/patchwork/migrations/0043_merge_patch_submission.py
> index 1d072ca18230..e17c394f3ffb 100644
> --- a/patchwork/migrations/0043_merge_patch_submission.py
> +++ b/patchwork/migrations/0043_merge_patch_submission.py
> @@ -4,9 +4,34 @@ import django.db.models.deletion
>  from django.db.models import Max
>  
>  import patchwork.fields
> +import datetime
>  
>  CHUNK_SIZE = 1
>  
> +def report(apps, schema_editor):
> +print(datetime.datetime.now())
> +
> +def add_fields(apps, schema_editor):
> +# mysql - each can be a costly rewrite, bundle.
> +# pgsql and others don't let us do multiple which is sad.
> +schema_editor.execute(
> +"""
> +ALTER TABLE `patchwork_submission` 
> +ADD COLUMN `archived` bool DEFAULT b'0' NOT NULL,
> +ADD COLUMN `commit_ref` varchar(255) NULL,
> +ADD COLUMN `delegate_id` integer NULL , ADD CONSTRAINT 
> `patchwork_submission_delegate_id_4b8639b8_fk_auth_user_id` FOREIGN KEY 
> (`delegate_id`) REFERENCES `auth_user`(`id`),
> +ADD COLUMN `diff` longtext NULL,
> +ADD COLUMN `hash` char(40) NULL,
> +ADD COLUMN `nu

Re: [PATCH 1/3] Allow a project to restrict submitter state changes

2021-08-31 Thread Daniel Axtens
Stephen Finucane  writes:

> On Fri, 2021-08-06 at 11:39 +1000, Daniel Axtens wrote:
>> Stephen Finucane  writes:
>> 
>> > On Tue, 2021-08-03 at 01:27 +1000, Daniel Axtens wrote:
>> > > In discussions about how to make patchwork more user-friendly and
>> > > suitable for more projects, we realised that perhaps the current
>> > > ability for submitters to change their patch state to any value
>> > > isn't the most appropriate setting for all maintainers, especially
>> > > in light of increasing automation.
>> > > 
>> > > Allow a project to stop a submitter from changing the state of
>> > > their patches. This is not the default but can be set by a patchwork
>> > > administrator.
>> > 
>> > Couple of comments below. Unfortunately two of them are of the "I don't 
>> > know
>> > about this so can you investigate?" variety. I can help resolve them if
>> > necessary but I'm hoping you've already done said investigation :-)
>> > 
>> > Stephen
>> > 
>> > > Signed-off-by: Daniel Axtens 
>> > > ---
>> > >  ...45_project_submitter_state_change_rules.py | 24 +
>> > >  patchwork/models.py | 36 +++
>> > >  patchwork/views/__init__.py | 8 +
>> > >  patchwork/views/patch.py | 14 ++--
>> > >  4 files changed, 80 insertions(+), 2 deletions(-)
>> > >  create mode 100644 
>> > > patchwork/migrations/0045_project_submitter_state_change_rules.py
>> > > 
>> > > diff --git 
>> > > a/patchwork/migrations/0045_project_submitter_state_change_rules.py 
>> > > b/patchwork/migrations/0045_project_submitter_state_change_rules.py
>> > > new file mode 100644
>> > > index ..9d0b2892bd5c
>> > > --- /dev/null
>> > > +++ b/patchwork/migrations/0045_project_submitter_state_change_rules.py
>> > > @@ -0,0 +1,24 @@
>> > > +# Generated by Django 3.1.12 on 2021-08-03 00:32
>> > > +
>> > > +from django.db import migrations, models
>> > > +
>> > > +
>> > > +class Migration(migrations.Migration):
>> > > +
>> > > + dependencies = [
>> > > + ('patchwork', '0044_add_project_linkname_validation'),
>> > > + ]
>> > > +
>> > > + operations = [
>> > > + migrations.AddField(
>> > > + model_name='project',
>> > > + name='submitter_state_change_rules',
>> > > + field=models.SmallIntegerField(
>> > > + choices=[
>> > > + (0, 'Submitters may not change patch states'),
>> > > + (1, 'Submitters may set any patch state')],
>> > > + default=1,
>> > > + help_text='What state changes can patch submitters make?'
>> > > + ' Does not affect maintainers.'),
>> > > + ),
>> > 
>> > This feels like a BooleanField rather than a SmallIntegerField (even if 
>> > they
>> > resolve to the same thing on e.g. MySQL 5.x, iirc). afaict, you can pass 
>> > the
>> > 'choices' argument for BooleanField too [1]. Any chance we could change 
>> > this?
>> > 
>> > [1] https://code.djangoproject.com/ticket/9640
>> 
>> I picked this because I want to add another mode that permits submitters
>> to change states but restricts the states that submitters can change
>> between (so for example allowing them to move around the {New, RFC,
>> Superseded, Not Applicable, Changes Requested} group but not to mark
>> their own patches as Accepted). And I don't want to do a migration then
>> if I can avoid it :)
>
> Is that necessary? What's the use case? fwiw, I'd like to get rid of State
> objects entirely. I've a long-term goal of being able to mark a Series as open
> or closed and make Series more of a first class citizen ('git-pw series list' 
> is
> effectively useless right now), but doing so requires Patches to also have a
> boolean open/closed state (assuming we don't want the series and patch states 
> to
> be totally disconnected and for users to be forced to manually manage series
> states, that is). I've been envisioning two solutions:
>
>  * Transform the 'Patch.state' field to a combination of a boolean
>'Patch.resolved' field and a 'Patch.resolution' field, with the latter 
> being
>set only if 'Patch.resolved' == 'True'
>  * Transform the 'Patch.state' field to a boolean and add support for patch
>tags, with the current States all becoming tags
>
> I've mostl

Re: [PATCH 1/4] Make addressed/unaddressed workflow opt-in

2021-08-31 Thread Daniel Axtens
 your
> description, I see that there should be precedence in the 'addressed'
> comments system. As you describe, those user configs would apply to
> comments of the submitter's patch. In the case that the patch
> submitter replies, I believe the 'addressed' status of the comment by
> the patch submitter should follow the behavior of the in-reply-to
> comment submitter's preference. For example, consider the two
> scenarios given that I have the feature configured to be disabled for
> the sake of these examples:
>
> Scenario 1:
> 1) Daniel sends out patch
> 2) Stephen replies ---> (comment is unaddressed automatically as per
> Daniel's settings)
> 3) Daniel replies ---> (addressed is NULL until manually marked as per
> Stephen's settings)
> 4) Raxel replies ---> (comment is unaddressed automatically as per
> Daniel's settings)
>
> Scenario 2:
> 1) Stephen sends out patch
> 2) Daniel replies ---> (addressed is NULL until manually marked as per
> Stephen's settings)
> 3) Raxel replies ---> (addressed is NULL until manually marked as per
> Stephen's settings)
> 4) Stephen replies ---> (addressed status for comment is disabled as
> per Raxel's settings)
>
> Based on the two scenarios, the settings of the patch submitter should
> take absolute precedence in determining the 'addressed' status of the
> comments to their patch. If the patch submitter replies to a comment,
> then that 'addressed' status should be determined by the settings of
> the in-reply-to comment submitter.

Hmm interesting. I had only considered it operating on the basis of the
preferences of the submitter of the patch/cover letter.

Using your example where I want all comments marked as unaddressed,
Stephen wants NULLs and you want the feature disabled, my thoughts were:

 - If I send a patch, all replies to my patch - regardless of who
   sends them - marked as unaddressed.

 - If Stephen sends a patch, all replies are marked with the null
   state, regardless of who sends them.

 - If you send a patch, the un/addressed states are just not shown,
   regardless of who sends them.

I am a little worried that trying to support threaded comment rules
starts to operate in surprising ways for people (and would be a
nightmare to implement) but I'm happy to be persuaded otherwise.

I guess I can muck about with some code for (the simple version of) this
over the next few days. Like the IETF I like the idea of decisions being
made via "rough consensus and working code" so I guess it's time to lead
by example :)

Kind regards,
Daniel

>
>> Kind regards,
>> Daniel
>> >
>> >> Another thing we could consider doing is making it opt-in by
>> >> project. For projects that keep pw very tidy (I'm thinking
>> >> e.g. https://patchwork.kernel.org/project/netdevbpf/list/) then
>> >> the addressed/unaddressed thing might be more useful and less noisy than
>> >> e.g. linuxppc which is a bit less well pruned.
>> >
>> > This does sound initially reasonable, but if these things are opt-in and
>> > intended for the submitter then the value of making this configurable on a 
>> > per-
>> > project basis is significantly lower, right? In fact, it might even be 
>> > actively
>> > harmful since an opinionated maintainer (let's say you or I) could disable 
>> > it
>> > for an entire project (patchwork) meaning no submitter (Raxel?) could use 
>> > this
>> > supposedly submitter-focused feature to track action items even if they 
>> > wanted
>> > to.
>> >
>> > If we were going to do a global'ish config option, I'd probably make it a 
>> > user
>> > preference like the "show patch IDs" feature, so submitters that wanted to 
>> > make
>> > use of the feature would see the various flags while maintainer's who 
>> > didn't
>> > care for it could remain blissfully unaware. That assumes that the feature 
>> > has
>> > no value whatsoever for maintainers though, which I'm not sure is entirely 
>> > true?
>> >
>> >> But, again, I see the un/addressed field as being for the submitter, not
>> >> the maintainer. The maintainer can't trust it anyway because what the
>> >> submitter considers 'addressed' and the maintainer considers 'addressed'
>> >> could be very different. So really I see this as helping a submitter to
>> >> track that there is nothing waiting on them.
>> >
>> > No arguments from me: I'm totally behind the feature as whole and 
>> > understand the
>> > motivation. I'm saying that submitters should be able to choose to set this
>>

[PATCH v3] Test Patch, please ignore

2021-08-30 Thread Daniel Axtens
oops, did not intend to need 3 versions. soz all.

Signed-off-by: Daniel Axtens 
---
 docs/usage/clients.rst | 15 +++
 1 file changed, 15 insertions(+)

diff --git a/docs/usage/clients.rst b/docs/usage/clients.rst
index 01dd62a28e50..1dbdacc4de41 100644
--- a/docs/usage/clients.rst
+++ b/docs/usage/clients.rst
@@ -53,3 +53,18 @@ appropriate builds and test suites, and reports the results 
back to Patchwork.
 Find out more about :program:`snowpatch` at its `GitHub repo`__.
 
 __ https://github.com/ruscur/snowpatch
+
+
+PaReD
+-
+
+:program:`PaReD` automatically detects patch relations based on a simple
+subject match. It monitors the REST API for incoming patches, checks them
+against a list of recent subjects and associated patch IDs, and creates a
+relation if the same subject is seen multiple times within a configurable
+time period (e.g. 90 days).
+
+You can find out more about it at its `GitHub repo`__, but you shouldn't use
+it, at least not in its current state.
+
+__ https://github.com/daxtens/pw-pared
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH v2] Test Patch, please ignore

2021-08-30 Thread Daniel Axtens
Signed-off-by: Daniel Axtens 
---
 docs/usage/clients.rst | 13 +
 1 file changed, 13 insertions(+)

diff --git a/docs/usage/clients.rst b/docs/usage/clients.rst
index 01dd62a28e50..e9c0ad49108b 100644
--- a/docs/usage/clients.rst
+++ b/docs/usage/clients.rst
@@ -53,3 +53,16 @@ appropriate builds and test suites, and reports the results 
back to Patchwork.
 Find out more about :program:`snowpatch` at its `GitHub repo`__.
 
 __ https://github.com/ruscur/snowpatch
+
+
+PaReD
+-
+
+:program:`PaReD` automatically detects patch relations based on a simple
+subject match. It monitors the REST API for incoming patches, checks them
+against a list of recent subjects and associated patch IDs, and creates a
+relation if the same subject is seen multiple times within a configurable
+time period (e.g. 90 days).
+
+You can find out more about it at its `GitHub repo`__, but you shouldn't use
+it, at least not in its current state.
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH] Test Patch, please ignore

2021-08-30 Thread Daniel Axtens
Signed-off-by: Daniel Axtens 
---
 docs/usage/clients.rst | 13 +
 1 file changed, 13 insertions(+)

diff --git a/docs/usage/clients.rst b/docs/usage/clients.rst
index 01dd62a28e50..5c0024c3ddbb 100644
--- a/docs/usage/clients.rst
+++ b/docs/usage/clients.rst
@@ -53,3 +53,16 @@ appropriate builds and test suites, and reports the results 
back to Patchwork.
 Find out more about :program:`snowpatch` at its `GitHub repo`__.
 
 __ https://github.com/ruscur/snowpatch
+
+
+PaReD
+-
+
+:program:`PaReD` automatically detects patch relations based on a simple
+subject match. It monitors the REST API for incoming patches, checks them
+against a list of recent subjects and associated patch IDs, and creates a
+relation if the same subject is seen multiple times within a configurable
+time period (e.g. 90 days).
+
+You can't find out more about it because it exists only in the shadowy
+depths of Daniel's laptop.
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH 1/4] Make addressed/unaddressed workflow opt-in

2021-08-29 Thread Daniel Axtens
Stephen Finucane  writes:

> On Fri, 2021-08-27 at 13:50 +1000, Daniel Axtens wrote:
>> Stephen Finucane  writes:
>> 
>> > The current workflow for the address/unaddressed attribute of comments
>> > sets all comments to unaddressed by default. This is unintuitive, as it
>> > assumes that all comments are actionable items. It also imposes a
>> > massive burden on maintainers, who will need to manually sift through
>> > every single comment received to a list and manually set the
>> > non-actionable items as "addressed".
>> 
>> I agree that not every email is an actionable item.
>> 
>> I'm not convinced it's a burden on maintainers specifically. The comment
>> can also be marked as addressed by the patch submitter. Also,
>> maintainers (and everyone else) are free to ignore the field (and every
>> other piece of data stored on patchwork).
>> 
>> In general I do think 'unaddressed by default' is a good behaviour. But,
>> I agree that we can improve the current behaviour.
>> 
>> I think it makes sense to have it as null for every old patch. So if you
>> migrate, old patch comments are neither addressed nor
>> unaddressed. That's something I didn't consider sufficiently earlier on.
>> 
>> I think it also makes sense for patches that add 'Acked-by:',
>> 'Reviewed-by:' or 'Tested-by:' to be considered automatically addressed.
>> 
>> But I worry that saying that everything is automatically neither means
>> that a patch sumbitter could very easily forget to do that and then we
>> risk losing the value that the feature is supposed to add.
>
> Right, but if as you've said this is a feature intended for submitters rather
> than maintainers, then surely we can assume that they will set the flag as
> necessary since they'll ultimately benefit from it? I get that nudges (in the
> psychology sense) are a thing but we shouldn't have to "force" people to use
> this feature by turning it on for every single non-code submission they make 
> to
> a list and not providing a way to opt out of it. That's not cool and I don't
> think it's all that productive either. Until we have sufficiently advanced 
> AI/ML
> to detect actionable comments, simply encouraging submitters to use this tool 
> as
> a way to manually track action items (rather than scribbling them in a diary 
> or
> whatever) seems more than okay.

Maybe? Easy to miss an actionable comment if they're not automatically
marked, I'd think.

Anyway, I feel like we could go back and forth on this a bit, so maybe
we should try to explore and see if there's a bigger set of potential
solutions that might make both of us happier...

How does this strike you?

 a) all old mail gets the NULL value.

and

 b) Projects get a switch to enable/disable the feature. If you're a
maintainer and you think these fields are more trouble than they're
worth, ask your PW admin to make them disappear.

and

 b) Users get a switch - maybe with "all automatically unaddressed",
"NULL until manually marked" and "don't show me any of this ever"
options (obviously with better names)

That way, with basically no extra load on the db:

 - you can get comments on your patches only marked as unaddressed if
   you manually do so,

 - I can get all of the comments on my patches automatically unaddressed
   (which, in all honestly, is what I want - I absolutely _do_ lose
   track of email comments even just on the PW list!),

 - a patchwork project which has tracking mechanisms formalised in
   another way can turn them off entierly. (I'm thinking of the
   kernel-team mailing list in Ubuntu which has a strict
   2-Acks-from-team-members requirement, and where people will vocally
   nack.)

Thoughts?

Kind regards,
Daniel
>
>> Another thing we could consider doing is making it opt-in by
>> project. For projects that keep pw very tidy (I'm thinking
>> e.g. https://patchwork.kernel.org/project/netdevbpf/list/) then
>> the addressed/unaddressed thing might be more useful and less noisy than
>> e.g. linuxppc which is a bit less well pruned.
>
> This does sound initially reasonable, but if these things are opt-in and
> intended for the submitter then the value of making this configurable on a 
> per-
> project basis is significantly lower, right? In fact, it might even be 
> actively
> harmful since an opinionated maintainer (let's say you or I) could disable it
> for an entire project (patchwork) meaning no submitter (Raxel?) could use this
> supposedly submitter-focused feature to track action items even if they wanted
> to.
>
> If we were going to do a global'ish config option, I'd probably make it a user
> preference like the &

Re: [PATCH 3/4] parser: Add 'X-Patchwork-Action-Required' header

2021-08-26 Thread Daniel Axtens


Stephen Finucane  writes:

> Allow submitters to indicate that their comment is something that needs
> to be addressed.

Hmm, do we have any evidence that any of our existing mail headers are
used by anything?

Also, I'm not confident I know how to set a header on a comment and I
write my email in emacs, notoriously one of the more configurable
platforms for any given task.

>
> Some minors issues are addressed in the docs while we're here.
>
> Signed-off-by: Stephen Finucane 
> ---
>  docs/usage/headers.rst | 17 ---
>  docs/usage/overview.rst|  7 +++
>  patchwork/parser.py| 10 +++-
>  patchwork/tests/test_parser.py | 89 --
>  4 files changed, 111 insertions(+), 12 deletions(-)
>
> diff --git docs/usage/headers.rst docs/usage/headers.rst
> index 26e97c0a..b2b4b2d6 100644
> --- docs/usage/headers.rst
> +++ docs/usage/headers.rst
> @@ -5,8 +5,7 @@ Patchwork provides a number of special email headers to 
> control how a patch is
>  handled when it is received. The examples provided below use 
> `git-send-email`,
>  but custom headers can also be set when using tools like `mutt`.
>  
> -`X-Patchwork-Hint`
> -
> +``X-Patchwork-Hint``
>Valid values: ignore
>  
>When set, this header will ensure the provided email is not parsed
> @@ -16,8 +15,7 @@ but custom headers can also be set when using tools like 
> `mutt`.
>  
>   $ git send-email --add-header="X-Patchwork-Hint: ignore" master
>  
> -`X-Patchwork-Delegate`
> -
> +``X-Patchwork-Delegate``
>Valid values: An email address associated with a Patchwork user
>  
>If set and valid, the user corresponding to the provided email address will
> @@ -28,8 +26,7 @@ but custom headers can also be set when using tools like 
> `mutt`.
>  
>   $ git send-email --add-header="X-Patchwork-Delegate: a...@example.com" 
> master
>  
> -`X-Patchwork-State`
> -
> +``X-Patchwork-State``
>Valid values: Varies between deployments. This can usually be one of
>"Accepted", "Rejected", "RFC" or "Awaiting Upstream", among others.
>  
> @@ -39,3 +36,11 @@ but custom headers can also be set when using tools like 
> `mutt`.
>.. code-block:: shell
>  
>   $ git send-email --add-header="X-Patchwork-State: RFC" master
> +
> +``X-Patchwork-Action-Required``
> +  Valid values: 
> +
> +  When set on a reply to an existing cover letter or patch, this header will
> +  mark the comment as "unaddressed". The comment can then be addressed using
> +  the API or web UI. For more details, refer to
> +  :ref:`overview-comment-metadata`.
> diff --git docs/usage/overview.rst docs/usage/overview.rst
> index a58acfa5..297569ec 100644
> --- docs/usage/overview.rst
> +++ docs/usage/overview.rst
> @@ -181,6 +181,8 @@ system to test patches. Checks have a number of fields 
> associated with them:
> to Patchwork.
>  
>  
> +.. _overview-comment-metadata:
> +
>  Comment Metadata
>  
>  
> @@ -198,6 +200,11 @@ the cover letters. Once the submitter has provided the 
> required information,
>  either the submitter or a maintainer can mark the comment as "addressed". 
> This
>  provides a more granular way of tracking work items than patch states.
>  
> +.. note::
> +
> +   Users can indicate that a comment requires an action using a custom mail
> +   header. For more information, refer to :doc:`/usage/headers`.
> +
>  
>  Collections
>  ---
> diff --git patchwork/parser.py patchwork/parser.py
> index 61a81246..e6e1a7fb 100644
> --- patchwork/parser.py
> +++ patchwork/parser.py
> @@ -1019,6 +1019,12 @@ def find_delegate_by_header(mail):
>  return None
>  
>  
> +def find_comment_addressed_by_header(mail):
> +"""Determine whether a comment is actionable or not."""
> +# we dispose of the value - it's irrelevant
> +return False if 'X-Patchwork-Action-Required' in mail else None
> +
> +
>  def parse_mail(mail, list_id=None):
>  """Parse a mail and add to the database.
>  
> @@ -1278,6 +1284,7 @@ def parse_mail(mail, list_id=None):
>  patch = find_patch_for_comment(project, refs)
>  if patch:
>  author = get_or_create_author(mail, project)
> +addressed = find_comment_addressed_by_header(mail)
>  
>  with transaction.atomic():
>  if PatchComment.objects.filter(patch=patch, msgid=msgid):
> @@ -1288,7 +1295,8 @@ def parse_mail(mail, list_id=None):
>  date=date,
>  headers=headers,
>  submitter=author,
> -content=message)
> +content=message,
> +addressed=addressed)
>  
>  logger.debug('Comment saved')
>  
> diff --git patchwork/tests/test_parser.py patchwork/tests/test_parser.py
> index eaf6599c..d0c5c2d7 100644
> --- patchwork/tests/test_parser.py
> +++ patchwork/tests/test_parser.py
> @@ -18,6 +18,7 @@ from django.db.transaction import atomic
>  from django.db import connection
>  
>  from patchwork.models import Cover
> +from 

Re: [PATCH 1/4] Make addressed/unaddressed workflow opt-in

2021-08-26 Thread Daniel Axtens
Stephen Finucane  writes:

> The current workflow for the address/unaddressed attribute of comments
> sets all comments to unaddressed by default. This is unintuitive, as it
> assumes that all comments are actionable items. It also imposes a
> massive burden on maintainers, who will need to manually sift through
> every single comment received to a list and manually set the
> non-actionable items as "addressed".

I agree that not every email is an actionable item.

I'm not convinced it's a burden on maintainers specifically. The comment
can also be marked as addressed by the patch submitter. Also,
maintainers (and everyone else) are free to ignore the field (and every
other piece of data stored on patchwork).

In general I do think 'unaddressed by default' is a good behaviour. But,
I agree that we can improve the current behaviour.

I think it makes sense to have it as null for every old patch. So if you
migrate, old patch comments are neither addressed nor
unaddressed. That's something I didn't consider sufficiently earlier on.

I think it also makes sense for patches that add 'Acked-by:',
'Reviewed-by:' or 'Tested-by:' to be considered automatically addressed.

But I worry that saying that everything is automatically neither means
that a patch sumbitter could very easily forget to do that and then we
risk losing the value that the feature is supposed to add.

Another thing we could consider doing is making it opt-in by
project. For projects that keep pw very tidy (I'm thinking
e.g. https://patchwork.kernel.org/project/netdevbpf/list/) then
the addressed/unaddressed thing might be more useful and less noisy than
e.g. linuxppc which is a bit less well pruned.

But, again, I see the un/addressed field as being for the submitter, not
the maintainer. The maintainer can't trust it anyway because what the
submitter considers 'addressed' and the maintainer considers 'addressed'
could be very different. So really I see this as helping a submitter to
track that there is nothing waiting on them.

> Change this workflow so that the 'addressed' field defaults to NULL.
> This means maintainers or users must manually set this to False when
> they're requesting additional feedback. This is currently possible via
> the web UI or REST API. A future change will make it possible via a
> custom mail header.
>
> Signed-off-by: Stephen Finucane 
> Cc: Raxel Gutierrez 
> Cc: Daniel Axtens 
> ---
> I think it's essential we make this change in order for this patch to be
> useful. I also think it's okay to modify the migration in place, since
> (a) we don't support deployment from master in production and (b) to the
> best of my knowledge, setting a default, non-NULL value on a new column
> is an expensive operation on certain databases (MySQL?) while changing
> a column value for all rows is *definitely* expensive. The template work
> could possibly do with tweaking. Feel free to advise if so.

We totally can change the migration in place.

Kind regards,
Daniel

> ---
>  docs/api/schemas/latest/patchwork.yaml|  2 ++
>  docs/api/schemas/patchwork.j2 |  2 ++
>  docs/api/schemas/v1.3/patchwork.yaml  |  2 ++
>  htdocs/js/submission.js   | 14 +++--
>  patchwork/migrations/0045_addressed_fields.py |  4 ++--
>  patchwork/models.py   |  4 ++--
>  patchwork/templates/patchwork/submission.html | 20 +++
>  7 files changed, 38 insertions(+), 10 deletions(-)
>
> diff --git docs/api/schemas/latest/patchwork.yaml 
> docs/api/schemas/latest/patchwork.yaml
> index e3bff990..2a98c179 100644
> --- docs/api/schemas/latest/patchwork.yaml
> +++ docs/api/schemas/latest/patchwork.yaml
> @@ -1669,12 +1669,14 @@ components:
>  addressed:
>title: Addressed
>type: boolean
> +  nullable: true
>  CommentUpdate:
>type: object
>properties:
>  addressed:
>title: Addressed
>type: boolean
> +  nullable: true
>  CoverList:
>type: object
>properties:
> diff --git docs/api/schemas/patchwork.j2 docs/api/schemas/patchwork.j2
> index 3b4ad2f6..02aa9f72 100644
> --- docs/api/schemas/patchwork.j2
> +++ docs/api/schemas/patchwork.j2
> @@ -1734,12 +1734,14 @@ components:
>  addressed:
>title: Addressed
>type: boolean
> +  nullable: true
>  CommentUpdate:
>type: object
>properties:
>  addressed:
>title: Addressed
>type: boolean
> +  nullable: true
>  {% endif %}
>  CoverList:
>type: object
> diff --git docs/api/schemas/v1.3/patchwork.yaml 
> docs/api/schemas/v1.3/patchwork.yaml
> index 6cbba646..0a9046a5 10

Re: [PATCH v4 5/5] patch-list: add inline dropdown for delegate and state one-off changes

2021-08-26 Thread Daniel Axtens
Ah, that is but one of the issues - I forgot to check what happened if
you were logged in as a normal user rather than a maintainer. Add this too:

diff --git a/patchwork/views/__init__.py b/patchwork/views/__init__.py
index d41c4609fe6e..f1acfb3a599e 100644
--- a/patchwork/views/__init__.py
+++ b/patchwork/views/__init__.py
@@ -280,7 +280,7 @@ def generic_list(request, project, view, view_args=None, 
filter_settings=None,
 # but we will need to follow the state and submitter relations for
 # rendering the list template
 patches = patches.select_related('state', 'submitter', 'delegate',
- 'series')
+ 'series', 'submitter__user')
 
 patches = patches.only('state', 'submitter', 'delegate', 'project',
'series__name', 'name', 'date', 'msgid')


Daniel Axtens  writes:

>> TODO: The loading of the patch-list page is very slow now because it
>> seems that for each call to check if a patch is editable by a user, the
>> db is accessed. Changes in the backend need to be made so this is done
>> with only done with only one call to the db.
>
> AFAICT, the issue is that the code does a new db query for every
> patch.is_editable() It didn't make things noticable slow for me, but I
> do see an explosion in query volume. Anyway, try the following:
>
> diff --git a/patchwork/models.py b/patchwork/models.py
> index 58e4c51e9716..c29f9a988fd5 100644
> --- a/patchwork/models.py
> +++ b/patchwork/models.py
> @@ -93,10 +93,14 @@ class Project(models.Model):
>  send_notifications = models.BooleanField(default=False)
>  use_tags = models.BooleanField(default=True)
>  
> +@cached_property
> +def maintainer_users(self):
> +return [x.user for x in self.maintainer_project.all().only('user')]
> +
>  def is_editable(self, user):
>  if not user.is_authenticated:
>  return False
> -return self in user.profile.maintainer_projects.all()
> +return user in self.maintainer_users
>  
>  @cached_property
>  def tags(self):
>
>
> FWIW, I still think more than a few maintainers would be surprised that
> delegates are editable by normal users... I still think we probably want
> to make some guardrails available for projects. Not entirely sure what
> that should look like just yet but that's where my head is at.
>
> Kind regards,
> Daniel
>
>>
>> Signed-off-by: Raxel Gutierrez 
>> ---
>>  htdocs/README.rst |  6 +++
>>  htdocs/js/patch-list.js   | 52 ++-
>>  .../patchwork/partials/patch-list.html| 35 +++--
>>  patchwork/views/__init__.py   |  6 +++
>>  4 files changed, 94 insertions(+), 5 deletions(-)
>>
>> diff --git a/htdocs/README.rst b/htdocs/README.rst
>> index 6c435124..32550119 100644
>> --- a/htdocs/README.rst
>> +++ b/htdocs/README.rst
>> @@ -133,6 +133,12 @@ js
>>  
>>Part of Patchwork.
>>  
>> +``patch-list.js.``
>> +  Event helpers and other application logic for patch-list.html. These
>> +  support patch list manipulation (e.g. inline dropdown changes).
>> +
>> +  Part of Patchwork.
>> +
>>  ``selectize.min.js``
>>Selectize is the hybrid of a ``textbox`` and  box. It's jQuery
>>based and it has autocomplete and native-feeling keyboard navigation; 
>> useful
>> diff --git a/htdocs/js/patch-list.js b/htdocs/js/patch-list.js
>> index 6ae13721..526f5370 100644
>> --- a/htdocs/js/patch-list.js
>> +++ b/htdocs/js/patch-list.js
>> @@ -1,5 +1,32 @@
>> +import { updateProperty } from "./rest.js";
>> +
>>  $( document ).ready(function() {
>> -$("#patch-list").stickyTableHeaders();
>> +let inlinePropertyDropdowns = $("td > 
>> select[class^='change-property-']");
>> +$(inlinePropertyDropdowns).each(function() {
>> +// Store previous dropdown selection
>> +$(this).data("prevProperty", $(this).val());
>> +});
>> +
>> +// Change listener for dropdowns that change an individual patch's 
>> delegate and state properties
>> +$(inlinePropertyDropdowns).change((event) => {
>> +const property = event.target.getAttribute("value");
>> +const { url, data } = getPatchProperties(event.target, property);
>> +const updateMessage = {
>> +'error': "No patches updated",
>> +'success': "1 patch updated",
>> +};
>> +updateProperty(url, data, updateMessage

Re: [PATCH v4 5/5] patch-list: add inline dropdown for delegate and state one-off changes

2021-08-26 Thread Daniel Axtens
> TODO: The loading of the patch-list page is very slow now because it
> seems that for each call to check if a patch is editable by a user, the
> db is accessed. Changes in the backend need to be made so this is done
> with only done with only one call to the db.

AFAICT, the issue is that the code does a new db query for every
patch.is_editable() It didn't make things noticable slow for me, but I
do see an explosion in query volume. Anyway, try the following:

diff --git a/patchwork/models.py b/patchwork/models.py
index 58e4c51e9716..c29f9a988fd5 100644
--- a/patchwork/models.py
+++ b/patchwork/models.py
@@ -93,10 +93,14 @@ class Project(models.Model):
 send_notifications = models.BooleanField(default=False)
 use_tags = models.BooleanField(default=True)
 
+@cached_property
+def maintainer_users(self):
+return [x.user for x in self.maintainer_project.all().only('user')]
+
 def is_editable(self, user):
 if not user.is_authenticated:
 return False
-return self in user.profile.maintainer_projects.all()
+return user in self.maintainer_users
 
 @cached_property
 def tags(self):


FWIW, I still think more than a few maintainers would be surprised that
delegates are editable by normal users... I still think we probably want
to make some guardrails available for projects. Not entirely sure what
that should look like just yet but that's where my head is at.

Kind regards,
Daniel

>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/README.rst |  6 +++
>  htdocs/js/patch-list.js   | 52 ++-
>  .../patchwork/partials/patch-list.html| 35 +++--
>  patchwork/views/__init__.py   |  6 +++
>  4 files changed, 94 insertions(+), 5 deletions(-)
>
> diff --git a/htdocs/README.rst b/htdocs/README.rst
> index 6c435124..32550119 100644
> --- a/htdocs/README.rst
> +++ b/htdocs/README.rst
> @@ -133,6 +133,12 @@ js
>  
>Part of Patchwork.
>  
> +``patch-list.js.``
> +  Event helpers and other application logic for patch-list.html. These
> +  support patch list manipulation (e.g. inline dropdown changes).
> +
> +  Part of Patchwork.
> +
>  ``selectize.min.js``
>Selectize is the hybrid of a ``textbox`` and  box. It's jQuery
>based and it has autocomplete and native-feeling keyboard navigation; 
> useful
> diff --git a/htdocs/js/patch-list.js b/htdocs/js/patch-list.js
> index 6ae13721..526f5370 100644
> --- a/htdocs/js/patch-list.js
> +++ b/htdocs/js/patch-list.js
> @@ -1,5 +1,32 @@
> +import { updateProperty } from "./rest.js";
> +
>  $( document ).ready(function() {
> -$("#patch-list").stickyTableHeaders();
> +let inlinePropertyDropdowns = $("td > 
> select[class^='change-property-']");
> +$(inlinePropertyDropdowns).each(function() {
> +// Store previous dropdown selection
> +$(this).data("prevProperty", $(this).val());
> +});
> +
> +// Change listener for dropdowns that change an individual patch's 
> delegate and state properties
> +$(inlinePropertyDropdowns).change((event) => {
> +const property = event.target.getAttribute("value");
> +const { url, data } = getPatchProperties(event.target, property);
> +const updateMessage = {
> +'error': "No patches updated",
> +'success': "1 patch updated",
> +};
> +updateProperty(url, data, updateMessage).then(isSuccess => {
> +if (!isSuccess) {
> +// Revert to previous selection
> +$(event.target).val($(event.target).data("prevProperty"));
> +} else {
> +// Update to new previous selection
> +$(event.target).data("prevProperty", $(event.target).val());
> +}
> +});
> +});
> +
> +$("#patchlist").stickyTableHeaders();
>  
>  $("#check-all").change(function(e) {
>  if(this.checked) {
> @@ -9,4 +36,25 @@ $( document ).ready(function() {
>  }
>  e.preventDefault();
>  });
> -});
> \ No newline at end of file
> +
> +/**
> + * Returns the data to make property changes to a patch through PATCH 
> request.
> + * @param {Element} propertySelect Property select element modified.
> + * @param {string} property Patch property modified (e.g. "state", 
> "delegate")
> + * @return {{property: string, value: string}}
> + * property: Property field to be modified in request.
> + * value: New value for property to be modified to in request.
> + */
> +function getPatchProperties(propertySelect, property) {
> +const selectedOption = 
> propertySelect.options[propertySelect.selectedIndex];
> +const patchId = 
> propertySelect.parentElement.parentElement.dataset.patchId;
> +const propertyValue = (property === "state") ? selectedOption.text
> +: (selectedOption.value === "*") ? null : 
> 

Re: [PATCH v4 0/9] patch-detail: add unaddressed/addressed status to patch comments

2021-08-23 Thread Daniel Axtens
> This series is a revision to the previous version of the patch series. 
> The series addresses the review comments from the v3 series [1] and also
> adds support for cover letter comments.

Series applied, with some minor tweaks as (hopefully) documented in my replies.

We could probably have bike-shedded it a bit more but at this stage I'm
pretty happy with where we're at.

 - The database schema changes are small and seem fast to apply.

 - The UI is fairly unobstrustive IMO, and I'm happy to iterate on it as
   we proceed.

 - I'm fairly happy that it makes patchwork more useful for more people,
   which I think is an outcome worth running some risks to attain.

 - The code seems pretty solid and there aren't any objections that I'm
   aware to to things like API design that would be difficult to change
   later.

Congrats Raxel!

Kind regards,
Daniel
>
> Since v3:
> - Patch 1: separates left align of comment headers
> - Patch 2: adds renaming of  to 
> - Patch 3: adds a utils file for template tags and filters
> - Patch 4: split from [v3 07/10] to only add addressed field to comments
> - Patch 5: split from [v3 07/10] to only change edit permissions for comments
> - Patch 6: add support for cover letter comments endpoint
> - Patch 7: split from [v3 08/10] to separate auto-generated files
> - Patch 8: add label and button for cover letter comments
> - Patch 9: reword release notes
>
> [1] https://lists.ozlabs.org/pipermail/patchwork/2021-August/007074.html
>
> Raxel Gutierrez (9):
>   patch-detail: left align message headers
>   api: change  parameter to  for cover comments endpoint
>   templatetags: add utils template filters and tags
>   models: add addressed field
>   models: change edit permissions for comments
>   api: add comments detail endpoint
>   api: add auto-generated OpenAPI schema files
>   patch-detail: add label and button for comment addressed status
>   docs: add release note for addressed/unaddressed comments
>
>  docs/api/schemas/generate-schemas.py  |4 +-
>  docs/api/schemas/latest/patchwork.yaml|  159 +-
>  docs/api/schemas/patchwork.j2 |  165 +
>  docs/api/schemas/v1.3/patchwork.yaml  | 2770 +
>  htdocs/css/style.css  |   50 +-
>  htdocs/js/submission.js   |   20 +
>  patchwork/api/base.py |   24 +-
>  patchwork/api/check.py|   20 +-
>  patchwork/api/comment.py  |  148 +-
>  patchwork/api/cover.py|2 +-
>  .../migrations/0045_auto_20210817_0136.py |   23 +
>  patchwork/models.py   |   20 +-
>  patchwork/templates/patchwork/submission.html |   77 +-
>  patchwork/templatetags/utils.py   |   18 +
>  patchwork/tests/api/test_comment.py   |  390 ++-
>  patchwork/urls.py |   22 +-
>  patchwork/views/patch.py  |4 +-
>  ...essed-patch-comments-bfe71689b6f35a22.yaml |   18 +
>  18 files changed, 3818 insertions(+), 116 deletions(-)
>  create mode 100644 docs/api/schemas/v1.3/patchwork.yaml
>  create mode 100644 patchwork/migrations/0045_auto_20210817_0136.py
>  create mode 100644 patchwork/templatetags/utils.py
>  create mode 100644 
> releasenotes/notes/comment-detail-endpoint-for-addressed-unaddressed-patch-comments-bfe71689b6f35a22.yaml
>
> Range-diff against v3:
>  1:  943864ee <  -:   api: change  parameter to  for 
> comments endpoint
>  2:  1c8ffa52 <  -:   patch-detail: clean up patch detail page 
> template
>  3:  acfff461 <  -:   patch-detail: refactor JS code into 
> submission.js
>  4:  f50f3450 <  -:   patch-detail: change patch info toggles from 
> links to buttons
>  5:  360b5454 <  -:   static: add JS Cookie library to get csrftoken 
> for client-side requests
>  6:  abee581c <  -:   static: add rest.js to handle PATCH requests & 
> respective responses
>  7:  a9cf3606 <  -:   models: add addressed field and change edit 
> permissions for comments
>  -:   >  1:  4898823a patch-detail: left align message headers
>  -:   >  2:  f03fc850 api: change  parameter to  for 
> cover comments endpoint
>  -:   >  3:  96d9495b templatetags: add utils template filters and 
> tags
>  -:   >  4:  97ac397f models: add addressed field
>  -:   >  5:  598e8432 models: change edit permissions for comments
>  -:   >  6:  0aff2bd8 api: add comments detail endpoint
>  8:  86e01dc6 !  7:  abb11759 api: add patch comments detail endpoint and 
> respective tests
> @@ Metadata
>  Author: Raxel Gutierrez 
>  
>   ## Commit message ##
> -api: add patch comments detail endpoint and respective tests
> -
> -Add new endpoint for patch comments at api/.../comments/.
> -The endpoint will make it possible to use the REST API to update the 
> new
> -`addressed` 

Re: [PATCH v4 6/9] api: add comments detail endpoint

2021-08-23 Thread Daniel Axtens
Raxel Gutierrez  writes:

I've merged this with some small changes as detailed below.

> Add new endpoint for patch and cover comments at 
> api/.../comments/.
> This involves updating the API version to v1.3 to reflect the new
> endpoints as a minor change, following the usual semantic versioning
> convention.
>
> The endpoint will make it possible to use the REST API to update the new
> `addressed` field for individual patch and cover comments with JavaScript
> on the client side. In the process of these changes, clean up the use of
> the CurrentPatchDefault context so that it exists in base.py and can be
> used throughout the API (e.g. Check and Comment REST endpoints).
>
> The tests cover retrieval and update requests and also handle calls from
> the various API versions. Also, they cover permissions for update
> requests and handle invalid update values for the new `addressed` field.
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  docs/api/schemas/generate-schemas.py |   4 +-
>  docs/api/schemas/patchwork.j2| 165 
>  patchwork/api/base.py|  24 +-
>  patchwork/api/check.py   |  20 +-
>  patchwork/api/comment.py | 146 --
>  patchwork/tests/api/test_comment.py  | 388 ---
>  patchwork/urls.py|  20 +-
>  7 files changed, 682 insertions(+), 85 deletions(-)
>
> diff --git a/docs/api/schemas/generate-schemas.py 
> b/docs/api/schemas/generate-schemas.py
> index a0c1e45f..3a436a16 100755
> --- a/docs/api/schemas/generate-schemas.py
> +++ b/docs/api/schemas/generate-schemas.py
> @@ -14,8 +14,8 @@ except ImportError:
>  yaml = None
>  
>  ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
> -VERSIONS = [(1, 0), (1, 1), (1, 2), None]
> -LATEST_VERSION = (1, 2)
> +VERSIONS = [(1, 0), (1, 1), (1, 2), (1, 3), None]
> +LATEST_VERSION = (1, 3)
>  
>  
>  def generate_schemas():
> diff --git a/docs/api/schemas/patchwork.j2 b/docs/api/schemas/patchwork.j2
> index af20743d..3b4ad2f6 100644
> --- a/docs/api/schemas/patchwork.j2
> +++ b/docs/api/schemas/patchwork.j2
> @@ -324,6 +324,74 @@ paths:
>  $ref: '#/components/schemas/Error'
>tags:
>  - comments
> +{% if version >= (1, 3) %}
> +  /api/{{ version_url }}covers/{cover_id}/comments/{comment_id}/:
> +parameters:
> +  - in: path
> +name: cover_id
> +description: A unique integer value identifying the parent cover.
> +required: true
> +schema:
> +  title: Cover ID
> +  type: integer
> +  - in: path
> +name: comment_id
> +description: A unique integer value identifying this comment.
> +required: true
> +schema:
> +  title: Comment ID
> +  type: integer
> +get:
> +  description: Show a cover comment.
> +  operationId: cover_comments_read
> +  responses:
> +'200':
> +  description: ''
> +  content:
> +application/json:
> +  schema:
> +$ref: '#/components/schemas/Comment'
> +'404':
> +  description: Not found
> +  content:
> +application/json:
> +  schema:
> +$ref: '#/components/schemas/Error'
> +  tags:
> +- comments
> +patch:
> +  description: Update a cover comment (partial).
> +  operationId: cover_comments_partial_update
> +  requestBody:
> +$ref: '#/components/requestBodies/Comment'
> +  responses:
> +'200':
> +  description: ''
> +  content:
> +application/json:
> +  schema:
> +$ref: '#/components/schemas/Comment'
> +'400':
> +  description: Invalid Request
> +  content:
> +application/json:
> +  schema:
> +$ref: '#/components/schemas/ErrorCommentUpdate'
> +'403':
> +  description: Forbidden
> +  content:
> +application/json:
> +  schema:
> +$ref: '#/components/schemas/Error'
> +'404':
> +  description: Not found
> +  content:
> +application/json:
> +  schema:
> +$ref: '#/components/schemas/Error'
> +  tags:
> +- comments
> +{% endif %}
>/api/{{ version_url }}events/:
>  get:
>description: List events.
> @@ -656,6 +724,74 @@ paths:
>  $ref: '#/components/schemas/Error'
>tags:
>  - comments
> +{% if version >= (1, 3) %}
> +  /api/{{ version_url }}patches/{patch_id}/comments/{comment_id}/:
> +parameters:
> +  - in: path
> +name: patch_id
> +description: A unique integer value identifying the parent patch.
> +required: true
> +schema:
> +  title: Patch ID
> +  type: integer
> +  - in: path
> +name: comment_id
> +description: A unique integer value 

Re: [PATCH v4 8/9] patch-detail: add label and button for comment addressed status

2021-08-23 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Add new label to patch and cover comments to show the status of whether
> they are addressed or not and add an adjacent button to allow users to
> change the status of the comment. Only users that can edit the patch
> (i.e. patch author, delegate, project maintainers) as well as comment
> authors can change the status of a patch comment. For cover comments,
> there are no delegates, so only maintainers and cover/cover comment
> authors can edit the status of the cover comment. Before [1] and after
> [2] images for reference.
>
> Use new comment detail REST API endpoint to update the addressed field
> value when "Addressed" or "Unaddressed" buttons are clicked. After a
> successful request is made, the appearance of the comment status label
> and buttons are toggled appropriately. For unsuccessful requests (e.g.
> network errors prevents reaching the server), the error message is
> populated to the page. A future improvement on this behavior is to add
> a spinner to the button to provide a feedback that the request is in a
> pending state until it's handled.

My only complaint with the current design is by the time I've scrolled
down far enough to see the comments, I can no longer see the top bar
where the messages appear. But hopefully we can sort that out later.

>
> [1] https://imgur.com/3ZKzgjN
> [2] https://imgur.com/hWZrrnM
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/css/style.css  | 38 
>  htdocs/js/submission.js   | 20 +++
>  patchwork/templates/patchwork/submission.html | 60 +++
>  patchwork/views/patch.py  |  4 +-
>  4 files changed, 110 insertions(+), 12 deletions(-)
>
> diff --git a/htdocs/css/style.css b/htdocs/css/style.css
> index a2a2e3c3..9156aa6e 100644
> --- a/htdocs/css/style.css
> +++ b/htdocs/css/style.css
> @@ -1,4 +1,5 @@
>  :root {
> +--light-color:rgb(247, 247, 247);
>  --success-color:rgb(92, 184, 92);
>  --warning-color:rgb(240, 173, 78);
>  --danger-color:rgb(217, 83, 79);
> @@ -27,6 +28,10 @@ pre {
>  top: 17em;
>  }
>  
> +.hidden {
> +visibility: hidden;
> +}
> +
>  /* Bootstrap overrides */
>  
>  .navbar-inverse .navbar-brand > a {
> @@ -315,6 +320,39 @@ table.patch-meta tr th, table.patch-meta tr td {
>  font-family: "DejaVu Sans Mono", fixed;
>  }
>  
> +div[class^="comment-status-bar-"] {
> +display: flex;
> +flex-wrap: wrap;
> +align-items: center;
> +}
> +
> +.comment-status-label {
> +margin: 0px 8px;
> +}
> +
> +button[class^=comment-action] {
> +background-color: var(--light-color);
> +border-radius: 4px;
> +}
> +
> +.comment-action-addressed {
> +border-color: var(--success-color);
> +}
> +
> +.comment-action-unaddressed {
> +border-color: var(--warning-color);
> +}
> +
> +.comment-action-addressed:hover {
> +background-color: var(--success-color);
> +color: var(--light-color);
> +}
> +
> +.comment-action-unaddressed:hover {
> +background-color: var(--warning-color);
> +color: var(--light-color);
> +}
> +
>  .quote {
>  color: #007f00;
>  }
> diff --git a/htdocs/js/submission.js b/htdocs/js/submission.js
> index 9676f348..47cffc82 100644
> --- a/htdocs/js/submission.js
> +++ b/htdocs/js/submission.js
> @@ -1,4 +1,7 @@
> +import { updateProperty } from "./rest.js";
> +
>  $( document ).ready(function() {
> +const patchMeta = document.getElementById("patch-meta");
>  function toggleDiv(link_id, headers_id, label_show, label_hide) {
>  const link = document.getElementById(link_id)
>  const headers = document.getElementById(headers_id)
> @@ -14,6 +17,23 @@ $( document ).ready(function() {
>  }
>  }
>  
> +$("button[class^='comment-action']").click((event) => {
> +const submissionType = patchMeta.dataset.submissionType;
> +const submissionId = patchMeta.dataset.submissionId;
> +const commentId = event.target.parentElement.dataset.commentId;
> +const url = 
> `/api/${submissionType}/${submissionId}/comments/${commentId}/`;
> +const data = {'addressed': event.target.value} ;
> +const updateMessage = {
> +'error': "No comments updated",
> +'success': "1 comment(s) updated",
> +};
> +updateProperty(url, data, updateMessage).then(isSuccess => {
> +if (isSuccess) {
> +
> $("div[class^='comment-status-bar-'][data-comment-id='"+commentId+"']").toggleClass("hidden");
> +}
> +})
> +});
> +
>  // Click listener to show/hide headers
>  
> document.getElementById("toggle-patch-headers").addEventListener("click", 
> function() {
>  toggleDiv("toggle-patch-headers", "patch-headers");
> diff --git a/patchwork/templates/patchwork/submission.html 
> b/patchwork/templates/patchwork/submission.html
> index 36b15d0e..2238e82e 100644
> --- a/patchwork/templates/patchwork/submission.html
> 

Re: [PATCH v4 2/9] api: change parameter to for cover comments endpoint

2021-08-23 Thread Daniel Axtens
I made some changes to handle the bugfixes that we made over the weekend
and then applied this.

Raxel Gutierrez  writes:

> Rename cover lookup parameter `pk` to `cover_id` for the cover comments
> list endpoints to disambiguate from the lookup parameter `comment_id` in
> upcoming patches which introduces the cover comments detail endpoint.
> This doesn't affect the user-facing API.
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  patchwork/api/comment.py| 6 +++---
>  patchwork/api/cover.py  | 2 +-
>  patchwork/tests/api/test_comment.py | 4 ++--
>  patchwork/urls.py   | 2 +-
>  4 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/patchwork/api/comment.py b/patchwork/api/comment.py
> index 0c578b44..5d7a77a1 100644
> --- a/patchwork/api/comment.py
> +++ b/patchwork/api/comment.py
> @@ -83,14 +83,14 @@ class CoverCommentList(ListAPIView):
>  search_fields = ('subject',)
>  ordering_fields = ('id', 'subject', 'date', 'submitter')
>  ordering = 'id'
> -lookup_url_kwarg = 'pk'
> +lookup_url_kwarg = 'cover_id'
>  
>  def get_queryset(self):
> -if not Cover.objects.filter(pk=self.kwargs['pk']).exists():
> +if not Cover.objects.filter(id=self.kwargs['cover_id']).exists():
>  raise Http404
>  
>  return CoverComment.objects.filter(
> -cover=self.kwargs['pk']
> +cover=self.kwargs['cover_id']
>  ).select_related('submitter')
>  
>  
> diff --git a/patchwork/api/cover.py b/patchwork/api/cover.py
> index b4a3a8f7..c4355f6b 100644
> --- a/patchwork/api/cover.py
> +++ b/patchwork/api/cover.py
> @@ -37,7 +37,7 @@ class CoverListSerializer(BaseHyperlinkedModelSerializer):
>  
>  def get_comments(self, cover):
>  return self.context.get('request').build_absolute_uri(
> -reverse('api-cover-comment-list', kwargs={'pk': cover.id}))
> +reverse('api-cover-comment-list', kwargs={'cover_id': cover.id}))
>  
>  def to_representation(self, instance):
>  # NOTE(stephenfin): This is here to ensure our API looks the same 
> even
> diff --git a/patchwork/tests/api/test_comment.py 
> b/patchwork/tests/api/test_comment.py
> index 59450d8a..53abf8f0 100644
> --- a/patchwork/tests/api/test_comment.py
> +++ b/patchwork/tests/api/test_comment.py
> @@ -27,7 +27,7 @@ class TestCoverComments(utils.APITestCase):
>  kwargs = {}
>  if version:
>  kwargs['version'] = version
> -kwargs['pk'] = cover.id
> +kwargs['cover_id'] = cover.id
>  
>  return reverse('api-cover-comment-list', kwargs=kwargs)
>  
> @@ -79,7 +79,7 @@ class TestCoverComments(utils.APITestCase):
>  def test_list_invalid_cover(self):
>  """Ensure we get a 404 for a non-existent cover letter."""
>  resp = self.client.get(
> -reverse('api-cover-comment-list', kwargs={'pk': '9'}))
> +reverse('api-cover-comment-list', kwargs={'cover_id': '9'}))
>  self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code)
>  
>  
> diff --git a/patchwork/urls.py b/patchwork/urls.py
> index 1e6c12ab..0180e76d 100644
> --- a/patchwork/urls.py
> +++ b/patchwork/urls.py
> @@ -337,7 +337,7 @@ if settings.ENABLE_REST_API:
>  name='api-patch-comment-list',
>  ),
>  path(
> -'covers//comments/',
> +'covers//comments/',
>  api_comment_views.CoverCommentList.as_view(),
>  name='api-cover-comment-list',
>  ),
> -- 
> 2.33.0.rc2.250.ged5fa647cd-goog
>
> ___
> 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


Re: [PATCH v4 1/9] patch-detail: left align message headers

2021-08-23 Thread Daniel Axtens
Applied.

Raxel Gutierrez  writes:

> Change both of the message containers in the "Commit Message" and
> "Comments" to have their content be left-aligned which improves the
> proximity of items which boosts the efficiency of gathering information
> and performing actions. Before [1] and after [2] images for reference.
>
> [1] https://i.imgur.com/ji2ZINL.png
> [2] https://i.imgur.com/Dtn8lm9.png
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/css/style.css  | 12 ++
>  patchwork/templates/patchwork/submission.html | 23 ++-
>  2 files changed, 19 insertions(+), 16 deletions(-)
>
> diff --git a/htdocs/css/style.css b/htdocs/css/style.css
> index f30988e0..a2a2e3c3 100644
> --- a/htdocs/css/style.css
> +++ b/htdocs/css/style.css
> @@ -296,17 +296,19 @@ table.patch-meta tr th, table.patch-meta tr td {
>  color: #f7977a;
>  }
>  
> -.comment .meta {
> +.submission-message .meta {
> +display: flex;
> +align-items: center;
>  background: #f0f0f0;
>  padding: 0.3em 0.5em;
>  }
>  
> -.comment .content {
> -border: 0;
> +.submission-message .message-date {
> +margin-left: 8px;
>  }
>  
> -.patch .content {
> -padding: 1em;
> +.submission-message .content {
> +border: 0;
>  }
>  
>  .patch-pull-url {
> diff --git a/patchwork/templates/patchwork/submission.html 
> b/patchwork/templates/patchwork/submission.html
> index 66efa0b5..36b15d0e 100644
> --- a/patchwork/templates/patchwork/submission.html
> +++ b/patchwork/templates/patchwork/submission.html
> @@ -257,14 +257,14 @@
>  {% else %}
>  Message
>  {% endif %}
> -
> -
> - {{ submission.submitter|personify:project }}
> - {{ submission.date }} UTC
> -
> -
> -{{ submission|commentsyntax }}
> -
> +
> +  
> +{{ submission.submitter|personify:project }}
> +{{ submission.date }} UTC
> +  
> +  
> +  {{ submission|commentsyntax }}
> +  
>  
>  
>  {% for item in comments %}
> @@ -273,11 +273,12 @@
>  {% endif %}
>  
>  
> -
> +
>  
>   {{ item.submitter|personify:project }}
> - {{ item.date }} UTC |  -   >#{{ forloop.counter }}
> + {{ item.date }} UTC |
> +   #{{ 
> forloop.counter }}
> +  
>  
>  
>  {{ item|commentsyntax }}
> -- 
> 2.33.0.rc2.250.ged5fa647cd-goog
>
> ___
> 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


Re: [PATCH] REST: squish final open-codings of get_object_or_404

2021-08-23 Thread Daniel Axtens
Applied in order to apply Raxel's first 2 patches.

Daniel Axtens  writes:

> Basically, finish the job of commit fecf7c86c2c5 ("urls: Add missing path
> converters for REST APIs"). With this commit, there are no more uses of
> Http404 in patchwork/api
>
> Signed-off-by: Daniel Axtens 
> ---
>  patchwork/api/comment.py | 8 +++-
>  1 file changed, 3 insertions(+), 5 deletions(-)
>
> diff --git a/patchwork/api/comment.py b/patchwork/api/comment.py
> index 0c578b44f1cb..e9c52b990080 100644
> --- a/patchwork/api/comment.py
> +++ b/patchwork/api/comment.py
> @@ -5,7 +5,7 @@
>  
>  import email.parser
>  
> -from django.http import Http404
> +from rest_framework.generics import get_object_or_404
>  from rest_framework.generics import ListAPIView
>  from rest_framework.serializers import SerializerMethodField
>  
> @@ -86,8 +86,7 @@ class CoverCommentList(ListAPIView):
>  lookup_url_kwarg = 'pk'
>  
>  def get_queryset(self):
> -if not Cover.objects.filter(pk=self.kwargs['pk']).exists():
> -raise Http404
> +get_object_or_404(Cover, pk=self.kwargs['pk'])
>  
>  return CoverComment.objects.filter(
>  cover=self.kwargs['pk']
> @@ -105,8 +104,7 @@ class PatchCommentList(ListAPIView):
>  lookup_url_kwarg = 'patch_id'
>  
>  def get_queryset(self):
> -if not Patch.objects.filter(id=self.kwargs['patch_id']).exists():
> -raise Http404
> +get_object_or_404(Patch, id=self.kwargs['patch_id'])
>  
>  return PatchComment.objects.filter(
>  patch=self.kwargs['patch_id']
> -- 
> 2.30.2
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH] REST: squish final open-codings of get_object_or_404

2021-08-23 Thread Daniel Axtens
Basically, finish the job of commit fecf7c86c2c5 ("urls: Add missing path
converters for REST APIs"). With this commit, there are no more uses of
Http404 in patchwork/api

Signed-off-by: Daniel Axtens 
---
 patchwork/api/comment.py | 8 +++-
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/patchwork/api/comment.py b/patchwork/api/comment.py
index 0c578b44f1cb..e9c52b990080 100644
--- a/patchwork/api/comment.py
+++ b/patchwork/api/comment.py
@@ -5,7 +5,7 @@
 
 import email.parser
 
-from django.http import Http404
+from rest_framework.generics import get_object_or_404
 from rest_framework.generics import ListAPIView
 from rest_framework.serializers import SerializerMethodField
 
@@ -86,8 +86,7 @@ class CoverCommentList(ListAPIView):
 lookup_url_kwarg = 'pk'
 
 def get_queryset(self):
-if not Cover.objects.filter(pk=self.kwargs['pk']).exists():
-raise Http404
+get_object_or_404(Cover, pk=self.kwargs['pk'])
 
 return CoverComment.objects.filter(
 cover=self.kwargs['pk']
@@ -105,8 +104,7 @@ class PatchCommentList(ListAPIView):
 lookup_url_kwarg = 'patch_id'
 
 def get_queryset(self):
-if not Patch.objects.filter(id=self.kwargs['patch_id']).exists():
-raise Http404
+get_object_or_404(Patch, id=self.kwargs['patch_id'])
 
 return PatchComment.objects.filter(
 patch=self.kwargs['patch_id']
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH] docs: prevent build error by rolling back Sphinx version

2021-08-23 Thread Daniel Axtens
We're hitting a sphinxcontrib-httpdomain vs sphinx issue that was
causing the openapi part of doc builds to error out with:

:1:Problem in http domain: field is supposed to use role 'obj', but 
that role is not in the domain.

See https://github.com/sphinx-contrib/httpdomain/pull/51

Until it's fixed, hold back the Sphinx version to < 4.1.0

Signed-off-by: Daniel Axtens 
---
 docs/requirements.txt | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/docs/requirements.txt b/docs/requirements.txt
index e2641c8fb996..b60bca53215f 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,4 +1,6 @@
-sphinx>=2.0
+# sphinx 4.1.x breaks sphinxcontrib-httpdomain which sphinxcontrib-openapi 
depends on
+# see https://github.com/sphinx-contrib/httpdomain/pull/51
+sphinx>=2.0,<4.1
 reno>=2.2
 sphinx_rtd_theme~=0.5.0
 jinja2~=2.11.2
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH v4 4/9] models: add addressed field

2021-08-22 Thread Daniel Axtens
Stephen Finucane  writes:

> On Fri, 2021-08-20 at 04:50 +, Raxel Gutierrez wrote:
>> Currently, there is no state or status associated with comments. In
>> particular, knowing whether a comment on a patch or cover letter is
>> addressed or not is useful for transparency and accountability in the
>> patch review and contribution process. This patch is backend setup for
>> tracking the state of patch and cover comments.
>> 
>> Add `addressed` boolean field to patch and cover comments to be able to
>> distinguish between unaddressed and addressed comments in the
>> patch-detail page.
>> 
>> Signed-off-by: Raxel Gutierrez 
>> Reviewed-by: Daniel Axtens 
>
> I'm still not entirely happy with this design. It's functional, but it seems
> overly verbose for what I think we're trying to do here. In particular, the 
> idea
> that every single comment is actionable and is unaddressed by default feels
> wrong to me.
>
> Who is this field intended for? Is it for maintainers to track whether a
> question they asked has been answered, or is it for submitter to track whether
> they've answered all of a reviewers questions? I suspect it's the former and, 
> if
> so, what are your thoughts on defaulting to addressed being unset by default
> (i.e. NULL or no action item). This would mean maintainers would be required 
> to
> set 'addressed=False' when needed? We could event allow them to do this via an
> email header which would be easily set in mail clients like mutt. If it's the
> latter, the same approach would probably still work, I think. WDYT?

>From my point of view, it's mostly aimed at submitters who want a method
to keep track of their own tasks. (I know Emily S was hoping to one day
see unaddressed comments in a user's to-do list, which I think gives you
more insight into the broader use case.)

I think maintainers could chose to make it part of their workflow, but
given that the submitter gets to mark a comment as addressed, a
maintainer couldn't really trust that 'addressed' meant 'addressed
satisfactorily'. It's not really a tool to tell maintainers that a patch
is ready.

IMO the impact is fairly small on people who don't want to use it - they
can just ignore the changes in the comment header bar.

The git folks do seem to have a use case in mind for this, and so unless
it's going to really mess up someone else's use case I'm inclined to
accept it and clean up any fallout later.

Kind regards,
Daniel

>
> Aside from this design question, the rest of the changes, particularly those 
> to
> the REST API look okay, but I'll need to spend more time on this over the
> weekend.
>
> Cheers,
> Stephen
>
> ___
> 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


Re: [PATCH] urls: Add missing path converters for REST APIs

2021-08-22 Thread Daniel Axtens
Stephen Finucane  writes:

> Almost all of the API endpoints expect numerical resource IDs, with
> '/projects' being the sole exception. However, we were not actually
> enforcing this anywhere. Instead, we were relying on the custom
> 'get_object_or_404' implementation used by 'GenericAPIView.retrieve' via
> 'GenericAPIView.get_object'. Unfortunately we weren't using this
> everywhere, most notably in our handler for 'GET /patches/{id}/checks'.
> The end result was a HTTP 500 due to a TypeError.

A ValueError, not a TypeError:
 Exception Type: ValueError at /api/patches/abc@def/comments/
 Exception Value: invalid literal for int() with base 10: 'abc@def'

> Resolve this by adding the path converters for all REST API paths in
> 'patchwork.urls', along with tests to prevent regressions going forward.
> We also switch to the DRF variant of 'get_object_or_404' in some places
> to provide additional protection.

LGTM, applied with a tweaked commit message and the change mentioned below:

>
> Signed-off-by: Stephen Finucane 
> Cc: Daniel Axtens 
> ---
>  patchwork/api/base.py   |  3 ++-
>  patchwork/api/check.py  |  7 +++
>  patchwork/tests/api/test_bundle.py  | 11 +++
>  patchwork/tests/api/test_comment.py | 19 +--
>  patchwork/tests/api/test_cover.py   | 11 +++
>  patchwork/tests/api/test_patch.py   | 11 +++
>  patchwork/tests/api/test_person.py  | 14 ++
>  patchwork/tests/api/test_project.py |  8 
>  patchwork/tests/api/test_series.py  | 11 +++
>  patchwork/tests/api/test_user.py| 14 ++
>  patchwork/urls.py   | 20 ++--
>  11 files changed, 112 insertions(+), 17 deletions(-)
>
> diff --git patchwork/api/base.py patchwork/api/base.py
> index 6cb703b1..5368c0cb 100644
> --- patchwork/api/base.py
> +++ patchwork/api/base.py
> @@ -59,7 +59,8 @@ class MultipleFieldLookupMixin(object):
>  queryset = self.filter_queryset(self.get_queryset())
>  filter_kwargs = {}
>  for field_name, field in zip(
> -self.lookup_fields, self.lookup_url_kwargs):
> +self.lookup_fields, self.lookup_url_kwargs,
> +):

AFAICT this is purely a cosmetic change in an otherwise-unmodified file,
so I have dropped it.

>  if self.kwargs[field]:
>  filter_kwargs[field_name] = self.kwargs[field]
>

The rest of the patch looks good, and the full suite of tox tests pass.*

Kind regards,
Daniel

* no they don't, the docs fail, but that's not because of this series.


> diff --git patchwork/api/check.py patchwork/api/check.py
> index a6bf5f8c..5137c1b9 100644
> --- patchwork/api/check.py
> +++ patchwork/api/check.py
> @@ -3,11 +3,10 @@
>  #
>  # SPDX-License-Identifier: GPL-2.0-or-later
>  
> -from django.http import Http404
>  from django.http.request import QueryDict
> -from django.shortcuts import get_object_or_404
>  import rest_framework
>  from rest_framework.exceptions import PermissionDenied
> +from rest_framework.generics import get_object_or_404
>  from rest_framework.generics import ListCreateAPIView
>  from rest_framework.generics import RetrieveAPIView
>  from rest_framework.serializers import CurrentUserDefault
> @@ -95,8 +94,8 @@ class CheckMixin(object):
>  def get_queryset(self):
>  patch_id = self.kwargs['patch_id']
>  
> -if not Patch.objects.filter(pk=self.kwargs['patch_id']).exists():
> -raise Http404
> +# ensure the patch exists
> +get_object_or_404(Patch, id=self.kwargs['patch_id'])
>  
>  return Check.objects.prefetch_related('user').filter(patch=patch_id)
>  
> diff --git patchwork/tests/api/test_bundle.py 
> patchwork/tests/api/test_bundle.py
> index 79924486..1ada79c3 100644
> --- patchwork/tests/api/test_bundle.py
> +++ patchwork/tests/api/test_bundle.py
> @@ -6,6 +6,7 @@
>  import unittest
>  
>  from django.conf import settings
> +from django.urls import NoReverseMatch
>  from django.urls import reverse
>  
>  from patchwork.models import Bundle
> @@ -184,6 +185,16 @@ class TestBundleAPI(utils.APITestCase):
>  self.assertIn('url', resp.data)
>  self.assertNotIn('web_url', resp.data)
>  
> +def test_detail_non_existent(self):
> +"""Ensure we get a 404 for a non-existent bundle."""
> +resp = self.client.get(self.api_url('99'))
> +self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code)
> +
> +def test_detail_invalid(self):
> +"""Ensure we get a 404 for an invalid bundle ID."""
> +with self.assertRaises(NoReverseMatch):
&

Re: [PATCH] migrations: ignore flake8 on 0041_python3

2021-08-22 Thread Daniel Axtens
Applied.

Daniel Axtens  writes:

> commit 3a979ed8bfc6 ("migrations: don't go to the db for 0041_python3 
> migration")
> made a bunch of strings go past 79 characters, breaking flake8 checks.
>
> `black` doesn't seem to fix this and reflowing the strings manually is
> error-prone.
>
> We're not really expecting future changes to this file so just don't run
> flake8 against it.
>
> Fixes: 3a979ed8bfc6 ("migrations: don't go to the db for 0041_python3 
> migration")
> Signed-off-by: Daniel Axtens 
> ---
>  patchwork/migrations/0041_python3.py | 8 
>  1 file changed, 8 insertions(+)
>
> diff --git a/patchwork/migrations/0041_python3.py 
> b/patchwork/migrations/0041_python3.py
> index 25d5de4b66e3..b9316bac04ea 100644
> --- a/patchwork/migrations/0041_python3.py
> +++ b/patchwork/migrations/0041_python3.py
> @@ -1,3 +1,11 @@
> +# commit 3a979ed8bfc6 ("migrations: don't go to the db for 0041_python3 
> migration")
> +# made a bunch of strings go past 79 characters, breaking flake8 checks.
> +#
> +# We're not really expecting future changes to this file so just don't run
> +# flake8 against it.
> +#
> +# flake8: noqa
> +
>  import datetime
>  
>  from django.conf import settings
> -- 
> 2.30.2
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: 2.2.5 release api-1.1 500 errors

2021-08-20 Thread Daniel Axtens
Hi Konstantin,

Thanks for your bug reports. Fixes to both for upstream are on the
list. Fixes for the first (v1.1 errors) should apply directly to
stable/2.2; the second one will require a minor backport.

It's now very late so I'm going to leave this for now. Stephen feel free
to take the wheel, take the patches for a spin and apply them and cut a
new release if you feel like it. Otherwise I'll do it next week. If
you've got better ideas for how to do the fixes I'd also be very open to
that!

Kind regards,
Daniel


> Hello:
>
> After the 2.2.2->2.2.5 upgrade, we seem to be getting lots of /api/1.1/ errors
> for PATCH calls:
>
> PATCH /api/1.1/patches/{id}/
>
> switching to /api/1.2/ seems to fix it, but requires other client app fixes
> for API incompatibility.
>
> Some of the others returning 500 errors are:
>
> GET /api/patches/{msgid}/comments/ 
>
> I'd be happy to provide tracebacks if I can figure out how to get them.
>
> -K
> ___
> 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


[PATCH] REST: don't 500 if we get a non-int ID in /comment/ views

2021-08-20 Thread Daniel Axtens
The Patch and Cover comment views passed unsanitised patch_id/pk
down to the query set filter, which attempts to convert them to
integers, fails, and propagates a ValueError up to us.

Rather than propagating it to the user as a 500, catch it explicitly
and return a 404. Ideally we'd like to return a 400 and some explanation,
but I can't see an easy way to bend Django or DRF to my will here.

Signed-off-by: Daniel Axtens 

---

Will need some tweaking for 2.2, but conceptually simple.

Stephen, would be interested if you can come up with a better way
to propagate a meaningful error out to the user!
---
 patchwork/api/comment.py | 14 ++
 1 file changed, 14 insertions(+)

diff --git a/patchwork/api/comment.py b/patchwork/api/comment.py
index 0c578b44f1cb..4fa48ff73e49 100644
--- a/patchwork/api/comment.py
+++ b/patchwork/api/comment.py
@@ -86,6 +86,13 @@ class CoverCommentList(ListAPIView):
 lookup_url_kwarg = 'pk'
 
 def get_queryset(self):
+# ideally we'd want to raise a 400 here with some explanation, but
+# it's not clear how to. DRF throws away any 404 message we provide
+try:
+int(self.kwargs['pk'])
+except ValueError:
+raise Http404
+
 if not Cover.objects.filter(pk=self.kwargs['pk']).exists():
 raise Http404
 
@@ -105,6 +112,13 @@ class PatchCommentList(ListAPIView):
 lookup_url_kwarg = 'patch_id'
 
 def get_queryset(self):
+# ideally we'd want to raise a 400 here with some explanation, but
+# it's not clear how to. DRF throws away any 404 message we provide
+try:
+int(self.kwargs['patch_id'])
+except ValueError:
+raise Http404
+
 if not Patch.objects.filter(id=self.kwargs['patch_id']).exists():
 raise Http404
 
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: 2.2.5 release api-1.1 500 errors

2021-08-20 Thread Daniel Axtens
> Some of the others returning 500 errors are:
>
> GET /api/patches/{msgid}/comments/ 
>
> I'd be happy to provide tracebacks if I can figure out how to get them.

yeah msgid is not a valid argument there, it needs to be a number. If
you got a traceback it would look like this:


File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/core/handlers/exception.py"
 in inner
  34. response = get_response(request)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/core/handlers/base.py"
 in _get_response
  115. response = self.process_exception_by_middleware(e, 
request)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/core/handlers/base.py"
 in _get_response
  113. response = wrapped_callback(request, *callback_args, 
**callback_kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/views/decorators/csrf.py"
 in wrapped_view
  54. return view_func(*args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/views/generic/base.py"
 in view
  71. return self.dispatch(request, *args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/views.py"
 in dispatch
  505. response = self.handle_exception(exc)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/views.py"
 in handle_exception
  465. self.raise_uncaught_exception(exc)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/views.py"
 in raise_uncaught_exception
  476. raise exc

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/views.py"
 in dispatch
  502. response = handler(request, *args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/generics.py"
 in get
  199. return self.list(request, *args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/mixins.py"
 in list
  38. queryset = self.filter_queryset(self.get_queryset())

File "/home/patchwork/patchwork/patchwork/api/comment.py" in get_queryset
  70. if not Submission.objects.filter(pk=self.kwargs['pk']).exists():

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/db/models/manager.py"
 in manager_method
  82. return getattr(self.get_queryset(), name)(*args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/db/models/query.py"
 in filter
  892. return self._filter_or_exclude(False, *args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/db/models/query.py"
 in _filter_or_exclude
  910. clone.query.add_q(Q(*args, **kwargs))

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/db/models/sql/query.py"
 in add_q
  1290. clause, _ = self._add_q(q_object, self.used_aliases)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/db/models/sql/query.py"
 in _add_q
  1315. child_clause, needed_inner = self.build_filter(

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/db/models/sql/query.py"
 in build_filter
  1251. condition = self.build_lookup(lookups, col, value)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/db/models/sql/query.py"
 in build_lookup
  1116. lookup = lookup_class(lhs, rhs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/db/models/lookups.py"
 in __init__
  20. self.rhs = self.get_prep_lookup()

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/db/models/lookups.py"
 in get_prep_lookup
  70. return self.lhs.output_field.get_prep_value(self.rhs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/db/models/fields/__init__.py"
 in get_prep_value
  972. return int(value)

Exception Type: ValueError at /api/patches/abc@def/comments/
Exception Value: invalid literal for int() with base 10: 'abc@def'

sigh; patch incoming.



>
> -K
> ___
> 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


[PATCH 2/2] tests: test PATCHing a patch in v1.1

2021-08-20 Thread Daniel Axtens
This has been broken for a long time and we didn't notice. Weird.
We fixed it, now add a test.

Signed-off-by: Daniel Axtens 
---
 patchwork/tests/api/test_patch.py | 14 ++
 1 file changed, 14 insertions(+)

diff --git a/patchwork/tests/api/test_patch.py 
b/patchwork/tests/api/test_patch.py
index da2dd6e9084b..74abd4dff460 100644
--- a/patchwork/tests/api/test_patch.py
+++ b/patchwork/tests/api/test_patch.py
@@ -334,6 +334,20 @@ class TestPatchAPI(utils.APITestCase):
 self.assertEqual(status.HTTP_200_OK, resp.status_code, resp)
 self.assertIsNone(Patch.objects.get(id=patch.id).delegate)
 
+def test_update_maintainer_v11(self):
+"""Update patch as maintainer on v1.1."""
+project = create_project()
+patch = create_patch(project=project)
+state = create_state()
+user = create_maintainer(project)
+
+self.client.force_authenticate(user=user)
+resp = self.client.patch(self.api_url(patch.id, version="1.1"),
+ {'state': state.slug, 'delegate': user.id})
+self.assertEqual(status.HTTP_200_OK, resp.status_code, resp)
+self.assertEqual(Patch.objects.get(id=patch.id).state, state)
+self.assertEqual(Patch.objects.get(id=patch.id).delegate, user)
+
 @utils.store_samples('patch-update-error-bad-request')
 def test_update_invalid_state(self):
 """Update patch with invalid fields.
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH 1/2] REST: Don't error if a versioned field we would remove is absent

2021-08-20 Thread Daniel Axtens
We remove fields that shouldn't be seen on old versions of the API.
This was done with `pop(field name)`, which will throw an exception
if the named field is absent from the data. However, sometimes if
a patch request is via an old API version, we hit this line without
ever having the field present.

This is odd, but not harmful and we definitely shouldn't 500.

Fixes: d944f17ec059 ("REST: Use versioning for modified responses")
Signed-off-by: Daniel Axtens 
---
 patchwork/api/base.py | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/patchwork/api/base.py b/patchwork/api/base.py
index 89a43114f9e7..6cb703b12bbb 100644
--- a/patchwork/api/base.py
+++ b/patchwork/api/base.py
@@ -96,6 +96,9 @@ class 
BaseHyperlinkedModelSerializer(HyperlinkedModelSerializer):
 # field was added, we drop it
 if not utils.has_version(request, version):
 for field in self.Meta.versioned_fields[version]:
-data.pop(field)
+# After a PATCH with an older API version, we may not see
+# these fields. If they don't exist, don't panic, return
+# (and then discard) None.
+data.pop(field, None)
 
 return data
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: 2.2.5 release api-1.1 500 errors

2021-08-20 Thread Daniel Axtens
> Here's the PATCH traceback:

It's broken upstream too - we don't have a test for PATCH of a patch
with APIv1.1. I can't figure out why 2.2.5 would have exposed it but I
don't pretend to understand the fine detail of the patch that went in
between 2.2.4 and 2.2.5.

I'll send a patch for this then look at the GET issue.

Kind regards,
Daniel
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: 2.2.5 release api-1.1 500 errors

2021-08-20 Thread Daniel Axtens
Konstantin Ryabitsev  writes:

> Hello:
>
> After the 2.2.2->2.2.5 upgrade, we seem to be getting lots of /api/1.1/ errors
> for PATCH calls:
>
> PATCH /api/1.1/patches/{id}/
>
> switching to /api/1.2/ seems to fix it, but requires other client app fixes
> for API incompatibility.
>
> Some of the others returning 500 errors are:
>
> GET /api/patches/{msgid}/comments/ 
>
> I'd be happy to provide tracebacks if I can figure out how to get them.


Here's the PATCH traceback:

Environment:


Request Method: PATCH
Request URL: http://localhost:8000/api/1.1/patches/1/

Django Version: 2.2.24
Python Version: 3.8.10
Installed Applications:
['django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.humanize',
 'django.contrib.messages',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.admin',
 'django.contrib.staticfiles',
 'patchwork',
 'rest_framework',
 'rest_framework.authtoken',
 'django_filters',
 'debug_toolbar',
 'dbbackup']
Installed Middleware:
['debug_toolbar.middleware.DebugToolbarMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.contrib.admindocs.middleware.XViewMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/core/handlers/exception.py"
 in inner
  34. response = get_response(request)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/core/handlers/base.py"
 in _get_response
  115. response = self.process_exception_by_middleware(e, 
request)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/core/handlers/base.py"
 in _get_response
  113. response = wrapped_callback(request, *callback_args, 
**callback_kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/views/decorators/csrf.py"
 in wrapped_view
  54. return view_func(*args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/django/views/generic/base.py"
 in view
  71. return self.dispatch(request, *args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/views.py"
 in dispatch
  505. response = self.handle_exception(exc)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/views.py"
 in handle_exception
  465. self.raise_uncaught_exception(exc)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/views.py"
 in raise_uncaught_exception
  476. raise exc

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/views.py"
 in dispatch
  502. response = handler(request, *args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/generics.py"
 in patch
  258. return self.partial_update(request, *args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/mixins.py"
 in partial_update
  82. return self.update(request, *args, **kwargs)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/mixins.py"
 in update
  75. return Response(serializer.data)

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/serializers.py"
 in data
  562. ret = super().data

File 
"/opt/pyenv/versions/3.8.10/lib/python3.8/site-packages/rest_framework/serializers.py"
 in data
  260. self._data = self.to_representation(self.instance)

File "/home/patchwork/patchwork/patchwork/api/patch.py" in to_representation
  129. data = super(PatchListSerializer, 
self).to_representation(instance)

File "/home/patchwork/patchwork/patchwork/api/base.py" in to_representation
  99. data.pop(field)

Exception Type: KeyError at /api/1.1/patches/1/
Exception Value: 'related'

Kind regards,
Daniel
>
> -K
> ___
> 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


Re: [PATCH v3 07/10] models: add addressed field and change edit permissions for comments

2021-08-19 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Currently, there is no state or status associated with patch comments.
> In particular, knowing whether a comment on a patch is addressed or not
> is useful for transparency and accountability in the patch review and
> contribution process. This patch is backend setup for tracking the state
> of patch comments.
>
> Add `addressed` boolean field to PatchComment to be able to distinguish
> between unaddressed and addressed comments in the patch-detail page.
> Change PatchComment edit permissions to match that of the patch
> associated with the comment (i.e. patch author, project maintainers, and
> delegate) and add permissions to change `addressed` status for comment
> authors as well.
>

As discussed in another email, I tested the migration and I have no
concerns about it.

Reviewed-by: Daniel Axtens 

I'm not planning to merge this until we have a user, but I think the
remainder of the series is very close to being mergable.

Kind regards,
Daniel

> Signed-off-by: Raxel Gutierrez 
> ---
>  .../migrations/0045_patchcomment_addressed.py  | 18 ++
>  patchwork/models.py|  5 -
>  2 files changed, 22 insertions(+), 1 deletion(-)
>  create mode 100644 patchwork/migrations/0045_patchcomment_addressed.py
>
> diff --git a/patchwork/migrations/0045_patchcomment_addressed.py 
> b/patchwork/migrations/0045_patchcomment_addressed.py
> new file mode 100644
> index 000..92e6c4e
> --- /dev/null
> +++ b/patchwork/migrations/0045_patchcomment_addressed.py
> @@ -0,0 +1,18 @@
> +# Generated by Django 3.1.12 on 2021-07-16 04:12
> +
> +from django.db import migrations, models
> +
> +
> +class Migration(migrations.Migration):
> +
> +dependencies = [
> +('patchwork', '0044_add_project_linkname_validation'),
> +]
> +
> +operations = [
> +migrations.AddField(
> +model_name='patchcomment',
> +name='addressed',
> +field=models.BooleanField(default=False),
> +),
> +]
> diff --git a/patchwork/models.py b/patchwork/models.py
> index 00273da..ef52f2c 100644
> --- a/patchwork/models.py
> +++ b/patchwork/models.py
> @@ -693,6 +693,7 @@ class PatchComment(EmailMixin, models.Model):
>  related_query_name='comment',
>  on_delete=models.CASCADE,
>  )
> +addressed = models.BooleanField(default=False)
>  
>  @property
>  def list_archive_url(self):
> @@ -718,7 +719,9 @@ class PatchComment(EmailMixin, models.Model):
>  self.patch.refresh_tag_counts()
>  
>  def is_editable(self, user):
> -return False
> +if user == self.submitter.user:
> +return True
> +return self.patch.is_editable(user)
>  
>  class Meta:
>  ordering = ['date']
> -- 
> 2.33.0.rc1.237.g0d66db33f3-goog
>
> ___
> 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


Re: [PATCH v3 09/10] patch-detail: add label and button for comment addressed status

2021-08-19 Thread Daniel Axtens
Daniel Axtens  writes:

> Raxel Gutierrez  writes:
>
>> Add new label to patch comments to show the status of whether they are
>> addressed or not and add an adjacent button to allow users to change the
>> status of the comment. Only users that can edit the patch (i.e. patch
>> author, delegate, project maintainers) as well as comment authors can
>> change the status of a comment. Before [1] and after [2] images for
>> reference.
>>
>> Refactor both the message containers in the "Commit Message" and
>> "Comments" to have the same styling through the `patch-message` class so
>> that the headers are normalized to be left-aligned. Also, pass context
>> about whether the patch is `editable` by the user to the patch-detail
>> template.
>>
>> Use new comment detail REST API endpoint to update the addressed field
>> value when "Addressed" or "Unaddressed" buttons are clicked. After, the
>> request is made, the appearance of the comment status label and buttons
>> are toggled appropriately.
>>
>> [1] https://imgur.com/NDfcPJy
>> [2] https://imgur.com/kIhohED
>>
>> Signed-off-by: Raxel Gutierrez 
>> ---
>>  htdocs/css/style.css  | 53 +-
>>  htdocs/js/submission.js   | 15 
>>  patchwork/templates/patchwork/submission.html | 69 ++-
>>  patchwork/views/patch.py  |  1 +
>>  4 files changed, 118 insertions(+), 20 deletions(-)
>>
>> diff --git a/htdocs/css/style.css b/htdocs/css/style.css
>> index 46a91ee..ba83de4 100644
>> --- a/htdocs/css/style.css
>> +++ b/htdocs/css/style.css
>> @@ -1,3 +1,9 @@
>> +:root {
>> +--light-color: #F7F7F7;
>> +--warning-color: #f0ad4e;
>> +--success-color: #5cb85c;
>> +}
>> +
>>  h2 {
>>  font-size: 25px;
>>  margin: 18px 0 18px 0;
>> @@ -21,6 +27,10 @@ pre {
>>  top: 17em;
>>  }
>>  
>> +.hidden {
>> +visibility: hidden;
>> +}
>> +
>>  /* Bootstrap overrides */
>>  
>>  .navbar-inverse .navbar-brand > a {
>> @@ -277,12 +287,18 @@ table.patch-meta tr th, table.patch-meta tr td {
>>  color: #f7977a;
>>  }
>>  
>> -.comment .meta {
>> +.patch-message .meta {
>> +display: flex;
>> +align-items: center;
>>  background: #f0f0f0;
>>  padding: 0.3em 0.5em;
>>  }
>>  
>> -.comment .content {
>> +.patch-message .message-date {
>> +margin-left: 8px;
>> +}
>> +
>> +.patch-message .content {
>>  border: 0;
>>  }
>>  
>> @@ -294,6 +310,39 @@ table.patch-meta tr th, table.patch-meta tr td {
>>  font-family: "DejaVu Sans Mono", fixed;
>>  }
>>  
>> +div[class^="comment-status-bar-"] {
>> +display: flex;
>> +flex-wrap: wrap;
>> +align-items: center;
>> +}
>> +
>> +.comment-status-label {
>> +margin: 0px 8px;
>> +}
>> +
>> +button[class^=comment-action] {
>> +background-color: var(--light-color);
>> +border-radius: 4px;
>> +}
>> +
>> +.comment-action-addressed {
>> +border-color: var(--success-color);
>> +}
>> +
>> +.comment-action-unaddressed {
>> +border-color: var(--warning-color);
>> +}
>> +
>> +.comment-action-addressed:hover {
>> +background-color: var(--success-color);
>> +color: var(--light-color);
>> +}
>> +
>> +.comment-action-unaddressed:hover {
>> +background-color: var(--warning-color);
>> +color: var(--light-color);
>> +}
>> +
>>  .quote {
>>  color: #007f00;
>>  }
>> diff --git a/htdocs/js/submission.js b/htdocs/js/submission.js
>> index 9676f34..27e4a02 100644
>> --- a/htdocs/js/submission.js
>> +++ b/htdocs/js/submission.js
>> @@ -1,3 +1,5 @@
>> +import { updateProperty } from "./rest.js";
>> +
>>  $( document ).ready(function() {
>>  function toggleDiv(link_id, headers_id, label_show, label_hide) {
>>  const link = document.getElementById(link_id)
>> @@ -14,6 +16,19 @@ $( document ).ready(function() {
>>  }
>>  }
>>  
>> +$("button[class^='comment-action']").click((event) => {
>> +const patchId = document.getElementById("patch").dataset.patchId;
>> +const commentId = event.target.parentElement.dataset.commentId;
>> +const url = "/api/patche

Re: [PATCH v2 1/5] api: add addressed field and detail endpoint for patch comments

2021-08-18 Thread Daniel Axtens
>> > +operations = [
>> > +migrations.AddField(
>> > +model_name='patchcomment',
>> > +name='addressed',
>> > +field=models.BooleanField(default=False),
>
> Thinking out loud: I suspect this will be an expensive migration since it will
> add a new non-nullable field to all patchcomment rows. Maybe that's something 
> we
> can live with, but making this nullable might make more sense, particularly if
> we want to make this feature enableable/disableable on a project-by-project
> basis (I don't know if we do).

I tested this on MariaDB 10.4 and it was ~instantaneous even with my
large data set.

I also tested it with Postgres and it was also less than a second.

I think we're fine as is.

Kind regards,
Daniel
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH] migrations: ignore flake8 on 0041_python3

2021-08-18 Thread Daniel Axtens
commit 3a979ed8bfc6 ("migrations: don't go to the db for 0041_python3 
migration")
made a bunch of strings go past 79 characters, breaking flake8 checks.

`black` doesn't seem to fix this and reflowing the strings manually is
error-prone.

We're not really expecting future changes to this file so just don't run
flake8 against it.

Fixes: 3a979ed8bfc6 ("migrations: don't go to the db for 0041_python3 
migration")
Signed-off-by: Daniel Axtens 
---
 patchwork/migrations/0041_python3.py | 8 
 1 file changed, 8 insertions(+)

diff --git a/patchwork/migrations/0041_python3.py 
b/patchwork/migrations/0041_python3.py
index 25d5de4b66e3..b9316bac04ea 100644
--- a/patchwork/migrations/0041_python3.py
+++ b/patchwork/migrations/0041_python3.py
@@ -1,3 +1,11 @@
+# commit 3a979ed8bfc6 ("migrations: don't go to the db for 0041_python3 
migration")
+# made a bunch of strings go past 79 characters, breaking flake8 checks.
+#
+# We're not really expecting future changes to this file so just don't run
+# flake8 against it.
+#
+# flake8: noqa
+
 import datetime
 
 from django.conf import settings
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH v2 3/3] static: add rest.js to handle PATCH requests & respective responses

2021-08-18 Thread Daniel Axtens
Hi,

This fixes all my functional issues. Applying your other patches, this
now counts to at least 102 without issue and displays a network error
appropriately when I kill the server.

At some point I want to turn this all into a tristate: add a 'pending'
state in the frontend while we wait for the network request to resolve,
but that's complex to do and is only really useful in the edgecase where
the request fails for some reason. So it shouldn't block inclusion now.

Pending Stephen's stylistic comments:
Reviewed-by: Daniel Axtens 

Stephen, I'll let you merge this once you're happy with the style.

Kind regards,
Daniel

> Add `rest.js` file to have a utilities JavaScript module that can be
> reused by any Patchwork JS files that make requests to the REST API. In
> particular, this patch provides the following function:
>
>  - `updateProperty`: make PATCH requests that partially update the
>fields of an object given it's REST API endpoint specified by the
>caller. Also, the caller can specify the field(s) to modify and the
>associated content for update messages in the case of both failed
>successful requests that render to the current webpage. The caller
>receives whether the request was successful or not.
>
> The `rest.js` module can be further expanded to support and provide
> functions that allow for other requests (e.g. GET, POST, PUT) to the
> REST API.
>
> Also, add functions that handle update & error messages for these PATCH
> requests that match the Django messages framework format and form error
> styling. These functions are internal to the module and aren't exposed
> outside of the `rest.js` file.
>
> Error and accompanying failed update messages are replaced by successful
> update messages and vice versa. Consecutive successful update messages
> add to a counter of updated objects. Consecutive error messages stack up.
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/js/rest.js | 110 ++
>  1 file changed, 110 insertions(+)
>  create mode 100644 htdocs/js/rest.js
>
> diff --git a/htdocs/js/rest.js b/htdocs/js/rest.js
> new file mode 100644
> index ..5be5682e
> --- /dev/null
> +++ b/htdocs/js/rest.js
> @@ -0,0 +1,110 @@
> +/**
> + * Sends PATCH requests to update objects' properties using the REST API.
> + * @param {string} url Path to the REST API endpoint.
> + * @param {{field: string, value: string}} data
> + * field: Name of the property field to update.
> + * value: Value to update the property field to.
> + * @param {{none: string, some: string}} updateMessage
> + * none: Message when object update failed due to errors.
> + * some: Message when object update successful.
> + */
> +async function updateProperty(url, data, updateMessage) {
> +const request = new Request(url, {
> +method: 'PATCH',
> +mode: "same-origin",
> +headers: {
> +"X-CSRFToken": Cookies.get("csrftoken"),
> +"Content-Type": "application/json",
> +},
> +body: JSON.stringify(data),
> +});
> +
> +return await fetch(request)
> +.then(response => {
> +let message = updateMessage.some;
> +let success = true;
> +if (!response.ok) {
> +response.text().then(text => {
> +const responseObject = JSON.parse(text);
> +// Add error messages from response to page
> +for (const [key,value] of 
> Object.entries(responseObject)) {
> +if (Array.isArray(value)) {
> +for (const error of value) {
> +handleErrorMessages(key + ": " + error);
> +}
> +} else {
> +handleErrorMessages(key + ": " + value);
> +}
> +}
> +});
> +// Update message to be unsuccessful
> +message = updateMessage.none;
> +success = false;
> +}
> +handleUpdateMessages(message, success);
> +return response.ok
> +}).catch(error => {
> +handleErrorMessages(error);
> +});
> +}
> +
> +/**
> + * Populates update messages for API REST requests.
> + * @param {string} messageContent Text for update message.
> + */
> +function handleUpdateMessages(messageContent, success) {
> +// Replace error and failure update messages with success update message
> +const errorContainer = document.getElementById("e

Re: [PATCH v2 1/3] messages: clean up messages and errors containers

2021-08-18 Thread Daniel Axtens
Stephen Finucane  writes:

> On Wed, 2021-08-18 at 12:01 +0100, Stephen Finucane wrote:
>> On Tue, 2021-08-17 at 21:31 +, Raxel Gutierrez wrote:
>> > Refactor the messages container to make use of message.tags [1] which
>> > allows for more customization for each level (e.g. success, warning,
>> > error, etc.) of a message through CSS selectors. As basic proof of
>> > concept, customize the text color of each existing message level. Also,
>> > this addition resolves a TODO by stephenfin in the previous code.
>> > 
>> > Move the errors container after the messages container in the DOM in the
>> > base.html template so that every template can share the same errors
>> > container. Also, add a background color to the errors container so that
>> > both containers blend in as a uniform block. Add bullet points to each
>> > error item in the list of errors.
>> > 
>> > Change both the messages and errors containers to always exist in
>> > the DOM. With this, the addition of update and error messages is simpler
>> > because it eliminates the need to create the containers if they don't
>> > exist. These changes will be useful in a following patch that introduces
>> > an internal JS module to make client-side requests to the REST API.
>> > 
>> > [1] 
>> > https://docs.djangoproject.com/en/3.2/ref/contrib/messages/#message-tags
>> > 
>> > Signed-off-by: Raxel Gutierrez 
>> > ---
>> >  htdocs/css/style.css  | 43 ++-
>> >  patchwork/templates/patchwork/list.html   | 10 -
>> >  .../patchwork/user-link-confirm.html  |  5 +--
>> >  patchwork/templates/patchwork/user-link.html  |  4 +-
>> >  templates/base.html   | 30 +
>> >  5 files changed, 58 insertions(+), 34 deletions(-)
>> > 
>> > diff --git a/htdocs/css/style.css b/htdocs/css/style.css
>> > index 46a91ee8..f30988e0 100644
>> > --- a/htdocs/css/style.css
>> > +++ b/htdocs/css/style.css
>> > @@ -1,3 +1,9 @@
>> > +:root {
>> > +--success-color:rgb(92, 184, 92);
>> > +--warning-color:rgb(240, 173, 78);
>> > +--danger-color:rgb(217, 83, 79);
>> > +}
>> 
>> Neat. I didn't know CSS variables were a thing now, but it seems they've been
>> available for a few years. TIL.
>> 
>> > +
>> >  h2 {
>> >  font-size: 25px;
>> >  margin: 18px 0 18px 0;
>> > @@ -78,14 +84,27 @@ dl dt {
>> >  }
>> >  
>> >  /* messages */
>> > -#messages {
>> > +.messages {
>> >  background: #e0e0f0;
>> > -margin: 0.5em 1em 0.0em 0.5em;
>> > +margin: 0.5em 1em 0.0em 1em;
>> >  padding: 0.3em;
>> > +list-style-type: none;
>> > +}
>> > +
>> > +.messages:empty {
>> > +display: none;
>> > +}
>> > +
>> > +.messages .success {
>> > +color: var(--success-color);
>> > +}
>> > +
>> > +.messages .warning {
>> > +color: var(--warning-color);
>> >  }
>> >  
>> > -#messages .message {
>> > -color: green;
>> > +.messages .error {
>> > +color: var(--danger-color);
>> >  }
>> >  
>> >  .filters {
>> > @@ -421,13 +440,17 @@ table.loginform {
>> >  }
>> >  
>> >  /* form errors */
>> > -.errorlist {
>> > -color: red;
>> > -list-style-type: none;
>> > -padding-left: 0.2em;
>> > -margin: 0em;
>> > +#errors {
>> > +background: #e0e0f0;
>> > +margin: 0em 1em 0.5em 1em;
>> > +padding: 0.3em;
>> >  }
>> > -.error {
>> > +
>> > +#errors:empty {
>> > +display: none;
>> > +}
>> > +
>> > +.error-list, .errorlist {
>> >  color: red;
>> >  }
>> >  
>> > diff --git a/patchwork/templates/patchwork/list.html 
>> > b/patchwork/templates/patchwork/list.html
>> > index 5d3d82aa..6efbed26 100644
>> > --- a/patchwork/templates/patchwork/list.html
>> > +++ b/patchwork/templates/patchwork/list.html
>> > @@ -8,16 +8,6 @@
>> >  
>> >  {% block body %}
>> >  
>> > -{% if errors %}
>> > -The following error{{ errors|length|pluralize:" was,s were" }} 
>> > encountered
>> > -while updating patches:
>> > -
>> > -{% for error in errors %}
>> > - {{ error }}
>> > -{% endfor %}
>> > -
>> > -{% endif %}
>> > -
>> >  {% include "patchwork/partials/patch-list.html" %}
>> >  
>> >  {% endblock %}
>> > diff --git a/patchwork/templates/patchwork/user-link-confirm.html 
>> > b/patchwork/templates/patchwork/user-link-confirm.html
>> > index 79678f64..a6d671f3 100644
>> > --- a/patchwork/templates/patchwork/user-link-confirm.html
>> > +++ b/patchwork/templates/patchwork/user-link-confirm.html
>> > @@ -5,12 +5,9 @@
>> >  
>> >  {% block body %}
>> >  
>> > -{% if errors %}
>> > -{{ errors }}
>> > -{% else %}
>> > +{% if not errors %}
>> >   You have successfully linked the email address {{ person.email }} to
>> >your Patchwork account
>> > -
>> >  {% endif %}
>> >  Back to your
>> >   profile.
>> > diff --git a/patchwork/templates/patchwork/user-link.html 
>> > b/patchwork/templates/patchwork/user-link.html
>> > index bf331520..c0595bdc 100644
>> > --- a/patchwork/templates/patchwork/user-link.html
>> > +++ b/patchwork/templates/patchwork/user-link.html
>> > @@ -9,12 

Re: [PATCH 1/3] messages: clean up messages and errors containers

2021-08-16 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Refactor the messages container to make use of message.tags [1] which
> allows for more customization for each level (e.g. success, warning,
> error, etc.) of a message through CSS selectors. As basic proof of
> concept, customize the text color of each existing message level. Also,
> this addition resolves a TODO by stephenfin in the previous code.
>
> Modularize the errors container with a new partial template errors.html
> that makes it easier to reuse errors in various pages. These changes
> make the code more readable and ready for change.
>
> Change both the messages and errors containers to always exist in
> the DOM. With this, the addition of update and error messages is simpler
> because it eliminates the need to create the containers if they don't
> exist. These changes will be useful in a following patch that introduces
> an internal JS module to make client-side requests to the REST API.

(With the rest of your other addressed/unaddressed series applied,) I'm
seeing the list bullets on errors and messages, and error display is
still a little odd:

https://imgur.com/a/y8vBckQ
https://imgur.com/a/jN8b3KA

Could you please hide the list bullets? (and maybe make the errors all
within a box? I don't know if the current display is by design or not.)

Other than that, looking promising! :)

Kind regards,
Daniel

>
> [1] https://docs.djangoproject.com/en/3.2/ref/contrib/messages/#message-tags
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/css/style.css  | 26 ---
>  patchwork/templates/patchwork/list.html   | 10 +--
>  .../templates/patchwork/partials/errors.html  | 10 +++
>  patchwork/templates/patchwork/submission.html |  2 ++
>  patchwork/templates/patchwork/user-link.html  |  2 +-
>  templates/base.html   | 22 +---
>  6 files changed, 49 insertions(+), 23 deletions(-)
>  create mode 100644 patchwork/templates/patchwork/partials/errors.html
>
> diff --git a/htdocs/css/style.css b/htdocs/css/style.css
> index 46a91ee8..bcc57b2c 100644
> --- a/htdocs/css/style.css
> +++ b/htdocs/css/style.css
> @@ -1,3 +1,9 @@
> +:root {
> +--success-color:rgb(92, 184, 92);
> +--warning-color:rgb(240, 173, 78);
> +--danger-color:rgb(217, 83, 79);
> +}
> +
>  h2 {
>  font-size: 25px;
>  margin: 18px 0 18px 0;
> @@ -78,14 +84,26 @@ dl dt {
>  }
>  
>  /* messages */
> -#messages {
> +.messages {
>  background: #e0e0f0;
>  margin: 0.5em 1em 0.0em 0.5em;
>  padding: 0.3em;
>  }
>  
> -#messages .message {
> -color: green;
> +.messages:empty {
> +display: none;
> +}
> +
> +.messages .success {
> +color: var(--success-color);
> +}
> +
> +.messages .warning {
> +color: var(--warning-color);
> +}
> +
> +.messages .error {
> +color: var(--danger-color);
>  }
>  
>  .filters {
> @@ -421,7 +439,7 @@ table.loginform {
>  }
>  
>  /* form errors */
> -.errorlist {
> +.error-list {
>  color: red;
>  list-style-type: none;
>  padding-left: 0.2em;
> diff --git a/patchwork/templates/patchwork/list.html 
> b/patchwork/templates/patchwork/list.html
> index 5d3d82aa..91387cf0 100644
> --- a/patchwork/templates/patchwork/list.html
> +++ b/patchwork/templates/patchwork/list.html
> @@ -8,15 +8,7 @@
>  
>  {% block body %}
>  
> -{% if errors %}
> -The following error{{ errors|length|pluralize:" was,s were" }} encountered
> -while updating patches:
> -
> -{% for error in errors %}
> - {{ error }}
> -{% endfor %}
> -
> -{% endif %}
> +{% include "patchwork/partials/errors.html" %}
>  
>  {% include "patchwork/partials/patch-list.html" %}
>  
> diff --git a/patchwork/templates/patchwork/partials/errors.html 
> b/patchwork/templates/patchwork/partials/errors.html
> new file mode 100644
> index ..86eec121
> --- /dev/null
> +++ b/patchwork/templates/patchwork/partials/errors.html
> @@ -0,0 +1,10 @@
> +
> +{% if errors %}
> +The following error{{ errors|length|pluralize:" was,s were" }} 
> encountered while updating patches:
> +
> +{% for error in errors %}
> +{{ error }}
> +{% endfor %}
> +
> +{% endif %}
> +
> diff --git a/patchwork/templates/patchwork/submission.html 
> b/patchwork/templates/patchwork/submission.html
> index 66efa0b5..3b65f81d 100644
> --- a/patchwork/templates/patchwork/submission.html
> +++ b/patchwork/templates/patchwork/submission.html
> @@ -14,6 +14,8 @@
>  
>  {% block body %}
>  
> +{% include "patchwork/partials/errors.html" %}
> +
>  
>{% include "patchwork/partials/download-buttons.html" %}
>{{ submission.name }}
> diff --git a/patchwork/templates/patchwork/user-link.html 
> b/patchwork/templates/patchwork/user-link.html
> index bf331520..e23e69d5 100644
> --- a/patchwork/templates/patchwork/user-link.html
> +++ b/patchwork/templates/patchwork/user-link.html
> @@ -14,7 +14,7 @@ you.
>  {{ form.non_field_errors }}
> {% endif %}
> {% if error %}
> -

Re: [PATCH 3/3] static: add rest.js to handle PATCH requests & respective responses

2021-08-16 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Add `rest.js` file to have a utilities JavaScript module that can be
> reused by any Patchwork JS files that make requests to the REST API. In
> particular, this patch provides the following function:
>
>  - `updateProperty`: make PATCH requests that partially update the
>fields of an object given it's REST API endpoint specified by the
>caller. Also, the caller can specify the field(s) to modify and the
>associated content for update messages in the case of both failed
>successful requests that render to the current webpage. The caller
>receives whether the request was successful or not.
>
> The `rest.js` module can be further expanded to support and provide
> functions that allow for other requests (e.g. GET, POST, PUT) to the
> REST API.
>
> Also, add functions that handle update & error messages for these PATCH
> requests that match the Django messages framework format and form error
> styling. These functions are internal to the module and aren't exposed
> outside of the `rest.js` file.
>
> Error and accompanying failed update messages are replaced by successful
> update messages and vice versa. Consecutive successful update messages
> add to a counter of updated objects. Consecutive error messages stack up.

I think most of my comments from the fuller series still apply, in particular:

 - the counter goes 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 2. I think just have
   a global variable to store the number of items successfully updated?

 - you need to handle the case where `fetch()` fails. (e.g. network issues)

[error display (when I cherry picked the rest of the fuller series on
top of this smaller series - and I may have resolved a conflict wrong)
is still a bit quirky but I don't think that's the fault of this patch.]

It is genuinely quite exciting to be calling the API from the
front-end, feels like patchwork is finally entering the 2010s! :P

Kind regards,
Daniel

>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/js/rest.js | 107 ++
>  1 file changed, 107 insertions(+)
>  create mode 100644 htdocs/js/rest.js
>
> diff --git a/htdocs/js/rest.js b/htdocs/js/rest.js
> new file mode 100644
> index ..f8e0a981
> --- /dev/null
> +++ b/htdocs/js/rest.js
> @@ -0,0 +1,107 @@
> +/**
> + * Sends PATCH requests to update objects' properties using the REST API.
> + * @param {string} url Path to the REST API endpoint.
> + * @param {{field: string, value: string}} data
> + * field: Name of the property field to update.
> + * value: Value to update the property field to.
> + * @param {{none: string, some: string}} updateMessage
> + * none: Message when object update failed due to errors.
> + * some: Message when object update successful.
> + */
> +async function updateProperty(url, data, updateMessage) {
> +const request = new Request(url, {
> +method: 'PATCH',
> +mode: "same-origin",
> +headers: {
> +"X-CSRFToken": Cookies.get("csrftoken"),
> +"Content-Type": "application/json",
> +},
> +body: JSON.stringify(data),
> +});
> +
> +return await fetch(request)
> +.then(response => {
> +let message = updateMessage.some;
> +let success = true;
> +if (!response.ok) {
> +response.text().then(text => {
> +const responseObject = JSON.parse(text);
> +// Add error messages from response to page
> +for (const [key,value] of 
> Object.entries(responseObject)) {
> +if (Array.isArray(value)) {
> +for (const error of value) {
> +handleErrorMessages(key + ": " + error);
> +}
> +} else {
> +handleErrorMessages(key + ": " + value);
> +}
> +}
> +});
> +// Update message to be unsuccessful
> +message = updateMessage.none;
> +success = false;
> +}
> +handleUpdateMessages(message, success);
> +return response.ok
> +});
> +}
> +
> +/**
> + * Populates update messages for API REST requests.
> + * @param {string} messageContent Text for update message.
> + */
> +function handleUpdateMessages(messageContent, success) {
> +// Replace error and failure update messages with success update message
> +const errorContainer = document.getElementById("errors");
> +let messages = document.getElementsByClassName("messages")[0];
> +if (success && errorContainer.firstChild != null) {
> +messages.replaceChildren();
> +errorContainer.replaceChildren();
> +} else if (!success) {
> +messages.replaceChildren();
> +}
> +
> +// Increment counter of consecutive success update messages
> +if 

Re: [PATCH 2/3] static: add JS Cookie library to get csrftoken for client-side requests

2021-08-16 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Currently in Patchwork, requests are made only through older methods via
> form submissions, which means the UI is rendered strictly server-side.
> This lags behind more modern and versatile approaches that use
> JavaScript to send requests and dynamically update the UI based on the
> respective responses.
>
> In order to make REST API requests on the client-side secure from CSRF
> attacks, add the JS Cookie library which allows the CSRF token to be
> passed in the request header. A following patch that introduces the
> `rest.js` module will make use of the JS Cookie library in this patch.
>
> The library is a recommendation from Django docs [1]. The files for the
> library can be downloaded in the releases page of the GitHub [2].
>
> [1] https://docs.djangoproject.com/en/3.2/ref/csrf/#ajax
> [2] https://github.com/js-cookie/js-cookie/releases
>
> Signed-off-by: Raxel Gutierrez 

Reviewed-by: Daniel Axtens 

I will merge this once we get the bugs fixed in the REST js.

Something somewhere in the pipeline is corrupting the JS file so I will
replace the js.cookie.min.js with the contents of the v3.0.0 release
from the github repo when I merge it - thank you for the link.

Kind regards,
Daniel

> ---
>  htdocs/README.rst  | 9 +
>  htdocs/js/js.cookie.min.js | 2 ++
>  templates/base.html| 1 +
>  3 files changed, 12 insertions(+)
>  create mode 100644 htdocs/js/js.cookie.min.js
>
> diff --git a/htdocs/README.rst b/htdocs/README.rst
> index 62f15c23..128dc7c2 100644
> --- a/htdocs/README.rst
> +++ b/htdocs/README.rst
> @@ -122,6 +122,15 @@ js
>:GitHub: jQuery plug-in to drag and drop rows in HTML tables
>:Version: ???
>  
> +``js.cookie.min.js``
> +
> +  Library used to handle cookies.
> +
> +  This is used to get the ``csrftoken`` cookie for AJAX requests in 
> JavaScript.
> +
> +  :GitHub: https://github.com/js-cookie/js-cookie/
> +  :Version: 3.0.0
> +
>  ``selectize.min.js``
>  
>Selectize is the hybrid of a ``textbox`` and  box. It's jQuery
> diff --git a/htdocs/js/js.cookie.min.js b/htdocs/js/js.cookie.min.js
> new file mode 100644
> index ..63fc3ef9
> --- /dev/null
> +++ b/htdocs/js/js.cookie.min.js
> @@ -0,0 +1,2 @@
> +/*! js-cookie v3.0.0 | MIT */
> +!function(e,t){"object"==typeof exports&&"undefined"!=typeof 
> module?module.exports=t():"function"==typeof 
> define&?define(t):(e=e||self,function(){var 
> n=e.Cookies,r=e.Cookies=t();r.noConflict=function(){return 
> e.Cookies=n,r}}())}(this,(function(){"use strict";function e(e){for(var 
> t=1;t n)e[r]=n[r]}return e}var t={read:function(e){return 
> e.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(e){return 
> encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}};return
>  function n(r,o){function i(t,n,i){if("undefined"!=typeof 
> document){"number"==typeof(i=e({},o,i)).expires&&(i.expires=new 
> Date(Date.now()+864e5*i.expires)),i.expires&&(i.expires=i.expires.toUTCString()),t=encodeURIComponent(t).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape),n=r.write(n,t);var
>  c="";for(var u in i)i[u]&&(c+="; 
> "+u,!0!==i[u]&&(c+="="+i[u].split(";")[0]));return document.c
 oo
>  kie=t+"="+n+c}}return 
> Object.create({set:i,get:function(e){if("undefined"!=typeof 
> document&&(!arguments.length||e)){for(var 
> n=document.cookie?document.cookie.split("; "):[],o={},i=0;i c=n[i].split("="),u=c.slice(1).join("=");'"'===u[0]&&(u=u.slice(1,-1));try{var
>  f=t.read(c[0]);if(o[f]=r.read(u,f),e===f)break}catch(e){}}return 
> e?o[e]:o}},remove:function(t,n){i(t,"",e({},n,{expires:-1}))},withAttributes:function(t){return
>  n(this.converter,e({},this.attributes,t))},withConverter:function(t){return 
> n(e({},this.converter,t),this.attributes)}},{attributes:{value:Object.freeze(o)},converter:{value:Object.freeze(r)}})}(t,{path:"/"})}));
> diff --git a/templates/base.html b/templates/base.html
> index a9d0906b..5d4349dd 100644
> --- a/templates/base.html
> +++ b/templates/base.html
> @@ -21,6 +21,7 @@
>
>
>
> +  
>

Re: [PATCH v3 09/10] patch-detail: add label and button for comment addressed status

2021-08-16 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Add new label to patch comments to show the status of whether they are
> addressed or not and add an adjacent button to allow users to change the
> status of the comment. Only users that can edit the patch (i.e. patch
> author, delegate, project maintainers) as well as comment authors can
> change the status of a comment. Before [1] and after [2] images for
> reference.
>
> Refactor both the message containers in the "Commit Message" and
> "Comments" to have the same styling through the `patch-message` class so
> that the headers are normalized to be left-aligned. Also, pass context
> about whether the patch is `editable` by the user to the patch-detail
> template.
>
> Use new comment detail REST API endpoint to update the addressed field
> value when "Addressed" or "Unaddressed" buttons are clicked. After, the
> request is made, the appearance of the comment status label and buttons
> are toggled appropriately.
>
> [1] https://imgur.com/NDfcPJy
> [2] https://imgur.com/kIhohED
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/css/style.css  | 53 +-
>  htdocs/js/submission.js   | 15 
>  patchwork/templates/patchwork/submission.html | 69 ++-
>  patchwork/views/patch.py  |  1 +
>  4 files changed, 118 insertions(+), 20 deletions(-)
>
> diff --git a/htdocs/css/style.css b/htdocs/css/style.css
> index 46a91ee..ba83de4 100644
> --- a/htdocs/css/style.css
> +++ b/htdocs/css/style.css
> @@ -1,3 +1,9 @@
> +:root {
> +--light-color: #F7F7F7;
> +--warning-color: #f0ad4e;
> +--success-color: #5cb85c;
> +}
> +
>  h2 {
>  font-size: 25px;
>  margin: 18px 0 18px 0;
> @@ -21,6 +27,10 @@ pre {
>  top: 17em;
>  }
>  
> +.hidden {
> +visibility: hidden;
> +}
> +
>  /* Bootstrap overrides */
>  
>  .navbar-inverse .navbar-brand > a {
> @@ -277,12 +287,18 @@ table.patch-meta tr th, table.patch-meta tr td {
>  color: #f7977a;
>  }
>  
> -.comment .meta {
> +.patch-message .meta {
> +display: flex;
> +align-items: center;
>  background: #f0f0f0;
>  padding: 0.3em 0.5em;
>  }
>  
> -.comment .content {
> +.patch-message .message-date {
> +margin-left: 8px;
> +}
> +
> +.patch-message .content {
>  border: 0;
>  }
>  
> @@ -294,6 +310,39 @@ table.patch-meta tr th, table.patch-meta tr td {
>  font-family: "DejaVu Sans Mono", fixed;
>  }
>  
> +div[class^="comment-status-bar-"] {
> +display: flex;
> +flex-wrap: wrap;
> +align-items: center;
> +}
> +
> +.comment-status-label {
> +margin: 0px 8px;
> +}
> +
> +button[class^=comment-action] {
> +background-color: var(--light-color);
> +border-radius: 4px;
> +}
> +
> +.comment-action-addressed {
> +border-color: var(--success-color);
> +}
> +
> +.comment-action-unaddressed {
> +border-color: var(--warning-color);
> +}
> +
> +.comment-action-addressed:hover {
> +background-color: var(--success-color);
> +color: var(--light-color);
> +}
> +
> +.comment-action-unaddressed:hover {
> +background-color: var(--warning-color);
> +color: var(--light-color);
> +}
> +
>  .quote {
>  color: #007f00;
>  }
> diff --git a/htdocs/js/submission.js b/htdocs/js/submission.js
> index 9676f34..27e4a02 100644
> --- a/htdocs/js/submission.js
> +++ b/htdocs/js/submission.js
> @@ -1,3 +1,5 @@
> +import { updateProperty } from "./rest.js";
> +
>  $( document ).ready(function() {
>  function toggleDiv(link_id, headers_id, label_show, label_hide) {
>  const link = document.getElementById(link_id)
> @@ -14,6 +16,19 @@ $( document ).ready(function() {
>  }
>  }
>  
> +$("button[class^='comment-action']").click((event) => {
> +const patchId = document.getElementById("patch").dataset.patchId;
> +const commentId = event.target.parentElement.dataset.commentId;
> +const url = "/api/patches/" + patchId + "/comments/" + commentId + 
> "/";
> +const data = {'addressed': event.target.value} ;
> +const updateMessage = {
> +'none': "No comments updated",
> +'some': "1 comment(s) updated",
> +};
> +updateProperty(url, data, updateMessage);
> +
> $("div[class^='comment-status-bar-'][data-comment-id='"+commentId+"']").toggleClass("hidden");
> +});
> +

If I hit this button repeatedly and quickly, I eventually get a 500
error from the server and the following on the console:

rest.js:22 PATCH http://localhost:8000/api/patches/17/comments/undefined/ 500 
(Internal Server Error)
VM283:1 Uncaught (in promise) SyntaxError: Unexpected token < in JSON at 
position 0
at JSON.parse ()
at rest.js:28

The django log contains the following revealing snippet (and then we hit
the error from the API patch where we try to look up the linkname field
which doesn't exist):

web_1  | "PATCH /api/patches/17/comments/undefined/ HTTP/1.1" 500 169238
web_1  | Internal Server Error: 

Re: [PATCH v3 06/10] static: add rest.js to handle PATCH requests & respective responses

2021-08-16 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Add `rest.js` file to have a utilities JavaScript module that can be
> reused by any Patchwork JS files that make requests to the REST API. In
> particular, this patch provides the following function:
>
>  - `updateProperty`: make PATCH requests that partially update the
>fields of an object given it's REST API endpoint specified by the
>caller. Also, the caller can specify the field(s) to modify and the
>associated content for update messages in the case of both failed
>successful requests that render to the current webpage.
>
> The `rest.js` module can be further expanded to support and provide
> functions that allow for other requests (e.g. GET, POST, PUT) to the
> REST API.
>
> Also, add functions that handle update & error messages for these PATCH
> requests that match the Django messages framework format and form error
> styling. These functions are internal to the module and aren't exposed
> outside of the `rest.js` file.
>
> Error and accompanying failed update messages are replaced by successful
> update messages and vice versa. Consecutive successful update messages
> add to counter of updated objects. Consecutive error messages stack up.
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/README.rst   |   7 +++
>  htdocs/js/rest.js   | 119 
>  templates/base.html |   3 ++
>  3 files changed, 129 insertions(+)
>  create mode 100644 htdocs/js/rest.js
>
> diff --git a/htdocs/README.rst b/htdocs/README.rst
> index 128dc7c..ced6bb8 100644
> --- a/htdocs/README.rst
> +++ b/htdocs/README.rst
> @@ -131,6 +131,13 @@ js
>:GitHub: https://github.com/js-cookie/js-cookie/
>:Version: 3.0.0
>  
> +``rest.js.``
> +
> +  Utility module for REST API requests to be used by other Patchwork js files
> +  (fetch requests, handling update & error messages).
> +
> +  Part of Patchwork.
> +
>  ``selectize.min.js``
>  
>Selectize is the hybrid of a ``textbox`` and  box. It's jQuery
> diff --git a/htdocs/js/rest.js b/htdocs/js/rest.js
> new file mode 100644
> index 000..faac05d
> --- /dev/null
> +++ b/htdocs/js/rest.js
> @@ -0,0 +1,119 @@
> +/**
> + * Sends PATCH requests to update objects' properties using the REST API.
> + * @param {string} url Path to the REST API endpoint.
> + * @param {{field: string, value: string}} data
> + * field: Name of the property field to update.
> + * value: Value to update the property field to.
> + * @param {{none: string, some: string}} updateMessage
> + * none: Message when object update failed due to errors.
> + * some: Message when object update successful.
> + */
> +async function updateProperty(url, data, updateMessage) {
> +const request = new Request(url, {
> +method: 'PATCH',
> +mode: "same-origin",
> +headers: {
> +"X-CSRFToken": Cookies.get("csrftoken"),
> +"Content-Type": "application/json",
> +},
> +body: JSON.stringify(data),
> +});
> +
> +return await fetch(request)


I tried simulating network issues by loading the page, then killing the
local server, then pressing the button a few times.

No error message was displayed, but there were the following messages in
the JS console:

PATCH http://localhost:8000/api/patches/17/comments/9/ 
net::ERR_CONNECTION_REFUSED

rest.js:46 Uncaught (in promise) TypeError: Failed to fetch

I'm not sure what the proper way to deal with this is in JS, so I will
leave it in your capable hands.

> +.then(response => {
> +let message = updateMessage.some;
> +let success = true;
> +if (!response.ok) {
> +response.text().then(text => {
> +const responseObject = JSON.parse(text);
> +// Add error messages from response to page
> +for (const [key,value] of 
> Object.entries(responseObject)) {

Should there be a space between the comma and value in `const [key,value]`?

> +if (Array.isArray(value)) {
> +for (const error of value) {
> +handleErrorMessages(key + ": " + error);
> +}
> +} else {
> +handleErrorMessages(key + ": " + value);
> +}
> +}
> +// Update message to be unsuccessful
> +message = updateMessage.none;
> +success = false;
> +});
> +}
> +handleUpdateMessages(message, success);
> +return response.ok
> +});
> +}
> +
> +/**
> + * Populates update messages for API REST requests.
> + * @param {string} messageContent Text for update message.
> + */
> +function handleUpdateMessages(messageContent, success=true) {
> +// Replace error and failure update messages with success update message
> +const 

Re: [PATCH v2 1/5] api: add addressed field and detail endpoint for patch comments

2021-08-16 Thread Daniel Axtens
> Thinking out loud: I suspect this will be an expensive migration since it will
> add a new non-nullable field to all patchcomment rows. Maybe that's something 
> we
> can live with, but making this nullable might make more sense, particularly if
> we want to make this feature enableable/disableable on a project-by-project
> basis (I don't know if we do).

There's probably some context here that hopefully you've caught up with
Raxel and co about.

But anyway, the addressed/unaddressed thing just appears in the header
bar of each comment along with the commenter, date and the
permalink. Maintainers/submitters/commenters can chose whether they want
to look at addressed/unaddressed or they want to ignore it. I suspect
the main value will be for submitters to make sure they've addressed the
comments before they send out their next version so in that sense it
isn't even all that dependent on the whims of maintainers. So I was
thinking of just making the whole thing on all the time rather than
introducing the complexity of a per-project feature-flag.

My vibe from the kernel lists I hang out on is that we don't tend to get
quite as many comments as patches, but it could be a pretty noisy
distribution so I might have underestimated it. Anyway, I'll do a test
with and without nullable on my largish data set tomorrow, hopefully.

Kind regards,
Daniel

>> > +),
>> > +]
>> > diff --git a/patchwork/models.py b/patchwork/models.py
>> > index 00273da..ef52f2c 100644
>> > --- a/patchwork/models.py
>> > +++ b/patchwork/models.py
>> > @@ -693,6 +693,7 @@ class PatchComment(EmailMixin, models.Model):
>> >  related_query_name='comment',
>> >  on_delete=models.CASCADE,
>> >  )
>> > +addressed = models.BooleanField(default=False)
>> >  
>> >  @property
>> >  def list_archive_url(self):
>> > @@ -718,7 +719,9 @@ class PatchComment(EmailMixin, models.Model):
>> >  self.patch.refresh_tag_counts()
>> >  
>> >  def is_editable(self, user):
>> > -return False
>> > +if user == self.submitter.user:
>> > +return True
>> > +return self.patch.is_editable(user)
>> >  
>> >  class Meta:
>> >  ordering = ['date']
>> > diff --git a/patchwork/tests/api/test_comment.py 
>> > b/patchwork/tests/api/test_comment.py
>> > index 5bbebf2..59450d8 100644
>> > --- a/patchwork/tests/api/test_comment.py
>> > +++ b/patchwork/tests/api/test_comment.py
>> > @@ -90,7 +90,7 @@ class TestPatchComments(utils.APITestCase):
>> >  kwargs = {}
>> >  if version:
>> >  kwargs['version'] = version
>> > -kwargs['pk'] = patch.id
>> > +kwargs['patch_id'] = patch.id
>> 
>> I think we might be able to get away with renaming the parameter
>> internally even if we can't rename it in old versions of the API, but
>> please split the 'pk'->'patch_id' change into a different patch.
>> 
>> Kind regards,
>> Daniel
>
> Cheers,
> Stephen
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH v3 09/10] patch-detail: add label and button for comment addressed status

2021-08-16 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Add new label to patch comments to show the status of whether they are
> addressed or not and add an adjacent button to allow users to change the
> status of the comment. Only users that can edit the patch (i.e. patch
> author, delegate, project maintainers) as well as comment authors can
> change the status of a comment. Before [1] and after [2] images for
> reference.
>
> Refactor both the message containers in the "Commit Message" and
> "Comments" to have the same styling through the `patch-message` class so
> that the headers are normalized to be left-aligned. Also, pass context
> about whether the patch is `editable` by the user to the patch-detail
> template.
>
> Use new comment detail REST API endpoint to update the addressed field
> value when "Addressed" or "Unaddressed" buttons are clicked. After, the
> request is made, the appearance of the comment status label and buttons
> are toggled appropriately.

[This comment fits firmly in the category of "things you are not
expected to know because it is part of the dark magic of Patchwork's db
optimisations", so please don't take it as a criticism of your patch!]

Please include the following change in your next revision of this patch:

diff --git a/patchwork/views/patch.py b/patchwork/views/patch.py
index ac9cb44c8d9b..00b0147ff57c 100644
--- a/patchwork/views/patch.py
+++ b/patchwork/views/patch.py
@@ -109,7 +109,8 @@ def patch_detail(request, project_id, msgid):
 
 comments = patch.comments.all()
 comments = comments.select_related('submitter')
-comments = comments.only('submitter', 'date', 'id', 'content', 'patch')
+comments = comments.only('submitter', 'date', 'id', 'content', 'patch',
+ 'addressed')
 
 if patch.related:
 related_same_project = patch.related.patches.only(

This stops your patch from adding a round trip to the database for every
comment. We restrict the fields of the comments we query to the ones
listed in only(), and you've added a new field we use in the UI so we
need to add that to the list.

I'd really like to have an intermediate spinner state that is resolved
when we get the response back from the server. It's not a dealbreaker,
but if it's not overly complex to add it would be good to have.

I'll continue my review of this patch tomorrow.

Kind regards,
Daniel

>
> [1] https://imgur.com/NDfcPJy
> [2] https://imgur.com/kIhohED
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/css/style.css  | 53 +-
>  htdocs/js/submission.js   | 15 
>  patchwork/templates/patchwork/submission.html | 69 ++-
>  patchwork/views/patch.py  |  1 +
>  4 files changed, 118 insertions(+), 20 deletions(-)
>
> diff --git a/htdocs/css/style.css b/htdocs/css/style.css
> index 46a91ee..ba83de4 100644
> --- a/htdocs/css/style.css
> +++ b/htdocs/css/style.css
> @@ -1,3 +1,9 @@
> +:root {
> +--light-color: #F7F7F7;
> +--warning-color: #f0ad4e;
> +--success-color: #5cb85c;
> +}
> +
>  h2 {
>  font-size: 25px;
>  margin: 18px 0 18px 0;
> @@ -21,6 +27,10 @@ pre {
>  top: 17em;
>  }
>  
> +.hidden {
> +visibility: hidden;
> +}
> +
>  /* Bootstrap overrides */
>  
>  .navbar-inverse .navbar-brand > a {
> @@ -277,12 +287,18 @@ table.patch-meta tr th, table.patch-meta tr td {
>  color: #f7977a;
>  }
>  
> -.comment .meta {
> +.patch-message .meta {
> +display: flex;
> +align-items: center;
>  background: #f0f0f0;
>  padding: 0.3em 0.5em;
>  }
>  
> -.comment .content {
> +.patch-message .message-date {
> +margin-left: 8px;
> +}
> +
> +.patch-message .content {
>  border: 0;
>  }
>  
> @@ -294,6 +310,39 @@ table.patch-meta tr th, table.patch-meta tr td {
>  font-family: "DejaVu Sans Mono", fixed;
>  }
>  
> +div[class^="comment-status-bar-"] {
> +display: flex;
> +flex-wrap: wrap;
> +align-items: center;
> +}
> +
> +.comment-status-label {
> +margin: 0px 8px;
> +}
> +
> +button[class^=comment-action] {
> +background-color: var(--light-color);
> +border-radius: 4px;
> +}
> +
> +.comment-action-addressed {
> +border-color: var(--success-color);
> +}
> +
> +.comment-action-unaddressed {
> +border-color: var(--warning-color);
> +}
> +
> +.comment-action-addressed:hover {
> +background-color: var(--success-color);
> +color: var(--light-color);
> +}
> +
> +.comment-action-unaddressed:hover {
> +background-color: var(--warning-color);
> +color: var(--light-color);
> +}
> +
>  .quote {
>  color: #007f00;
>  }
> diff --git a/htdocs/js/submission.js b/htdocs/js/submission.js
> index 9676f34..27e4a02 100644
> --- a/htdocs/js/submission.js
> +++ b/htdocs/js/submission.js
> @@ -1,3 +1,5 @@
> +import { updateProperty } from "./rest.js";
> +
>  $( document ).ready(function() {
>  function toggleDiv(link_id, headers_id, label_show, label_hide) {
>  const link = 

Re: [PATCH v3 08/10] api: add patch comments detail endpoint and respective tests

2021-08-16 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Add new endpoint for patch comments at api/.../comments/.
> The endpoint will make it possible to use the REST API to update the new
> `addressed` field for individual patch comments with JavaScript on the
> client side. In the process of these changes, clean up use of the
> CurrentPatchDefault context so that it exists in base.py and can be used
> throughout the API (e.g. Check and Comment REST endpoints).

I was poking around the API to check DB load.

I wondered how comments are numbered - comment ID refers, I discovered,
to the ID of the comment in the DB, it's not the n'th comment on that
patch.

That's fine, but if I go to a URL of an invalid comment I get the
following splat - the key parts of which I've highlighted at the end.

Traceback (most recent call last):
  File "/home/patchwork/patchwork/patchwork/api/comment.py", line 101, in 
get_object
obj = queryset.get(id=int(comment_id))
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/db/models/query.py",
 line 429, in get
raise self.model.DoesNotExist(

During handling of the above exception (PatchComment matching query does not 
exist.), another exception occurred:
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/core/handlers/exception.py",
 line 47, in inner
response = get_response(request)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/core/handlers/base.py",
 line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/views/decorators/csrf.py",
 line 54, in wrapped_view
return view_func(*args, **kwargs)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/views/generic/base.py",
 line 70, in view
return self.dispatch(request, *args, **kwargs)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/rest_framework/views.py",
 line 509, in dispatch
response = self.handle_exception(exc)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/rest_framework/views.py",
 line 469, in handle_exception
self.raise_uncaught_exception(exc)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/rest_framework/views.py",
 line 480, in raise_uncaught_exception
raise exc
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/rest_framework/views.py",
 line 506, in dispatch
response = handler(request, *args, **kwargs)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/rest_framework/generics.py",
 line 252, in get
return self.retrieve(request, *args, **kwargs)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/rest_framework/mixins.py",
 line 54, in retrieve
instance = self.get_object()
  File "/home/patchwork/patchwork/patchwork/api/comment.py", line 103, in 
get_object
obj = get_object_or_404(queryset, linkname=comment_id)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/shortcuts.py", 
line 76, in get_object_or_404
return queryset.get(*args, **kwargs)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/db/models/query.py",
 line 418, in get
clone = self._chain() if self.query.combinator else self.filter(*args, 
**kwargs)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/db/models/query.py",
 line 942, in filter
return self._filter_or_exclude(False, *args, **kwargs)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/db/models/query.py",
 line 962, in _filter_or_exclude
clone._filter_or_exclude_inplace(negate, *args, **kwargs)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/db/models/query.py",
 line 969, in _filter_or_exclude_inplace
self._query.add_q(Q(*args, **kwargs))
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/db/models/sql/query.py",
 line 1358, in add_q
clause, _ = self._add_q(q_object, self.used_aliases)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/db/models/sql/query.py",
 line 1377, in _add_q
child_clause, needed_inner = self.build_filter(
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/db/models/sql/query.py",
 line 1258, in build_filter
lookups, parts, reffed_expression = self.solve_lookup_type(arg)
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/db/models/sql/query.py",
 line 1084, in solve_lookup_type
_, field, _, lookup_parts = self.names_to_path(lookup_splitted, 
self.get_meta())
  File 
"/opt/pyenv/versions/3.9.5/lib/python3.9/site-packages/django/db/models/sql/query.py",
 line 1481, in names_to_path
raise FieldError("Cannot resolve keyword '%s' into field. "

Exception Type: FieldError at /api/patches/2/comments/1/
Exception Value: Cannot resolve keyword 'linkname' into field. Choices are: 
addressed, content, date, headers, id, msgid, patch, patch_id, submitter, 
submitter_id

It looks like 

Re: [PATCH v2 0/5] patch-detail: add unaddressed/addressed status to patch comments

2021-08-10 Thread Daniel Axtens
Daniel Axtens  writes:

> Hi Raxel,
>
> Some general comments:
>
>> Currently, there is no state or status associated with patch comments. 
>> In particular, knowing whether a comment on a patch is addressed is 
>> useful for transparency and accountability in the review and 
>> contribution process. This series adds labels to comments to show 
>> whether they are “Addressed” or “Unaddressed”. Also, the addressed 
>> status of a comment can be manually changed given the required 
>> permissions to edit a patch. A future feature that would be useful to 
>> implement with this new feature is the ability to automatically add 
>> unaddressed comments to a user’s TODO page. 
>
> So:
>
>  - I wonder if a comment that adds a tag like "Reviewed-by:
><>"/Acked-by/Tested-by should automatically be labelled as
>"Addressed" as these comments usually don't require a response from
>the patch submitter. I haven't got strong feelings on this, I just
>explored your series using some test data with a comment that was
>just a "Reviewed-by" and it seemed odd to me. The more I think about
>it the more I think if you're using pw as a code review platform you
>probably don't mind just pressing "addressed" on these sorts of
>comments? OTOH maybe if someone ever wants to build API tooling
>around it (e.g. auto-merge a patch if it has an appropriate
>Reviewed-by and all comments are addressed) then maybe they'd care a
>bit more?

On further thinking, if you haven't already done it, don't worry about
this.

>
>  - I haven't checked, but what happens if (somehow) a state change
>fails? It looks like the buttons flip from addressed <-> unaddressed
>without a pending state --- what happens if something goes wrong?
>(like a dropped internet connection) Is that reflected in the UI?

I'm still keen to see this addressed.

>  - One thing we've repeatedly screwed up in the past is accidentally
>creating massive db load through the API. django-debug-toolbar is
>quite helpful for spotting db query pattern changes but it's a bit
>fiddly to get set up in Docker. I haven't looked to see what query
>changes have been created yet, but I just wanted to flag that as
>something you should check when making API changes.
>

I will have a look at this myself when you send the next version, I know
you're not primarily a backend person.

> I haven't reviewed all the patches in detail yet - having a split
> between refactoring and user-visible change would help - but looking at
> the final product I think this is going in a direction I'm happy with.

I'm now done reviewing this version of this series.

> Kind regards,
> Daniel
>
>> Something important to note is that this patch series relies on the 
>> JS cookie library [1] and rest.js file [2], both introduced in my 
>> previous patch series. Also, the patch series was previously a RFC [3]
>> that lacked tests and release notes. Also, the first patch now adds the
>> OpenAPI definition of the new comment detail endpoint and upgrades
>> the REST API to v1.3 accordingly. 
>>

Please include those two patches in the series when you send the next
revision. That will make it a bit easier for me when I go to apply the
series. If you can also include a link to the JS cookie library in the
commit message that'd help because it gets corrupted at some point
between you and me and so I have to redownload the file locally.

Kind regards,
Daniel

>> For the first patch, the addressed field is added to the data model and 
>> a new endpoint for the REST API to work with a specific comment is added
>> as well. The endpoint is upgraded to v1.3 and defined for OpenAPI. 
>>
>> For the second patch, tests are added for the new endpoint. Also, the
>> addressed field is added to create_patch_comment in the api tests
>> utils.py. 
>>
>> For the third patch, the addressed status label and button to change the 
>> addressed status are added with styling.
>>
>> For the fourth patch, the REST API call is added when the buttons are 
>> clicked to change the addressed status of a comment. Also, the UI is set
>> to update accordingly. 
>>
>> For the fifth patch, release notes are added for these changes.
>>
>> [1] https://lists.ozlabs.org/pipermail/patchwork/2021-July/006994.html
>> [2] https://lists.ozlabs.org/pipermail/patchwork/2021-July/006997.html
>> [3] https://lists.ozlabs.org/pipermail/patchwork/2021-July/006974.html
>>
>> Raxel Gutierrez (5):
>>   api: add addressed field and detail endpoint for patch comments
>>   tests: add tests for patch comments 

Re: [PATCH 5/5] docs: add release note for addressed/unaddressed comments

2021-08-10 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Signed-off-by: Raxel Gutierrez 
> ---
>  ...essed-patch-comments-bfe71689b6f35a22.yaml | 20 +++
>  1 file changed, 20 insertions(+)
>  create mode 100644 
> releasenotes/notes/comment-detail-endpoint-for-addressed-unaddressed-patch-comments-bfe71689b6f35a22.yaml
>
> diff --git 
> a/releasenotes/notes/comment-detail-endpoint-for-addressed-unaddressed-patch-comments-bfe71689b6f35a22.yaml
>  
> b/releasenotes/notes/comment-detail-endpoint-for-addressed-unaddressed-patch-comments-bfe71689b6f35a22.yaml
> new file mode 100644
> index 000..1b11d01
> --- /dev/null
> +++ 
> b/releasenotes/notes/comment-detail-endpoint-for-addressed-unaddressed-patch-comments-bfe71689b6f35a22.yaml
> @@ -0,0 +1,20 @@
> +---
> +prelude: >
> +Comments on a patch now have an addressed/unaddressed state visible as a
> +label on each comment's header. Also, the patch comment header includes a
> +button to change the ``addressed`` state of each respective comment. Only
> +users with edit permissions can see the button and make these changes. To
> +make updates to the ``addressed`` field of the ``PatchComment`` model, a
> +new REST API endpoint ``/api/patches//comments//`` 
> is
> +added to retrieve and update individual patch comments.

Looking at existing release notes, we only use 'prelude' for release
notes specifically announcing new
versions. (e.g. releasenotes/notes/prelude-3_0-5a4905b9df203595.yaml)

A couple of changes:

 - please move any content you want preserved from prelude to another
   type of note.

 - as these are user-visible, it would be good if you could explain the
   changes without referring to the model names inside of Patchwork.

> +features:
> +  - |
> +Patch comments now have "Addressed" and "Unaddressed" labels in the patch
> +detail page. Users that are project maintainers, patch delegates, patch
> +submitter, or comment submitter can change the ``addressed`` state of 
> each
> +comment on a patch with a button next to each label.
> +api:
> +  - |
> +The API version has been updated to v1.3.
> +  - |
> +New REST API endpoint at 
> ``/api/patches//comments//``.
> -- 
> 2.32.0.554.ge1b32706d8-goog
>
> ___
> 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


Re: [PATCH v2 3/5] patch-detail: add label and button for comment addressed status

2021-08-10 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Add new label to show the status of whether a comment is addressed and a
> button to change the status of the comment. Only users that can edit
> the patch (submitter, delegate, project maintainers) can change the
> status of a comment. Clean up submission.html to have hyphen delimited
> id and class selector names as well. Before [1] and after [2] images
> for reference.
>
So another thing you do in this patch is change the show related and
show headers from links to buttons.

Please could you split this up. I think there are 3 things going on,
each of which should be in its own patch:

 - rename things/refactor
 - change show related/show header links to buttons
 - add UI elements for comment addressed status

But if there's a different, more logical or simpler split feel free to
do that.

Skimming the diff I don't have many review comments. I'll do a deeper
dive when it's split.

> [1] https://imgur.com/NDfcPJy
> [2] https://imgur.com/kIhohED
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/css/style.css  |  46 +-
>  patchwork/templates/patchwork/submission.html | 146 +++---
>  patchwork/views/patch.py  |   1 +
>  3 files changed, 134 insertions(+), 59 deletions(-)
>
> diff --git a/htdocs/css/style.css b/htdocs/css/style.css
> index 243caa0..ff34ff5 100644
> --- a/htdocs/css/style.css
> +++ b/htdocs/css/style.css
> @@ -1,3 +1,9 @@
> +:root {
> +--light-color: #F7F7F7;
> +--warning-color: #f0ad4e;
> +--success-color: #5cb85c;
> +}
> +
>  h2 {
>  font-size: 25px;
>  margin: 18px 0 18px 0;
> @@ -192,7 +198,7 @@ table.patchmeta tr th, table.patchmeta tr td {
>  vertical-align: top;
>  }
>  
> -.submissionlist ul {
> +.submission-list ul {
>  list-style-type: none;
>  padding: 0;
>  margin: 0;
> @@ -277,12 +283,18 @@ table.patchmeta tr th, table.patchmeta tr td {
>  color: #f7977a;
>  }
>  
> -.comment .meta {
> +.patch-message .meta {
> +display: flex;
> +align-items: center;
>  background: #f0f0f0;
>  padding: 0.3em 0.5em;
>  }
>  
> -.comment .content {
> +.patch-message .message-date {
> +margin-left: 8px;
> +}
> +
> +.patch-message .content {
>  border: 0;
>  }
>  
> @@ -294,6 +306,34 @@ table.patchmeta tr th, table.patchmeta tr td {
>  font-family: "DejaVu Sans Mono", fixed;
>  }
>  
> +#comment-status-bar {
> +display: flex;
> +flex-wrap: wrap;
> +align-items: center;
> +}
> +
> +#comment-status-label {
> +margin: 0px 8px;
> +}
> +
> +#comment-action-addressed, #comment-action-unaddressed {
> +background-color: var(--light-color);
> +border-radius: 4px;
> +}
> +
> +#comment-action-addressed {
> +border-color: var(--success-color);
> +}
> +
> +#comment-action-unaddressed {
> +border-color: var(--warning-color);
> +}
> +
> +#comment-action-addressed:hover, #comment-action-unaddressed:hover {
> +background-color: var(--success-color);
> +color: var(--light-color);
> +}
> +
>  .quote {
>  color: #007f00;
>  }
> diff --git a/patchwork/templates/patchwork/submission.html 
> b/patchwork/templates/patchwork/submission.html
> index 978559b..4109442 100644
> --- a/patchwork/templates/patchwork/submission.html
> +++ b/patchwork/templates/patchwork/submission.html
> @@ -1,3 +1,4 @@
> +
You probably don't need the extra newline here?

>  {% extends "base.html" %}
>  
>  {% load humanize %}
> @@ -32,7 +33,7 @@ function toggle_div(link_id, headers_id, label_show, 
> label_hide)
>{{ submission.name }}
>  
>  
> -
> +
>   
>Message ID
>{% if submission.list_archive_url %}
> @@ -61,12 +62,14 @@ function toggle_div(link_id, headers_id, label_show, 
> label_hide)
>  {% endif %}
>   
>Headers
> -   -   href="javascript:toggle_div('togglepatchheaders', 'patchheaders')"
> -   >show
> -   
> -{{submission.headers}}
> -   
> +  
> + +  id="toggle-patch-headers"
> +  onclick="javascript:toggle_div('toggle-patch-headers', 
> 'patch-headers')"
> +  >show
> +
> +  {{submission.headers}}
> +
>
>   
>  {% if submission.series %}
> @@ -75,11 +78,12 @@ function toggle_div(link_id, headers_id, label_show, 
> label_hide)
>
> 
>  {{ submission.series.name }}
> -|
> --  href="javascript:toggle_div('togglepatchseries', 'patchseries', 
> 'expand', 'collapse')"
> -   >expand
> -   
> +   
> ++id="toggle-patch-series"
> +onclick="javascript:toggle_div('toggle-patch-series', 'patch-series', 
> 'expand', 'collapse')"
> +>expand
> +   
>  
>   {% with submission.series.cover_letter as cover %}
>
> @@ -114,36 +118,38 @@ function toggle_div(link_id, headers_id, label_show, 
> label_hide)
>   
>Related
>
> --  href="javascript:toggle_div('togglerelated', 'related')"
> -   >show
> -   
> -
> - {% for sibling in related_same_project %}
> -  
> -   {% if sibling.id != submission.id %}
> -
> - {{ 

Re: [PATCH v2 2/5] tests: add tests for patch comments detail endpoint

2021-08-10 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Add tests for the new api/.../comments/ endpoint that takes
> GET, PATCH, and PUT requests. The tests cover retrieval and update
> requests and handle calls from the various API versions. Also, they
> handle permissions for update requests on the new `addressed` field and
> invalid update values for the `addressed` field.
>
> Add `addressed` field to create_patch_comment helper in api tests
> utils.py.
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  patchwork/tests/api/test_comment.py | 199 +---
>  patchwork/tests/utils.py|   1 +
>  2 files changed, 183 insertions(+), 17 deletions(-)
>
> diff --git a/patchwork/tests/api/test_comment.py 
> b/patchwork/tests/api/test_comment.py
> index 59450d8..f43d1c7 100644
> --- a/patchwork/tests/api/test_comment.py
> +++ b/patchwork/tests/api/test_comment.py
> @@ -9,11 +9,16 @@ from django.conf import settings
>  from django.urls import NoReverseMatch
>  from django.urls import reverse
>  
> +from patchwork.models import PatchComment
>  from patchwork.tests.api import utils
>  from patchwork.tests.utils import create_cover
>  from patchwork.tests.utils import create_cover_comment
>  from patchwork.tests.utils import create_patch
>  from patchwork.tests.utils import create_patch_comment
> +from patchwork.tests.utils import create_maintainer
> +from patchwork.tests.utils import create_project
> +from patchwork.tests.utils import create_person
> +from patchwork.tests.utils import create_user
>  from patchwork.tests.utils import SAMPLE_CONTENT
>  
>  if settings.ENABLE_REST_API:
> @@ -86,34 +91,40 @@ class TestCoverComments(utils.APITestCase):
>  @unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API')
>  class TestPatchComments(utils.APITestCase):
>  @staticmethod
> -def api_url(patch, version=None):
> -kwargs = {}
> +def api_url(patch, version=None, item=None):
> +kwargs = {'patch_id': patch.id}
>  if version:
>  kwargs['version'] = version
> -kwargs['patch_id'] = patch.id
> +if item is None:
> +return reverse('api-patch-comment-list', kwargs=kwargs)
> +kwargs['comment_id'] = item.id
> +return reverse('api-patch-comment-detail', kwargs=kwargs)
>  
> -return reverse('api-patch-comment-list', kwargs=kwargs)
> +def setUp(self):
> +super(TestPatchComments, self).setUp()
> +self.project = create_project()
> +self.user = create_maintainer(self.project)
> +self.patch = create_patch(project=self.project)
>  
>  def assertSerialized(self, comment_obj, comment_json):
>  self.assertEqual(comment_obj.id, comment_json['id'])
>  self.assertEqual(comment_obj.submitter.id,
>   comment_json['submitter']['id'])
> +self.assertEqual(comment_obj.addressed, comment_json['addressed'])
>  self.assertIn(SAMPLE_CONTENT, comment_json['content'])
>  
>  def test_list_empty(self):
>  """List patch comments when none are present."""
> -patch = create_patch()
> -resp = self.client.get(self.api_url(patch))
> +resp = self.client.get(self.api_url(self.patch))
>  self.assertEqual(status.HTTP_200_OK, resp.status_code)
>  self.assertEqual(0, len(resp.data))
>  
>  @utils.store_samples('patch-comment-list')
>  def test_list(self):
>  """List patch comments."""
> -patch = create_patch()
> -comment = create_patch_comment(patch=patch)
> +comment = create_patch_comment(patch=self.patch)
>  
> -resp = self.client.get(self.api_url(patch))
> +resp = self.client.get(self.api_url(self.patch))
>  self.assertEqual(status.HTTP_200_OK, resp.status_code)
>  self.assertEqual(1, len(resp.data))
>  self.assertSerialized(comment, resp.data[0])
> @@ -121,26 +132,180 @@ class TestPatchComments(utils.APITestCase):
>  
>  def test_list_version_1_1(self):
>  """List patch comments using API v1.1."""
> -patch = create_patch()
> -comment = create_patch_comment(patch=patch)
> +comment = create_patch_comment(patch=self.patch)
>  
> -resp = self.client.get(self.api_url(patch, version='1.1'))
> +resp = self.client.get(self.api_url(self.patch, version='1.1'))
>  self.assertEqual(status.HTTP_200_OK, resp.status_code)
>  self.assertEqual(1, len(resp.data))
>  self.assertSerialized(comment, resp.data[0])
>  self.assertNotIn('list_archive_url', resp.data[0])
>  
>  def test_list_version_1_0(self):
> -"""List patch comments using API v1.0."""
> -patch = create_patch()
> -create_patch_comment(patch=patch)
> +"""List patch comments using API v1.0.
>  
> -# check we can't access comments using the old version of the API
> +Ensure we can't access comments using the old version of the API.
> +"""
>  

Re: [PATCH v2 0/5] patch-detail: add unaddressed/addressed status to patch comments

2021-08-05 Thread Daniel Axtens
Hi Raxel,

Some general comments:

> Currently, there is no state or status associated with patch comments. 
> In particular, knowing whether a comment on a patch is addressed is 
> useful for transparency and accountability in the review and 
> contribution process. This series adds labels to comments to show 
> whether they are “Addressed” or “Unaddressed”. Also, the addressed 
> status of a comment can be manually changed given the required 
> permissions to edit a patch. A future feature that would be useful to 
> implement with this new feature is the ability to automatically add 
> unaddressed comments to a user’s TODO page. 

So:

 - I wonder if a comment that adds a tag like "Reviewed-by:
   <>"/Acked-by/Tested-by should automatically be labelled as
   "Addressed" as these comments usually don't require a response from
   the patch submitter. I haven't got strong feelings on this, I just
   explored your series using some test data with a comment that was
   just a "Reviewed-by" and it seemed odd to me. The more I think about
   it the more I think if you're using pw as a code review platform you
   probably don't mind just pressing "addressed" on these sorts of
   comments? OTOH maybe if someone ever wants to build API tooling
   around it (e.g. auto-merge a patch if it has an appropriate
   Reviewed-by and all comments are addressed) then maybe they'd care a
   bit more?
   
 - I haven't checked, but what happens if (somehow) a state change
   fails? It looks like the buttons flip from addressed <-> unaddressed
   without a pending state --- what happens if something goes wrong?
   (like a dropped internet connection) Is that reflected in the UI?

 - One thing we've repeatedly screwed up in the past is accidentally
   creating massive db load through the API. django-debug-toolbar is
   quite helpful for spotting db query pattern changes but it's a bit
   fiddly to get set up in Docker. I haven't looked to see what query
   changes have been created yet, but I just wanted to flag that as
   something you should check when making API changes.

I haven't reviewed all the patches in detail yet - having a split
between refactoring and user-visible change would help - but looking at
the final product I think this is going in a direction I'm happy with.

Kind regards,
Daniel

> Something important to note is that this patch series relies on the 
> JS cookie library [1] and rest.js file [2], both introduced in my 
> previous patch series. Also, the patch series was previously a RFC [3]
> that lacked tests and release notes. Also, the first patch now adds the
> OpenAPI definition of the new comment detail endpoint and upgrades
> the REST API to v1.3 accordingly. 
>
> For the first patch, the addressed field is added to the data model and 
> a new endpoint for the REST API to work with a specific comment is added
> as well. The endpoint is upgraded to v1.3 and defined for OpenAPI. 
>
> For the second patch, tests are added for the new endpoint. Also, the
> addressed field is added to create_patch_comment in the api tests
> utils.py. 
>
> For the third patch, the addressed status label and button to change the 
> addressed status are added with styling.
>
> For the fourth patch, the REST API call is added when the buttons are 
> clicked to change the addressed status of a comment. Also, the UI is set
> to update accordingly. 
>
> For the fifth patch, release notes are added for these changes.
>
> [1] https://lists.ozlabs.org/pipermail/patchwork/2021-July/006994.html
> [2] https://lists.ozlabs.org/pipermail/patchwork/2021-July/006997.html
> [3] https://lists.ozlabs.org/pipermail/patchwork/2021-July/006974.html
>
> Raxel Gutierrez (5):
>   api: add addressed field and detail endpoint for patch comments
>   tests: add tests for patch comments detail endpoint
>   patch-detail: add label and button for comment addressed status
>   patch-detail: add functionality for comment status updates
>   docs: add release note for addressed/unaddressed comments
>
>  docs/api/schemas/generate-schemas.py  |4 +-
>  docs/api/schemas/latest/patchwork.yaml|  144 +-
>  docs/api/schemas/patchwork.j2 |  148 +-
>  docs/api/schemas/v1.0/patchwork.yaml  |   35 +-
>  docs/api/schemas/v1.1/patchwork.yaml  |   35 +-
>  docs/api/schemas/v1.2/patchwork.yaml  |   35 +-
>  docs/api/schemas/v1.3/patchwork.yaml  | 2749 +
>  htdocs/css/style.css  |   55 +-
>  htdocs/js/submission.js   |   52 +
>  patchwork/api/base.py |   24 +-
>  patchwork/api/check.py|   21 +-
>  patchwork/api/comment.py  |   76 +-
>  patchwork/api/patch.py|2 +-
>  .../migrations/0045_patchcomment_addressed.py |   18 +
>  patchwork/models.py   |5 +-
>  patchwork/templates/patchwork/submission.html |  165 +-
>  

Re: [PATCH v3 4/5] static: add rest.js to handle requests & respective messages

2021-08-05 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Add js file to have REST API requests utilities JavaScript module to be
> reused by other Patchwork js files making updates to objects.
>
>  - Add function to make fetch requests that update properties through
>'PATCH' requests with the REST API endpoints.
>
>  - Add functions that handle update & error messages for these 'PATCH'
>update requests following the Django messages framework format and
>form error styling.
>
> The subsequent patch will make use of these functions which will be also
> reused in future features the make use fetch requests to update object
> fields.

Excellent. Using the rest API from the web interface will help drag us
into only being several years behind the modern web rather than over a
decade :P

>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/README.rst |  7 +
>  htdocs/js/rest.js | 72 +++
>  2 files changed, 79 insertions(+)
>  create mode 100644 htdocs/js/rest.js
>
> diff --git a/htdocs/README.rst b/htdocs/README.rst
> index 404356a..2e76013 100644
> --- a/htdocs/README.rst
> +++ b/htdocs/README.rst
> @@ -138,6 +138,13 @@ js
>  
>Part of Patchwork.
>  
> +``rest.js.``
> +
> +  Utility module for REST API requests to be used by other Patchwork js files
> +  (fetch requests, handling update & error messages).
> +
> +  Part of Patchwork.
> +
>  ``selectize.min.js``
>  
>Selectize is the hybrid of a ``textbox`` and  box. It's jQuery
> diff --git a/htdocs/js/rest.js b/htdocs/js/rest.js
> new file mode 100644
> index 000..18c0295
> --- /dev/null
> +++ b/htdocs/js/rest.js
> @@ -0,0 +1,72 @@
> +/**
> + * Sends fetch requests to update objects' properties using REST api 
> endpoints.
> + * @param {string} url Path to the REST api endpoint.
> + * @param {{field: string, value: string}} data
> + * field: Name of the property field to update.
> + * value: Value to update the property field to.
> + * @param {{none: string, some: string}} updateMessage
> + * none: Message when object update unsuccessful due to errors.
> + * some: Message when object update successful.
> + */
> +async function updateProperty(url, data, updateMessage) {
> +const request = new Request(url, {
> +method: 'PATCH',
> +mode: "same-origin",
> +headers: {
> +"X-CSRFToken": Cookies.get("csrftoken"),
> +"Content-Type": "application/json",
> +},
> +body: JSON.stringify(data),
> +});
> +
> +return await fetch(request)
> +.then(response => {
> +if (!response.ok) {
> +response.text().then(text => {
> +// Error occurred, so update message specifies no 
> objects updated
> +handleUpdateMessages(updateMessage.none);
> +handleErrorMessages(JSON.parse(text).detail);
> +});
> +} else {
> +// Update message for successful changes
> +handleUpdateMessages(updateMessage.some);
> +}
> +return response.ok
> +});
> +}
> +
> +/**
> + * Populates update messages for fetch requests.
> + * @param {string} messageContent Text for update message.
> + */
> +function handleUpdateMessages(messageContent) {
> +let messages = document.getElementById("messages");
> +if (messages == null) {
> +messages = document.createElement("div");
> +messages.setAttribute("id", "messages");
> +}
> +let message = document.createElement("div");
> +message.setAttribute("class", "message");
> +message.textContent = messageContent;
> +messages.appendChild(message);
> +if (messages) $(messages).insertAfter("nav");
> +}
> +
> +/**
> + * Populates error messages for fetch requests.
> + * @param {string} errorMessage Text for error message.
> + */
> +function handleErrorMessages(errorMessage) {
> +let container = document.getElementById("main-content");
> +let errorHeader = document.createElement("p");
> +let errorList = document.createElement("ul");
> +let error = document.createElement("li");
> +errorHeader.textContent = "The following error was encountered while 
> updating comments:";
> +errorList.setAttribute("class", "errorlist");
> +error.textContent = errorMessage;
> +errorList.appendChild(error);
> +container.prepend(errorList);
> +container.prepend(errorHeader);
> +}
> +
> +export { updateProperty, handleUpdateMessages, handleUpdateMessages};

Chrome doesn't like the double-up of handleUpdateMessages here, it
throws an error on the console.

I haven't reviewed the rest of the code and I'm not sure I'm super
qualified to do so. I guess I can give it a go though at some point.

One thing to consider: currently the messages never time out. So if I
mark 3 comments as addressed/unaddressed (using the example from the
other series) I end up with a top message bar that reads:

1 comment updated
1 

Re: [PATCH v2 4/5] patch-detail: add functionality for comment status updates

2021-08-05 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Use new comment detail REST API endpoint to update the addressed field
> value when "Addressed" or "Unaddressed" buttons are clicked. After, the
> request is made, the appearance of the comment status label and buttons
> are toggled appropriately.

As a general note, if you're able to split the code movement parts out
from the new code parts, I am happy to merge the refactoring earlier.
That will also make it easier for me to do reviews.

Overall this patch (and by extension the series) seems to work in a
reasonable way.

I go back and forth on whether this needs to sit behind
a project gate flag; on one hand it's possibly confusing for a submitter
to have state tracked here that the maintainer doesn't care about. On
the other hand, it isn't a massive UI change and maybe there's no real
harm in leaving a feature around that people don't use. Let's stick with
having it on everywhere for now.

> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/css/style.css  | 15 -
>  htdocs/js/submission.js   | 52 
>  patchwork/templates/patchwork/submission.html | 61 ++-
>  templates/base.html   |  2 +-
>  4 files changed, 85 insertions(+), 45 deletions(-)
>  create mode 100644 htdocs/js/submission.js
>
> diff --git a/htdocs/css/style.css b/htdocs/css/style.css
> index ff34ff5..2a5c81f 100644
> --- a/htdocs/css/style.css
> +++ b/htdocs/css/style.css
> @@ -27,6 +27,10 @@ pre {
>  top: 17em;
>  }
>  
> +.hidden {
> +visibility: hidden;
> +}
> +
>  /* Bootstrap overrides */
>  
>  .navbar-inverse .navbar-brand > a {
> @@ -306,7 +310,7 @@ table.patchmeta tr th, table.patchmeta tr td {
>  font-family: "DejaVu Sans Mono", fixed;
>  }
>  
> -#comment-status-bar {
> +div[id^="comment-status-bar-"] {
>  display: flex;
>  flex-wrap: wrap;
>  align-items: center;
> @@ -316,7 +320,7 @@ table.patchmeta tr th, table.patchmeta tr td {
>  margin: 0px 8px;
>  }
>  
> -#comment-action-addressed, #comment-action-unaddressed {
> +button[id^=comment-action] {
>  background-color: var(--light-color);
>  border-radius: 4px;
>  }
> @@ -329,11 +333,16 @@ table.patchmeta tr th, table.patchmeta tr td {
>  border-color: var(--warning-color);
>  }
>  
> -#comment-action-addressed:hover, #comment-action-unaddressed:hover {
> +#comment-action-addressed:hover {
>  background-color: var(--success-color);
>  color: var(--light-color);
>  }
>  
> +#comment-action-unaddressed:hover {
> +background-color: var(--warning-color);
> +color: var(--light-color);
> +}
> +
>  .quote {
>  color: #007f00;
>  }
> diff --git a/htdocs/js/submission.js b/htdocs/js/submission.js
> new file mode 100644
> index 000..8894890
> --- /dev/null
> +++ b/htdocs/js/submission.js
> @@ -0,0 +1,52 @@
> +import { updateProperty } from "./rest.js";
> +
> +$( document ).ready(function() {
> +function toggleDiv(link_id, headers_id, label_show, label_hide) {
> +const link = document.getElementById(link_id)
> +const headers = document.getElementById(headers_id)
> +
> +const hidden = headers.style['display'] == 'none';
> +
> +if (hidden) {
> +link.innerHTML = label_hide || 'hide';
> +headers.style['display'] = 'block';
> +} else {
> +link.innerHTML = label_show || 'show';
> +headers.style['display'] = 'none';
> +}
> +}
> +
> +$("button[id^='comment-action']").click((event) => {
> +const patchId = document.getElementById("patch").dataset.patchId;
> +const commentId = event.target.parentElement.dataset.commentId;
> +const url = "/api/patches/" + patchId + "/comments/" + commentId + 
> "/";
> +const data = {'addressed': event.target.value} ;
> +const updateMessage = {
> +'none': "No comments updated",
> +'some': "1 comment updated",
> +};
> +updateProperty(url, data, updateMessage);
> +
> $("div[id^='comment-status-bar-'][data-comment-id='"+commentId+"']").toggleClass("hidden");
> +});
> +
> +// Click listener to show/hide headers
> +
> document.getElementById("toggle-patch-headers").addEventListener("click", 
> function() {
> +toggleDiv("toggle-patch-headers", "patch-headers");
> +});
> +
> +// Click listener to expand/collapse series
> +document.getElementById("toggle-patch-series").addEventListener("click", 
> function() {
> +toggleDiv("toggle-patch-series", "patch-series", "expand", 
> "collapse");
> +});
> +
> +// Click listener to show/hide related patches
> +document.getElementById("toggle-related").addEventListener("click", 
> function() {
> +toggleDiv("toggle-related", "related");
> +});
> +
> +// Click listener to show/hide related patches from different projects
> +
> 

Re: [PATCH 2/3] xmlrpc: Allow a project to restrict submitter state changes

2021-08-05 Thread Daniel Axtens
Stephen Finucane  writes:

> On Tue, 2021-08-03 at 01:27 +1000, Daniel Axtens wrote:
>> As with the UI.
>> 
>> Signed-off-by: Daniel Axtens 
>> ---
>>  patchwork/tests/test_xmlrpc.py | 90 ++
>>  patchwork/views/xmlrpc.py  |  4 ++
>>  2 files changed, 94 insertions(+)
>> 
>> diff --git a/patchwork/tests/test_xmlrpc.py b/patchwork/tests/test_xmlrpc.py
>> index 4726fdffa5d5..eea0b4eaf560 100644
>> --- a/patchwork/tests/test_xmlrpc.py
>> +++ b/patchwork/tests/test_xmlrpc.py
>> @@ -10,6 +10,9 @@ from django.conf import settings
>>  from django.test import LiveServerTestCase
>>  from django.urls import reverse
>>  
>> +from patchwork.models import Person
>> +from patchwork.models import Project
>> +from patchwork.models import State
>>  from patchwork.tests import utils
>>  
>>  
>> @@ -81,6 +84,93 @@ class XMLRPCAuthenticatedTest(LiveServerTestCase):
>>  self.assertTrue(result['archived'])
>>  
>>  
>> +@unittest.skipUnless(settings.ENABLE_XMLRPC,
>> + 'requires xmlrpc interface (use the ENABLE_XMLRPC '
>> + 'setting)')
>> +class XMLRPCStateSettingTest(LiveServerTestCase):
>> +
>> +def url_for_user(self, user):
>> +return ('http://%s:%s@' + self.url[7:]) % \
>> +   (user.username, user.username)
>> +
>> +def setUp(self):
>> +self.url = self.live_server_url + reverse('xmlrpc')
>> +# url is of the form http://localhost:PORT/PATH
>> +# strip the http and replace it with the username/passwd of a user.
>> +self.projects = {}
>> +self.maintainers = {}
>> +self.delegates = {}
>> +self.submitters = {}
>> +self.patches = {}
>> +self.rpcs = {}
>> +
>> +for project_type in (Project.SUBMITTER_NO_STATE_CHANGES,
>> + Project.SUBMITTER_ALL_STATE_CHANGES):
>> +project = utils.create_project(
>> +submitter_state_change_rules=project_type)
>> +self.projects[project_type] = project
>> +self.maintainers[project_type] = 
>> utils.create_maintainer(project)
>> +submitter = utils.create_user(project)
>> +self.submitters[project_type] = submitter
>> +delegate = utils.create_user(project)
>> +self.delegates[project_type] = delegate
>> +
>> +self.rpcs[project_type] = {
>> +'maintainer': ServerProxy(self.url_for_user(
>> +self.maintainers[project_type])),
>> +'delegate': ServerProxy(self.url_for_user(delegate)),
>> +'submitter': ServerProxy(self.url_for_user(submitter)),
>> +}
>> +
>> +patch = utils.create_patch(project=project,
>> +   submitter=Person.objects.get(
>> +   user=submitter),
>> +   delegate=delegate)
>> +self.patches[project_type] = patch
>> +
>> +utils.create_state(name="New")
>> +utils.create_state(name="RFC")
>> +
>> +def tearDown(self):
>> +for project_type in self.rpcs:
>> +rpc_dict = self.rpcs[project_type]
>> +for user in rpc_dict:
>> +rpc_dict[user].close()
>> +
>> +def can_set_state(self, patch, rpc):
>> +new_state = State.objects.get(name="New")
>> +rfc_state = State.objects.get(name="RFC")
>> +patch.state = new_state
>> +patch.save()
>> +
>> +result = rpc.patch_get(patch.id)
>> +self.assertEqual(result['state_id'], new_state.id)
>> +
>> +try:
>> +rpc.patch_set(patch.id, {'state': rfc_state.id})
>> +except xmlrpc_client.Fault:
>> +return False
>> +
>> +# reload the patch
>> +result = rpc.patch_get(patch.id)
>> +self.assertEqual(result['state_id'], rfc_state.id)
>> +return True
>> +
>> +def test_allset(self):
>> +rpc_dict = self.rpcs[Project.SUBMITTER_ALL_STATE_CHANGES]
>> +patch = self.patches[Project.SUBMITTER_ALL_STATE_CHANGES]
>> +self.assertTrue(self.can_set_state(patch, rpc_dict['maintainer']))
>> +self.assertTrue(self.can_set_state(patch, rpc_dict['delegate']))
>> +self.as

Re: [PATCH 1/3] Allow a project to restrict submitter state changes

2021-08-05 Thread Daniel Axtens
Stephen Finucane  writes:

> On Tue, 2021-08-03 at 01:27 +1000, Daniel Axtens wrote:
>> In discussions about how to make patchwork more user-friendly and
>> suitable for more projects, we realised that perhaps the current
>> ability for submitters to change their patch state to any value
>> isn't the most appropriate setting for all maintainers, especially
>> in light of increasing automation.
>> 
>> Allow a project to stop a submitter from changing the state of
>> their patches. This is not the default but can be set by a patchwork
>> administrator.
>
> Couple of comments below. Unfortunately two of them are of the "I don't know
> about this so can you investigate?" variety. I can help resolve them if
> necessary but I'm hoping you've already done said investigation :-)
>
> Stephen
>
>> Signed-off-by: Daniel Axtens 
>> ---
>>  ...45_project_submitter_state_change_rules.py | 24 +
>>  patchwork/models.py   | 36 +++
>>  patchwork/views/__init__.py   |  8 +
>>  patchwork/views/patch.py  | 14 ++--
>>  4 files changed, 80 insertions(+), 2 deletions(-)
>>  create mode 100644 
>> patchwork/migrations/0045_project_submitter_state_change_rules.py
>> 
>> diff --git 
>> a/patchwork/migrations/0045_project_submitter_state_change_rules.py 
>> b/patchwork/migrations/0045_project_submitter_state_change_rules.py
>> new file mode 100644
>> index ..9d0b2892bd5c
>> --- /dev/null
>> +++ b/patchwork/migrations/0045_project_submitter_state_change_rules.py
>> @@ -0,0 +1,24 @@
>> +# Generated by Django 3.1.12 on 2021-08-03 00:32
>> +
>> +from django.db import migrations, models
>> +
>> +
>> +class Migration(migrations.Migration):
>> +
>> +dependencies = [
>> +('patchwork', '0044_add_project_linkname_validation'),
>> +]
>> +
>> +operations = [
>> +migrations.AddField(
>> +model_name='project',
>> +name='submitter_state_change_rules',
>> +field=models.SmallIntegerField(
>> +choices=[
>> +(0, 'Submitters may not change patch states'),
>> +(1, 'Submitters may set any patch state')],
>> +default=1,
>> +help_text='What state changes can patch submitters make?'
>> +  ' Does not affect maintainers.'),
>> +),
>
> This feels like a BooleanField rather than a SmallIntegerField (even if they
> resolve to the same thing on e.g. MySQL 5.x, iirc). afaict, you can pass the
> 'choices' argument for BooleanField too [1]. Any chance we could change this?
>
> [1] https://code.djangoproject.com/ticket/9640

I picked this because I want to add another mode that permits submitters
to change states but restricts the states that submitters can change
between (so for example allowing them to move around the {New, RFC,
Superseded, Not Applicable, Changes Requested} group but not to mark
their own patches as Accepted). And I don't want to do a migration then
if I can avoid it :)

>
>> +]
>> diff --git a/patchwork/models.py b/patchwork/models.py
>> index 00273da9f5bd..706b912c349a 100644
>> --- a/patchwork/models.py
>> +++ b/patchwork/models.py
>> @@ -93,6 +93,19 @@ class Project(models.Model):
>>  send_notifications = models.BooleanField(default=False)
>>  use_tags = models.BooleanField(default=True)
>>  
>> +# how much can a patch submitter change?
>> +SUBMITTER_NO_STATE_CHANGES = 0
>> +SUBMITTER_ALL_STATE_CHANGES = 1
>> +SUBMITTER_STATE_CHANGE_CHOICES = (
>> +(SUBMITTER_NO_STATE_CHANGES, 'Submitters may not change patch 
>> states'),
>> +(SUBMITTER_ALL_STATE_CHANGES, 'Submitters may set any patch state'),
>> +)
>> +submitter_state_change_rules = models.SmallIntegerField(
>> +choices=SUBMITTER_STATE_CHANGE_CHOICES,
>> +default=SUBMITTER_ALL_STATE_CHANGES,
>> +help_text='What state changes can patch submitters make?'
>> +  ' Does not affect maintainers.')
>
> Ditto.
>
>> +
>>  def is_editable(self, user):
>>  if not user.is_authenticated:
>>  return False
>> @@ -518,6 +531,29 @@ class Patch(SubmissionMixin):
>>  return True
>>  return False
>>  
>> +def can_set_state(self, user):
>> +# an unauthenticated user can never change state
>> +if not user.is_authenticat

Re: Browser compatibility and IE support for JavaScript functions

2021-08-05 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Hi,
>
> I want to use this fairly new JS function `Element.replaceChildren()` [1]
> to replace error/update messages. Looking at browser stats, it seems pretty
> compatible but it doesn't work with IE. How should browser compatibility be
> approached?

>From caniuse.com it looks like by IE they mean the old legacy IE which
is now out of support - it looks like Edge supports this. So I would be
happy to use replaceChildren.

In general, while I want to avoid needlessly breaking compat, I'm happy
if things work in Firefox and Chrome.

Kind regards,
Daniel

>
> Also, https://caniuse.com/?search=replaceChildren.
>
> [1] https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceChildren
>
> [2] https://www.stetic.com/market-share/browser/
> ___
> 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


Re: [PATCH v3 0/5] patch-list: improve usability of list action bar

2021-08-02 Thread Daniel Axtens
Hi Raxel,

A few bugs:

 - if you submit a change with the dropdowns that errors out, and then
   subsequently submit one that succeeds, the error messages are not
   cleared.

 - You've somehow ended up with two td.patch-delegate:

   

  

  
 
 
  
New
  
 
  

  This is throwing off column alignment: delegates now line up under the
  State heading.

> For the first patch, the recently released v3.0.0 of the JS cookie 
> library replaces the previous version. 

 - the js.cookie.min.js file is still corrupted and I redownloaded it
   from a CDN. I'm not sure where the bug lies here and it's probably
   not something you need to fix, but a link to the source in the commit
   message would be nice.

> For the second patch, following jrnieder’s comments [2], the commit 
> message is updated to better explain the benefits of the change and its
> details. Also, an unclear comment in forms.py for BundleForm is cleaned
> up to better explain what the changes do. The id for  table cells in
> patch-list.html are correctly differentiated by patch id. I noticed a 
> bug where property changes in the patch detail page weren’t working
> because the respective “update” action was not accounted for.  
>

This (patch 2) is where I'm up to in reviews atm. I'm hoping to do more soon.

> For the third patch, the “jump to form” arrow is removed [3].
>
> For the fourth patch, the `updateProperty` function now returns whether
> the update request was successful or not with `response.ok` so that 
> callers can use that information and respond accordingly. The next patch
> adds a use case for the return value.
>
> For the fifth patch, the inline dropdowns are changed so that they are 
> only visible to logged in users. Also, upon any unsuccessful update
> requests, the dropdown selection reverts to its previous selection.
> Since update requests to a patch’s state and delegate fields fail for
> unauthorized users, this behavior covers the case where unauthorized
> users don't see the current state of the db after they change the
> dropdown selection. This behavior is a useful example of patch four’s
> change of returning whether an update request is successful.

 - they're visible to logged in users but still suggest that if I am a
   regular user that I have the ability to change anyone's patch
   state/delegate. I am still a bit dubious overall about inline
   dropdowns in general, although I am cognisant that it's much more
   discoverable this way. Either way, IMO dropdowns need to be
   restricted to editable patches.

Kind regards,
Daniel

> [1] https://lists.ozlabs.org/pipermail/patchwork/2021-July/006968.html
> [2] https://lists.ozlabs.org/pipermail/patchwork/2021-July/006988.html
> [3] https://lists.ozlabs.org/pipermail/patchwork/2021-July/006991.html
>
> Raxel Gutierrez (5):
>   static: add JS Cookie Library to get csrftoken for fetch requests
>   patch-list: clean up patch-list page and refactor patch forms
>   patch-list: style modification forms as an action bar
>   static: add rest.js to handle requests & respective messages
>   patch-list: add inline dropdown for delegate and state one-off changes
>
>  htdocs/README.rst |  23 +++
>  htdocs/css/style.css  |  85 +++--
>  htdocs/js/js.cookie.min.js|   2 +
>  htdocs/js/patch-list.js   |  60 ++
>  htdocs/js/rest.js |  72 
>  patchwork/forms.py|  66 +--
>  patchwork/templates/patchwork/list.html   |  11 +-
>  .../templates/patchwork/partials/errors.html  |   9 +
>  .../patchwork/partials/patch-forms.html   |  45 +
>  .../patchwork/partials/patch-list.html| 171 ++
>  patchwork/templates/patchwork/submission.html |  91 +-
>  patchwork/tests/views/test_bundles.py |  44 ++---
>  patchwork/tests/views/test_patch.py   |   4 +-
>  patchwork/views/__init__.py   |  76 
>  patchwork/views/patch.py  |  35 +---
>  templates/base.html   |   3 +-
>  16 files changed, 472 insertions(+), 325 deletions(-)
>  create mode 100644 htdocs/js/js.cookie.min.js
>  create mode 100644 htdocs/js/patch-list.js
>  create mode 100644 htdocs/js/rest.js
>  create mode 100644 patchwork/templates/patchwork/partials/errors.html
>  create mode 100644 patchwork/templates/patchwork/partials/patch-forms.html
>
> Range-diff:
> 1:  2a34394 ! 1:  859789e static: add JS Cookie Library to get csrftoken for 
> fetch requests
> @@ htdocs/README.rst: js
>  +  This is used to get the ``csrftoken`` cookie for AJAX requests in 
> JavaScript.
>  +
>  +  :GitHub: https://github.com/js-cookie/js-cookie/
> -+  :Version: 2.2.1
> ++  :Version: 3.0.0
>  +
>   ``selectize.min.js``
>   
> @@ htdocs/README.rst: js
>  
>   ## 

Re: [PATCH v3 1/5] static: add JS Cookie Library to get csrftoken for fetch requests

2021-08-02 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Currently, requests are made only through form submission and the
> csrftoken is added to templates using {% csrf_token %}. Following Django
> docs [1], the library is useful to add csrftoken when making requests in
> JavaScript.
>
> [1] https://docs.djangoproject.com/en/3.2/ref/csrf/#ajax

This still isn't applying properly but I can at least find it online
fairly easily. I suspect mailman is forcing linebreaks that it shouldn't
do, but idk.

Anyway,
Acked-by: Daniel Axtens 

Once I find a patch that uses this that is ready to apply I'm happy to
apply this.

Kind regards,
Daniel

> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/README.rst  | 9 +
>  htdocs/js/js.cookie.min.js | 2 ++
>  templates/base.html| 1 +
>  3 files changed, 12 insertions(+)
>  create mode 100644 htdocs/js/js.cookie.min.js
>
> diff --git a/htdocs/README.rst b/htdocs/README.rst
> index 62f15c2..128dc7c 100644
> --- a/htdocs/README.rst
> +++ b/htdocs/README.rst
> @@ -122,6 +122,15 @@ js
>:GitHub: jQuery plug-in to drag and drop rows in HTML tables
>:Version: ???
>  
> +``js.cookie.min.js``
> +
> +  Library used to handle cookies.
> +
> +  This is used to get the ``csrftoken`` cookie for AJAX requests in 
> JavaScript.
> +
> +  :GitHub: https://github.com/js-cookie/js-cookie/
> +  :Version: 3.0.0
> +
>  ``selectize.min.js``
>  
>Selectize is the hybrid of a ``textbox`` and  box. It's jQuery
> diff --git a/htdocs/js/js.cookie.min.js b/htdocs/js/js.cookie.min.js
> new file mode 100644
> index 000..63fc3ef
> --- /dev/null
> +++ b/htdocs/js/js.cookie.min.js
> @@ -0,0 +1,2 @@
> +/*! js-cookie v3.0.0 | MIT */
> +!function(e,t){"object"==typeof exports&&"undefined"!=typeof 
> module?module.exports=t():"function"==typeof 
> define&?define(t):(e=e||self,function(){var 
> n=e.Cookies,r=e.Cookies=t();r.noConflict=function(){return 
> e.Cookies=n,r}}())}(this,(function(){"use strict";function e(e){for(var 
> t=1;t n)e[r]=n[r]}return e}var t={read:function(e){return 
> e.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(e){return 
> encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}};return
>  function n(r,o){function i(t,n,i){if("undefined"!=typeof 
> document){"number"==typeof(i=e({},o,i)).expires&&(i.expires=new 
> Date(Date.now()+864e5*i.expires)),i.expires&&(i.expires=i.expires.toUTCString()),t=encodeURIComponent(t).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape),n=r.write(n,t);var
>  c="";for(var u in i)i[u]&&(c+="; 
> "+u,!0!==i[u]&&(c+="="+i[u].split(";")[0]));return document.c
 oo
>  kie=t+"="+n+c}}return 
> Object.create({set:i,get:function(e){if("undefined"!=typeof 
> document&&(!arguments.length||e)){for(var 
> n=document.cookie?document.cookie.split("; "):[],o={},i=0;i c=n[i].split("="),u=c.slice(1).join("=");'"'===u[0]&&(u=u.slice(1,-1));try{var
>  f=t.read(c[0]);if(o[f]=r.read(u,f),e===f)break}catch(e){}}return 
> e?o[e]:o}},remove:function(t,n){i(t,"",e({},n,{expires:-1}))},withAttributes:function(t){return
>  n(this.converter,e({},this.attributes,t))},withConverter:function(t){return 
> n(e({},this.converter,t),this.attributes)}},{attributes:{value:Object.freeze(o)},converter:{value:Object.freeze(r)}})}(t,{path:"/"})}));
> diff --git a/templates/base.html b/templates/base.html
> index 8accb4c..8700602 100644
> --- a/templates/base.html
> +++ b/templates/base.html
> @@ -21,6 +21,7 @@
>
>
>
> +  
>

Re: [PATCH v3 2/5] patch-list: clean up patch-list page and refactor patch forms

2021-08-02 Thread Daniel Axtens
Hi Raxel,

I think this patch may do a bit much, and I am struggling to follow it.

If I understand correctly,

> Clean up the patch-list page by moving forms to a new template file
> patch-forms.html and move them to the top of the page, add ids to

this bit makes a user visible change, but

> No user-visible change should be noticed since this change does not
> make stye changes. Refactor forms.py, __init__.py, patch.py, and

this other change doesn't.

Could you please split user-visible vs non-user-visible changes into
separate patches? I'm pretty keen on being able to apply refactorings
and cleanups while we debate the UI and I can't do that if with they're
intermingled. (In general, as with the kernel, patches that do one thing
at a time are generally preferred --- although I'm not dogmatic about
this and I am aware that refactoring is often hard to neatly split!)

> Clean up the patch-list page by moving forms to a new template file
> patch-forms.html and move them to the top of the page, add ids to
> table cells, and rename selectors using hyphen delimited strings where
> the relevant changes were made. Also, create a partial template for
> errors that render with form submission. These changes improve the
> discoverability of the patch-list forms and make the code healthier,
> ready for change, and overall more readable.

Is there a reason to prefer hyphen-delimited selectors? It looks like in
the css file we have a mix of hyphen-delimited ones from bootstrap, ones
without delimiters at all, and even underscores. I wonder if this would
be something we should try to change all at once, rather than just
changing the odd selector here and there? (Not saying you have to do
that now! Just trying to contain the churn and review size.)

Apart from that, these non-user-visible changes seem like good ideas to me.

> Also, move patch-list related js code to a new patch-list.js file, to
> make the JavaScript easy to read and change in one place. This makes
> automatic code formatting easier, makes it more straightforward to
> measure test coverage and discover opportunities for refactoring, and
> simplifies a possible future migration to TypeScript if the project
> chooses to go in that direction.

Keen on this. I'd be happy to apply a patch that does just this.

> No user-visible change should be noticed since this change does not
> make stye changes. Refactor forms.py, __init__.py, patch.py, and
> test_bundles.py files so that the shared bundle form in patch-forms.html
> works for both the patch-list and patch-detail pages. In particular, the
> changes normalize the behavior of the error and update messages of the
> patch forms and updates tests to reflect the changes. Overall, these
> changes make patch forms ready for change and more synchronized in their
> behavior. More specifically:
>
> - Previously patch forms changes were separated between the patch-detail
>   and patch-list pages. Thus, desired changes to the patch forms
>   required changes to patch-list.html, submission.html, and forms.py.
>   So, the most important benefit to this change is that forms.py and
>   patch-forms.html become the two places to adjust the forms to handle
>   form validation and functionality as well as UI changes.
>
> - Previously the patch forms in patch-list.html handled error and
>   update messages through views in patch.py, whereas the patch forms in
>   submission handled the messages with forms.py. Now, with a single
>   patch forms component in patch-forms.html, forms.py is set to handle
>   the messages and handle form validation for both pages.
>

I like this in theory but some of the python is a bit scary and I'm
struggling to figure out which parts of this patch belong to this change
and which parts of the patch belong to the other changes.

Some smaller comments follow.

> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/README.rst |   7 +
>  htdocs/css/style.css  |  10 +-
>  htdocs/js/patch-list.js   |  12 ++
>  patchwork/forms.py|  22 ++-
>  patchwork/templates/patchwork/list.html   |  11 +-
>  .../templates/patchwork/partials/errors.html  |   9 ++
>  .../patchwork/partials/patch-forms.html   |  45 ++
>  .../patchwork/partials/patch-list.html| 137 +++---
>  patchwork/templates/patchwork/submission.html |  91 +---
>  patchwork/tests/views/test_bundles.py |  44 +++---
>  patchwork/tests/views/test_patch.py   |   4 +-
>  patchwork/views/__init__.py   |  73 +-
>  patchwork/views/patch.py  |  35 ++---
>  13 files changed, 197 insertions(+), 303 deletions(-)
>  create mode 100644 htdocs/js/patch-list.js
>  create mode 100644 patchwork/templates/patchwork/partials/errors.html
>  create mode 100644 patchwork/templates/patchwork/partials/patch-forms.html
>
> diff --git a/htdocs/README.rst b/htdocs/README.rst
> index 

[PATCH 3/3] REST: Allow a project to restrict submitter state changes

2021-08-02 Thread Daniel Axtens
As with xmlrpc and the UI.

Signed-off-by: Daniel Axtens 
---
 patchwork/api/patch.py| 10 +
 patchwork/tests/api/test_patch.py | 70 +++
 2 files changed, 80 insertions(+)

diff --git a/patchwork/api/patch.py b/patchwork/api/patch.py
index 9d222754412e..b8d0d5e17749 100644
--- a/patchwork/api/patch.py
+++ b/patchwork/api/patch.py
@@ -122,6 +122,16 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer):
   "'%s'" % (value, self.instance.project))
 return value
 
+def validate_state(self, value):
+"""Check that the users is authorised to set this state."""
+user = self.context.get('request').user
+if not self.instance.can_set_state(user):
+raise ValidationError(
+"User '%s' is not permitted to set state '%s' on this patch." %
+(user, value.name))
+
+return value
+
 def to_representation(self, instance):
 # NOTE(stephenfin): This is here to ensure our API looks the same even
 # after we changed the series-patch relationship from M:N to 1:N. It
diff --git a/patchwork/tests/api/test_patch.py 
b/patchwork/tests/api/test_patch.py
index da2dd6e9084b..9de7b0d105f4 100644
--- a/patchwork/tests/api/test_patch.py
+++ b/patchwork/tests/api/test_patch.py
@@ -11,6 +11,9 @@ from django.conf import settings
 from django.urls import reverse
 
 from patchwork.models import Patch
+from patchwork.models import Person
+from patchwork.models import Project
+from patchwork.models import State
 from patchwork.tests.api import utils
 from patchwork.tests.utils import create_maintainer
 from patchwork.tests.utils import create_patch
@@ -409,3 +412,70 @@ class TestPatchAPI(utils.APITestCase):
 self.client.force_authenticate(user=user)
 resp = self.client.delete(self.api_url(patch.id))
 self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code)
+
+
+@unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API')
+class TestPatchStateChecks(utils.APITestCase):
+fixtures = ['default_tags']
+
+@staticmethod
+def api_url(item=None):
+kwargs = {'pk': item}
+return reverse('api-patch-detail', kwargs=kwargs)
+
+def setUp(self):
+self.projects = {}
+self.maintainers = {}
+self.delegates = {}
+self.submitters = {}
+self.patches = {}
+
+for project_type in (Project.SUBMITTER_NO_STATE_CHANGES,
+ Project.SUBMITTER_ALL_STATE_CHANGES):
+project = create_project(
+submitter_state_change_rules=project_type)
+self.projects[project_type] = project
+self.maintainers[project_type] = create_maintainer(project)
+submitter = create_user(project)
+self.submitters[project_type] = submitter
+delegate = create_user(project)
+self.delegates[project_type] = delegate
+
+patch = create_patch(project=project,
+ submitter=Person.objects.get(
+ user=submitter),
+ delegate=delegate)
+self.patches[project_type] = patch
+
+create_state(name="New")
+create_state(name="RFC")
+
+def can_set_state(self, patch, user):
+new_state = State.objects.get(name="New")
+rfc_state = State.objects.get(name="RFC")
+patch.state = new_state
+patch.save()
+
+self.client.force_authenticate(user=user)
+resp = self.client.patch(self.api_url(patch.id),
+ {'state': rfc_state.slug})
+
+if resp.status_code != status.HTTP_200_OK:
+return False
+
+self.assertEqual(Patch.objects.get(id=patch.id).state, rfc_state)
+return True
+
+def test_allset(self):
+project = Project.SUBMITTER_ALL_STATE_CHANGES
+patch = self.patches[project]
+self.assertTrue(self.can_set_state(patch, self.maintainers[project]))
+self.assertTrue(self.can_set_state(patch, self.delegates[project]))
+self.assertTrue(self.can_set_state(patch, self.submitters[project]))
+
+def test_noset(self):
+project = Project.SUBMITTER_NO_STATE_CHANGES
+patch = self.patches[project]
+self.assertTrue(self.can_set_state(patch, self.maintainers[project]))
+self.assertTrue(self.can_set_state(patch, self.delegates[project]))
+self.assertFalse(self.can_set_state(patch, self.submitters[project]))
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH 2/3] xmlrpc: Allow a project to restrict submitter state changes

2021-08-02 Thread Daniel Axtens
As with the UI.

Signed-off-by: Daniel Axtens 
---
 patchwork/tests/test_xmlrpc.py | 90 ++
 patchwork/views/xmlrpc.py  |  4 ++
 2 files changed, 94 insertions(+)

diff --git a/patchwork/tests/test_xmlrpc.py b/patchwork/tests/test_xmlrpc.py
index 4726fdffa5d5..eea0b4eaf560 100644
--- a/patchwork/tests/test_xmlrpc.py
+++ b/patchwork/tests/test_xmlrpc.py
@@ -10,6 +10,9 @@ from django.conf import settings
 from django.test import LiveServerTestCase
 from django.urls import reverse
 
+from patchwork.models import Person
+from patchwork.models import Project
+from patchwork.models import State
 from patchwork.tests import utils
 
 
@@ -81,6 +84,93 @@ class XMLRPCAuthenticatedTest(LiveServerTestCase):
 self.assertTrue(result['archived'])
 
 
+@unittest.skipUnless(settings.ENABLE_XMLRPC,
+ 'requires xmlrpc interface (use the ENABLE_XMLRPC '
+ 'setting)')
+class XMLRPCStateSettingTest(LiveServerTestCase):
+
+def url_for_user(self, user):
+return ('http://%s:%s@' + self.url[7:]) % \
+   (user.username, user.username)
+
+def setUp(self):
+self.url = self.live_server_url + reverse('xmlrpc')
+# url is of the form http://localhost:PORT/PATH
+# strip the http and replace it with the username/passwd of a user.
+self.projects = {}
+self.maintainers = {}
+self.delegates = {}
+self.submitters = {}
+self.patches = {}
+self.rpcs = {}
+
+for project_type in (Project.SUBMITTER_NO_STATE_CHANGES,
+ Project.SUBMITTER_ALL_STATE_CHANGES):
+project = utils.create_project(
+submitter_state_change_rules=project_type)
+self.projects[project_type] = project
+self.maintainers[project_type] = utils.create_maintainer(project)
+submitter = utils.create_user(project)
+self.submitters[project_type] = submitter
+delegate = utils.create_user(project)
+self.delegates[project_type] = delegate
+
+self.rpcs[project_type] = {
+'maintainer': ServerProxy(self.url_for_user(
+self.maintainers[project_type])),
+'delegate': ServerProxy(self.url_for_user(delegate)),
+'submitter': ServerProxy(self.url_for_user(submitter)),
+}
+
+patch = utils.create_patch(project=project,
+   submitter=Person.objects.get(
+   user=submitter),
+   delegate=delegate)
+self.patches[project_type] = patch
+
+utils.create_state(name="New")
+utils.create_state(name="RFC")
+
+def tearDown(self):
+for project_type in self.rpcs:
+rpc_dict = self.rpcs[project_type]
+for user in rpc_dict:
+rpc_dict[user].close()
+
+def can_set_state(self, patch, rpc):
+new_state = State.objects.get(name="New")
+rfc_state = State.objects.get(name="RFC")
+patch.state = new_state
+patch.save()
+
+result = rpc.patch_get(patch.id)
+self.assertEqual(result['state_id'], new_state.id)
+
+try:
+rpc.patch_set(patch.id, {'state': rfc_state.id})
+except xmlrpc_client.Fault:
+return False
+
+# reload the patch
+result = rpc.patch_get(patch.id)
+self.assertEqual(result['state_id'], rfc_state.id)
+return True
+
+def test_allset(self):
+rpc_dict = self.rpcs[Project.SUBMITTER_ALL_STATE_CHANGES]
+patch = self.patches[Project.SUBMITTER_ALL_STATE_CHANGES]
+self.assertTrue(self.can_set_state(patch, rpc_dict['maintainer']))
+self.assertTrue(self.can_set_state(patch, rpc_dict['delegate']))
+self.assertTrue(self.can_set_state(patch, rpc_dict['submitter']))
+
+def test_noset(self):
+rpc_dict = self.rpcs[Project.SUBMITTER_NO_STATE_CHANGES]
+patch = self.patches[Project.SUBMITTER_NO_STATE_CHANGES]
+self.assertTrue(self.can_set_state(patch, rpc_dict['maintainer']))
+self.assertTrue(self.can_set_state(patch, rpc_dict['delegate']))
+self.assertFalse(self.can_set_state(patch, rpc_dict['submitter']))
+
+
 class XMLRPCModelTestMixin(object):
 
 def create_multiple(self, count):
diff --git a/patchwork/views/xmlrpc.py b/patchwork/views/xmlrpc.py
index 6701bf20f386..d73cfa7a8441 100644
--- a/patchwork/views/xmlrpc.py
+++ b/patchwork/views/xmlrpc.py
@@ -713,6 +713,10 @@ def patch_set(user, patch_id, params):
 if not patch.is_editable(user):
 raise Exception('No permissions to edit this patch')
 
+if 'state' in params:
+if not patch.can_set_state(user):
+raise Exception('No permissions to set patch state')
+
 for (k, v) in params

[PATCH 1/3] Allow a project to restrict submitter state changes

2021-08-02 Thread Daniel Axtens
In discussions about how to make patchwork more user-friendly and
suitable for more projects, we realised that perhaps the current
ability for submitters to change their patch state to any value
isn't the most appropriate setting for all maintainers, especially
in light of increasing automation.

Allow a project to stop a submitter from changing the state of
their patches. This is not the default but can be set by a patchwork
administrator.

Signed-off-by: Daniel Axtens 
---
 ...45_project_submitter_state_change_rules.py | 24 +
 patchwork/models.py   | 36 +++
 patchwork/views/__init__.py   |  8 +
 patchwork/views/patch.py  | 14 ++--
 4 files changed, 80 insertions(+), 2 deletions(-)
 create mode 100644 
patchwork/migrations/0045_project_submitter_state_change_rules.py

diff --git a/patchwork/migrations/0045_project_submitter_state_change_rules.py 
b/patchwork/migrations/0045_project_submitter_state_change_rules.py
new file mode 100644
index ..9d0b2892bd5c
--- /dev/null
+++ b/patchwork/migrations/0045_project_submitter_state_change_rules.py
@@ -0,0 +1,24 @@
+# Generated by Django 3.1.12 on 2021-08-03 00:32
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+dependencies = [
+('patchwork', '0044_add_project_linkname_validation'),
+]
+
+operations = [
+migrations.AddField(
+model_name='project',
+name='submitter_state_change_rules',
+field=models.SmallIntegerField(
+choices=[
+(0, 'Submitters may not change patch states'),
+(1, 'Submitters may set any patch state')],
+default=1,
+help_text='What state changes can patch submitters make?'
+  ' Does not affect maintainers.'),
+),
+]
diff --git a/patchwork/models.py b/patchwork/models.py
index 00273da9f5bd..706b912c349a 100644
--- a/patchwork/models.py
+++ b/patchwork/models.py
@@ -93,6 +93,19 @@ class Project(models.Model):
 send_notifications = models.BooleanField(default=False)
 use_tags = models.BooleanField(default=True)
 
+# how much can a patch submitter change?
+SUBMITTER_NO_STATE_CHANGES = 0
+SUBMITTER_ALL_STATE_CHANGES = 1
+SUBMITTER_STATE_CHANGE_CHOICES = (
+(SUBMITTER_NO_STATE_CHANGES, 'Submitters may not change patch states'),
+(SUBMITTER_ALL_STATE_CHANGES, 'Submitters may set any patch state'),
+)
+submitter_state_change_rules = models.SmallIntegerField(
+choices=SUBMITTER_STATE_CHANGE_CHOICES,
+default=SUBMITTER_ALL_STATE_CHANGES,
+help_text='What state changes can patch submitters make?'
+  ' Does not affect maintainers.')
+
 def is_editable(self, user):
 if not user.is_authenticated:
 return False
@@ -518,6 +531,29 @@ class Patch(SubmissionMixin):
 return True
 return False
 
+def can_set_state(self, user):
+# an unauthenticated user can never change state
+if not user.is_authenticated:
+return False
+
+# a maintainer can always set state
+if self.project.is_editable(user):
+self._edited_by = user
+return True
+
+# a delegate can always set state
+if user == self.delegate:
+self._edited_by = user
+return True
+
+# if the state change rules prohibit it, no other user can set change
+if (self.project.submitter_state_change_rules ==
+Project.SUBMITTER_NO_STATE_CHANGES):
+return False
+
+# otherwise, a submitter can change state
+return self.is_editable(user)
+
 @staticmethod
 def filter_unique_checks(checks):
 """Filter the provided checks to generate the unique list."""
diff --git a/patchwork/views/__init__.py b/patchwork/views/__init__.py
index 3efe90cd6929..9f5d316d18b5 100644
--- a/patchwork/views/__init__.py
+++ b/patchwork/views/__init__.py
@@ -312,6 +312,14 @@ def process_multiplepatch_form(request, form, action, 
patches, context):
   % patch.name)
 continue
 
+field = form.fields.get('state', None)
+if field and not field.is_no_change(form.cleaned_data['state']) \
+   and not patch.can_set_state(request.user):
+errors.append(
+"You don't have the permissions to set the state of patch '%s'"
+% patch.name)
+continue
+
 changed_patches += 1
 form.save(patch)
 
diff --git a/patchwork/views/patch.py b/patchwork/views/patch.py
index 3e6874ae1e5d..72c199135cbb 100644
--- a/patchwork/views/patch.py
+++ b/patchwork/views/patch.py
@@ -101,8 +101,18 @@ def patch_detail(request, project_id, msgid):
 elif action is None:
   

[PATCH 0/3] Allow a project to restrict submitter state changes

2021-08-02 Thread Daniel Axtens
In discussions about how to make patchwork more user-friendly and
suitable for more projects, we realised that perhaps the current
ability for submitters to change their patch state to any value
isn't the most appropriate setting for all maintainers, especially
in light of increasing maintainer-driven API-centric automation.

This set of 3 patches allows a project to stop a submitter from
changing the state of their patches - in the UI, via xmlrpc and via
REST. This is not the default behaviour but can be set by a patchwork
administrator on a per-project basis.

Daniel Axtens (3):
  Allow a project to restrict submitter state changes
  xmlrpc: Allow a project to restrict submitter state changes
  REST: Allow a project to restrict submitter state changes

 patchwork/api/patch.py| 10 +++
 ...45_project_submitter_state_change_rules.py | 24 +
 patchwork/models.py   | 36 
 patchwork/tests/api/test_patch.py | 70 +++
 patchwork/tests/test_xmlrpc.py| 90 +++
 patchwork/views/__init__.py   |  8 ++
 patchwork/views/patch.py  | 14 ++-
 patchwork/views/xmlrpc.py |  4 +
 8 files changed, 254 insertions(+), 2 deletions(-)
 create mode 100644 
patchwork/migrations/0045_project_submitter_state_change_rules.py

-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH v2 3/5] patch-list: style modification forms as an action bar

2021-07-26 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Added styling to the new patch list html code to make the change
> property and bundle action forms more usable. Before [1] and after [2]
> images for reference.
>
> [1] https://imgur.com/Pzelipp
> [2] https://imgur.com/UtNJXuf

Nice! I'll run it past the local Patchwork power users.

There's a little 'jump to form' arrow (▾) that probably needs to point
up now, rather than down.

Kind regards,
Daniel

>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/css/style.css | 77 
>  patchwork/forms.py   | 44 +++--
>  2 files changed, 99 insertions(+), 22 deletions(-)
>
> diff --git a/htdocs/css/style.css b/htdocs/css/style.css
> index 1bcc93e..9982f92 100644
> --- a/htdocs/css/style.css
> +++ b/htdocs/css/style.css
> @@ -1,3 +1,7 @@
> +:root {
> +--light-color: #F7F7F7;
> +}
> +
>  h2 {
>  font-size: 25px;
>  margin: 18px 0 18px 0;
> @@ -122,10 +126,6 @@ a.colinactive:hover {
>  div.filters {
>  }
>  
> -div.patch-forms {
> -margin-top: 1em;
> -}
> -
>  /* list order manipulation */
>  
>  table.patchlist tr.draghover {
> @@ -149,7 +149,7 @@ input#reorder-change {
>  .paginator {
>  text-align: right;
>  clear: both;
> -margin: 8px 0 15px;
> +margin: 8px 0 15px;
>  }
>  
>  .paginator .prev-na,
> @@ -346,13 +346,62 @@ table.bundlelist td
>  padding-right: 2em;
>  }
>  
> +.patch-list-actions {
> +width: 100%;
> +display: inline-flex;
> +flex-wrap: wrap;
> +justify-content: space-between;
> +}
> +
>  /* forms that appear for a patch */
> +.patch-forms {
> +display: inline-flex;
> +flex-wrap: wrap;
> +margin: 16px 0px;
> +}
> +
>  div.patch-form {
> -border: thin solid #080808;
> -padding-left: 0.6em;
> -padding-right: 0.6em;
> -float: left;
> -margin: 0.5em 5em 0.5em 10px;
> +display: flex;
> +flex-wrap: wrap;
> +align-items: center;
> +}
> +
> +select[class^=change-property-], .archive-patch-select, .add-bundle {
> +padding: 4px;
> +margin-right: 8px;
> +box-sizing: border-box;
> +border-radius: 4px;
> +background-color: var(--light-color);
> +}
> +
> +#patch-form-archive {
> +display: flex;
> +align-items: center;
> +margin-right: 4px;
> +}
> +
> +#patch-form-archive > label {
> +margin: 0px;
> +}
> +
> +#patch-form-archive > select, #patch-form-archive > input {
> +margin: 0px 4px 0px 4px;
> +}
> +
> +.patch-form-submit {
> +font-weight: bold;
> +padding: 4px;
> +}
> +
> +#patch-form-bundle, #add-to-bundle, #remove-bundle {
> +margin-left: 16px;
> +}
> +
> +.create-bundle {
> +padding: 4px;
> +margin-right: 8px;
> +box-sizing: border-box;
> +border-radius: 4px;
>  }
>  
>  div.patch-form h3 {
> @@ -371,15 +420,17 @@ div.patch-form ul {
>  margin-top: 0em;
>  }
>  
> -/* forms */
> -table.form {
> +.create-bundle {
> +padding: 4px;
> +margin-right: 8px;
> +box-sizing: border-box;
> +border-radius: 4px;
>  }
>  
>  span.help_text {
>  font-size: 80%;
>  }
>  
> -
>  table.form td {
>  padding: 0.6em;
>  vertical-align: top;
> diff --git a/patchwork/forms.py b/patchwork/forms.py
> index 7f1fd31..9727932 100644
> --- a/patchwork/forms.py
> +++ b/patchwork/forms.py
> @@ -54,7 +54,10 @@ class BundleForm(forms.ModelForm):
>  field_mapping = {'name': 'bundle_name'}
>  name = forms.RegexField(
>  regex=r'^[^/]+$', min_length=1, max_length=50, required=False,
> -error_messages={'invalid': 'Bundle names can\'t contain slashes'})
> +error_messages={'invalid': 'Bundle names can\'t contain slashes'},
> +widget=forms.TextInput(
> +attrs={'class': 'create-bundle',
> +   'placeholder': 'Bundle name'}))
>  
>  # Maps form fields 'name' attr rendered in HTML element
>  def add_prefix(self, field_name):
> @@ -126,18 +129,28 @@ class PatchForm(forms.ModelForm):
>  def __init__(self, instance=None, project=None, *args, **kwargs):
>  super(PatchForm, self).__init__(instance=instance, *args, **kwargs)
>  self.fields['delegate'] = forms.ModelChoiceField(
> -queryset=_get_delegate_qs(project, instance), required=False)
> +queryset=_get_delegate_qs(project, instance),
> +widget=forms.Select(attrs={'class': 'change-property-delegate'}),
> +required=False)
>  
>  class Meta:
>  model = Patch
>  fields = ['state', 'archived', 'delegate']
> +widgets = {
> +'state': forms.Select(
> +attrs={'class': 'change-property-state'}),
> +'archived': forms.CheckboxInput(
> +attrs={'class': 'archive-patch-check'}),
> +}
>  
>  
>  class OptionalModelChoiceField(forms.ModelChoiceField):
> -no_change_choice = ('*', 'no change')
> +no_change_choice = ('*', 'No change')
>  to_field_name = None
>  
> -def __init__(self, 

Re: [PATCH v2 5/5] patch-list: add inline dropdown for delegate and state one-off changes

2021-07-26 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Add dropdown for the cell values of the Delegate and State columns for
> each individual patch to make one-off changes to patches. Change the
> generic_list method to pass the list of states and maintainers to the
> patch list view context to populate the dropdown options. The static
> patch-list.js file now uses the modularity of the fetch request and
> update/error messages handling of rest.js.
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/js/patch-list.js   | 34 +++
>  htdocs/js/rest.js |  2 +-
>  .../patchwork/partials/patch-list.html| 31 +++--
>  patchwork/views/__init__.py   |  3 ++
>  templates/base.html   |  2 +-
>  5 files changed, 67 insertions(+), 5 deletions(-)
>
> diff --git a/htdocs/js/patch-list.js b/htdocs/js/patch-list.js
> index 8c7640f..75d9b38 100644
> --- a/htdocs/js/patch-list.js
> +++ b/htdocs/js/patch-list.js
> @@ -1,4 +1,17 @@
> +import { updateProperty } from "./rest.js";
> +
>  $( document ).ready(function() {
> +// Change listener for dropdowns that change an individual patch's 
> delegate and state properties
> +$("select[class^='change-property-']").change((event) => {
> +const property = event.target.getAttribute("value");
> +const { url, data } = getPatchProperties(event.target, property);
> +const updateMessage = {
> +'none': "No patches updated",
> +'some': "1 patch updated",
> +};
> +updateProperty(url, data, updateMessage);
> +});
> +
>  $("#patchlist").stickyTableHeaders();
>  
>  $("#check-all").change(function(e) {
> @@ -9,4 +22,25 @@ $( document ).ready(function() {
>  }
>  e.preventDefault();
>  });
> +
> +/**
> + * Returns the data to make property changes to a patch through fetch 
> request.
> + * @param {Element} propertySelect Property select element modified.
> + * @param {string} property Patch property modified (e.g. "state", 
> "delegate")
> + * @return {{property: string, value: string}}
> + * property: Property field to be modified in request.
> + * value: New value for property to be modified to in request.
> + */
> +function getPatchProperties(propertySelect, property) {
> +const selectedOption = 
> propertySelect.options[propertySelect.selectedIndex];
> +const patchId = 
> propertySelect.parentElement.parentElement.dataset.patchId;
> +const propertyValue = (property === "state") ? selectedOption.text
> +: (selectedOption.value === "*") ? null : 
> selectedOption.value
> +const data = {};
> +data[property] = propertyValue;
> +return {
> +"url": "/api/patches/" + patchId + "/",
> +"data": data,
> +};
> +}
>  });
> \ No newline at end of file
> diff --git a/htdocs/js/rest.js b/htdocs/js/rest.js
> index 34d41f5..254b5ae 100644
> --- a/htdocs/js/rest.js
> +++ b/htdocs/js/rest.js
> @@ -68,4 +68,4 @@ function handleErrorMessages(errorMessage) {
>  container.prepend(errorHeader);
>  }
>  
> -export { updateProperty, handleUpdateMessages, handleUpdateMessages};
> \ No newline at end of file
> +export { updateProperty };
> \ No newline at end of file
> diff --git a/patchwork/templates/patchwork/partials/patch-list.html 
> b/patchwork/templates/patchwork/partials/patch-list.html
> index 339cf42..8967cdd 100644
> --- a/patchwork/templates/patchwork/partials/patch-list.html
> +++ b/patchwork/templates/patchwork/partials/patch-list.html
> @@ -5,7 +5,7 @@
>  {% load static %}
>  
>  {% block headers %}
> -  
> +  
>  {% endblock %}
>  
>  {% include "patchwork/partials/filters.html" %}
> @@ -195,8 +195,33 @@
> {{ patch|patch_checks }}
> {{ patch.date|date:"Y-m-d" }}
> {{ patch.submitter|personify:project }}
> -   {{ patch.delegate.username }}
> -   {{ patch.state }}
> +   
> +  
> +{% if not patch.delegate.username %}
> +  No delegate
> +{% else %}
> +  No delegate
> +{% endif %}
> +{% for maintainer in maintainers %}
> +  {% if maintainer.name == patch.delegate.username %}
> +{{ 
> patch.delegate.username }}
> +  {% else %}
> +{{ maintainer.name 
> }}
> +  {% endif %}
> +{% endfor %}
> +  
> +
> +   
> +
> +  {% for state in states %}
> +{% if state.name == patch.state.name %}
> +  {{ patch.state 
> }}
> +{% else %}
> +  {{ state.name }}
> +{% endif %}
> +  {% endfor %}
> +
> +  

This is pretty cool. My initial thoughts are as follows:

Can we make these only appear editable if you are either:
 - logged in,
 - or ideally, if you have the rights to change them

Currently I see a bunch of drop downs when I access the page as a
non-logged-in user where I couldn't possibly 

Re: [PATCH v2 1/5] static: add JS Cookie Library to get csrftoken for fetch requests

2021-07-26 Thread Daniel Axtens
Raxel Gutierrez  writes:

> Currently, requests are made only through form submission and the
> csrftoken is added to templates using {% csrf_token %}. Following Django
> docs [1], the library is useful to add csrftoken when making requests in
> JavaScript.
>
> [1] https://docs.djangoproject.com/en/3.2/ref/csrf/#ajax
>
> Signed-off-by: Raxel Gutierrez 
> ---
>  htdocs/README.rst  | 9 +
>  htdocs/js/js.cookie.min.js | 3 +++
>  templates/base.html| 1 +
>  3 files changed, 13 insertions(+)
>  create mode 100644 htdocs/js/js.cookie.min.js
>
> diff --git a/htdocs/README.rst b/htdocs/README.rst
> index 62f15c2..fa1616c 100644
> --- a/htdocs/README.rst
> +++ b/htdocs/README.rst
> @@ -122,6 +122,15 @@ js
>:GitHub: jQuery plug-in to drag and drop rows in HTML tables
>:Version: ???
>  
> +``js.cookie.min.js``
> +
> +  Library used to handle cookies.
> +
> +  This is used to get the ``csrftoken`` cookie for AJAX requests in 
> JavaScript.
> +
> +  :GitHub: https://github.com/js-cookie/js-cookie/
> +  :Version: 2.2.1
> +
>  ``selectize.min.js``
>  
>Selectize is the hybrid of a ``textbox`` and  box. It's jQuery
> diff --git a/htdocs/js/js.cookie.min.js b/htdocs/js/js.cookie.min.js
> new file mode 100644
> index 000..f5f4c36
> --- /dev/null
> +++ b/htdocs/js/js.cookie.min.js
> @@ -0,0 +1,3 @@
> +/*! js-cookie v2.2.1 | MIT */
> +
> +!function(a){var b;if("function"==typeof 
> define&&&(define(a),b=!0),"object"==typeof 
> exports&&(module.exports=a(),b=!0),!b){var 
> c=window.Cookies,d=window.Cookies=a();d.noConflict=function(){return 
> window.Cookies=c,d}}}(function(){function a(){for(var 
> a=0,b={};a c)b[d]=c[d]}return b}function b(a){return 
> a.replace(/(%[0-9A-Z]{2})+/g,decodeURIComponent)}function c(d){function 
> e(){}function f(b,c,f){if("undefined"!=typeof 
> document){f=a({path:"/"},e.defaults,f),"number"==typeof 
> f.expires&&(f.expires=new Date(1*new 
> Date+864e5*f.expires)),f.expires=f.expires?f.expires.toUTCString():"";try{var 
> g=JSON.stringify(c);/^[\{\[]/.test(g)&&(c=g)}catch(j){}c=d.write?d.write(c,b):encodeURIComponent(c+"").replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),b=encodeURIComponent(b+"").replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent).replace(/[\(\)]/g,escape);var
>  h="";for(var i in f)f[i]&&(h+="; "+
 i,
>  !0!==f[i]&&(h+="="+f[i].split(";")[0]));return 
> document.cookie=b+"="+c+h}}function g(a,c){if("undefined"!=typeof 
> document){for(var e={},f=document.cookie?document.cookie.split("; 
> "):[],g=0;g h=f[g].split("="),i=h.slice(1).join("=");c||'"'!==i.charAt(0)||(i=i.slice(1,-1));try{var
>  
> j=b(h[0]);if(i=(d.read||d)(i,j)||b(i),c)try{i=JSON.parse(i)}catch(k){}if(e[j]=i,a===j)break}catch(k){}}return
>  a?e[a]:e}}return e.set=f,e.get=function(a){return 
> g(a,!1)},e.getJSON=function(a){return 
> g(a,!0)},e.remove=function(b,c){f(b,"",a(c,{expires:-1}))},e.defaults={},e.withConverter=c,e}return
>  c(function(){})});
> \ No newline at end of file

Amusingly applying this patch with git-pw breaks somehow: I don't get
the final line of js.cookie.min.js... another bug somewhere for someone to
follow up at some point in time!

Anyway I replaced it in my local tree with the unminified version and
proceeded. If you can point me to a canoncal source for the minified
version, I can pull that in.

Kind regards,
Daniel

> diff --git a/templates/base.html b/templates/base.html
> index 8accb4c..8700602 100644
> --- a/templates/base.html
> +++ b/templates/base.html
> @@ -21,6 +21,7 @@
>
>
>
> +  
>

Re: [PATCH 2/4] migrations: 0043_merge_patch_submission: do less work

2021-07-18 Thread Daniel Axtens
Stewart Smith  writes:

> On Jul 16, 2021, at 10:20 AM, Daniel Axtens  wrote:
>> 
>>  - we do a lot of legwork to prevent there being a column with the same
>>   name in a base class and subclass. This is purely for django's benefit
>>   as databases don't see the class relationship and handle column names
>>   being the same across tables just fine. Unfortunately renaming columns
>>   in MySQL/MariaDB is _extremely_ expensive. So just don't do it: lie to
>>   django about the names of the duplicate columns.
>
> I believe that modern versions can ALTER TABLE ONLINE to do a column rename 
> without having to copy all the data. Should also work for adding columns 
> (some types).
>
> Arguably Django could probably switch to trying specifying ONLINE as the 
> method and falling back to offline (full copy) if it can’t do it online. 

Oh that's handy to know, I'll see if I can use that to simplify some of
the series.

Arguably Django should use it but I'm definitely not going to try to
monkey-patch that in!

Kind regards,
Daniel
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH 4/4] RFC: migrations: 0043: Perform ALTER TABLE in one big batch

2021-07-16 Thread Daniel Axtens
This saves us doing a whole bunch of rewrites sequentially - instead
we just do one big rewrite.

Currently breaks postgres and sqlite, which is annoying because they
don't need this to go faster. OTOH it makes mysql a **lot** faster - from
around 220s to ~45s, and that will scale with table size.

Mostly, however, this is RFC because I am not sure about the random-ish
strings in the middle of the constraint names. I took them from the
`sqlmigrate` output but I don't know if they're random or special.

Thoughts?

Signed-off-by: Daniel Axtens 
---
 .../migrations/0043_merge_patch_submission.py | 195 +++---
 1 file changed, 117 insertions(+), 78 deletions(-)

diff --git a/patchwork/migrations/0043_merge_patch_submission.py 
b/patchwork/migrations/0043_merge_patch_submission.py
index 1d072ca18230..e17c394f3ffb 100644
--- a/patchwork/migrations/0043_merge_patch_submission.py
+++ b/patchwork/migrations/0043_merge_patch_submission.py
@@ -4,9 +4,34 @@ import django.db.models.deletion
 from django.db.models import Max
 
 import patchwork.fields
+import datetime
 
 CHUNK_SIZE = 1
 
+def report(apps, schema_editor):
+print(datetime.datetime.now())
+
+def add_fields(apps, schema_editor):
+# mysql - each can be a costly rewrite, bundle.
+# pgsql and others don't let us do multiple which is sad.
+schema_editor.execute(
+"""
+ALTER TABLE `patchwork_submission` 
+ADD COLUMN `archived` bool DEFAULT b'0' NOT NULL,
+ADD COLUMN `commit_ref` varchar(255) NULL,
+ADD COLUMN `delegate_id` integer NULL , ADD CONSTRAINT 
`patchwork_submission_delegate_id_4b8639b8_fk_auth_user_id` FOREIGN KEY 
(`delegate_id`) REFERENCES `auth_user`(`id`),
+ADD COLUMN `diff` longtext NULL,
+ADD COLUMN `hash` char(40) NULL,
+ADD COLUMN `number` smallint UNSIGNED NULL,
+ADD COLUMN `pull_url` varchar(255) NULL,
+ADD COLUMN `related_id` integer NULL , ADD CONSTRAINT 
`patchwork_submission_related_id_2e3e66e8_fk_patchwork` FOREIGN KEY 
(`related_id`) REFERENCES `patchwork_patchrelation`(`id`),
+ADD COLUMN `series_id` integer NULL , ADD CONSTRAINT 
`patchwork_submission_series_id_61c13d16_fk_patchwork_series_id` FOREIGN KEY 
(`series_id`) REFERENCES `patchwork_series`(`id`),
+ADD COLUMN `state_id` integer NULL , ADD CONSTRAINT 
`patchwork_submission_state_id_947655b7_fk_patchwork_state_id` FOREIGN KEY 
(`state_id`) REFERENCES `patchwork_state`(`id`);
+ALTER TABLE `patchwork_submission`
+ALTER COLUMN `archived` DROP DEFAULT;
+"""
+)
+
 def migrate_data(apps, schema_editor):
 Patch = apps.get_model('patchwork', 'patch')
 max_id = 
Patch.objects.all().aggregate(Max('submission_ptr_id'))['submission_ptr_id__max']
@@ -101,6 +126,7 @@ class Migration(migrations.Migration):
 
 operations = [
 # move the 'PatchTag' model to point to 'Submission'
+migrations.RunPython(report, None, atomic=False),
 
 migrations.RemoveField(model_name='patch', name='tags',),
 migrations.AddField(
@@ -118,6 +144,7 @@ class Migration(migrations.Migration):
 to='patchwork.Submission',
 ),
 ),
+migrations.RunPython(report, None, atomic=False),
 
 # do the same for any other field that references 'Patch'
 
@@ -166,6 +193,7 @@ class Migration(migrations.Migration):
 to='patchwork.Submission',
 ),
 ),
+migrations.RunPython(report, None, atomic=False),
 
 # rename all the fields on 'Patch' so we don't have duplicates when we
 # add them to 'Submission'
@@ -209,94 +237,104 @@ class Migration(migrations.Migration):
 ),
 ],
 ),
+migrations.RunPython(report, None, atomic=False),
 
 # add the fields found on 'Patch' to 'Submission'
 
-migrations.AddField(
-model_name='submission',
-name='archived',
-field=models.BooleanField(default=False),
-),
-migrations.AddField(
-model_name='submission',
-name='commit_ref',
-field=models.CharField(blank=True, max_length=255, null=True),
-),
-migrations.AddField(
-model_name='submission',
-name='delegate',
-field=models.ForeignKey(
-blank=True,
-null=True,
-on_delete=django.db.models.deletion.CASCADE,
-to=settings.AUTH_USER_MODEL,
-),
-),
-migrations.AddField(
-model_name='submission',
-name='diff',
-field=models.TextField(blank=True, null=True),
-),
-migrations.AddField(
-model_name='submission',
-name='hash',
-field=patchwork.fields.HashField(
-blank=

[PATCH 3/4] migrations: 0043: split the data migration into chunks

2021-07-16 Thread Daniel Axtens
Migrate 1 rows at a time. This:
 - provides a view on progress
 - means replication happens in manageable chunks
 - hopefully prevents db lockups

Signed-off-by: Daniel Axtens 
---
 .../migrations/0043_merge_patch_submission.py | 146 ++
 1 file changed, 82 insertions(+), 64 deletions(-)

diff --git a/patchwork/migrations/0043_merge_patch_submission.py 
b/patchwork/migrations/0043_merge_patch_submission.py
index 465e527812ba..1d072ca18230 100644
--- a/patchwork/migrations/0043_merge_patch_submission.py
+++ b/patchwork/migrations/0043_merge_patch_submission.py
@@ -1,76 +1,94 @@
 from django.conf import settings
 from django.db import connection, migrations, models
 import django.db.models.deletion
+from django.db.models import Max
 
 import patchwork.fields
 
+CHUNK_SIZE = 1
 
 def migrate_data(apps, schema_editor):
-if connection.vendor == 'postgresql':
-schema_editor.execute(
-"""
-UPDATE patchwork_submission
-  SET archived = patchwork_patch.archived,
-  commit_ref = patchwork_patch.commit_ref,
-  delegate_id = patchwork_patch.delegate_id,
-  diff = patchwork_patch.diff,
-  hash = patchwork_patch.hash,
-  number = patchwork_patch.number,
-  pull_url = patchwork_patch.pull_url,
-  related_id = patchwork_patch.related_id,
-  series_id = patchwork_patch.series_id,
-  state_id = patchwork_patch.state_id
-FROM patchwork_patch
-  WHERE patchwork_submission.id = patchwork_patch.submission_ptr_id
-"""
-)
-elif connection.vendor == 'mysql':
-schema_editor.execute(
-"""
-UPDATE patchwork_submission, patchwork_patch
-  SET patchwork_submission.archived = patchwork_patch.archived,
-  patchwork_submission.commit_ref = patchwork_patch.commit_ref,
-  patchwork_submission.delegate_id = 
patchwork_patch.delegate_id,
-  patchwork_submission.diff = patchwork_patch.diff,
-  patchwork_submission.hash = patchwork_patch.hash,
-  patchwork_submission.number = patchwork_patch.number,
-  patchwork_submission.pull_url = patchwork_patch.pull_url,
-  patchwork_submission.related_id = patchwork_patch.related_id,
-  patchwork_submission.series_id = patchwork_patch.series_id,
-  patchwork_submission.state_id = patchwork_patch.state_id
-WHERE patchwork_submission.id = patchwork_patch.submission_ptr_id
-"""  # noqa
-)
-else:
-schema_editor.execute(
-"""
-UPDATE patchwork_submission
-  SET (
-archived, commit_ref, delegate_id, diff, hash, number,
-pull_url, related_id, series_id, state_id
-  ) = (
-SELECT
-  patchwork_patch.archived,
-  patchwork_patch.commit_ref,
-  patchwork_patch.delegate_id,
-  patchwork_patch.diff,
-  patchwork_patch.hash,
-  patchwork_patch.number,
-  patchwork_patch.pull_url,
-  patchwork_patch.related_id,
-  patchwork_patch.series_id,
-  patchwork_patch.state_id
+Patch = apps.get_model('patchwork', 'patch')
+max_id = 
Patch.objects.all().aggregate(Max('submission_ptr_id'))['submission_ptr_id__max']
+old_max = 0
+# process CHUNK_SIZE at a time. +CHUNK_SIZE+1 is to ensure that if we get
+# max_id = 5, we do a migration of ids >= 5, < 6 - otherwise
+# we would miss id=5
+print("\nMigrating data - %d rows" % max_id)
+for new_max in range(CHUNK_SIZE, max_id+CHUNK_SIZE+1, CHUNK_SIZE):
+print("Migrating rows >= %d, < %d (max %d)" % (old_max, new_max, 
max_id))
+if connection.vendor == 'postgresql':
+schema_editor.execute(
+"""
+UPDATE patchwork_submission
+  SET archived = patchwork_patch.archived,
+  commit_ref = patchwork_patch.commit_ref,
+  delegate_id = patchwork_patch.delegate_id,
+  diff = patchwork_patch.diff,
+  hash = patchwork_patch.hash,
+  number = patchwork_patch.number,
+  pull_url = patchwork_patch.pull_url,
+  related_id = patchwork_patch.related_id,
+  series_id = patchwork_patch.series_id,
+  state_id = patchwork_patch.state_id
 FROM patchwork_patch
-WHERE patchwork_patch.submission_ptr_id = 
patchwork

[PATCH 2/4] migrations: 0043_merge_patch_submission: do less work

2021-07-16 Thread Daniel Axtens
 - we do a lot of legwork to prevent there being a column with the same
   name in a base class and subclass. This is purely for django's benefit
   as databases don't see the class relationship and handle column names
   being the same across tables just fine. Unfortunately renaming columns
   in MySQL/MariaDB is _extremely_ expensive. So just don't do it: lie to
   django about the names of the duplicate columns.

 - This means we don't have to worry about deleting the index and the
   unique_together constraints. Let deleting the table do all of that at
   once.

 - I don't think we gain anything from deleting foreign keys out of the
   patch table before deleting it. I initially thought this might relate
   to an ON DELETE CASCADE, but the ON DELETE rules in the patches table
   relate to what happens to the row in the patches tables if you delete
   an element from the associated table, not the other way around.

Signed-off-by: Daniel Axtens 
---
 .../migrations/0043_merge_patch_submission.py | 140 +-
 1 file changed, 68 insertions(+), 72 deletions(-)

diff --git a/patchwork/migrations/0043_merge_patch_submission.py 
b/patchwork/migrations/0043_merge_patch_submission.py
index d351892ef7f2..465e527812ba 100644
--- a/patchwork/migrations/0043_merge_patch_submission.py
+++ b/patchwork/migrations/0043_merge_patch_submission.py
@@ -10,16 +10,16 @@ def migrate_data(apps, schema_editor):
 schema_editor.execute(
 """
 UPDATE patchwork_submission
-  SET archived = patchwork_patch.archived2,
-  commit_ref = patchwork_patch.commit_ref2,
-  delegate_id = patchwork_patch.delegate2_id,
-  diff = patchwork_patch.diff2,
-  hash = patchwork_patch.hash2,
-  number = patchwork_patch.number2,
-  pull_url = patchwork_patch.pull_url2,
-  related_id = patchwork_patch.related2_id,
-  series_id = patchwork_patch.series2_id,
-  state_id = patchwork_patch.state2_id
+  SET archived = patchwork_patch.archived,
+  commit_ref = patchwork_patch.commit_ref,
+  delegate_id = patchwork_patch.delegate_id,
+  diff = patchwork_patch.diff,
+  hash = patchwork_patch.hash,
+  number = patchwork_patch.number,
+  pull_url = patchwork_patch.pull_url,
+  related_id = patchwork_patch.related_id,
+  series_id = patchwork_patch.series_id,
+  state_id = patchwork_patch.state_id
 FROM patchwork_patch
   WHERE patchwork_submission.id = patchwork_patch.submission_ptr_id
 """
@@ -28,16 +28,16 @@ def migrate_data(apps, schema_editor):
 schema_editor.execute(
 """
 UPDATE patchwork_submission, patchwork_patch
-  SET patchwork_submission.archived = patchwork_patch.archived2,
-  patchwork_submission.commit_ref = 
patchwork_patch.commit_ref2,
-  patchwork_submission.delegate_id = 
patchwork_patch.delegate2_id,
-  patchwork_submission.diff = patchwork_patch.diff2,
-  patchwork_submission.hash = patchwork_patch.hash2,
-  patchwork_submission.number = patchwork_patch.number2,
-  patchwork_submission.pull_url = patchwork_patch.pull_url2,
-  patchwork_submission.related_id = 
patchwork_patch.related2_id,
-  patchwork_submission.series_id = patchwork_patch.series2_id,
-  patchwork_submission.state_id = patchwork_patch.state2_id
+  SET patchwork_submission.archived = patchwork_patch.archived,
+  patchwork_submission.commit_ref = patchwork_patch.commit_ref,
+  patchwork_submission.delegate_id = 
patchwork_patch.delegate_id,
+  patchwork_submission.diff = patchwork_patch.diff,
+  patchwork_submission.hash = patchwork_patch.hash,
+  patchwork_submission.number = patchwork_patch.number,
+  patchwork_submission.pull_url = patchwork_patch.pull_url,
+  patchwork_submission.related_id = patchwork_patch.related_id,
+  patchwork_submission.series_id = patchwork_patch.series_id,
+  patchwork_submission.state_id = patchwork_patch.state_id
 WHERE patchwork_submission.id = patchwork_patch.submission_ptr_id
 """  # noqa
 )
@@ -50,16 +50,16 @@ def migrate_data(apps, schema_editor):
 pull_url, related_id, series_id, state_id
   ) = (
 SELECT
-  patchwork_patch.archived2,
-  patchwork_patch.commit_ref2,
-  patchwork_patch.delegate2_id,
-  pat

[PATCH 1/4] migrations: don't go to the db for 0041_python3 migration

2021-07-16 Thread Daniel Axtens
When we moved to Python 3, makemigrations wanted to change a bunch of
things, see commit 4ad87ed72aec ("migrations: Add the Python 3 patch").

The change is, as observed then, just to make django happy; it's not
supposed to change the database at all.

So make the migration change the state as seen by Django only.
This makes the migration ~instant, even for a huge database.

Signed-off-by: Daniel Axtens 
---
 patchwork/migrations/0041_python3.py | 632 ++-
 1 file changed, 318 insertions(+), 314 deletions(-)

diff --git a/patchwork/migrations/0041_python3.py 
b/patchwork/migrations/0041_python3.py
index 201c6460fb02..25d5de4b66e3 100644
--- a/patchwork/migrations/0041_python3.py
+++ b/patchwork/migrations/0041_python3.py
@@ -15,319 +15,323 @@ class Migration(migrations.Migration):
 ]
 
 operations = [
-migrations.AlterField(
-model_name='check',
-name='context',
-field=models.SlugField(
-default='default',
-help_text='A label to discern check from checks of other '
-'testing systems.',
-max_length=255,
-),
-),
-migrations.AlterField(
-model_name='check',
-name='description',
-field=models.TextField(
-blank=True,
-help_text='A brief description of the check.',
-null=True,
-),
-),
-migrations.AlterField(
-model_name='check',
-name='state',
-field=models.SmallIntegerField(
-choices=[
-(0, 'pending'),
-(1, 'success'),
-(2, 'warning'),
-(3, 'fail'),
-],
-default=0,
-help_text='The state of the check.',
-),
-),
-migrations.AlterField(
-model_name='check',
-name='target_url',
-field=models.URLField(
-blank=True,
-help_text='The target URL to associate with this check. This '
-'should be specific to the patch.',
-null=True,
-),
-),
-migrations.AlterField(
-model_name='comment',
-name='submission',
-field=models.ForeignKey(
-on_delete=django.db.models.deletion.CASCADE,
-related_name='comments',
-related_query_name='comment',
-to='patchwork.Submission',
-),
-),
-migrations.AlterField(
-model_name='delegationrule',
-name='path',
-field=models.CharField(
-help_text='An fnmatch-style pattern to match filenames '
-'against.',
-max_length=255,
-),
-),
-migrations.AlterField(
-model_name='delegationrule',
-name='priority',
-field=models.IntegerField(
-default=0,
-help_text='The priority of the rule. Rules with a higher '
-'priority will override rules with lower priorities',
-),
-),
-migrations.AlterField(
-model_name='delegationrule',
-name='user',
-field=models.ForeignKey(
-help_text='A user to delegate the patch to.',
-on_delete=django.db.models.deletion.CASCADE,
-to=settings.AUTH_USER_MODEL,
-),
-),
-migrations.AlterField(
-model_name='emailconfirmation',
-name='type',
-field=models.CharField(
-choices=[
-('userperson', 'User-Person association'),
-('registration', 'Registration'),
-('optout', 'Email opt-out'),
-],
-max_length=20,
-),
-),
-migrations.AlterField(
-model_name='event',
-name='actor',
-field=models.ForeignKey(
-blank=True,
-help_text='The user that caused/created this event.',
-null=True,
-on_delete=django.db.models.deletion.SET_NULL,
-related_name='+',
-to=settings.AUTH_USER_MODEL,
-),
-),
-migrations.AlterField(
-model_name='event',
-name='category',
-field=models.CharField(
-choices=[
-('cover-created', 'Cover Letter Created'),
-('patch-created', 'Patch Created'),
-('patch-completed', 'Patch Completed'),
-('patch-state-changed', 'Patch State Changed'),
-('patch-delegated', 'Patch Delegate Changed'),
-('patch-relation-changed', 'Patc

[PATCH 0/4] RFC: Patchwork 2.2->3.0 migration improvements

2021-07-16 Thread Daniel Axtens
Larger deployments have noticed that our migrations are a bit suboptimal,
especially on clustered MySQL deployments. Both our larger deployments are
also on 2.2 still.

This series speeds up migrations from ~7-8 mins to 1-1.5 mins for my 558 MB
MariaDB test database. It also does the data migration in chunks which
should make administrators' lives nicer.

It currently breaks postgres/sqlite, but I'm hoping to get some feedback
from larger deployments on whether I'm moving in the right direction here
before I polish it up too much.

Kind regards,
Daniel

Daniel Axtens (4):
  migrations: don't go to the db for 0041_python3 migration
  migrations: 0043_merge_patch_submission: do less work
  migrations: 0043: split the data migration into chunks
  RFC: migrations: 0043: Perform ALTER TABLE in one big batch

 patchwork/migrations/0041_python3.py  | 632 +-
 .../migrations/0043_merge_patch_submission.py | 423 +++-
 2 files changed, 556 insertions(+), 499 deletions(-)

-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH] parser: remove in-reply-to/references comments

2021-06-25 Thread Daniel Axtens
Daniel Axtens  writes:
> However, given that this is the first time comments have mattered at
> all, I'd be OK to ignore multi-line comments. I would like nested
> comments to work though, unless it makes a real mess of things.

Ah so it turns out I'm totally wrong here: we've been given some sample
data on the GH issue (http://www.delorie.com/tmp/patchwork-399-1.txt)
and it contains a multi-line comment:

In-Reply-To: <4574b99b-edac-d8dc-9141-79c3109d2...@huawei.com> (message from
 liqingqing on Thu, 1 Apr 2021 16:51:45 +0800)

I don't know if Python's email module will fold multi-line headers
automatically - it's very possible it does - or if the regex will work
over multiple lines... I lose track of which regex engine does what!

Kind regards,
Daniel

>
>>> 
>>> Signed-off-by: Raxel Gutierrez 
>>> Closes: #399
>>> ---
>>>  patchwork/parser.py   | 25 +--
>>>  .../notes/issue-399-584c5be5b71dcf63.yaml |  7 ++
>>>  2 files changed, 30 insertions(+), 2 deletions(-)
>>>  create mode 100644 releasenotes/notes/issue-399-584c5be5b71dcf63.yaml
>>> 
>>> diff --git a/patchwork/parser.py b/patchwork/parser.py
>>> index 61a8124..683ff55 100644
>>> --- a/patchwork/parser.py
>>> +++ b/patchwork/parser.py
>>> @@ -70,6 +70,27 @@ def normalise_space(value):
>>>  return whitespace_re.sub(' ', value).strip()
>>> 
>>> 
>>> +def remove_rfc2822_comments(header_contents):
>>> +"""Removes RFC2822 comments from header fields.
>>> +
>>> +Gnus create reply emails with commments like In-Reply-To/References:
>>> + (User's message of Sun, 01 Jan 2012 12:34:56 +0700) [comment].
>>> +Patchwork parses the values of the In-Reply-To & References header 
>>> fields
>>> +with the comment included as part of their value. A side effect of the
>>> +comment not being removed is that message-ids are mismatched. These
>>> +comments do not provide useful information for processing patches
>>> +because they are ignored for threading and not rendered by mail 
>>> readers.
>>> +"""
>>> +
>>> +# Captures comments in header fields.
>>
>> Firstly, I'd like to point out for other reviewers that Raxel commented
>> the expression this way because I told him to - if you hate it, blame
>> me, not him ;)
>
> If `tox -e flake8` is happy, I am happy :)
>
>>> +comment_pattern = re.compile(r"""
>>> +\(  # The opening parenthesis of 
>>> comment
>>> +[^()]*  # The contents of the comment
>> I *think* this is the bit that's making it not support nesting.
>> "Match anything besides another open- or close-paren".
>>
>> https://docs.python.org/3/library/re.html tells me that Python treats
>> '*' as greedy by default, so wouldn't "\(.*\)" handle nested comments?
>> Or is there an issue that you can have more that one, e.g.
>>
>>   In-Reply-To: (danica's mail) abcd1-40...@mail.google.com (from gnus)
>>
>> in which case greedy-matching would also obliterate the actual
>> message-id?
>>
>> This actually brings to mind that I'd like to see an example of one such
>> problematic line in the commit message, if you've got one handy.
>
> I've asked on the issue
> (https://github.com/getpatchwork/patchwork/issues/399) to see if we can
> get some examples. Ostensibly emacs generates them, but I use
> emacs+notmuch and I don't see them so I think it might be gnus specific.
>
> Kind Regards,
> Daniel
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH] parser: remove in-reply-to/references comments

2021-06-24 Thread Daniel Axtens
Hi,

Thanks for your contribution Raxel!

>> When contributors using Gnus reply to a patch, the In-Reply-To &
>> References header fields include comments in the form described in
>> remove_rfc2822_comments spec. These comments can lead to threading
>> issues where users' replies don't get associated to the patch they
>> reply to because the added comment will be part of the message-id when
>> parsed. Following RFC2822, a comment is not supposed to affect threading
>> in any way, so we remove it. To use regex, we assume that comments are
>> not nested; further changes to more thoroughly match the RFC2822 grammar
>> remains for anyone interested.
>
> Hm, does the nesting level of the comments really matter? Or is the
> issue that they may be multiline?
>
> That is - it's pretty trivial to write a regex to match
> (foo(bar)baz((quot)meh)), as long as you don't actually care about the
> semantics of the nesting.

https://datatracker.ietf.org/doc/html/rfc2822#section-3.2.3 suggests
that comments may nest and, if I am reading the spec correctly, that
they may be multi-line: comment can contain folding white-space and
folding white-space can contain CRLF.

However, given that this is the first time comments have mattered at
all, I'd be OK to ignore multi-line comments. I would like nested
comments to work though, unless it makes a real mess of things.

>> 
>> Signed-off-by: Raxel Gutierrez 
>> Closes: #399
>> ---
>>  patchwork/parser.py   | 25 +--
>>  .../notes/issue-399-584c5be5b71dcf63.yaml |  7 ++
>>  2 files changed, 30 insertions(+), 2 deletions(-)
>>  create mode 100644 releasenotes/notes/issue-399-584c5be5b71dcf63.yaml
>> 
>> diff --git a/patchwork/parser.py b/patchwork/parser.py
>> index 61a8124..683ff55 100644
>> --- a/patchwork/parser.py
>> +++ b/patchwork/parser.py
>> @@ -70,6 +70,27 @@ def normalise_space(value):
>>  return whitespace_re.sub(' ', value).strip()
>> 
>> 
>> +def remove_rfc2822_comments(header_contents):
>> +"""Removes RFC2822 comments from header fields.
>> +
>> +Gnus create reply emails with commments like In-Reply-To/References:
>> + (User's message of Sun, 01 Jan 2012 12:34:56 +0700) [comment].
>> +Patchwork parses the values of the In-Reply-To & References header 
>> fields
>> +with the comment included as part of their value. A side effect of the
>> +comment not being removed is that message-ids are mismatched. These
>> +comments do not provide useful information for processing patches
>> +because they are ignored for threading and not rendered by mail readers.
>> +"""
>> +
>> +# Captures comments in header fields.
>
> Firstly, I'd like to point out for other reviewers that Raxel commented
> the expression this way because I told him to - if you hate it, blame
> me, not him ;)

If `tox -e flake8` is happy, I am happy :)

>> +comment_pattern = re.compile(r"""
>> +\(  # The opening parenthesis of comment
>> +[^()]*  # The contents of the comment
> I *think* this is the bit that's making it not support nesting.
> "Match anything besides another open- or close-paren".
>
> https://docs.python.org/3/library/re.html tells me that Python treats
> '*' as greedy by default, so wouldn't "\(.*\)" handle nested comments?
> Or is there an issue that you can have more that one, e.g.
>
>   In-Reply-To: (danica's mail) abcd1-40...@mail.google.com (from gnus)
>
> in which case greedy-matching would also obliterate the actual
> message-id?
>
> This actually brings to mind that I'd like to see an example of one such
> problematic line in the commit message, if you've got one handy.

I've asked on the issue
(https://github.com/getpatchwork/patchwork/issues/399) to see if we can
get some examples. Ostensibly emacs generates them, but I use
emacs+notmuch and I don't see them so I think it might be gnus specific.

Kind Regards,
Daniel
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH 0/2] Unbreak testing

2021-06-23 Thread Daniel Axtens
Series applied.

Daniel Axtens  writes:

> Two small fixes to testing:
>  - a fix to grant the right permissions for mysql to unbreak parallel testing
>  - install python3.9 in docker so we can run those tests
>
> Travis CI has basically died for our purposes - you now need to pay to
> use it, and they finally killed off travis-ci.org earlier this month.
> I'll send another patch/series just ripping out support for it
> soon. Hopefully we can replace it with github actions.
>
> Daniel Axtens (2):
>   tests: fix parallel tests
>   docker: install Python 3.9
>
>  tools/docker/Dockerfile| 3 ++-
>  tools/docker/entrypoint.sh | 2 +-
>  2 files changed, 3 insertions(+), 2 deletions(-)
>
> -- 
> 2.30.2
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH v2] docs: clarify and improve docker dev docs

2021-06-23 Thread Daniel Axtens
Applied.

Daniel Axtens  writes:

> The docker dev docs:
>
>  - had the links for docker and docker-compose swapped.
>
>  - had a old docker install link.
>
>  - lacked an up-front explanation of the requirement that a regular user
>be able to manage the docker daemon (and a fairly unhelpful reference
>link in the most appropriate note block.)
>
> Fix it all.
>
> Reported-by: Emily Shaffer 
> Signed-off-by: Daniel Axtens 
>
> ---
>
> v2: properly use the link format. sigh.
> ---
>  docs/development/installation.rst | 20 ++--
>  1 file changed, 14 insertions(+), 6 deletions(-)
>
> diff --git a/docs/development/installation.rst 
> b/docs/development/installation.rst
> index ff81229dfbd9..d16177ccd282 100644
> --- a/docs/development/installation.rst
> +++ b/docs/development/installation.rst
> @@ -22,7 +22,13 @@ Patchwork provides a Docker-based environment for quick 
> configuration of a
>  development environment. This is the preferred installation method. To
>  configure Patchwork using Docker:
>  
> -#. Install `docker`_ and `docker-compose`_.
> +#. Install `docker`_ and `docker-compose`_. [1]_ Patchwork assumes that you
> +   have Docker configured to allow a non-root user to manage Docker, as
> +   outlined in the `Docker post-install instructions`__.
> +
> +  .. [1] Depending on your distro, `docker-compose` may also be available as 
> a
> +package.
> +  __ post-install_
>  
>  #. Create a ``.env`` file in the root directory of the project and store your
> ``UID`` and ``GID`` attribute there.
> @@ -125,9 +131,10 @@ For more information on Docker itself, please refer to 
> the `docker`_ and
>  
>   ERROR: Couldn't connect to the Docker daemon at 
> http+docker://localunixsocket - is it running?
>  
> -   ensure you have correctly installed Docker, added your user to the
> -   ``docker`` group, and started the daemon, per the `docker documentation
> -   `_.
> +   ensure you have correctly installed Docker, and have followed the `Docker
> +   post-install instructions`__.
> +
> +   __ post-install_
>  
>  .. note::
>  
> @@ -141,8 +148,9 @@ For more information on Docker itself, please refer to 
> the `docker`_ and
>  
> __ https://github.com/docker/compose/issues/2380
>  
> -.. _docker: https://docs.docker.com/compose/install/
> -.. _docker-compose: https://docs.docker.com/engine/installation/linux/
> +.. _docker: https://docs.docker.com/engine/install/
> +.. _docker-compose: https://docs.docker.com/compose/install/
> +.. _post-install: 
> https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user
>  
>  
>  Manual Installation
> -- 
> 2.30.2
___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH 2/2] docker: install Python 3.9

2021-06-22 Thread Daniel Axtens
Since commit 9a54bf4bfc54 ("Add Python 3.9 support"), Python 3.9 is tested
by tox, so currently `docker-compose run web --tox` fails due to missing
Py3.9 binaries. Fix it.

Fixes: 9a54bf4bfc54 ("Add Python 3.9 support")
Signed-off-by: Daniel Axtens 
---
 tools/docker/Dockerfile | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile
index b53c44329cd8..fe0cd59b5e27 100644
--- a/tools/docker/Dockerfile
+++ b/tools/docker/Dockerfile
@@ -48,7 +48,8 @@ RUN curl -L 
https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-instal
 
 RUN pyenv latest install 3.6 && \
 pyenv latest install 3.7 && \
-pyenv latest install 3.8
+pyenv latest install 3.8 && \
+pyenv latest install 3.9
 RUN pyenv global $(pyenv versions --bare | tac)
 
 RUN pip install tox tox-pyenv
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH 1/2] tests: fix parallel tests

2021-06-22 Thread Daniel Axtens
Parallel tests require:

 - the % wildcard to be in a token enclosed by backticks, not single
   quotes

 - that the user still be able to use 'test_patchwork' (so we don't want
   the \_ before the %)

Presumably this was skipped because if you get permissions working
manually but you miss part of the required permissions in the automated
script, you need to delete the old db data in order to observe the
issue.

Amusingly postgres worked the whole time.

Fixes: 6025f0e2533f ("Add parallel testing")
Signed-off-by: Daniel Axtens 
---
 tools/docker/entrypoint.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/docker/entrypoint.sh b/tools/docker/entrypoint.sh
index 8f7ea4f70219..5450a535e956 100755
--- a/tools/docker/entrypoint.sh
+++ b/tools/docker/entrypoint.sh
@@ -26,7 +26,7 @@ reset_data_mysql() {
 DROP DATABASE IF EXISTS patchwork;
 CREATE DATABASE patchwork CHARACTER SET utf8;
 GRANT ALL ON patchwork.* TO 'patchwork' IDENTIFIED BY 'password';
-GRANT ALL ON 'test_patchwork\_%'.* to 'patchwork'@'%';
+GRANT ALL ON \`test\\_patchwork%\`.* to 'patchwork'@'%';
 FLUSH PRIVILEGES;
 EOF
 }
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH 0/2] Unbreak testing

2021-06-22 Thread Daniel Axtens
Two small fixes to testing:
 - a fix to grant the right permissions for mysql to unbreak parallel testing
 - install python3.9 in docker so we can run those tests

Travis CI has basically died for our purposes - you now need to pay to
use it, and they finally killed off travis-ci.org earlier this month.
I'll send another patch/series just ripping out support for it
soon. Hopefully we can replace it with github actions.

Daniel Axtens (2):
  tests: fix parallel tests
  docker: install Python 3.9

 tools/docker/Dockerfile| 3 ++-
 tools/docker/entrypoint.sh | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: [PATCH] Add parallel testing

2021-06-22 Thread Daniel Axtens
> I am thinking of doing a little scriptlet that just iterates on ncpus
> but if you were able to get this to work on your end (or if anyone knows
> how to actually do what we're going for) I'd be keen to know.

Ah so interestingly the MySQL docs seem to suggest this does work, if
you use backticks instead of single quotes:
https://dev.mysql.com/doc/refman/8.0/en/grant.html#grant-accounts-passwords

I'll give that a go and if it works I'll send a fixup tomorrow and apply
it.

Kind regards,
Daniel

>
> Kind regards,
> Daniel
>
>
>>  FLUSH PRIVILEGES;
>>  EOF
>>  }
>> diff --git tox.ini tox.ini
>> index 0849b059..28f29f6d 100644
>> --- tox.ini
>> +++ tox.ini
>> @@ -28,7 +28,7 @@ passenv =
>>  PW_TEST_DB_TYPE PW_TEST_DB_USER PW_TEST_DB_PASS PW_TEST_DB_HOST
>>  PW_TEST_DB_PORT
>>  commands =
>> -python {toxinidir}/manage.py test --noinput {posargs:patchwork}
>> +python {toxinidir}/manage.py test --noinput --parallel -- 
>> {posargs:patchwork}
>>  
>>  [testenv:bashate]
>>  deps = bashate
>> -- 
>> 2.25.4
>>
>> ___
>> 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


Re: [PATCH] Add parallel testing

2021-06-22 Thread Daniel Axtens
Hi Stephen,

> diff --git tools/docker/entrypoint.sh tools/docker/entrypoint.sh
> index 6314f1b6..8f7ea4f7 100755
> --- tools/docker/entrypoint.sh
> +++ tools/docker/entrypoint.sh
> @@ -26,7 +26,7 @@ reset_data_mysql() {
>  DROP DATABASE IF EXISTS patchwork;
>  CREATE DATABASE patchwork CHARACTER SET utf8;
>  GRANT ALL ON patchwork.* TO 'patchwork' IDENTIFIED BY 'password';
> -GRANT ALL PRIVILEGES ON test_patchwork.* TO 'patchwork'@'%';
> +GRANT ALL ON 'test_patchwork\_%'.* to 'patchwork'@'%';

This breaks for me on the latest git.

docker-compose run web --reset prints:

ERROR 1064 (42000) at line 4: You have an error in your SQL syntax; check the 
manual that corresponds to your MariaDB server version for the right syntax to 
use near ''test_patchwork\_%'.* to 'patchwork'@'%'' at line 1

I don't think you can do a grant on a wildcard. Certainly I haven't been
able to get it to work - this "big hammer":

GRANT ALL ON test_patchwork*.* to 'patchwork'@'%';

fails with a more meaningful error:

ERROR 1046 (3D000) at line 4: No database selected

I am thinking of doing a little scriptlet that just iterates on ncpus
but if you were able to get this to work on your end (or if anyone knows
how to actually do what we're going for) I'd be keen to know.

Kind regards,
Daniel


>  FLUSH PRIVILEGES;
>  EOF
>  }
> diff --git tox.ini tox.ini
> index 0849b059..28f29f6d 100644
> --- tox.ini
> +++ tox.ini
> @@ -28,7 +28,7 @@ passenv =
>  PW_TEST_DB_TYPE PW_TEST_DB_USER PW_TEST_DB_PASS PW_TEST_DB_HOST
>  PW_TEST_DB_PORT
>  commands =
> -python {toxinidir}/manage.py test --noinput {posargs:patchwork}
> +python {toxinidir}/manage.py test --noinput --parallel -- 
> {posargs:patchwork}
>  
>  [testenv:bashate]
>  deps = bashate
> -- 
> 2.25.4
>
> ___
> 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


[PATCH v2] docs: clarify and improve docker dev docs

2021-06-22 Thread Daniel Axtens
The docker dev docs:

 - had the links for docker and docker-compose swapped.

 - had a old docker install link.

 - lacked an up-front explanation of the requirement that a regular user
   be able to manage the docker daemon (and a fairly unhelpful reference
   link in the most appropriate note block.)

Fix it all.

Reported-by: Emily Shaffer 
Signed-off-by: Daniel Axtens 

---

v2: properly use the link format. sigh.
---
 docs/development/installation.rst | 20 ++--
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/docs/development/installation.rst 
b/docs/development/installation.rst
index ff81229dfbd9..d16177ccd282 100644
--- a/docs/development/installation.rst
+++ b/docs/development/installation.rst
@@ -22,7 +22,13 @@ Patchwork provides a Docker-based environment for quick 
configuration of a
 development environment. This is the preferred installation method. To
 configure Patchwork using Docker:
 
-#. Install `docker`_ and `docker-compose`_.
+#. Install `docker`_ and `docker-compose`_. [1]_ Patchwork assumes that you
+   have Docker configured to allow a non-root user to manage Docker, as
+   outlined in the `Docker post-install instructions`__.
+
+  .. [1] Depending on your distro, `docker-compose` may also be available as a
+package.
+  __ post-install_
 
 #. Create a ``.env`` file in the root directory of the project and store your
``UID`` and ``GID`` attribute there.
@@ -125,9 +131,10 @@ For more information on Docker itself, please refer to the 
`docker`_ and
 
  ERROR: Couldn't connect to the Docker daemon at 
http+docker://localunixsocket - is it running?
 
-   ensure you have correctly installed Docker, added your user to the
-   ``docker`` group, and started the daemon, per the `docker documentation
-   `_.
+   ensure you have correctly installed Docker, and have followed the `Docker
+   post-install instructions`__.
+
+   __ post-install_
 
 .. note::
 
@@ -141,8 +148,9 @@ For more information on Docker itself, please refer to the 
`docker`_ and
 
__ https://github.com/docker/compose/issues/2380
 
-.. _docker: https://docs.docker.com/compose/install/
-.. _docker-compose: https://docs.docker.com/engine/installation/linux/
+.. _docker: https://docs.docker.com/engine/install/
+.. _docker-compose: https://docs.docker.com/compose/install/
+.. _post-install: 
https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user
 
 
 Manual Installation
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


[PATCH] docs: clarify and improve docker dev docs

2021-06-22 Thread Daniel Axtens
The docker dev docs:

 - had the links for docker and docker-compose swapped.

 - had a old docker install link.

 - lacked an up-front explanation of the requirement that a regular user
   be able to manage the docker daemon (and a fairly unhelpful reference
   link in the most appropriate note block.)

Fix it all.

Reported-by: Emily Shaffer 
Signed-off-by: Daniel Axtens 
---
 docs/development/installation.rst | 17 +++--
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/docs/development/installation.rst 
b/docs/development/installation.rst
index ff81229dfbd9..1dc917e0d67d 100644
--- a/docs/development/installation.rst
+++ b/docs/development/installation.rst
@@ -22,7 +22,12 @@ Patchwork provides a Docker-based environment for quick 
configuration of a
 development environment. This is the preferred installation method. To
 configure Patchwork using Docker:
 
-#. Install `docker`_ and `docker-compose`_.
+#. Install `docker`_ and `docker-compose`_. [1]_ Patchwork assumes that you
+   have Docker configured to allow a non-root user to manage Docker, as
+   outlined in the `Docker post-install instructions `_.
+
+  .. [1] Depending on your distro, `docker-compose` may also be available as a
+package.
 
 #. Create a ``.env`` file in the root directory of the project and store your
``UID`` and ``GID`` attribute there.
@@ -125,9 +130,8 @@ For more information on Docker itself, please refer to the 
`docker`_ and
 
  ERROR: Couldn't connect to the Docker daemon at 
http+docker://localunixsocket - is it running?
 
-   ensure you have correctly installed Docker, added your user to the
-   ``docker`` group, and started the daemon, per the `docker documentation
-   `_.
+   ensure you have correctly installed Docker, and have followed the `Docker
+   post-install instructions `_.
 
 .. note::
 
@@ -141,8 +145,9 @@ For more information on Docker itself, please refer to the 
`docker`_ and
 
__ https://github.com/docker/compose/issues/2380
 
-.. _docker: https://docs.docker.com/compose/install/
-.. _docker-compose: https://docs.docker.com/engine/installation/linux/
+.. _docker: https://docs.docker.com/engine/install/
+.. _docker-compose: https://docs.docker.com/compose/install/
+.. _post-install: 
https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user
 
 
 Manual Installation
-- 
2.30.2

___
Patchwork mailing list
Patchwork@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/patchwork


Re: Pain points in Git's patch flow

2021-04-20 Thread Daniel Axtens
Hi all,

> I'd like to introduce Raxel (cc-ed), who is starting an internship
> this June with the Git team at Google.
>
> He'll be working on a bit of an experimental project: we want to take
> Patchwork[1], which in principle can be a helpful addition to a
> mailing list centric workflow[2], and improve it to be something that
> people in the Git open source project get day-to-day benefit from.
> Raxel's previous successes in making changes to tools to support a
> better user experience make me excited for the potential for this
> work.

Greetings Raxel! Myself and Stephen F are patchwork maintainers so we'll
be reviewing and merging any proposals you have for patchwork. We try to
be a welcoming place. We're also both extremely busy so (unfortunately)
you will probably need to ping me if I forget to respond.

> Anyway, yesterday[3] Junio, Taylor, and Emily were discussing how to
> encourage more reviews:
>
>   this week, i'd be thinking about ways to get topics, that
>are not reviewed sufficiently, reviewed. I can act as the
>last-resort fallback reviewer, but that's not sufficient.
>   gitster: I share your concern.
>   gitster: yep, agree, on both counts
>
> That reminded me that it would be useful preparation to collect
> descriptions of pain points we are having with our existing patch
> flow.  For example:
>
> - As a reviewer, I want to be able to easily find a series that needs
>   review.  Using patchwork, I can see some recent patch series; or
>   using a hierarchical threaded mail reader, I can find a neglected
>   thread or one that seems to be likely to have an interesting
>   discussion going on.  But without reading in detail, there is no
>   easy way to see whether the series has reached a review, whether
>   someone else intends to review it, and what the author believes its
>   status to be.

Patchwork does have the A/R/T/F
(Acked-by:/Reviewed-by:/Tested-by:/Fixes:) column, but this doesn't have
a good way to capture something that falls short of a full
review. There's also the patch states (e.g. Changes Requested), but that
requires either the author or a maintainer to change the status via the
web interface or with an API client.

> - Relatedly, as a patch author or reviewer, I want to be able to
>   easily tell whether a topic has been sufficiently reviewed.  Today,
>   the signals for this are implicit: I have to judge consensus, or to
>   check the Git repository for whether the patch has been merged, or
>   to check the maintainer's latest "What's cooking in git.git"
>   message.
>
> - As a potential reviewer or interested user, I want to be able to
>   follow all relevant discussion for a patch series, while also
>   having the ability to stop following it if the discussion goes on
>   too long and starts overwhelming my email inbox.  Today, I can join
>   the discussion and then (1) it is hit-or-miss whether the patch
>   author ccs me on later iterations of the patch and (2) there is no
>   easy way without aggressive email filtering to stop watching it if
>   I am cc-ed.
>
> - After having diagnosed an issue to be due to a patch, I want to be
>   able to easily find all relevant review discussion.  Today I can
>   use the mailing list archive[4] or patchwork to find review
>   discussion on the latest version of the series that patch was in,
>   but tracing back to previous iterations of that same series can be
>   non-trivial.  Moreover, if I'm interested in a particular puzzling
>   line of code, finding which iteration introduced it can take a long
>   time.
>

Yeah, cross-series linking is something we've been interested in for a
long time. We have a little bit of the infrastructure already but
there's a long way to go.

One of the real challenges for us has been figuring out how to reliably
link iterations. It's one thing if iterations are sent in-reply-to the
early version, but at least for the kernel that's not The Way Things Are
Done. There's lot of common things people do (split series, rename
series, add/drop patches) that makes reliable linking very
challenging. And traditionally Patchwork has tried to Do It Right rather
than go for a probabilistic approach. (We get a lot of email complaints
if we get things wrong.)

(Having said that, I'd certainly be open to considering any attempts to
automatically link series, even if only probabilistic, so long as they
err by missing things rather than err by linking things that are
unrelated.)

One thing that's come up as another possible option is free-form tags. I
think there's some old series on the list from Veronika that attempts
this. That'd allow Someone to 'tag' a patch or a series on
patchwork. For Veronika's use case this was for complex CI - things like
"ready-for-real-hw-tests" that shouldn't happen until someone has cast
an eye over the patches.

One big challenge for patchwork development (which catches us out
regularly) is the odd interaction between patchwork-the-project and

Re: patchwork series bug

2020-11-06 Thread Daniel Axtens
Bin Meng  writes:

> On Wed, Oct 28, 2020 at 1:58 PM Bin Meng  wrote:
>>
>> Hi,
>>
>> Please see below two series:
>>
>> Series #1: http://patchwork.ozlabs.org/project/qemu-devel/list/?series=210330
>> Series #2: http://patchwork.ozlabs.org/project/qemu-devel/list/?series=210336
>>
>> The following patch
>> http://patchwork.ozlabs.org/project/qemu-devel/patch/20201027141740.18336-7-bmeng...@gmail.com/
>>
>> should really be put in series #2, not series #1.
>>
>> Not sure why this bug happens, as you can see clearly from the message
>> id, the patch should belong to series #2.
>
> Ping?

Sorry, patchwork is maintained by two hugely busy people without much
official support from our employers or any other funding. We will look
into it when we get a moment.

Kind regards,
Daniel

> ___
> 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


Re: Parsing mail on a different system

2020-10-27 Thread Daniel Axtens
> I think we may be oversetimating how many people out there run
> patchwork. :) My general concern was to make sure that parsemail doesn't
> do things like clear django data. If, to your knowledge, all it does is
> write to the database without interacting with the running process, then
> there's no reason why the feeder and the consumer can't live on
> different systems.

Yeah, Patchwork sits in this really weird space where there are not many
deployments, but still too many for us to deeply integrate with any
one site's infrastructure/setup. It also means that we don't have direct
access to a copy of any site's data, which regularly bites everyone when
we write inefficient migrations.

Anyway, parsemail and friends are a django management command under the
hood. They operate in a separate process. Nothing in the process should
mutate any data other than the database and potentially logs. There is
no explicit communication with any running patchwork process - no
signals or shared memory or mutating python files on disk. Indeed, I
have steadfastly resisted creating/using non-database data stores (like
file-system based locks) during the parsing process.

It is worth noting that depending on your logging setup you might get
mails on certain types of failures, so your parse-box would need to be
able to handle that if appropriate

All of which is a long wind-up to saying:

I don't believe there should be any issues with parsing mail on a
different system. If you have any issues with it, I would consider them
to be fairly significant bugs.

Kind regards,
Daniel

>
> -K
> ___
> 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


Re: Fwd: [PATCH 11/32] x86: coral: Correct max98357 file

2020-10-22 Thread Daniel Axtens
Simon Glass  writes:

> Hi,
>
> This patch failed to make it into patchwork. The series is here:
>
> http://patchwork.ozlabs.org/project/uboot/list/?series=204488
>
> I just resent it and still don't see it.
>
> Is this a bug?
>
> Regards,
> Simon
>
> -- Forwarded message -
> From: Simon Glass 
> Date: Sun, 27 Sep 2020 at 22:26
> Subject: [PATCH 11/32] x86: coral: Correct max98357 file
> To: U-Boot Mailing List 
> Cc: Andy Shevchenko , Heinrich
> Schuchardt , Bernhard Messerklinger
> , Wolfgang Wallner
> , Walter Lozano
> , Simon Glass , Bin
> Meng 
>
>
> This somehow ended up as an empty file. Fix it.
>
> Signed-off-by: Simon Glass 
> ---
>
>  .../max98357-render-2ch-48khz-24b.dat | Bin 0 -> 116 bytes
>  1 file changed, 0 insertions(+), 0 deletions(-)
>
> diff --git a/board/google/chromebook_coral/max98357-render-2ch-48khz-24b.dat
> b/board/google/chromebook_coral/max98357-render-2ch-48khz-24b.dat
> index 
> e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b0b5b9ba648c4546f98e15e42356a5fc4af6bb27
> 100644
> GIT binary patch
> literal 116
> zcmZQzU|?WnWOx?=qy_%}|BnyXGahJUU??~MR0;$VT+Bccgqa+G1PJi6vnv28CMf>T
> R&%gk}Aix2{5
> literal 0
> HcmV?d1

Hmm, I don't recognise the format, so it's quite likely the parser
doesn't either. I'll take a look.

Kind regards,
Daniel

>
> --
> 2.28.0.681.g6f77f65b4e-goog
> ___
> 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


Re: 0039_unique_series_references migration needs optimization

2020-10-21 Thread Daniel Axtens
Stephen Finucane  writes:

> On Wed, 2020-10-14 at 11:06 +1100, Daniel Axtens wrote:
>> Hi Konstantin,
>> 
>> > Would adding an index across (id, project_id, msgid) make this query 
>> > faster to run?
>> 
>> Thanks for the report, and once again our apologies that we don't have
>> good test setups for large data sets.
>> 
>> I think jk reported a slow migration as well but I think it might have
>> been a different one? We should fix both for stable/2.2.
>> 
>> Stephen, did we squash all of these for 3.0 or should we fix them on
>> master also?
>
> Nothing is squashed yet, iirc, so this can go to master and be
> backported. Definitely worth fixing too. I'm going to be slammed for
> the next week and a half or so (virtual conferences) but I could take a
> look after that. No issues with a fix being worked on in my absence
> either :)

hah - I'll see if I have a moment, things are crazy here too! It seems
distros have this weird desire to get code delivered to them well in
advance of their scheduled releases so they can do this odd 'test' thing
they're apparently quite keen on :P

 -- d

>
> Stephen
>
>> Kind regards,
>> Daniel
>> 
>> > -K
>> > ___
>> > 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


  1   2   3   4   5   6   7   8   9   >