diff mbox

[06/15] series: Add a Series model

Message ID 1444387202-25735-7-git-send-email-damien.lespiau@intel.com
State Superseded
Headers show

Commit Message

Damien Lespiau Oct. 9, 2015, 10:39 a.m. UTC
v2: Add 'order' metadata to revisions as they do have a natural
    ordering.
v3: Add a couple of utility functions to Series and SeriesRevision
v4: Provide a get_absolute_url() method on Series
v5: Add a dump() method to series, handy for debugging

Signed-off-by: Damien Lespiau <damien.lespiau@intel.com>
---
 patchwork/models.py | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

Comments

Stephen Finucane Oct. 9, 2015, 11:21 p.m. UTC | #1
> v2: Add 'order' metadata to revisions as they do have a natural

>     ordering.

> v3: Add a couple of utility functions to Series and SeriesRevision

> v4: Provide a get_absolute_url() method on Series

> v5: Add a dump() method to series, handy for debugging

> 

> Signed-off-by: Damien Lespiau <damien.lespiau@intel.com>


Couple of comments below.

Stephen

> ---

>  patchwork/models.py | 83

> +++++++++++++++++++++++++++++++++++++++++++++++++++++

>  1 file changed, 83 insertions(+)

> 

> diff --git a/patchwork/models.py b/patchwork/models.py

> index a1c2974..68673bf 100644

> --- a/patchwork/models.py

> +++ b/patchwork/models.py

> @@ -410,6 +410,89 @@ class BundlePatch(models.Model):

>          unique_together = [('bundle', 'patch')]

>          ordering = ['order']

> 

> +# This Model represents the "top level" Series, an object that doesn't

> change

> +# with the various versions of patches sent to the mailing list.


This should be a docstring.

> +class Series(models.Model):

> +    project = models.ForeignKey(Project)

> +    name = models.CharField(max_length=200, null=True, blank=False)

> +    submitter = models.ForeignKey(Person, related_name='submitters')

> +    reviewer = models.ForeignKey(User, related_name='reviewers',

> null=True,

> +                                 blank=True)

> +    submitted = models.DateTimeField(default=datetime.datetime.now)


Potential edge case: what happens if a series doesn't have a cover letter (and therefore no email to get the "submitted" time from)?

> +    last_updated = models.DateTimeField(auto_now=True)

> +    # Caches the latest version so we can display it without looking at

> the max

> +    # of all SeriesRevision.version

> +    version = models.IntegerField(default=1)

> +    # This is the number of patches of the latest version.

> +    n_patches = models.IntegerField(default=0)

> +

> +    def __unicode__(self):

> +        return self.name

> +

> +    def revisions(self):

> +        return SeriesRevision.objects.filter(series=self)

> +

> +    def latest_revision(self):

> +        return self.revisions().reverse()[0]


return self.revisions[-1]

> +

> +    def get_absolute_url(self):

> +        return reverse('series', kwargs={ 'series': self.pk })

> +

> +    def dump(self):

> +        print('')

> +        print('===')

> +        print('Series: %s' % self)

> +        print('    version %d' % self.version)

> +        for rev in self.revisions():

> +            print('    rev %d:' % rev.version)

> +            i = 1

> +            for patch in rev.ordered_patches():

> +                print('        patch %d:' % i)

> +                print('            subject: %s' % patch.name)

> +                print('            msgid  : %s' % patch.msgid)

> +                i += 1



No need reinventing the wheel: override the '__str__' (not the '__repr__') function instead

> +

> +# A 'revision' of a series. Resending a new version of a patch or a full

> new

> +# iteration of a series will create a new revision.


Docstring

> +class SeriesRevision(models.Model):

> +    series = models.ForeignKey(Series)

> +    version = models.IntegerField(default=1)

> +    root_msgid = models.CharField(max_length=255)

> +    cover_letter = models.TextField(null = True, blank = True)

> +    patches = models.ManyToManyField(Patch, through =

> 'SeriesRevisionPatch')

> +

> +    class Meta:

> +        unique_together = [('series', 'version')]

> +        ordering = ['version']

> +

> +    def ordered_patches(self):

> +        return self.patches.order_by('seriesrevisionpatch__order')

> +

> +    def add_patch(self, patch, order):

> +        # see if the patch is already in this revision

> +        if SeriesRevisionPatch.objects.filter(revision=self,

> +                                              patch=patch).count():

> +            raise Exception("patch is already in revision")

> +

> +        sp = SeriesRevisionPatch.objects.create(revision=self,

> patch=patch,

> +                                                order=order)

> +        sp.save()

> +

> +    def __unicode__(self):

> +        if hasattr(self, 'series'):

> +            return self.series.name + " (rev " + str(self.version) + ")"

> +        else:

> +            return "New revision" + " (rev " + str(self.version) + ")"


You could stick in the '@python_2_unicode_compatible' decorator on this function and use '__str__' instead - this would make this model Python 3 compatible. Up to you, however.

> +

> +class SeriesRevisionPatch(models.Model):

> +    patch = models.ForeignKey(Patch)

> +    revision = models.ForeignKey(SeriesRevision)

