@@ -147,6 +147,8 @@ def inspect_sav(source: str | Path) -> dict[str, Any]:
147147 _require_source (source_path )
148148 metadata , loss_report = _dictionary (source_path )
149149 names = list (metadata .get ("var_names" ) or [])
150+ variables = _variables (metadata , names )
151+ loss_report = _merge_loss_reports (tuple (loss_report .values ()), _compat_name_loss_report (variables ))
150152 return {
151153 "source_format" : source_path .suffix [1 :].upper (),
152154 "source_name" : source_path .name ,
@@ -157,9 +159,9 @@ def inspect_sav(source: str | Path) -> dict[str, Any]:
157159 "file_attributes" : dict (metadata .get ("file_attributes" ) or {}),
158160 "case_weight_variable" : metadata .get ("case_weight_var" ) or None ,
159161 "multiple_response_sets" : dict (metadata .get ("mrsets" ) or {}),
160- "loss_report" : tuple ( loss_report . values ()) ,
162+ "loss_report" : loss_report ,
161163 "variable_count" : len (names ),
162- "variables" : _variables ( metadata , names ) ,
164+ "variables" : variables ,
163165 }
164166
165167
@@ -175,6 +177,7 @@ def import_sav_dataset(*, source: str | Path, database_url: str, dataset_id: str
175177 # frame read's values where a future pyspssio version exposes more there.
176178 metadata = {** dictionary , ** metadata }
177179 variables = _variables (metadata , list (frame .columns ))
180+ loss_report = _merge_loss_reports (tuple (loss_report .values ()), _compat_name_loss_report (variables ))
178181 result = create_wide_dataset (
179182 database_url = database_url ,
180183 dataset_id = dataset_id ,
@@ -196,9 +199,9 @@ def import_sav_dataset(*, source: str | Path, database_url: str, dataset_id: str
196199 {"spss.variable_sets" : metadata ["_var_sets" ]}
197200 if metadata .get ("_var_sets" ) else {}
198201 ),
199- fidelity_events = tuple ( loss_report . values ()) ,
202+ fidelity_events = loss_report ,
200203 )
201- return {** result , "loss_report" : tuple ( loss_report . values ()) }
204+ return {** result , "loss_report" : loss_report }
202205
203206
204207def export_sav_dataset (
@@ -215,8 +218,8 @@ def export_sav_dataset(
215218 multiple_response_sets = dataset .get ("multiple_response_sets" ),
216219 )
217220 loss_report = _merge_loss_reports (
221+ _export_loss_report (dataset , variables ),
218222 read_fidelity_events (database_url = database_url , dataset_id = dataset_id ),
219- _export_loss_report (dataset ),
220223 )
221224 rejected = [event ["code" ] for event in loss_report if event ["code" ] not in allow_loss ]
222225 if rejected :
@@ -365,14 +368,48 @@ def _engine_loss_report(metadata: dict[str, Any]) -> dict[str, dict[str, Any]]:
365368 return events
366369
367370
368- def _export_loss_report (dataset : dict [str , Any ]) -> tuple [dict [str , Any ], ...]:
371+ def _compat_name_loss_report (variables : list [dict [str , Any ]]) -> tuple [dict [str , Any ], ...]:
372+ """Report legacy SPSS compatible names the writer cannot set explicitly.
373+
374+ A compatible name is meaningful only when it differs from the long source
375+ variable name. ``pyspssio`` exposes these names while reading, but its
376+ public writer metadata has no ``var_compat_names`` input. It may derive a
377+ name today, but that is not a preservation contract: export must therefore
378+ require an explicit, auditable acceptance rather than silently rederive or
379+ rename the value.
380+ """
381+ events : list [dict [str , Any ]] = []
382+ for variable in variables :
383+ source_name = str (variable ["source_name" ])
384+ compat_name = variable .get ("compat_name" )
385+ if compat_name is None or str (compat_name ).casefold () == source_name .casefold ():
386+ continue
387+ events .append ({
388+ "code" : "compatible-variable-name-not-exportable" ,
389+ "detail" : (
390+ "pyspssio exposes the source compatible variable name but its public "
391+ "writer API cannot set or guarantee preservation of that name."
392+ ),
393+ "details" : {
394+ "source_name" : source_name ,
395+ "compatible_name" : str (compat_name ),
396+ "physical_name" : str (variable ["physical_name" ]),
397+ },
398+ })
399+ return tuple (events )
400+
401+
402+ def _export_loss_report (
403+ dataset : dict [str , Any ], variables : list [dict [str , Any ]],
404+ ) -> tuple [dict [str , Any ], ...]:
369405 events : list [dict [str , Any ]] = []
370406 if _is_non_utf8_encoding (dataset .get ("source_encoding" )):
371407 events .append ({
372408 "code" : "source-encoding-not-preserved" ,
373409 "detail" : "The pyspssio writer has no source-encoding preservation contract for this legacy code page." ,
374410 "details" : {"source_encoding" : dataset .get ("source_encoding" )},
375411 })
412+ events .extend (_compat_name_loss_report (variables ))
376413 return tuple (events )
377414
378415
0 commit comments