The rule library supports course order, spelling-to-sound families, and sound-to-spelling families. The two family views adopt the tree-map organization from 英语--自然拼读-调整(1).pdf, while IPA, audio, and examples continue to come from the project's reviewed structured curriculum rather than copying inconsistent PDF transcriptions.
Phonics English Teacher is a local web app for learning English through natural phonics. It guides the learner to decode printed words into sounds and encode heard or prompted words back into spelling.
The public repository intentionally excludes runtime databases, secrets, learner data, private reference PDFs, and third-party human-recording files. See THIRD_PARTY_NOTICES.md and run python scripts/sync_human_phonics_audio.py before building when your instructional use complies with the source site's terms.
- Consonants:
p b t d c/k g f v s z h m n l r j y w qu - Short vowels:
a e i o u y - Long vowels and split digraphs:
a_e e_e i_e o_e u_e - Vowel teams:
ai ay ea ee ey ie igh oa ow ew ui ue - R-controlled vowels:
ar er ir or ur - Consonant patterns:
sh ch th wh ph ck ng nk tch dge, softc/g, and voiceds/th - Alternate vowel spellings: both common sounds of
ea,oo, andow, plusou oi oy au aw air ear - Silent-letter patterns, common endings, and high-frequency tricky words
The learning loop is teacher-guided:
- Recall two old sounds without hints.
- Build the current word with touch-friendly sound boxes.
- Blend sounds left to right and spell from sound back to print.
- Transfer the code to a different word.
- Read a short human-curated decodable story and answer one meaning question.
- Review weak rules using adaptive spaced practice.
Print-to-sound questions use three playable sound choices, so beginners do not need a special IPA keyboard. Choosing to hear a target or option before answering is treated as guided practice rather than independent mastery evidence. Sound-to-print questions still require typing the complete spelling.
The dashboard uses the learner's saved daily practice target, tracks consecutive study days, and puts weak or due words first. The word analyzer reports coverage, alternate possible sounds, a syllable-count hint, and a special heart-word routine for irregular spellings.
Known course words use reviewed whole-word sound maps. The study and lookup pages render the complete word with an accessible semantic color system for vowels, consonants, multi-letter teams, endings, silent letters, and irregular parts; split digraph endpoints share a numbered marker. Unseen words remain explicitly labeled as heuristic analysis rather than dictionary-confirmed pronunciation. See the 2026-07-17 phonics optimization audit for the PDF synthesis, three design-review rounds, acceptance criteria, and detailed roadmap.
Unfinished teacher sessions are stored in the configured SQL database and automatically recovered when the learner returns to the study page. Browser microphone results are stored as intelligibility-practice evidence, but they remain separate from decoding, spelling, retention, and mastery certification.
Teacher sessions now mix current-target work with due recall and cumulative transfer instead of repeating one rule in a block. A wrong word answer identifies the first affected sound unit and inserts one guided retry; that retry is never counted as independent evidence. Story comprehension also remains separate from phonics certification. The child dashboard leads with an eight-minute mission and a gentle word-garden reward, while the parent section reports seven-day learning days, demonstrated capabilities, the next focus, and one two-minute home activity.
English is not perfectly phonetic. The app therefore marks ambiguous and tricky words for confirmation instead of claiming that one printed pattern always has one pronunciation.
- Backend: FastAPI, SQLAlchemy, PostgreSQL on the audited server, or SQLite for one-machine use
- Frontend: Vue 3, TypeScript, Pinia, Element Plus, Vite
- Tests: pytest and frontend production build
Install backend and frontend dependencies:
python3 -m venv venv
./venv/bin/pip install -r backend/requirements.txt
cd frontend
npm ciStart the complete local application:
./start_dev.shThe development server exposes both the Vue interface and FastAPI routes through one local origin. This avoids a half-started state where the page opens but the rule library and study APIs are unavailable.
For conventional two-process development, start FastAPI separately and disable the embedded development transport:
cd backend
../venv/bin/python -m uvicorn app.main:app --host 127.0.0.1 --port 8000
cd ../frontend
VITE_EMBED_BACKEND=false npm run dev -- --host 127.0.0.1 --port 5174Open:
- Application: http://127.0.0.1:5174
- Integrated health check: http://127.0.0.1:5174/health
- Backend readiness check: http://127.0.0.1:8000/ready
GET /api/phonics/curriculumGET /api/phonics/search?q=ighGET /api/phonics/analyze?word=brightGET /api/phonics/dashboardGET /api/phonics/progressGET /api/phonics/session/currentPOST /api/phonics/session/startPOST /api/phonics/session/{session_id}/answerPOST /api/phonics/pronunciation-attemptsDELETE /api/admin/users/{user_id}(administrator only; permanently removes the account and owned learning data)
./venv/bin/python -m pytest backend/tests/test_phonics_service.py backend/tests/test_phonics_api.py -q
npm --prefix frontend run build
npm --prefix frontend testLocal development creates backend/app/database/.jwt_secret with owner-only permissions when SECRET_KEY is not set. Production and Docker deployments must provide a secret of at least 32 characters through the runtime environment; the application refuses to start in ENVIRONMENT=production without it. Never commit or print this value.
When DATABASE_URL is unset, create and verify an online SQLite backup while the application is running:
./venv/bin/python backend/scripts/database_backup.py backup \
--database backend/app/database/hanzi_learning.db \
--backup-dir backupsRestore is intentionally guarded and must run only after the application container/process has stopped:
./venv/bin/python backend/scripts/database_backup.py restore \
--backup backups/hanzi_learning-YYYYMMDDTHHMMSSffffffZ.db \
--database backend/app/database/hanzi_learning.db \
--backup-dir backups \
--confirm-application-stoppedThe restore command verifies the candidate, creates a pre-restore backup, restores through a temporary database, and verifies the result before replacing the target. Docker deployment must mount backend/app/database and backups as persistent volumes rather than storing them in the image layer. When DATABASE_URL points to PostgreSQL, use a PostgreSQL/1Panel backup instead; the SQLite backup script must not be used for that database.
The login page includes a complete forgot password -> one-time link -> new password flow. Recovery responses are deliberately identical for known and unknown accounts, requests are rate limited, reset tokens are stored only as protected hashes, expire after 20 minutes, and can be used once. Changing or resetting a password invalidates every existing browser session.
When SMTP is not configured, a recovery request appears in the administrator account console. An administrator can set a temporary password for the learner and communicate it through a trusted channel. This makes recovery usable on a private installation before mail delivery is enabled. The only administrator can use the server terminal break-glass command; the new password is read with hidden input and never appears in shell history:
docker compose -f docker-compose.server.yml exec app \
python scripts/reset_admin_password.py --username adminFor automatic email delivery, add these values to the private runtime .env through 1Panel or a server terminal. Never send the SMTP authorization code through chat or commit it:
PUBLIC_APP_URL=https://your-learning-site.example
AUTH_COOKIE_SECURE=true
ENABLE_PASSWORD_RESET_EMAIL=true
SMTP_HOST=smtp.example.com
SMTP_PORT=465
SMTP_USER=sender@example.com
SMTP_PASSWORD=use-an-app-specific-authorization-code
FROM_EMAIL=sender@example.com
SMTP_USE_SSL=true
SMTP_USE_STARTTLS=falseEnable automatic delivery only after confirming that existing account email addresses belong to their users. Until then, keep ENABLE_PASSWORD_RESET_EMAIL=false and use the administrator-assisted queue.
Access JWTs expire after 15 minutes. Refresh tokens last at most seven days, rotate after every use, live only in an HttpOnly same-site cookie, and are revoked by server-side logout, password changes, account suspension, or administrator resets.
Production uses one learning-app-1 container. A multi-stage Docker build compiles Vue and copies the resulting PWA into the Python image; one Uvicorn process serves the frontend, audio, API, and readiness endpoint on loopback port 8088. PostgreSQL remains in the existing shared database container. The audited server Compose uses host networking only because its Docker bridge cannot pass container traffic; it does not alter Docker daemon, proxy, Tailscale, DNS, firewall, or unrelated containers.
On the target server, keep the complete deployment under /media/qingshan/D/1panel/apps/learning. Run every Compose and backup command from that directory so the relative persistent paths remain on the requested storage location.
Create the runtime environment without committing the secret:
cp .env.docker.example .env
umask 077
generated_secret="$(openssl rand -hex 32)"
sed -i.bak "s/^SECRET_KEY=.*/SECRET_KEY=${generated_secret}/" .env
rm -f .env.bak
unset generated_secretBuild and start:
docker compose -f docker-compose.server.yml config
docker compose -f docker-compose.server.yml build
docker compose -f docker-compose.server.yml up -d
docker compose -f docker-compose.server.yml ps
curl -fsS http://127.0.0.1:8088/health
curl -fsS http://127.0.0.1:8088/ready
curl -fsS http://127.0.0.1:8088/api/phonics/curriculumPersistent host paths are:
- Project root:
/media/qingshan/D/1panel/apps/learning - SQLite fallback and the generated local secret directory:
/media/qingshan/D/1panel/apps/learning/backend/app/database - Media:
/media/qingshan/D/1panel/apps/learning/backend/media - Verified SQLite backups:
/media/qingshan/D/1panel/apps/learning/backups
Before an SQLite deployment upgrade, create a backup from the running application container:
docker compose -f docker-compose.server.yml exec app python scripts/database_backup.py backup \
--database app/database/hanzi_learning.db \
--backup-dir backupsFor PostgreSQL deployments, create a verified database-native or 1Panel backup instead. Rollback means restoring the prior image or source revision and restarting the Compose project. A database restore is a separate guarded operation: stop the application first, run the matching database restore procedure, then start and verify readiness. Never restore over a running SQLite writer.
The local Mac does not have Docker installed, so local development uses ./start_dev.sh. The server image build, loopback listeners, container health, API, authentication, audio, and restart persistence are verified on the Docker-capable target server.