Phase 6: write important school events to the Home Assistant calendar
Add the Home Assistant REST calendar client and the idempotent sync that writes important school events to Sam's Home Assistant calendar (FR-8). The school stays read-only; the only write is to the calendar (rule 7). - internal/calendar: HA REST client (states, create_event, get_events, delete_event) and the sync that selects important future events, adopts an existing event by its description marker, records the HA event id on the item, and records every attempt. - internal/store: migration 003 (items.ha_event_uid, items.ha_pushed_at and the append-only calendar_pushes table), candidates, mark-synced and the push log. - internal/parse: a school event is important; a routine class period is not. - cmd/school: `school calendar entities|status|push|events`. - web: an "Add to calendar" item action and a fail-loud calendar banner, so a failed write is never swallowed (FR-12). - scripts/calendar-push.sh, Makefile target, env example, docs and tests. The target is configurable (SCHOOL_HA_CALENDAR_ENTITY). Home Assistant has no non-Google writable calendar today, so the entity is asked on AUD-110. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
61
scripts/calendar-push.sh
Executable file
61
scripts/calendar-push.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# Phase 6: write important school events to Sam's Home Assistant calendar and
|
||||
# read the calendar back (AGENTS.md §4 FR-8, §11 Phase 6).
|
||||
#
|
||||
# The school stays read-only. The only write is to the calendar. The Home
|
||||
# Assistant token comes from the environment or from the service env file and is
|
||||
# never printed.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/calendar-push.sh entities
|
||||
# scripts/calendar-push.sh status
|
||||
# scripts/calendar-push.sh push [--child Harry] [--dry-run]
|
||||
# scripts/calendar-push.sh events [--days 14]
|
||||
#
|
||||
# Environment overrides:
|
||||
# IMAGE image tag (default 0.4.0-phase6)
|
||||
# VOLUME SQLite volume (default compass-teams-nhs_compass-teams-nhs-data)
|
||||
# ENV_FILE HA config source (default environment.d/school-service.env)
|
||||
# SCHOOL_TIMEZONE family timezone (default Australia/Sydney)
|
||||
# SCHOOL_HA_URL Home Assistant base URL (no default; required)
|
||||
# SCHOOL_HA_TOKEN long-lived access token (no default; required)
|
||||
# SCHOOL_HA_CALENDAR_ENTITY calendar entity id (no default; required)
|
||||
# TIMEOUT docker command timeout (default 300)
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
IMAGE="${IMAGE:-compass-teams-nhs/school:0.4.0-phase6}"
|
||||
VOLUME="${VOLUME:-compass-teams-nhs_compass-teams-nhs-data}"
|
||||
ENV_FILE="${ENV_FILE:-$REPO_ROOT/environment.d/school-service.env}"
|
||||
SCHOOL_TIMEZONE="${SCHOOL_TIMEZONE:-Australia/Sydney}"
|
||||
TIMEOUT="${TIMEOUT:-300}"
|
||||
|
||||
# The real service env file is gitignored. Take only the calendar variables from
|
||||
# it, so the service's bcrypt hash (which contains "$$") is never re-expanded.
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
SCHOOL_HA_URL=*|SCHOOL_HA_TOKEN=*|SCHOOL_HA_CALENDAR_ENTITY=*|SCHOOL_HA_INSECURE=*|SCHOOL_HA_TIMEOUT=*)
|
||||
key="${line%%=*}"
|
||||
val="${line#*=}"
|
||||
if [[ -z "${!key:-}" ]]; then
|
||||
export "$key=$val"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done < "$ENV_FILE"
|
||||
fi
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "usage: scripts/calendar-push.sh entities|status|push|events [options]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
exec timeout "$TIMEOUT" docker run --rm --network host \
|
||||
-v "$VOLUME:/data" \
|
||||
-e SCHOOL_DB_PATH=/data/school.db \
|
||||
-e SCHOOL_TIMEZONE="$SCHOOL_TIMEZONE" \
|
||||
-e SCHOOL_HA_URL -e SCHOOL_HA_TOKEN -e SCHOOL_HA_CALENDAR_ENTITY \
|
||||
-e SCHOOL_HA_INSECURE -e SCHOOL_HA_TIMEOUT \
|
||||
"$IMAGE" calendar "$@"
|
||||
@@ -9,7 +9,7 @@
|
||||
# scripts/parse-compass.sh [--child Harry] [--run latest] [--classify auto]
|
||||
#
|
||||
# Environment overrides:
|
||||
# IMAGE image tag (default 0.2.0-phase3)
|
||||
# IMAGE image tag (default 0.4.0-phase6)
|
||||
# VOLUME SQLite volume (default compass-teams-nhs_compass-teams-nhs-data)
|
||||
# RAW_ROOT host path to var/raw (default <repo>/var/raw)
|
||||
# SCHOOL_TIMEZONE family timezone (default Australia/Melbourne)
|
||||
@@ -22,7 +22,7 @@ set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
IMAGE="${IMAGE:-compass-teams-nhs/school:0.3.0-phase4}"
|
||||
IMAGE="${IMAGE:-compass-teams-nhs/school:0.4.0-phase6}"
|
||||
VOLUME="${VOLUME:-compass-teams-nhs_compass-teams-nhs-data}"
|
||||
RAW_ROOT="${RAW_ROOT:-$REPO_ROOT/var/raw}"
|
||||
SCHOOL_TIMEZONE="${SCHOOL_TIMEZONE:-Australia/Melbourne}"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
# scripts/scan-compass.sh --child Harry --child Finn --classify none
|
||||
#
|
||||
# Environment overrides:
|
||||
# IMAGE image tag (default compass-teams-nhs/school:0.3.0-phase4)
|
||||
# IMAGE image tag (default compass-teams-nhs/school:0.4.0-phase6)
|
||||
# VOLUME SQLite volume (default compass-teams-nhs_compass-teams-nhs-data)
|
||||
# RAW_ROOT host path to var/raw (default <repo>/var/raw)
|
||||
# SCHOOL_TIMEZONE family timezone (default Australia/Melbourne)
|
||||
@@ -33,7 +33,7 @@ set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
IMAGE="${IMAGE:-compass-teams-nhs/school:0.3.0-phase4}"
|
||||
IMAGE="${IMAGE:-compass-teams-nhs/school:0.4.0-phase6}"
|
||||
VOLUME="${VOLUME:-compass-teams-nhs_compass-teams-nhs-data}"
|
||||
RAW_ROOT="${RAW_ROOT:-$REPO_ROOT/var/raw}"
|
||||
SCHOOL_TIMEZONE="${SCHOOL_TIMEZONE:-Australia/Melbourne}"
|
||||
|
||||
Reference in New Issue
Block a user