use bids_core::error::Result;
use bids_layout::BidsLayout;
use serde_json::{Value, json};
pub fn auto_model(layout: &BidsLayout) -> Result<Vec<Value>> {
let tasks = layout.get_tasks()?;
let subjects = layout.get_subjects()?;
let sessions = layout.get_sessions()?;
let root_name = layout
.root()
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("dataset");
let mut models = Vec::new();
for task_name in &tasks {
let model_name = format!("{root_name}_{task_name}");
let event_files = layout
.get()
.suffix("events")
.extension("tsv")
.task(task_name)
.collect()?;
let mut trial_types = std::collections::BTreeSet::new();
for ef in &event_files {
if let Ok(rows) = ef.get_df() {
for row in &rows {
if let Some(tt) = row.get("trial_type")
&& !tt.is_empty()
{
trial_types.insert(tt.clone());
}
}
}
}
let trial_type_factors: Vec<String> = trial_types
.iter()
.map(|tt| format!("trial_type.{tt}"))
.collect();
let run_node = json!({
"Level": "Run",
"Name": "run",
"GroupBy": ["run", "subject"],
"Transformations": {
"Transformer": "pybids-transforms-v1",
"Instructions": [{"Name": "Factor", "Input": ["trial_type"]}]
},
"Model": {"Type": "glm", "X": trial_type_factors},
"DummyContrasts": {"Test": "t"}
});
let mut nodes = vec![run_node];
if sessions.len() > 1 {
nodes.push(json!({
"Level": "Session",
"Name": "session",
"GroupBy": ["session", "contrast"],
"Model": {"Type": "meta", "X": [1]},
"DummyContrasts": {"Test": "t"}
}));
}
if subjects.len() > 1 {
nodes.push(json!({
"Level": "Subject",
"Name": "subject",
"GroupBy": ["subject", "contrast"],
"Model": {"Type": "meta", "X": [1]},
"DummyContrasts": {"Test": "t"}
}));
}
nodes.push(json!({
"Level": "Dataset",
"Name": "dataset",
"GroupBy": ["contrast"],
"Model": {"Type": "glm", "X": [1]},
"DummyContrasts": {"Test": "t"}
}));
let model = json!({
"Name": model_name,
"Description": format!("Autogenerated model for {} task from {}", task_name, root_name),
"BIDSModelVersion": "1.0.0",
"Input": {"task": [task_name]},
"Nodes": nodes
});
models.push(model);
}
Ok(models)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auto_model_signature() {
let _: fn(&BidsLayout) -> Result<Vec<Value>> = auto_model;
}
}