From 2cdecfb331c281d6ab99a3e96b52f5560114a41c Mon Sep 17 00:00:00 2001 From: harryface Date: Mon, 20 Feb 2023 11:20:28 -0700 Subject: [PATCH] create training app, models and fixtures --- physionet-django/physionet/settings/base.py | 4 + physionet-django/physionet/urls.py | 5 +- physionet-django/training/__init__.py | 0 physionet-django/training/admin.py | 30 ++++++++ physionet-django/training/apps.py | 5 ++ .../training/fixtures/demo-training.json | 73 +++++++++++++++++++ .../training/migrations/0001_initial.py | 67 +++++++++++++++++ .../training/migrations/__init__.py | 0 physionet-django/training/models.py | 33 +++++++++ physionet-django/training/to_do | 14 ++++ physionet-django/training/views.py | 3 + physionet-django/user/enums.py | 1 + .../user/fixtures/demo-training-type.json | 18 +++++ physionet-django/user/fixtures/demo-user.json | 5 ++ .../0052_alter_trainingtype_required_field.py | 21 ++++++ 15 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 physionet-django/training/__init__.py create mode 100644 physionet-django/training/admin.py create mode 100644 physionet-django/training/apps.py create mode 100644 physionet-django/training/fixtures/demo-training.json create mode 100644 physionet-django/training/migrations/0001_initial.py create mode 100644 physionet-django/training/migrations/__init__.py create mode 100644 physionet-django/training/models.py create mode 100644 physionet-django/training/to_do create mode 100644 physionet-django/training/views.py create mode 100644 physionet-django/user/migrations/0052_alter_trainingtype_required_field.py diff --git a/physionet-django/physionet/settings/base.py b/physionet-django/physionet/settings/base.py index 5a32fb7e51..5a9a1b1b15 100644 --- a/physionet-django/physionet/settings/base.py +++ b/physionet-django/physionet/settings/base.py @@ -52,6 +52,7 @@ 'background_task', 'rest_framework', + 'training', 'user', 'project', 'console', @@ -371,6 +372,7 @@ ['InlineEquation', 'BlockEquation', 'CodeSnippet', 'Table'], ['Link', 'Unlink'], ['RemoveFormat', 'Source'], + ['Image'] ], 'removeDialogTabs': 'link:advanced', 'disableNativeSpellChecker': False, @@ -604,3 +606,5 @@ class StorageTypes: # minimum number of word needed for research_summary field for Credentialing Model. MIN_WORDS_RESEARCH_SUMMARY_CREDENTIALING = config('MIN_WORDS_RESEARCH_SUMMARY_CREDENTIALING', cast=int, default=20) + +CKEDITOR_UPLOAD_PATH = 'ckeditor/' diff --git a/physionet-django/physionet/urls.py b/physionet-django/physionet/urls.py index 7911587d96..84975d667a 100644 --- a/physionet-django/physionet/urls.py +++ b/physionet-django/physionet/urls.py @@ -80,7 +80,10 @@ # path for Database List - to be deprecated soon path('rest/database-list/', database_list, - name='database_list') + name='database_list'), + + # for ckeditor + path('ckeditor/', include('ckeditor_uploader.urls')) ] if settings.ENABLE_LIGHTWAVE and ProjectFiles().is_lightwave_supported: diff --git a/physionet-django/training/__init__.py b/physionet-django/training/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/physionet-django/training/admin.py b/physionet-django/training/admin.py new file mode 100644 index 0000000000..881a801987 --- /dev/null +++ b/physionet-django/training/admin.py @@ -0,0 +1,30 @@ +from django.contrib import admin +from training import models + + +class ContentBlockInline(admin.StackedInline): + model = models.ContentBlock + extra = 1 + + +class QuizChoiceInline(admin.TabularInline): + model = models.QuizChoice + extra = 1 + + +class QuizInline(admin.StackedInline): + model = models.Quiz + inlines = [QuizChoiceInline, ] + extra = 1 + + +@admin.register(models.Quiz) +class QuizAdmin(admin.ModelAdmin): + list_display = ('training', 'question', 'order') + inlines = [QuizChoiceInline, ] + + +@admin.register(models.OnPlatformTraining) +class OnPlatformTrainingAdmin(admin.ModelAdmin): + list_display = ('training', 'version') + inlines = [ContentBlockInline, QuizInline] diff --git a/physionet-django/training/apps.py b/physionet-django/training/apps.py new file mode 100644 index 0000000000..a94e5537a6 --- /dev/null +++ b/physionet-django/training/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class TrainingConfig(AppConfig): + name = 'training' diff --git a/physionet-django/training/fixtures/demo-training.json b/physionet-django/training/fixtures/demo-training.json new file mode 100644 index 0000000000..dd1b9eacc5 --- /dev/null +++ b/physionet-django/training/fixtures/demo-training.json @@ -0,0 +1,73 @@ +[ + { + "model": "training.onplatformtraining", + "pk": 1, + "fields": { + "training": 2, + "version": 1.0 + } + }, + { + "model": "training.contentblock", + "pk": 1, + "fields": { + "training": 1, + "body": "Content Body 1", + "order": 1 + } + }, + { + "model": "training.contentblock", + "pk": 2, + "fields": { + "training": 1, + "body": "Content Body 2", + "order": 3 + } + }, + { + "model": "training.quiz", + "pk": 1, + "fields": { + "training": 1, + "question": "Which of this is not a country in America?", + "order": 2 + } + }, + { + "model": "training.quizchoice", + "pk": 1, + "fields": { + "quiz": 1, + "body": "Canada", + "is_correct": false + } + }, + { + "model": "training.quizchoice", + "pk": 2, + "fields": { + "quiz": 1, + "body": "Spain", + "is_correct": true + } + }, + { + "model": "training.quizchoice", + "pk": 3, + "fields": { + "quiz": 1, + "body": "Cuba", + "is_correct": false + } + }, + { + "model": "training.quizchoice", + "pk": 4, + "fields": { + "quiz": 1, + "body": "Mexico", + "is_correct": false + } + } +] \ No newline at end of file diff --git a/physionet-django/training/migrations/0001_initial.py b/physionet-django/training/migrations/0001_initial.py new file mode 100644 index 0000000000..e0c9775e42 --- /dev/null +++ b/physionet-django/training/migrations/0001_initial.py @@ -0,0 +1,67 @@ +# Generated by Django 4.1.5 on 2023-02-20 18:16 + +import ckeditor_uploader.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('user', '0052_alter_trainingtype_required_field'), + ] + + operations = [ + migrations.CreateModel( + name='OnPlatformTraining', + fields=[ + ('id', models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('version', models.FloatField(default=1.0, unique=True)), + ('training', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='op_trainings', to='user.trainingtype')), + ], + options={ + 'default_permissions': ('change',), + }, + ), + migrations.CreateModel( + name='Quiz', + fields=[ + ('id', models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('question', ckeditor_uploader.fields.RichTextUploadingField()), + ('order', models.PositiveIntegerField()), + ('training', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='quizzes', to='training.onplatformtraining')), + ], + ), + migrations.CreateModel( + name='QuizChoice', + fields=[ + ('id', models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('body', models.TextField()), + ('is_correct', models.BooleanField(default=False, verbose_name='Correct Choice?')), + ('quiz', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='choices', to='training.quiz')), + ], + ), + migrations.CreateModel( + name='ContentBlock', + fields=[ + ('id', models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('body', ckeditor_uploader.fields.RichTextUploadingField()), + ('order', models.PositiveIntegerField()), + ('training', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='contents', to='training.onplatformtraining')), + ], + ), + ] diff --git a/physionet-django/training/migrations/__init__.py b/physionet-django/training/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/physionet-django/training/models.py b/physionet-django/training/models.py new file mode 100644 index 0000000000..30b51153bc --- /dev/null +++ b/physionet-django/training/models.py @@ -0,0 +1,33 @@ +from django.db import models + +from ckeditor_uploader.fields import RichTextUploadingField + + +class OnPlatformTraining(models.Model): + training = models.ForeignKey('user.TrainingType', + on_delete=models.CASCADE, related_name='op_trainings') + version = models.FloatField(default=1.0, unique=True) + + class Meta: + default_permissions = ('change',) + + +class Quiz(models.Model): + question = RichTextUploadingField() + training = models.ForeignKey('training.OnPlatformTraining', + on_delete=models.CASCADE, related_name='quizzes') + order = models.PositiveIntegerField() + + +class ContentBlock(models.Model): + training = models.ForeignKey('training.OnPlatformTraining', + on_delete=models.CASCADE, related_name='contents') + body = RichTextUploadingField() + order = models.PositiveIntegerField() + + +class QuizChoice(models.Model): + quiz = models.ForeignKey('training.Quiz', + on_delete=models.CASCADE, related_name='choices') + body = models.TextField() + is_correct = models.BooleanField('Correct Choice?', default=False) diff --git a/physionet-django/training/to_do b/physionet-django/training/to_do new file mode 100644 index 0000000000..840670f1d0 --- /dev/null +++ b/physionet-django/training/to_do @@ -0,0 +1,14 @@ +Console +- Add a create training type button * +- View all Training with button beside asking to make it OP * +- Button on-top asking to create a training type + - Add a button to download sample json/ paste the sample json on the page * +- Modify the settings training page to display the latest versions +- Write test cases for them all +- Write a function that puts all validity at 30 days and sends a mail asking them to retake the training for major version change * +- Change the trainingType required_field to Platform from serializer + + +New Branch +- Create the Training App, as well as their models, admins, fixtures, migrations, apps, +- Another PR for the views and viewing it from settings \ No newline at end of file diff --git a/physionet-django/training/views.py b/physionet-django/training/views.py new file mode 100644 index 0000000000..f4fb6055dc --- /dev/null +++ b/physionet-django/training/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# write your views here diff --git a/physionet-django/user/enums.py b/physionet-django/user/enums.py index b3d32278f2..004ebbc96e 100644 --- a/physionet-django/user/enums.py +++ b/physionet-django/user/enums.py @@ -17,6 +17,7 @@ def choices(cls): class RequiredField(IntEnum): DOCUMENT = 0 URL = 1 + PLATFORM = 2 @classmethod def choices(cls): diff --git a/physionet-django/user/fixtures/demo-training-type.json b/physionet-django/user/fixtures/demo-training-type.json index 35b34ca87d..a1be555f27 100644 --- a/physionet-django/user/fixtures/demo-training-type.json +++ b/physionet-django/user/fixtures/demo-training-type.json @@ -17,6 +17,24 @@ ] } }, + { + "model": "user.trainingtype", + "pk": 2, + "fields": { + "name": "On Platform Training", + "description": "

