Skip to content

Commit 31631ba

Browse files
authored
Merge pull request #15 from aarkue/feat/oc-declare-integration
Feat/oc declare integration
2 parents f60f8c2 + a8aeeb1 commit 31631ba

File tree

126 files changed

+26062
-2611
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+26062
-2611
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/shared/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ serde = {version = "1.0.192" , features = ["derive", "rc"]}
1010
serde_json = "1.0.108"
1111
serde_with = "3.11.0"
1212
ts-rs = { version = "8.1", features = ["serde-compat","chrono-impl"] }
13-
process_mining = { git = "https://github.com/aarkue/rust4pm", features = ["ocel-sqlite"], branch = "feat/kuzudb" }
13+
# process_mining = {path = "../../../rust4pm/process_mining", features = ['ocel-sqlite']}
14+
process_mining = { git = "https://github.com/aarkue/rust4pm", features = ["ocel-sqlite"], branch = "feat/oc-declare" }
1415
rayon = "1.8.0"
1516
itertools = "0.11.0"
1617
chrono = "0.4.33"

backend/shared/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ pub mod preprocessing {
2222
}
2323
pub mod cel;
2424
pub mod table_export;
25+
pub mod oc_declare {
26+
pub mod statistics;
27+
}
2528

2629
pub mod hpc_backend;
2730
#[derive(Debug, Serialize, Deserialize)]
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use std::collections::{HashMap, HashSet};
2+
3+
use process_mining::{
4+
object_centric::oc_declare::{
5+
perf::get_evs_with_objs_perf, OCDeclareArc, OCDeclareArcType, EXIT_EVENT_PREFIX,
6+
INIT_EVENT_PREFIX,
7+
},
8+
ocel::linked_ocel::{IndexLinkedOCEL, LinkedOCELAccess},
9+
};
10+
use serde::{Deserialize, Serialize};
11+
use ts_rs::TS;
12+
13+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
14+
#[ts(export, export_to = "../../../frontend/src/types/generated/")]
15+
pub struct ActivityStatistics {
16+
pub num_evs_per_ot_type: HashMap<String, Vec<usize>>,
17+
pub num_obs_of_ot_per_ev: HashMap<String, Vec<usize>>,
18+
}
19+
20+
pub fn get_activity_statistics(locel: &IndexLinkedOCEL, activity: &str) -> ActivityStatistics {
21+
if activity.starts_with(INIT_EVENT_PREFIX) || activity.starts_with(EXIT_EVENT_PREFIX) {
22+
let ob_type = if activity.starts_with(INIT_EVENT_PREFIX) {
23+
&activity[INIT_EVENT_PREFIX.len() + 1..activity.len()]
24+
} else {
25+
&activity[INIT_EVENT_PREFIX.len() + 1..activity.len()]
26+
};
27+
return ActivityStatistics {
28+
num_evs_per_ot_type: vec![(
29+
ob_type.to_string(),
30+
vec![1; locel.get_obs_of_type(ob_type).count()],
31+
)]
32+
.into_iter()
33+
.collect(),
34+
num_obs_of_ot_per_ev: vec![(
35+
ob_type.to_string(),
36+
vec![1; locel.get_obs_of_type(ob_type).count()],
37+
)]
38+
.into_iter()
39+
.collect(),
40+
};
41+
}
42+
// Number of activity events per object (of a type)
43+
let mut num_evs_per_type: HashMap<String, Vec<usize>> = HashMap::new();
44+
let mut relevant_object_types = HashSet::new();
45+
// Number of objects (of a type) per activity
46+
let mut num_objects_per_type: HashMap<&String, Vec<usize>> = HashMap::new();
47+
48+
for ev in locel.get_evs_of_type(activity) {
49+
let mut num_obs_of_type_for_ev = HashMap::new();
50+
for (_q, ob) in locel.get_e2o(ev) {
51+
let ot = &locel[ob].object_type;
52+
*num_obs_of_type_for_ev.entry(ot).or_default() += 1
53+
}
54+
for (a, b) in num_obs_of_type_for_ev {
55+
relevant_object_types.insert(a);
56+
num_objects_per_type.entry(a).or_default().push(b);
57+
}
58+
}
59+
60+
for ot in relevant_object_types {
61+
num_evs_per_type.insert(
62+
ot.to_string(),
63+
locel
64+
.get_obs_of_type(&ot)
65+
.into_iter()
66+
.map(|o| {
67+
locel
68+
.get_e2o_rev(o)
69+
.filter(|(_q, e)| locel[*e].event_type == activity)
70+
.count()
71+
})
72+
.collect(),
73+
);
74+
}
75+
ActivityStatistics {
76+
num_obs_of_ot_per_ev: num_objects_per_type
77+
.into_iter()
78+
.map(|(k, v)| (k.to_string(), v))
79+
.collect(),
80+
num_evs_per_ot_type: num_evs_per_type,
81+
}
82+
}
83+
84+
pub fn get_edge_stats(locel: &IndexLinkedOCEL, arc: &OCDeclareArc) -> Vec<i64> {
85+
process_mining::object_centric::oc_declare::TriggerEvent::get_all_trigger_evs(
86+
locel,
87+
arc.from.as_str(),
88+
)
89+
.iter()
90+
.flat_map(|ev_index| {
91+
let ev_time = ev_index.get_timestamp(locel);
92+
arc.label
93+
.get_bindings(ev_index, locel)
94+
.flat_map(move |binding| {
95+
let target_ev_iterator = get_evs_with_objs_perf(&binding, locel, arc.to.as_str())
96+
.filter(|ev2| {
97+
let ev2_time = ev2.get_timestamp(locel);
98+
match arc.arc_type {
99+
OCDeclareArcType::EF | OCDeclareArcType::DF => ev_time < ev2_time,
100+
OCDeclareArcType::EP | OCDeclareArcType::DP => ev_time > ev2_time,
101+
OCDeclareArcType::AS => true,
102+
}
103+
});
104+
// First event (could also implement this for last, or all matching target events)
105+
let first_ev = target_ev_iterator.min_by_key(|e| e.get_timestamp(locel));
106+
first_ev.map(|ev2| (ev2.get_timestamp(locel) - ev_time).num_milliseconds())
107+
})
108+
})
109+
.collect()
110+
}

