ParentCalendar
Scan a paper calendar (school, lunch menu, sports, activities), review the parsed events, and export an .ics file or a subscribed feed for Apple and Google Calendar.
Self-hosted only: runs directly with Python or as a Docker image, with SQLite as the backend. The page is read by a vision model on your own Ollama server (or, optionally, Claude).
Status: Phase 1 (MVP for one household) in progress.
Run it
Copy .env.example to .env and set at least PUBLIC_URL and OLLAMA_URL.
Plain Python (Python 3.11+, and Node only to build the front end):
cd frontend && npm ci && npm run build && cd ..
pip install -r requirements.txt
python main.py
Then open http://127.0.0.1:8000. Rebuild the front end after changing anything in frontend/.
Docker: copy compose.example.yml to compose.yml, then docker compose up -d --build.
Subscription feeds: each calendar has a feed at PUBLIC_URL/feed/<token>.ics (Calendar tab → Subscribe). It's the one URL that needs no login, so your family's phones can reach it from anywhere: expose /feed/ publicly on your reverse proxy and keep the rest of the app on your LAN or VPN (deploy/nginx.conf is an example). The token is the secret; "Make a new one" on the Calendar tab replaces it. The access log shows /feed/*** instead of the token.
Holidays: Settings → Holidays shows federal holidays on the Calendar tab (never exported; phones already have them) and, with a free CHECKIDAY_API_KEY, national days like National Chicken Sandwich Day, in the app, in exports, or both. Checkiday's free plan only gives today's list, so each day's arrives that morning.
Backups: python main.py backup <folder> copies the database (safely, while running) and the photos. Schedule it nightly.
Develop
cd frontend && npm run devserves the front end with hot reload and proxies/apitopython main.py.- Tests:
python tests/test_phase1.py,python -m parentcalendar.ics,python -m parentcalendar.extraction --selftest. - Extraction quality (Phase 0):
python phase0/extract.py, thenpython phase0/score.py private/runs/<run>. The golden set lives inprivate/, outside git.