///|
/// Semantic version of the public scheduling model.
pub const SCHEMA_VERSION : String = "neighbor-rota.schedule/v1"
///|
/// Severity attached to a stable diagnostic code.
pub(all) enum DiagnosticLevel {
Info
Warning
Error
} derive(Debug, Eq)
///|
/// A validation or solver diagnostic suitable for machine processing.
pub(all) struct Diagnostic {
level : DiagnosticLevel
code : String
message : String
entity_id : String?
field : String?
} derive(Debug, Eq)
///|
/// Build an informational diagnostic.
pub fn info_diagnostic(code : String, message : String) -> Diagnostic {
{ level: Info, code, message, entity_id: None, field: None }
}
///|
/// Build a warning diagnostic.
pub fn warning_diagnostic(code : String, message : String) -> Diagnostic {
{ level: Warning, code, message, entity_id: None, field: None }
}
///|
/// Build an error diagnostic.
pub fn error_diagnostic(code : String, message : String) -> Diagnostic {
{ level: Error, code, message, entity_id: None, field: None }
}
///|
/// Attach a domain entity identifier to a diagnostic.
pub fn Diagnostic::for_entity(self : Diagnostic, id : String) -> Diagnostic {
{ ..self, entity_id: Some(id) }
}
///|
/// Attach an input field name to a diagnostic.
pub fn Diagnostic::at_field(self : Diagnostic, field : String) -> Diagnostic {
{ ..self, field: Some(field) }
}
///|
/// A half-open minute range `[start, end)` within one planning horizon.
pub(all) struct TimeWindow {
start_minute : Int
end_minute : Int
} derive(Debug, Eq)
///|
/// Construct a time window without silently normalizing invalid values.
pub fn time_window(start_minute : Int, end_minute : Int) -> TimeWindow {
{ start_minute, end_minute }
}
///|
/// Duration of the window in minutes. Invalid windows report zero here.
pub fn TimeWindow::duration(self : TimeWindow) -> Int {
if self.end_minute > self.start_minute {
self.end_minute - self.start_minute
} else {
0
}
}
///|
/// Whether two half-open windows overlap.
pub fn TimeWindow::overlaps(self : TimeWindow, other : TimeWindow) -> Bool {
self.start_minute < other.end_minute && other.start_minute < self.end_minute
}
///|
/// Whether this window fully contains another window.
pub fn TimeWindow::contains(self : TimeWindow, other : TimeWindow) -> Bool {
self.start_minute <= other.start_minute && self.end_minute >= other.end_minute
}
///|
/// Clamp a requested start so a service duration remains inside the window.
pub fn TimeWindow::latest_start(self : TimeWindow, duration : Int) -> Int {
self.end_minute - duration
}
///|
/// Integer coordinates used for deterministic travel estimates.
pub(all) struct Location {
id : String
x : Int
y : Int
zone : String
} derive(Debug, Eq)
///|
/// Create a location in a named service zone.
pub fn location(id : String, x : Int, y : Int, zone : String) -> Location {
{ id, x, y, zone }
}
///|
/// A capability that may be required by a service visit.
pub(all) struct Skill {
name : String
level : Int
} derive(Debug, Eq)
///|
/// Create a named skill with a positive proficiency level.
pub fn skill(name : String, level : Int) -> Skill {
{ name, level }
}
///|
/// Worker classifications are useful for reporting and policy decisions.
pub(all) enum WorkerKind {
Volunteer
CareWorker
Coordinator
} derive(Debug, Eq)
///|
/// A person who may be assigned one or more visits.
pub(all) struct Worker {
id : String
display_name : String
kind : WorkerKind
skills : Array[Skill]
availability : Array[TimeWindow]
home : Location
max_minutes : Int
max_visits : Int
min_break_minutes : Int
preferred_zones : Array[String]
unavailable : Bool
} derive(Debug, Eq)
///|
/// Construct a worker with conservative defaults.
pub fn worker(
id : String,
display_name : String,
kind : WorkerKind,
home : Location,
) -> Worker {
{
id,
display_name,
kind,
skills: [],
availability: [],
home,
max_minutes: 480,
max_visits: 8,
min_break_minutes: 10,
preferred_zones: [],
unavailable: false,
}
}
///|
/// Return the worker's level for a named skill, or zero when absent.
pub fn Worker::skill_level(self : Worker, name : String) -> Int {
for item in self.skills {
if item.name == name {
return item.level
}
}
0
}
///|
/// Whether the worker has a required skill at the requested level.
pub fn Worker::has_skill(self : Worker, required : Skill) -> Bool {
self.skill_level(required.name) >= required.level
}
///|
/// Whether the worker is available for an entire interval.
pub fn Worker::is_available(self : Worker, interval : TimeWindow) -> Bool {
if self.unavailable {
return false
}
for window in self.availability {
if window.contains(interval) {
return true
}
}
false
}
///|
/// Service urgency. Higher values receive stronger scheduling preference.
pub(all) enum Priority {
Low
Normal
High
Critical
} derive(Debug, Eq)
///|
/// A visit requested by a community service recipient.
pub(all) struct Visit {
id : String
recipient_id : String
title : String
location : Location
window : TimeWindow
duration_minutes : Int
required_skills : Array[Skill]
priority : Priority
preferred_worker_ids : Array[String]
forbidden_worker_ids : Array[String]
continuity_group : String?
required : Bool
} derive(Debug, Eq)
///|
/// Construct a visit with no skill or continuity requirements.
pub fn visit(
id : String,
recipient_id : String,
title : String,
location : Location,
window : TimeWindow,
duration_minutes : Int,
) -> Visit {
{
id,
recipient_id,
title,
location,
window,
duration_minutes,
required_skills: [],
priority: Normal,
preferred_worker_ids: [],
forbidden_worker_ids: [],
continuity_group: None,
required: true,
}
}
///|
/// Whether a worker identifier is explicitly forbidden for this visit.
pub fn Visit::forbids(self : Visit, worker_id : String) -> Bool {
string_array_contains(self.forbidden_worker_ids, worker_id)
}
///|
/// Whether a worker identifier is explicitly preferred for this visit.
pub fn Visit::prefers(self : Visit, worker_id : String) -> Bool {
string_array_contains(self.preferred_worker_ids, worker_id)
}
///|
/// One fixed assignment in a schedule.
pub(all) struct Assignment {
visit_id : String
worker_id : String
start_minute : Int
end_minute : Int
travel_before_minutes : Int
score : Int
reasons : Array[String]
} derive(Debug, Eq)
///|
/// A visit that could not be placed, with stable reason codes.
pub(all) struct UnassignedVisit {
visit_id : String
reason_codes : Array[String]
details : Array[String]
} derive(Debug, Eq)
///|
/// Aggregate dimensions used to compare schedules.
pub(all) struct ScheduleScore {
total : Int
assigned_priority : Int
travel_penalty : Int
fairness_penalty : Int
continuity_bonus : Int
preference_bonus : Int
disruption_penalty : Int
} derive(Debug, Eq)
///|
/// Complete auditable scheduling result.
pub(all) struct Schedule {
schema : String
assignments : Array[Assignment]
unassigned : Array[UnassignedVisit]
diagnostics : Array[Diagnostic]
score : ScheduleScore
feasible : Bool
} derive(Debug, Eq)
///|
/// Policy weights and operational limits.
pub(all) struct SchedulePolicy {
travel_minutes_per_unit : Int
cross_zone_penalty_minutes : Int
priority_weight : Int
preference_weight : Int
continuity_weight : Int
travel_weight : Int
fairness_weight : Int
disruption_weight : Int
allow_optional_unassigned : Bool
local_search_rounds : Int
} derive(Debug, Eq)
///|
/// Balanced policy for the built-in demonstrations.
pub fn default_policy() -> SchedulePolicy {
{
travel_minutes_per_unit: 5,
cross_zone_penalty_minutes: 15,
priority_weight: 100,
preference_weight: 30,
continuity_weight: 40,
travel_weight: 2,
fairness_weight: 1,
disruption_weight: 50,
allow_optional_unassigned: true,
local_search_rounds: 8,
}
}
///|
/// All inputs required for a deterministic scheduling run.
pub(all) struct ScheduleRequest {
workers : Array[Worker]
visits : Array[Visit]
policy : SchedulePolicy
baseline : Schedule?
} derive(Debug, Eq)
///|
/// Create a request without a previous schedule.
pub fn schedule_request(
workers : Array[Worker],
visits : Array[Visit],
) -> ScheduleRequest {
{ workers, visits, policy: default_policy(), baseline: None }
}
///|
/// Stable human-readable name for diagnostic levels.
pub fn diagnostic_level_name(level : DiagnosticLevel) -> String {
match level {
Info => "info"
Warning => "warning"
Error => "error"
}
}
///|
/// Stable human-readable name for worker kinds.
pub fn worker_kind_name(kind : WorkerKind) -> String {
match kind {
Volunteer => "volunteer"
CareWorker => "care_worker"
Coordinator => "coordinator"
}
}
///|
/// Stable human-readable name for priority values.
pub fn priority_name(priority : Priority) -> String {
match priority {
Low => "low"
Normal => "normal"
High => "high"
Critical => "critical"
}
}
///|
/// Numeric priority used by deterministic ordering and scoring.
pub fn priority_value(priority : Priority) -> Int {
match priority {
Low => 1
Normal => 2
High => 4
Critical => 8
}
}