Files
truenas-stacks/home/doris-schoolhouse/README.md
2026-05-16 15:54:20 +00:00

200 lines
5.8 KiB
Markdown

# Doris Schoolhouse
Desktop-first local web app for school intake, recordings, D2L sync, and Paperless linkage.
## Purpose
This app gives John a fast local UI to:
- upload assignment turn-ins
- upload class recordings
- confirm suggested metadata
- track assignment status through grading
- sync classes/assignments/grades/comments from D2L
- archive submissions/transcripts/summaries in Paperless
## Source vs runtime
- **Repo source:** `home/doris-schoolhouse`
- **Expected live NOMAD runtime:** `/opt/doris-schoolhouse`
Do not run production from an agent workspace.
## Stack
- FastAPI
- Jinja2 templates
- shared Postgres
- Pangolin network
- existing Paperless and n8n school workflows
- container build now includes Python deps + `ffmpeg` for recording conversion
## Current state
The app is live on NOMAD at `/opt/doris-schoolhouse` and uses shared Postgres in production.
For local iteration, the source tree still supports a JSON-backed mode:
- default state dir: `/data/state`
- default upload temp dir: `/data/uploads`
For local host-side testing, override with env vars such as:
```bash
export SCHOOLHOUSE_STATE_DIR=/tmp/doris-schoolhouse-state
export UPLOAD_TMP_DIR=/tmp/doris-schoolhouse-uploads
```
Production runtime uses shared Postgres plus Paperless/n8n integration.
`SCHOOLHOUSE_STORAGE_BACKEND=auto` prefers Postgres when psycopg and a PostgreSQL DSN are available, and falls back to JSON state for local iteration.
Repo contents still include:
- route handlers and templates
- schema/bootstrap helpers
- compose/env examples
- D2L / Paperless / n8n integration code
## Layout
```text
home/doris-schoolhouse/
├── .env.example
├── docker-compose.yaml
├── requirements.txt
└── app/
├── main.py
├── config.py
├── models.py
├── db/schema.sql
├── routes/
├── services/
├── static/
└── templates/
```
## Environment and integration helpers
Useful helpers now included:
```bash
bin/check-env.sh
bin/apply-postgres-schema.sh
bin/test-assignment-handoff.sh <file> <class_name> [assignment_name]
bin/test-recording-handoff.sh <file> <class_name>
```
These are safe prep helpers. The handoff scripts are dry-run previews; they print the exact curl commands rather than firing them automatically.
## Helper scripts
Local iteration helpers:
```bash
bin/bootstrap-local.sh
bin/run-local.sh
bin/process-recording.sh <recording_id>
```
These default to the JSON backend and `/tmp` paths unless you override the env vars.
## Bootstrap local state quickly
For local iteration without live Postgres:
```bash
cd /home/fizzlepoof/repos/truenas-stacks/home/doris-schoolhouse
export SCHOOLHOUSE_STORAGE_BACKEND=json
export SCHOOLHOUSE_STATE_DIR=/tmp/doris-schoolhouse-state
export UPLOAD_TMP_DIR=/tmp/doris-schoolhouse-uploads
PYTHONPATH=. python3 app/scripts/bootstrap_state.py
```
That seeds local state from the latest D2L snapshot.
## Recording worker helper
There is now a local worker entrypoint for WAV → MP3 processing:
```bash
cd /home/fizzlepoof/repos/truenas-stacks/home/doris-schoolhouse
export SCHOOLHOUSE_STORAGE_BACKEND=json
export SCHOOLHOUSE_STATE_DIR=/tmp/doris-schoolhouse-state
export UPLOAD_TMP_DIR=/tmp/doris-schoolhouse-uploads
PYTHONPATH=. python3 app/scripts/process_recording.py <recording_id>
```
If `ffmpeg` is not installed, the worker records a clean `blocked` result with the exact conversion command it wanted to run.
## Local validation
```bash
cd /home/fizzlepoof/repos/truenas-stacks/home/doris-schoolhouse
python3 -m py_compile app/main.py app/config.py app/models.py app/routes/*.py app/services/*.py
python3 - <<'PY'
import yaml
from pathlib import Path
print(yaml.safe_load(Path('docker-compose.yaml').read_text())['services'].keys())
PY
```
## Container build
The source now includes a `Dockerfile` so the app can run with its own dependencies instead of relying on the host.
That image includes:
- Python dependencies from `requirements.txt`
- `ffmpeg`
- `curl` for healthchecks
So even if the host is missing `ffmpeg`, the containerized runtime can still do WAV → MP3 conversion.
## Deploy shape
Expected live deploy flow on NOMAD:
```bash
sudo mkdir -p /opt/doris-schoolhouse/data
sudo rsync -a --delete --exclude '.env' --exclude 'data/' /home/fizzlepoof/repos/truenas-stacks/home/doris-schoolhouse/ /opt/doris-schoolhouse/
cd /opt/doris-schoolhouse
sudo cp .env.nomad.example .env
docker compose --env-file .env config
docker compose --env-file .env up -d --build
```
That flow is the reference for refreshing the live runtime from repo source.
## Live integration notes
Current verified live behavior:
- `https://n8n.paccoco.com/webhook/school/intake/upload` accepts Doris Schoolhouse multipart assignment uploads and returned a successful live smoke-test response on 2026-05-15.
- `https://n8n.paccoco.com/webhook/class/upload` now succeeds in live smoke testing after upstream workflow and LiteLLM fixes on 2026-05-15.
- Direct Whisper testing against `http://10.5.1.16:8786/v1/audio/transcriptions` succeeded with a tiny MP3.
- Direct LiteLLM testing against `http://10.5.1.6:4000/v1/chat/completions` now succeeds for model `medium` after the PD route fix.
The app now captures webhook HTTP failures as structured responses instead of crashing blindly.
## NOMAD-specific deployment notes
On NOMAD, Schoolhouse should run as a standalone service from `/opt/doris-schoolhouse`.
Unlike PD stacks, this host does **not** currently use the PD Docker external networks for Pangolin or shared databases. The practical deploy model here is:
- publish host port `8091`
- point Pangolin/Newt at that host port
- reach shared Postgres on PD over `10.5.1.6:5432`
Use `.env.nomad.example` as the starting point for the live runtime.
## Deployment references
- `DEPLOYMENT.md` — clean NOMAD rollout notes
- `.env.nomad.example` — compose-oriented runtime example for NOMAD