vault backup: 2025-12-22 02:56:57
All checks were successful
Deploy Quartz site to GitHub Pages / build (push) Successful in 2m13s
All checks were successful
Deploy Quartz site to GitHub Pages / build (push) Successful in 2m13s
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
# Conflicts
|
||||
Please resolve them and commit them using the commands `Git: Commit all changes` followed by `Git: Push`
|
||||
(This file will automatically be deleted before commit)
|
||||
[[#Additional Instructions]] available below file list
|
||||
|
||||
- Not a file: content/.obsidian/workspace.json
|
||||
|
||||
# Additional Instructions
|
||||
I strongly recommend to use "Source mode" for viewing the conflicted files. For simple conflicts, in each file listed above replace every occurrence of the following text blocks with the desired text.
|
||||
|
||||
```diff
|
||||
<<<<<<< HEAD
|
||||
File changes in local repository
|
||||
=======
|
||||
File changes in remote repository
|
||||
>>>>>>> origin/main
|
||||
```
|
||||
BIN
quiz/__pycache__/settings.cpython-313.pyc
Normal file
BIN
quiz/__pycache__/settings.cpython-313.pyc
Normal file
Binary file not shown.
BIN
quiz/db.sqlite3
BIN
quiz/db.sqlite3
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
quiz/quiz/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
quiz/quiz/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
quiz/quiz/__pycache__/admin.cpython-313.pyc
Normal file
BIN
quiz/quiz/__pycache__/admin.cpython-313.pyc
Normal file
Binary file not shown.
BIN
quiz/quiz/__pycache__/apps.cpython-313.pyc
Normal file
BIN
quiz/quiz/__pycache__/apps.cpython-313.pyc
Normal file
Binary file not shown.
BIN
quiz/quiz/__pycache__/middleware.cpython-313.pyc
Normal file
BIN
quiz/quiz/__pycache__/middleware.cpython-313.pyc
Normal file
Binary file not shown.
BIN
quiz/quiz/__pycache__/models.cpython-313.pyc
Normal file
BIN
quiz/quiz/__pycache__/models.cpython-313.pyc
Normal file
Binary file not shown.
BIN
quiz/quiz/__pycache__/urls.cpython-313.pyc
Normal file
BIN
quiz/quiz/__pycache__/urls.cpython-313.pyc
Normal file
Binary file not shown.
BIN
quiz/quiz/__pycache__/views.cpython-313.pyc
Normal file
BIN
quiz/quiz/__pycache__/views.cpython-313.pyc
Normal file
Binary file not shown.
BIN
quiz/quiz/management/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
quiz/quiz/management/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
26
quiz/quiz/migrations/0006_tag_question_tags.py
Normal file
26
quiz/quiz/migrations/0006_tag_question_tags.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# Generated by Django 6.0 on 2025-12-22 01:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('quiz', '0005_course_exam_question_exam'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Tag',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', models.SlugField(unique=True)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='question',
|
||||
name='tags',
|
||||
field=models.ManyToManyField(blank=True, related_name='questions', to='quiz.tag'),
|
||||
),
|
||||
]
|
||||
BIN
quiz/quiz/migrations/__pycache__/0001_initial.cpython-313.pyc
Normal file
BIN
quiz/quiz/migrations/__pycache__/0001_initial.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
quiz/quiz/migrations/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
quiz/quiz/migrations/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
@@ -46,11 +46,20 @@ class Question(models.Model):
|
||||
file_mtime = models.FloatField(null=True, blank=True) # Track file modification time
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
tags = models.ManyToManyField('Tag', blank=True, related_name='questions')
|
||||
|
||||
def __str__(self):
|
||||
return self.text[:50]
|
||||
|
||||
|
||||
class Tag(models.Model):
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(max_length=50, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Option(models.Model):
|
||||
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='options')
|
||||
letter = models.CharField(max_length=1)
|
||||
|
||||
@@ -80,6 +80,7 @@ def parse_markdown_question(file_path: Path, content: str) -> Tuple[bool, dict]:
|
||||
- options: list of (letter, text) tuples
|
||||
- correct_answer: the correct answer letter(s)
|
||||
- has_answer: whether it has an answer (not TODO)
|
||||
- tags: list of tag strings
|
||||
"""
|
||||
lines = content.split('\n')
|
||||
|
||||
@@ -104,7 +105,15 @@ def parse_markdown_question(file_path: Path, content: str) -> Tuple[bool, dict]:
|
||||
question_type = 'textalternativ'
|
||||
elif 'frågetyp/textfält' in line:
|
||||
question_type = 'textfält'
|
||||
break
|
||||
elif in_frontmatter and line.strip().lower().startswith('tags:'):
|
||||
# Extract tags
|
||||
# Handle: tags: [tag1, tag2] or tags: tag1, tag2
|
||||
tag_content = line.split(':', 1)[1].strip()
|
||||
# Remove brackets if present
|
||||
tag_content = tag_content.strip('[]')
|
||||
# Split by comma
|
||||
tags = [t.strip() for t in tag_content.split(',') if t.strip()]
|
||||
|
||||
|
||||
if not is_question:
|
||||
return False, {}
|
||||
@@ -138,7 +147,9 @@ def parse_markdown_question(file_path: Path, content: str) -> Tuple[bool, dict]:
|
||||
'options': [],
|
||||
'correct_answer': '',
|
||||
'has_answer': False,
|
||||
'question_type': question_type
|
||||
'question_type': question_type,
|
||||
'tags': tags if 'tags' in locals() else []
|
||||
}
|
||||
}
|
||||
|
||||
# Extract options (pattern: "- A:" or "- A" for MCQ, or text for textalternativ)
|
||||
@@ -244,7 +255,9 @@ def parse_markdown_question(file_path: Path, content: str) -> Tuple[bool, dict]:
|
||||
'options': options_data,
|
||||
'correct_answer': correct_answer,
|
||||
'has_answer': has_answer,
|
||||
'question_type': question_type
|
||||
'question_type': question_type,
|
||||
'tags': tags if 'tags' in locals() else []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -368,6 +381,19 @@ def import_question_file(file_path: Path, base_path: Path, stats: ImportStats, f
|
||||
stats.created += 1
|
||||
else:
|
||||
stats.updated += 1
|
||||
|
||||
# Update tags
|
||||
from django.utils.text import slugify
|
||||
from quiz.models import Tag
|
||||
|
||||
question.tags.clear()
|
||||
for tag_name in question_data.get('tags', []):
|
||||
tag_slug = slugify(tag_name)
|
||||
tag, _ = Tag.objects.get_or_create(
|
||||
slug=tag_slug,
|
||||
defaults={'name': tag_name}
|
||||
)
|
||||
question.tags.add(tag)
|
||||
|
||||
# Update options
|
||||
question.options.all().delete()
|
||||
|
||||
@@ -2,23 +2,46 @@ from django.http import HttpResponse
|
||||
from django.shortcuts import render
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
from .models import Question, QuizResult
|
||||
from .models import Question, QuizResult, Tag
|
||||
|
||||
|
||||
def handle_tag_filter(request):
|
||||
tag_slug = request.GET.get('tag')
|
||||
if tag_slug is not None:
|
||||
if tag_slug == "":
|
||||
if 'quiz_tag' in request.session:
|
||||
del request.session['quiz_tag']
|
||||
else:
|
||||
request.session['quiz_tag'] = tag_slug
|
||||
|
||||
def index(request):
|
||||
handle_tag_filter(request)
|
||||
total_questions = Question.objects.count()
|
||||
answered_count = QuizResult.objects.filter(user=request.quiz_user).count()
|
||||
|
||||
context = {
|
||||
'total_questions': total_questions,
|
||||
'answered_count': answered_count,
|
||||
'tags': Tag.objects.all(),
|
||||
'current_tag': request.session.get('quiz_tag'),
|
||||
}
|
||||
return render(request, 'index.html', context)
|
||||
|
||||
|
||||
def get_next_question(request):
|
||||
# Handle tag filtering
|
||||
handle_tag_filter(request)
|
||||
|
||||
current_tag = request.session.get('quiz_tag')
|
||||
|
||||
answered_ids = QuizResult.objects.filter(user=request.quiz_user).values_list('question_id', flat=True)
|
||||
next_question = Question.objects.exclude(id__in=answered_ids).first()
|
||||
|
||||
questions = Question.objects.exclude(id__in=answered_ids)
|
||||
|
||||
if current_tag:
|
||||
questions = questions.filter(tags__slug=current_tag)
|
||||
|
||||
next_question = questions.first()
|
||||
|
||||
if not next_question:
|
||||
return render(request, 'partials/complete.html')
|
||||
|
||||
@@ -1,6 +1,42 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.filter-section { margin-bottom: 20px; }
|
||||
.tag-chip {
|
||||
display: inline-block;
|
||||
padding: 5px 12px;
|
||||
margin: 4px;
|
||||
border-radius: 16px;
|
||||
background: #e0e0e0;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.tag-chip.active {
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
.tag-chip:hover {
|
||||
background: #d5d5d5;
|
||||
}
|
||||
.tag-chip.active:hover {
|
||||
background: #45a049;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>Quiz Application</h1>
|
||||
|
||||
<div class="filter-section">
|
||||
<a href="?tag=" class="tag-chip {% if not current_tag %}active{% endif %}">All</a>
|
||||
{% for tag in tags %}
|
||||
<a href="?tag={{ tag.slug }}" class="tag-chip {% if current_tag == tag.slug %}active{% endif %}">
|
||||
{{ tag.name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style="width: {% if total_questions > 0 %}{{ answered_count|floatformat:0 }}{% else %}0{% endif %}%"></div>
|
||||
</div>
|
||||
|
||||
83
quiz/uv.lock
generated
Normal file
83
quiz/uv.lock
generated
Normal file
@@ -0,0 +1,83 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.13"
|
||||
|
||||
[[package]]
|
||||
name = "asgiref"
|
||||
version = "3.11.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/76/b9/4db2509eabd14b4a8c71d1b24c8d5734c52b8560a7b1e1a8b56c8d25568b/asgiref-3.11.0.tar.gz", hash = "sha256:13acff32519542a1736223fb79a715acdebe24286d98e8b164a73085f40da2c4", size = 37969, upload-time = "2025-11-19T15:32:20.106Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/91/be/317c2c55b8bbec407257d45f5c8d1b6867abc76d12043f2d3d58c538a4ea/asgiref-3.11.0-py3-none-any.whl", hash = "sha256:1db9021efadb0d9512ce8ffaf72fcef601c7b73a8807a1bb2ef143dc6b14846d", size = 24096, upload-time = "2025-11-19T15:32:19.004Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "sqlparse" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/75/19762bfc4ea556c303d9af8e36f0cd910ab17dff6c8774644314427a2120/django-6.0.tar.gz", hash = "sha256:7b0c1f50c0759bbe6331c6a39c89ae022a84672674aeda908784617ef47d8e26", size = 10932418, upload-time = "2025-12-03T16:26:21.878Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/ae/f19e24789a5ad852670d6885f5480f5e5895576945fcc01817dfd9bc002a/django-6.0-py3-none-any.whl", hash = "sha256:1cc2c7344303bbfb7ba5070487c17f7fc0b7174bbb0a38cebf03c675f5f19b6d", size = 8339181, upload-time = "2025-12-03T16:26:16.231Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quiz"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
{ name = "watchdog" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "django", specifier = ">=6.0.0" },
|
||||
{ name = "watchdog", specifier = ">=6.0.0" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = []
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.5.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "watchdog"
|
||||
version = "6.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
|
||||
]
|
||||
Reference in New Issue
Block a user