Skip to content

Commit 8df1c06

Browse files
remove timestamp for 2d files
1 parent 4f031a6 commit 8df1c06

2 files changed

Lines changed: 84 additions & 49 deletions

File tree

rust/crates/sift_cli/src/cmd/import/hdf5/detect_hdf5_schema.rs

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -130,37 +130,51 @@ pub fn detect_config(
130130
Ok((data, _)) if data.is_empty() => Err(no_match_error(
131131
&datasets, schema, time_index, time_field, time_name,
132132
)),
133-
Ok((data, mut channel_configs)) => {
134-
let time_rows = build_time_channel_rows(&data);
135-
channel_configs.splice(0..0, time_rows);
136-
Ok((data, channel_configs))
133+
Ok((data, channel_configs)) => {
134+
// 2D mode has no signal — the time column is whatever index the user passed
135+
// via --time-index. We can't verify it, so we don't tag it in the preview.
136+
// 1D auto-detects from dataset name; compound takes an explicit --time-field.
137+
let woven = if matches!(schema, Hdf5Schema::TwoD) {
138+
channel_configs
139+
} else {
140+
weave_time_channel_rows(&data, &channel_configs)
141+
};
142+
Ok((data, woven))
137143
}
138144
Err(e) => Err(e),
139145
}
140146
}
141147

