Skip to content

Commit 1b937aa

Browse files
committed
Record pinned SPSS engine identity
1 parent 8967628 commit 1b937aa

6 files changed

Lines changed: 24 additions & 4 deletions

File tree

‎docs/sav-profile.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ equivalence of supported values, order, and dictionary metadata.
3232

3333
The `openstatspec capabilities` command and the
3434
`openstatspec.capability_matrix()` function are the machine-readable declaration
35-
of this boundary. They deliberately distinguish a supported feature from a
36-
feature that the underlying engine cannot observe or write faithfully.
35+
of this boundary. It records the pinned source commit and installed engine version, and the adapter stores the same identity in every import and export operation record. The matrix deliberately distinguishes a supported feature from a feature that the underlying engine cannot observe or write faithfully.
3736

3837
| SPSS semantic | Pinned pyspssio status | Behaviour |
3938
| --- | --- | --- |

‎src/openstatspec/api.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .core import CapabilityDeclaration
88
from .core.results import result
99
from .spss import export_dataset, import_dataset, inspect_source
10+
from .spss.sav import engine_identity
1011
from .sql import declared_profiles, validate_dataset
1112

1213

@@ -21,6 +22,7 @@ def capability_matrix() -> Mapping[str, Any]:
2122
fail-closed feature has no faithful writer route at all.
2223
"""
2324
return {
25+
"engine": engine_identity(),
2426
"spss": {
2527
"values": "supported", "variable_labels": "supported",
2628
"value_labels": "supported",

‎src/openstatspec/spss/sav.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@
3939
)
4040

4141
_UTF8_ENCODINGS = {"UTF-8", "UTF8"}
42+
def engine_identity() -> dict[str, str]:
43+
"""Return the exact pinned SPSS engine identity for audit records."""
44+
return {
45+
"package": "pyspssio",
46+
"distribution": "TonisOrmisson/pyspssio",
47+
"pinned_commit": "0b3f879",
48+
"installed_version": str(pyspssio.__version__),
49+
}
4250

4351

4452
def _dictionary(source_path: Path) -> tuple[dict[str, Any], dict[str, Any]]:
@@ -175,6 +183,7 @@ def inspect_sav(source: str | Path) -> dict[str, Any]:
175183
loss_report = _merge_loss_reports(tuple(loss_report.values()), _compat_name_loss_report(variables))
176184
return {
177185
"source_format": source_path.suffix[1:].upper(),
186+
"engine": engine_identity(),
178187
"source_name": source_path.name,
179188
"source_sha256": _sha256(source_path),
180189
"source_encoding": metadata.get("encoding"),
@@ -224,6 +233,7 @@ def import_sav_dataset(*, source: str | Path, database_url: str, dataset_id: str
224233
if metadata.get("_var_sets") else {}
225234
),
226235
fidelity_events=loss_report,
236+
operation_details={"engine": engine_identity()},
227237
)
228238
return {**result, "loss_report": loss_report}
229239

@@ -273,6 +283,7 @@ def export_sav_dataset(
273283
allowed_fidelity_events=tuple(
274284
event for event in loss_report if event["code"] in allow_loss
275285
),
286+
operation_details={"engine": engine_identity()},
276287
)
277288
return {
278289
"dataset_id": dataset_id,

‎src/openstatspec/sql/wide.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ def create_wide_dataset(
737737
multiple_response_sets: str = "{}",
738738
source_extensions: Mapping[str, Any] | None = None,
739739
fidelity_events: Iterable[Mapping[str, Any]] = (),
740+
operation_details: Mapping[str, Any] | None = None,
740741
) -> dict[str, Any]:
741742
profile = validate_connection_url(database_url)
742743
engine = create_engine(database_url)
@@ -773,7 +774,7 @@ def create_wide_dataset(
773774
_migrate_catalog_columns(connection, datasets, variable_catalog, multiple_response_catalog)
774775
connection.execute(insert(operation_catalog).values(
775776
operation_id=operation_id, direction="import", status="running", dataset_id=dataset_id,
776-
source=source_name, created_at=_now(), details=json.dumps({"variable_count": len(variables)}, sort_keys=True),
777+
source=source_name, created_at=_now(), details=json.dumps({"variable_count": len(variables), **dict(operation_details or {})}, sort_keys=True),
777778
))
778779
if connection.execute(select(datasets.c.dataset_id).where(datasets.c.dataset_id == dataset_id)).first():
779780
raise ValueError(f"Dataset {dataset_id!r} already exists; imports never overwrite a dataset.")
@@ -958,6 +959,7 @@ def read_fidelity_events(*, database_url: str, dataset_id: str) -> tuple[dict[st
958959
def record_export_operation(
959960
*, database_url: str, dataset_id: str, destination: str,
960961
allowed_fidelity_events: Iterable[Mapping[str, Any]],
962+
operation_details: Mapping[str, Any] | None = None,
961963
) -> str:
962964
"""Persist a completed export and the fidelity loss explicitly accepted by its caller."""
963965
engine = create_engine(database_url)
@@ -971,7 +973,7 @@ def record_export_operation(
971973
connection.execute(insert(operations).values(
972974
operation_id=operation_id, direction="export", status="succeeded", dataset_id=dataset_id,
973975
destination=destination, created_at=_now(), completed_at=_now(),
974-
details=json.dumps({"allow_loss": [event["code"] for event in events]}, sort_keys=True),
976+
details=json.dumps({"allow_loss": [event["code"] for event in events], **dict(operation_details or {})}, sort_keys=True),
975977
))
976978
rows = _event_rows(
977979
operation_id=operation_id, dataset_id=dataset_id, direction="export",

‎tests/test_cli.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def test_cli_import_inspect_validate_and_export_emit_json(tmp_path, capsys) -> N
1717
assert openstatspec.cli.main(["inspect", str(source)]) == 0
1818
inspected = json.loads(capsys.readouterr().out)
1919
assert inspected["source_format"] == "SAV"
20+
assert inspected["engine"]["pinned_commit"] == "0b3f879"
2021
assert inspected["source_sha256"]
2122
assert {event["code"] for event in inspected["loss_report"]} == {
2223
"documents-unobservable",
@@ -39,6 +40,7 @@ def test_cli_import_inspect_validate_and_export_emit_json(tmp_path, capsys) -> N
3940

4041
def test_capability_matrix_is_public_and_cli_matches_engine_boundary(capsys) -> None:
4142
matrix = openstatspec.capability_matrix()
43+
assert matrix["engine"]["pinned_commit"] == "0b3f879"
4244

4345
assert matrix["spss"] == {
4446
"values": "supported",

‎tests/test_loss_reports.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,17 @@ def test_persisted_import_fidelity_events_require_consent_after_reopen(tmp_path)
2525
assert {diagnostic.code for diagnostic in imported.diagnostics} == set(_REQUIRED_ENGINE_LOSS)
2626
connection = sqlite3.connect(database_path)
2727
assert {row[0] for row in connection.execute("select code from fidelity_event_catalog")} == set(_REQUIRED_ENGINE_LOSS)
28+
import_details = json.loads(connection.execute("select details from operation_catalog order by created_at limit 1").fetchone()[0])
29+
assert import_details["engine"]["pinned_commit"] == "0b3f879"
2830

2931
with pytest.raises(UnsupportedOperationError, match="documents-unobservable"):
3032
openstatspec.export_sav(database_url=database, dataset_id="persisted", destination=blocked)
3133
assert not blocked.exists()
3234

3335
exported = openstatspec.export_sav(database_url=database, dataset_id="persisted", destination=approved, allow_loss=_REQUIRED_ENGINE_LOSS)
3436
assert approved.exists()
37+
export_details = json.loads(connection.execute("select details from operation_catalog where operation_id = ?", (exported["operation_id"],)).fetchone()[0])
38+
assert export_details["engine"]["installed_version"] == pyspssio.__version__
3539
assert {diagnostic.code for diagnostic in exported.diagnostics} == set(_REQUIRED_ENGINE_LOSS)
3640

3741

0 commit comments

Comments
 (0)