From 40563532d417dc0e519883e8cb8311340359411f Mon Sep 17 00:00:00 2001
From: Jorran de Wit <jorrandewit@outlook.com>
Date: Sat, 29 Jul 2017 08:16:06 +0200
Subject: [PATCH] Migrate the Comments!

---
 commentaries/models.py                        |   2 +-
 comments/admin.py                             |   2 +-
 .../migrations/0017_auto_20170729_0717.py     | 136 ++++++++++++++++++
 comments/models.py                            |  19 ++-
 4 files changed, 150 insertions(+), 9 deletions(-)
 create mode 100644 comments/migrations/0017_auto_20170729_0717.py

diff --git a/commentaries/models.py b/commentaries/models.py
index a20ad2df2..a6176b576 100644
--- a/commentaries/models.py
+++ b/commentaries/models.py
@@ -58,7 +58,7 @@ class Commentary(TimeStampedModel):
     pub_abstract = models.TextField(verbose_name='abstract')
 
     # Comments can be added to a Commentary
-    comments = GenericRelation('comments.Comment', related_query_name='theses')
+    comments = GenericRelation('comments.Comment', related_query_name='commentaries')
 
     objects = CommentaryManager()
 
diff --git a/comments/admin.py b/comments/admin.py
index 25c767aa4..c330e4ff1 100644
--- a/comments/admin.py
+++ b/comments/admin.py
@@ -18,7 +18,7 @@ def comment_is_vetted(comment):
 class CommentAdmin(GuardedModelAdmin):
     list_display = (comment_opening, 'author', 'date_submitted', comment_is_vetted)
     date_hierarchy = 'date_submitted'
-    list_filter = ('status',)
+    list_filter = ('status', 'content_type',)
     comment_is_vetted.boolean = True
 
 
diff --git a/comments/migrations/0017_auto_20170729_0717.py b/comments/migrations/0017_auto_20170729_0717.py
new file mode 100644
index 000000000..ebf8e864b
--- /dev/null
+++ b/comments/migrations/0017_auto_20170729_0717.py
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-07-29 05:17
+from __future__ import unicode_literals
+
+from django.contrib.auth import get_user_model
+from django.contrib.contenttypes.models import ContentType
+from django.db import migrations
+
+from guardian.shortcuts import assign_perm
+
+
+def update_all_contenttypes(**kwargs):
+    from django.apps import apps
+    from django.contrib.contenttypes.management import update_contenttypes
+
+    for app_config in apps.get_app_configs():
+        update_contenttypes(app_config, **kwargs)
+
+
+def create_all_permissions(**kwargs):
+    from django.contrib.auth.management import create_permissions
+    from django.apps import apps
+
+    for app_config in apps.get_app_configs():
+        print(app_config)
+        create_permissions(app_config, **kwargs)
+
+
+def forward():
+    update_all_contenttypes()
+    create_all_permissions()
+
+
+def migrate_comments_from_generic_relations(apps, schema_editor):
+    """
+    Migrate all GenericRelations a Comment has to the oldschool ForeignKey relations.
+    """
+    return
+    Comment = apps.get_model('comments', 'Comment')
+    Report = apps.get_model('submissions', 'Report')
+    Submission = apps.get_model('submissions', 'Submission')
+    Commentary = apps.get_model('commentaries', 'Commentary')
+    ThesisLink = apps.get_model('theses', 'ThesisLink')
+
+    all_comments = Comment.objects.all()
+    for comment in all_comments:
+        _object = comment.content_object
+        if isinstance(_object, Comment):
+            comment.in_reply_to_comment = _object
+
+            # Nested Comments have more relations
+            if isinstance(_object.content_object, ThesisLink):
+                comment.thesislink = _object.content_object
+            elif isinstance(_object.content_object, Submission):
+                comment.submission = _object.content_object
+            elif isinstance(_object.content_object, Commentary):
+                comment.commentary = _object.content_object
+        elif isinstance(_object, Report):
+            comment.in_reply_to_report = _object
+            comment.submission = _object.submission
+        elif isinstance(_object, ThesisLink):
+            comment.thesislink = _object
+        elif isinstance(_object, Submission):
+            comment.submission = _object
+        elif isinstance(_object, Commentary):
+            comment.commentary = _object
+        else:
+            raise AttributeError('Comment has no relation to another valid object.')
+        comment.save()
+    print('\nMigrated %i comments back to oldschool ForeignKey relations.' % len(all_comments))
+
+
+def migrate_comments_to_generic_relations(apps, schema_editor):
+    """
+    Migrate all foreignkey relations a Comment has to the new GenericRelation.
+    """
+    forward()
+    Comment = apps.get_model('comments', 'Comment')
+    # Report = apps.get_model('submissions', 'Report')
+    # Submission = apps.get_model('submissions', 'Submission')
+    # Commentary = apps.get_model('commentaries', 'Commentary')
+    # ThesisLink = apps.get_model('theses', 'ThesisLink')
+    User = get_user_model()
+
+    all_comments = Comment.objects.all()
+    for comment in all_comments:
+        if comment.in_reply_to_comment:
+            _object = comment.in_reply_to_comment
+            _object_id = comment.in_reply_to_comment.id
+            _object_type = ContentType.objects.get(app_label="comments", model="comment").id
+            print('comment', comment.id, comment.in_reply_to_comment.id)
+        elif comment.in_reply_to_report:
+            _object = comment.in_reply_to_report
+            _object_id = comment.in_reply_to_report.id
+            _object_type = ContentType.objects.get(app_label="submissions", model="report").id
+            print('report', comment.id, comment.in_reply_to_report.id)
+        elif comment.thesislink:
+            _object = comment.thesislink
+            _object_id = comment.thesislink.id
+            _object_type = ContentType.objects.get(app_label="theses", model="thesislink").id
+            print('thesis', comment.id, comment.thesislink.id)
+        elif comment.submission:
+            _object = comment.submission
+            _object_id = comment.submission.id
+            _object_type = ContentType.objects.get(app_label="submissions", model="submission").id
+            print('submission', comment.id, comment.submission.id)
+        elif comment.commentary:
+            _object = comment.commentary
+            _object_id = comment.commentary.id
+            _object_type = ContentType.objects.get(app_label="commentaries", model="commentary").id
+            print('commentary', comment.id, comment.commentary.id)
+        else:
+            print('\nNo valid relation for Comment: ', comment.id,)
+        comment.content_object = _object
+        comment.content_type_id = _object_type
+        comment.object_id = _object_id
+        comment.save()
+
+        # Grant Permissions
+        if comment.submission:
+            user = User.objects.get(id=comment.submission.editor_in_charge.user.id)
+            assign_perm('comments.can_vet_comments', user, comment)
+
+    print('\nMigrated %i comments to GenericRelations.' % len(all_comments))
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('comments', '0016_auto_20170728_1901'),
+    ]
+
+    operations = [
+        migrations.RunPython(migrate_comments_to_generic_relations,
+                             migrate_comments_from_generic_relations),
+    ]
diff --git a/comments/models.py b/comments/models.py
index 176a814db..b87966c4a 100644
--- a/comments/models.py
+++ b/comments/models.py
@@ -15,6 +15,10 @@ from .constants import COMMENT_STATUS, STATUS_PENDING
 from .managers import CommentQuerySet
 
 
