added test app forms

This commit is contained in:
Alexander Karpov 2023-02-25 00:05:22 +03:00
parent 902408ca9d
commit 878158aa09
7 changed files with 154 additions and 9 deletions

View File

@ -5,3 +5,9 @@
class TestPlatformConfig(AppConfig): class TestPlatformConfig(AppConfig):
name = "akarpov.test_platform" name = "akarpov.test_platform"
verbose_name = _("Test platform") verbose_name = _("Test platform")
def ready(self):
try:
import akarpov.test_platform.signals # noqa F401
except ImportError:
pass

View File

@ -0,0 +1,79 @@
from abc import ABC
from django import forms
from akarpov.test_platform.models import (
BaseQuestion,
Form,
NumberQuestion,
NumberRangeQuestion,
SelectAnswerQuestion,
SelectQuestion,
TextQuestion,
)
class FormFormClass(forms.ModelForm):
class Meta:
model = Form
fields = ["name", "description", "public", "image", "time_since", "time_till"]
class BaseQuestionForm(forms.ModelForm, ABC):
class Meta:
model = BaseQuestion
fields = ["question", "help", "required"]
class TextQuestionForm(BaseQuestionForm):
def __init__(self, *args, **kwargs):
super(BaseQuestionForm, self).__init__(*args, **kwargs)
class Meta(BaseQuestionForm.Meta):
model = TextQuestion
fields = BaseQuestionForm.Meta.fields + [
"correct_answer",
"answer_should_contain",
"answer_should_not_contain",
]
class NumberQuestionForm(BaseQuestionForm):
def __init__(self, *args, **kwargs):
super(BaseQuestionForm, self).__init__(*args, **kwargs)
class Meta(BaseQuestionForm.Meta):
model = NumberQuestion
fields = BaseQuestionForm.Meta.fields + [
"correct_answer",
]
class NumberRangeQuestionForm(BaseQuestionForm):
def __init__(self, *args, **kwargs):
super(BaseQuestionForm, self).__init__(*args, **kwargs)
class Meta(BaseQuestionForm.Meta):
model = NumberRangeQuestion
fields = BaseQuestionForm.Meta.fields + [
"number_range_min",
"number_range_max",
]
class SelectQuestionForm(BaseQuestionForm):
def __init__(self, *args, **kwargs):
super(BaseQuestionForm, self).__init__(*args, **kwargs)
class Meta(BaseQuestionForm.Meta):
model = SelectQuestion
fields = BaseQuestionForm.Meta.fields + [
"min_required_answers",
"max_required_answers",
]
class SelectAnswerQuestionForm(forms.ModelForm):
class Meta:
model = SelectAnswerQuestion
fields = ["value", "correct"]

View File

@ -0,0 +1,30 @@
# Generated by Django 4.1.7 on 2023-02-24 20:00
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("test_platform", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="form",
name="time_since",
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name="selectanswerquestion",
name="question",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
related_name="answers",
to="test_platform.selectquestion",
),
preserve_default=False,
),
]

View File

