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