fixes
authorBen Pfaff <blp@cs.stanford.edu>
Fri, 26 Dec 2025 17:16:47 +0000 (09:16 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Fri, 26 Dec 2025 17:16:47 +0000 (09:16 -0800)
rust/pspp/src/spv/read/legacy_xml.rs
rust/pspp/src/spv/read/tests.rs
rust/pspp/src/spv/testdata/legacy11.expected [new file with mode: 0644]
rust/pspp/src/spv/testdata/legacy11.spv [new file with mode: 0644]

index b25e4699102abe9e2547d5bb1acd19ec73b4030e..f728acf8e8ddc2a6af7e5b3247c65ff8064be87b 100644 (file)
@@ -843,6 +843,7 @@ impl Visualization {
                     series: &BTreeMap<&str, Series>,
                     dims: &mut [Dim],
                     data: &mut HashMap<Vec<usize>, 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<format::Format> {
+        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 {
index a2b4cb3f2a64d3d8047c26425636057b985753b6..634446b6b7f848fe086fcb58d037399c063819ae 100644 (file)
@@ -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 (file)
index 0000000..1eee86a
--- /dev/null
@@ -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 (file)
index 0000000..c44dcee
Binary files /dev/null and b/rust/pspp/src/spv/testdata/legacy11.spv differ