Basic Forecasting#
The primary entry point is forecastFromParameters():
import 'package:cicada/cicada.dart';
import 'package:fhir_r4/fhir_r4.dart';
// Run a CDC forecast (default)
final Parameters result = forecastFromParameters(inputParameters);
// Run a WHO forecast
final Parameters whoResult = forecastFromParameters(
inputParameters,
mode: ForecastMode.who,
);
Building Input Parameters#
The input is a FHIR R4 Parameters resource. Each parameter has a name and either a
resource or a value:
final input = Parameters(parameter: [
// Assessment date (the date to evaluate against)
ParametersParameter(name: FhirString('2024-06-15')),
// Patient demographics
ParametersParameter(
name: FhirString('patient'),
resource: Patient(
birthDate: FhirDate('2024-01-15'),
gender: PatientGender.male,
),
),
// Past immunizations
ParametersParameter(
name: FhirString('immunization'),
resource: Immunization(
status: ImmunizationStatus.completed,
vaccineCode: CodeableConcept(coding: [
Coding(
system: FhirUri('http://hl7.org/fhir/sid/cvx'),
code: FhirCode('110'), // DTaP-HepB-IPV
),
]),
occurrenceDateTime: FhirDateTime.fromString('2024-03-15'),
patient: Reference(reference: FhirString('Patient/1')),
),
),
// Clinical conditions (optional)
ParametersParameter(
name: FhirString('condition'),
resource: Condition(
code: CodeableConcept(coding: [
Coding(
system: FhirUri('http://snomed.info/sct'),
code: FhirCode('370388006'), // Patient immunocompromised
),
]),
subject: Reference(reference: FhirString('Patient/1')),
),
),
]);
Assessment Date#
The assessment date is encoded as a parameter whose name is the date string (e.g., "2024-06-15"). This is the date against which all age calculations and interval checks are performed.
Supported Resource Types#
| Resource | Parameter Name | Purpose |
|---|---|---|
Patient | patient | Demographics (birth date, sex) |
Immunization |
immunization |
Past vaccine doses (CVX code, date) |
Condition | condition | Clinical conditions |
AllergyIntolerance |
allergyIntolerance |
Allergies and contraindications |
Observation | observation | Clinical observations |
Procedure | procedure | Relevant procedures |
MedicationStatement |
medicationStatement |
Medication history |
MedicationRequest |
medicationRequest |
Active prescriptions |
MedicationAdministration |
medicationAdministration |
Administered medications |
CVX Codes#
Vaccine doses are identified by CVX (Vaccine Administered) codes. The engine maps each CVX code to one or more target diseases using the schedule supporting data.
Reading the Output#
The output Parameters contains ImmunizationEvaluation and ImmunizationRecommendation
resources:
final result = forecastFromParameters(input);
for (final param in result.parameter ?? []) {
if (param.resource is ImmunizationEvaluation) {
final eval = param.resource as ImmunizationEvaluation;
print('Dose: ${eval.date}');
print('Status: ${eval.status}');
print('Target Disease: ${eval.targetDisease}');
print('Dose Number: ${eval.doseNumberPositiveInt}');
}
if (param.resource is ImmunizationRecommendation) {
final rec = param.resource as ImmunizationRecommendation;
for (final recommendation in rec.recommendation ?? []) {
print('Vaccine Group: ${recommendation.vaccineCode}');
print('Forecast Status: ${recommendation.forecastStatus}');
for (final dateCriterion in recommendation.dateCriterion ?? []) {
print('${dateCriterion.code}: ${dateCriterion.value}');
}
}
}
}
Forecast Statuses#
| Status | Meaning |
|---|---|
| Not Complete | More doses needed; forecast dates provided |
| Complete | Series is complete; no more doses needed |
| Immune | Patient has clinical evidence of immunity |
| Contraindicated | Vaccine is contraindicated for this patient |
| Not Recommended | Vaccine is not recommended (e.g., aged out) |
| Aged Out | Patient has exceeded the maximum age for the series |
Dose Statuses#
| Status | Meaning |
|---|---|
| Valid | Dose meets all requirements |
| Not Valid | Dose fails one or more checks |
| Sub-standard | Dose partially meets requirements |
| Extraneous | Dose is not needed by the series |
Lower-Level API#
For more control, use evaluateForForecast() which returns the full model hierarchy:
final ForecastResult result = evaluateForForecast(
input,
mode: ForecastMode.cdc,
);
// Access the parsed patient
final VaxPatient patient = result.patient;
print('Birth date: ${patient.dob}');
// Access antigen-level results
for (final entry in result.agMap.entries) {
final String disease = entry.key;
final VaxAntigen antigen = entry.value;
print('$disease: ${antigen.seriesStatus}');
}
// Access vaccine group forecasts
for (final entry in result.vaccineGroupForecasts.entries) {
final String groupName = entry.key;
final VaccineGroupForecast forecast = entry.value;
print('$groupName: ${forecast.status}');
print(' Recommended: ${forecast.recommendedDate}');
print(' Earliest: ${forecast.earliestDate}');
print(' Past due: ${forecast.pastDueDate}');
}
ForecastMode#
The ForecastMode enum controls which supporting data is used:
// Check current mode
print(currentForecastMode); // ForecastMode.cdc
// Set mode explicitly
setForecastMode(ForecastMode.who);
// Or pass mode to forecast functions
forecastFromParameters(input, mode: ForecastMode.who);
The mode selects between:
-
activeAntigenMap— CDC'santigenSupportingDataMapor WHO'swhoAntigenSupportingDataMap -
activeScheduleData— CDC'sscheduleSupportingDataor WHO'swhoScheduleSupportingData -
activeMultiAntigenGroups— Derived from the active schedule's vaccine-group-to-antigen map
ImmDS Server#
Cicada includes an HTTP server for the FHIR $immds-forecast operation:
# Start the server
dart run bin/server.dart -p 8080
# CDC forecast (JSON)
curl -X POST http://localhost:8080/\$immds-forecast \
-H 'Content-Type: application/fhir+json' \
-d @input.json
# CDC forecast (XML)
curl -X POST http://localhost:8080/\$immds-forecast \
-H 'Content-Type: application/xml' \
-H 'Accept: application/xml' \
-d @input.xml
# WHO forecast (JSON)
curl -X POST http://localhost:8080/\$immds-forecast-who \
-H 'Content-Type: application/fhir+json' \
-d @input.json
Both $immds-forecast (CDC) and $immds-forecast-who (WHO) accept identical FHIR
Parameters input.
The server:
- Accepts JSON or XML FHIR Parameters
- Returns the same format as the input (or as specified by
Acceptheader) - Includes CORS headers for browser clients
- Logs requests to stdout
- Serves a metadata endpoint at
GET /metadata
FITS Testing#
The server is compatible with NIST FITS (Forecasting and Immunization Testing Standard). To test:
- Start the server:
dart run bin/server.dart -p 8080 - Expose with ngrok:
ngrok http 8080 - Register the ngrok URL in FITS as an R4 endpoint
- Run FITS test suites
FITS sends XML with Content-Type: application/xml; charset=utf8 and Accept: application/xml.
From JSON Map#
If you have the input as a raw JSON map:
final Map<String, dynamic> jsonInput = {
'resourceType': 'Parameters',
'parameter': [/* ... */],
};
final Parameters result = forecastFromMap(jsonInput);
// Or with WHO mode:
final Parameters whoResult = forecastFromMap(jsonInput, mode: ForecastMode.who);