Re: [PATCH] models: Add State.slug field

2019-12-27 Thread Stephen Finucane
On Sun, 2019-12-08 at 16:00 +, Stephen Finucane wrote:
> This reduces a lot of the tech debt we had built up around this. This
> field is populated from a slugified representation of State.name and has
> a uniqueness constraint. As a result, we also need to a uniqueness
> constraint to the State.name field. This shouldn't be an issue and
> probably should have been used from day one.
> 
> Note that there is no REST API impact for this since we've been using
> state slugs all along.
> 
> Signed-off-by: Stephen Finucane 

Applied.

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


Re: [PATCH] models: Add State.slug field

2019-12-10 Thread Andrew Donnellan

On 11/12/19 2:32 am, Stephen Finucane wrote:

I think the migration can still fail without printing a helpful
exception message if two states have names that are different but still
slugify to the same thing?


Good point. I can fix this if we think it's worth it. Personally, I
think the uniqueness check itself is overkill and could probably be
removed.


Agreed, though if we're gonna do it I want it done thoroughly ;)

--
Andrew Donnellan  OzLabs, ADL Canberra
a...@linux.ibm.com IBM Australia Limited

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


Re: [PATCH] models: Add State.slug field

2019-12-10 Thread Stephen Finucane
On Tue, 2019-12-10 at 15:43 +1100, Andrew Donnellan wrote:
> On 9/12/19 3:00 am, Stephen Finucane wrote:
> > --- /dev/null
> > +++ b/patchwork/migrations/0038_state_slug.py
> > @@ -0,0 +1,68 @@
> > +# -*- coding: utf-8 -*-
> > +
> > +from __future__ import unicode_literals
> > +
> > +from django.db import migrations, models, transaction
> > +from django.utils.text import slugify
> > +
> > +
> > +def validate_uniqueness(apps, schema_editor):
> > +"""Ensure all State.name entries are unique.
> > +
> > +We need to do this before enforcing a uniqueness constraint.
> > +"""
> > +
> > +State = apps.get_model('patchwork', 'State')
> > +
> > +unique_count = len(State.objects.order_by().values_list(
> > +'name', flat=True).distinct())
> > +total_count = State.objects.count()
> > +
> > +if unique_count != total_count:
> > +raise Exception(
> > +'You have non-unique States entries that need to be combined '
> > +'before you can run this migration. This migration must be 
> > done '
> > +'by hand. If you need assistance, please contact '
> > +'patchw...@ozlabs.org')
> 
> I think the migration can still fail without printing a helpful 
> exception message if two states have names that are different but still 
> slugify to the same thing?

Good point. I can fix this if we think it's worth it. Personally, I
think the uniqueness check itself is overkill and could probably be
removed.

Stephen

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


Re: [PATCH] models: Add State.slug field

2019-12-09 Thread Andrew Donnellan

On 9/12/19 3:00 am, Stephen Finucane wrote:

--- /dev/null
+++ b/patchwork/migrations/0038_state_slug.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import unicode_literals
+
+from django.db import migrations, models, transaction
+from django.utils.text import slugify
+
+
+def validate_uniqueness(apps, schema_editor):
+"""Ensure all State.name entries are unique.
+
+We need to do this before enforcing a uniqueness constraint.
+"""
+
+State = apps.get_model('patchwork', 'State')
+
+unique_count = len(State.objects.order_by().values_list(
+'name', flat=True).distinct())
+total_count = State.objects.count()
+
+if unique_count != total_count:
+raise Exception(
+'You have non-unique States entries that need to be combined '
+'before you can run this migration. This migration must be done '
+'by hand. If you need assistance, please contact '
+'patchw...@ozlabs.org')


I think the migration can still fail without printing a helpful 
exception message if two states have names that are different but still 
slugify to the same thing?


--
Andrew Donnellan  OzLabs, ADL Canberra
a...@linux.ibm.com IBM Australia Limited

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


[PATCH] models: Add State.slug field

2019-12-08 Thread Stephen Finucane
This reduces a lot of the tech debt we had built up around this. This
field is populated from a slugified representation of State.name and has
a uniqueness constraint. As a result, we also need to a uniqueness
constraint to the State.name field. This shouldn't be an issue and
probably should have been used from day one.

Note that there is no REST API impact for this since we've been using
state slugs all along.

