[Patchew-devel] [PATCH 3/9] add topic model

Paolo Bonzini pbonzini at redhat.com
Thu Jan 16 15:09:03 UTC 2020


This will be used to allow grouping together even series whose subject
has changed.  It will also replace the is_series_head field.

To migrate the database, create a new topic for every stripped_subject.

Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
---
 api/migrations/0053_auto_20200116_0955.py | 27 +++++++++++++
 api/migrations/0054_populate_topic.py     | 29 ++++++++++++++
 api/models.py                             | 46 ++++++++++++++++++++---
 tests/test_message.py                     | 30 +++++++++++++++
 4 files changed, 126 insertions(+), 6 deletions(-)
 create mode 100644 api/migrations/0053_auto_20200116_0955.py
 create mode 100644 api/migrations/0054_populate_topic.py

diff --git a/api/migrations/0053_auto_20200116_0955.py b/api/migrations/0053_auto_20200116_0955.py
new file mode 100644
index 0000000..3fae555
--- /dev/null
+++ b/api/migrations/0053_auto_20200116_0955.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.20 on 2020-01-16 09:55
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('api', '0052_auto_20190418_1357'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Topic',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+            ],
+        ),
+        migrations.AddField(
+            model_name='message',
+            name='topic',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.Topic'),
+        ),
+    ]
diff --git a/api/migrations/0054_populate_topic.py b/api/migrations/0054_populate_topic.py
new file mode 100644
index 0000000..359f00e
--- /dev/null
+++ b/api/migrations/0054_populate_topic.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations
+
+
+def topic_fill(apps, schema_editor):
+    Message = apps.get_model("api", "Message")
+    Topic = apps.get_model("api", "Topic")
+    for record in (
+        Message.objects.filter(is_series_head=True)
+        .values("stripped_subject")
+        .distinct()
+    ):
+        topic = Topic()
+        topic.save()
+        topic_subject = record["stripped_subject"]
+        Message.objects.filter(
+            is_series_head=True, stripped_subject=topic_subject
+        ).update(topic=topic)
+
+
+class Migration(migrations.Migration):
+    dependencies = [("api", "0053_auto_20200116_0955")]
+
+    operations = [
+        migrations.RunPython(topic_fill, reverse_code=migrations.RunPython.noop)
+    ]
diff --git a/api/models.py b/api/models.py
index 81bc167..6f4ea4b 100644
--- a/api/models.py
+++ b/api/models.py
@@ -457,7 +457,11 @@ class MessageManager(models.Manager):
         msg.stripped_subject = m.get_subject(strip_tags=True)
         msg.version = m.get_version()
         msg.prefixes = m.get_prefixes()
-        msg.is_series_head = m.is_series_head()
+        msg.is_series_head = False
+        if m.is_series_head():
+            msg.is_series_head = True
+            msg.topic = Topic.objects.for_stripped_subject(msg.stripped_subject)
+
         msg.is_patch = m.is_patch()
         msg.patch_num = m.get_num()[0]
         msg.project = project
@@ -477,18 +481,25 @@ class MessageManager(models.Manager):
             projects = [Project.object.get(name=project_name)]
         else:
             projects = find_message_projects(m)
+        stripped_subject = m.get_subject(strip_tags=True)
+        is_series_head = m.is_series_head()
         for p in projects:
             msg = Message(
                 message_id=msgid,
                 in_reply_to=m.get_in_reply_to() or "",
                 date=m.get_date(),
                 subject=m.get_subject(),
-                stripped_subject=m.get_subject(strip_tags=True),
+                stripped_subject=stripped_subject,
                 version=m.get_version(),
                 sender=m.get_from(),
                 recipients=m.get_to() + m.get_cc(),
                 prefixes=m.get_prefixes(),
-                is_series_head=m.is_series_head(),
+                is_series_head=is_series_head,
+                topic=(
+                    Topic.objects.for_stripped_subject(stripped_subject)
+                    if is_series_head
+                    else None
+                ),
                 is_patch=m.is_patch(),
                 patch_num=m.get_num()[0],
             )
@@ -520,6 +531,26 @@ class QueuedSeries(models.Model):
         index_together = [("user", "message")]
 
 
+class TopicManager(models.Manager):
+    def for_stripped_subject(self, stripped_subject):
+        q = (
+            Message.objects.filter(stripped_subject=stripped_subject)
+            .order_by("date")
+            .reverse()[:1]
+            .values("topic")
+        )
+        if q:
+            topic = self.get(pk=q[0]["topic"])
+        else:
+            topic = Topic()
+            topic.save()
+        return topic
+
+
+class Topic(models.Model):
+    objects = TopicManager()
+
+
 class Message(models.Model):
     """ Patch email message """
 
@@ -555,6 +586,11 @@ class Message(models.Model):
     is_tested = models.BooleanField(default=False)
     is_reviewed = models.BooleanField(default=False)
 
+    # is series head if not Null
+    topic = models.ForeignKey(
+        "Topic", on_delete=models.CASCADE, null=True, db_index=True
+    )
+
     # patch index number if is_patch
     patch_num = models.PositiveSmallIntegerField(null=True, blank=True)
 
@@ -854,9 +890,7 @@ class Message(models.Model):
 
     def get_alternative_revisions(self):
         assert self.is_series_head
-        return Message.objects.series_heads().filter(
-            project=self.project, stripped_subject=self.stripped_subject
-        )
+        return Message.objects.filter(project=self.project, topic=self.topic)
 
     def set_complete(self):
         if self.is_complete:
diff --git a/tests/test_message.py b/tests/test_message.py
index bc0a98d..3999948 100755
--- a/tests/test_message.py
+++ b/tests/test_message.py
@@ -20,6 +20,7 @@ from api.models import Message
 class MessageTest(PatchewTestCase):
     def setUp(self):
         self.create_superuser()
+        self.p = self.add_project("QEMU", "qemu-devel at nongnu.org")
 
     def test_0_second(self):
         message = Message()
@@ -54,6 +55,35 @@ class MessageTest(PatchewTestCase):
         asctime = message.get_asctime()
         self.assertEqual(asctime, "Sat Oct 22 9:06:04 2016")
 
+    def test_topic_on_series_head(self):
+        self.cli_login()
+        self.cli_import("0004-multiple-patch-reviewed.mbox.gz")
+        m1 = Message.objects.get(
+            message_id="1469192015-16487-1-git-send-email-berrange at redhat.com"
+        )
+        assert m1.is_series_head
+        assert m1.topic is not None
+        m2 = Message.objects.get(
+            message_id="1469192015-16487-2-git-send-email-berrange at redhat.com"
+        )
+        assert not m2.is_series_head
+        assert m2.topic is None
+
+    def test_topic_assignment(self):
+        self.cli_login()
+        self.cli_import("0004-multiple-patch-reviewed.mbox.gz")
+        self.cli_import("0009-obsolete-by.mbox.gz")
+
+        m1 = Message.objects.get(message_id="20160628014747.20971-1-famz at redhat.com")
+        m2 = Message.objects.get(message_id="20160628014747.20971-2-famz at redhat.com")
+        m3 = Message.objects.get(message_id="20160628014747.20971-3-famz at redhat.com")
+        self.assertEqual(m1.topic, m2.topic)
+        self.assertEqual(m1.topic, m3.topic)
+        n = Message.objects.get(
+            message_id="1469192015-16487-1-git-send-email-berrange at redhat.com"
+        )
+        self.assertNotEqual(m1.topic, n.topic)
+
 
 if __name__ == "__main__":
     main()
-- 
2.21.0





More information about the Patchew-devel mailing list