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.
@@ -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
+