mirror of
				https://github.com/Alexander-D-Karpov/akarpov
				synced 2025-10-31 02:17:26 +03:00 
			
		
		
		
	added test forms
This commit is contained in:
		
							parent
							
								
									4a70342669
								
							
						
					
					
						commit
						902408ca9d
					
				
							
								
								
									
										0
									
								
								akarpov/test_platform/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								akarpov/test_platform/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										21
									
								
								akarpov/test_platform/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								akarpov/test_platform/admin.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | from django.contrib import admin | ||||||
|  | 
 | ||||||
|  | from akarpov.test_platform.models import ( | ||||||
|  |     Form, | ||||||
|  |     FormHistory, | ||||||
|  |     FormPermission, | ||||||
|  |     NumberQuestion, | ||||||
|  |     NumberRangeQuestion, | ||||||
|  |     SelectAnswerQuestion, | ||||||
|  |     SelectQuestion, | ||||||
|  |     TextQuestion, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | admin.site.register(Form) | ||||||
|  | admin.site.register(TextQuestion) | ||||||
|  | admin.site.register(NumberQuestion) | ||||||
|  | admin.site.register(NumberRangeQuestion) | ||||||
|  | admin.site.register(SelectQuestion) | ||||||
|  | admin.site.register(SelectAnswerQuestion) | ||||||
|  | admin.site.register(FormPermission) | ||||||
|  | admin.site.register(FormHistory) | ||||||
							
								
								
									
										7
									
								
								akarpov/test_platform/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								akarpov/test_platform/apps.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | from django.apps import AppConfig | ||||||
|  | from django.utils.translation import gettext_lazy as _ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TestPlatformConfig(AppConfig): | ||||||
|  |     name = "akarpov.test_platform" | ||||||
|  |     verbose_name = _("Test platform") | ||||||
							
								
								
									
										270
									
								
								akarpov/test_platform/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								akarpov/test_platform/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,270 @@ | ||||||
|  | # Generated by Django 4.1.5 on 2023-02-24 16:18 | ||||||
|  | 
 | ||||||
|  | import akarpov.utils.files | ||||||
|  | from django.conf import settings | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | import uuid | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     initial = True | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||||||
|  |         ("contenttypes", "0002_remove_content_type_name"), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="BaseQuestion", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "id", | ||||||
|  |                     models.BigAutoField( | ||||||
|  |                         auto_created=True, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                         verbose_name="ID", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("question", models.CharField(max_length=250)), | ||||||
|  |                 ("help", models.CharField(blank=True, max_length=200)), | ||||||
|  |                 ("required", models.BooleanField(default=True)), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 "abstract": False, | ||||||
|  |                 "base_manager_name": "objects", | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="Form", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "id", | ||||||
|  |                     models.BigAutoField( | ||||||
|  |                         auto_created=True, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                         verbose_name="ID", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("name", models.CharField(max_length=200)), | ||||||
|  |                 ("description", models.TextField(blank=True)), | ||||||
|  |                 ("slug", models.SlugField(blank=True, max_length=20)), | ||||||
|  |                 ("public", models.BooleanField(default=True)), | ||||||
|  |                 ("anyone_can_access", models.BooleanField(default=True)), | ||||||
|  |                 ("created", models.DateTimeField(auto_now_add=True)), | ||||||
|  |                 ("updated", models.DateTimeField(auto_now=True)), | ||||||
|  |                 ( | ||||||
|  |                     "image", | ||||||
|  |                     models.ImageField( | ||||||
|  |                         blank=True, upload_to=akarpov.utils.files.user_file_upload_mixin | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("image_cropped", models.ImageField(blank=True, upload_to="cropped/")), | ||||||
|  |                 ("passed", models.IntegerField(default=0)), | ||||||
|  |                 ("time_till", models.DateTimeField(blank=True, null=True)), | ||||||
|  |                 ( | ||||||
|  |                     "creator", | ||||||
|  |                     models.ForeignKey( | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         to=settings.AUTH_USER_MODEL, | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |             ], | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="SelectAnswerQuestion", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "id", | ||||||
|  |                     models.UUIDField( | ||||||
|  |                         default=uuid.uuid4, | ||||||
|  |                         editable=False, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("value", models.CharField(max_length=150)), | ||||||
|  |                 ("correct", models.BooleanField(null=True)), | ||||||
|  |             ], | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="NumberQuestion", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "basequestion_ptr", | ||||||
|  |                     models.OneToOneField( | ||||||
|  |                         auto_created=True, | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         parent_link=True, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                         to="test_platform.basequestion", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("correct_answer", models.IntegerField()), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 "abstract": False, | ||||||
|  |                 "base_manager_name": "objects", | ||||||
|  |             }, | ||||||
|  |             bases=("test_platform.basequestion",), | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="NumberRangeQuestion", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "basequestion_ptr", | ||||||
|  |                     models.OneToOneField( | ||||||
|  |                         auto_created=True, | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         parent_link=True, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                         to="test_platform.basequestion", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("number_range_min", models.IntegerField()), | ||||||
|  |                 ("number_range_max", models.IntegerField()), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 "abstract": False, | ||||||
|  |                 "base_manager_name": "objects", | ||||||
|  |             }, | ||||||
|  |             bases=("test_platform.basequestion",), | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="SelectQuestion", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "basequestion_ptr", | ||||||
|  |                     models.OneToOneField( | ||||||
|  |                         auto_created=True, | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         parent_link=True, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                         to="test_platform.basequestion", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("min_required_answers", models.IntegerField()), | ||||||
|  |                 ("max_required_answers", models.IntegerField()), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 "abstract": False, | ||||||
|  |                 "base_manager_name": "objects", | ||||||
|  |             }, | ||||||
|  |             bases=("test_platform.basequestion",), | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="TextQuestion", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "basequestion_ptr", | ||||||
|  |                     models.OneToOneField( | ||||||
|  |                         auto_created=True, | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         parent_link=True, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                         to="test_platform.basequestion", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("correct_answer", models.CharField(max_length=250)), | ||||||
|  |                 ("answer_should_contain", models.CharField(max_length=250)), | ||||||
|  |                 ("answer_should_not_contain", models.CharField(max_length=250)), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 "abstract": False, | ||||||
|  |                 "base_manager_name": "objects", | ||||||
|  |             }, | ||||||
|  |             bases=("test_platform.basequestion",), | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="FormHistory", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "id", | ||||||
|  |                     models.BigAutoField( | ||||||
|  |                         auto_created=True, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                         verbose_name="ID", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("opened", models.DateTimeField(auto_now_add=True)), | ||||||
|  |                 ( | ||||||
|  |                     "form", | ||||||
|  |                     models.ForeignKey( | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         related_name="users_opened", | ||||||
|  |                         to="test_platform.form", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ( | ||||||
|  |                     "user", | ||||||
|  |                     models.ForeignKey( | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         related_name="opened_test_platform", | ||||||
|  |                         to=settings.AUTH_USER_MODEL, | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |             ], | ||||||
|  |         ), | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name="basequestion", | ||||||
|  |             name="form", | ||||||
|  |             field=models.ForeignKey( | ||||||
|  |                 on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                 related_name="fields", | ||||||
|  |                 to="test_platform.form", | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name="basequestion", | ||||||
|  |             name="polymorphic_ctype", | ||||||
|  |             field=models.ForeignKey( | ||||||
|  |                 editable=False, | ||||||
|  |                 null=True, | ||||||
|  |                 on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                 related_name="polymorphic_%(app_label)s.%(class)s_set+", | ||||||
|  |                 to="contenttypes.contenttype", | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="FormPermission", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "id", | ||||||
|  |                     models.BigAutoField( | ||||||
|  |                         auto_created=True, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                         verbose_name="ID", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ( | ||||||
|  |                     "form", | ||||||
|  |                     models.ForeignKey( | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         related_name="allowed", | ||||||
|  |                         to="test_platform.form", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ( | ||||||
|  |                     "user", | ||||||
|  |                     models.ForeignKey( | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         related_name="allowed_test_platform", | ||||||
|  |                         to=settings.AUTH_USER_MODEL, | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 "unique_together": {("form", "user")}, | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										0
									
								
								akarpov/test_platform/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								akarpov/test_platform/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										114
									
								
								akarpov/test_platform/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								akarpov/test_platform/models.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,114 @@ | ||||||
|  | import uuid | ||||||
|  | 
 | ||||||
|  | from django.db import models | ||||||
|  | from django.utils import timezone | ||||||
|  | from django.utils.translation import gettext_lazy as _ | ||||||
|  | from polymorphic.models import PolymorphicModel | ||||||
|  | 
 | ||||||
|  | from akarpov.users.models import User | ||||||
|  | from akarpov.utils.files import user_file_upload_mixin | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Form(models.Model): | ||||||
|  |     name = models.CharField(max_length=200, blank=False) | ||||||
|  |     description = models.TextField(blank=True) | ||||||
|  | 
 | ||||||
|  |     creator = models.ForeignKey("users.User", on_delete=models.CASCADE) | ||||||
|  |     slug = models.SlugField(max_length=20, blank=True) | ||||||
|  | 
 | ||||||
|  |     public = models.BooleanField(default=True) | ||||||
|  |     anyone_can_access = models.BooleanField(default=True) | ||||||
|  | 
 | ||||||
|  |     created = models.DateTimeField(auto_now_add=True) | ||||||
|  |     updated = models.DateTimeField(auto_now=True) | ||||||
|  | 
 | ||||||
|  |     image = models.ImageField(upload_to=user_file_upload_mixin, blank=True) | ||||||
|  |     image_cropped = models.ImageField(upload_to="cropped/", blank=True) | ||||||
|  | 
 | ||||||
|  |     passed = models.IntegerField(default=0) | ||||||
|  |     time_till = models.DateTimeField(null=True, blank=True) | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def available(self) -> bool: | ||||||
|  |         return ( | ||||||
|  |             self.time_till.timestamp() >= timezone.now().timestamp() | ||||||
|  |             if self.time_till | ||||||
|  |             else True | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return f"form: {self.name}" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class BaseQuestion(PolymorphicModel): | ||||||
|  |     type = _("No type") | ||||||
|  |     form: Form = models.ForeignKey( | ||||||
|  |         "test_platform.Form", related_name="fields", on_delete=models.CASCADE | ||||||
|  |     ) | ||||||
|  |     question = models.CharField(max_length=250, blank=False) | ||||||
|  |     help = models.CharField(max_length=200, blank=True) | ||||||
|  |     required = models.BooleanField(default=True) | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return f"{self.type} - {self.question}" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TextQuestion(BaseQuestion): | ||||||
|  |     type = _("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) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class NumberQuestion(BaseQuestion): | ||||||
|  |     type = _("Number question") | ||||||
|  |     correct_answer = models.IntegerField() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class NumberRangeQuestion(BaseQuestion): | ||||||
|  |     type = _("Number question") | ||||||
|  |     number_range_min = models.IntegerField(blank=False) | ||||||
|  |     number_range_max = models.IntegerField(blank=False) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SelectQuestion(BaseQuestion): | ||||||
|  |     min_required_answers = models.IntegerField(blank=False) | ||||||
|  |     max_required_answers = models.IntegerField(blank=False) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SelectAnswerQuestion(models.Model): | ||||||
|  |     id: uuid.UUID = models.UUIDField( | ||||||
|  |         primary_key=True, default=uuid.uuid4, editable=False | ||||||
|  |     ) | ||||||
|  |     value = models.CharField(max_length=150) | ||||||
|  |     correct = models.BooleanField(null=True) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class FormPermission(models.Model): | ||||||
|  |     form: Form = models.ForeignKey( | ||||||
|  |         "test_platform.Form", related_name="allowed", on_delete=models.CASCADE | ||||||
|  |     ) | ||||||
|  |     user: User = models.ForeignKey( | ||||||
|  |         "users.User", related_name="allowed_test_platform", on_delete=models.CASCADE | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         unique_together = ["form", "user"] | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return f"user {self.user.username} can access {self.form.name}" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class FormHistory(models.Model): | ||||||
|  |     form: Form = models.ForeignKey( | ||||||
|  |         "test_platform.Form", related_name="users_opened", on_delete=models.CASCADE | ||||||
|  |     ) | ||||||
|  |     user: User = models.ForeignKey( | ||||||
|  |         "users.User", related_name="opened_test_platform", on_delete=models.CASCADE | ||||||
|  |     ) | ||||||
|  |     opened = models.DateTimeField(auto_now_add=True) | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return ( | ||||||
|  |             f"user {self.user.username} opened form {self.form.name} at {self.opened}" | ||||||
|  |         ) | ||||||
							
								
								
									
										0
									
								
								akarpov/test_platform/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								akarpov/test_platform/views.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -142,6 +142,7 @@ | ||||||
|     "akarpov.blog", |     "akarpov.blog", | ||||||
|     "akarpov.files", |     "akarpov.files", | ||||||
|     "akarpov.pipeliner", |     "akarpov.pipeliner", | ||||||
|  |     "akarpov.test_platform", | ||||||
|     "akarpov.tools.shortener", |     "akarpov.tools.shortener", | ||||||
|     "akarpov.tools.qr", |     "akarpov.tools.qr", | ||||||
| ] | ] | ||||||
|  |  | ||||||
							
								
								
									
										601
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										601
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
		Reference in New Issue
	
	Block a user