New Catalog.LookupEvents(ctx, axes, depth) method exposes a unified
graph query over paliad.deadline_rules + paliad.proceeding_types + the
deadline_concept_event_types junction. Used by the Determinator
cascade, the scenarios surface (Slice D), and any future "show me
events matching X" query — centralises a fan-out that today is
duplicated across multiple client-side paths.
Package additions (pkg/litigationplanner):
- EventLookupAxes: optional Jurisdiction / *ProceedingTypeID / Party
/ *EventCategoryID / AppealTarget. All fields optional; the empty
value (or nil pointer) is "no filter on this axis". Multiple
non-zero axes apply as AND.
- EventLookupDepth: "next" (1 hop downstream) or "all-following"
(full chain).
- EventMatch: Rule + ProceedingType + Priority + DepthFromAnchor +
*ParentRuleID (populated only when the parent itself is in the
returned set, so the frontend can render a tree).
- Catalog interface gains LookupEvents.
paliad-side implementation (internal/services/fristenrechner.go):
- SQL pass with progressively-built WHERE clauses (one $N
placeholder per non-zero axis). EventCategoryID uses an EXISTS
subquery against paliad.event_category_concepts joined via
concept_id.
- Post-fetch parent_id graph walk in Go for depth control. Loads
the per-proceeding rule corpus via DeadlineRuleService.List so
children whose parent_id is in the anchor set can be added even
when those children don't match the axes themselves. AllFollowing
iterates to fixpoint; Next stops after one pass.
- DepthFromAnchor computed by walking each result row up the
parent_id chain until it hits an anchor (iteration-bounded to
prevent infinite loops on hypothetical cycles).
- Unknown axis values (jurisdiction="XX", party="foo",
appealTarget="invalid") silently fall through as "no filter on
this axis" — a stale frontend chip should not drop the entire
result set.
- "published + active" gate (lifecycle_state='published' AND
is_active=true) matches LoadProceeding's WHERE clause.
- Results ordered by (proceeding_type_id, sequence_order) so the
frontend can render without re-sorting.
Tests (internal/services/lookup_events_test.go):
- Live-DB driven (skipped without TEST_DATABASE_URL, matches the
existing TestCalculateRule pattern).
- Cases: UPC-jurisdiction returns the UPC corpus only;
party=defendant scopes anchor matches to defendant rules;
unknown jurisdiction falls through; appeal_target=endentscheidung
returns the merits rules from B1 mig 134;
appeal_target=schadensbemessung returns empty (no rules seeded).
No schema delta. No frontend wiring (the new HTTP endpoint at
GET /api/tools/lookup-events can land in a follow-up slice — the
package + paliad-side impl are the deliverable here).
63 lines
3.1 KiB
Go
63 lines
3.1 KiB
Go
package litigationplanner
|
|
|
|
import "context"
|
|
|
|
// Catalog supplies proceeding-type metadata + rules for the calculator.
|
|
//
|
|
// Implementations:
|
|
// - paliad: reads paliad.deadline_rules + paliad.proceeding_types,
|
|
// filtered to lifecycle_state='published' AND is_active=true.
|
|
// ProjectHint scopes future per-project rule merges.
|
|
// - embedded/upc (Slice C): in-memory map keyed by code, populated
|
|
// once at init from the embedded JSON snapshot.
|
|
//
|
|
// All methods return ErrUnknownProceedingType / ErrUnknownRule when the
|
|
// caller asks for a code/id that doesn't exist in the catalog.
|
|
type Catalog interface {
|
|
// LoadProceeding returns the proceeding-type metadata + the full
|
|
// rule list (sorted by sequence_order). Caller passes the user-
|
|
// facing proceeding code (e.g. "upc.inf.cfi"). The hint scopes a
|
|
// future per-project rule merge — implementations that don't
|
|
// support projects ignore it.
|
|
LoadProceeding(ctx context.Context, code string, hint ProjectHint) (*ProceedingType, []Rule, error)
|
|
|
|
// LoadProceedingByID is the resolver used by CalculateRule when it
|
|
// has a rule + needs the rule's parent proceeding metadata.
|
|
LoadProceedingByID(ctx context.Context, id int) (*ProceedingType, error)
|
|
|
|
// LoadRuleByID resolves a rule UUID to the rule row. Used by
|
|
// CalculateRule when the caller supplies CalcRuleParams.RuleID.
|
|
LoadRuleByID(ctx context.Context, ruleID string) (*Rule, error)
|
|
|
|
// LoadRuleByCode resolves a rule by (proceedingCode, submissionCode)
|
|
// + returns the parent proceeding for use in the response identity.
|
|
// Used by CalculateRule when the caller supplies the (code, local)
|
|
// pair from a concept-card pill.
|
|
LoadRuleByCode(ctx context.Context, proceedingCode, submissionCode string) (*Rule, *ProceedingType, error)
|
|
|
|
// LoadRulesByTriggerEvent lists Pipeline-C trigger-event-rooted
|
|
// rules (rules whose trigger_event_id matches). Used by
|
|
// EventDeadlineService → Calculate via CalcOptions.TriggerEventIDFilter.
|
|
LoadRulesByTriggerEvent(ctx context.Context, triggerEventID int64) ([]Rule, error)
|
|
|
|
// LoadTriggerEventsByIDs bulk-loads paliad.trigger_events rows
|
|
// for the conditional-label override (t-paliad-294 /
|
|
// m/paliad#126). Returns a map keyed by event id; missing ids
|
|
// are simply absent (caller treats absence as "no override").
|
|
// Empty input returns an empty map without a DB roundtrip.
|
|
LoadTriggerEventsByIDs(ctx context.Context, ids []int64) (map[int64]TriggerEvent, error)
|
|
|
|
// LookupEvents returns deadline rules matching any subset of the
|
|
// requested axes, at the requested sequence depth (Slice B2,
|
|
// m/paliad#124 §18.2). Used by the Determinator cascade, the
|
|
// scenarios surface (Slice D), and any future "show me events
|
|
// matching X" query. Empty result is NOT an error.
|
|
//
|
|
// Implementations must respect the catalog's "published + active"
|
|
// rule gate (rules with lifecycle_state='draft' or is_active=false
|
|
// must NEVER appear in the result). Sort order is
|
|
// (proceeding_type_id, sequence_order) so the frontend can render
|
|
// without re-sorting.
|
|
LookupEvents(ctx context.Context, axes EventLookupAxes, depth EventLookupDepth) ([]EventMatch, error)
|
|
}
|