openstatspec 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openstatspec-0.1.0/.github/workflows/ci.yml +140 -0
- openstatspec-0.1.0/.github/workflows/release.yml +66 -0
- openstatspec-0.1.0/.gitignore +7 -0
- openstatspec-0.1.0/CHANGELOG.md +28 -0
- openstatspec-0.1.0/PKG-INFO +90 -0
- openstatspec-0.1.0/README.md +73 -0
- openstatspec-0.1.0/THIRD_PARTY_NOTICES.md +34 -0
- openstatspec-0.1.0/docs/architecture.md +14 -0
- openstatspec-0.1.0/docs/pyspssio-spike.md +45 -0
- openstatspec-0.1.0/docs/release-readiness.md +64 -0
- openstatspec-0.1.0/docs/sav-profile.md +57 -0
- openstatspec-0.1.0/pyproject.toml +28 -0
- openstatspec-0.1.0/src/openstatspec/__init__.py +6 -0
- openstatspec-0.1.0/src/openstatspec/api.py +95 -0
- openstatspec-0.1.0/src/openstatspec/cli.py +42 -0
- openstatspec-0.1.0/src/openstatspec/core/__init__.py +33 -0
- openstatspec-0.1.0/src/openstatspec/core/results.py +47 -0
- openstatspec-0.1.0/src/openstatspec/spss/__init__.py +22 -0
- openstatspec-0.1.0/src/openstatspec/spss/dictionary.py +180 -0
- openstatspec-0.1.0/src/openstatspec/spss/raw_dictionary.py +393 -0
- openstatspec-0.1.0/src/openstatspec/spss/sav.py +569 -0
- openstatspec-0.1.0/src/openstatspec/sql/__init__.py +14 -0
- openstatspec-0.1.0/src/openstatspec/sql/capabilities.py +260 -0
- openstatspec-0.1.0/src/openstatspec/sql/normative.py +513 -0
- openstatspec-0.1.0/src/openstatspec/sql/profiles.py +217 -0
- openstatspec-0.1.0/src/openstatspec/sql/wide.py +1087 -0
- openstatspec-0.1.0/tests/conformance.py +141 -0
- openstatspec-0.1.0/tests/test_atomic_import.py +115 -0
- openstatspec-0.1.0/tests/test_attribute_catalog.py +139 -0
- openstatspec-0.1.0/tests/test_cli.py +95 -0
- openstatspec-0.1.0/tests/test_document_round_trip.py +129 -0
- openstatspec-0.1.0/tests/test_loss_reports.py +156 -0
- openstatspec-0.1.0/tests/test_normative_catalog.py +170 -0
- openstatspec-0.1.0/tests/test_official_conformance.py +371 -0
- openstatspec-0.1.0/tests/test_pyspssio_catalog_authority.py +99 -0
- openstatspec-0.1.0/tests/test_sav_sqlite.py +217 -0
- openstatspec-0.1.0/tests/test_spss_catalog_preflight.py +168 -0
- openstatspec-0.1.0/tests/test_sql_profiles.py +112 -0
- openstatspec-0.1.0/tests/test_sql_services.py +77 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
env:
|
|
8
|
+
OPENSTATSPEC_SPECIFICATION_DIR: ${{ github.workspace }}/openstatspec-specification
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v7
|
|
19
|
+
- name: Checkout OpenStatSpec specification fixtures
|
|
20
|
+
uses: actions/checkout@v7
|
|
21
|
+
with: &specification-checkout
|
|
22
|
+
repository: OpenStatSpec/specification
|
|
23
|
+
ref: 6b9d1fc38f2f083c0ac5cf1c64874a6d07b95045
|
|
24
|
+
path: openstatspec-specification
|
|
25
|
+
- name: Checkout required SPSS engine
|
|
26
|
+
uses: actions/checkout@v7
|
|
27
|
+
with: &engine-checkout
|
|
28
|
+
repository: TonisOrmisson/pyspssio
|
|
29
|
+
ref: e069adf33c70bcd9e8e6ee495106479463a84fa2
|
|
30
|
+
path: openstatspec-pyspssio
|
|
31
|
+
- uses: actions/setup-python@v7
|
|
32
|
+
with:
|
|
33
|
+
python-version: ${{ matrix.python-version }}
|
|
34
|
+
- run: python -m pip install --upgrade pip
|
|
35
|
+
- run: python -m pip install ./openstatspec-pyspssio
|
|
36
|
+
- run: python -m pip install -e ".[dev]"
|
|
37
|
+
- run: python -m pytest -m "not services"
|
|
38
|
+
|
|
39
|
+
package-smoke:
|
|
40
|
+
name: Build and install package artifact
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v7
|
|
44
|
+
- name: Checkout OpenStatSpec specification fixtures
|
|
45
|
+
uses: actions/checkout@v7
|
|
46
|
+
with: *specification-checkout
|
|
47
|
+
- name: Checkout required SPSS engine
|
|
48
|
+
uses: actions/checkout@v7
|
|
49
|
+
with: *engine-checkout
|
|
50
|
+
- uses: actions/setup-python@v7
|
|
51
|
+
with:
|
|
52
|
+
python-version: "3.12"
|
|
53
|
+
- run: python -m pip install --upgrade pip build
|
|
54
|
+
- run: python -m build
|
|
55
|
+
- run: python -m venv /tmp/openstatspec-wheel-smoke
|
|
56
|
+
- run: /tmp/openstatspec-wheel-smoke/bin/python -m pip install ./openstatspec-pyspssio
|
|
57
|
+
- run: /tmp/openstatspec-wheel-smoke/bin/python -m pip install dist/*.whl
|
|
58
|
+
- run: /tmp/openstatspec-wheel-smoke/bin/openstatspec capabilities
|
|
59
|
+
|
|
60
|
+
postgres-integration:
|
|
61
|
+
name: PostgreSQL ${{ matrix.postgres }} integration
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
strategy:
|
|
64
|
+
fail-fast: false
|
|
65
|
+
matrix:
|
|
66
|
+
postgres: ["17", "18"]
|
|
67
|
+
services:
|
|
68
|
+
postgres:
|
|
69
|
+
image: postgres:${{ matrix.postgres }}
|
|
70
|
+
ports: ["5432:5432"]
|
|
71
|
+
env:
|
|
72
|
+
POSTGRES_DB: openstatspec
|
|
73
|
+
POSTGRES_USER: openstatspec
|
|
74
|
+
POSTGRES_PASSWORD: openstatspec
|
|
75
|
+
options: >-
|
|
76
|
+
--health-cmd "pg_isready -U openstatspec -d openstatspec"
|
|
77
|
+
--health-interval 10s --health-timeout 5s --health-retries 5
|
|
78
|
+
env:
|
|
79
|
+
OPENSTATSPEC_POSTGRES_URL: postgresql+psycopg://openstatspec:openstatspec@localhost:5432/openstatspec
|
|
80
|
+
steps: &sql-test-steps
|
|
81
|
+
- uses: actions/checkout@v7
|
|
82
|
+
- name: Checkout OpenStatSpec specification fixtures
|
|
83
|
+
uses: actions/checkout@v7
|
|
84
|
+
with: *specification-checkout
|
|
85
|
+
- name: Checkout required SPSS engine
|
|
86
|
+
uses: actions/checkout@v7
|
|
87
|
+
with: *engine-checkout
|
|
88
|
+
- uses: actions/setup-python@v7
|
|
89
|
+
with:
|
|
90
|
+
python-version: "3.12"
|
|
91
|
+
- run: python -m pip install --upgrade pip
|
|
92
|
+
- run: python -m pip install ./openstatspec-pyspssio
|
|
93
|
+
- run: python -m pip install -e ".[dev,sql]"
|
|
94
|
+
- run: python -m pytest -m services
|
|
95
|
+
|
|
96
|
+
mysql-integration:
|
|
97
|
+
name: MySQL ${{ matrix.mysql }} integration
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
strategy:
|
|
100
|
+
fail-fast: false
|
|
101
|
+
matrix:
|
|
102
|
+
mysql: ["8.4", "9.7"]
|
|
103
|
+
services:
|
|
104
|
+
mysql:
|
|
105
|
+
image: mysql:${{ matrix.mysql }}
|
|
106
|
+
ports: ["3306:3306"]
|
|
107
|
+
env:
|
|
108
|
+
MYSQL_DATABASE: openstatspec
|
|
109
|
+
MYSQL_USER: openstatspec
|
|
110
|
+
MYSQL_PASSWORD: openstatspec
|
|
111
|
+
MYSQL_ROOT_PASSWORD: root
|
|
112
|
+
options: >-
|
|
113
|
+
--health-cmd "mysqladmin ping -h 127.0.0.1 -uopenstatspec -popenstatspec"
|
|
114
|
+
--health-interval 10s --health-timeout 5s --health-retries 10
|
|
115
|
+
env:
|
|
116
|
+
OPENSTATSPEC_MYSQL_URL: mysql+pymysql://openstatspec:openstatspec@localhost:3306/openstatspec
|
|
117
|
+
steps: *sql-test-steps
|
|
118
|
+
|
|
119
|
+
mariadb-integration:
|
|
120
|
+
name: MariaDB ${{ matrix.mariadb }} integration
|
|
121
|
+
runs-on: ubuntu-latest
|
|
122
|
+
strategy:
|
|
123
|
+
fail-fast: false
|
|
124
|
+
matrix:
|
|
125
|
+
mariadb: ["11.4", "11.8", "12.3"]
|
|
126
|
+
services:
|
|
127
|
+
mariadb:
|
|
128
|
+
image: mariadb:${{ matrix.mariadb }}
|
|
129
|
+
ports: ["3306:3306"]
|
|
130
|
+
env:
|
|
131
|
+
MARIADB_DATABASE: openstatspec
|
|
132
|
+
MARIADB_USER: openstatspec
|
|
133
|
+
MARIADB_PASSWORD: openstatspec
|
|
134
|
+
MARIADB_ROOT_PASSWORD: root
|
|
135
|
+
options: >-
|
|
136
|
+
--health-cmd "healthcheck.sh --connect --innodb_initialized"
|
|
137
|
+
--health-interval 10s --health-timeout 5s --health-retries 10
|
|
138
|
+
env:
|
|
139
|
+
OPENSTATSPEC_MARIADB_URL: mysql+pymysql://openstatspec:openstatspec@localhost:3306/openstatspec
|
|
140
|
+
steps: *sql-test-steps
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
env:
|
|
4
|
+
OPENSTATSPEC_SPECIFICATION_DIR: ${{ github.workspace }}/openstatspec-specification
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags:
|
|
9
|
+
- "v*"
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v7
|
|
19
|
+
- name: Checkout normative specification fixtures
|
|
20
|
+
uses: actions/checkout@v7
|
|
21
|
+
with:
|
|
22
|
+
repository: OpenStatSpec/specification
|
|
23
|
+
ref: 6b9d1fc38f2f083c0ac5cf1c64874a6d07b95045
|
|
24
|
+
path: openstatspec-specification
|
|
25
|
+
- name: Checkout required SPSS engine
|
|
26
|
+
uses: actions/checkout@v7
|
|
27
|
+
with:
|
|
28
|
+
repository: TonisOrmisson/pyspssio
|
|
29
|
+
ref: e069adf33c70bcd9e8e6ee495106479463a84fa2
|
|
30
|
+
path: openstatspec-pyspssio
|
|
31
|
+
- uses: actions/setup-python@v7
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.13"
|
|
34
|
+
- run: python -m pip install --upgrade pip build
|
|
35
|
+
- name: Verify required SPSS engine is publicly installable
|
|
36
|
+
run: >-
|
|
37
|
+
python -m pip download --only-binary=:all: --no-deps
|
|
38
|
+
--dest /tmp/openstatspec-engine-release
|
|
39
|
+
openstatspec-pyspssio==0.5.1.post2
|
|
40
|
+
- run: python -m pip install ./openstatspec-pyspssio
|
|
41
|
+
- run: python -m pip install -e ".[dev]"
|
|
42
|
+
- run: python -m pytest -m "not services"
|
|
43
|
+
- run: python -m build
|
|
44
|
+
- run: python -m venv /tmp/openstatspec-release-smoke
|
|
45
|
+
- run: /tmp/openstatspec-release-smoke/bin/python -m pip install ./openstatspec-pyspssio
|
|
46
|
+
- run: /tmp/openstatspec-release-smoke/bin/python -m pip install dist/*.whl
|
|
47
|
+
- run: /tmp/openstatspec-release-smoke/bin/openstatspec capabilities
|
|
48
|
+
- uses: actions/upload-artifact@v7
|
|
49
|
+
with:
|
|
50
|
+
name: python-distributions
|
|
51
|
+
path: dist/
|
|
52
|
+
|
|
53
|
+
publish:
|
|
54
|
+
needs: build
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
environment:
|
|
57
|
+
name: pypi
|
|
58
|
+
url: https://pypi.org/p/openstatspec
|
|
59
|
+
permissions:
|
|
60
|
+
id-token: write
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/download-artifact@v8
|
|
63
|
+
with:
|
|
64
|
+
name: python-distributions
|
|
65
|
+
path: dist/
|
|
66
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this reference implementation are documented here.
|
|
4
|
+
|
|
5
|
+
## 0.1.0 — unreleased
|
|
6
|
+
|
|
7
|
+
First public reference implementation of the OpenStatSpec strict wide-table
|
|
8
|
+
SPSS profile.
|
|
9
|
+
|
|
10
|
+
### Included
|
|
11
|
+
|
|
12
|
+
- Import of unencrypted SAV and ZSAV sources into one dedicated wide SQL table
|
|
13
|
+
and its metadata catalog.
|
|
14
|
+
- Export of supported dataset semantics to SAV and ZSAV.
|
|
15
|
+
- SQLite, PostgreSQL, MySQL, and MariaDB profiles, including service-backed
|
|
16
|
+
PostgreSQL 17/18, MySQL 8.4/9.7, and MariaDB 11.4/11.8/12.3 CI coverage.
|
|
17
|
+
- Preflight checks for target profile limits, atomic imports, validation, a
|
|
18
|
+
command-line interface, and machine-readable capability and loss reports.
|
|
19
|
+
|
|
20
|
+
### Important boundaries
|
|
21
|
+
|
|
22
|
+
- This release promises semantic round-trip equivalence only for supported
|
|
23
|
+
features; it never promises byte-identical SAV/ZSAV output.
|
|
24
|
+
- The adapter requires explicit `allow_loss` consent before exporting known
|
|
25
|
+
unsupported or lossy dictionary semantics.
|
|
26
|
+
- Multiple-response sets, variable alignment, variable sets, and custom
|
|
27
|
+
attributes have explicit capability diagnostics; see the SAV profile.
|
|
28
|
+
- Encrypted SPSS files are not supported.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openstatspec
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reference adapter for the OpenStatSpec relational contract
|
|
5
|
+
Author: OpenStatSpec
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: openstatspec-pyspssio==0.5.1.post2
|
|
9
|
+
Requires-Dist: pandas>=2.2
|
|
10
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
13
|
+
Provides-Extra: sql
|
|
14
|
+
Requires-Dist: psycopg[binary]>=3.2; extra == 'sql'
|
|
15
|
+
Requires-Dist: pymysql>=1.1; extra == 'sql'
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# OpenStatSpec Python
|
|
19
|
+
|
|
20
|
+
The reference Python implementation of the OpenStatSpec specification.
|
|
21
|
+
|
|
22
|
+
This package implements the specification; it does not define or extend it.
|
|
23
|
+
The normative model lives in the `OpenStatSpec/specification` repository.
|
|
24
|
+
|
|
25
|
+
## Boundaries
|
|
26
|
+
|
|
27
|
+
For each supported import, one source dataset becomes one dedicated wide SQL
|
|
28
|
+
table. Cases are rows and source variables are physical SQL columns. The
|
|
29
|
+
singular UUID-keyed tables from the specification (`dataset`, `variable`,
|
|
30
|
+
`operation`, `fidelity_event`, and related metadata tables) are the public
|
|
31
|
+
catalog contract. Historical `*_catalog` tables are an internal compatibility
|
|
32
|
+
layer for the current exporter and are not the standard database interface.
|
|
33
|
+
The adapter does not reshape data, create EAV
|
|
34
|
+
or long-form tables, or harmonize studies or waves.
|
|
35
|
+
|
|
36
|
+
Unsupported source features, SQL targets, or export paths fail explicitly.
|
|
37
|
+
There is no silent truncation, type conversion, metadata loss, or partial
|
|
38
|
+
import.
|
|
39
|
+
|
|
40
|
+
## Package layout
|
|
41
|
+
|
|
42
|
+
- `openstatspec.core`: pure standard concepts, validation, versions,
|
|
43
|
+
capabilities, and loss reports.
|
|
44
|
+
- `openstatspec.sql`: database connection and wide-table/catalog operations.
|
|
45
|
+
- `openstatspec.spss`: SAV/ZSAV adapter boundary.
|
|
46
|
+
|
|
47
|
+
## Intended workflow
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from openstatspec import export_sav, import_sav
|
|
51
|
+
|
|
52
|
+
import_sav("responses.sav", database_url="postgresql+psycopg://user:password@server/database", dataset_id="responses-2026")
|
|
53
|
+
export_sav(database_url="postgresql+psycopg://user:password@server/database", dataset_id="responses-2026", destination="responses-roundtrip.sav")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
openstatspec import responses.sav --database-url postgresql+psycopg://... --dataset-id responses-2026
|
|
58
|
+
openstatspec export --database-url postgresql+psycopg://... --dataset-id responses-2026 --output responses-roundtrip.sav
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 0.1.0 support status
|
|
62
|
+
|
|
63
|
+
The adapter requires `openstatspec-pyspssio==0.5.1.post2` as its sole SPSS
|
|
64
|
+
engine. Its import module remains `pyspssio`; the exact source commit is recorded
|
|
65
|
+
in operation metadata. There is no fallback reader or writer. It supports unencrypted SAV and ZSAV import and
|
|
66
|
+
SAV/ZSAV export for the semantics exposed by that engine. SQLite is the local
|
|
67
|
+
reference path.
|
|
68
|
+
PostgreSQL, MySQL, and MariaDB are each covered by separate service-backed CI
|
|
69
|
+
conformance checks. Use these explicit SQLAlchemy URLs:
|
|
70
|
+
|
|
71
|
+
- SQLite: `sqlite:///dataset.sqlite`
|
|
72
|
+
- PostgreSQL: `postgresql+psycopg://user:password@host/database`
|
|
73
|
+
- MySQL/MariaDB: `mysql+pymysql://user:password@host/database`
|
|
74
|
+
|
|
75
|
+
Run `openstatspec capabilities` before an integration to inspect the
|
|
76
|
+
machine-readable feature matrix. Export is deliberately strict: if known
|
|
77
|
+
dictionary semantics cannot be reproduced, it stops until you pass the exact
|
|
78
|
+
diagnostic code with `--allow-loss`. This avoids silent loss while making an
|
|
79
|
+
intentional lossy export auditable.
|
|
80
|
+
|
|
81
|
+
The matrix is also available to Python callers as
|
|
82
|
+
`openstatspec.capability_matrix()`. It distinguishes supported semantics from
|
|
83
|
+
unobservable and fail-closed paths; see the SAV profile for the exact
|
|
84
|
+
the openstatspec-pyspssio boundary.
|
|
85
|
+
|
|
86
|
+
See [the SAV profile](docs/sav-profile.md) for feature boundaries and
|
|
87
|
+
[release readiness](docs/release-readiness.md) for the pre-tag checklist.
|
|
88
|
+
Read [third-party notices](THIRD_PARTY_NOTICES.md) before distributing a
|
|
89
|
+
bundled application: the required engine includes IBM redistributables under
|
|
90
|
+
separate terms.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# OpenStatSpec Python
|
|
2
|
+
|
|
3
|
+
The reference Python implementation of the OpenStatSpec specification.
|
|
4
|
+
|
|
5
|
+
This package implements the specification; it does not define or extend it.
|
|
6
|
+
The normative model lives in the `OpenStatSpec/specification` repository.
|
|
7
|
+
|
|
8
|
+
## Boundaries
|
|
9
|
+
|
|
10
|
+
For each supported import, one source dataset becomes one dedicated wide SQL
|
|
11
|
+
table. Cases are rows and source variables are physical SQL columns. The
|
|
12
|
+
singular UUID-keyed tables from the specification (`dataset`, `variable`,
|
|
13
|
+
`operation`, `fidelity_event`, and related metadata tables) are the public
|
|
14
|
+
catalog contract. Historical `*_catalog` tables are an internal compatibility
|
|
15
|
+
layer for the current exporter and are not the standard database interface.
|
|
16
|
+
The adapter does not reshape data, create EAV
|
|
17
|
+
or long-form tables, or harmonize studies or waves.
|
|
18
|
+
|
|
19
|
+
Unsupported source features, SQL targets, or export paths fail explicitly.
|
|
20
|
+
There is no silent truncation, type conversion, metadata loss, or partial
|
|
21
|
+
import.
|
|
22
|
+
|
|
23
|
+
## Package layout
|
|
24
|
+
|
|
25
|
+
- `openstatspec.core`: pure standard concepts, validation, versions,
|
|
26
|
+
capabilities, and loss reports.
|
|
27
|
+
- `openstatspec.sql`: database connection and wide-table/catalog operations.
|
|
28
|
+
- `openstatspec.spss`: SAV/ZSAV adapter boundary.
|
|
29
|
+
|
|
30
|
+
## Intended workflow
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from openstatspec import export_sav, import_sav
|
|
34
|
+
|
|
35
|
+
import_sav("responses.sav", database_url="postgresql+psycopg://user:password@server/database", dataset_id="responses-2026")
|
|
36
|
+
export_sav(database_url="postgresql+psycopg://user:password@server/database", dataset_id="responses-2026", destination="responses-roundtrip.sav")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
openstatspec import responses.sav --database-url postgresql+psycopg://... --dataset-id responses-2026
|
|
41
|
+
openstatspec export --database-url postgresql+psycopg://... --dataset-id responses-2026 --output responses-roundtrip.sav
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 0.1.0 support status
|
|
45
|
+
|
|
46
|
+
The adapter requires `openstatspec-pyspssio==0.5.1.post2` as its sole SPSS
|
|
47
|
+
engine. Its import module remains `pyspssio`; the exact source commit is recorded
|
|
48
|
+
in operation metadata. There is no fallback reader or writer. It supports unencrypted SAV and ZSAV import and
|
|
49
|
+
SAV/ZSAV export for the semantics exposed by that engine. SQLite is the local
|
|
50
|
+
reference path.
|
|
51
|
+
PostgreSQL, MySQL, and MariaDB are each covered by separate service-backed CI
|
|
52
|
+
conformance checks. Use these explicit SQLAlchemy URLs:
|
|
53
|
+
|
|
54
|
+
- SQLite: `sqlite:///dataset.sqlite`
|
|
55
|
+
- PostgreSQL: `postgresql+psycopg://user:password@host/database`
|
|
56
|
+
- MySQL/MariaDB: `mysql+pymysql://user:password@host/database`
|
|
57
|
+
|
|
58
|
+
Run `openstatspec capabilities` before an integration to inspect the
|
|
59
|
+
machine-readable feature matrix. Export is deliberately strict: if known
|
|
60
|
+
dictionary semantics cannot be reproduced, it stops until you pass the exact
|
|
61
|
+
diagnostic code with `--allow-loss`. This avoids silent loss while making an
|
|
62
|
+
intentional lossy export auditable.
|
|
63
|
+
|
|
64
|
+
The matrix is also available to Python callers as
|
|
65
|
+
`openstatspec.capability_matrix()`. It distinguishes supported semantics from
|
|
66
|
+
unobservable and fail-closed paths; see the SAV profile for the exact
|
|
67
|
+
the openstatspec-pyspssio boundary.
|
|
68
|
+
|
|
69
|
+
See [the SAV profile](docs/sav-profile.md) for feature boundaries and
|
|
70
|
+
[release readiness](docs/release-readiness.md) for the pre-tag checklist.
|
|
71
|
+
Read [third-party notices](THIRD_PARTY_NOTICES.md) before distributing a
|
|
72
|
+
bundled application: the required engine includes IBM redistributables under
|
|
73
|
+
separate terms.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
OpenStatSpec Python source code is licensed under Apache-2.0. Its required
|
|
4
|
+
SPSS engine is a separate third-party distribution:
|
|
5
|
+
|
|
6
|
+
| Component | Version | Applicable terms |
|
|
7
|
+
| --- | --- | --- |
|
|
8
|
+
| openstatspec-pyspssio (import module `pyspssio`) | 0.5.1.post2, source commit e069adf33c70bcd9e8e6ee495106479463a84fa2 | MIT for the Python wrapper, except for its IBM I/O Module files |
|
|
9
|
+
| IBM I/O Modules for IBM SPSS Statistics Data Files | bundled by openstatspec-pyspssio | IBM International License Agreement for Non-Warranted Programs and the accompanying License Information / REDIST files |
|
|
10
|
+
|
|
11
|
+
pyspssio ships the IBM licence, redistribution list, and third-party notices
|
|
12
|
+
inside its installed distribution, normally under pyspssio/spssio/license/.
|
|
13
|
+
Those materials are authoritative. They are not relicensed by this repository's
|
|
14
|
+
Apache-2.0 licence.
|
|
15
|
+
|
|
16
|
+
## Distribution responsibility
|
|
17
|
+
|
|
18
|
+
OpenStatSpec requires the exact openstatspec-pyspssio release declared in pyproject.toml; it has no alternate SPSS engine. This
|
|
19
|
+
repository does not vendor or alter the IBM binaries. Anyone who
|
|
20
|
+
distributes an application bundle containing the installed engine must review
|
|
21
|
+
and comply with the IBM materials supplied with that engine. In particular, the
|
|
22
|
+
IBM redistribution conditions require object-code-only distribution as part of
|
|
23
|
+
the distributor's application, preservation of notices, restrictions on use
|
|
24
|
+
and onward distribution, and end-user terms that protect IBM at least as much
|
|
25
|
+
as the IBM agreement. They also include other conditions, including support,
|
|
26
|
+
indemnity, and trademark restrictions.
|
|
27
|
+
|
|
28
|
+
Do not remove the pyspssio licence directory or notices from a bundled
|
|
29
|
+
installation. Do not describe IBM or its trademarks as endorsing OpenStatSpec.
|
|
30
|
+
|
|
31
|
+
This is a factual third-party notice, not legal advice. A party preparing a
|
|
32
|
+
binary or application distribution should have its own legal review of the
|
|
33
|
+
exact LA_en, REDIST, and notices files installed with the selected pyspssio
|
|
34
|
+
release.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
`openstatspec.core` contains only pure concepts defined by the standard:
|
|
4
|
+
versioning, validation concepts, capability declarations, and loss reports.
|
|
5
|
+
`openstatspec.sql` owns database-target and strict wide-table/catalog work.
|
|
6
|
+
`openstatspec.spss` owns SAV/ZSAV source and export behavior.
|
|
7
|
+
|
|
8
|
+
The public workflow remains database-connected. A supported import receives an
|
|
9
|
+
SPSS source, a database URL or connection, and a dataset identity. It creates
|
|
10
|
+
one dedicated wide data table plus separate catalog metadata. Export identifies
|
|
11
|
+
the database dataset and writes a supported SPSS file.
|
|
12
|
+
|
|
13
|
+
No layer may silently substitute EAV, long-form views, JSON, pivots, automatic
|
|
14
|
+
harmonization, truncation, or partial import for the standard's strict mapping.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# pyspssio engine assessment
|
|
2
|
+
|
|
3
|
+
**Status:** selected as the required, sole Python SPSS engine.
|
|
4
|
+
|
|
5
|
+
OpenStatSpec Python requires `openstatspec-pyspssio==0.5.1.post2`, built from
|
|
6
|
+
TonisOrmisson/pyspssio commit `e069adf33c70bcd9e8e6ee495106479463a84fa2`, for both SAV and ZSAV import and
|
|
7
|
+
export. There is no alternate SPSS engine.
|
|
8
|
+
|
|
9
|
+
## Probe result
|
|
10
|
+
|
|
11
|
+
A local round trip created both .sav and .zsav, then read each back. The
|
|
12
|
+
following metadata survived in the probe:
|
|
13
|
+
|
|
14
|
+
- UTF-8 encoding, variable names and storage widths;
|
|
15
|
+
- variable and value labels;
|
|
16
|
+
- numeric user-missing values;
|
|
17
|
+
- measurement levels, roles, alignments and display widths;
|
|
18
|
+
- file attributes and variable attributes;
|
|
19
|
+
- case-weight variable;
|
|
20
|
+
- a dichotomous multiple-response set.
|
|
21
|
+
|
|
22
|
+
The public API used was read_metadata(), read_sav(), and write_sav().
|
|
23
|
+
|
|
24
|
+
## Required conformance work
|
|
25
|
+
|
|
26
|
+
This engine selection does not relax the OpenStatSpec profile. Every claimed
|
|
27
|
+
feature must have a hermetic round-trip fixture. Current engine/API questions
|
|
28
|
+
that require explicit tests or upstream work include:
|
|
29
|
+
|
|
30
|
+
1. variable-set round trips;
|
|
31
|
+
2. independent print and write formats;
|
|
32
|
+
3. ordered document text;
|
|
33
|
+
4. legacy code-page files and unusual very-long-string boundaries beyond the covered 340-byte UTF-8 fixture.
|
|
34
|
+
|
|
35
|
+
Until a feature is proved, the adapter must report it as unsupported rather
|
|
36
|
+
than silently losing it.
|
|
37
|
+
|
|
38
|
+
## Licence boundary
|
|
39
|
+
|
|
40
|
+
The pyspssio wrapper is MIT, except for IBM I/O Module files bundled in its
|
|
41
|
+
wheel. Those redistributables have their own IBM terms. pyspssio is a required
|
|
42
|
+
runtime dependency, not an optional plugin; OpenStatSpec does not copy or
|
|
43
|
+
modify its IBM binaries. See [third-party notices](../THIRD_PARTY_NOTICES.md)
|
|
44
|
+
for the distribution boundary and the location of the authoritative installed
|
|
45
|
+
terms.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# 0.1.0 release readiness
|
|
2
|
+
|
|
3
|
+
This page records the expected release contract, not a publication event.
|
|
4
|
+
Creating a version tag remains a separate maintainer action.
|
|
5
|
+
|
|
6
|
+
## Supported workflow
|
|
7
|
+
|
|
8
|
+
For a supported unencrypted SAV or ZSAV source, the adapter imports one source
|
|
9
|
+
dataset into exactly one dedicated wide data table plus the complete singular,
|
|
10
|
+
UUID-keyed normative catalog from the SPSS profile. It can validate that
|
|
11
|
+
representation and export it back to SAV or ZSAV. Historical `*_catalog`
|
|
12
|
+
tables remain private compatibility storage and do not replace the normative
|
|
13
|
+
database contract.
|
|
14
|
+
|
|
15
|
+
## SQL profiles
|
|
16
|
+
|
|
17
|
+
| Profile | Connection URL | Verification |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| SQLite | `sqlite:///dataset.sqlite` | Python 3.11–3.14 local reference jobs |
|
|
20
|
+
| PostgreSQL | `postgresql+psycopg://…` | PostgreSQL 17 and 18 service jobs |
|
|
21
|
+
| MySQL | `mysql+pymysql://…` | MySQL 8.4 and 9.7 service jobs |
|
|
22
|
+
| MariaDB | `mysql+pymysql://…` | MariaDB 11.4, 11.8, and 12.3 service jobs |
|
|
23
|
+
|
|
24
|
+
Separate service matrices test the shared MySQL/MariaDB profile contract while
|
|
25
|
+
retaining distinct active-server identities and version claims. This does not
|
|
26
|
+
claim coverage for every server configuration. The machine-readable capability
|
|
27
|
+
declaration reports both the theoretical profile boundaries and effective
|
|
28
|
+
limits observed from the active connection.
|
|
29
|
+
|
|
30
|
+
## Export-loss policy
|
|
31
|
+
|
|
32
|
+
`capability_matrix()` and `openstatspec capabilities` expose the current
|
|
33
|
+
feature state. Before export, the adapter combines source and writer
|
|
34
|
+
diagnostics. An export with known loss fails unless the caller passes the
|
|
35
|
+
relevant diagnostic codes through `allow_loss` or repeats `--allow-loss CODE`
|
|
36
|
+
in the CLI.
|
|
37
|
+
|
|
38
|
+
The concrete pyspssio boundaries are documented in the [SAV profile](sav-profile.md).
|
|
39
|
+
The pinned OpenStatSpec fork preserves document text, file labels, legacy
|
|
40
|
+
compatible names, separate print/write formats, variable sets,
|
|
41
|
+
multiple-response sets, alignment, scalar attributes, and ordered
|
|
42
|
+
custom-attribute arrays. Non-UTF-8 output requires an explicit matching locale;
|
|
43
|
+
otherwise the strict export policy reports and blocks the loss. A caller
|
|
44
|
+
that supplies consent for an available loss code receives the
|
|
45
|
+
machine-readable loss report with the export result.
|
|
46
|
+
|
|
47
|
+
## Maintainer checks before tagging
|
|
48
|
+
|
|
49
|
+
1. Publish the pinned `openstatspec-pyspssio==0.5.1.post2` engine distribution
|
|
50
|
+
first and confirm that a clean environment can download it from PyPI. The
|
|
51
|
+
main package has no fallback SPSS engine.
|
|
52
|
+
2. Run `python -m pytest`.
|
|
53
|
+
3. Confirm the GitHub Actions matrix is green for PostgreSQL 17/18, MySQL
|
|
54
|
+
8.4/9.7, and MariaDB 11.4/11.8/12.3.
|
|
55
|
+
4. Build with `python -m build` and install the generated wheel in a clean
|
|
56
|
+
environment.
|
|
57
|
+
5. Confirm `openstatspec capabilities` reflects the intended support boundary.
|
|
58
|
+
6. Review this document, the README, and CHANGELOG for accurate scope.
|
|
59
|
+
|
|
60
|
+
The tag-triggered release workflow repeats the non-service test suite, builds
|
|
61
|
+
the distributions, and installs the wheel with the exact required SPSS engine
|
|
62
|
+
in a clean environment before it can reach the protected `pypi` environment.
|
|
63
|
+
It fails closed if that exact engine version is not already downloadable from
|
|
64
|
+
PyPI, preventing publication of an uninstallable OpenStatSpec release.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# SAV/ZSAV profile status
|
|
2
|
+
|
|
3
|
+
The Python adapter implements a strict, source-faithful wide-table mapping.
|
|
4
|
+
One SAV or ZSAV dataset becomes one table; every source variable is a physical
|
|
5
|
+
column and __case_ordinal preserves case order. The adapter never pivots,
|
|
6
|
+
chunks, or creates a cell/EAV representation.
|
|
7
|
+
|
|
8
|
+
## SPSS engine
|
|
9
|
+
|
|
10
|
+
OpenStatSpec Python requires `openstatspec-pyspssio==0.5.1.post2` as its sole
|
|
11
|
+
SPSS engine. There is no fallback reader or writer. The engine reads
|
|
12
|
+
unencrypted .sav and .zsav files, and writes both formats through the same implementation.
|
|
13
|
+
|
|
14
|
+
## Verified SQL profiles
|
|
15
|
+
|
|
16
|
+
SQLite has a local reference fixture. PostgreSQL, MySQL, and MariaDB have
|
|
17
|
+
separate service-backed conformance checks in GitHub Actions that import,
|
|
18
|
+
validate, and export the supported fixture. MySQL 8.4 and MariaDB 11.4 exercise
|
|
19
|
+
the shared profile contract; this does not claim coverage for every server
|
|
20
|
+
configuration. An import that exceeds a target's strict single-table column
|
|
21
|
+
limit fails before it creates a dataset.
|
|
22
|
+
|
|
23
|
+
## Fidelity contract
|
|
24
|
+
|
|
25
|
+
The adapter must preserve and validate only the source semantics that the
|
|
26
|
+
selected engine or the adapter's narrow, fail-closed type-6 document bridge exposes and that a conformance fixture proves. Unsupported
|
|
27
|
+
semantics fail explicitly; they never silently disappear. Encryption and
|
|
28
|
+
byte-identical reproduction are outside the contract. The contract is semantic
|
|
29
|
+
equivalence of supported values, order, and dictionary metadata.
|
|
30
|
+
|
|
31
|
+
## Engine capability boundary
|
|
32
|
+
|
|
33
|
+
The `openstatspec capabilities` command and the
|
|
34
|
+
`openstatspec.capability_matrix()` function are the machine-readable declaration
|
|
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.
|
|
36
|
+
|
|
37
|
+
| SPSS semantic | Pinned pyspssio status | Behaviour |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| File label | Supported through openstatspec-pyspssio | The adapter persists it in the dataset catalog and writes it through the IBM I/O identifier-string API. |
|
|
40
|
+
| Very-long UTF-8 strings | Supported | The SAV and ZSAV fixture preserves a 340-byte, multi-byte UTF-8 string through the SQL catalog and back. |
|
|
41
|
+
| Ordered document text | Supported through a strict type-6 dictionary bridge plus the pinned fork | Import stores normalized document rows; export creates a temporary UTF-8 SAV source and has IBM I/O copy the records into SAV or ZSAV without invalidating ZSAV dictionary offsets. |
|
|
42
|
+
| Print and write formats independently | Supported as raw IBM I/O tuples | The adapter stores both tuples separately and writes them without collapsing either value. |
|
|
43
|
+
| Variable sets | Supported through raw IBM I/O | The adapter stores source sets in the extension catalog and writes them through the raw dictionary setter. Invalid target definitions fail before data are written. |
|
|
44
|
+
| Legacy compatible variable names | Supported through the strict dictionary bridge | The adapter stores the short-name mapping and rewrites both the fixed variable record and long-name extension before export. |
|
|
45
|
+
| Source encoding | UTF-8 by default; legacy code page with an explicit OS locale | The caller supplies legacy_locale and the writer verifies that the emitted encoding matches the stored source encoding. Without it, export fails before output creation. |
|
|
46
|
+
| Custom attributes with one value | Supported | File and variable scalar attributes round trip through the normalized attribute catalog. |
|
|
47
|
+
| Custom-attribute value arrays | Supported through raw IBM I/O | The adapter represents an array as IBM SPSS `Name[1]`, `Name[2]`, … members and reconstructs the ordered array in the normalized catalog. |
|
|
48
|
+
|
|
49
|
+
The remaining loss codes can be supplied through `allow_loss` only when a
|
|
50
|
+
caller intentionally accepts the documented, machine-readable loss report.
|
|
51
|
+
|
|
52
|
+
## Distribution
|
|
53
|
+
|
|
54
|
+
The required engine includes IBM I/O Module redistributables under terms that
|
|
55
|
+
are separate from OpenStatSpec's Apache-2.0 source licence. See
|
|
56
|
+
[third-party notices](../THIRD_PARTY_NOTICES.md) before distributing a bundled
|
|
57
|
+
application.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "openstatspec"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Reference adapter for the OpenStatSpec relational contract"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = {text = "Apache-2.0"}
|
|
12
|
+
authors = [{name = "OpenStatSpec"}]
|
|
13
|
+
dependencies = ["SQLAlchemy>=2.0", "pandas>=2.2", "openstatspec-pyspssio==0.5.1.post2"]
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
dev = ["pytest>=8"]
|
|
17
|
+
sql = ["psycopg[binary]>=3.2", "PyMySQL>=1.1"]
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
openstatspec = "openstatspec.cli:main"
|
|
21
|
+
|
|
22
|
+
[tool.pytest.ini_options]
|
|
23
|
+
testpaths = ["tests"]
|
|
24
|
+
addopts = "-q"
|
|
25
|
+
markers = ["services: requires a configured PostgreSQL or MySQL/MariaDB service"]
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.wheel]
|
|
28
|
+
packages = ["src/openstatspec"]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Public API for the OpenStatSpec Python reference implementation."""
|
|
2
|
+
|
|
3
|
+
from .api import capabilities, capability_matrix, export_sav, import_sav, inspect, validate
|
|
4
|
+
from .core import CapabilityDeclaration, LossReport, UnsupportedOperationError
|
|
5
|
+
|
|
6
|
+
__all__ = ["CapabilityDeclaration", "LossReport", "UnsupportedOperationError", "capabilities", "capability_matrix", "export_sav", "import_sav", "inspect", "validate"]
|