Skip to main content

ocel_mine/
cases.rs

1//! Per-object case digests.
2//!
3//! The list a cases view paginates and filters: one row per object with
4//! events, carrying its activity sequence and lead time. Variant filtering
5//! and pagination are lookups and stay in the consumer.
6
7use chrono::{DateTime, Utc};
8use ocel::Ocel;
9use serde::Serialize;
10
11use crate::trace;
12
13/// One object's trace digest.
14#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
15#[serde(rename_all = "camelCase")]
16pub struct CaseSummary {
17    pub object_id: String,
18    /// Activity sequence in trace order.
19    pub activities: Vec<String>,
20    /// Steps in the trace.
21    pub events: usize,
22    pub start: DateTime<Utc>,
23    pub end: DateTime<Utc>,
24    pub lead_secs: i64,
25}
26
27/// Digest every object of `object_type` that has events, sorted by start time
28/// (ties by object id).
29#[must_use]
30pub fn cases(log: &Ocel, object_type: &str) -> Vec<CaseSummary> {
31    let traces = trace::build(log, object_type);
32    let mut out: Vec<CaseSummary> = traces
33        .steps
34        .iter()
35        .enumerate()
36        .filter_map(|(slot, steps)| {
37            let (&(_, start), &(_, end)) = (steps.first()?, steps.last()?);
38            Some(CaseSummary {
39                object_id: traces.object_ids[slot].to_owned(),
40                activities: steps
41                    .iter()
42                    .map(|&(a, _)| traces.activity_names[a as usize].to_owned())
43                    .collect(),
44                events: steps.len(),
45                start,
46                end,
47                lead_secs: (end - start).num_seconds(),
48            })
49        })
50        .collect();
51    out.sort_unstable_by(|a, b| {
52        a.start
53            .cmp(&b.start)
54            .then_with(|| a.object_id.cmp(&b.object_id))
55    });
56    out
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use crate::test_util::log_from_sequences;
63
64    #[test]
65    fn digests_only_objects_with_events() {
66        let log = log_from_sequences(&[&["a", "b", "c"], &["a"]]);
67        let all = cases(&log, "case");
68        assert_eq!(all.len(), 2);
69        assert_eq!(all[0].object_id, "o0");
70        assert_eq!(all[0].activities, ["a", "b", "c"]);
71        assert_eq!(all[0].events, 3);
72        assert_eq!(all[0].lead_secs, 120);
73        assert_eq!(all[1].lead_secs, 0);
74    }
75
76    #[test]
77    fn empty_type_is_empty() {
78        let log = log_from_sequences(&[]);
79        assert!(cases(&log, "case").is_empty());
80    }
81}