webquizplus 1.15.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- webquizplus-1.15.4/PKG-INFO +753 -0
- webquizplus-1.15.4/README.md +716 -0
- webquizplus-1.15.4/pyproject.toml +128 -0
- webquizplus-1.15.4/webquiz/__init__.py +6 -0
- webquizplus-1.15.4/webquiz/binary_entry.py +36 -0
- webquizplus-1.15.4/webquiz/build.py +56 -0
- webquizplus-1.15.4/webquiz/checker.py +64 -0
- webquizplus-1.15.4/webquiz/cli.py +316 -0
- webquizplus-1.15.4/webquiz/config.py +240 -0
- webquizplus-1.15.4/webquiz/server.py +4200 -0
- webquizplus-1.15.4/webquiz/server_config.yaml.example +63 -0
- webquizplus-1.15.4/webquiz/templates/admin.html +3733 -0
- webquizplus-1.15.4/webquiz/templates/files.html +1258 -0
- webquizplus-1.15.4/webquiz/templates/index.html +2134 -0
- webquizplus-1.15.4/webquiz/templates/live_stats.html +1113 -0
- webquizplus-1.15.4/webquiz/templates/quiz_selection_required.html +81 -0
- webquizplus-1.15.4/webquiz/templates/template_error.html +25 -0
- webquizplus-1.15.4/webquiz/tunnel.py +388 -0
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: webquizplus
|
|
3
|
+
Version: 1.15.4
|
|
4
|
+
Summary: A modern web-based quiz and testing system built with Python and aiohttp
|
|
5
|
+
Home-page: https://github.com/oduvan/webquiz
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: quiz,testing,aiohttp,web,assessment,education
|
|
8
|
+
Author: Oleksandr Liabakh
|
|
9
|
+
Author-email: oduvan@gmail.com
|
|
10
|
+
Requires-Python: >=3.9,<3.15
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Education
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Education :: Testing
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Requires-Dist: PyYAML (>=6.0.2,<7.0.0)
|
|
27
|
+
Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
|
|
28
|
+
Requires-Dist: aiohttp (>=3.12.13,<4.0.0)
|
|
29
|
+
Requires-Dist: asyncssh (>=2.14.0,<3.0.0)
|
|
30
|
+
Requires-Dist: cryptography (>=41.0.0,<42.0.0)
|
|
31
|
+
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
32
|
+
Project-URL: Bug Tracker, https://github.com/oduvan/webquiz/issues
|
|
33
|
+
Project-URL: Documentation, https://github.com/oduvan/webquiz
|
|
34
|
+
Project-URL: Repository, https://github.com/oduvan/webquiz
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# WebQuiz
|
|
38
|
+
|
|
39
|
+
A modern web-based quiz and testing system built with Python and aiohttp that allows users to take multiple-choice and text input tests with real-time answer validation and performance tracking.
|
|
40
|
+
|
|
41
|
+
## ✨ Features
|
|
42
|
+
|
|
43
|
+
- **Multi-Quiz System**: Questions loaded from `quizzes/` directory with multiple YAML files
|
|
44
|
+
- **Multiple Question Types**: Single choice, multiple choice, and text input questions with Python-based validation
|
|
45
|
+
- **Admin Interface**: Web-based admin panel with master key authentication for quiz management
|
|
46
|
+
- **Registration Approval**: Optional admin approval workflow for new registrations with real-time notifications
|
|
47
|
+
- **Question Randomization**: Configurable per-student question order randomization for fair testing
|
|
48
|
+
- **Question Grouping**: Keep related questions together during randomization with `stick_to_the_previous` attribute
|
|
49
|
+
- **Dynamic Answer Visibility**: Optional delayed answer reveal - show correct answers only after all students complete
|
|
50
|
+
- **Manual Answer Control**: Admin button to reveal answers immediately without waiting for all students
|
|
51
|
+
- **Dynamic Quiz Switching**: Real-time quiz switching with automatic server state reset
|
|
52
|
+
- **Config File Editor**: Web-based configuration editor with real-time validation
|
|
53
|
+
- **Live Statistics**: Real-time WebSocket-powered dashboard showing user progress
|
|
54
|
+
- **Real-time Validation**: Server-side answer checking with immediate feedback
|
|
55
|
+
- **Session Persistence**: Cookie-based user sessions for seamless experience
|
|
56
|
+
- **Performance Tracking**: Server-side timing for accurate response measurement
|
|
57
|
+
- **Data Export**: Automatic CSV export with quiz-prefixed filenames and unique suffixes
|
|
58
|
+
- **AI Integration**: Use ChatGPT/Claude for analyzing quiz results and generating questions from existing materials
|
|
59
|
+
- **Responsive UI**: Clean web interface with dark/light theme support
|
|
60
|
+
- **SSH Tunnel Support**: Optional public access via SSH reverse tunnel with auto-reconnect
|
|
61
|
+
- **Binary Distribution**: Standalone PyInstaller executable with auto-configuration
|
|
62
|
+
- **Comprehensive Testing**: 222+ tests covering all functionality with CI/CD pipeline
|
|
63
|
+
- **Flexible File Paths**: Configurable paths for quizzes, logs, CSV, and static files
|
|
64
|
+
|
|
65
|
+
## 🚀 Quick Start
|
|
66
|
+
|
|
67
|
+
### Prerequisites
|
|
68
|
+
|
|
69
|
+
- Python 3.9-3.14 (required by aiohttp)
|
|
70
|
+
- Poetry (recommended) or pip
|
|
71
|
+
- Git
|
|
72
|
+
|
|
73
|
+
### Installation with Poetry (Recommended)
|
|
74
|
+
|
|
75
|
+
1. **Clone the repository**
|
|
76
|
+
```bash
|
|
77
|
+
git clone git@github.com:oduvan/webquiz.git
|
|
78
|
+
cd webquiz
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
2. **Install with Poetry**
|
|
82
|
+
```bash
|
|
83
|
+
poetry install
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
3. **Run the server**
|
|
87
|
+
```bash
|
|
88
|
+
webquiz # Foreground mode
|
|
89
|
+
webquiz -d # Daemon mode
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
4. **Open your browser**
|
|
93
|
+
```
|
|
94
|
+
http://localhost:8080
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The server will automatically create necessary directories and files on first run.
|
|
98
|
+
|
|
99
|
+
### Alternative Installation with pip
|
|
100
|
+
|
|
101
|
+
1. **Clone and set up virtual environment**
|
|
102
|
+
```bash
|
|
103
|
+
git clone git@github.com:oduvan/webquiz.git
|
|
104
|
+
cd webquiz
|
|
105
|
+
python3 -m venv venv
|
|
106
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
2. **Install dependencies**
|
|
110
|
+
```bash
|
|
111
|
+
pip install -r requirements.txt
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
3. **Run the server**
|
|
115
|
+
```bash
|
|
116
|
+
python -m webquiz.cli
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The server will automatically create necessary directories and files on first run.
|
|
120
|
+
|
|
121
|
+
## 📁 Project Structure
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
webquiz/
|
|
125
|
+
├── pyproject.toml # Poetry configuration and dependencies
|
|
126
|
+
├── requirements.txt # Legacy pip dependencies
|
|
127
|
+
├── .gitignore # Git ignore rules
|
|
128
|
+
├── CLAUDE.md # Project documentation
|
|
129
|
+
├── README.md # This file
|
|
130
|
+
├── webquiz/ # Main package
|
|
131
|
+
│ ├── __init__.py # Package initialization
|
|
132
|
+
│ ├── cli.py # CLI entry point (webquiz command)
|
|
133
|
+
│ ├── server.py # Main application server
|
|
134
|
+
│ ├── build.py # PyInstaller binary build script
|
|
135
|
+
│ ├── binary_entry.py # Binary executable entry point
|
|
136
|
+
│ ├── version_check.py # Version update checking
|
|
137
|
+
│ ├── server_config.yaml.example # Configuration example
|
|
138
|
+
│ └── templates/ # HTML templates
|
|
139
|
+
│ ├── index.html # Main quiz interface
|
|
140
|
+
│ ├── admin.html # Admin management panel
|
|
141
|
+
│ ├── files.html # File manager interface
|
|
142
|
+
│ ├── live_stats.html # Live statistics dashboard
|
|
143
|
+
│ ├── quiz_selection_required.html # Quiz selection prompt
|
|
144
|
+
│ └── template_error.html # Error page template
|
|
145
|
+
├── tests/ # Test suite (14 test files)
|
|
146
|
+
│ ├── conftest.py # Test fixtures and configuration
|
|
147
|
+
│ ├── test_cli_directory_creation.py # CLI and directory tests
|
|
148
|
+
│ ├── test_admin_api.py # Admin API tests
|
|
149
|
+
│ ├── test_admin_quiz_management.py # Quiz management tests
|
|
150
|
+
│ ├── test_config_management.py # Config editor tests
|
|
151
|
+
│ ├── test_registration_approval.py # Registration approval tests
|
|
152
|
+
│ ├── test_registration_fields.py # Registration fields tests
|
|
153
|
+
│ ├── test_index_generation.py # Template generation tests
|
|
154
|
+
│ ├── test_files_management.py # File manager tests
|
|
155
|
+
│ ├── test_integration_multiple_choice.py # Multiple choice integration tests
|
|
156
|
+
│ ├── test_multiple_answers.py # Multiple answer tests
|
|
157
|
+
│ ├── test_show_right_answer.py # Show answer tests
|
|
158
|
+
│ ├── test_selenium_multiple_choice.py # Selenium multiple choice tests
|
|
159
|
+
│ ├── test_selenium_registration_fields.py # Selenium registration tests
|
|
160
|
+
│ └── test_user_journey_selenium.py # Selenium user journey tests
|
|
161
|
+
└── .github/
|
|
162
|
+
└── workflows/
|
|
163
|
+
└── test.yml # CI/CD pipeline
|
|
164
|
+
|
|
165
|
+
# Generated at runtime (excluded from git):
|
|
166
|
+
└── quizzes/ # Quiz files directory
|
|
167
|
+
├── default.yaml # Default quiz (auto-created)
|
|
168
|
+
└── *.yaml # Additional quiz files
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 🖥️ CLI Commands
|
|
172
|
+
|
|
173
|
+
The `webquiz` command provides several options:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Start server in foreground (default)
|
|
177
|
+
webquiz
|
|
178
|
+
|
|
179
|
+
# Start server with admin interface (requires master key)
|
|
180
|
+
webquiz --master-key secret123
|
|
181
|
+
|
|
182
|
+
# Start server with custom directories
|
|
183
|
+
webquiz --quizzes-dir my_quizzes
|
|
184
|
+
webquiz --logs-dir /var/log
|
|
185
|
+
webquiz --csv-dir /data
|
|
186
|
+
webquiz --static /var/www/quiz
|
|
187
|
+
|
|
188
|
+
# Combine multiple options
|
|
189
|
+
webquiz --master-key secret123 --quizzes-dir quizzes --logs-dir logs
|
|
190
|
+
|
|
191
|
+
# Set master key via environment variable
|
|
192
|
+
export WEBQUIZ_MASTER_KEY=secret123
|
|
193
|
+
webquiz
|
|
194
|
+
|
|
195
|
+
# Start server as daemon (background)
|
|
196
|
+
webquiz -d
|
|
197
|
+
webquiz --daemon
|
|
198
|
+
|
|
199
|
+
# Stop daemon server
|
|
200
|
+
webquiz --stop
|
|
201
|
+
|
|
202
|
+
# Check daemon status
|
|
203
|
+
webquiz --status
|
|
204
|
+
|
|
205
|
+
# Show help
|
|
206
|
+
webquiz --help
|
|
207
|
+
|
|
208
|
+
# Show version
|
|
209
|
+
webquiz --version
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Key Options
|
|
213
|
+
|
|
214
|
+
- `--master-key`: Enable admin interface with authentication
|
|
215
|
+
- `--quizzes-dir`: Directory containing quiz YAML files (default: `./quizzes`)
|
|
216
|
+
- `--logs-dir`: Directory for server logs (default: current directory)
|
|
217
|
+
- `--csv-dir`: Directory for CSV exports (default: current directory)
|
|
218
|
+
- `--static`: Directory for static files (default: `./static`)
|
|
219
|
+
- `-d, --daemon`: Run server in background
|
|
220
|
+
- `--stop`: Stop daemon server
|
|
221
|
+
- `--status`: Check daemon status
|
|
222
|
+
|
|
223
|
+
### Daemon Mode Features
|
|
224
|
+
|
|
225
|
+
- **Background execution**: Server runs independently in background
|
|
226
|
+
- **PID file management**: Automatic process tracking via `webquiz.pid`
|
|
227
|
+
- **Graceful shutdown**: Proper cleanup on stop
|
|
228
|
+
- **Status monitoring**: Check if daemon is running
|
|
229
|
+
- **Log preservation**: All output still goes to `server.log`
|
|
230
|
+
|
|
231
|
+
## 🚀 Release Management
|
|
232
|
+
|
|
233
|
+
This project uses GitHub Actions for automated versioning, PyPI deployment, and GitHub Release creation.
|
|
234
|
+
|
|
235
|
+
### Creating a New Release
|
|
236
|
+
|
|
237
|
+
1. **Go to GitHub Actions** in the repository
|
|
238
|
+
2. **Select "Release and Deploy to PyPI" workflow**
|
|
239
|
+
3. **Click "Run workflow"**
|
|
240
|
+
4. **Enter the new version** (e.g., `1.0.6`, `2.0.0`)
|
|
241
|
+
5. **Click "Run workflow"**
|
|
242
|
+
|
|
243
|
+
The action will automatically:
|
|
244
|
+
- ✅ Update version in `pyproject.toml` and `webquiz/__init__.py`
|
|
245
|
+
- ✅ Run tests to ensure everything works
|
|
246
|
+
- ✅ Commit the version changes
|
|
247
|
+
- ✅ Create a git tag with the version
|
|
248
|
+
- ✅ Build the package using Poetry
|
|
249
|
+
- ✅ Publish to PyPI
|
|
250
|
+
- 🆕 **Create a GitHub Release** with built artifacts
|
|
251
|
+
|
|
252
|
+
### What's included in GitHub Releases
|
|
253
|
+
|
|
254
|
+
Each release automatically includes:
|
|
255
|
+
- 📦 **Python wheel package** (`.whl` file)
|
|
256
|
+
- 📋 **Source distribution** (`.tar.gz` file)
|
|
257
|
+
- 📝 **Formatted release notes** with installation instructions
|
|
258
|
+
- 🔗 **Links to commit history** for detailed changelog
|
|
259
|
+
- 📋 **Installation commands** for the specific version
|
|
260
|
+
|
|
261
|
+
### Prerequisites for Release Deployment
|
|
262
|
+
|
|
263
|
+
Repository maintainers need to set up:
|
|
264
|
+
- `PYPI_API_TOKEN` secret in GitHub repository settings
|
|
265
|
+
- PyPI account with publish permissions for the `webquiz` package
|
|
266
|
+
- `GITHUB_TOKEN` is automatically provided by GitHub Actions
|
|
267
|
+
|
|
268
|
+
## 🧪 Testing
|
|
269
|
+
|
|
270
|
+
Run the comprehensive test suite:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
# With Poetry
|
|
274
|
+
poetry run pytest
|
|
275
|
+
|
|
276
|
+
# Or directly
|
|
277
|
+
pytest tests/
|
|
278
|
+
|
|
279
|
+
# Run with verbose output
|
|
280
|
+
pytest tests/ -v
|
|
281
|
+
|
|
282
|
+
# Run in parallel with 4 workers
|
|
283
|
+
pytest tests/ -v -n 4
|
|
284
|
+
|
|
285
|
+
# Run specific test file
|
|
286
|
+
pytest tests/test_admin_api.py
|
|
287
|
+
pytest tests/test_registration_approval.py
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Test Coverage
|
|
291
|
+
|
|
292
|
+
The project has **222+ tests** across **15 test files** covering:
|
|
293
|
+
|
|
294
|
+
- **CLI and Directory Creation** (7 tests): Directory and file creation
|
|
295
|
+
- **Admin API** (14 tests): Admin interface and authentication
|
|
296
|
+
- **Admin Quiz Management** (18 tests): Quiz switching and management
|
|
297
|
+
- **Admin Quiz Editor** (7 tests): Wizard mode quiz creation with randomize_questions and show_answers_on_completion
|
|
298
|
+
- **Config Management** (16 tests): Config editor and validation
|
|
299
|
+
- **Registration Approval** (20 tests): Approval workflow and timing
|
|
300
|
+
- **Files Management** (32 tests): File manager interface
|
|
301
|
+
- **Index Generation** (13 tests): Template generation tests
|
|
302
|
+
- **Registration Fields** (12 tests): Custom registration fields
|
|
303
|
+
- **Show Right Answer** (5 tests): Answer display functionality
|
|
304
|
+
- **Show Answers on Completion** (10 tests): Dynamic answer visibility after all students complete
|
|
305
|
+
- **Integration Tests** (6 tests): Multiple choice, multiple answers
|
|
306
|
+
- **Selenium Tests** (56 tests): End-to-end browser testing
|
|
307
|
+
- **Auto-Advance** (6 tests): Automatic progression behavior
|
|
308
|
+
|
|
309
|
+
The test suite uses GitHub Actions CI/CD for automated testing on every commit.
|
|
310
|
+
|
|
311
|
+
## 🔥 Stress Testing
|
|
312
|
+
|
|
313
|
+
Stress testing has been moved to a separate project for better maintainability and independent versioning.
|
|
314
|
+
|
|
315
|
+
### Installation
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# Install from PyPI
|
|
319
|
+
pip install webquiz-stress-test
|
|
320
|
+
|
|
321
|
+
# Or download pre-built binaries from releases
|
|
322
|
+
# https://github.com/oduvan/webquiz-stress-test/releases
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Quick Start
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
# Basic test with 10 concurrent users
|
|
329
|
+
webquiz-stress-test
|
|
330
|
+
|
|
331
|
+
# Heavy load test with 100 users
|
|
332
|
+
webquiz-stress-test -c 100
|
|
333
|
+
|
|
334
|
+
# Test custom server
|
|
335
|
+
webquiz-stress-test -u http://localhost:9000 -c 50
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Features
|
|
339
|
+
|
|
340
|
+
- Concurrent client simulation with configurable users
|
|
341
|
+
- Realistic user behavior (random delays, page reloads)
|
|
342
|
+
- Randomized quiz support
|
|
343
|
+
- Approval workflow testing
|
|
344
|
+
- Detailed performance statistics
|
|
345
|
+
- Multi-platform binaries (Linux, macOS, Windows)
|
|
346
|
+
|
|
347
|
+
### Documentation
|
|
348
|
+
|
|
349
|
+
For complete documentation, see the [webquiz-stress-test repository](https://github.com/oduvan/webquiz-stress-test).
|
|
350
|
+
|
|
351
|
+
## 📋 Configuration Format
|
|
352
|
+
|
|
353
|
+
### Quiz Files
|
|
354
|
+
|
|
355
|
+
Questions are stored in YAML files in the `quizzes/` directory. The server automatically creates a `default.yaml` file if the directory is empty.
|
|
356
|
+
|
|
357
|
+
**Example quiz file** (`quizzes/math_quiz.yaml`):
|
|
358
|
+
|
|
359
|
+
```yaml
|
|
360
|
+
title: "Mathematics Quiz"
|
|
361
|
+
randomize_questions: true # Set to true to randomize question order for each student (default: false)
|
|
362
|
+
questions:
|
|
363
|
+
- question: "What is 2 + 2?"
|
|
364
|
+
options:
|
|
365
|
+
- "3"
|
|
366
|
+
- "4"
|
|
367
|
+
- "5"
|
|
368
|
+
- "6"
|
|
369
|
+
correct_answer: 1 # 0-indexed (option "4")
|
|
370
|
+
|
|
371
|
+
- question: "What is 5 × 3?"
|
|
372
|
+
options:
|
|
373
|
+
- "10"
|
|
374
|
+
- "15"
|
|
375
|
+
- "20"
|
|
376
|
+
- "25"
|
|
377
|
+
correct_answer: 1 # 0-indexed (option "15")
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Question Randomization:**
|
|
381
|
+
- Set `randomize_questions: true` in your quiz YAML to give each student a unique question order
|
|
382
|
+
- Each student receives a randomized order that persists across sessions
|
|
383
|
+
- Helps prevent cheating and ensures fair testing
|
|
384
|
+
- Default is `false` (questions appear in YAML order)
|
|
385
|
+
|
|
386
|
+
**Question Grouping (stick_to_the_previous):**
|
|
387
|
+
- Use `stick_to_the_previous: true` on questions that should stay adjacent to their predecessor during randomization
|
|
388
|
+
- Useful for reading passages followed by related questions
|
|
389
|
+
- Groups are shuffled as units while preserving internal order
|
|
390
|
+
- Example: Q1→Q2(sticky)→Q3(sticky) always appear together as [Q1,Q2,Q3]
|
|
391
|
+
- First question cannot have this flag (no previous question)
|
|
392
|
+
- Admin panel shows 🔗 indicator on grouped questions
|
|
393
|
+
|
|
394
|
+
**Question Points:**
|
|
395
|
+
- Each question can have a custom point value using the `points` field (default: 1)
|
|
396
|
+
- Points are tracked and displayed in:
|
|
397
|
+
- Live stats: shows earned points / total points for each user
|
|
398
|
+
- Final results: displays points earned along with correct/incorrect count
|
|
399
|
+
- Users CSV: includes `earned_points` and `total_points` columns
|
|
400
|
+
- Questions with more than 1 point show a trophy indicator (🏆) during the quiz
|
|
401
|
+
|
|
402
|
+
Example:
|
|
403
|
+
```yaml
|
|
404
|
+
questions:
|
|
405
|
+
- question: "Easy question"
|
|
406
|
+
options:
|
|
407
|
+
- "A"
|
|
408
|
+
- "B"
|
|
409
|
+
- "C"
|
|
410
|
+
- "D"
|
|
411
|
+
correct_answer: 0
|
|
412
|
+
# points: 1 (default, can be omitted)
|
|
413
|
+
|
|
414
|
+
- question: "Hard question worth 3 points"
|
|
415
|
+
options:
|
|
416
|
+
- "X"
|
|
417
|
+
- "Y"
|
|
418
|
+
- "Z"
|
|
419
|
+
correct_answer: 2
|
|
420
|
+
points: 3 # This question is worth 3 points
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Text Input Questions:**
|
|
424
|
+
|
|
425
|
+
In addition to multiple choice questions, you can create text input questions where students type their answer. Questions with `checker` field are automatically detected as text input questions:
|
|
426
|
+
|
|
427
|
+
```yaml
|
|
428
|
+
questions:
|
|
429
|
+
- question: "What is 2+2?"
|
|
430
|
+
default_value: "" # Initial value shown in textarea
|
|
431
|
+
correct_value: "4" # Shown when answer is wrong (if show_right_answer is true)
|
|
432
|
+
checker: | # Python code to validate the answer
|
|
433
|
+
assert user_answer.strip() == '4', 'Expected 4'
|
|
434
|
+
points: 1
|
|
435
|
+
|
|
436
|
+
- question: "Calculate sqrt(16)"
|
|
437
|
+
correct_value: "4.0"
|
|
438
|
+
checker: |
|
|
439
|
+
result = float(user_answer)
|
|
440
|
+
assert abs(result - sqrt(16)) < 0.01, f'Expected 4, got {result}'
|
|
441
|
+
points: 2
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**Text Question Fields:**
|
|
445
|
+
- `question` - Question text (required)
|
|
446
|
+
- `checker` - Required to identify as text question (can be empty for exact match)
|
|
447
|
+
- `default_value` - Initial value shown in textarea (optional)
|
|
448
|
+
- `correct_value` - Correct answer shown when student is wrong (optional)
|
|
449
|
+
- `points` - Points for correct answer (default: 1)
|
|
450
|
+
|
|
451
|
+
**Checker Code:**
|
|
452
|
+
- Uses variable `user_answer` (the student's text input)
|
|
453
|
+
- Available: `math` module (use `math.sqrt`, `math.sin`, etc.)
|
|
454
|
+
- Available helper functions:
|
|
455
|
+
- `to_int(str)` - Convert string to integer (strips whitespace)
|
|
456
|
+
- `distance(str)` - Parse distance with units: "2000", "2000m", "2км", "2km" all return 2000
|
|
457
|
+
- `direction_angle(str)` - Parse direction angle: "20" returns 2000, "20-30" returns 2030
|
|
458
|
+
- If checker raises any exception, the answer is marked incorrect
|
|
459
|
+
- If no checker is provided, exact match with `correct_value` is used (with whitespace stripped)
|
|
460
|
+
|
|
461
|
+
**Checker Templates:**
|
|
462
|
+
|
|
463
|
+
You can configure reusable checker templates in your `webquiz.yaml`:
|
|
464
|
+
|
|
465
|
+
```yaml
|
|
466
|
+
checker_templates:
|
|
467
|
+
- name: "Exact Match"
|
|
468
|
+
code: "assert user_answer.strip() == 'expected', 'Wrong answer'"
|
|
469
|
+
- name: "Numeric Check"
|
|
470
|
+
code: "assert float(user_answer) == 42, 'Expected 42'"
|
|
471
|
+
- name: "Contains Check"
|
|
472
|
+
code: "assert 'keyword' in user_answer.lower(), 'Must contain keyword'"
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Templates appear in the admin quiz editor for easy insertion.
|
|
476
|
+
|
|
477
|
+
### Server Configuration
|
|
478
|
+
|
|
479
|
+
Optional server configuration file (`webquiz.yaml`):
|
|
480
|
+
|
|
481
|
+
```yaml
|
|
482
|
+
server:
|
|
483
|
+
host: "0.0.0.0"
|
|
484
|
+
port: 8080
|
|
485
|
+
include_ipv6: false # Include IPv6 addresses in network interfaces list
|
|
486
|
+
url_format: "http://{IP}:{PORT}/" # URL format for admin panel (use {IP} and {PORT} placeholders)
|
|
487
|
+
|
|
488
|
+
registration:
|
|
489
|
+
approve: false # Set to true to require admin approval
|
|
490
|
+
fields:
|
|
491
|
+
- name: "full_name"
|
|
492
|
+
label: "Full Name"
|
|
493
|
+
required: true
|
|
494
|
+
|
|
495
|
+
quiz:
|
|
496
|
+
show_right_answer: false # Show correct answer after submission
|
|
497
|
+
show_answers_on_completion: true # Reveal answers only after all students complete
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
All configuration sections are optional and have sensible defaults.
|
|
501
|
+
|
|
502
|
+
### Answer Visibility Options
|
|
503
|
+
|
|
504
|
+
WebQuiz offers flexible control over when students can see correct answers:
|
|
505
|
+
|
|
506
|
+
- **`show_right_answer: true`** (default): Students see correct answers immediately after submitting each question
|
|
507
|
+
- **`show_right_answer: false`**: Correct answers are completely hidden during the quiz and on the final results page
|
|
508
|
+
- **`show_answers_on_completion: true`**: Works with `show_right_answer: false` to reveal answers dynamically:
|
|
509
|
+
- Answers remain hidden until ALL students complete the quiz
|
|
510
|
+
- Students see a waiting message with a reload button
|
|
511
|
+
- Once all students finish, correct answers become visible
|
|
512
|
+
- If new students register, answers are hidden again until everyone completes
|
|
513
|
+
- In approval mode, only approved students count toward completion
|
|
514
|
+
|
|
515
|
+
**Example Use Case**: Useful for collaborative learning environments where you want students to discuss answers together after everyone has completed the quiz independently.
|
|
516
|
+
|
|
517
|
+
### SSH Tunnel for Public Access
|
|
518
|
+
|
|
519
|
+
WebQuiz can expose your local server to the internet via an SSH reverse tunnel, making it accessible from a public URL. This is useful for:
|
|
520
|
+
- Running quizzes from a local computer without port forwarding
|
|
521
|
+
- Temporary public access without dedicated hosting
|
|
522
|
+
- Classroom environments where students connect from outside the network
|
|
523
|
+
|
|
524
|
+
**Configuration** (`webquiz.yaml`):
|
|
525
|
+
|
|
526
|
+
**Option 1: Fetch config from server**
|
|
527
|
+
```yaml
|
|
528
|
+
tunnel:
|
|
529
|
+
server: "tunnel.example.com" # SSH tunnel server hostname
|
|
530
|
+
public_key: "keys/id_ed25519.pub" # Path to SSH public key (auto-generated if missing)
|
|
531
|
+
private_key: "keys/id_ed25519" # Path to SSH private key (auto-generated if missing)
|
|
532
|
+
# Config will be fetched from https://tunnel.example.com/tunnel_config.yaml
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Option 2: Local config (bypasses server fetch)**
|
|
536
|
+
```yaml
|
|
537
|
+
tunnel:
|
|
538
|
+
server: "tunnel.example.com"
|
|
539
|
+
public_key: "keys/id_ed25519.pub"
|
|
540
|
+
private_key: "keys/id_ed25519"
|
|
541
|
+
socket_name: "my-quiz-socket" # Optional: Fixed socket name (default: random 6-8 chars)
|
|
542
|
+
config: # Optional: Provide locally to skip fetching from server
|
|
543
|
+
username: "tunneluser"
|
|
544
|
+
socket_directory: "/var/run/tunnels"
|
|
545
|
+
base_url: "https://tunnel.example.com/tests"
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
**How it works:**
|
|
549
|
+
|
|
550
|
+
1. **Automatic Key Generation**: If keys don't exist, WebQuiz automatically generates ED25519 SSH key pairs
|
|
551
|
+
2. **Admin Control**: Navigate to the admin panel to see the tunnel status
|
|
552
|
+
3. **Copy Public Key**: Copy the generated public key and add it to `~/.ssh/authorized_keys` on your tunnel server
|
|
553
|
+
4. **Connect**: Click the "Connect" button in the admin panel to establish the tunnel
|
|
554
|
+
5. **Public URL**: Once connected, a public URL will be displayed (e.g., `https://tunnel.example.com/tests/a3f7b2/`)
|
|
555
|
+
6. **Auto-Reconnect**: If the connection drops, WebQuiz automatically attempts to reconnect
|
|
556
|
+
|
|
557
|
+
**Server Requirements:**
|
|
558
|
+
- The tunnel server must be configured to support Unix domain socket forwarding
|
|
559
|
+
- The server should provide a `tunnel_config.yaml` endpoint at `https://[server]/tunnel_config.yaml` with:
|
|
560
|
+
```yaml
|
|
561
|
+
username: tunneluser
|
|
562
|
+
socket_directory: /var/run/tunnels
|
|
563
|
+
base_url: https://tunnel.example.com/tests
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Security Notes:**
|
|
567
|
+
- SSH keys are generated with no passphrase for automated connection
|
|
568
|
+
- Keys are stored with proper permissions (600 for private key)
|
|
569
|
+
- Connection is admin-initiated (no auto-connect on startup)
|
|
570
|
+
- Connection status is shown in real-time via WebSocket
|
|
571
|
+
|
|
572
|
+
## 📊 Data Export
|
|
573
|
+
|
|
574
|
+
User responses are automatically exported to CSV files with quiz-prefixed filenames and unique suffixes to prevent overwrites:
|
|
575
|
+
|
|
576
|
+
**Example:** `math_quiz_user_responses_0001.csv`
|
|
577
|
+
|
|
578
|
+
```csv
|
|
579
|
+
user_id,question,selected_answer,correct_answer,is_correct,time_taken_seconds
|
|
580
|
+
123456,"What is 2 + 2?","4","4",True,3.45
|
|
581
|
+
123456,"What is 5 × 3?","15","15",True,2.87
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
A second file with `.users.csv` suffix contains user statistics:
|
|
585
|
+
|
|
586
|
+
```csv
|
|
587
|
+
user_id,username,registered_at,total_questions_asked,correct_answers,earned_points,total_points,total_time
|
|
588
|
+
123456,student1,2025-01-15T10:30:00,5,4,7,9,12:46
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
- `total_time` - Total quiz completion time in `MM:SS` format (minutes:seconds)
|
|
592
|
+
- `earned_points` - Points earned from correct answers
|
|
593
|
+
- `total_points` - Maximum possible points for questions answered
|
|
594
|
+
|
|
595
|
+
CSV files are created with proper escaping and include all user response data. Files are flushed periodically (every 5 seconds) to ensure data persistence.
|
|
596
|
+
|
|
597
|
+
## 🎨 Customization
|
|
598
|
+
|
|
599
|
+
### Adding Your Own Quizzes
|
|
600
|
+
|
|
601
|
+
1. **Create a YAML file** in the `quizzes/` directory
|
|
602
|
+
```bash
|
|
603
|
+
# Example: quizzes/science_quiz.yaml
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
2. **Add your questions** following the format:
|
|
607
|
+
```yaml
|
|
608
|
+
title: "Science Quiz"
|
|
609
|
+
questions:
|
|
610
|
+
- question: "What is H2O?"
|
|
611
|
+
options:
|
|
612
|
+
- "Water"
|
|
613
|
+
- "Hydrogen"
|
|
614
|
+
- "Oxygen"
|
|
615
|
+
- "Salt"
|
|
616
|
+
correct_answer: 0
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
3. **Switch to your quiz** via the admin interface
|
|
620
|
+
- Access `/admin` with your master key
|
|
621
|
+
- Select your quiz from the dropdown
|
|
622
|
+
- Click "Switch Quiz"
|
|
623
|
+
|
|
624
|
+
### Admin Interface
|
|
625
|
+
|
|
626
|
+
Enable admin features with a master key:
|
|
627
|
+
|
|
628
|
+
```bash
|
|
629
|
+
webquiz --master-key secret123
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
Access admin panels:
|
|
633
|
+
- `/admin` - Quiz management and user approval
|
|
634
|
+
- `/files` - View logs, CSV files, and edit configuration
|
|
635
|
+
- `/live-stats` - Real-time user progress dashboard
|
|
636
|
+
|
|
637
|
+
The admin panel automatically detects when the package has been updated while the server is running and displays a "Restart Required" notification, prompting you to restart the server to use the new version.
|
|
638
|
+
|
|
639
|
+
### Styling
|
|
640
|
+
|
|
641
|
+
- Templates are located in `webquiz/templates/`
|
|
642
|
+
- Built-in dark/light theme toggle
|
|
643
|
+
- Responsive design works on mobile and desktop
|
|
644
|
+
- Generated `static/index.html` can be customized (regenerates on quiz switch)
|
|
645
|
+
|
|
646
|
+
## 🛠️ Development
|
|
647
|
+
|
|
648
|
+
### Building Binary Executable
|
|
649
|
+
|
|
650
|
+
Create a standalone executable with PyInstaller:
|
|
651
|
+
|
|
652
|
+
```bash
|
|
653
|
+
# Build binary
|
|
654
|
+
poetry run build_binary
|
|
655
|
+
|
|
656
|
+
# Or directly
|
|
657
|
+
python -m webquiz.build
|
|
658
|
+
|
|
659
|
+
# The binary will be created at:
|
|
660
|
+
./dist/webquiz
|
|
661
|
+
|
|
662
|
+
# Run the binary
|
|
663
|
+
./dist/webquiz
|
|
664
|
+
./dist/webquiz --master-key secret123
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
The binary includes all templates and configuration examples, with automatic directory creation on first run.
|
|
668
|
+
|
|
669
|
+
### Key Technical Decisions
|
|
670
|
+
|
|
671
|
+
- **Multi-quiz system**: Questions loaded from `quizzes/` directory with YAML files
|
|
672
|
+
- **Master key authentication**: Admin endpoints protected with decorator-based authentication
|
|
673
|
+
- **Server-side timing**: All timing calculated server-side for accuracy
|
|
674
|
+
- **Server-side question randomization**: Random question order generated server-side, stored per-user, ensures unique randomized order for each student with session persistence
|
|
675
|
+
- **Middleware error handling**: Clean error management with proper HTTP status codes
|
|
676
|
+
- **CSV module usage**: Proper escaping for data with commas/quotes
|
|
677
|
+
- **Smart file naming**: CSV files prefixed with quiz names, unique suffixes prevent overwrites
|
|
678
|
+
- **Dynamic quiz switching**: Complete server state reset when switching quizzes
|
|
679
|
+
- **WebSocket support**: Real-time updates for admin and live statistics
|
|
680
|
+
- **Binary distribution**: PyInstaller for standalone executable with auto-configuration
|
|
681
|
+
|
|
682
|
+
### Architecture
|
|
683
|
+
|
|
684
|
+
- **Backend**: Python 3.9-3.14 with aiohttp async web framework
|
|
685
|
+
- **Frontend**: Vanilla HTML/CSS/JavaScript (no frameworks)
|
|
686
|
+
- **Storage**: In-memory with periodic CSV backups (30-second intervals)
|
|
687
|
+
- **Session Management**: Cookie-based with server-side validation
|
|
688
|
+
- **Real-time Features**: WebSocket for live stats and admin notifications
|
|
689
|
+
|
|
690
|
+
## 🐛 Troubleshooting
|
|
691
|
+
|
|
692
|
+
### Common Issues
|
|
693
|
+
|
|
694
|
+
**Port already in use:**
|
|
695
|
+
```bash
|
|
696
|
+
# Kill process using port 8080
|
|
697
|
+
lsof -ti:8080 | xargs kill -9
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
**Virtual environment issues:**
|
|
701
|
+
```bash
|
|
702
|
+
# Recreate virtual environment
|
|
703
|
+
rm -rf venv
|
|
704
|
+
python3 -m venv venv
|
|
705
|
+
source venv/bin/activate
|
|
706
|
+
pip install -r requirements.txt
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
**Quiz not loading:**
|
|
710
|
+
- Check that quiz YAML files have valid syntax
|
|
711
|
+
- Verify `quizzes/` directory exists and contains `.yaml` files
|
|
712
|
+
- Check server logs for errors
|
|
713
|
+
- Restart server after adding new quiz files
|
|
714
|
+
|
|
715
|
+
**Admin interface not accessible:**
|
|
716
|
+
- Ensure you started server with `--master-key` option
|
|
717
|
+
- Or set `WEBQUIZ_MASTER_KEY` environment variable
|
|
718
|
+
- Check that you're using the correct master key
|
|
719
|
+
|
|
720
|
+
**Tests failing:**
|
|
721
|
+
- Always run tests in virtual environment: `source venv/bin/activate`
|
|
722
|
+
- Install test dependencies: `poetry install` or `pip install -r requirements.txt`
|
|
723
|
+
- Use parallel testing: `pytest tests/ -v -n 4`
|
|
724
|
+
|
|
725
|
+
**Daemon not stopping:**
|
|
726
|
+
```bash
|
|
727
|
+
# Check status
|
|
728
|
+
webquiz --status
|
|
729
|
+
|
|
730
|
+
# Force kill if needed
|
|
731
|
+
cat webquiz.pid | xargs kill -9
|
|
732
|
+
rm webquiz.pid
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
## 📝 License
|
|
736
|
+
|
|
737
|
+
This project is open source. Feel free to use and modify as needed.
|
|
738
|
+
|
|
739
|
+
## 🤝 Contributing
|
|
740
|
+
|
|
741
|
+
1. Fork the repository
|
|
742
|
+
2. Create a feature branch
|
|
743
|
+
3. Add tests for new functionality
|
|
744
|
+
4. Ensure all tests pass
|
|
745
|
+
5. Submit a pull request
|
|
746
|
+
|
|
747
|
+
## 📞 Support
|
|
748
|
+
|
|
749
|
+
For questions or issues:
|
|
750
|
+
- Check the server logs (`server.log`)
|
|
751
|
+
- Run the test suite to verify setup
|
|
752
|
+
- Review this README and `CLAUDE.md` for detailed documentation
|
|
753
|
+
|