This training teaches about name of places.


Lorem ipsum dolor sit amet. Et excepturi amet Et autem aut quisquam galisum eos totam esse ut omnis eveniet. Et eius assumenda non tempore sequiqui laboriosam est corporis dolores. Eos consequatur fugiatAut iste ad corrupti nostrum est quos autem est optio quod et iure nemo ea temporibus magni. Est placeat dicta 33 laborum nulla33 veritatis non nihil quod sit quia dolor.

Id internos repellatSed culpa cum natus internos aut enim consectetur et architecto quibusdam eum ipsam fugit et quia animi. Eos obcaecati exercitationem Ut repellat aut nemo quia ut quia perferendis.

", + "home_page": "/", + "valid_duration": "1095 00:00:00", + "required_field": 2, + "questions": [ + 1, + 2, + 3, + 4, + 5 + ] + } + }, { "model": "user.question", "pk": 1, diff --git a/physionet-django/user/fixtures/demo-user.json b/physionet-django/user/fixtures/demo-user.json index c4293d0190..1b9aaaca85 100644 --- a/physionet-django/user/fixtures/demo-user.json +++ b/physionet-django/user/fixtures/demo-user.json @@ -14362,6 +14362,11 @@ "can_review_training", "user", "trainingtype" + ], + [ + "change_onplatformtraining", + "training", + "onplatformtraining" ] ] } diff --git a/physionet-django/user/migrations/0052_alter_trainingtype_required_field.py b/physionet-django/user/migrations/0052_alter_trainingtype_required_field.py new file mode 100644 index 0000000000..ae96c835b8 --- /dev/null +++ b/physionet-django/user/migrations/0052_alter_trainingtype_required_field.py @@ -0,0 +1,21 @@ +# Generated by Django 4.1.5 on 2023-02-18 02:12 + +from django.db import migrations, models +import user.enums + + +class Migration(migrations.Migration): + + dependencies = [ + ('user', '0051_credentialapplication_auto_rejection_reason'), + ] + + operations = [ + migrations.AlterField( + model_name='trainingtype', + name='required_field', + field=models.PositiveSmallIntegerField( + choices=[(0, 'DOCUMENT'), (1, 'URL'), (2, 'PLATFORM')], + default=user.enums.RequiredField['DOCUMENT']), + ), + ]