+WARNING_TEXT = 'Warning: Rather use/edit `content_object` instead or be 100% sure you know what you are doing!'
+US_NOTICE = 'Warning: This field is out of service and will be removed in the future.'
+
+
 class Comment(TimeStampedModel):
     """ A Comment is an unsollicited note, submitted by a Contributor,
     on a particular publication or in reply to an earlier Comment. """
@@ -28,24 +32,25 @@ class Comment(TimeStampedModel):
 
     # A Comment is always related to another model
     # This construction implicitly has property: `on_delete=models.CASCADE`
-    content_type = models.ForeignKey(ContentType)
-    object_id = models.PositiveIntegerField()
+    content_type = models.ForeignKey(ContentType, help_text=WARNING_TEXT)
+    object_id = models.PositiveIntegerField(help_text=WARNING_TEXT)
     content_object = GenericForeignKey()
 
     nested_comments = GenericRelation('comments.Comment', related_query_name='comments')
 
     commentary = models.ForeignKey('commentaries.Commentary', blank=True, null=True,
-                                   on_delete=models.CASCADE)
+                                   on_delete=models.CASCADE, help_text=US_NOTICE)
     submission = models.ForeignKey('submissions.Submission', blank=True, null=True,
-                                   on_delete=models.CASCADE, related_name='comments_old')
+                                   on_delete=models.CASCADE, related_name='comments_old',
+                                   help_text=US_NOTICE)
     thesislink = models.ForeignKey('theses.ThesisLink', blank=True, null=True,
-                                   on_delete=models.CASCADE)
+                                   on_delete=models.CASCADE, help_text=US_NOTICE)
     is_author_reply = models.BooleanField(default=False)
     in_reply_to_comment = models.ForeignKey('self', blank=True, null=True,
                                             related_name="nested_comments_old",
-                                            on_delete=models.CASCADE)
+                                            on_delete=models.CASCADE, help_text=US_NOTICE)
     in_reply_to_report = models.ForeignKey('submissions.Report', blank=True, null=True,
-                                           on_delete=models.CASCADE)
+                                           on_delete=models.CASCADE, help_text=US_NOTICE)
     author = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE)
     anonymous = models.BooleanField(default=False, verbose_name='Publish anonymously')
 
-- 
GitLab