## Architecture - Notes live in [content](content) and ship to [public](public) via Quartz under [quartz](quartz); Django lives in [stroma](stroma) and hosts the quiz and ingest services. - Static Quartz pages are configured through [quartz.config.ts](quartz.config.ts) and layouts in [quartz.layout.ts](quartz.layout.ts), while Django templates sit in [stroma/templates](stroma/templates) with HTMX-like partials in [stroma/templates/partials](stroma/templates/partials). - Data flows: Obsidian markdown → question importer ([stroma/quiz/utils/importer.py](stroma/quiz/utils/importer.py#L1-L220)) → relational models ([stroma/quiz/models](stroma/quiz/models)) → quiz/session views → rendered partials front-end. ## Quartz Site Workflow - Use Node 22+ and npm 10.9+ as enforced in [package.json](package.json#L1-L60); run `npm install` once, then `npm run docs` to build+serve Quartz against the `content` dir. - The CLI is exposed via `npm run quartz` / `npx quartz ...` and reads custom config/theme in [quartz.config.ts](quartz.config.ts) plus layout definition in [quartz.layout.ts](quartz.layout.ts). - [content](content) follows Obsidian conventions (Swedish folder names, embedded PDFs/CSV) and Quartz ignores private/templates per the `ignorePatterns` value. - When editing markdown, keep Obsidian-flavored syntax and frontmatter tags because the quiz importer keys off tags like `frågetyp/mcq`. ## Django Quiz Service - The project root is [stroma](stroma); run it with `uv run python manage.py runserver` (Python 3.13, deps in [stroma/pyproject.toml](stroma/pyproject.toml)). - Core models: `QuizSession`, `QuizResult`, `QuizUser`, `Question`, `Tag`, `Exam`, etc. in [stroma/quiz/models](stroma/quiz/models); sessions filter questions via [stroma/quiz/views/get_session_questions_view.py](stroma/quiz/views/get_session_questions_view.py#L1-L35). - Primary views live in [stroma/quiz/views](stroma/quiz/views) and always expect `request.quiz_user`; e.g. [stroma/quiz/views/get_next_question_view.py](stroma/quiz/views/get_next_question_view.py#L1-L40) renders partials consumed by the `quiz_mode` page. - URL routing: [stroma/quiz/urls.py](stroma/quiz/urls.py) registers quiz endpoints under `/quiz/`, and [stroma/quiz/quiz_urls.py](stroma/quiz/quiz_urls.py#L1-L25) defines per-view paths (`session//question`, `/next/`, `/submit/`, etc.). ## Question Import Pipeline - [stroma/quiz/utils/importer.py](stroma/quiz/utils/importer.py) parses Obsidian markdown, infers question type via `tags`, and stores options/matching_data plus tag slugs; the importer respects file mtimes and tracks stats. - [stroma/quiz/utils/watcher.py](stroma/quiz/utils/watcher.py) plus [stroma/quiz/apps.py](stroma/quiz/apps.py#L1-L20) spin up a background Watchdog thread on startup (unless running management commands/tests) targeting `settings.QUESTION_WATCH_PATH` from [stroma/settings.py](stroma/settings.py#L1-L70). - Management commands `uv run python manage.py import_questions [--folder ... --force]` and `populate_exams` live in [stroma/quiz/management/commands](stroma/quiz/management/commands) for bulk ingest/backfilling. - The importer expects spoiler blocks for answers and tags like `frågetyp/mcq`; missing answers mark the file as TODO and skip DB writes, so keep metadata consistent. ## File Uploads & Assets - The `file` app exposes `/file/upload/` + `/file/upload/api/` via [stroma/file/urls.py](stroma/file/urls.py); UI renders from [stroma/file/views/upload_files_page_view.py](stroma/file/views/upload_files_page_view.py#L1-L7). - Upload handling in [stroma/file/views/upload_files_api_view.py](stroma/file/views/upload_files_api_view.py#L1-L130) mirrors the client’s folder hierarchy, infers MIME types, stores text content when possible, and persists binaries under `uploads/`. - Metadata/state for uploaded artifacts sit in [stroma/file/models/file_model.py](stroma/file/models/file_model.py#L1-L40) and are owned by `quiz.QuizUser`. ## Session & Request Patterns - [stroma/quiz/middleware.py](stroma/quiz/middleware.py#L1-L21) auto-creates `QuizUser` records tied to Django session keys and attaches `request.quiz_user`; every view assumes this, so never bypass the middleware. - `QuizResult` enforces one row per user+question via `unique_together` ([stroma/quiz/models/quiz_result_model.py](stroma/quiz/models/quiz_result_model.py#L1-L27)) and normalization logic in [submit_answer_view](stroma/quiz/views/submit_answer_view.py#L1-L60); reuse `update_or_create` when recording answers. - Navigational/partial views such as [quiz_question_view](stroma/quiz/views/quiz_question_view.py#L1-L60) and [navigate_question_view](stroma/quiz/views/navigate_question_view.py#L1-L60) rely on consistent ordering from `get_session_questions`; maintain that helper when altering filtering behavior. ## Testing & Tooling - Python deps/tests are run through `uv` (`uv sync`, then `uv run pytest`) per [stroma/AGENT.md](stroma/AGENT.md) and versions pinned in [stroma/pyproject.toml](stroma/pyproject.toml#L1-L20). - Pytest config in [stroma/pytest.ini](stroma/pytest.ini#L1-L20) enables `--reuse-db` and short tracebacks; [stroma/conftest.py](stroma/conftest.py#L1-L70) switches to in-memory SQLite and provides markdown fixtures for parser tests. - Comprehensive integration coverage exists in [stroma/quiz/tests/test_views.py](stroma/quiz/tests/test_views.py#L1-L210); extend these when touching quiz flows (session creation, submissions, difficulty ratings). - SQLite WAL is default (see [stroma/settings.py](stroma/settings.py#L30-L65)); when debugging concurrency, clear `db.sqlite3-wal/shm` files before reruns. ## Coding Conventions - Follow [stroma/AGENT.md](stroma/AGENT.md): Obsidian-flavored Markdown, no extra docstrings/comments unless asked, favor early returns, narrow `try/except`, and keep regexes as upper-case `_RE` constants with inline comments. - Use type hints everywhere, prefer function-based Django views, and leverage django-stubs/pytest-django for typing/tests. - When working in markdown ingestion, maintain tag-driven semantics (`frågetyp/*`, completion stats) and keep regex patterns centralized in parser modules. - Tests should live beside implementations (e.g., `quiz/utils/tests`) and use parametrized `pytest` subtests when iterating over inputs. - Keep new dependencies declared in `pyproject.toml` (Python) or `package.json` (Quartz) and prefer latest stable versions unless compatibility dictates otherwise.