Pamela Fox
commited on
Commit
·
f243d4f
1
Parent(s):
4114b0e
Flake8 and black
Browse files- .flake8 +5 -0
- README.md +1 -1
- manage.py +6 -6
- pyproject.toml +10 -0
- quizsite/production.py +11 -11
- quizsite/settings.py +5 -5
- quizsite/urls.py +3 -3
- quizsite/wsgi.py +1 -1
- quizzes/admin.py +6 -2
- quizzes/models.py +41 -43
- quizzes/tests.py +1 -1
- quizzes/urls.py +6 -6
- quizzes/views.py +20 -8
- requirements-dev.txt +4 -0
.flake8
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[flake8]
|
2 |
+
ignore = D203
|
3 |
+
max-complexity = 10
|
4 |
+
max-line-length = 120
|
5 |
+
exclude = .venv
|
README.md
CHANGED
@@ -15,7 +15,7 @@ https://django-example-quizsite.azurewebsites.net/quizzes/
|
|
15 |
Install the requirements:
|
16 |
|
17 |
```
|
18 |
-
pip install -r requirements.txt
|
19 |
```
|
20 |
|
21 |
Create a local PostGreSQL database called "quizsite"
|
|
|
15 |
Install the requirements:
|
16 |
|
17 |
```
|
18 |
+
pip install -r requirements-dev.txt
|
19 |
```
|
20 |
|
21 |
Create a local PostGreSQL database called "quizsite"
|
manage.py
CHANGED
@@ -11,13 +11,13 @@ def main():
|
|
11 |
# If WEBSITE_HOSTNAME is defined as an environment variable, then we're running on Azure App Service
|
12 |
|
13 |
# Only for Local Development - Load environment variables from the .env file
|
14 |
-
if not
|
15 |
print("Loading environment variables for .env file")
|
16 |
-
load_dotenv(
|
17 |
|
18 |
# When running on Azure App Service you should use the production settings.
|
19 |
-
settings_module = "quizsite.production" if
|
20 |
-
os.environ.setdefault(
|
21 |
|
22 |
try:
|
23 |
from django.core.management import execute_from_command_line
|
@@ -30,5 +30,5 @@ def main():
|
|
30 |
execute_from_command_line(sys.argv)
|
31 |
|
32 |
|
33 |
-
if __name__ ==
|
34 |
-
main()
|
|
|
11 |
# If WEBSITE_HOSTNAME is defined as an environment variable, then we're running on Azure App Service
|
12 |
|
13 |
# Only for Local Development - Load environment variables from the .env file
|
14 |
+
if "WEBSITE_HOSTNAME" not in os.environ:
|
15 |
print("Loading environment variables for .env file")
|
16 |
+
load_dotenv("./.env")
|
17 |
|
18 |
# When running on Azure App Service you should use the production settings.
|
19 |
+
settings_module = "quizsite.production" if "WEBSITE_HOSTNAME" in os.environ else "quizsite.settings"
|
20 |
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module)
|
21 |
|
22 |
try:
|
23 |
from django.core.management import execute_from_command_line
|
|
|
30 |
execute_from_command_line(sys.argv)
|
31 |
|
32 |
|
33 |
+
if __name__ == "__main__":
|
34 |
+
main()
|
pyproject.toml
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[tool.black]
|
2 |
+
line-length = 120
|
3 |
+
target-version = ['py39']
|
4 |
+
exclude = '''
|
5 |
+
/(
|
6 |
+
| \.venv
|
7 |
+
| migrations
|
8 |
+
)/
|
9 |
+
|
10 |
+
'''
|
quizsite/production.py
CHANGED
@@ -1,23 +1,23 @@
|
|
1 |
-
from .settings import *
|
2 |
import os
|
3 |
|
4 |
# Configure the domain name using the environment variable
|
5 |
# that Azure automatically creates for us.
|
6 |
-
ALLOWED_HOSTS = [os.environ[
|
7 |
-
CSRF_TRUSTED_ORIGINS = [
|
8 |
DEBUG = False
|
9 |
|
10 |
# DBHOST is only the server name, not the full URL
|
11 |
-
hostname = os.environ[
|
12 |
|
13 |
# Configure Postgres database; the full username for PostgreSQL flexible server is
|
14 |
# username (not @sever-name).
|
15 |
DATABASES = {
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
}
|
23 |
-
}
|
|
|
1 |
+
from .settings import * # noqa
|
2 |
import os
|
3 |
|
4 |
# Configure the domain name using the environment variable
|
5 |
# that Azure automatically creates for us.
|
6 |
+
ALLOWED_HOSTS = [os.environ["WEBSITE_HOSTNAME"]] if "WEBSITE_HOSTNAME" in os.environ else []
|
7 |
+
CSRF_TRUSTED_ORIGINS = ["https://" + os.environ["WEBSITE_HOSTNAME"]] if "WEBSITE_HOSTNAME" in os.environ else []
|
8 |
DEBUG = False
|
9 |
|
10 |
# DBHOST is only the server name, not the full URL
|
11 |
+
hostname = os.environ["DBHOST"]
|
12 |
|
13 |
# Configure Postgres database; the full username for PostgreSQL flexible server is
|
14 |
# username (not @sever-name).
|
15 |
DATABASES = {
|
16 |
+
"default": {
|
17 |
+
"ENGINE": "django.db.backends.postgresql",
|
18 |
+
"NAME": os.environ["DBNAME"],
|
19 |
+
"HOST": hostname + ".postgres.database.azure.com",
|
20 |
+
"USER": os.environ["DBUSER"],
|
21 |
+
"PASSWORD": os.environ["DBPASS"],
|
22 |
}
|
23 |
+
}
|
quizsite/settings.py
CHANGED
@@ -33,7 +33,7 @@ ALLOWED_HOSTS = []
|
|
33 |
# Application definition
|
34 |
|
35 |
INSTALLED_APPS = [
|
36 |
-
|
37 |
"django.contrib.admin",
|
38 |
"django.contrib.auth",
|
39 |
"django.contrib.contenttypes",
|
@@ -79,10 +79,10 @@ WSGI_APPLICATION = "quizsite.wsgi.application"
|
|
79 |
DATABASES = {
|
80 |
"default": {
|
81 |
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
}
|
87 |
}
|
88 |
|
|
|
33 |
# Application definition
|
34 |
|
35 |
INSTALLED_APPS = [
|
36 |
+
"quizzes.apps.QuizzesConfig",
|
37 |
"django.contrib.admin",
|
38 |
"django.contrib.auth",
|
39 |
"django.contrib.contenttypes",
|
|
|
79 |
DATABASES = {
|
80 |
"default": {
|
81 |
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
82 |
+
"NAME": os.environ["DBNAME"],
|
83 |
+
"HOST": os.environ["DBHOST"],
|
84 |
+
"USER": os.environ["DBUSER"],
|
85 |
+
"PASSWORD": os.environ["DBPASS"],
|
86 |
}
|
87 |
}
|
88 |
|
quizsite/urls.py
CHANGED
@@ -17,6 +17,6 @@ from django.contrib import admin
|
|
17 |
from django.urls import include, path
|
18 |
|
19 |
urlpatterns = [
|
20 |
-
path(
|
21 |
-
path(
|
22 |
-
]
|
|
|
17 |
from django.urls import include, path
|
18 |
|
19 |
urlpatterns = [
|
20 |
+
path("quizzes/", include("quizzes.urls")),
|
21 |
+
path("admin/", admin.site.urls),
|
22 |
+
]
|
quizsite/wsgi.py
CHANGED
@@ -11,7 +11,7 @@ import os
|
|
11 |
|
12 |
from django.core.wsgi import get_wsgi_application
|
13 |
|
14 |
-
settings_module =
|
15 |
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module)
|
16 |
|
17 |
application = get_wsgi_application()
|
|
|
11 |
|
12 |
from django.core.wsgi import get_wsgi_application
|
13 |
|
14 |
+
settings_module = "quizsite.production" if "WEBSITE_HOSTNAME" in os.environ else "quizsite.settings"
|
15 |
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module)
|
16 |
|
17 |
application = get_wsgi_application()
|
quizzes/admin.py
CHANGED
@@ -1,15 +1,19 @@
|
|
1 |
from django.contrib import admin
|
2 |
-
from .models import Quiz, Question,
|
3 |
|
4 |
admin.site.register(Quiz)
|
5 |
|
|
|
6 |
class FreeTextAnswerInline(admin.StackedInline):
|
7 |
model = FreeTextAnswer
|
8 |
|
|
|
9 |
class MultipleChoiceAnswerInline(admin.StackedInline):
|
10 |
model = MultipleChoiceAnswer
|
11 |
|
|
|
12 |
class QuestionAdmin(admin.ModelAdmin):
|
13 |
inlines = [FreeTextAnswerInline, MultipleChoiceAnswerInline]
|
14 |
|
15 |
-
|
|
|
|
1 |
from django.contrib import admin
|
2 |
+
from .models import Quiz, Question, FreeTextAnswer, MultipleChoiceAnswer
|
3 |
|
4 |
admin.site.register(Quiz)
|
5 |
|
6 |
+
|
7 |
class FreeTextAnswerInline(admin.StackedInline):
|
8 |
model = FreeTextAnswer
|
9 |
|
10 |
+
|
11 |
class MultipleChoiceAnswerInline(admin.StackedInline):
|
12 |
model = MultipleChoiceAnswer
|
13 |
|
14 |
+
|
15 |
class QuestionAdmin(admin.ModelAdmin):
|
16 |
inlines = [FreeTextAnswerInline, MultipleChoiceAnswerInline]
|
17 |
|
18 |
+
|
19 |
+
admin.site.register(Question, QuestionAdmin)
|
quizzes/models.py
CHANGED
@@ -3,62 +3,60 @@ from django.contrib.postgres import fields
|
|
3 |
|
4 |
|
5 |
class Quiz(models.Model):
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
|
27 |
|
28 |
class Question(models.Model):
|
29 |
-
|
30 |
-
|
|
|
|
|
|
|
31 |
|
32 |
-
def __str__(self):
|
33 |
-
return self.prompt
|
34 |
|
35 |
class Answer(models.Model):
|
36 |
-
|
37 |
-
|
38 |
-
on_delete=models.CASCADE
|
39 |
-
)
|
40 |
-
correct_answer = models.CharField(max_length=200)
|
41 |
|
|
|
|
|
42 |
|
43 |
-
class Meta:
|
44 |
-
abstract = True
|
45 |
|
46 |
class FreeTextAnswer(Answer):
|
47 |
-
|
|
|
|
|
|
|
48 |
|
49 |
-
|
50 |
-
|
|
|
|
|
51 |
|
52 |
-
def is_correct(self, user_answer):
|
53 |
-
if not self.case_sensitive:
|
54 |
-
return user_answer.lower() == self.correct_answer.lower()
|
55 |
-
return user_answer == self.correct_answer
|
56 |
|
57 |
class MultipleChoiceAnswer(Answer):
|
58 |
-
|
59 |
|
60 |
-
|
61 |
-
|
62 |
|
63 |
-
|
64 |
-
|
|
|
3 |
|
4 |
|
5 |
class Quiz(models.Model):
|
6 |
+
name = models.CharField(max_length=100)
|
7 |
+
|
8 |
+
def __str__(self):
|
9 |
+
return f"<Quiz: {self.name}>"
|
10 |
+
|
11 |
+
def display(self):
|
12 |
+
# Display the quiz name
|
13 |
+
print(f"Welcome to the quiz on {self.name}!")
|
14 |
+
# Initialize the correct counter to 0
|
15 |
+
correct_count = 0
|
16 |
+
# Iterate through the questions
|
17 |
+
# Display each question
|
18 |
+
# Increment the correct counter accordingly
|
19 |
+
for question in self.questions:
|
20 |
+
question.display()
|
21 |
+
if question.answer_status == "correct":
|
22 |
+
correct_count += 1
|
23 |
+
# Print the ratio of correct/total
|
24 |
+
print(f"You got {correct_count}/{len(self.questions)} correct.")
|
|
|
25 |
|
26 |
|
27 |
class Question(models.Model):
|
28 |
+
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
|
29 |
+
prompt = models.CharField(max_length=200)
|
30 |
+
|
31 |
+
def __str__(self):
|
32 |
+
return self.prompt
|
33 |
|
|
|
|
|
34 |
|
35 |
class Answer(models.Model):
|
36 |
+
question = models.OneToOneField(Question, on_delete=models.CASCADE)
|
37 |
+
correct_answer = models.CharField(max_length=200)
|
|
|
|
|
|
|
38 |
|
39 |
+
class Meta:
|
40 |
+
abstract = True
|
41 |
|
|
|
|
|
42 |
|
43 |
class FreeTextAnswer(Answer):
|
44 |
+
case_sensitive = models.BooleanField(default=False)
|
45 |
+
|
46 |
+
def __str__(self):
|
47 |
+
return self.correct_answer
|
48 |
|
49 |
+
def is_correct(self, user_answer):
|
50 |
+
if not self.case_sensitive:
|
51 |
+
return user_answer.lower() == self.correct_answer.lower()
|
52 |
+
return user_answer == self.correct_answer
|
53 |
|
|
|
|
|
|
|
|
|
54 |
|
55 |
class MultipleChoiceAnswer(Answer):
|
56 |
+
choices = fields.ArrayField(models.CharField(max_length=200, blank=True))
|
57 |
|
58 |
+
def __str__(self):
|
59 |
+
return f"{self.correct_answer} from {self.choices}"
|
60 |
|
61 |
+
def is_correct(self, user_answer):
|
62 |
+
return user_answer == self.correct_answer
|
quizzes/tests.py
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
-
from django.test import TestCase
|
2 |
|
3 |
# Create your tests here.
|
|
|
1 |
+
# from django.test import TestCase
|
2 |
|
3 |
# Create your tests here.
|
quizzes/urls.py
CHANGED
@@ -2,11 +2,11 @@ from django.urls import path
|
|
2 |
|
3 |
from . import views
|
4 |
|
5 |
-
app_name =
|
6 |
|
7 |
urlpatterns = [
|
8 |
-
path(
|
9 |
-
path(
|
10 |
-
path(
|
11 |
-
path(
|
12 |
-
]
|
|
|
2 |
|
3 |
from . import views
|
4 |
|
5 |
+
app_name = "quizzes"
|
6 |
|
7 |
urlpatterns = [
|
8 |
+
path("", views.index, name="index"),
|
9 |
+
path("<int:quiz_id>/", views.display_quiz, name="display_quiz"),
|
10 |
+
path("<int:quiz_id>/questions/<int:question_id>", views.display_question, name="display_question"),
|
11 |
+
path("questions/<int:question_id>/grade/", views.grade_question, name="grade_question"),
|
12 |
+
]
|
quizzes/views.py
CHANGED
@@ -1,17 +1,20 @@
|
|
1 |
-
from django.shortcuts import get_object_or_404,render, redirect
|
2 |
from django.urls import reverse
|
3 |
|
4 |
from .models import Quiz, Question
|
5 |
|
|
|
6 |
def index(request):
|
7 |
quiz_list = Quiz.objects.all()
|
8 |
-
context = {
|
9 |
-
return render(request,
|
|
|
10 |
|
11 |
def display_quiz(request, quiz_id):
|
12 |
quiz = get_object_or_404(Quiz, pk=quiz_id)
|
13 |
question = quiz.question_set.first()
|
14 |
-
return redirect(reverse(
|
|
|
15 |
|
16 |
def display_question(request, quiz_id, question_id):
|
17 |
quiz = get_object_or_404(Quiz, pk=quiz_id)
|
@@ -23,10 +26,19 @@ def display_question(request, quiz_id, question_id):
|
|
23 |
current_question = question
|
24 |
if ind != len(questions) - 1:
|
25 |
next_question = questions[ind + 1]
|
26 |
-
return render(
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
def grade_question(request, question_id):
|
29 |
question = get_object_or_404(Question, pk=question_id)
|
30 |
-
answer = getattr(question,
|
31 |
-
is_correct = answer.is_correct(request.POST.get(
|
32 |
-
return render(
|
|
|
|
|
|
|
|
|
|
1 |
+
from django.shortcuts import get_object_or_404, render, redirect
|
2 |
from django.urls import reverse
|
3 |
|
4 |
from .models import Quiz, Question
|
5 |
|
6 |
+
|
7 |
def index(request):
|
8 |
quiz_list = Quiz.objects.all()
|
9 |
+
context = {"quiz_list": quiz_list}
|
10 |
+
return render(request, "quizzes/index.html", context)
|
11 |
+
|
12 |
|
13 |
def display_quiz(request, quiz_id):
|
14 |
quiz = get_object_or_404(Quiz, pk=quiz_id)
|
15 |
question = quiz.question_set.first()
|
16 |
+
return redirect(reverse("quizzes:display_question", kwargs={"quiz_id": quiz_id, "question_id": question.pk}))
|
17 |
+
|
18 |
|
19 |
def display_question(request, quiz_id, question_id):
|
20 |
quiz = get_object_or_404(Quiz, pk=quiz_id)
|
|
|
26 |
current_question = question
|
27 |
if ind != len(questions) - 1:
|
28 |
next_question = questions[ind + 1]
|
29 |
+
return render(
|
30 |
+
request,
|
31 |
+
"quizzes/display.html",
|
32 |
+
{"quiz": quiz, "question": current_question, "next_question": next_question},
|
33 |
+
)
|
34 |
+
|
35 |
|
36 |
def grade_question(request, question_id):
|
37 |
question = get_object_or_404(Question, pk=question_id)
|
38 |
+
answer = getattr(question, "multiplechoiceanswer", None) or getattr(question, "freetextanswer")
|
39 |
+
is_correct = answer.is_correct(request.POST.get("answer"))
|
40 |
+
return render(
|
41 |
+
request,
|
42 |
+
"quizzes/partial.html",
|
43 |
+
{"is_correct": is_correct, "correct_answer": answer.correct_answer},
|
44 |
+
)
|
requirements-dev.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-r requirements.txt
|
2 |
+
black==22.3.0
|
3 |
+
pre-commit
|
4 |
+
flake8
|