freedom_models/
task.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use strum::{AsRefStr, EnumString};
5use time::OffsetDateTime;
6use url::Url;
7
8use crate::Hateoas;
9
10#[cfg(feature = "serde")]
11use super::utils;
12
13#[cfg_attr(
14    feature = "serde",
15    derive(Serialize, Deserialize),
16    serde(rename_all = "SCREAMING_SNAKE_CASE")
17)]
18#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, AsRefStr, EnumString)]
19#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
20#[non_exhaustive]
21pub enum TaskStatusType {
22    Received,
23    Pending,
24    Scheduled,
25    Rejected,
26    Denied,
27    MovedVis,
28    Bumped,
29    Moved,
30    Cancelled,
31    SystemError,
32    QueuedPass,
33    Configured,
34    Downlinking,
35    Recording,
36    CompletedPass,
37    DataCloud,
38    Processing,
39    ReadyCustom,
40    ProcessedCustom,
41    ErrorCustom,
42    Completed,
43    CompletedError,
44    PushedToCustomer,
45    ErrorPushToCustomer,
46    Invoiced,
47    Paid,
48}
49
50#[cfg_attr(
51    feature = "serde",
52    derive(Serialize, Deserialize),
53    serde(rename_all = "camelCase")
54)]
55#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
56pub struct TaskStatus {
57    #[cfg_attr(feature = "serde", serde(with = "crate::utils::timestamp"))]
58    pub created: OffsetDateTime,
59    pub status: TaskStatusType,
60    pub reason: String,
61}
62
63#[cfg_attr(
64    feature = "serde",
65    derive(Serialize, Deserialize),
66    serde(rename_all = "SCREAMING_SNAKE_CASE")
67)]
68#[derive(
69    Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, AsRefStr, EnumString,
70)]
71#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
72#[non_exhaustive]
73pub enum Polarization {
74    #[default]
75    Right,
76    Left,
77    Vertical,
78    Horizontal,
79}
80
81#[cfg_attr(
82    feature = "serde",
83    derive(Serialize, Deserialize),
84    serde(rename_all = "SCREAMING_SNAKE_CASE")
85)]
86#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, AsRefStr, EnumString)]
87#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
88#[non_exhaustive]
89pub enum TaskType {
90    Before,
91    After,
92    Test,
93    Around,
94    Exact,
95}
96
97#[cfg_attr(
98    feature = "serde",
99    derive(Serialize, Deserialize),
100    serde(rename_all = "camelCase")
101)]
102#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct Task {
104    #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
105    pub created: OffsetDateTime,
106    #[cfg_attr(
107        feature = "serde",
108        serde(default, with = "time::serde::iso8601::option")
109    )]
110    pub modified: Option<OffsetDateTime>,
111    pub found_visibility: bool,
112    /// Unavailable for user accounts
113    #[cfg_attr(feature = "serde", serde(default))]
114    pub internal_meta_data: Option<HashMap<String, String>>,
115    /// Unavailable for user accounts
116    #[cfg_attr(feature = "serde", serde(default))]
117    pub score: Option<u8>,
118    #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
119    pub start: OffsetDateTime,
120    #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
121    pub end: OffsetDateTime,
122    #[cfg_attr(
123        feature = "serde",
124        serde(default, with = "time::serde::iso8601::option")
125    )]
126    pub visibility_start: Option<OffsetDateTime>,
127    #[cfg_attr(
128        feature = "serde",
129        serde(default, with = "time::serde::iso8601::option")
130    )]
131    pub visibility_end: Option<OffsetDateTime>,
132    pub billable: bool,
133    pub duration_in_seconds: u32,
134    pub task_within_config_window: bool,
135    pub duration: String,
136    pub file_results: Vec<String>,
137    #[cfg_attr(feature = "serde", serde(default))]
138    pub meta_data: Option<HashMap<String, String>>,
139    #[cfg_attr(
140        feature = "serde",
141        serde(rename = "_links", with = "utils::links::serde", default)
142    )]
143    pub links: HashMap<String, Url>,
144}
145
146impl Hateoas for Task {
147    fn get_links(&self) -> &HashMap<String, url::Url> {
148        &self.links
149    }
150
151    fn get_links_mut(&mut self) -> &mut HashMap<String, url::Url> {
152        &mut self.links
153    }
154}
155
156#[cfg_attr(
157    feature = "serde",
158    derive(Serialize, Deserialize),
159    serde(rename_all = "camelCase")
160)]
161#[derive(Debug, Clone, PartialEq, Eq)]
162pub struct TaskStatusEvent {
163    pub task_request_id: i32,
164    pub task_request_uri: String,
165    pub status_changes: Vec<TaskStatus>,
166}
167
168impl TaskStatusEvent {
169    pub fn latest_status(&self) -> Option<&TaskStatus> {
170        self.status_changes
171            .iter()
172            .max_by_key(|status| status.created)
173    }
174}
175
176#[cfg_attr(
177    feature = "serde",
178    derive(Serialize, Deserialize),
179    serde(rename_all = "camelCase")
180)]
181#[derive(Debug, Clone, PartialEq, Eq)]
182pub struct TaskRequest {
183    #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
184    pub created: OffsetDateTime,
185    #[cfg_attr(
186        feature = "serde",
187        serde(default, with = "time::serde::iso8601::option")
188    )]
189    pub modified: Option<OffsetDateTime>,
190    /// Unavailable for user accounts
191    #[cfg_attr(feature = "serde", serde(default))]
192    pub internal_meta_data: Option<HashMap<String, String>>,
193    #[cfg_attr(feature = "serde", serde(rename = "type"))]
194    pub task_type: TaskType,
195    #[cfg_attr(feature = "serde", serde(default))]
196    pub hours_of_flex: u32,
197    pub duration: u32,
198    pub minimum_duration: u32,
199    #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
200    pub target_date: OffsetDateTime,
201    #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
202    pub earliest_start: OffsetDateTime,
203    #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
204    pub latest_start: OffsetDateTime,
205    pub transmitting: bool,
206    #[cfg_attr(feature = "serde", serde(default))]
207    pub test_file: Option<String>,
208    pub status_changes: Vec<TaskStatus>,
209    pub task_active: bool,
210    pub task_request_scheduled: bool,
211    pub task_request_cancelled: bool,
212    pub flex: bool,
213    pub latest_status_change: TaskStatus,
214    #[cfg_attr(feature = "serde", serde(default))]
215    pub meta_data: Option<HashMap<String, String>>,
216    #[cfg_attr(
217        feature = "serde",
218        serde(rename = "_links", with = "utils::links::serde", default)
219    )]
220    pub links: HashMap<String, Url>,
221}
222
223impl Hateoas for TaskRequest {
224    fn get_links(&self) -> &HashMap<String, url::Url> {
225        &self.links
226    }
227
228    fn get_links_mut(&mut self) -> &mut HashMap<String, url::Url> {
229        &mut self.links
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236
237    #[test]
238    #[cfg(feature = "serde")]
239    fn task_status_event() {
240        let json = r#"
241{
242  "taskRequestId": 189348,
243  "taskRequestUri": "https://test-api.atlasground.com/api/requests/189348",
244  "statusChanges": [
245    {
246      "created": "2025-08-01T19:55:07.061Z",
247      "status": "RECEIVED",
248      "reason": "Saved to Database and awaiting scheduling"
249    },
250    {
251      "created": "2025-08-01T19:55:07.674Z",
252      "status": "SCHEDULED",
253      "reason": "Test Task Scheduled"
254    },
255    {
256      "created": "2025-08-01T19:55:08.986Z",
257      "status": "QUEUED_PASS",
258      "reason": "Pass has been queued to execute"
259    }
260  ]
261}"#;
262        let event: TaskStatusEvent = serde_json::from_str(json).unwrap();
263        assert_eq!(
264            event.latest_status().unwrap().status,
265            TaskStatusType::QueuedPass
266        );
267    }
268
269    #[test]
270    #[cfg(feature = "serde")]
271    fn task_status_event_float_timestamp() {
272        use time::macros::datetime;
273
274        let json = r#"
275{
276  "taskRequestId": 190029,
277  "taskRequestUri": "https://test-api.atlasground.com/api/requests/190029",
278  "statusChanges": [
279    {
280      "created": 1754409946.362000000,
281      "status": "RECEIVED",
282      "reason": "Saved to Database and awaiting scheduling"
283    }
284  ]
285}"#;
286        let event: TaskStatusEvent = serde_json::from_str(json).unwrap();
287        assert_eq!(
288            datetime!(2025 - 08 - 05 16:05:46.361_999_989).assume_utc(),
289            event.status_changes[0].created
290        );
291    }
292}