From da12a206be15f780c22db628f14f5ae986deb6a1 Mon Sep 17 00:00:00 2001 From: Alexandr Karpov Date: Sun, 26 Feb 2023 22:12:12 +0300 Subject: [PATCH] added form generate and process logic --- akarpov/static/js/form_logic.js | 0 akarpov/templates/base.html | 10 +-- akarpov/templates/test_platform/admin.html | 10 +++ akarpov/templates/test_platform/create.html | 65 +++++++++++++++---- akarpov/test_platform/forms.py | 8 ++- ...rangequestion_number_range_max_and_more.py | 48 ++++++++++++++ akarpov/test_platform/models.py | 23 ++++--- akarpov/test_platform/services/forms.py | 33 +++++++++- akarpov/test_platform/views.py | 6 +- poetry.lock | 20 +++--- 10 files changed, 177 insertions(+), 46 deletions(-) create mode 100644 akarpov/static/js/form_logic.js create mode 100644 akarpov/templates/test_platform/admin.html create mode 100644 akarpov/test_platform/migrations/0003_alter_numberrangequestion_number_range_max_and_more.py diff --git a/akarpov/static/js/form_logic.js b/akarpov/static/js/form_logic.js new file mode 100644 index 0000000..e69de29 diff --git a/akarpov/templates/base.html b/akarpov/templates/base.html index 256941b..eb7dad2 100644 --- a/akarpov/templates/base.html +++ b/akarpov/templates/base.html @@ -25,15 +25,9 @@ ================================================== --> {# Placed at the top of the document so pages load faster with defer #} {% block javascript %} - - - - - - - {% endblock javascript %} - + + diff --git a/akarpov/templates/test_platform/admin.html b/akarpov/templates/test_platform/admin.html new file mode 100644 index 0000000..04e75bc --- /dev/null +++ b/akarpov/templates/test_platform/admin.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + diff --git a/akarpov/templates/test_platform/create.html b/akarpov/templates/test_platform/create.html index 8fb047d..3819a34 100644 --- a/akarpov/templates/test_platform/create.html +++ b/akarpov/templates/test_platform/create.html @@ -5,22 +5,63 @@ {% block title %}Creating new Form on akarpov{% endblock %} {% block content %} -
+ {% csrf_token %} {{ form.media }} {% for field in form %} {{ field|as_crispy_field }} {% endfor %} -
- -
-{% for question, question_form in questions.items %} -{{ question }} -{% for field in question_form %} - {{ field|as_crispy_field }} -{% endfor %} -{% endfor %}
+
+
+
+ + +
+
+
+
+ +
+{% endblock %} + +{% block inline_javascript %} + {% endblock %} diff --git a/akarpov/test_platform/forms.py b/akarpov/test_platform/forms.py index efb0f26..e0f41de 100644 --- a/akarpov/test_platform/forms.py +++ b/akarpov/test_platform/forms.py @@ -12,8 +12,12 @@ class FormFormClass(forms.ModelForm): - time_since = forms.DateField(widget=forms.TextInput(attrs={"type": "date"})) - time_till = forms.DateField(widget=forms.TextInput(attrs={"type": "date"})) + time_since = forms.DateField( + widget=forms.TextInput(attrs={"type": "date"}), required=False + ) + time_till = forms.DateField( + widget=forms.TextInput(attrs={"type": "date"}), required=False + ) class Meta: model = Form diff --git a/akarpov/test_platform/migrations/0003_alter_numberrangequestion_number_range_max_and_more.py b/akarpov/test_platform/migrations/0003_alter_numberrangequestion_number_range_max_and_more.py new file mode 100644 index 0000000..134bdb7 --- /dev/null +++ b/akarpov/test_platform/migrations/0003_alter_numberrangequestion_number_range_max_and_more.py @@ -0,0 +1,48 @@ +# Generated by Django 4.1.7 on 2023-02-25 11:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("test_platform", "0002_form_time_since_selectanswerquestion_question"), + ] + + operations = [ + migrations.AlterField( + model_name="numberrangequestion", + name="number_range_max", + field=models.IntegerField(blank=True), + ), + migrations.AlterField( + model_name="numberrangequestion", + name="number_range_min", + field=models.IntegerField(blank=True), + ), + migrations.AlterField( + model_name="selectquestion", + name="max_required_answers", + field=models.IntegerField(blank=True), + ), + migrations.AlterField( + model_name="selectquestion", + name="min_required_answers", + field=models.IntegerField(blank=True), + ), + migrations.AlterField( + model_name="textquestion", + name="answer_should_contain", + field=models.CharField(blank=True, max_length=250), + ), + migrations.AlterField( + model_name="textquestion", + name="answer_should_not_contain", + field=models.CharField(blank=True, max_length=250), + ), + migrations.AlterField( + model_name="textquestion", + name="correct_answer", + field=models.CharField(blank=True, max_length=250), + ), + ] diff --git a/akarpov/test_platform/models.py b/akarpov/test_platform/models.py index ab4e806..28b5304 100644 --- a/akarpov/test_platform/models.py +++ b/akarpov/test_platform/models.py @@ -1,6 +1,7 @@ import uuid from django.db import models +from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _ from polymorphic.models import PolymorphicModel @@ -38,6 +39,10 @@ def available(self) -> bool: else True ) + def get_absolute_url(self): + # TODO change to admin + return reverse("test_platform:create") + def __str__(self): return f"form: {self.name}" @@ -59,29 +64,29 @@ def __str__(self): class TextQuestion(BaseQuestion): type = "text" type_plural = _("Text question") - correct_answer = models.CharField(max_length=250, blank=False) - answer_should_contain = models.CharField(max_length=250, blank=False) - answer_should_not_contain = models.CharField(max_length=250, blank=False) + correct_answer = models.CharField(max_length=250, blank=True) + answer_should_contain = models.CharField(max_length=250, blank=True) + answer_should_not_contain = models.CharField(max_length=250, blank=True) class NumberQuestion(BaseQuestion): type = "number" type_plural = _("Number question") - correct_answer = models.IntegerField() + correct_answer = models.IntegerField(blank=True) class NumberRangeQuestion(BaseQuestion): type = "range" - type_plural = _("Number question") - number_range_min = models.IntegerField(blank=False) - number_range_max = models.IntegerField(blank=False) + type_plural = _("Number range question") + number_range_min = models.IntegerField(blank=True) + number_range_max = models.IntegerField(blank=True) class SelectQuestion(BaseQuestion): type = "select" type_plural = _("Select question") - min_required_answers = models.IntegerField(blank=False) - max_required_answers = models.IntegerField(blank=False) + min_required_answers = models.IntegerField(blank=True) + max_required_answers = models.IntegerField(blank=True) class SelectAnswerQuestion(models.Model): diff --git a/akarpov/test_platform/services/forms.py b/akarpov/test_platform/services/forms.py index c28e7fc..295dbc0 100644 --- a/akarpov/test_platform/services/forms.py +++ b/akarpov/test_platform/services/forms.py @@ -1,4 +1,5 @@ from akarpov.test_platform.forms import ( + BaseQuestionForm, NumberQuestionForm, NumberRangeQuestionForm, SelectQuestionForm, @@ -20,9 +21,35 @@ } -def get_question_types(): +def _get_fields_from_type(type: str): + for question in BaseQuestion.get_subclasses()[::-1]: + if question.type == type: + form = question_forms[question] + return form.Meta.fields + raise ValueError + + +def get_question_types() -> dict[BaseQuestion, BaseQuestionForm]: res = {} - questions = BaseQuestion.get_subclasses() + questions = BaseQuestion.get_subclasses()[::-1] for question in questions: - res[question.type_plural] = question_forms[question] + res[question] = question_forms[question] + return res + + +def parse_form_create(values) -> list[dict[str, str]]: + offset: dict[str, int] = {} + res: list[dict[str, str]] = [] + question_amount = len(values.getlist("type")) + for i in range(question_amount): + type = values.getlist("type")[i] + res.append({"type": type}) + fields = _get_fields_from_type(type) + for field in fields: + if field in offset: + offset[field] += 1 + else: + offset[field] = 0 + value = values.getlist(field)[offset[field]] + res[i][field] = value return res diff --git a/akarpov/test_platform/views.py b/akarpov/test_platform/views.py index ee819fc..4c9079b 100644 --- a/akarpov/test_platform/views.py +++ b/akarpov/test_platform/views.py @@ -1,12 +1,13 @@ from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.messages.views import SuccessMessageMixin from django.views.generic import CreateView from akarpov.test_platform.forms import FormFormClass from akarpov.test_platform.models import Form -from akarpov.test_platform.services.forms import get_question_types +from akarpov.test_platform.services.forms import get_question_types, parse_form_create -class FromCreateView(LoginRequiredMixin, CreateView): +class FromCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): model = Form form_class = FormFormClass @@ -19,6 +20,7 @@ def get_context_data(self, **kwargs): def form_valid(self, form): form.instance.creator = self.request.user + print(parse_form_create(self.request.POST)) return super().form_valid(form) diff --git a/poetry.lock b/poetry.lock index ed13e35..3519c03 100644 --- a/poetry.lock +++ b/poetry.lock @@ -988,14 +988,14 @@ Pillow = ">=9.0.0" [[package]] name = "django-cors-headers" -version = "3.13.0" +version = "3.14.0" description = "django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS)." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "django-cors-headers-3.13.0.tar.gz", hash = "sha256:f9dc6b4e3f611c3199700b3e5f3398c28757dcd559c2f82932687f3d0443cfdf"}, - {file = "django_cors_headers-3.13.0-py3-none-any.whl", hash = "sha256:37e42883b5f1f2295df6b4bba96eb2417a14a03270cb24b2a07f021cd4487cf4"}, + {file = "django_cors_headers-3.14.0-py3-none-any.whl", hash = "sha256:684180013cc7277bdd8702b80a3c5a4b3fcae4abb2bf134dceb9f5dfe300228e"}, + {file = "django_cors_headers-3.14.0.tar.gz", hash = "sha256:5fbd58a6fb4119d975754b2bc090f35ec160a8373f276612c675b00e8a138739"}, ] [package.dependencies] @@ -1492,14 +1492,14 @@ doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"] [[package]] name = "faker" -version = "17.1.0" +version = "17.3.0" description = "Faker is a Python package that generates fake data for you." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "Faker-17.1.0-py3-none-any.whl", hash = "sha256:abc383d8e02403fe2aa3b5369283dd468494a450533b513ab6d23637f3f25396"}, - {file = "Faker-17.1.0.tar.gz", hash = "sha256:b94dc47512234fc5fff6f0752aaddd5e6ffea550f6ba31f4865b48d2b35429a1"}, + {file = "Faker-17.3.0-py3-none-any.whl", hash = "sha256:1dfffa43b4492b899a839619f2056060b585ba0bc76f329525194ca04dde0988"}, + {file = "Faker-17.3.0.tar.gz", hash = "sha256:26b2864a5332094f2c7f3968deebabce69be39ed5db4dbf22b4fa0ba3d1acdae"}, ] [package.dependencies] @@ -2051,7 +2051,6 @@ category = "main" optional = false python-versions = "*" files = [ - {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, ] @@ -3679,6 +3678,7 @@ category = "main" optional = false python-versions = "*" files = [ + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, ] @@ -3703,14 +3703,14 @@ watchdog = ["watchdog"] [[package]] name = "whitenoise" -version = "6.3.0" +version = "6.4.0" description = "Radically simplified static file serving for WSGI applications" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "whitenoise-6.3.0-py3-none-any.whl", hash = "sha256:cf8ecf56d86ba1c734fdb5ef6127312e39e92ad5947fef9033dc9e43ba2777d9"}, - {file = "whitenoise-6.3.0.tar.gz", hash = "sha256:fe0af31504ab08faa1ec7fc02845432096e40cc1b27e6a7747263d7b30fb51fa"}, + {file = "whitenoise-6.4.0-py3-none-any.whl", hash = "sha256:599dc6ca57e48929dfeffb2e8e187879bfe2aed0d49ca419577005b7f2cc930b"}, + {file = "whitenoise-6.4.0.tar.gz", hash = "sha256:a02d6660ad161ff17e3042653c8e3f5ecbb2a2481a006bde125b9efb9a30113a"}, ] [package.extras]