> +    order = models.IntegerField()

> +

> +    class Meta:

> +        unique_together = [('revision', 'patch'), ('revision', 'order')]

> +        ordering = ['order']

> +

>  class EmailConfirmation(models.Model):

>      validity = datetime.timedelta(days =

> settings.CONFIRMATION_VALIDITY_DAYS)

>      type = models.CharField(max_length = 20, choices = [

> --

> 2.1.0

> 

> _______________________________________________

> Patchwork mailing list

> Patchwork@lists.ozlabs.org

> https://lists.ozlabs.org/listinfo/patchwork
Damien Lespiau Oct. 19, 2015, 11:18 a.m. UTC | #2
On Sat, Oct 10, 2015 at 12:21:15AM +0100, Finucane, Stephen wrote:
> > +class Series(models.Model):
> > +    project = models.ForeignKey(Project)
> > +    name = models.CharField(max_length=200, null=True, blank=False)
> > +    submitter = models.ForeignKey(Person, related_name='submitters')
> > +    reviewer = models.ForeignKey(User, related_name='reviewers',
> > null=True,
> > +                                 blank=True)
> > +    submitted = models.DateTimeField(default=datetime.datetime.now)
> 
> Potential edge case: what happens if a series doesn't have a cover
> letter (and therefore no email to get the "submitted" time from)?

submitted is the timestamp of the row creation, disregarding if the
series has a cover letter or not. No email is getting parsed to set that
field.

> > +    last_updated = models.DateTimeField(auto_now=True)
> > +    # Caches the latest version so we can display it without looking at
> > the max
> > +    # of all SeriesRevision.version
> > +    version = models.IntegerField(default=1)
> > +    # This is the number of patches of the latest version.
> > +    n_patches = models.IntegerField(default=0)
> > +
> > +    def __unicode__(self):
> > +        return self.name
> > +
> > +    def revisions(self):
> > +        return SeriesRevision.objects.filter(series=self)
> > +
> > +    def latest_revision(self):
> > +        return self.revisions().reverse()[0]
> 
> return self.revisions[-1]

Django doesn't support that.

> > +
> > +    def get_absolute_url(self):
> > +        return reverse('series', kwargs={ 'series': self.pk })
> > +
> > +    def dump(self):
> > +        print('')
> > +        print('===')
> > +        print('Series: %s' % self)
> > +        print('    version %d' % self.version)
> > +        for rev in self.revisions():
> > +            print('    rev %d:' % rev.version)
> > +            i = 1
> > +            for patch in rev.ordered_patches():
> > +                print('        patch %d:' % i)
> > +                print('            subject: %s' % patch.name)
> > +                print('            msgid  : %s' % patch.msgid)
> > +                i += 1
> 
> 
> No need reinventing the wheel: override the '__str__' (not the '__repr__') function instead

This is for debugging only and contains too much details to be part of  __str__.
Damien Lespiau Oct. 19, 2015, 2:02 p.m. UTC | #3
On Mon, Oct 19, 2015 at 12:18:58PM +0100, Damien Lespiau wrote:
> On Sat, Oct 10, 2015 at 12:21:15AM +0100, Finucane, Stephen wrote:
> > > +class Series(models.Model):
> > > +    project = models.ForeignKey(Project)
> > > +    name = models.CharField(max_length=200, null=True, blank=False)
> > > +    submitter = models.ForeignKey(Person, related_name='submitters')
> > > +    reviewer = models.ForeignKey(User, related_name='reviewers',
> > > null=True,
> > > +                                 blank=True)
> > > +    submitted = models.DateTimeField(default=datetime.datetime.now)
> > 
> > Potential edge case: what happens if a series doesn't have a cover
> > letter (and therefore no email to get the "submitted" time from)?
> 
> submitted is the timestamp of the row creation, disregarding if the
> series has a cover letter or not. No email is getting parsed to set that
> field.

Actually this is not true. I forgot I changed that behaviour :/ So
submitted is taken from the mail headers, each email of the series being
inspected and the earliest timestamp taken.
Stephen Finucane Oct. 20, 2015, 12:19 a.m. UTC | #4
> On Sat, Oct 10, 2015 at 12:21:15AM +0100, Finucane, Stephen wrote:
> > > +class Series(models.Model):
> > > +    project = models.ForeignKey(Project)
> > > +    name = models.CharField(max_length=200, null=True, blank=False)
> > > +    submitter = models.ForeignKey(Person, related_name='submitters')
> > > +    reviewer = models.ForeignKey(User, related_name='reviewers',
> > > null=True,
> > > +                                 blank=True)
> > > +    submitted = models.DateTimeField(default=datetime.datetime.now)
> >
> > Potential edge case: what happens if a series doesn't have a cover
> > letter (and therefore no email to get the "submitted" time from)?
> 
> submitted is the timestamp of the row creation, disregarding if the
> series has a cover letter or not. No email is getting parsed to set that
> field.
> 
> > > +    last_updated = models.DateTimeField(auto_now=True)
> > > +    # Caches the latest version so we can display it without looking
> at
> > > the max
> > > +    # of all SeriesRevision.version
> > > +    version = models.IntegerField(default=1)
> > > +    # This is the number of patches of the latest version.
> > > +    n_patches = models.IntegerField(default=0)
> > > +
> > > +    def __unicode__(self):
> > > +        return self.name
> > > +
> > > +    def revisions(self):
> > > +        return SeriesRevision.objects.filter(series=self)
> > > +
> > > +    def latest_revision(self):
> > > +        return self.revisions().reverse()[0]
> >
> > return self.revisions[-1]
> 
> Django doesn't support that.

Right you are: my mistake.

> > > +
> > > +    def get_absolute_url(self):
> > > +        return reverse('series', kwargs={ 'series': self.pk })
> > > +
> > > +    def dump(self):
> > > +        print('')
> > > +        print('===')
> > > +        print('Series: %s' % self)
> > > +        print('    version %d' % self.version)
> > > +        for rev in self.revisions():
> > > +            print('    rev %d:' % rev.version)
> > > +            i = 1
> > > +            for patch in rev.ordered_patches():
> > > +                print('        patch %d:' % i)
> > > +                print('            subject: %s' % patch.name)
> > > +                print('            msgid  : %s' % patch.msgid)
> > > +                i += 1
> >
> >
> > No need reinventing the wheel: override the '__str__' (not the
> '__repr__') function instead
> 
> This is for debugging only and contains too much details to be part of
> __str__.

Ehh, we could get away with it would be inconsistent with the rest of the '__str__' functions in that file. Given this (and other replies):

Acked-by: Stephen Finucane <stephen.finucane@intel.com.
diff mbox

Patch

diff --git a/patchwork/models.py b/patchwork/models.py
index a1c2974..68673bf 100644
--- a/patchwork/models.py
+++ b/patchwork/models.py
@@ -410,6 +410,89 @@  class BundlePatch(models.Model):
         unique_together = [('bundle', 'patch')]
         ordering = ['order']
 
+# This Model represents the "top level" Series, an object that doesn't change
+# with the various versions of patches sent to the mailing list.
+class Series(models.Model):
+    project = models.ForeignKey(Project)
+    name = models.CharField(max_length=200, null=True, blank=False)
+    submitter = models.ForeignKey(Person, related_name='submitters')
+    reviewer = models.ForeignKey(User, related_name='reviewers', null=True,
+                                 blank=True)
+    submitted = models.DateTimeField(default=datetime.datetime.now)
+    last_updated = models.DateTimeField(auto_now=True)
+    # Caches the latest version so we can display it without looking at the max
+    # of all SeriesRevision.version
+    version = models.IntegerField(default=1)
+    # This is the number of patches of the latest version.
+    n_patches = models.IntegerField(default=0)
+
+    def __unicode__(self):
+        return self.name
+
+    def revisions(self):
+        return SeriesRevision.objects.filter(series=self)
+
+    def latest_revision(self):
+        return self.revisions().reverse()[0]
+
+    def get_absolute_url(self):
+        return reverse('series', kwargs={ 'series': self.pk })
+
+    def dump(self):
+        print('')
+        print('===')
+        print('Series: %s' % self)
+        print('    version %d' % self.version)
+        for rev in self.revisions():
+            print('    rev %d:' % rev.version)
+            i = 1
+            for patch in rev.ordered_patches():
+                print('        patch %d:' % i)
+                print('            subject: %s' % patch.name)
+                print('            msgid  : %s' % patch.msgid)
+                i += 1
+
+# A 'revision' of a series. Resending a new version of a patch or a full new
+# iteration of a series will create a new revision.
+class SeriesRevision(models.Model):
+    series = models.ForeignKey(Series)
+    version = models.IntegerField(default=1)
+    root_msgid = models.CharField(max_length=255)
+    cover_letter = models.TextField(null = True, blank = True)
+    patches = models.ManyToManyField(Patch, through = 'SeriesRevisionPatch')
+
+    class Meta:
+        unique_together = [('series', 'version')]
+        ordering = ['version']
+
+    def ordered_patches(self):
+        return self.patches.order_by('seriesrevisionpatch__order')
+
+    def add_patch(self, patch, order):
+        # see if the patch is already in this revision
+        if SeriesRevisionPatch.objects.filter(revision=self,
+                                              patch=patch).count():
+            raise Exception("patch is already in revision")
+
+        sp = SeriesRevisionPatch.objects.create(revision=self, patch=patch,
+                                                order=order)
+        sp.save()
+
+    def __unicode__(self):
+        if hasattr(self, 'series'):
+            return self.series.name + " (rev " + str(self.version) + ")"
+        else:
+            return "New revision" + " (rev " + str(self.version) + ")"
+
+class SeriesRevisionPatch(models.Model):
+    patch = models.ForeignKey(Patch)
+    revision = models.ForeignKey(SeriesRevision)
+    order = models.IntegerField()
+
+    class Meta:
+        unique_together = [('revision', 'patch'), ('revision', 'order')]
+        ordering = ['order']
+
 class EmailConfirmation(models.Model):
     validity = datetime.timedelta(days = settings.CONFIRMATION_VALIDITY_DAYS)
     type = models.CharField(max_length = 20, choices = [