backend/web-server/src/load_ocel/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,17 @@ pub async fn load_ocel_file_req(
5050
State(state): State<AppState>,
5151
Json(payload): Json<LoadOcel>,
5252
) -> (StatusCode, Json<Option<OCELInfo>>) {
53-
match load_ocel_file_to_state(&payload.name, &state) {
53+
match load_ocel_file_to_state(&payload.name, &state, false) {
5454
Some(ocel_info) => (StatusCode::OK, Json(Some(ocel_info))),
5555
None => (StatusCode::BAD_REQUEST, Json(None)),
5656
}
5757
}
5858

59-
pub fn load_ocel_file_to_state(name: &str, state: &AppState) -> Option<OCELInfo> {
59+
pub fn load_ocel_file_to_state(
60+
name: &str,
61+
state: &AppState,
62+
ignore_errors: bool,
63+
) -> Option<OCELInfo> {
6064
match load_ocel_file(name) {
6165
Ok(ocel) => {
6266
let locel = IndexLinkedOCEL::from_ocel(ocel);
@@ -66,7 +70,9 @@ pub fn load_ocel_file_to_state(name: &str, state: &AppState) -> Option<OCELInfo>
6670
Some(ocel_info)
6771
}
6872
Err(e) => {
69-
eprintln!("Error importing OCEL: {e:?}");
73+
if !ignore_errors {
74+
eprintln!("Error importing OCEL: {e:?}");
75+
}
7076
None
7177
}
7278
}

backend/web-server/src/main.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@ use ocpq_shared::{
3232
get_job_status, login_on_hpc, start_port_forwarding, submit_hpc_job, Client,
3333
ConnectionConfig, JobStatus, OCPQJobOptions,
3434
},
35+
oc_declare::statistics::{get_activity_statistics, get_edge_stats, ActivityStatistics},
3536
ocel_graph::{get_ocel_graph, OCELGraph, OCELGraphOptions},
3637
ocel_qualifiers::qualifiers::{
3738
get_qualifiers_for_event_types, QualifierAndObjectType, QualifiersForEventType,
3839
},
3940
preprocessing::preprocess::get_object_rels_per_type,
41+
process_mining::object_centric::oc_declare::{self, OCDeclareArc},
4042
table_export::{export_bindings_to_writer, TableExportOptions},
4143
EventWithIndex, IndexOrID, OCELInfo, ObjectWithIndex,
4244
};
@@ -78,9 +80,8 @@ async fn main() {
7880
// .allow_headers([CONTENT_TYPE])
7981
// .allow_origin(tower_http::cors::Any);
8082

81-
load_ocel_file_to_state(DEFAULT_OCEL_FILE, &state);
83+
load_ocel_file_to_state(DEFAULT_OCEL_FILE, &state, true);
8284

83-
// build our application with a single route
8485
let app = Router::new()
8586
.route("/ocel/load", post(load_ocel_file_req))
8687
.route("/ocel/info", get(get_loaded_ocel_info))
@@ -119,6 +120,22 @@ async fn main() {
119120
"/ocel/discover-constraints",
120121
post(auto_discover_constraints_handler),
121122
)
123+
.route(
124+
"/ocel/discover-oc-declare",
125+
post(auto_discover_oc_declare_handler),
126+
)
127+
.route(
128+
"/ocel/evaluate-oc-declare-arcs",
129+
post(evaluate_oc_declare_arcs_handler),
130+
)
131+
.route(
132+
"/ocel/get-activity-statistics",
133+
post(get_activity_statistics_handler),
134+
)
135+
.route(
136+
"/ocel/get-oc-declare-edge-statistics",
137+
post(get_oc_declare_edge_statistics_handler),
138+
)
122139
.route(
123140
"/ocel/export-bindings",
124141
post(export_bindings_table).layer(DefaultBodyLimit::disable()),
@@ -305,6 +322,40 @@ pub async fn auto_discover_constraints_handler<'a>(
305322
}))
306323
}
307324

