Calendar Rules Engine
Calendar-based rules engine for date-based survey activation/deactivation, scheduling logic, and recurring rules
Overview
The calendar rules engine enables dynamic, date-based survey logic. It auto-prefills calendar grid columns based on answers to previous questions, supporting complex scheduling and conditional display.
Architecture
The rules engine spans two services:
- survey-service: Stores
calendar_rulesin the survey JSONB schema - form-service: Evaluates rules at render time via the calendar rules engine
The frontend also has a client-side implementation at frontend/app/src/lib/calendar-rules-engine.ts.
Rule Structure
class CalendarRule(BaseModel):
target_column: str # Column to auto-fill
conditions: List[RuleCondition] # When to apply
logic: Literal["AND", "OR"] = "AND" # Condition combination
months_scope: MonthScope # Which months to affect
value_source: ValueSource # Where values come fromConditions
Rules apply only when all (AND) or any (OR) conditions are met:
conditions = [
{"question_id": "pregnancy", "operator": "eq", "value": "yes"},
{"question_id": "age", "operator": "gte", "value": 18},
]Month Scopes
| Scope | Description |
|---|---|
current | Only current month |
last_n | Last N months (static number) |
all_past | All past months |
from_answer | Use answer value for number of months |
custom | Static from/to month range |
custom_from_answer | Dynamic from/to using two questions |
Value Sources
| Source | Description |
|---|---|
fixed | Constant value |
lookup | Map trigger question answers to target values |
xpath | Expression evaluation |
Example
{
"calendar_rules": [
{
"target_column": "monthly_visit",
"conditions": [
{"question_id": "needs_followup", "operator": "eq", "value": "yes"}
],
"logic": "AND",
"months_scope": {"type": "last_n", "value": 6},
"value_source": {"type": "fixed", "value": "Scheduled"}
}
]
}Engine Behavior
- Auto-fill: Target column cells are pre-filled when conditions are met
- Toggle: Cells are cleared when conditions are no longer met
- Trigger on dependency: Recalculates when
months_scope.from_questionormonths_scope.to_questionanswers change
Frontend Integration
// Client-side rules engine
import { applyCalendarRules } from "@/lib/calendar-rules-engine";
const updatedForm = applyCalendarRules(surveySchema, currentAnswers);