142-
pub(super) fn build_time_channel_rows(data: &[Hdf5DataConfig]) -> Vec<ChannelConfig> {
148+
fn time_channel_name(cfg: &Hdf5DataConfig) -> String {
149+
let dataset_name = group_path_to_channel_name(&cfg.time_dataset);
150+
match &cfg.time_field {
151+
Some(field) if !field.is_empty() => format!("{dataset_name}.{field}"),
152+
_ if cfg.time_dataset == cfg.value_dataset => {
153+
format!("{dataset_name}.{}", cfg.time_index)
154+
}
155+
_ => dataset_name,
156+
}
157+
}
158+
159+
pub(super) fn weave_time_channel_rows(
160+
data: &[Hdf5DataConfig],
161+
channels: &[ChannelConfig],
162+
) -> Vec<ChannelConfig> {
143163
let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
144-
let mut rows: Vec<ChannelConfig> = Vec::new();
145-
for cfg in data {
146-
let dataset_name = group_path_to_channel_name(&cfg.time_dataset);
147-
let name = match &cfg.time_field {
148-
Some(field) if !field.is_empty() => format!("{dataset_name}.{field}"),
149-
_ if cfg.time_dataset == cfg.value_dataset => {
150-
format!("{dataset_name}.{}", cfg.time_index)
151-
}
152-
_ => dataset_name,
153-
};
164+
let mut woven: Vec<ChannelConfig> = Vec::with_capacity(channels.len() + data.len());
165+
for (cfg, channel) in data.iter().zip(channels.iter()) {
166+
let name = time_channel_name(cfg);
154167
if seen.insert(name.clone()) {
155-
rows.push(ChannelConfig {
168+
woven.push(ChannelConfig {
156169
name,
157170
description: "[time]".into(),
158171
data_type: ChannelDataType::Int64 as i32,
159172
..Default::default()
160173
});
161174
}
175+
woven.push(channel.clone());
162176
}
163-
rows
177+
woven
164178
}
165179

166180
fn no_match_error(

rust/crates/sift_cli/src/cmd/import/hdf5/tests.rs

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use crate::cli::hdf5::Hdf5Schema;
99
use crate::cli::time::TimeFormat;
1010
use crate::cli::{CommonImportArgs, ImportHdf5Args};
1111
use crate::cmd::import::hdf5::detect_hdf5_schema::{
12-
basename, build_time_channel_rows, enum_types_for, hdf5_to_sift_data_type,
13-
is_time_dataset_name, parent_path,
12+
basename, enum_types_for, hdf5_to_sift_data_type, is_time_dataset_name, parent_path,
13+
weave_time_channel_rows,
1414
};
1515
use crate::cmd::import::hdf5::import::build_hdf5_config;
1616
use crate::cmd::import::utils::group_path_to_channel_name;
@@ -392,37 +392,46 @@ fn make_data_config(
392392
}
393393
}
394394

395+
fn make_channel(name: &str) -> sift_rs::common::r#type::v1::ChannelConfig {
396+
sift_rs::common::r#type::v1::ChannelConfig {
397+
name: name.into(),
398+
data_type: ChannelDataType::Double as i32,
399+
..Default::default()
400+
}
401+
}
402+
395403
#[test]
396-
fn build_time_channel_rows_one_d_dedups_shared_time_dataset() {
397-
let configs = vec![
404+
fn weave_one_d_shared_time_appears_once_before_channels() {
405+
let data = vec![
398406
make_data_config("/group_a/time", "/group_a/voltage", 0, None),
399407
make_data_config("/group_a/time", "/group_a/current", 0, None),
400408
make_data_config("/group_b/timestamp", "/group_b/value", 0, None),
401409
];
402-
let rows = build_time_channel_rows(&configs);
403-
assert_eq!(rows.len(), 2);
404-
assert_eq!(rows[0].name, "group_a.time");
405-
assert_eq!(rows[0].description, "[time]");
406-
assert_eq!(rows[0].data_type, ChannelDataType::Int64 as i32);
407-
assert_eq!(rows[1].name, "group_b.timestamp");
408-
assert_eq!(rows[1].description, "[time]");
409-
}
410-
411-
#[test]
412-
fn build_time_channel_rows_two_d_includes_time_index() {
413-
let configs = vec![
414-
make_data_config("/sensor_0", "/sensor_0", 0, None),
415-
make_data_config("/sensor_1", "/sensor_1", 0, None),
410+
let channels = vec![
411+
make_channel("group_a.voltage"),
412+
make_channel("group_a.current"),
413+
make_channel("group_b.value"),
416414
];
417-
let rows = build_time_channel_rows(&configs);
418-
assert_eq!(rows.len(), 2);
419-
assert_eq!(rows[0].name, "sensor_0.0");
420-
assert_eq!(rows[1].name, "sensor_1.0");
415+
let woven = weave_time_channel_rows(&data, &channels);
416+
let layout: Vec<(&str, &str)> = woven
417+
.iter()
418+
.map(|c| (c.name.as_str(), c.description.as_str()))
419+
.collect();
420+
assert_eq!(
421+
layout,
422+
vec![
423+
("group_a.time", "[time]"),
424+
("group_a.voltage", ""),
425+
("group_a.current", ""),
426+
("group_b.timestamp", "[time]"),
427+
("group_b.value", ""),
428+
]
429+
);
421430
}
422431

423432
#[test]
424-
fn build_time_channel_rows_compound_renders_dataset_dot_field() {
425-
let configs = vec![
433+
fn weave_compound_shows_dataset_dot_field_once_per_compound_dataset() {
434+
let data = vec![
426435
make_data_config("/measurements/run1", "/measurements/run1", 0, Some("ts")),
427436
make_data_config("/measurements/run1", "/measurements/run1", 0, Some("ts")),
428437
make_data_config(
@@ -432,15 +441,27 @@ fn build_time_channel_rows_compound_renders_dataset_dot_field() {
432441
Some("timestamp"),
433442
),
434443
];
435-
let rows = build_time_channel_rows(&configs);
436-
assert_eq!(rows.len(), 2);
437-
assert_eq!(rows[0].name, "measurements.run1.ts");
438-
assert_eq!(rows[0].description, "[time]");
439-
assert_eq!(rows[1].name, "measurements.run2.timestamp");
444+
let channels = vec![
445+
make_channel("measurements.run1.voltage"),
446+
make_channel("measurements.run1.current"),
447+
make_channel("measurements.run2.voltage"),
448+
];
449+
let woven = weave_time_channel_rows(&data, &channels);
450+
let names: Vec<&str> = woven.iter().map(|c| c.name.as_str()).collect();
451+
assert_eq!(
452+
names,
453+
vec![
454+
"measurements.run1.ts",
455+
"measurements.run1.voltage",
456+
"measurements.run1.current",
457+
"measurements.run2.timestamp",
458+
"measurements.run2.voltage",
459+
]
460+
);
440461
}
441462

442463
#[test]
443-
fn build_time_channel_rows_empty_when_no_data_configs() {
444-
let rows = build_time_channel_rows(&[]);
445-
assert!(rows.is_empty());
464+
fn weave_empty_returns_empty() {
465+
let woven = weave_time_channel_rows(&[], &[]);
466+
assert!(woven.is_empty());
446467
}

0 commit comments

Comments
 (0)