gofmt notification files
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 49s
CI / test-go-edu-search (push) Successful in 45s
CI / test-python-klausur (push) Failing after 3m41s
CI / test-python-agent-core (push) Successful in 38s
CI / test-nodejs-website (push) Successful in 49s

This commit is contained in:
Benjamin Admin
2026-05-22 18:14:15 +02:00
parent 8311b33fb3
commit 89011d64f7
3 changed files with 29 additions and 28 deletions
@@ -2,10 +2,10 @@ package database
// NotificationMigrations creates the one table Phase 9d needs: // NotificationMigrations creates the one table Phase 9d needs:
// //
// notification_log — one row per (event, lead_days, audience, channel) // notification_log — one row per (event, lead_days, audience, channel)
// that the cron scanner has already attempted. The UNIQUE constraint // that the cron scanner has already attempted. The UNIQUE constraint
// makes the cron idempotent — running it twice on the same day does // makes the cron idempotent — running it twice on the same day does
// not re-send. // not re-send.
// //
// channel ∈ {'matrix', 'email'} — set by the dispatcher. // channel ∈ {'matrix', 'email'} — set by the dispatcher.
// audience ∈ {'parents', 'students'}. // audience ∈ {'parents', 'students'}.
@@ -14,10 +14,10 @@ import (
// lead_days, audience) pairs are due today, dispatches via the configured // lead_days, audience) pairs are due today, dispatches via the configured
// upstream URLs, and writes a notification_log row for idempotency. // upstream URLs, and writes a notification_log row for idempotency.
type Service struct { type Service struct {
db *pgxpool.Pool db *pgxpool.Pool
matrixURL string // empty → status=skipped matrixURL string // empty → status=skipped
emailURL string // empty → status=skipped emailURL string // empty → status=skipped
httpTimeout time.Duration httpTimeout time.Duration
} }
// Dispatch is the contract our upstream services (Matrix bridge + Email // Dispatch is the contract our upstream services (Matrix bridge + Email
@@ -85,11 +85,11 @@ func (s *Service) ListLog(ctx context.Context, eventID, ownerUserID string) ([]L
// renders + dispatches + logs. Idempotent via the UNIQUE constraint on // renders + dispatches + logs. Idempotent via the UNIQUE constraint on
// notification_log. // notification_log.
type RunResult struct { type RunResult struct {
Date string `json:"date"` Date string `json:"date"`
Sent int `json:"sent"` Sent int `json:"sent"`
Failed int `json:"failed"` Failed int `json:"failed"`
Skipped int `json:"skipped"` Skipped int `json:"skipped"`
AlreadyLogged int `json:"already_logged"` AlreadyLogged int `json:"already_logged"`
} }
func (s *Service) RunForDate(ctx context.Context, runDate time.Time) (*RunResult, error) { func (s *Service) RunForDate(ctx context.Context, runDate time.Time) (*RunResult, error) {
@@ -141,15 +141,15 @@ func (s *Service) RunForDate(ctx context.Context, runDate time.Time) (*RunResult
// dueEvent holds the small slice of cal_school_event row we need plus the // dueEvent holds the small slice of cal_school_event row we need plus the
// matched lead_day for this run. // matched lead_day for this run.
type dueEvent struct { type dueEvent struct {
ID string ID string
Title string Title string
EventType string EventType string
StartDate time.Time StartDate time.Time
ClassName string // optional, may be empty for "alle Klassen" ClassName string // optional, may be empty for "alle Klassen"
OwnerUserID string OwnerUserID string
NotifyParents bool NotifyParents bool
NotifyStudents bool NotifyStudents bool
LeadDays int LeadDays int
} }
func (s *Service) dueEvents(ctx context.Context, runDate time.Time) ([]dueEvent, error) { func (s *Service) dueEvents(ctx context.Context, runDate time.Time) ([]dueEvent, error) {
@@ -177,9 +177,9 @@ func (s *Service) dueEvents(ctx context.Context, runDate time.Time) ([]dueEvent,
for rows.Next() { for rows.Next() {
var ( var (
id, title, eventType, ownerUserID, classNames string id, title, eventType, ownerUserID, classNames string
notifyParents, notifyStudents bool notifyParents, notifyStudents bool
startDate time.Time startDate time.Time
leadDays []int32 leadDays []int32
) )
if err := rows.Scan(&id, &title, &eventType, &startDate, &ownerUserID, if err := rows.Scan(&id, &title, &eventType, &startDate, &ownerUserID,
&notifyParents, &notifyStudents, &leadDays, &classNames); err != nil { &notifyParents, &notifyStudents, &leadDays, &classNames); err != nil {
@@ -19,9 +19,10 @@ type Vars struct {
// bucket and language, then substitutes the {{var}} placeholders. // bucket and language, then substitutes the {{var}} placeholders.
// //
// lead is grouped into three buckets: // lead is grouped into three buckets:
// 0 → "today" //
// 1 → "tomorrow" // 0 → "today"
// >=2 → "in X days" with X = lead value // 1 → "tomorrow"
// >=2 → "in X days" with X = lead value
// //
// Falls back through (lang → de) and (event_type → "andere") so we never // Falls back through (lang → de) and (event_type → "andere") so we never
// fail to render even with custom rule combos. // fail to render even with custom rule combos.