diff --git a/CHANGELOG.md b/CHANGELOG.md index c78541b5..54675459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ TLDR: The new task state representation is more verbose but significantly cleane - Add `pueue reset --groups [group_names]` to allow resetting individual groups. [#482](https://github.com/Nukesor/pueue/issues/482) \ This also refactors the way resets are done internally, resulting in a cleaner code architecture. - Ability to set the Unix socket permissions through the new `unix_socket_permissions` configuration option. [#544](https://github.com/Nukesor/pueue/pull/544) +- Allow `pueue status` to order tasks by `enqueue_at`. [#554](https://github.com/Nukesor/pueue/issues/554) ## \[3.4.1\] - 2024-06-04 diff --git a/pueue/src/client/query/mod.rs b/pueue/src/client/query/mod.rs index 7e90093c..97a63cc7 100644 --- a/pueue/src/client/query/mod.rs +++ b/pueue/src/client/query/mod.rs @@ -2,6 +2,7 @@ #![allow(clippy::empty_docs)] use anyhow::{bail, Context, Result}; +use chrono::prelude::*; use pest::Parser; use pest_derive::Parser; @@ -93,6 +94,24 @@ impl QueryResult { Rule::column_label => task1.label.cmp(&task2.label), Rule::column_command => task1.command.cmp(&task2.command), Rule::column_path => task1.path.cmp(&task2.path), + Rule::column_enqueue_at => { + fn enqueue_date(task: &Task) -> DateTime { + match &task.status { + TaskStatus::Queued { enqueued_at, .. } + | TaskStatus::Running { enqueued_at, .. } + | TaskStatus::Paused { enqueued_at, .. } + | TaskStatus::Done { enqueued_at, .. } + | TaskStatus::Stashed { + enqueue_at: Some(enqueued_at), + .. + } => *enqueued_at, + // considered far in the future when no explicit date: + _ => DateTime::::MAX_UTC.into(), + } + } + + enqueue_date(task1).cmp(&enqueue_date(task2)) + } Rule::column_start => { let (start1, _) = task1.start_and_end(); let (start2, _) = task2.start_and_end(); diff --git a/pueue/tests/client/unit/status_query.rs b/pueue/tests/client/unit/status_query.rs index a617dc15..a8287bf7 100644 --- a/pueue/tests/client/unit/status_query.rs +++ b/pueue/tests/client/unit/status_query.rs @@ -281,6 +281,46 @@ async fn order_by_status() -> Result<()> { Ok(()) } +/// Order the tasks by enqueue(d) date. +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn order_by_enqueue_at() -> Result<()> { + let tasks = test_tasks_with_query("order_by enqueue_at asc", &None)?; + + let expected = vec![ + TaskStatus::Done { + enqueued_at: Local.with_ymd_and_hms(2022, 1, 8, 10, 0, 0).unwrap(), + start: Local.with_ymd_and_hms(2022, 1, 8, 10, 5, 0).unwrap(), + end: Local.with_ymd_and_hms(2022, 1, 8, 10, 10, 0).unwrap(), + result: TaskResult::Success, + }, + TaskStatus::Done { + enqueued_at: Local.with_ymd_and_hms(2022, 1, 10, 10, 0, 0).unwrap(), + start: Local.with_ymd_and_hms(2022, 1, 10, 10, 5, 0).unwrap(), + end: Local.with_ymd_and_hms(2022, 1, 10, 10, 10, 0).unwrap(), + result: TaskResult::Failed(255), + }, + TaskStatus::Running { + enqueued_at: Local.with_ymd_and_hms(2022, 1, 10, 10, 0, 0).unwrap(), + start: Local.with_ymd_and_hms(2022, 1, 2, 12, 0, 0).unwrap(), + }, + TaskStatus::Queued { + enqueued_at: Local.with_ymd_and_hms(2022, 1, 10, 10, 0, 0).unwrap(), + }, + TaskStatus::Queued { + enqueued_at: Local.with_ymd_and_hms(2022, 1, 10, 10, 0, 0).unwrap(), + }, + TaskStatus::Stashed { + enqueue_at: Some(Local.with_ymd_and_hms(2022, 1, 10, 11, 0, 0).unwrap()), + }, + TaskStatus::Stashed { enqueue_at: None }, + ]; + + let actual: Vec = tasks.iter().map(|task| task.status.clone()).collect(); + assert_eq!(actual, expected); + + Ok(()) +} + /// Filter tasks by label with the "contains" `%=` filter. #[rstest] #[case("%=", "label", 3)]