|
|
@@ -1,6 +1,6 @@
|
|
|
use serde::Serialize;
|
|
|
|
|
|
-use crate::api::models::{CfcTask, DailyCheckin, EnergyOverview};
|
|
|
+use crate::api::models::{CfcTask, EnergyOverview};
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
@@ -33,7 +33,7 @@ pub struct PetStatePayload {
|
|
|
pub fn map_state(
|
|
|
tasks: Option<&[CfcTask]>,
|
|
|
energy: Option<&EnergyOverview>,
|
|
|
- checkin: Option<&DailyCheckin>,
|
|
|
+ water_done_today: bool,
|
|
|
user_inactive: bool,
|
|
|
) -> PetState {
|
|
|
let has_pending = tasks
|
|
|
@@ -46,10 +46,8 @@ pub fn map_state(
|
|
|
return PetState::Sleeping;
|
|
|
}
|
|
|
|
|
|
- if let Some(c) = checkin {
|
|
|
- if c.checked {
|
|
|
- return PetState::Excited;
|
|
|
- }
|
|
|
+ if water_done_today {
|
|
|
+ return PetState::Excited;
|
|
|
}
|
|
|
|
|
|
if let Some(ts) = tasks {
|
|
|
@@ -60,12 +58,9 @@ pub fn map_state(
|
|
|
let today = chrono::Local::now().date_naive();
|
|
|
let overdue = ts.iter().any(|t| {
|
|
|
matches!(t.status.as_deref(), Some("PENDING") | Some("IN_PROGRESS"))
|
|
|
- && t.due_date
|
|
|
- .as_deref()
|
|
|
- .and_then(|d| {
|
|
|
- let head = &d[..d.len().min(10)];
|
|
|
- chrono::NaiveDate::parse_from_str(head, "%Y-%m-%d").ok()
|
|
|
- })
|
|
|
+ && t.deadline
|
|
|
+ .as_ref()
|
|
|
+ .and_then(|d| d.to_local_date())
|
|
|
.map(|due| due < today)
|
|
|
.unwrap_or(false)
|
|
|
});
|
|
|
@@ -78,22 +73,22 @@ pub fn map_state(
|
|
|
|
|
|
if let Some(e) = energy {
|
|
|
if !e.dimensions.is_empty() {
|
|
|
- let all_low = e.dimensions.iter().all(|d| d.value < 30);
|
|
|
+ let all_low = e.dimensions.iter().all(|d| d.energy < 30);
|
|
|
if all_low {
|
|
|
return PetState::Sad;
|
|
|
}
|
|
|
- let body = e.dimensions.iter().find(|d| d.dimension.as_deref() == Some("身"));
|
|
|
+ let body = e.dimensions.iter().find(|d| d.code.as_deref() == Some("body"));
|
|
|
if let Some(b) = body {
|
|
|
- if b.value < 30 {
|
|
|
+ if b.energy < 30 {
|
|
|
return PetState::Tired;
|
|
|
}
|
|
|
- if b.value >= 85 {
|
|
|
+ if b.energy >= 85 {
|
|
|
return PetState::Energetic;
|
|
|
}
|
|
|
}
|
|
|
- let mind = e.dimensions.iter().find(|d| d.dimension.as_deref() == Some("智"));
|
|
|
+ let mind = e.dimensions.iter().find(|d| d.code.as_deref() == Some("wisdom"));
|
|
|
if let Some(m) = mind {
|
|
|
- if m.value >= 85 {
|
|
|
+ if m.energy >= 85 {
|
|
|
return PetState::Curious;
|
|
|
}
|
|
|
}
|
|
|
@@ -106,14 +101,14 @@ pub fn map_state(
|
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
|
use super::*;
|
|
|
- use crate::api::models::{CfcTask, DailyCheckin, DimensionValue, EnergyOverview};
|
|
|
+ use crate::api::models::{DimensionValue, FlexDate};
|
|
|
|
|
|
fn task(status: &str, due: Option<&str>) -> CfcTask {
|
|
|
CfcTask {
|
|
|
id: 1,
|
|
|
title: None,
|
|
|
status: Some(status.to_string()),
|
|
|
- due_date: due.map(|s| s.to_string()),
|
|
|
+ deadline: due.map(|s| FlexDate::Iso(s.to_string())),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -121,78 +116,82 @@ mod tests {
|
|
|
EnergyOverview {
|
|
|
dimensions: dims
|
|
|
.iter()
|
|
|
- .map(|(d, v)| DimensionValue {
|
|
|
- dimension: Some(d.to_string()),
|
|
|
- value: *v,
|
|
|
- color: None,
|
|
|
+ .map(|(code, v)| DimensionValue {
|
|
|
+ code: Some(code.to_string()),
|
|
|
+ name: None,
|
|
|
+ icon: None,
|
|
|
+ element: None,
|
|
|
+ energy: *v,
|
|
|
+ health_index: 0,
|
|
|
})
|
|
|
.collect(),
|
|
|
+ total_energy: 0,
|
|
|
+ total_health_index: 0,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn sleeping_when_inactive_and_no_pending() {
|
|
|
- assert_eq!(map_state(Some(&[]), None, None, true), PetState::Sleeping);
|
|
|
+ assert_eq!(map_state(Some(&[]), None, false, true), PetState::Sleeping);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn not_sleeping_when_inactive_but_pending() {
|
|
|
let tasks = vec![task("PENDING", None)];
|
|
|
- assert_eq!(map_state(Some(&tasks), None, None, true), PetState::Busy);
|
|
|
+ assert_eq!(map_state(Some(&tasks), None, false, true), PetState::Busy);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
- fn excited_beats_tasks_when_checked() {
|
|
|
+ fn excited_when_water_done_today() {
|
|
|
let tasks = vec![task("PENDING", None)];
|
|
|
- let checkin = DailyCheckin { checked: true, items: vec!["water".into()] };
|
|
|
- assert_eq!(map_state(Some(&tasks), None, Some(&checkin), false), PetState::Excited);
|
|
|
+ assert_eq!(map_state(Some(&tasks), None, true, false), PetState::Excited);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn happy_when_all_completed() {
|
|
|
let tasks = vec![task("COMPLETED", None), task("COMPLETED", Some("2026-01-01"))];
|
|
|
- assert_eq!(map_state(Some(&tasks), None, None, false), PetState::Happy);
|
|
|
+ assert_eq!(map_state(Some(&tasks), None, false, false), PetState::Happy);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn worried_when_overdue() {
|
|
|
let tasks = vec![task("PENDING", Some("2000-01-01"))];
|
|
|
- assert_eq!(map_state(Some(&tasks), None, None, false), PetState::Worried);
|
|
|
+ assert_eq!(map_state(Some(&tasks), None, false, false), PetState::Worried);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn busy_when_in_progress_not_overdue() {
|
|
|
let tasks = vec![task("IN_PROGRESS", Some("2099-12-31"))];
|
|
|
- assert_eq!(map_state(Some(&tasks), None, None, false), PetState::Busy);
|
|
|
+ assert_eq!(map_state(Some(&tasks), None, false, false), PetState::Busy);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn sad_when_all_dimensions_low() {
|
|
|
- let e = energy(&[("身", 10), ("智", 20), ("心", 15)]);
|
|
|
- assert_eq!(map_state(None, Some(&e), None, false), PetState::Sad);
|
|
|
+ let e = energy(&[("body", 10), ("wisdom", 20), ("mind", 15)]);
|
|
|
+ assert_eq!(map_state(None, Some(&e), false, false), PetState::Sad);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn tired_when_body_low() {
|
|
|
- let e = energy(&[("身", 20), ("智", 90)]);
|
|
|
- assert_eq!(map_state(None, Some(&e), None, false), PetState::Tired);
|
|
|
+ let e = energy(&[("body", 20), ("wisdom", 90)]);
|
|
|
+ assert_eq!(map_state(None, Some(&e), false, false), PetState::Tired);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn energetic_when_body_high() {
|
|
|
- let e = energy(&[("身", 90)]);
|
|
|
- assert_eq!(map_state(None, Some(&e), None, false), PetState::Energetic);
|
|
|
+ let e = energy(&[("body", 90)]);
|
|
|
+ assert_eq!(map_state(None, Some(&e), false, false), PetState::Energetic);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn curious_when_mind_high() {
|
|
|
- let e = energy(&[("身", 60), ("智", 88)]);
|
|
|
- assert_eq!(map_state(None, Some(&e), None, false), PetState::Curious);
|
|
|
+ let e = energy(&[("body", 60), ("wisdom", 88)]);
|
|
|
+ assert_eq!(map_state(None, Some(&e), false, false), PetState::Curious);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn idle_when_nothing_qualifies() {
|
|
|
- assert_eq!(map_state(None, None, None, false), PetState::Idle);
|
|
|
+ assert_eq!(map_state(None, None, false, false), PetState::Idle);
|
|
|
}
|
|
|
|
|
|
#[test]
|