From 172ae89bd78875cb9f15c60ab03e7cdd50b9868d Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 26 Dec 2025 09:16:47 -0800 Subject: [PATCH] fixes --- rust/pspp/src/spv/read/legacy_xml.rs | 56 +++++++++++++++---- rust/pspp/src/spv/read/tests.rs | 7 +++ rust/pspp/src/spv/testdata/legacy11.expected | 12 ++++ rust/pspp/src/spv/testdata/legacy11.spv | Bin 0 -> 4910 bytes 4 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 rust/pspp/src/spv/testdata/legacy11.expected create mode 100644 rust/pspp/src/spv/testdata/legacy11.spv diff --git a/rust/pspp/src/spv/read/legacy_xml.rs b/rust/pspp/src/spv/read/legacy_xml.rs index b25e469910..f728acf8e8 100644 --- a/rust/pspp/src/spv/read/legacy_xml.rs +++ b/rust/pspp/src/spv/read/legacy_xml.rs @@ -843,6 +843,7 @@ impl Visualization { series: &BTreeMap<&str, Series>, dims: &mut [Dim], data: &mut HashMap, Value>, + footnotes: &pivot::Footnotes, ) { let mut wheres = Vec::new(); let mut alternating = false; @@ -928,6 +929,7 @@ impl Visualization { self.major_ticks, self.frame, &look.areas[Area::Labels(axis)], + footnotes, ); } } @@ -991,6 +993,7 @@ impl Visualization { self.major_ticks, self.frame, &look.areas[Area::Data(RowParity::Even)], + footnotes ); } } @@ -1036,6 +1039,7 @@ impl Visualization { &series, dims.as_mut_slice(), &mut data, + &footnotes ); } } @@ -1056,6 +1060,7 @@ impl Visualization { None, None, &look.areas[Area::Data(RowParity::Even)], + &footnotes, ); } } @@ -1138,7 +1143,7 @@ impl Series { && let Ok(index) = usize::try_from(index) && let Some(footnote) = footnotes.get(index) { - value = value.with_footnote(footnote); + value.add_footnote(footnote); } } value @@ -1957,19 +1962,11 @@ impl Style { fg: Option<&Style>, bg: Option<&Style>, base_style: &AreaStyle, + footnotes: &pivot::Footnotes, ) { if let Some(sf) = sf { - let format = match &sf.child { - Some(SetFormatChild::Format(format)) => Some(format.decode()), - Some(SetFormatChild::NumberFormat(format)) => { - Some(SignificantNumberFormat::from(format).decode()) - } - Some(SetFormatChild::StringFormat(_)) => None, - Some(SetFormatChild::DateTimeFormat(format)) => Some(format.decode()), - Some(SetFormatChild::ElapsedTimeFormat(format)) => Some(format.decode()), - None => None, - }; - if let Some(format) = format + if let Some(child) = &sf.child + && let Some(format) = child.decode_format() && let Some(datum_value) = value.inner.as_datum_value_mut() { match &datum_value.datum { @@ -1995,6 +1992,16 @@ impl Style { } } } + if let Some(child) = &sf.child { + for affix in child.affixes() { + if let Some(index) = affix.defines_reference.checked_sub(1) + && let Ok(index) = usize::try_from(index) + && let Some(footnote) = footnotes.get(index) + { + value.add_footnote(footnote); + } + } + } } if fg.is_some() || bg.is_some() { @@ -2475,6 +2482,31 @@ enum SetFormatChild { ElapsedTimeFormat(ElapsedTimeFormat), } +impl SetFormatChild { + fn decode_format(&self) -> Option { + match self { + SetFormatChild::Format(format) => Some(format.decode()), + SetFormatChild::NumberFormat(format) => { + Some(SignificantNumberFormat::from(format).decode()) + } + SetFormatChild::StringFormat(_) => None, + SetFormatChild::DateTimeFormat(format) => Some(format.decode()), + SetFormatChild::ElapsedTimeFormat(format) => Some(format.decode()), + } + } + fn affixes(&self) -> &[Affix] { + match self { + SetFormatChild::Format(format) => &format.affixes, + SetFormatChild::NumberFormat(number_format) => &number_format.affixes, + SetFormatChild::StringFormat(string_formats) => { + string_formats.first().map_or(&[], |first| &first.affixes) + } + SetFormatChild::DateTimeFormat(date_time_format) => &date_time_format.affixes, + SetFormatChild::ElapsedTimeFormat(elapsed_time_format) => &elapsed_time_format.affixes, + } + } +} + #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct SetFrameStyle { diff --git a/rust/pspp/src/spv/read/tests.rs b/rust/pspp/src/spv/read/tests.rs index a2b4cb3f2a..634446b6b7 100644 --- a/rust/pspp/src/spv/read/tests.rs +++ b/rust/pspp/src/spv/read/tests.rs @@ -68,6 +68,13 @@ fn legacy10() { test_raw_spvfile("legacy10"); } +/// Checks for footnotes on data cells added via XML rather than `cellFootnotes` +/// series. +#[test] +fn legacy11() { + test_raw_spvfile("legacy11"); +} + fn test_raw_spvfile(name: &str) { let input_filename = Path::new("src/spv/testdata") .join(name) diff --git a/rust/pspp/src/spv/testdata/legacy11.expected b/rust/pspp/src/spv/testdata/legacy11.expected new file mode 100644 index 0000000000..1eee86aee4 --- /dev/null +++ b/rust/pspp/src/spv/testdata/legacy11.expected @@ -0,0 +1,12 @@ + Ranks +╭───────────────────────────────────┬────┬─────────┬────────────╮ +│ │ N │Mean Rank│Sum of Ranks│ +├───────────────────────────────────┼────┼─────────┼────────────┤ +│Post_Test - Pre_Test Negative Ranks│5[a]│ 3.00│ 15.00│ +│ Positive Ranks│0[b]│ .00│ .00│ +│ Ties │0[c]│ │ │ +│ Total │ 5│ │ │ +╰───────────────────────────────────┴────┴─────────┴────────────╯ +a. Post_Test < Pre_Test +b. Post_Test > Pre_Test +c. Post_Test = Pre_Test diff --git a/rust/pspp/src/spv/testdata/legacy11.spv b/rust/pspp/src/spv/testdata/legacy11.spv new file mode 100644 index 0000000000000000000000000000000000000000..c44dceeceabc4d00d99080123f58b8edd6936288 GIT binary patch literal 4910 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 bP~!i4!fG%K%->b$zm_-(05JEf4gmNMYm>js literal 0 HcmV?d00001 -- 2.30.2