Skip to content

Commit 1adc2c6

Browse files
authored
Merge pull request #120 from codersforcauses/i116-shared_quiz_models_app
I116 shared quiz models app
2 parents b8cb327 + b670dee commit 1adc2c6

19 files changed

Lines changed: 558 additions & 0 deletions
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Shared Models App
2+
3+
## Overview and Motivation
4+
5+
The `shared_models` app is designed to hold the models and serializers that need to be used across multiple apps. This is to allow them to all access the same underlying models that don't belong to a single app, rather than having their own versions of these models and accessing each other's at random.
6+
7+
## Models
8+
9+
### Quiz
10+
11+
There are three shared quiz models:
12+
13+
* Question - a model representing a generic quiz question. Questions come in three types (represented by the `question_type` field): multiple choice, numerical answer and short answer.
14+
* Tag - a model representing a generic tag which can be attached to many Questions (and each Question may have many associated Tags). Example Tags would include the subject and unit of study the question pertains to, as well as the topic within that subject/unit. Tags will be able to be created and modified by admins for regular users to use to tag their questions.
15+
* Answer - a model representing an answer associated with a particular Question. Each Question can have multiple associated answers. Each answer can be marked as being correct or incorrect. For the three different types of Question, the intended use of the Answer model varies:
16+
* For multiple choice questions, each Answer will appear as an option for the user taking the quiz to select. One or more Answers will be correct, and the remaining Answers will be incorrect.
17+
* For numerical answer questions, no options will be displayed, and the user must enter a numerical value into a text field. Each of the Answers associated with such a Question should have `is_correct = True`, and if the user's input matches any of these answers, they will be marked correct. Answers which have `is_correct = False` will not do anything and hence **should not be added** to numerical answer questions.
18+
* For short answer questions, the user will be given an extended text field to enter their answer into. Due to the nature of this question, automated marking is not possible, and so the user will have to self-mark their response. Upon submitting their answer, all Answers associated to the Question with `is_correct = True` will be displayed for the user to compare their response to. As with numerical answer questions, Answers with `is_correct = False` will not do anything and hence **should not be added**.
19+
20+
### Statistics
21+
22+
* QuestionResponse - a model representing a response to a Question submitted by a User, stored in the database for statistics purposes. The QuestionResponse model contains all of the necessary data for the following statistics to be surmised, as per the client's requirements:
23+
* How long the User took to complete the Question
24+
* How many Users in total have attempted a given Question
25+
* How many Users have gotten the Question right and how many have gotten it wrong
26+
* The average score (across all Users) for a given Question
27+
* The average score (across all Users) for a given Tag (i.e. subject and topic)
28+
29+
## Serializers
30+
31+
The `shared_models` app provides serializers for each of the above models.
32+
33+
## Testing
34+
35+
Unit tests can be run from within the backend Docker container using the command `python manage.py test` (more verbosely if executing from a terminal not SSH'd into the container: `docker exec -it elucidate_server python manage.py test`). Ensure you are executing the command from the same directory as `manage.py`.

server/api/apps/shared_models/__init__.py

Whitespace-only changes.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from django.contrib import admin
2+
from .models.quiz_models import Question, Tag, Answer
3+
from .models.statistics_models import QuestionResponse
4+
5+
6+
class AnswerInline(admin.TabularInline):
7+
model = Answer
8+
9+
10+
class TagInline(admin.TabularInline):
11+
model = Tag.question.through
12+
verbose_name = "Tag"
13+
14+
15+
@admin.register(Question)
16+
class QuestionAdmin(admin.ModelAdmin):
17+
inlines = [
18+
AnswerInline,
19+
TagInline,
20+
]
21+
22+
23+
admin.site.register(Tag)
24+
admin.site.register(QuestionResponse)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Generated by Django 4.0.6 on 2022-07-16 06:23
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
initial = True
9+
10+
dependencies = []
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name="Question",
15+
fields=[
16+
(
17+
"id",
18+
models.BigAutoField(
19+
auto_created=True,
20+
primary_key=True,
21+
serialize=False,
22+
verbose_name="ID",
23+
),
24+
),
25+
("text", models.TextField(blank=True, default="")),
26+
(
27+
"question_type",
28+
models.CharField(
29+
choices=[
30+
("MC", "Multiple Choice"),
31+
("NA", "Numerical Answer"),
32+
],
33+
default="MC",
34+
max_length=2,
35+
),
36+
),
37+
(
38+
"date_created",
39+
models.DateTimeField(auto_now_add=True, null=True),
40+
),
41+
],
42+
),
43+
migrations.CreateModel(
44+
name="Tag",
45+
fields=[
46+
(
47+
"id",
48+
models.BigAutoField(
49+
auto_created=True,
50+
primary_key=True,
51+
serialize=False,
52+
verbose_name="ID",
53+
),
54+
),
55+
("name", models.CharField(max_length=100)),
56+
(
57+
"question",
58+
models.ManyToManyField(to="shared_models.question"),
59+
),
60+
],
61+
),
62+
migrations.CreateModel(
63+
name="Answer",
64+
fields=[
65+
(
66+
"id",
67+
models.BigAutoField(
68+
auto_created=True,
69+
primary_key=True,
70+
serialize=False,
71+
verbose_name="ID",
72+
),
73+
),
74+
("text", models.CharField(max_length=200)),
75+
("is_correct", models.BooleanField(default=False)),
76+
(
77+
"question",
78+
models.ForeignKey(
79+
on_delete=django.db.models.deletion.CASCADE,
80+
to="shared_models.question",
81+
),
82+
),
83+
],
84+
),
85+
]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Generated by Django 4.0.6 on 2022-07-16 07:08
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("shared_models", "0001_initial"),
9+
]
10+
11+
operations = [
12+
migrations.RemoveField(
13+
model_name="tag",
14+
name="question",
15+
),
16+
migrations.DeleteModel(
17+
name="Answer",
18+
),
19+
migrations.DeleteModel(
20+
name="Question",
21+
),
22+
migrations.DeleteModel(
23+
name="Tag",
24+
),
25+
]
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# Generated by Django 4.0.6 on 2022-07-16 07:15
2+
3+
import datetime
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
8+
class Migration(migrations.Migration):
9+
initial = True
10+
11+
dependencies = [
12+
(
13+
"shared_models",
14+
"0002_remove_tag_question_delete_answer_delete_question_and_more",
15+
),
16+
]
17+
18+
operations = [
19+
migrations.CreateModel(
20+
name="Answer",
21+
fields=[
22+
(
23+
"id",
24+
models.BigAutoField(
25+
auto_created=True,
26+
primary_key=True,
27+
serialize=False,
28+
verbose_name="ID",
29+
),
30+
),
31+
("text", models.CharField(max_length=200)),
32+
("is_correct", models.BooleanField(default=False)),
33+
],
34+
),
35+
migrations.CreateModel(
36+
name="Question",
37+
fields=[
38+
(
39+
"id",
40+
models.BigAutoField(
41+
auto_created=True,
42+
primary_key=True,
43+
serialize=False,
44+
verbose_name="ID",
45+
),
46+
),
47+
("text", models.TextField(blank=True, default="")),
48+
(
49+
"question_type",
50+
models.CharField(
51+
choices=[
52+
("MC", "Multiple Choice"),
53+
("NA", "Numerical Answer"),
54+
("SA", "Short Answer"),
55+
],
56+
default="MC",
57+
max_length=2,
58+
),
59+
),
60+
(
61+
"date_created",
62+
models.DateTimeField(auto_now_add=True, null=True),
63+
),
64+
],
65+
),
66+
migrations.CreateModel(
67+
name="Tag",
68+
fields=[
69+
(
70+
"id",
71+
models.BigAutoField(
72+
auto_created=True,
73+
primary_key=True,
74+
serialize=False,
75+
verbose_name="ID",
76+
),
77+
),
78+
("name", models.CharField(max_length=100)),
79+
(
80+
"question",
81+
models.ManyToManyField(to="shared_models.question"),
82+
),
83+
],
84+
),
85+
migrations.CreateModel(
86+
name="QuestionResponse",
87+
fields=[
88+
(
89+
"id",
90+
models.BigAutoField(
91+
auto_created=True,
92+
primary_key=True,
93+
serialize=False,
94+
verbose_name="ID",
95+
),
96+
),
97+
("is_correct", models.BooleanField(default=False)),
98+
(
99+
"time_taken",
100+
models.DurationField(default=datetime.timedelta(0)),
101+
),
102+
(
103+
"date_created",
104+
models.DateTimeField(auto_now_add=True, null=True),
105+
),
106+
(
107+
"question",
108+
models.ForeignKey(
109+
on_delete=django.db.models.deletion.CASCADE,
110+
to="shared_models.question",
111+
),
112+
),
113+
(
114+
"selected_answer",
115+
models.ForeignKey(
116+
null=True,
117+
on_delete=django.db.models.deletion.CASCADE,
118+
to="shared_models.answer",
119+
),
120+
),
121+
],
122+
),
123+
migrations.AddField(
124+
model_name="answer",
125+
name="question",
126+
field=models.ForeignKey(
127+
on_delete=django.db.models.deletion.CASCADE,
128+
to="shared_models.question",
129+
),
130+
),
131+
]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 4.0.6 on 2022-07-16 07:39
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
8+
class Migration(migrations.Migration):
9+
dependencies = [
10+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
11+
("shared_models", "0003_initial"),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name="question",
17+
name="creator",
18+
field=models.ForeignKey(
19+
null=True,
20+
on_delete=django.db.models.deletion.SET_NULL,
21+
to=settings.AUTH_USER_MODEL,
22+
),
23+
),
24+
migrations.AddField(
25+
model_name="questionresponse",
26+
name="user",
27+
field=models.ForeignKey(
28+
null=True,
29+
on_delete=django.db.models.deletion.SET_NULL,
30+
to=settings.AUTH_USER_MODEL,
31+
),
32+
),
33+
]

server/api/apps/shared_models/migrations/__init__.py

Whitespace-only changes.

server/api/apps/shared_models/models/__init__.py

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
QUIZ_NAME_MAXLEN = 200
2+
QUIZ_SUBJECT_MAXLEN = 100
3+
OBJECTIVE_NAME_MAXLEN = 100
4+
TAG_NAME_MAXLEN = 100
5+
ANSWER_TEXT_MAXLEN = 200

0 commit comments

Comments
 (0)