Signed-off-by: Stephen Finucane 
---
 patchwork/api/event.py  |  6 +--
 patchwork/api/patch.py  | 18 ++-
 patchwork/fixtures/default_states.xml   | 10 
 patchwork/migrations/0038_state_slug.py | 68 +
 patchwork/models.py |  8 ++-
 patchwork/tests/api/test_patch.py   |  4 +-
 patchwork/tests/utils.py|  1 +
 7 files changed, 92 insertions(+), 23 deletions(-)
 create mode 100644 patchwork/migrations/0038_state_slug.py

diff --git a/patchwork/api/event.py b/patchwork/api/event.py
index e00ce051..a066faae 100644
--- a/patchwork/api/event.py
+++ b/patchwork/api/event.py
@@ -8,6 +8,7 @@ from collections import OrderedDict
 from rest_framework.generics import ListAPIView
 from rest_framework.serializers import ModelSerializer
 from rest_framework.serializers import SerializerMethodField
+from rest_framework.serializers import SlugRelatedField
 
 from patchwork.api.embedded import CheckSerializer
 from patchwork.api.embedded import CoverLetterSerializer
@@ -16,7 +17,6 @@ from patchwork.api.embedded import ProjectSerializer
 from patchwork.api.embedded import SeriesSerializer
 from patchwork.api.embedded import UserSerializer
 from patchwork.api.filters import EventFilterSet
-from patchwork.api.patch import StateField
 from patchwork.models import Event
 
 
@@ -27,8 +27,8 @@ class EventSerializer(ModelSerializer):
 patch = PatchSerializer(read_only=True)
 series = SeriesSerializer(read_only=True)
 cover = CoverLetterSerializer(read_only=True)
-previous_state = StateField()
-current_state = StateField()
+previous_state = SlugRelatedField(slug_field='slug', read_only=True)
+current_state = SlugRelatedField(slug_field='slug', read_only=True)
 previous_delegate = UserSerializer()
 current_delegate = UserSerializer()
 created_check = SerializerMethodField()
diff --git a/patchwork/api/patch.py b/patchwork/api/patch.py
index d1c9904d..a29a1ab0 100644
--- a/patchwork/api/patch.py
+++ b/patchwork/api/patch.py
@@ -5,6 +5,7 @@
 
 import email.parser
 
+from django.utils.text import slugify
 from django.utils.translation import ugettext_lazy as _
 from rest_framework.generics import ListAPIView
 from rest_framework.generics import RetrieveUpdateAPIView
@@ -28,10 +29,7 @@ from patchwork.parser import clean_subject
 class StateField(RelatedField):
 """Avoid the need for a state endpoint.
 
-NOTE(stephenfin): This field will only function for State names consisting
-of alphanumeric characters, underscores and single spaces. In Patchwork
-2.0+, we should consider adding a slug field to the State object and make
-use of the SlugRelatedField in DRF.
+TODO(stephenfin): Consider switching to SlugRelatedField for the v2.0 API.
 """
 default_error_messages = {
 'required': _('This field is required.'),
@@ -41,19 +39,13 @@ class StateField(RelatedField):
 '{data_type}.'),
 }
 
-@staticmethod
-def format_state_name(state):
-return ' '.join(state.split('-'))
-
 def to_internal_value(self, data):
+data = slugify(data.lower())
 try:
-data = self.format_state_name(data)
-return self.get_queryset().get(name__iexact=data)
+return self.get_queryset().get(slug=data)
 except State.DoesNotExist:
 self.fail('invalid_choice', name=data, choices=', '.join([
-self.format_state_name(x.name) for x in self.get_queryset()]))
-except (TypeError, ValueError):
-self.fail('incorrect_type', data_type=type(data).__name__)
+x.slug for x in self.get_queryset()]))
 
 def to_representation(self, obj):
 return obj.slug
diff --git a/patchwork/fixtures/default_states.xml 
b/patchwork/fixtures/default_states.xml
index 86e1105f..1bbd919d 100644
--- a/patchwork/fixtures/default_states.xml
+++ b/patchwork/fixtures/default_states.xml
@@ -4,51 +4,61 @@
   
   
 New
+new
 0
 True
   
   
 Under Review
+under-review
 1
 True
   
   
 Accepted
+accepted
 2
 False
   
   
 Rejected
+rejected
 3
 False
   
   
 RFC
+rfc
 4
 False
   
   
 Not Applicable
+not-applicable
 5
 False
   
   
 Changes Requested
+changes-requested
 6
 False
   
   
 Awaiting Upstream
+awaiting-upstream
 7
 False
   
   
 Superseded
+superseded
 8
 False
   
   
 Deferred
+deferred