qualtrics 0.1.1__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.
Files changed (41) hide show
  1. qualtrics-0.1.1/.gitignore +201 -0
  2. qualtrics-0.1.1/PKG-INFO +351 -0
  3. qualtrics-0.1.1/README.md +338 -0
  4. qualtrics-0.1.1/pyproject.toml +120 -0
  5. qualtrics-0.1.1/src/qualtrics/__init__.py +18 -0
  6. qualtrics-0.1.1/src/qualtrics/__main__.py +3 -0
  7. qualtrics-0.1.1/src/qualtrics/analytics/__init__.py +3 -0
  8. qualtrics-0.1.1/src/qualtrics/analytics/report.py +134 -0
  9. qualtrics-0.1.1/src/qualtrics/api/__init__.py +49 -0
  10. qualtrics-0.1.1/src/qualtrics/api/client.py +168 -0
  11. qualtrics-0.1.1/src/qualtrics/api/domains/__init__.py +10 -0
  12. qualtrics-0.1.1/src/qualtrics/api/domains/base.py +11 -0
  13. qualtrics-0.1.1/src/qualtrics/api/domains/response_exports.py +203 -0
  14. qualtrics-0.1.1/src/qualtrics/api/domains/survey_definitions.py +31 -0
  15. qualtrics-0.1.1/src/qualtrics/api/domains/surveys.py +34 -0
  16. qualtrics-0.1.1/src/qualtrics/api/exceptions.py +13 -0
  17. qualtrics-0.1.1/src/qualtrics/api/models.py +128 -0
  18. qualtrics-0.1.1/src/qualtrics/api/settings.py +13 -0
  19. qualtrics-0.1.1/src/qualtrics/cli/__init__.py +3 -0
  20. qualtrics-0.1.1/src/qualtrics/cli/api.py +70 -0
  21. qualtrics-0.1.1/src/qualtrics/cli/app.py +10 -0
  22. qualtrics-0.1.1/src/qualtrics/cli/build.py +40 -0
  23. qualtrics-0.1.1/src/qualtrics/cli/report.py +40 -0
  24. qualtrics-0.1.1/src/qualtrics/models/__init__.py +4 -0
  25. qualtrics-0.1.1/src/qualtrics/models/entities.py +29 -0
  26. qualtrics-0.1.1/src/qualtrics/models/entity_set.py +19 -0
  27. qualtrics-0.1.1/src/qualtrics/parsers/__init__.py +3 -0
  28. qualtrics-0.1.1/src/qualtrics/parsers/identity.py +52 -0
  29. qualtrics-0.1.1/src/qualtrics/parsers/paths.py +16 -0
  30. qualtrics-0.1.1/src/qualtrics/parsers/qsf.py +64 -0
  31. qualtrics-0.1.1/src/qualtrics/parsers/survey.py +213 -0
  32. qualtrics-0.1.1/src/qualtrics/py.typed +0 -0
  33. qualtrics-0.1.1/src/qualtrics/reporting/__init__.py +3 -0
  34. qualtrics-0.1.1/src/qualtrics/reporting/assets.py +6 -0
  35. qualtrics-0.1.1/src/qualtrics/reporting/report.py +337 -0
  36. qualtrics-0.1.1/src/qualtrics/reporting/static/report.css +70 -0
  37. qualtrics-0.1.1/src/qualtrics/reporting/static/report.js +48 -0
  38. qualtrics-0.1.1/src/qualtrics/serialization/__init__.py +3 -0
  39. qualtrics-0.1.1/src/qualtrics/serialization/io.py +61 -0
  40. qualtrics-0.1.1/src/qualtrics/services/__init__.py +3 -0
  41. qualtrics-0.1.1/src/qualtrics/version.py +1 -0
