from datetime import UTC, datetime
from enum import IntEnum
from uuid import UUID
from pydantic import ConfigDict, Field, field_validator
from pydantic.alias_generators import to_pascal
from pydantic_core.core_schema import ValidationInfo
__all__ = [
"Patient",
"Trend",
"GlucoseMeasurement",
"GlucoseMeasurementWithTrend",
"F",
"L",
"H",
"Nd",
"Std",
]
from pylibrelinkup.models.base import ConfigBaseModel
[docs]
class Patient(ConfigBaseModel):
"""Patient class to store patient data."""
id: UUID
patient_id: UUID
first_name: str
last_name: str
def __repr__(self):
return f"{self.first_name} {self.last_name}: {self.patient_id}"
def __str__(self):
return f"{self.first_name} {self.last_name}: {self.patient_id}"
[docs]
class Trend(IntEnum):
DOWN_FAST = 1
DOWN_SLOW = 2
STABLE = 3
UP_SLOW = 4
UP_FAST = 5
@property
def indicator(self) -> str:
arrow_map: dict[Trend, str] = {
Trend.DOWN_FAST: "↓",
Trend.DOWN_SLOW: "↘",
Trend.STABLE: "→",
Trend.UP_SLOW: "↗",
Trend.UP_FAST: "↑",
}
return arrow_map[self]
[docs]
class GlucoseMeasurement(ConfigBaseModel):
"""GlucoseMeasurement class to store glucose measurement data."""
# Glucose measurements _mostly_ use PascalCase, instead of camelCase because
# ¯\_(ツ)_/¯
model_config = ConfigDict(
alias_generator=to_pascal,
)
factory_timestamp: datetime
timestamp: datetime
type: int = Field(default=0)
value_in_mg_per_dl: float = Field(default=0.0)
measurement_color: int = Field(default=0)
glucose_units: int = Field(default=0)
value: float = Field(default=0.0)
is_high: bool = Field(alias="isHigh")
is_low: bool = Field(alias="isLow")
def __str__(self):
return f"{self.timestamp}: {self.value}"
[docs]
@field_validator("timestamp", "factory_timestamp", mode="before")
@classmethod
def parse_timestamp(cls, v: str, info: ValidationInfo):
if isinstance(v, str):
datetime_value = datetime.strptime(v, "%m/%d/%Y %I:%M:%S %p")
elif isinstance(v, datetime):
datetime_value = v
else:
raise ValueError(
f"Invalid type for {info.field_name}: {type(v)}. Expected str or datetime."
)
if info.field_name == "factory_timestamp":
# factory_timestamp is in UTC
datetime_value = datetime_value.replace(tzinfo=UTC)
return datetime_value
[docs]
class F(ConfigBaseModel):
"""F class to store F data."""
th: int = Field(default=0)
thmm: float = Field(default=0.0)
d: int = Field(default=0)
tl: int = Field(default=0)
tlmm: float = Field(default=0.0)
[docs]
class L(ConfigBaseModel):
"""L class to store L data."""
th: int = Field(default=0)
thmm: float = Field(default=0.0)
d: int = Field(default=0)
tl: int = Field(default=0)
tlmm: float = Field(default=0.0)
[docs]
class H(ConfigBaseModel):
"""H class to store H data."""
th: int = Field(default=0)
thmm: float = Field(default=0.0)
d: int = Field(default=0)
f: float = Field(default=0.0)
[docs]
class Nd(ConfigBaseModel):
"""Nd class to store Nd data."""
i: int = Field(default=0)
r: int = Field(default=0)
l: int = Field(default=0)
[docs]
class Std(ConfigBaseModel):
"""Std class to store Std data."""
sd: bool | None = Field(default=None)
[docs]
class GlucoseMeasurementWithTrend(GlucoseMeasurement):
trend: Trend = Field(default=Trend.STABLE, alias="TrendArrow")