Scaffold project layout
This commit is contained in:
18
.env.example
Normal file
18
.env.example
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# SQLite database path
|
||||||
|
QUIZ_DB_PATH=./quiz.db
|
||||||
|
|
||||||
|
# Required cookie signing secret
|
||||||
|
QUIZ_SECRET_KEY=change-me-to-a-random-secret
|
||||||
|
|
||||||
|
# Required shared instructor password
|
||||||
|
QUIZ_ADMIN_PASSWORD=change-me
|
||||||
|
|
||||||
|
# Uvicorn bind settings
|
||||||
|
QUIZ_HOST=127.0.0.1
|
||||||
|
QUIZ_PORT=8001
|
||||||
|
|
||||||
|
# Public URL used for student join links and QR codes
|
||||||
|
QUIZ_PUBLIC_URL=https://quiz.ahkhan.me
|
||||||
|
|
||||||
|
# Python logging level
|
||||||
|
QUIZ_LOG_LEVEL=INFO
|
||||||
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
.env
|
||||||
|
quiz.db
|
||||||
|
*.db
|
||||||
|
*.db-shm
|
||||||
|
*.db-wal
|
||||||
3
NOTES.md
Normal file
3
NOTES.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Notes
|
||||||
|
|
||||||
|
Implementation notes will be filled in as the application lands.
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Live In-Lecture Quiz Portal
|
||||||
|
|
||||||
|
Development notes will be filled in as the implementation lands.
|
||||||
3
app/__init__.py
Normal file
3
app/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"""Live quiz application package."""
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
1
app/auth.py
Normal file
1
app/auth.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Cookie signing helpers."""
|
||||||
1
app/config.py
Normal file
1
app/config.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Application configuration."""
|
||||||
1
app/csv_export.py
Normal file
1
app/csv_export.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""CSV export helpers."""
|
||||||
9
app/main.py
Normal file
9
app/main.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
|
||||||
|
def create_app() -> FastAPI:
|
||||||
|
app = FastAPI(title="Live In-Lecture Quiz Portal")
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
1
app/models.py
Normal file
1
app/models.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Pydantic request and response models."""
|
||||||
1
app/pool.py
Normal file
1
app/pool.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Question pool validation."""
|
||||||
1
app/room.py
Normal file
1
app/room.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""In-process WebSocket room manager."""
|
||||||
1
app/routes_admin.py
Normal file
1
app/routes_admin.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Instructor routes."""
|
||||||
1
app/routes_student.py
Normal file
1
app/routes_student.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Student routes."""
|
||||||
1
app/scoring.py
Normal file
1
app/scoring.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Score functions."""
|
||||||
39
pyproject.toml
Normal file
39
pyproject.toml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=68"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "quiz"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Live in-lecture quiz portal"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
dependencies = [
|
||||||
|
"fastapi~=0.115",
|
||||||
|
"uvicorn[standard]~=0.34",
|
||||||
|
"aiosqlite~=0.20",
|
||||||
|
"itsdangerous~=2.2",
|
||||||
|
"python-multipart~=0.0.20",
|
||||||
|
"qrcode[pil]~=8.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"pytest~=8.3",
|
||||||
|
"pytest-asyncio~=0.25",
|
||||||
|
"pytest-cov~=6.0",
|
||||||
|
"httpx~=0.28",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.setuptools]
|
||||||
|
packages = ["app"]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
asyncio_mode = "auto"
|
||||||
|
testpaths = ["tests"]
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
source = ["app"]
|
||||||
|
|
||||||
|
[tool.coverage.report]
|
||||||
|
show_missing = true
|
||||||
|
fail_under = 80
|
||||||
13
static/admin.html
Normal file
13
static/admin.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Quiz Admin</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main id="admin-app"></main>
|
||||||
|
<script type="module" src="/static/admin.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
2
static/admin.js
Normal file
2
static/admin.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
const app = document.querySelector("#admin-app");
|
||||||
|
app.textContent = "Loading admin...";
|
||||||
15
static/observer.html
Normal file
15
static/observer.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Quiz Observer</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell">
|
||||||
|
<h1>Quiz Observer</h1>
|
||||||
|
<p>This read-only view is reserved for a future classroom display.</p>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
2
static/quiz.js
Normal file
2
static/quiz.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
const app = document.querySelector("#app");
|
||||||
|
app.textContent = "Loading quiz...";
|
||||||
13
static/student.html
Normal file
13
static/student.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Quiz</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main id="app"></main>
|
||||||
|
<script type="module" src="/static/quiz.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
17
static/style.css
Normal file
17
static/style.css
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: light dark;
|
||||||
|
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f6f7f9;
|
||||||
|
color: #18212f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell {
|
||||||
|
max-width: 960px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
1
tests/conftest.py
Normal file
1
tests/conftest.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Test fixtures."""
|
||||||
2
tests/test_api_admin.py
Normal file
2
tests/test_api_admin.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_api_admin():
|
||||||
|
assert True
|
||||||
2
tests/test_api_student.py
Normal file
2
tests/test_api_student.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_api_student():
|
||||||
|
assert True
|
||||||
2
tests/test_auth.py
Normal file
2
tests/test_auth.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_auth():
|
||||||
|
assert True
|
||||||
2
tests/test_csv_export.py
Normal file
2
tests/test_csv_export.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_csv_export():
|
||||||
|
assert True
|
||||||
2
tests/test_late_join.py
Normal file
2
tests/test_late_join.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_late_join():
|
||||||
|
assert True
|
||||||
2
tests/test_load_simulation.py
Normal file
2
tests/test_load_simulation.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_load_simulation():
|
||||||
|
assert True
|
||||||
2
tests/test_pool.py
Normal file
2
tests/test_pool.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_pool():
|
||||||
|
assert True
|
||||||
2
tests/test_reconnect.py
Normal file
2
tests/test_reconnect.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_reconnect():
|
||||||
|
assert True
|
||||||
2
tests/test_scoring.py
Normal file
2
tests/test_scoring.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_scoring():
|
||||||
|
assert True
|
||||||
2
tests/test_state_machine.py
Normal file
2
tests/test_state_machine.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_state_machine():
|
||||||
|
assert True
|
||||||
2
tests/test_ws_admin.py
Normal file
2
tests/test_ws_admin.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_ws_admin():
|
||||||
|
assert True
|
||||||
2
tests/test_ws_student.py
Normal file
2
tests/test_ws_student.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_placeholder_ws_student():
|
||||||
|
assert True
|
||||||
Reference in New Issue
Block a user