|
13 | 13 | from ..sql.wide import create_wide_dataset, physical_name, read_fidelity_events, read_wide_dataset, record_export_operation |
14 | 14 |
|
15 | 15 |
|
| 16 | +_UTF8_ENCODINGS = {"UTF-8", "UTF8"} |
| 17 | + |
| 18 | + |
16 | 19 | def _variables(meta: Any, names: list[str]) -> list[dict[str, Any]]: |
17 | 20 | used = {"__case_ordinal"} |
18 | 21 | labels = dict(getattr(meta, "column_names_to_labels", {}) or {}) |
@@ -167,21 +170,30 @@ def _sha256(path: Path) -> str: |
167 | 170 | return digest.hexdigest() |
168 | 171 |
|
169 | 172 | def _import_loss_report(meta: Any) -> tuple[dict[str, str], ...]: |
170 | | - events = [{"code": "unobservable-source-dictionary-features", "detail": "The reader does not expose SPSS variable sets or custom file/variable attributes, so their presence cannot be established or preserved."}] |
| 173 | + events = [{"code": "unobservable-source-dictionary-features", "detail": "The reader does not expose SPSS variable sets, custom file/variable attributes, variable roles, or separate write formats, so their presence cannot be established or preserved."}] |
| 174 | + if _is_non_utf8_encoding(getattr(meta, "file_encoding", None)): |
| 175 | + events.append({"code": "source-encoding-not-preserved", "detail": "The SAV writer has no output-encoding option and writes UTF-8, so a non-UTF-8 source encoding cannot be restored."}) |
171 | 176 | if getattr(meta, "mr_sets", {}) or {}: |
172 | 177 | events.append({"code": "multiple-response-sets-not-exportable", "detail": "Multiple-response sets are catalogued but pyreadstat cannot write them back to SAV."}) |
173 | 178 | return tuple(events) |
174 | 179 |
|
175 | 180 |
|
176 | 181 | def _export_loss_report(dataset: dict[str, Any], variables: list[dict[str, Any]]) -> tuple[dict[str, str], ...]: |
177 | | - events = [{"code": "unobservable-source-dictionary-features", "detail": "Variable sets and custom file/variable attributes are not observable through the reader and therefore cannot be restored by export."}] |
| 182 | + events = [{"code": "unobservable-source-dictionary-features", "detail": "Variable sets, custom file/variable attributes, variable roles, and separate SPSS write formats are not observable through the reader and therefore cannot be restored by export."}] |
| 183 | + if _is_non_utf8_encoding(dataset.get("source_encoding")): |
| 184 | + events.append({"code": "source-encoding-not-preserved", "detail": "The SAV writer has no output-encoding option and writes UTF-8, so a non-UTF-8 source encoding cannot be restored."}) |
178 | 185 | if dataset["multiple_response_sets"] != "{}": |
179 | 186 | events.append({"code": "multiple-response-sets-not-exported", "detail": "The SAV writer has no multiple-response-set output capability."}) |
180 | 187 | if any(item.get("alignment") not in (None, "unknown") for item in variables): |
181 | 188 | events.append({"code": "variable-alignment-not-exported", "detail": "The SAV writer has no variable-alignment output capability."}) |
182 | 189 | return tuple(events) |
183 | 190 |
|
184 | 191 |
|
| 192 | +def _is_non_utf8_encoding(encoding: Any) -> bool: |
| 193 | + """Whether a known source encoding differs from the writer's UTF-8 output.""" |
| 194 | + return encoding is not None and str(encoding).strip().replace("_", "-").upper() not in _UTF8_ENCODINGS |
| 195 | + |
| 196 | + |
185 | 197 | def _merge_loss_reports(*reports: tuple[dict[str, str], ...]) -> tuple[dict[str, str], ...]: |
186 | 198 | """Keep one deterministic diagnostic per loss code across import and export.""" |
187 | 199 | events: dict[str, dict[str, str]] = {} |
|
0 commit comments