325+
pub async fn auto_discover_oc_declare_handler(
326+
state: State<AppState>,
327+
Json(req): Json<oc_declare::OCDeclareDiscoveryOptions>,
328+
) -> Json<Option<Vec<OCDeclareArc>>> {
329+
Json(with_ocel_from_state(&state, |locel| {
330+
oc_declare::discover_behavior_constraints(&locel, req)
331+
}))
332+
}
333+
pub async fn evaluate_oc_declare_arcs_handler(
334+
state: State<AppState>,
335+
Json(req): Json<Vec<oc_declare::OCDeclareArc>>,
336+
) -> Json<Option<Vec<f64>>> {
337+
Json(with_ocel_from_state(&state, |locel| {
338+
req.iter()
339+
.map(|arc| arc.get_for_all_evs_perf(&locel))
340+
.collect()
341+
}))
342+
}
343+
pub async fn get_activity_statistics_handler(
344+
state: State<AppState>,
345+
Json(req): Json<String>,
346+
) -> Json<Option<ActivityStatistics>> {
347+
Json(with_ocel_from_state(&state, |ocel| {
348+
get_activity_statistics(ocel, &req)
349+
}))
350+
}
351+
pub async fn get_oc_declare_edge_statistics_handler(
352+
state: State<AppState>,
353+
Json(req): Json<OCDeclareArc>,
354+
) -> Json<Option<Vec<i64>>> {
355+
Json(with_ocel_from_state(&state, |ocel| {
356+
get_edge_stats(ocel, &req)
357+
}))
358+
}
308359
pub async fn export_bindings_table(
309360
state: State<AppState>,
310361
Json((node_index, table_options)): Json<(usize, TableExportOptions)>,

0 commit comments

Comments
 (0)