@@ -0,0 +1,201 @@
1
+ # Created by https://www.toptal.com/developers/gitignore/api/python,venv
2
+ # Edit at https://www.toptal.com/developers/gitignore?templates=python,venv
3
+
4
+ ### Python ###
5
+ # Byte-compiled / optimized / DLL files
6
+ __pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+
10
+ # C extensions
11
+ *.so
12
+
13
+ # Distribution / packaging
14
+ .DS_Store
15
+ .Python
16
+ build/
17
+ develop-eggs/
18
+ dist/
19
+ downloads/
20
+ eggs/
21
+ .eggs/
22
+ lib/
23
+ lib64/
24
+ parts/
25
+ sdist/
26
+ var/
27
+ wheels/
28
+ share/python-wheels/
29
+ *.egg-info/
30
+ .installed.cfg
31
+ *.egg
32
+ MANIFEST
33
+
34
+ # PyInstaller
35
+ # Usually these files are written by a python script from a template
36
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
37
+ *.manifest
38
+ *.spec
39
+
40
+ # Installer logs
41
+ pip-log.txt
42
+ pip-delete-this-directory.txt
43
+
44
+ # Unit test / coverage reports
45
+ htmlcov/
46
+ .tox/
47
+ .nox/
48
+ .coverage
49
+ .coverage.*
50
+ .cache
51
+ nosetests.xml
52
+ coverage.xml
53
+ *.cover
54
+ *.py,cover
55
+ .hypothesis/
56
+ .pytest_cache/
57
+ cover/
58
+ reports/
59
+
60
+ # Translations
61
+ *.mo
62
+ *.pot
63
+
64
+ # Django stuff:
65
+ *.log
66
+ local_settings.py
67
+ db.sqlite3
68
+ db.sqlite3-journal
69
+
70
+ # Flask stuff:
71
+ instance/
72
+ .webassets-cache
73
+
74
+ # Scrapy stuff:
75
+ .scrapy
76
+
77
+ # Sphinx documentation
78
+ docs/_build/
79
+
80
+ # PyBuilder
81
+ .pybuilder/
82
+ target/
83
+
84
+ # Jupyter Notebook
85
+ .ipynb_checkpoints
86
+
87
+ # IPython
88
+ profile_default/
89
+ ipython_config.py
90
+
91
+ # pyenv
92
+ # For a library or package, you might want to ignore these files since the code is
93
+ # intended to run in multiple environments; otherwise, check them in:
94
+ # .python-version
95
+
96
+ # pipenv
97
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
98
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
99
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
100
+ # install all needed dependencies.
101
+ #Pipfile.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/#use-with-ide
116
+ .pdm.toml
117
+
118
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
119
+ __pypackages__/
120
+
121
+ # Celery stuff
122
+ celerybeat-schedule
123
+ celerybeat.pid
124
+
125
+ # SageMath parsed files
126
+ *.sage.py
127
+
128
+ # Environments
129
+ *.env
130
+ .env
131
+ .venv
132
+ env/
133
+ venv/
134
+ ENV/
135
+ env.bak/
136
+ venv.bak/
137
+
138
+ # Spyder project settings
139
+ .spyderproject
140
+ .spyproject
141
+
142
+ # Rope project settings
143
+ .ropeproject
144
+
145
+ # mkdocs documentation
146
+ /site
147
+
148
+ # mypy
149
+ .mypy_cache/
150
+ .dmypy.json
151
+ dmypy.json
152
+
153
+ # Pyre type checker
154
+ .pyre/
155
+
156
+ # pytype static type analyzer
157
+ .pytype/
158
+
159
+ # Cython debug symbols
160
+ cython_debug/
161
+
162
+ # PyCharm
163
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
164
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
165
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
166
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
167
+ #.idea/
168
+
169
+ ### Python Patch ###
170
+ # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
171
+ poetry.toml
172
+
173
+ # ruff
174
+ .ruff_cache/
175
+
176
+ # LSP config files
177
+ pyrightconfig.json
178
+
179
+ ### venv ###
180
+ # Virtualenv
181
+ # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
182
+ [Bb]in
183
+ [Ii]nclude
184
+ [Ll]ib
185
+ [Ll]ib64
186
+ [Ll]ocal
187
+ [Ss]cripts
188
+ pyvenv.cfg
189
+ pip-selfcheck.json
190
+
191
+ # Visual Studio Code #
192
+ .vscode
193
+ !.vscode/settings.json
194
+ !.vscode/tasks.json
195
+ !.vscode/launch.json
196
+ !.vscode/extensions.json
197
+ .history
198
+
199
+ # Repository maintenance scripts are source files, not virtualenv scripts.
200
+ !/scripts/
201
+ !/scripts/*.py
@@ -0,0 +1,351 @@
1
+ Metadata-Version: 2.5
2
+ Name: qualtrics
3
+ Version: 0.1.1
4
+ Summary: A typed Qualtrics SDK and offline survey data toolkit
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: httpx<1,>=0.27
7
+ Requires-Dist: pydantic-settings<3,>=2.4
8
+ Requires-Dist: pydantic<3,>=2.8
9
+ Requires-Dist: typer<1,>=0.12
10
+ Provides-Extra: parquet
11
+ Requires-Dist: pyarrow>=17; extra == 'parquet'
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Qualtrics
15
+
16
+ `qualtrics` is a Python library and Typer CLI for working with
17
+ Qualtrics surveys end to end:
18
+
19
+ - list and update surveys through Qualtrics API v3;
20
+ - import responses and download response exports;
21
+ - parse one or many Qualtrics CSV exports, with or without QSF metadata;
22
+ - preserve survey, question, concrete field, block, and loop identities;
23
+ - build survey-local entities and cross-survey canonical catalogs;
24
+ - write JSON, CSV, or Parquet datasets;
25
+ - calculate question-type-aware analytics and data-quality signals; and
26
+ - create a self-contained, interactive HTML report.
27
+
28
+ It is useful for conventional surveys and for administrative data-intake
29
+ workflows where different people answer different sections—for example
30
+ institutional reporting, accreditation, compliance, annual collections, grant
31
+ reporting, and multi-stakeholder intake forms.
32
+
33
+ ## Why CSV and QSF are both useful
34
+
35
+ A Qualtrics response CSV normally begins with three header rows:
36
+
37
+ 1. exported field name, such as `4_cat_train`;
38
+ 2. question and field text;
39
+ 3. metadata such as `{"ImportId":"4_QID30"}`.
40
+
41
+ Multi-field, matrix, form, looped, and text-entry questions can create several
42
+ CSV fields for one logical survey question. The toolkit never identifies a
43
+ concrete answer using stripped question text. It retains the full field name,
44
+ ImportId, suffix, and column index.
45
+
46
+ QSF metadata is optional but strongly recommended. It supplies definitive
47
+ question types, complete question text, choices, survey blocks, and loop
48
+ configuration. Without QSF, the toolkit infers what it safely can from the CSV.
49
+ When `--qsf` is omitted, the toolkit automatically uses a `.qsf` file beside
50
+ the CSV when both files have the same filename stem. Extension matching is
51
+ case-insensitive, so `annual-survey.csv` can match `annual-survey.QSF`.
52
+
53
+ For a manual export in Qualtrics:
54
+
55
+ 1. Open **Data & Analysis → Export & Import → Export Data**.
56
+ 2. Choose CSV and, for readable reports, enable choice text rather than numeric
57
+ codes.
58
+ 3. Download the QSF from **Survey → Tools → Import/Export → Export Survey**.
59
+ 4. Give matching CSV and QSF files the same sortable base name.
60
+
61
+ ## Installation with uv
62
+
63
+ From this repository:
64
+
65
+ ```bash
66
+ uv sync
67
+ ```
68
+
69
+ Run the CLI without installing it globally:
70
+
71
+ ```bash
72
+ uv run qualtrics --help
73
+ ```
74
+
75
+ Parquet support:
76
+
77
+ ```bash
78
+ uv sync --extra parquet
79
+ ```
80
+
81
+ ## Build entity datasets
82
+
83
+ One survey:
84
+
85
+ ```bash
86
+ uv run qualtrics build \
87
+ survey.csv --qsf survey.qsf --output entities --format json
88
+ ```
89
+
90
+ With matching files such as `survey.csv` and `survey.qsf`, `--qsf` is optional:
91
+
92
+ ```bash
93
+ uv run qualtrics build \
94
+ survey.csv --output entities --format json
95
+ ```
96
+
97
+ Multiple surveys:
98
+
99
+ ```bash
100
+ uv run qualtrics build \
101
+ survey-v1.csv survey-v2.csv \
102
+ --qsf survey-v1.qsf --qsf survey-v2.qsf \
103
+ --output entities --format parquet
104
+ ```
105
+
106
+ Directories are supported too. CSV and QSF directory contents are sorted by
107
+ filename and paired in that order:
108
+
109
+ ```bash
110
+ uv run qualtrics build \
111
+ ./exports/csv --qsf ./exports/qsf --output entities
112
+ ```
113
+
114
+ The output contains:
115
+
116
+ | Entity | Identity and purpose |
117
+ | --- | --- |
118
+ | `surveys` | Survey/version metadata |
119
+ | `sections` | Survey-local Qualtrics blocks and their display order |
120
+ | `question_catalog` | Canonical logical questions shared across surveys |
121
+ | `question_field_catalog` | Canonical fields shared across surveys |
122
+ | `questions` | Survey-local question occurrences, types, blocks, and order |
123
+ | `question_fields` | Concrete CSV columns and ImportIds |
124
+ | `answer_options` | Defined respondent options—not Meta Info fields |
125
+ | `responses` | Response metadata |
126
+ | `response_answers` | Answers linked through survey, response, question, and field |
127
+
128
+ The central answer relationship is:
129
+
130
+ ```text
131
+ response_answer
132
+ → (survey_id, response_id)
133
+ → (survey_id, question_id, field_id)
134
+ → question_catalog_id / question_field_catalog_id
135
+ ```
136
+
137
+ Entity records describe survey data only. Pipeline lineage such as ingestion
138
+ run IDs belongs in the surrounding platform manifest or control tables and is
139
+ therefore not added by the parser.
140
+
141
+ `parse_survey` also accepts wildcard paths. This is useful for run-oriented
142
+ lakehouse layouts where each survey has its own folder:
143
+
144
+ ```python
145
+ from qualtrics import parse_survey
146
+
147
+ entities = parse_survey("/lakehouse/default/Files/qualtrics/run-1/*/*.csv")
148
+ ```
149
+
150
+ Each CSV is paired automatically with a same-stem `.qsf` or `.json` definition
151
+ in its directory. Prefer a single-level pattern like `*/*.csv`; recursive
152
+ patterns may also select translated CSV files stored below `translations/`.
153
+
154
+ ## Generate an HTML report
155
+
156
+ ```bash
157
+ uv run qualtrics report \
158
+ --folder entities --output report.html
159
+ ```
160
+
161
+ The report is one portable HTML file with embedded styling and behavior. It
162
+ includes survey selection, response and question filters, blocks, metadata,
163
+ coverage, question-type-aware analytics, and per-survey data-quality findings.
164
+ All source values are HTML-escaped.
165
+
166
+ ## Qualtrics API SDK
167
+
168
+ Set credentials without putting the token in shell history:
169
+
170
+ ```bash
171
+ export QUALTRICS_API_TOKEN="..."
172
+ export QUALTRICS_DATA_CENTER="ca1"
173
+ ```
174
+
175
+ Your data-center identifier is the first part of the Qualtrics host used by
176
+ your account. You may instead set `QUALTRICS_BASE_URL` for a custom API base.
177
+ Explicit constructor arguments override matching environment variables:
178
+
179
+ ```python
180
+ from qualtrics import QualtricsClient
181
+
182
+ client = QualtricsClient() # reads QUALTRICS_API_TOKEN and connection settings
183
+ client = QualtricsClient(api_token="...", data_center="ca1")
184
+ ```
185
+
186
+ List surveys:
187
+
188
+ ```bash
189
+ uv run qualtrics api surveys
190
+ ```
191
+
192
+ Export labeled CSV responses and name the ZIP after the survey ID:
193
+
194
+ ```bash
195
+ uv run qualtrics api export SV_123 --output exports --labels \
196
+ --naming survey_id
197
+ ```
198
+
199
+ Import a UTF-8 CSV response file and wait for processing:
200
+
201
+ ```bash
202
+ uv run qualtrics api import SV_123 responses.csv
203
+ ```
204
+
205
+ Naming strategies are `qualtrics`, `survey_id`, `survey_name`, and `custom`.
206
+ For custom naming, add `--filename my-export`. An explicit output file path
207
+ always takes precedence.
208
+
209
+ Python usage:
210
+
211
+ ```python
212
+ from pathlib import Path
213
+
214
+ from qualtrics import QualtricsClient
215
+ from qualtrics.api import FilenameStrategy, ResponseExportRequest
216
+
217
+ # With no arguments, credentials are read from QUALTRICS_* variables.
218
+ with QualtricsClient() as client:
219
+ surveys = list(client.surveys.iter())
220
+ result = client.responses.export(
221
+ surveys[0].id,
222
+ Path("exports"),
223
+ options=ResponseExportRequest(format="csv", use_labels=True),
224
+ naming=FilenameStrategy.SURVEY_NAME,
225
+ survey_name=surveys[0].name,
226
+ )
227
+ print(result.path)
228
+ ```
229
+
230
+ The context manager is recommended because it closes the underlying HTTPX
231
+ connection pool deterministically. Long-lived applications can instead create
232
+ one client, reuse it, and call `client.close()` during application shutdown.
233
+
234
+ The export workflow starts an asynchronous job, polls its `progressId`, obtains
235
+ the resulting `fileId`, and downloads the binary file. The low-level
236
+ `client.request(...)` method provides access to API v3 endpoints not yet covered
237
+ by a typed resource method.
238
+
239
+ The API client uses domain resources rather than placing every endpoint on the
240
+ root client:
241
+
242
+ ```python
243
+ with QualtricsClient() as client:
244
+ survey = client.surveys.get("SV_123")
245
+ client.surveys.update("SV_123", {"name": "Annual survey"})
246
+ filters = list(client.responses.iter_filters("SV_123"))
247
+ job = client.responses.start("SV_123")
248
+ progress = client.responses.wait("SV_123", job.progress_id)
249
+ ```
250
+
251
+ `client.responses` covers local-file and hosted-file imports, import progress,
252
+ saved filters, export creation/progress, and export download. The older
253
+ `client.response_exports` attribute remains as an alias. Survey structure
254
+ operations such as definitions and metadata are intentionally exposed through
255
+ `client.survey_definitions`, separate from the `/surveys` CRUD resource.
256
+
257
+ The root client owns authentication, error handling, and HTTP transport. Domain
258
+ packages own endpoint paths and workflows. Compatibility delegates such as
259
+ `client.iter_surveys()` remain available for existing callers.
260
+
261
+ Qualtrics currently documents CSV, TSV, JSON, NDJSON, XML, and SPSS response
262
+ exports. Large exports should use filters, date ranges, selected questions, or
263
+ continuation tokens where appropriate.
264
+
265
+ ## Python API
266
+
267
+ ```python
268
+ from qualtrics import parse_surveys, render_report, write_entities
269
+
270
+ entities = parse_surveys(
271
+ ["survey-v1.csv", "survey-v2.csv"],
272
+ ["survey-v1.qsf", "survey-v2.qsf"],
273
+ )
274
+ write_entities(entities, "entities", format="json")
275
+ render_report(entities, "report.html")
276
+ ```
277
+
278
+ ## Source layout
279
+
280
+ ```text
281
+ src/qualtrics/
282
+ ├── api/ # HTTP client, API models, and domain resources
283
+ ├── analytics/ # Coverage and data-quality calculations
284
+ ├── cli/ # Small Typer command modules
285
+ ├── models/ # EntitySet and entity collection operations
286
+ ├── parsers/ # CSV, QSF, identity, and path parsing
287
+ ├── reporting/ # HTML renderer and bundled CSS/JavaScript
288
+ ├── serialization/ # CSV, JSON, and Parquet entity I/O
289
+ └── services/ # Cross-domain application services
290
+ ```
291
+
292
+ The API and offline data tooling are equal package capabilities. The `api/`
293
+ domain owns HTTP resources and API models; parsing, analytics, reporting, and
294
+ serialization remain independent and never require network credentials.
295
+ The package contains no catch-all core module: each public operation is exported
296
+ from the domain that implements it.
297
+
298
+ The distribution, CLI, and Python import are all named `qualtrics`.
299
+
300
+ ## Development
301
+
302
+ ```bash
303
+ uv sync --group dev --group test --extra parquet
304
+ uv run pre-commit install --hook-type pre-commit --hook-type pre-push
305
+ uv run poe check
306
+ uv run poe build
307
+ ```
308
+
309
+ Run every commit hook manually with `uv run poe pre-commit`. Ruff and `ty` run
310
+ before commits; the complete pytest suite additionally runs before pushes.
311
+
312
+ ## Releases
313
+
314
+ Run the **Prepare Release** workflow from GitHub Actions and choose a patch,
315
+ minor, or major bump. The workflow updates `pyproject.toml` and `uv.lock`,
316
+ prepends a dated section to [`release-notes.md`](release-notes.md), validates the
317
+ package, and opens a release pull request. After merging it, publish a GitHub
318
+ release using the matching `vX.Y.Z` tag; the **Publish** workflow then verifies
319
+ the tag, builds and attests the distributions, and publishes them through the
320
+ PyPI `pypi` environment.
321
+
322
+ ## Examples
323
+
324
+ Runnable examples live in [`examples/`](examples/):
325
+
326
+ ```bash
327
+ # Parse one CSV; a matching QSF is discovered automatically.
328
+ uv run python examples/parse_survey.py survey.csv
329
+
330
+ # Parse all CSV files in a directory into one multi-survey report.
331
+ uv run python examples/parse_multiple_surveys.py exports
332
+
333
+ # List surveys, or add --survey-id SV_123 to export responses.
334
+ uv run python examples/api_list_and_export.py
335
+
336
+ # Import responses into an existing survey.
337
+ uv run python examples/api_import_responses.py \
338
+ SV_123 responses.csv
339
+ ```
340
+
341
+ API examples read `QUALTRICS_API_TOKEN` and `QUALTRICS_DATA_CENTER` from the
342
+ environment. Importing responses changes data in the target survey, so verify
343
+ the survey ID before running that example.
344
+
345
+ ## Acknowledgements
346
+
347
+ The usage guidance and administrative-survey examples were informed by the
348
+ [Qualtrics Report Generator](https://github.com/hihipy/qualtrics-report-generator)
349
+ README. API behavior should be checked against the
350
+ [official Qualtrics API documentation](https://api.qualtrics.com/) for the
351
+ features enabled on your account.