From bea731ae928284861be62f5b62617a84bb03adc4 Mon Sep 17 00:00:00 2001 From: Daniel Zeljko Date: Thu, 20 Jul 2023 23:37:25 -0700 Subject: [PATCH 01/10] Start new Django app 'corrections' --- config/settings/base.py | 1 + langcorrect/corrections/__init__.py | 0 langcorrect/corrections/admin.py | 3 +++ langcorrect/corrections/apps.py | 8 ++++++++ langcorrect/corrections/migrations/__init__.py | 0 langcorrect/corrections/models.py | 3 +++ langcorrect/corrections/tests.py | 3 +++ langcorrect/corrections/views.py | 3 +++ 8 files changed, 21 insertions(+) create mode 100644 langcorrect/corrections/__init__.py create mode 100644 langcorrect/corrections/admin.py create mode 100644 langcorrect/corrections/apps.py create mode 100644 langcorrect/corrections/migrations/__init__.py create mode 100644 langcorrect/corrections/models.py create mode 100644 langcorrect/corrections/tests.py create mode 100644 langcorrect/corrections/views.py diff --git a/config/settings/base.py b/config/settings/base.py index 50829a13..309c484c 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -95,6 +95,7 @@ "langcorrect.challenges", "langcorrect.prompts", "langcorrect.posts", + "langcorrect.corrections", ] # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS diff --git a/langcorrect/corrections/__init__.py b/langcorrect/corrections/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/langcorrect/corrections/admin.py b/langcorrect/corrections/admin.py new file mode 100644 index 00000000..5533e268 --- /dev/null +++ b/langcorrect/corrections/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin # noqa: 401 + +# Register your models here. diff --git a/langcorrect/corrections/apps.py b/langcorrect/corrections/apps.py new file mode 100644 index 00000000..ef0fed69 --- /dev/null +++ b/langcorrect/corrections/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class CorrectionsConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "langcorrect.corrections" + verbose_name = _("Corrections") diff --git a/langcorrect/corrections/migrations/__init__.py b/langcorrect/corrections/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/langcorrect/corrections/models.py b/langcorrect/corrections/models.py new file mode 100644 index 00000000..b3b17527 --- /dev/null +++ b/langcorrect/corrections/models.py @@ -0,0 +1,3 @@ +from django.db import models # noqa: 401 + +# Create your models here. diff --git a/langcorrect/corrections/tests.py b/langcorrect/corrections/tests.py new file mode 100644 index 00000000..18485082 --- /dev/null +++ b/langcorrect/corrections/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase # noqa: 401 + +# Create your tests here. diff --git a/langcorrect/corrections/views.py b/langcorrect/corrections/views.py new file mode 100644 index 00000000..61c2a95e --- /dev/null +++ b/langcorrect/corrections/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render # noqa: 401 + +# Create your views here. From 609b58d7e6e4f37a05b81a9e9ae267bf9d0aa97f Mon Sep 17 00:00:00 2001 From: Daniel Zeljko Date: Fri, 21 Jul 2023 00:30:17 -0700 Subject: [PATCH 02/10] Migrate data from journal.correctedrows to corrections.correctedrow --- .../corrections/migrations/0001_initial.py | 80 +++++++++++++++++++ .../0002_alter_correctedrow_post_row.py | 21 +++++ langcorrect/corrections/models.py | 26 +++++- migrate_data.py | 65 ++++++++++++++- 4 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 langcorrect/corrections/migrations/0001_initial.py create mode 100644 langcorrect/corrections/migrations/0002_alter_correctedrow_post_row.py diff --git a/langcorrect/corrections/migrations/0001_initial.py b/langcorrect/corrections/migrations/0001_initial.py new file mode 100644 index 00000000..a2905227 --- /dev/null +++ b/langcorrect/corrections/migrations/0001_initial.py @@ -0,0 +1,80 @@ +# Generated by Django 4.2.3 on 2023-07-21 06:42 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import model_utils.fields + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("posts", "0002_postrow"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="CorrectionType", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, editable=False, verbose_name="created" + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, editable=False, verbose_name="modified" + ), + ), + ("is_removed", models.BooleanField(default=False)), + ("name", models.CharField(max_length=100, unique=True)), + ("description", models.TextField()), + ], + options={ + "verbose_name": "Correction Type", + "verbose_name_plural": "Correction Types", + }, + ), + migrations.CreateModel( + name="CorrectedRow", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, editable=False, verbose_name="created" + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, editable=False, verbose_name="modified" + ), + ), + ("is_removed", models.BooleanField(default=False)), + ("correction", models.TextField()), + ("note", models.TextField(blank=True, default=None, null=True)), + ("correction_types", models.ManyToManyField(to="corrections.correctiontype")), + ( + "post", + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="posts.post" + ), + ), + ( + "post_row", + models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to="posts.postrow"), + ), + ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/langcorrect/corrections/migrations/0002_alter_correctedrow_post_row.py b/langcorrect/corrections/migrations/0002_alter_correctedrow_post_row.py new file mode 100644 index 00000000..5b71d54c --- /dev/null +++ b/langcorrect/corrections/migrations/0002_alter_correctedrow_post_row.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.3 on 2023-07-21 07:27 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("posts", "0002_postrow"), + ("corrections", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="correctedrow", + name="post_row", + field=models.ForeignKey( + default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to="posts.postrow" + ), + ), + ] diff --git a/langcorrect/corrections/models.py b/langcorrect/corrections/models.py index b3b17527..e13e6370 100644 --- a/langcorrect/corrections/models.py +++ b/langcorrect/corrections/models.py @@ -1,3 +1,25 @@ -from django.db import models # noqa: 401 +from django.conf import settings +from django.db import models +from django.utils.translation import gettext_lazy as _ +from model_utils.models import SoftDeletableModel, TimeStampedModel -# Create your models here. + +class CorrectionType(SoftDeletableModel, TimeStampedModel): + class Meta: + verbose_name = _("Correction Type") + verbose_name_plural = _("Correction Types") + + name = models.CharField(max_length=100, unique=True) + description = models.TextField() + + def __str__(self): + return self.name + + +class CorrectedRow(SoftDeletableModel, TimeStampedModel): + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + post = models.ForeignKey("posts.Post", on_delete=models.CASCADE, null=True, blank=True) + post_row = models.ForeignKey("posts.PostRow", on_delete=models.CASCADE, null=True, default=None) + correction = models.TextField() + note = models.TextField(default=None, null=True, blank=True) + correction_types = models.ManyToManyField(CorrectionType) diff --git a/migrate_data.py b/migrate_data.py index 2b8672a2..bfbb0bce 100644 --- a/migrate_data.py +++ b/migrate_data.py @@ -10,12 +10,20 @@ from taggit.models import Tag # noqa: E402 from langcorrect.challenges.models import Challenge # noqa: E402 +from langcorrect.corrections.models import CorrectedRow, CorrectionType # noqa: E402 from langcorrect.languages.models import Language, LanguageLevel # noqa: E402 from langcorrect.posts.models import Post, PostRow # noqa: E402 from langcorrect.prompts.models import Prompt # noqa: E402 User = get_user_model() +CORRECTION_TYPES_MAPPING = { + "Grammar": "Grammar", + "Spelling": "Spelling", + "Stylistic": "Style and Tone", + "Usage": "Word Choice", +} + def migrate_users(): print("Migrating users...") @@ -284,6 +292,60 @@ def migrate_post_rows(): print(f"Finished importing postrow {curr_count}/{total_count}") +def migrate_corrected_rows(): + print("Migrating CorrectedRows...") + + with open("./temp_data/correctedrow_data.json") as file: + data = json.load(file) + + total_count = len(data) + curr_count = 0 + + for entry in data: + pk = entry["pk"] + created = entry["fields"]["created"] + modified = entry["fields"]["modified"] + is_removed = entry["fields"]["is_removed"] + user_pk = entry["fields"]["user"] + post_pk = entry["fields"]["post"] + post_row_pk = entry["fields"]["post_row"] + correction = entry["fields"]["correction"] + note = entry["fields"]["note"] + pretty_corrections = entry["fields"]["pretty_corrections"] + + try: + post = Post.all_objects.get(pk=post_pk) if post_pk else None + except Post.DoesNotExist: + post = None + + try: + post_row = PostRow.all_objects.get(pk=post_row_pk) if post_row_pk else None + except PostRow.DoesNotExist: + post_row = None + + correction = CorrectedRow.objects.create( + pk=pk, + created=created, + modified=modified, + is_removed=is_removed, + user=User.objects.get(pk=user_pk), + post=post, + post_row=post_row, + correction=correction, + note=note, + ) + + for correction_type in pretty_corrections: + c_type = CORRECTION_TYPES_MAPPING.get(correction_type) + ctype = CorrectionType.objects.get(name=c_type) + correction.correction_types.add(ctype) + + correction.save() + + curr_count += 1 + print(f"Finished importing correctedrow {curr_count}/{total_count}") + + def main(): # migrate_users() # migrate_languages() # load fixture instead @@ -291,7 +353,8 @@ def main(): # migrate_challenges() # migrate_prompts() # migrate_posts() - migrate_post_rows() + # migrate_post_rows() + migrate_corrected_rows() main() From 7a3ebea1f34aa9c217cefb288a0ec9941ca3533c Mon Sep 17 00:00:00 2001 From: Daniel Zeljko Date: Fri, 21 Jul 2023 00:30:32 -0700 Subject: [PATCH 03/10] Add fixture for correction types --- fixtures/correction_types.json | 112 +++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 fixtures/correction_types.json diff --git a/fixtures/correction_types.json b/fixtures/correction_types.json new file mode 100644 index 00000000..10a4b95a --- /dev/null +++ b/fixtures/correction_types.json @@ -0,0 +1,112 @@ +[ +{ + "model": "corrections.correctiontype", + "pk": 1, + "fields": { + "created": "2023-07-12T01:02:18.866Z", + "modified": "2023-07-12T01:02:18.866Z", + "is_removed": false, + "name": "Formatting and Citations", + "description": "This correction type deals with issues related to the proper formatting of the document, including indentation, spacing, font styles, and citation styles. It aims to ensure adherence to standard writing conventions, citation guidelines, and any specific formatting requirements." + } +}, +{ + "model": "corrections.correctiontype", + "pk": 2, + "fields": { + "created": "2023-07-12T01:03:20.975Z", + "modified": "2023-07-12T01:03:20.975Z", + "is_removed": false, + "name": "Content and Development", + "description": "This correction type involves evaluating the substance and development of ideas in the writing. It focuses on the relevance, coherence, and depth of the content, ensuring that ideas are well-supported, logical, and effectively communicated to engage the reader and convey the intended message." + } +}, +{ + "model": "corrections.correctiontype", + "pk": 3, + "fields": { + "created": "2023-07-12T01:04:32.395Z", + "modified": "2023-07-12T01:04:32.395Z", + "is_removed": false, + "name": "Style and Tone", + "description": "This correction type focuses on refining the writing style, tone, and voice. It aims to create a consistent and appropriate tone throughout the writing, aligning it with the intended audience and purpose. This correction type may involve suggestions to enhance the writing's persuasiveness, formality, informality, or other stylistic aspects." + } +}, +{ + "model": "corrections.correctiontype", + "pk": 4, + "fields": { + "created": "2023-07-12T01:05:44.944Z", + "modified": "2023-07-12T01:05:44.944Z", + "is_removed": false, + "name": "Clarity and Coherence", + "description": "This correction type aims to improve the overall clarity, logical flow, and organization of ideas in writing. It involves identifying and rectifying issues related to unclear or ambiguous expressions, lack of logical connections between ideas, or inconsistencies in paragraph organization to enhance the reader's understanding." + } +}, +{ + "model": "corrections.correctiontype", + "pk": 5, + "fields": { + "created": "2023-07-12T01:06:46.076Z", + "modified": "2023-07-12T01:06:46.076Z", + "is_removed": false, + "name": "Sentence Structure", + "description": "This correction type focuses on analyzing and improving the structure of sentences. It involves addressing issues such as run-on sentences, sentence fragments, awkward phrasing, and inconsistencies in sentence construction to improve the overall flow and readability of the writing." + } +}, +{ + "model": "corrections.correctiontype", + "pk": 6, + "fields": { + "created": "2023-07-12T01:07:38.225Z", + "modified": "2023-07-12T01:07:38.225Z", + "is_removed": false, + "name": "Word Choice", + "description": "This correction type involves suggesting alternatives or improvements for specific words or phrases used in the writing. It aims to enhance precision, clarity, and vocabulary richness by recommending more appropriate or effective words that convey the intended meaning more accurately." + } +}, +{ + "model": "corrections.correctiontype", + "pk": 7, + "fields": { + "created": "2023-07-12T01:08:29.733Z", + "modified": "2023-07-12T01:08:29.733Z", + "is_removed": false, + "name": "Capitalization", + "description": "This correction type focuses on ensuring correct capitalization throughout the writing. It involves correcting errors related to the capitalization of proper nouns, sentence beginnings, titles, and other instances where capital letters should be more appropriately used." + } +}, +{ + "model": "corrections.correctiontype", + "pk": 8, + "fields": { + "created": "2023-07-12T01:09:31.829Z", + "modified": "2023-07-12T01:09:31.829Z", + "is_removed": false, + "name": "Punctuation", + "description": "This correction type entails reviewing and correcting errors related to the proper use of punctuation marks. It includes fixing issues with commas, periods, question marks, exclamation marks, quotation marks, colons, semicolons, and other punctuation symbols to enhance readability and convey meaning effectively." + } +}, +{ + "model": "corrections.correctiontype", + "pk": 9, + "fields": { + "created": "2023-07-12T01:10:24.955Z", + "modified": "2023-07-12T01:10:24.955Z", + "is_removed": false, + "name": "Grammar", + "description": "This correction type addresses errors in sentence structure and grammatical rules. It involves rectifying mistakes in verb tenses, subject-verb agreement, articles, pronouns, prepositions, and other grammatical elements to improve the overall accuracy and clarity of the writing." + } +}, +{ + "model": "corrections.correctiontype", + "pk": 10, + "fields": { + "created": "2023-07-12T01:11:06.092Z", + "modified": "2023-07-12T01:11:06.092Z", + "is_removed": false, + "name": "Spelling", + "description": "This correction type focuses on identifying and correcting errors related to incorrect spelling. This correction type involves fixing mistakes in the spelling of words, ensuring proper usage of homophones, and correcting any typos or misspelled words" + } +} +] From a2fe750d9e913c3247892763142aba4596425524 Mon Sep 17 00:00:00 2001 From: Daniel Zeljko Date: Fri, 21 Jul 2023 00:57:43 -0700 Subject: [PATCH 04/10] Do not add correction if missing post_row --- .../corrections/migrations/0001_initial.py | 7 ++-- .../0002_alter_correctedrow_post_row.py | 21 ----------- langcorrect/corrections/models.py | 2 +- migrate_data.py | 36 +++++++++---------- 4 files changed, 21 insertions(+), 45 deletions(-) delete mode 100644 langcorrect/corrections/migrations/0002_alter_correctedrow_post_row.py diff --git a/langcorrect/corrections/migrations/0001_initial.py b/langcorrect/corrections/migrations/0001_initial.py index a2905227..243dc53b 100644 --- a/langcorrect/corrections/migrations/0001_initial.py +++ b/langcorrect/corrections/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2023-07-21 06:42 +# Generated by Django 4.2.3 on 2023-07-21 07:53 from django.conf import settings from django.db import migrations, models @@ -67,10 +67,7 @@ class Migration(migrations.Migration): blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="posts.post" ), ), - ( - "post_row", - models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to="posts.postrow"), - ), + ("post_row", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posts.postrow")), ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ diff --git a/langcorrect/corrections/migrations/0002_alter_correctedrow_post_row.py b/langcorrect/corrections/migrations/0002_alter_correctedrow_post_row.py deleted file mode 100644 index 5b71d54c..00000000 --- a/langcorrect/corrections/migrations/0002_alter_correctedrow_post_row.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 4.2.3 on 2023-07-21 07:27 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - dependencies = [ - ("posts", "0002_postrow"), - ("corrections", "0001_initial"), - ] - - operations = [ - migrations.AlterField( - model_name="correctedrow", - name="post_row", - field=models.ForeignKey( - default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to="posts.postrow" - ), - ), - ] diff --git a/langcorrect/corrections/models.py b/langcorrect/corrections/models.py index e13e6370..1868e8e6 100644 --- a/langcorrect/corrections/models.py +++ b/langcorrect/corrections/models.py @@ -19,7 +19,7 @@ def __str__(self): class CorrectedRow(SoftDeletableModel, TimeStampedModel): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) post = models.ForeignKey("posts.Post", on_delete=models.CASCADE, null=True, blank=True) - post_row = models.ForeignKey("posts.PostRow", on_delete=models.CASCADE, null=True, default=None) + post_row = models.ForeignKey("posts.PostRow", on_delete=models.CASCADE) correction = models.TextField() note = models.TextField(default=None, null=True, blank=True) correction_types = models.ManyToManyField(CorrectionType) diff --git a/migrate_data.py b/migrate_data.py index bfbb0bce..e1eb7287 100644 --- a/migrate_data.py +++ b/migrate_data.py @@ -320,27 +320,27 @@ def migrate_corrected_rows(): try: post_row = PostRow.all_objects.get(pk=post_row_pk) if post_row_pk else None - except PostRow.DoesNotExist: - post_row = None - correction = CorrectedRow.objects.create( - pk=pk, - created=created, - modified=modified, - is_removed=is_removed, - user=User.objects.get(pk=user_pk), - post=post, - post_row=post_row, - correction=correction, - note=note, - ) + correction = CorrectedRow.objects.create( + pk=pk, + created=created, + modified=modified, + is_removed=is_removed, + user=User.objects.get(pk=user_pk), + post=post, + post_row=post_row, + correction=correction, + note=note, + ) - for correction_type in pretty_corrections: - c_type = CORRECTION_TYPES_MAPPING.get(correction_type) - ctype = CorrectionType.objects.get(name=c_type) - correction.correction_types.add(ctype) + for correction_type in pretty_corrections: + c_type = CORRECTION_TYPES_MAPPING.get(correction_type) + ctype = CorrectionType.objects.get(name=c_type) + correction.correction_types.add(ctype) - correction.save() + correction.save() + except PostRow.DoesNotExist: + pass curr_count += 1 print(f"Finished importing correctedrow {curr_count}/{total_count}") From 83b1541caee66b469ecc1058edcf79619e5672fc Mon Sep 17 00:00:00 2001 From: Daniel Zeljko Date: Fri, 21 Jul 2023 00:58:01 -0700 Subject: [PATCH 05/10] Register admin for corrections --- langcorrect/corrections/admin.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/langcorrect/corrections/admin.py b/langcorrect/corrections/admin.py index 5533e268..750ef345 100644 --- a/langcorrect/corrections/admin.py +++ b/langcorrect/corrections/admin.py @@ -1,3 +1,30 @@ -from django.contrib import admin # noqa: 401 +from django.contrib import admin -# Register your models here. +from langcorrect.corrections.models import CorrectedRow, CorrectionType + + +@admin.register(CorrectionType) +class CorrectionTypeAdmin(admin.ModelAdmin): + list_display = ["name", "description"] + + +@admin.register(CorrectedRow) +class CorrectedRowAdmin(admin.ModelAdmin): + readonly_fields = ["post", "user", "post_row", "correction", "note", "correction_types"] + list_display = ["author", "corrector", "original_sentence", "correction", "note", "get_correction_types"] + search_fields = ["post__user__username"] + + def author(self, obj): + if obj.post: + return obj.post.user + else: + return None + + def corrector(self, obj): + return obj.user + + def original_sentence(self, obj): + return obj.post_row.sentence + + def get_correction_types(self, obj): + return [t.name for t in obj.correction_types.all()] From 4c73c796e1492fdff245e0ce0998bfbda44c0040 Mon Sep 17 00:00:00 2001 From: Daniel Zeljko Date: Fri, 21 Jul 2023 18:53:11 -0700 Subject: [PATCH 06/10] Migrate journal.PerfectRows to corrections.PerfectRow --- .../corrections/migrations/0002_perfectrow.py | 48 +++++++++++++++++++ langcorrect/corrections/models.py | 6 +++ migrate_data.py | 46 +++++++++++++++++- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 langcorrect/corrections/migrations/0002_perfectrow.py diff --git a/langcorrect/corrections/migrations/0002_perfectrow.py b/langcorrect/corrections/migrations/0002_perfectrow.py new file mode 100644 index 00000000..32b162f2 --- /dev/null +++ b/langcorrect/corrections/migrations/0002_perfectrow.py @@ -0,0 +1,48 @@ +# Generated by Django 4.2.3 on 2023-07-22 00:51 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import model_utils.fields + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("posts", "0002_postrow"), + ("corrections", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="PerfectRow", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, editable=False, verbose_name="created" + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, editable=False, verbose_name="modified" + ), + ), + ("is_removed", models.BooleanField(default=False)), + ( + "post", + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="posts.post" + ), + ), + ("post_row", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posts.postrow")), + ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/langcorrect/corrections/models.py b/langcorrect/corrections/models.py index 1868e8e6..ccb6f9e0 100644 --- a/langcorrect/corrections/models.py +++ b/langcorrect/corrections/models.py @@ -23,3 +23,9 @@ class CorrectedRow(SoftDeletableModel, TimeStampedModel): correction = models.TextField() note = models.TextField(default=None, null=True, blank=True) correction_types = models.ManyToManyField(CorrectionType) + + +class PerfectRow(SoftDeletableModel, TimeStampedModel): + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + post = models.ForeignKey("posts.Post", on_delete=models.CASCADE, null=True, blank=True) + post_row = models.ForeignKey("posts.PostRow", on_delete=models.CASCADE) diff --git a/migrate_data.py b/migrate_data.py index e1eb7287..7a3fe995 100644 --- a/migrate_data.py +++ b/migrate_data.py @@ -10,7 +10,7 @@ from taggit.models import Tag # noqa: E402 from langcorrect.challenges.models import Challenge # noqa: E402 -from langcorrect.corrections.models import CorrectedRow, CorrectionType # noqa: E402 +from langcorrect.corrections.models import CorrectedRow, CorrectionType, PerfectRow # noqa: E402 from langcorrect.languages.models import Language, LanguageLevel # noqa: E402 from langcorrect.posts.models import Post, PostRow # noqa: E402 from langcorrect.prompts.models import Prompt # noqa: E402 @@ -346,6 +346,47 @@ def migrate_corrected_rows(): print(f"Finished importing correctedrow {curr_count}/{total_count}") +def migrate_perfect_rows(): + print("Migrating perfect rows...") + + with open("./temp_data/perfects_data.json") as file: + data = json.load(file) + + total_count = len(data) + curr_count = 0 + + for entry in data: + pk = entry["pk"] + created = entry["fields"]["created"] + modified = entry["fields"]["modified"] + is_removed = entry["fields"]["is_removed"] + user_pk = entry["fields"]["user"] + post_pk = entry["fields"]["post"] + post_row_pk = entry["fields"]["post_row"] + + try: + post = Post.all_objects.get(pk=post_pk) if post_pk else None + except Post.DoesNotExist: + post = None + + try: + post_row = PostRow.all_objects.get(pk=post_row_pk) if post_row_pk else None + PerfectRow.objects.create( + pk=pk, + created=created, + modified=modified, + is_removed=is_removed, + user=User.objects.get(pk=user_pk), + post=post, + post_row=post_row, + ) + except PostRow.DoesNotExist: + pass + + curr_count += 1 + print(f"Finished importing PerfectRow {curr_count}/{total_count}") + + def main(): # migrate_users() # migrate_languages() # load fixture instead @@ -354,7 +395,8 @@ def main(): # migrate_prompts() # migrate_posts() # migrate_post_rows() - migrate_corrected_rows() + # migrate_corrected_rows() + migrate_perfect_rows() main() From 0c796bd40eb70d88cebe0db3a215d6ed8d82f778 Mon Sep 17 00:00:00 2001 From: Daniel Zeljko Date: Fri, 21 Jul 2023 18:53:43 -0700 Subject: [PATCH 07/10] Register admin for PerfectRow --- langcorrect/corrections/admin.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/langcorrect/corrections/admin.py b/langcorrect/corrections/admin.py index 750ef345..8543ebae 100644 --- a/langcorrect/corrections/admin.py +++ b/langcorrect/corrections/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from langcorrect.corrections.models import CorrectedRow, CorrectionType +from langcorrect.corrections.models import CorrectedRow, CorrectionType, PerfectRow @admin.register(CorrectionType) @@ -28,3 +28,22 @@ def original_sentence(self, obj): def get_correction_types(self, obj): return [t.name for t in obj.correction_types.all()] + + +@admin.register(PerfectRow) +class PerfectRowAdmin(admin.ModelAdmin): + readonly_fields = ["post", "user", "post_row"] + list_display = ["author", "corrector", "original_sentence"] + search_fields = ["post__user__username"] + + def author(self, obj): + if obj.post: + return obj.post.user + else: + return None + + def corrector(self, obj): + return obj.user + + def original_sentence(self, obj): + return obj.post_row.sentence From b58121fca0735e36209d06243e0e659430641534 Mon Sep 17 00:00:00 2001 From: Daniel Zeljko Date: Fri, 21 Jul 2023 19:15:08 -0700 Subject: [PATCH 08/10] Update readme --- .env.example | 2 ++ README.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..340b7269 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +DATABASE_URL=postgres:///langcorrect +CELERY_BROKER_URL=redis://127.0.0.1:6379 diff --git a/README.md b/README.md index dbf51a03..237fa0d9 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,34 @@ Master grammar, spelling, and syntax in the language(s) you’re learning throug [![Built with Cookiecutter Django](https://img.shields.io/badge/built%20with-Cookiecutter%20Django-ff69b4.svg?logo=cookiecutter)](https://github.com/cookiecutter/cookiecutter-django/) [![Black code style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) +## Installation + +### Create the environment + +- To create a new virtual environment, run the following command: + + $ python -m venv venv + +- Install the project dependencies by running the following command: + + $ pip install -r requirements/local.txt + +- Create the database: + + createdb --username=postgres langcorrect + +- Set up your environment variables in .env (check .env example) + +### Install the fixtures + +- To install the supported languages, run the following command: + + python manage.py loaddata fixtures/languages.json + +- To install the correction types, run the following command: + + python manage.py loaddata fixtures/correction_types.json + ## Settings Moved to [settings](http://cookiecutter-django.readthedocs.io/en/latest/settings.html). From 69d2a70edf4d84ff7a677e89564a3fdd1999aa19 Mon Sep 17 00:00:00 2001 From: Daniel Zeljko Date: Fri, 21 Jul 2023 20:01:16 -0700 Subject: [PATCH 09/10] Migrate data from journal.Feedback to corrections.OverallFeedback --- .../migrations/0003_overallfeedback.py | 44 +++++++++++++++++++ langcorrect/corrections/models.py | 7 +++ migrate_data.py | 44 ++++++++++++++++++- 3 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 langcorrect/corrections/migrations/0003_overallfeedback.py diff --git a/langcorrect/corrections/migrations/0003_overallfeedback.py b/langcorrect/corrections/migrations/0003_overallfeedback.py new file mode 100644 index 00000000..830298d9 --- /dev/null +++ b/langcorrect/corrections/migrations/0003_overallfeedback.py @@ -0,0 +1,44 @@ +# Generated by Django 4.2.3 on 2023-07-22 02:17 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import model_utils.fields + + +class Migration(migrations.Migration): + dependencies = [ + ("posts", "0002_postrow"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("corrections", "0002_perfectrow"), + ] + + operations = [ + migrations.CreateModel( + name="OverallFeedback", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, editable=False, verbose_name="created" + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, editable=False, verbose_name="modified" + ), + ), + ("is_removed", models.BooleanField(default=False)), + ("comment", models.TextField()), + ("is_draft", models.BooleanField(default=False)), + ("post", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posts.post")), + ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/langcorrect/corrections/models.py b/langcorrect/corrections/models.py index ccb6f9e0..36bba818 100644 --- a/langcorrect/corrections/models.py +++ b/langcorrect/corrections/models.py @@ -29,3 +29,10 @@ class PerfectRow(SoftDeletableModel, TimeStampedModel): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) post = models.ForeignKey("posts.Post", on_delete=models.CASCADE, null=True, blank=True) post_row = models.ForeignKey("posts.PostRow", on_delete=models.CASCADE) + + +class OverallFeedback(SoftDeletableModel, TimeStampedModel): + user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + post = models.ForeignKey("posts.Post", on_delete=models.CASCADE) + comment = models.TextField() + is_draft = models.BooleanField(default=False) diff --git a/migrate_data.py b/migrate_data.py index 7a3fe995..d4ef35f4 100644 --- a/migrate_data.py +++ b/migrate_data.py @@ -10,7 +10,7 @@ from taggit.models import Tag # noqa: E402 from langcorrect.challenges.models import Challenge # noqa: E402 -from langcorrect.corrections.models import CorrectedRow, CorrectionType, PerfectRow # noqa: E402 +from langcorrect.corrections.models import CorrectedRow, CorrectionType, OverallFeedback, PerfectRow # noqa: E402 from langcorrect.languages.models import Language, LanguageLevel # noqa: E402 from langcorrect.posts.models import Post, PostRow # noqa: E402 from langcorrect.prompts.models import Prompt # noqa: E402 @@ -387,6 +387,45 @@ def migrate_perfect_rows(): print(f"Finished importing PerfectRow {curr_count}/{total_count}") +def migrate_overall_feedback(): + print("Migrating overall feedback...") + + with open("./temp_data/overall_feedback_data.json") as file: + data = json.load(file) + + total_count = len(data) + curr_count = 0 + + for entry in data: + pk = entry["pk"] + created = entry["fields"]["created"] + modified = entry["fields"]["modified"] + is_removed = entry["fields"]["is_removed"] + user_pk = entry["fields"]["user"] + post_pk = entry["fields"]["post"] + comment = entry["fields"]["comment"] + is_draft = entry["fields"]["is_draft"] + + try: + post = Post.all_objects.get(pk=post_pk) + + OverallFeedback.objects.create( + pk=pk, + created=created, + modified=modified, + is_removed=is_removed, + user=User.objects.get(pk=user_pk), + post=post, + comment=comment, + is_draft=is_draft, + ) + except Post.DoesNotExist: + pass + + curr_count += 1 + print(f"Finished importing OverallFeedback {curr_count}/{total_count}") + + def main(): # migrate_users() # migrate_languages() # load fixture instead @@ -396,7 +435,8 @@ def main(): # migrate_posts() # migrate_post_rows() # migrate_corrected_rows() - migrate_perfect_rows() + # migrate_perfect_rows() + migrate_overall_feedback() main() From a3c3af1be1e99ca5e62c0e7c93f6ce542d7f0ec3 Mon Sep 17 00:00:00 2001 From: Daniel Zeljko Date: Fri, 21 Jul 2023 20:01:41 -0700 Subject: [PATCH 10/10] Register admin for OverallFeedback --- langcorrect/corrections/admin.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/langcorrect/corrections/admin.py b/langcorrect/corrections/admin.py index 8543ebae..5d52abbb 100644 --- a/langcorrect/corrections/admin.py +++ b/langcorrect/corrections/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from langcorrect.corrections.models import CorrectedRow, CorrectionType, PerfectRow +from langcorrect.corrections.models import CorrectedRow, CorrectionType, OverallFeedback, PerfectRow @admin.register(CorrectionType) @@ -47,3 +47,19 @@ def corrector(self, obj): def original_sentence(self, obj): return obj.post_row.sentence + + +@admin.register(OverallFeedback) +class OverallFeedbackAdmin(admin.ModelAdmin): + readonly_fields = ["post", "user", "comment"] + list_display = ["receiver", "corrector", "comment", "is_draft"] + search_fields = ["post__user__username"] + + def receiver(self, obj): + if obj.post: + return obj.post.user + else: + return None + + def corrector(self, obj): + return obj.user