@ -6,6 +6,7 @@
from polymorphic.models import PolymorphicModel from polymorphic.models import PolymorphicModel
from akarpov.users.models import User from akarpov.users.models import User
from akarpov.utils.base import SubclassesMixin
from akarpov.utils.files import user_file_upload_mixin from akarpov.utils.files import user_file_upload_mixin
@ -26,6 +27,7 @@ class Form(models.Model):
image_cropped = models.ImageField(upload_to="cropped/", blank=True) image_cropped = models.ImageField(upload_to="cropped/", blank=True)
passed = models.IntegerField(default=0) passed = models.IntegerField(default=0)
time_since = models.DateTimeField(null=True, blank=True)
time_till = models.DateTimeField(null=True, blank=True) time_till = models.DateTimeField(null=True, blank=True)
@property @property
@ -40,8 +42,9 @@ def __str__(self):
return f"form: {self.name}" return f"form: {self.name}"
class BaseQuestion(PolymorphicModel): class BaseQuestion(PolymorphicModel, SubclassesMixin):
type = _("No type") type = "no_type"
type_plural = _("No type")
form: Form = models.ForeignKey( form: Form = models.ForeignKey(
"test_platform.Form", related_name="fields", on_delete=models.CASCADE "test_platform.Form", related_name="fields", on_delete=models.CASCADE
) )
@ -54,24 +57,29 @@ def __str__(self):
class TextQuestion(BaseQuestion): class TextQuestion(BaseQuestion):
type = _("Text question") type = "text"
type_plural = _("Text question")
correct_answer = models.CharField(max_length=250, blank=False) correct_answer = models.CharField(max_length=250, blank=False)
answer_should_contain = 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) answer_should_not_contain = models.CharField(max_length=250, blank=False)
class NumberQuestion(BaseQuestion): class NumberQuestion(BaseQuestion):
type = _("Number question") type = "number"
type_plural = _("Number question")
correct_answer = models.IntegerField() correct_answer = models.IntegerField()
class NumberRangeQuestion(BaseQuestion): class NumberRangeQuestion(BaseQuestion):
type = _("Number question") type = "range"
type_plural = _("Number question")
number_range_min = models.IntegerField(blank=False) number_range_min = models.IntegerField(blank=False)
number_range_max = models.IntegerField(blank=False) number_range_max = models.IntegerField(blank=False)
class SelectQuestion(BaseQuestion): class SelectQuestion(BaseQuestion):
type = "select"
type_plural = _("Select question")
min_required_answers = models.IntegerField(blank=False) min_required_answers = models.IntegerField(blank=False)
max_required_answers = models.IntegerField(blank=False) max_required_answers = models.IntegerField(blank=False)
@ -80,6 +88,9 @@ class SelectAnswerQuestion(models.Model):
id: uuid.UUID = models.UUIDField( id: uuid.UUID = models.UUIDField(
primary_key=True, default=uuid.uuid4, editable=False primary_key=True, default=uuid.uuid4, editable=False
) )
question = models.ForeignKey(
"test_platform.SelectQuestion", related_name="answers", on_delete=models.CASCADE
)
value = models.CharField(max_length=150) value = models.CharField(max_length=150)
correct = models.BooleanField(null=True) correct = models.BooleanField(null=True)

View File

19
akarpov/utils/base.py Normal file
View File

@ -0,0 +1,19 @@
from django.contrib.contenttypes.models import ContentType
def all_subclasses(cls):
return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c)]
)
class SubclassesMixin:
@classmethod
def get_subclasses(cls):
content_types = ContentType.objects.filter(app_label=cls._meta.app_label)
models = [ct.model_class() for ct in content_types]
return [
model
for model in models
if (model is not None and issubclass(model, cls) and model is not cls)
]

8
poetry.lock generated
View File

@ -1492,14 +1492,14 @@ doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"]
[[package]] [[package]]
name = "faker" name = "faker"
version = "17.0.0" version = "17.1.0"
description = "Faker is a Python package that generates fake data for you." description = "Faker is a Python package that generates fake data for you."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "Faker-17.0.0-py3-none-any.whl", hash = "sha256:21c3c6c45183308151c14f62afe59bf54ace68f663e0180973698ba2a9a3b2c4"}, {file = "Faker-17.1.0-py3-none-any.whl", hash = "sha256:abc383d8e02403fe2aa3b5369283dd468494a450533b513ab6d23637f3f25396"},
{file = "Faker-17.0.0.tar.gz", hash = "sha256:17cf85aeb0363a3384ccd4c1f52b52ec8f414c7afaab74ae1f4c3e09a06e14de"}, {file = "Faker-17.1.0.tar.gz", hash = "sha256:b94dc47512234fc5fff6f0752aaddd5e6ffea550f6ba31f4865b48d2b35429a1"},
] ]
[package.dependencies] [package.dependencies]
@ -2051,6 +2051,7 @@ category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
{file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"},
{file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"},
] ]
@ -3678,7 +3679,6 @@ category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
{file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"},
{file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"},
] ]