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_rules in 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 from

Conditions

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

ScopeDescription
currentOnly current month
last_nLast N months (static number)
all_pastAll past months
from_answerUse answer value for number of months
customStatic from/to month range
custom_from_answerDynamic from/to using two questions

Value Sources

SourceDescription
fixedConstant value
lookupMap trigger question answers to target values
xpathExpression 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_question or months_scope.to_question answers change

Frontend Integration

// Client-side rules engine
import { applyCalendarRules } from "@/lib/calendar-rules-engine";

const updatedForm = applyCalendarRules(surveySchema, currentAnswers);