Spaces:
Running
Running
File size: 1,965 Bytes
414dc55 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | """Spatial and temporal primitives: locations, time windows, and whereabouts.
Times are integer minutes since midnight to keep alibi reasoning exact and cheap.
"""
from __future__ import annotations
from pydantic import BaseModel, ConfigDict, Field, model_validator
from ..constants import DAY_MINUTES
class Location(BaseModel):
model_config = ConfigDict(frozen=True)
loc_id: str
name: str
description: str = ""
adjacent_to: tuple[str, ...] = ()
class TimeWindow(BaseModel):
"""A closed interval [start_min, end_min] within a single day."""
model_config = ConfigDict(frozen=True)
start_min: int = Field(ge=0, le=DAY_MINUTES)
end_min: int = Field(ge=0, le=DAY_MINUTES)
@model_validator(mode="after")
def _ordered(self) -> TimeWindow:
if self.end_min < self.start_min:
raise ValueError(f"end_min {self.end_min} precedes start_min {self.start_min}")
return self
def contains(self, minute: int) -> bool:
return self.start_min <= minute <= self.end_min
def covers(self, other: TimeWindow) -> bool:
return self.start_min <= other.start_min and other.end_min <= self.end_min
def overlaps(self, other: TimeWindow) -> bool:
return self.start_min <= other.end_min and other.start_min <= self.end_min
class WhereaboutsSegment(BaseModel):
"""Where a person actually was during one slice of the murder window."""
model_config = ConfigDict(frozen=True)
window: TimeWindow
loc_id: str
activity: str = ""
co_present_sus_ids: tuple[str, ...] = ()
class AlibiSegment(BaseModel):
"""Where a suspect *claims* to have been, with any claimed witnesses."""
model_config = ConfigDict(frozen=True)
window: TimeWindow
loc_id: str
witness_sus_ids: tuple[str, ...] = ()
class StatedAlibi(BaseModel):
model_config = ConfigDict(frozen=True)
claim_text: str
claimed_segments: tuple[AlibiSegment, ...] = ()
|