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:
//
// notification_log — one row per (event, lead_days, audience, channel)
// that the cron scanner has already attempted. The UNIQUE constraint
// makes the cron idempotent — running it twice on the same day does
// not re-send.
// notification_log — one row per (event, lead_days, audience, channel)
// that the cron scanner has already attempted. The UNIQUE constraint
// makes the cron idempotent — running it twice on the same day does
// not re-send.
//
// channel ∈ {'matrix', 'email'} — set by the dispatcher.
// audience ∈ {'parents', 'students'}.
@@ -14,10 +14,10 @@ import (
// lead_days, audience) pairs are due today, dispatches via the configured
// upstream URLs, and writes a notification_log row for idempotency.
type Service struct {
db *pgxpool.Pool
matrixURL string // empty → status=skipped
emailURL string // empty → status=skipped
httpTimeout time.Duration
db *pgxpool.Pool
matrixURL string // empty → status=skipped
emailURL string // empty → status=skipped
httpTimeout time.Duration
}
// 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
// notification_log.
type RunResult struct {
Date string `json:"date"`
Sent int `json:"sent"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
AlreadyLogged int `json:"already_logged"`
Date string `json:"date"`
Sent int `json:"sent"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
AlreadyLogged int `json:"already_logged"`
}
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
// matched lead_day for this run.
type dueEvent struct {
ID string
Title string
EventType string
StartDate time.Time
ClassName string // optional, may be empty for "alle Klassen"
OwnerUserID string
NotifyParents bool
NotifyStudents bool
LeadDays int
ID string
Title string
EventType string
StartDate time.Time
ClassName string // optional, may be empty for "alle Klassen"
OwnerUserID string
NotifyParents bool
NotifyStudents bool
LeadDays int
}
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() {
var (
id, title, eventType, ownerUserID, classNames string
notifyParents, notifyStudents bool
startDate time.Time
leadDays []int32
notifyParents, notifyStudents bool
startDate time.Time
leadDays []int32
)
if err := rows.Scan(&id, &title, &eventType, &startDate, &ownerUserID,
&notifyParents, &notifyStudents, &leadDays, &classNames); err != nil {
@@ -19,9 +19,10 @@ type Vars struct {
// bucket and language, then substitutes the {{var}} placeholders.
//
// lead is grouped into three buckets:
// 0 → "today"
// 1 → "tomorrow"
// >=2 → "in X days" with X = lead value
//
// 0 → "today"
// 1 → "tomorrow"
// >=2 → "in X days" with X = lead value
//
// Falls back through (lang → de) and (event_type → "andere") so we never
// fail to render even with custom rule combos.