hdx-python-api 6.6.7__tar.gz → 6.6.8__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.
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/.pre-commit-config.yaml +1 -1
- hdx_python_api-6.6.8/CLAUDE.md +57 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/PKG-INFO +2 -2
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/pyproject.toml +1 -1
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/_version.py +2 -2
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/resource.py +50 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/utilities/test_hdx_state.py +2 -2
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_dataset_add_hapi_error.py +1 -1
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_dataset_core.py +10 -10
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_dataset_noncore.py +5 -5
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_organization.py +9 -9
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_resource.py +47 -15
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_resource_view.py +5 -5
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_showcase.py +5 -5
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_user.py +11 -11
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_vocabulary.py +6 -6
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/uv.lock +4 -5
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/.github/workflows/publish.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/.github/workflows/run-python-tests.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/.gitignore +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/LICENSE +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/README.md +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/documentation/.readthedocs.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/documentation/index.md +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/documentation/mkdocs.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/__init__.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/configuration.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/hdx_base_configuration.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/locations.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/remotehdx.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/__init__.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/date_helper.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/filestore_helper.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/hdx_error_handler.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/hdx_state.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/url_utils.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/__init__.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/dataset.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/hdxobject.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/organization.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/resource_matcher.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/resource_view.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/showcase.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/user.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/vocabulary.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/facades/__init__.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/facades/infer_arguments.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/facades/keyword_arguments.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/facades/simple.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/__init__.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/Accepted_Tags.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/CKAN/hdx_dataset_static.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/Tag_Mapping.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/Tag_Mapping_ChainRuleError.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/empty.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_base_config.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_base_config.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_config.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_config.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_dataset_static.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_dataset_static.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_datasource_topline.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_datasource_topline.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_email_configuration.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_email_configuration.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_missing_site_config.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_organization_static.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_organization_static.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_resource_static.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_resource_static.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_resource_view_static.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_resource_view_static.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_showcase_static.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_showcase_static.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_user_static.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_user_static.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_vocabulary_static.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_vocabulary_static.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/project_configuration.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/project_configuration.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/user_agent_config.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/user_agent_config2.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/user_agent_config3.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/user_agent_config_wrong.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/dataset_search_results.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/download_gen_resource/conflict_data_alg.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/download_gen_resource/min_qc_conflict_data_alg.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/download_gen_resource/qc_conflict_data_alg.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/download_gen_resource/test_data_no_data.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/download_gen_resource/test_data_no_years.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/empty.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/gen_resource/conflict_data_alg.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/gen_resource/min_qc_conflict_data_alg.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/gen_resource/qc_conflict_data_alg.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/gen_resource/test_data_no_data.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/gen_resource/test_data_no_years.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/organization/organization_list_all_fields.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/organization/organization_show_results.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/qc_from_rows/qc_conflict_data_alg.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/qc_from_rows/qc_conflict_data_alg_one_col.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/resource_formats.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/showcase_all_search_results.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/size_hash/ACLED-All-Africa-File_20170101-to-20170708.xlsx +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/state/analysis_dates.txt +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/state/last_build_date.txt +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/test_data.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/test_data.zip +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/dem_data_zwe.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/dem_indicatorlist_zwe.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/expected_resources_to_update.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/opri_data_zwe.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/opri_indicatorlist_zwe.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/opri_metadata_zwe.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/qc_sdg_data_zwe.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/sdg_data_zwe.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/sdg_indicatorlist_zwe.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/sdg_metadata_zwe.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/unesco_dataset.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/unesco_update_dataset.json +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_logic/update_logic_resources.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_logic/update_logic_resources_new.yaml +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/__init__.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/__init__.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/test_ckan.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/test_configuration.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/test_locations.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/utilities/__init__.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/utilities/test_filestore_helper.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/utilities/test_hdx_error_handler.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/utilities/test_url_utils.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/conftest.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/__init__.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_dataset_resource_generation.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_update_dataset_resources.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_update_logic.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/facades/__init__.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/facades/test_infer_arguments.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/facades/test_keyword_arguments.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/facades/test_simple.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/workingexample/my_code.py +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/workingexample/my_resource.csv +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/workingexample/my_resource.xlsx +0 -0
- {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/workingexample/run.py +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
**hdx-python-api** is the official Python library for interacting with the [Humanitarian Data Exchange (HDX)](https://data.humdata.org/) platform. It provides ORM-like classes wrapping the CKAN API to read, create, update, and delete datasets, resources, organizations, users, vocabularies, showcases, and datastores.
|
|
6
|
+
|
|
7
|
+
## Source Layout
|
|
8
|
+
|
|
9
|
+
- `src/hdx/api/` — Configuration, session management, locations, utilities
|
|
10
|
+
- `configuration.py` — `Configuration` class (credentials, site URL, user agent)
|
|
11
|
+
- `remotehdx.py` — Low-level CKAN API client
|
|
12
|
+
- `src/hdx/data/` — Data model objects, all inheriting from `HDXObject`
|
|
13
|
+
- `dataset.py`, `resource.py`, `organization.py`, `user.py`, `vocabulary.py`, `showcase.py`, `resource_view.py`
|
|
14
|
+
- `hdxobject.py` — Base class providing `_read_from_hdx` / `_write_to_hdx` and CRUD scaffolding
|
|
15
|
+
- `src/hdx/facades/` — High-level entry-point helpers (`simple`, `keyword_arguments`, `infer_arguments`)
|
|
16
|
+
- `tests/hdx/` — Test suite mirroring src layout; fixtures in `tests/fixtures/`
|
|
17
|
+
|
|
18
|
+
## Running Tests
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uv run pytest
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Tests mock the CKAN HTTP session via inner `MockSession` classes in each test file — no live HDX connection needed for the standard suite. Integration tests require `HDX_KEY_TEST` and `HDX_PIPELINE_GSHEET_AUTH` environment variables.
|
|
25
|
+
|
|
26
|
+
Coverage is written to `coverage.lcov` and JUnit XML to `test-results.xml`.
|
|
27
|
+
|
|
28
|
+
## Code Style
|
|
29
|
+
|
|
30
|
+
Formatted and linted with `ruff` (rules: E, F, I, UP; line-length not enforced). Run before committing:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pre-commit run --all-files
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- Python ≥ 3.10
|
|
37
|
+
- Type hints throughout; use `X | Y` union syntax (PEP 604), not `Optional`/`Union`
|
|
38
|
+
- Google-style docstrings with `Args:` and `Returns:` sections
|
|
39
|
+
- No inline comments unless the *why* is non-obvious
|
|
40
|
+
|
|
41
|
+
## Key Patterns
|
|
42
|
+
|
|
43
|
+
**HDXObject subclasses** expose a standard interface: `create_in_hdx()`, `update_in_hdx()`, `delete_from_hdx()`, `read_from_hdx()`. Each class defines an `actions()` static method mapping action names to CKAN API action strings.
|
|
44
|
+
|
|
45
|
+
**Writing to HDX** uses `self._write_to_hdx(action_key, data_dict, id_field_name)` inherited from `HDXObject`. The `action_key` must be in `actions()`.
|
|
46
|
+
|
|
47
|
+
**Test mocking** — each test fixture defines a `MockSession` with a `post()` method that inspects the URL and decoded JSON body to return `MockResponse` objects. State is tracked on class variables (e.g. `TestResource.datastore`).
|
|
48
|
+
|
|
49
|
+
## Collaboration Style
|
|
50
|
+
|
|
51
|
+
- Be objective, not agreeable. Act as a partner, not a sycophant. Push back when you disagree, flag tradeoffs honestly, and don't sugarcoat problems.
|
|
52
|
+
- Keep explanations brief and to the point.
|
|
53
|
+
- Don't rely on recalled knowledge for facts that could be stale (API behaviour, library versions, external systems). Search or read the actual source first. If you lack verified information, say so rather than speculate.
|
|
54
|
+
|
|
55
|
+
## Scope of Changes
|
|
56
|
+
|
|
57
|
+
When fixing a bug or addressing PR feedback, change only what is necessary to resolve the specific issue. Do not refactor surrounding code, rename variables, adjust formatting, or make improvements in the same commit unless they are directly required by the fix.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hdx-python-api
|
|
3
|
-
Version: 6.6.
|
|
3
|
+
Version: 6.6.8
|
|
4
4
|
Summary: HDX Python API for interacting with the Humanitarian Data Exchange
|
|
5
5
|
Project-URL: Homepage, https://github.com/OCHA-DAP/hdx-python-api
|
|
6
6
|
Author: Michael Rans
|
|
@@ -25,7 +25,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
25
25
|
Classifier: Programming Language :: Python :: 3.14
|
|
26
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
27
|
Requires-Python: >=3.10
|
|
28
|
-
Requires-Dist: ckanapi>=4.
|
|
28
|
+
Requires-Dist: ckanapi>=4.11
|
|
29
29
|
Requires-Dist: defopt>=7.0.0
|
|
30
30
|
Requires-Dist: email-validator
|
|
31
31
|
Requires-Dist: hdx-python-country>=4.1.1
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '6.6.
|
|
22
|
-
__version_tuple__ = version_tuple = (6, 6,
|
|
21
|
+
__version__ = version = '6.6.8'
|
|
22
|
+
__version_tuple__ = version_tuple = (6, 6, 8)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -62,6 +62,9 @@ class Resource(HDXObject):
|
|
|
62
62
|
"search": "resource_search",
|
|
63
63
|
"broken": "hdx_mark_broken_link_in_resource",
|
|
64
64
|
"datastore_delete": "datastore_delete",
|
|
65
|
+
"datastore_create": "datastore_create",
|
|
66
|
+
"datastore_insert": "datastore_insert",
|
|
67
|
+
"datastore_upsert": "datastore_upsert",
|
|
65
68
|
"datastore_search": "datastore_search",
|
|
66
69
|
}
|
|
67
70
|
|
|
@@ -701,6 +704,53 @@ class Resource(HDXObject):
|
|
|
701
704
|
return True
|
|
702
705
|
return False
|
|
703
706
|
|
|
707
|
+
def create_datastore(
|
|
708
|
+
self,
|
|
709
|
+
schema: Sequence[dict],
|
|
710
|
+
primary_key: str | Sequence[str] | None = None,
|
|
711
|
+
) -> None:
|
|
712
|
+
"""Create a datastore for the resource with the given schema.
|
|
713
|
+
|
|
714
|
+
Args:
|
|
715
|
+
schema: Sequence of field definitions, each a dict with 'id' and 'type' keys.
|
|
716
|
+
primary_key: Primary key field name(s). Defaults to None.
|
|
717
|
+
|
|
718
|
+
Returns:
|
|
719
|
+
None
|
|
720
|
+
"""
|
|
721
|
+
data: dict = {
|
|
722
|
+
"resource_id": self.data["id"],
|
|
723
|
+
"force": True,
|
|
724
|
+
"fields": schema,
|
|
725
|
+
}
|
|
726
|
+
if primary_key is not None:
|
|
727
|
+
if not isinstance(primary_key, str):
|
|
728
|
+
primary_key = ",".join(primary_key)
|
|
729
|
+
data["primary_key"] = primary_key
|
|
730
|
+
self._write_to_hdx("datastore_create", data, "resource_id")
|
|
731
|
+
|
|
732
|
+
def update_datastore(
|
|
733
|
+
self,
|
|
734
|
+
records: Sequence[dict],
|
|
735
|
+
method: str = "upsert",
|
|
736
|
+
) -> None:
|
|
737
|
+
"""Update (upsert) records into the resource datastore.
|
|
738
|
+
|
|
739
|
+
Args:
|
|
740
|
+
records: Sequence of record dicts to insert or update.
|
|
741
|
+
method: Datastore update method ('upsert', 'insert', or 'update'). Defaults to 'upsert'.
|
|
742
|
+
|
|
743
|
+
Returns:
|
|
744
|
+
None
|
|
745
|
+
"""
|
|
746
|
+
data = {
|
|
747
|
+
"resource_id": self.data["id"],
|
|
748
|
+
"force": True,
|
|
749
|
+
"method": method,
|
|
750
|
+
"records": records,
|
|
751
|
+
}
|
|
752
|
+
self._write_to_hdx("datastore_upsert", data, "resource_id")
|
|
753
|
+
|
|
704
754
|
def delete_datastore(self) -> None:
|
|
705
755
|
"""Delete a resource from the HDX datastore
|
|
706
756
|
|
|
@@ -47,7 +47,7 @@ class TestState:
|
|
|
47
47
|
def do_state(self, tempfolder, statefile):
|
|
48
48
|
class MockSession:
|
|
49
49
|
@staticmethod
|
|
50
|
-
def post(url, data,
|
|
50
|
+
def post(url, data, **kwargs):
|
|
51
51
|
if "resource" in url:
|
|
52
52
|
result = json.dumps(resultdict)
|
|
53
53
|
return MockResponse(
|
|
@@ -71,7 +71,7 @@ class TestState:
|
|
|
71
71
|
def do_state_multi(self, tempfolder, multidatestatefile):
|
|
72
72
|
class MockSession:
|
|
73
73
|
@staticmethod
|
|
74
|
-
def post(url, data,
|
|
74
|
+
def post(url, data, **kwargs):
|
|
75
75
|
if "resource" in url:
|
|
76
76
|
result = json.dumps(resultdict)
|
|
77
77
|
return MockResponse(
|
|
@@ -13,7 +13,7 @@ class TestDatasetAddHAPIError:
|
|
|
13
13
|
def hapi_resource_update(self):
|
|
14
14
|
class MockSession:
|
|
15
15
|
@staticmethod
|
|
16
|
-
def post(url, data,
|
|
16
|
+
def post(url, data, **kwargs):
|
|
17
17
|
datadict = json.loads(data.decode("utf-8"))
|
|
18
18
|
if "show" in url:
|
|
19
19
|
resource_id = datadict["id"]
|
|
@@ -182,7 +182,7 @@ class TestDatasetCore:
|
|
|
182
182
|
def read(self):
|
|
183
183
|
class MockSession:
|
|
184
184
|
@staticmethod
|
|
185
|
-
def post(url, data,
|
|
185
|
+
def post(url, data, **kwargs):
|
|
186
186
|
datadict = json.loads(data.decode("utf-8"))
|
|
187
187
|
return dataset_mockshow(url, datadict)
|
|
188
188
|
|
|
@@ -192,7 +192,7 @@ class TestDatasetCore:
|
|
|
192
192
|
def post_revise(self):
|
|
193
193
|
class MockSession:
|
|
194
194
|
@staticmethod
|
|
195
|
-
def post(url, data,
|
|
195
|
+
def post(url, data, **kwargs):
|
|
196
196
|
if isinstance(data, dict):
|
|
197
197
|
datadict = {
|
|
198
198
|
k.decode("utf8"): v.decode("utf8") for k, v in data.items()
|
|
@@ -219,7 +219,7 @@ class TestDatasetCore:
|
|
|
219
219
|
def post_create(self):
|
|
220
220
|
class MockSession:
|
|
221
221
|
@staticmethod
|
|
222
|
-
def post(url, data,
|
|
222
|
+
def post(url, data, **kwargs):
|
|
223
223
|
if isinstance(data, dict):
|
|
224
224
|
datadict = {
|
|
225
225
|
k.decode("utf8"): v.decode("utf8") for k, v in data.items()
|
|
@@ -286,7 +286,7 @@ class TestDatasetCore:
|
|
|
286
286
|
def post_update(self):
|
|
287
287
|
class MockSession:
|
|
288
288
|
@staticmethod
|
|
289
|
-
def post(url, data,
|
|
289
|
+
def post(url, data, **kwargs):
|
|
290
290
|
if isinstance(data, dict):
|
|
291
291
|
datadict = {
|
|
292
292
|
k.decode("utf8"): v.decode("utf8") for k, v in data.items()
|
|
@@ -363,7 +363,7 @@ class TestDatasetCore:
|
|
|
363
363
|
def post_reorder(self):
|
|
364
364
|
class MockSession:
|
|
365
365
|
@staticmethod
|
|
366
|
-
def post(url, data,
|
|
366
|
+
def post(url, data, **kwargs):
|
|
367
367
|
decodedata = data.decode("utf-8")
|
|
368
368
|
datadict = json.loads(decodedata)
|
|
369
369
|
if "show" in url:
|
|
@@ -390,7 +390,7 @@ class TestDatasetCore:
|
|
|
390
390
|
def post_delete(self):
|
|
391
391
|
class MockSession:
|
|
392
392
|
@staticmethod
|
|
393
|
-
def post(url, data,
|
|
393
|
+
def post(url, data, **kwargs):
|
|
394
394
|
decodedata = data.decode("utf-8")
|
|
395
395
|
datadict = json.loads(decodedata)
|
|
396
396
|
if "show" in url:
|
|
@@ -423,7 +423,7 @@ class TestDatasetCore:
|
|
|
423
423
|
def search(self):
|
|
424
424
|
class MockSession:
|
|
425
425
|
@staticmethod
|
|
426
|
-
def post(url, data,
|
|
426
|
+
def post(url, data, **kwargs):
|
|
427
427
|
datadict = json.loads(data.decode("utf-8"))
|
|
428
428
|
return mocksearch(url, datadict)
|
|
429
429
|
|
|
@@ -433,7 +433,7 @@ class TestDatasetCore:
|
|
|
433
433
|
def post_list(self):
|
|
434
434
|
class MockSession:
|
|
435
435
|
@staticmethod
|
|
436
|
-
def post(url, data,
|
|
436
|
+
def post(url, data, **kwargs):
|
|
437
437
|
datadict = json.loads(data.decode("utf-8"))
|
|
438
438
|
return mocklist(url, datadict)
|
|
439
439
|
|
|
@@ -443,7 +443,7 @@ class TestDatasetCore:
|
|
|
443
443
|
def all(self):
|
|
444
444
|
class MockSession:
|
|
445
445
|
@staticmethod
|
|
446
|
-
def post(url, data,
|
|
446
|
+
def post(url, data, **kwargs):
|
|
447
447
|
datadict = json.loads(data.decode("utf-8"))
|
|
448
448
|
return mockall(url, datadict)
|
|
449
449
|
|
|
@@ -453,7 +453,7 @@ class TestDatasetCore:
|
|
|
453
453
|
def post_autocomplete(self):
|
|
454
454
|
class MockSession:
|
|
455
455
|
@staticmethod
|
|
456
|
-
def post(url, data,
|
|
456
|
+
def post(url, data, **kwargs):
|
|
457
457
|
decodedata = data.decode("utf-8")
|
|
458
458
|
datadict = json.loads(decodedata)
|
|
459
459
|
if "autocomplete" not in url or "acled" not in datadict["q"]:
|
|
@@ -52,7 +52,7 @@ class TestDatasetNoncore:
|
|
|
52
52
|
|
|
53
53
|
class MockSession:
|
|
54
54
|
@staticmethod
|
|
55
|
-
def post(url, data,
|
|
55
|
+
def post(url, data, **kwargs):
|
|
56
56
|
datadict = json.loads(data.decode("utf-8"))
|
|
57
57
|
return vocabulary_mockshow(url, datadict)
|
|
58
58
|
|
|
@@ -62,7 +62,7 @@ class TestDatasetNoncore:
|
|
|
62
62
|
def user_read(self):
|
|
63
63
|
class MockSession:
|
|
64
64
|
@staticmethod
|
|
65
|
-
def post(url, data,
|
|
65
|
+
def post(url, data, **kwargs):
|
|
66
66
|
datadict = json.loads(data.decode("utf-8"))
|
|
67
67
|
return user_mockshow(url, datadict)
|
|
68
68
|
|
|
@@ -72,7 +72,7 @@ class TestDatasetNoncore:
|
|
|
72
72
|
def organization_read(self):
|
|
73
73
|
class MockSession:
|
|
74
74
|
@staticmethod
|
|
75
|
-
def post(url, data,
|
|
75
|
+
def post(url, data, **kwargs):
|
|
76
76
|
datadict = json.loads(data.decode("utf-8"))
|
|
77
77
|
return organization_mockshow(url, datadict)
|
|
78
78
|
|
|
@@ -82,7 +82,7 @@ class TestDatasetNoncore:
|
|
|
82
82
|
def showcase_read(self):
|
|
83
83
|
class MockSession:
|
|
84
84
|
@staticmethod
|
|
85
|
-
def post(url, data,
|
|
85
|
+
def post(url, data, **kwargs):
|
|
86
86
|
datadict = json.loads(data.decode("utf-8"))
|
|
87
87
|
if "showcase_list" in url:
|
|
88
88
|
result = json.dumps([showcase_resultdict])
|
|
@@ -111,7 +111,7 @@ class TestDatasetNoncore:
|
|
|
111
111
|
def vocabulary_update(self):
|
|
112
112
|
class MockSession:
|
|
113
113
|
@staticmethod
|
|
114
|
-
def post(url, data,
|
|
114
|
+
def post(url, data, **kwargs):
|
|
115
115
|
if isinstance(data, dict):
|
|
116
116
|
datadict = {
|
|
117
117
|
k.decode("utf8"): v.decode("utf8") for k, v in data.items()
|
|
@@ -115,7 +115,7 @@ class TestOrganization:
|
|
|
115
115
|
def read(self):
|
|
116
116
|
class MockSession:
|
|
117
117
|
@staticmethod
|
|
118
|
-
def post(url, data,
|
|
118
|
+
def post(url, data, **kwargs):
|
|
119
119
|
datadict = json.loads(data.decode("utf-8"))
|
|
120
120
|
return organization_mockshow(url, datadict)
|
|
121
121
|
|
|
@@ -125,7 +125,7 @@ class TestOrganization:
|
|
|
125
125
|
def post_create(self):
|
|
126
126
|
class MockSession:
|
|
127
127
|
@staticmethod
|
|
128
|
-
def post(url, data,
|
|
128
|
+
def post(url, data, **kwargs):
|
|
129
129
|
datadict = json.loads(data.decode("utf-8"))
|
|
130
130
|
if "show" in url:
|
|
131
131
|
return organization_mockshow(url, datadict)
|
|
@@ -165,7 +165,7 @@ class TestOrganization:
|
|
|
165
165
|
def post_update(self):
|
|
166
166
|
class MockSession:
|
|
167
167
|
@staticmethod
|
|
168
|
-
def post(url, data,
|
|
168
|
+
def post(url, data, **kwargs):
|
|
169
169
|
datadict = json.loads(data.decode("utf-8"))
|
|
170
170
|
if "show" in url:
|
|
171
171
|
return organization_mockshow(url, datadict)
|
|
@@ -205,7 +205,7 @@ class TestOrganization:
|
|
|
205
205
|
def post_delete(self):
|
|
206
206
|
class MockSession:
|
|
207
207
|
@staticmethod
|
|
208
|
-
def post(url, data,
|
|
208
|
+
def post(url, data, **kwargs):
|
|
209
209
|
decodedata = data.decode("utf-8")
|
|
210
210
|
datadict = json.loads(decodedata)
|
|
211
211
|
if "show" in url:
|
|
@@ -232,7 +232,7 @@ class TestOrganization:
|
|
|
232
232
|
def post_list(self):
|
|
233
233
|
class MockSession:
|
|
234
234
|
@staticmethod
|
|
235
|
-
def post(url, data,
|
|
235
|
+
def post(url, data, **kwargs):
|
|
236
236
|
json.loads(data.decode("utf-8"))
|
|
237
237
|
return mocklist(url)
|
|
238
238
|
|
|
@@ -242,7 +242,7 @@ class TestOrganization:
|
|
|
242
242
|
def post_all_fields(self, fixturesfolder):
|
|
243
243
|
class MockSession:
|
|
244
244
|
@staticmethod
|
|
245
|
-
def post(url, data,
|
|
245
|
+
def post(url, data, **kwargs):
|
|
246
246
|
kwargs = json.loads(data.decode("utf-8"))
|
|
247
247
|
if "show" in url:
|
|
248
248
|
return organization_mockshow(url, kwargs)
|
|
@@ -264,7 +264,7 @@ class TestOrganization:
|
|
|
264
264
|
def user_read(self):
|
|
265
265
|
class MockSession:
|
|
266
266
|
@staticmethod
|
|
267
|
-
def post(url, data,
|
|
267
|
+
def post(url, data, **kwargs):
|
|
268
268
|
datadict = json.loads(data.decode("utf-8"))
|
|
269
269
|
return user_mockshow(url, datadict)
|
|
270
270
|
|
|
@@ -274,7 +274,7 @@ class TestOrganization:
|
|
|
274
274
|
def datasets_get(self):
|
|
275
275
|
class MockSession:
|
|
276
276
|
@staticmethod
|
|
277
|
-
def post(url, data,
|
|
277
|
+
def post(url, data, **kwargs):
|
|
278
278
|
datadict = json.loads(data.decode("utf-8"))
|
|
279
279
|
return mockgetdatasets(url, datadict)
|
|
280
280
|
|
|
@@ -284,7 +284,7 @@ class TestOrganization:
|
|
|
284
284
|
def post_autocomplete(self):
|
|
285
285
|
class MockSession:
|
|
286
286
|
@staticmethod
|
|
287
|
-
def post(url, data,
|
|
287
|
+
def post(url, data, **kwargs):
|
|
288
288
|
decodedata = data.decode("utf-8")
|
|
289
289
|
datadict = json.loads(decodedata)
|
|
290
290
|
if "autocomplete" not in url or "innago" not in datadict["q"]:
|
|
@@ -359,7 +359,7 @@ class TestResource:
|
|
|
359
359
|
def read(self):
|
|
360
360
|
class MockSession:
|
|
361
361
|
@staticmethod
|
|
362
|
-
def post(url, data,
|
|
362
|
+
def post(url, data, **kwargs):
|
|
363
363
|
datadict = json.loads(data.decode("utf-8"))
|
|
364
364
|
return mockshow(url, datadict)
|
|
365
365
|
|
|
@@ -369,7 +369,8 @@ class TestResource:
|
|
|
369
369
|
def post_create(self):
|
|
370
370
|
class MockSession:
|
|
371
371
|
@staticmethod
|
|
372
|
-
def post(url, data,
|
|
372
|
+
def post(url, data, **kwargs):
|
|
373
|
+
files = kwargs.get("files")
|
|
373
374
|
if isinstance(data, dict):
|
|
374
375
|
datadict = {
|
|
375
376
|
k.decode("utf8"): v.decode("utf8") for k, v in data.items()
|
|
@@ -431,7 +432,8 @@ class TestResource:
|
|
|
431
432
|
def post_update(self):
|
|
432
433
|
class MockSession:
|
|
433
434
|
@staticmethod
|
|
434
|
-
def post(url, data,
|
|
435
|
+
def post(url, data, **kwargs):
|
|
436
|
+
files = kwargs.get("files")
|
|
435
437
|
if isinstance(data, dict):
|
|
436
438
|
datadict = {
|
|
437
439
|
k.decode("utf8"): v.decode("utf8") for k, v in data.items()
|
|
@@ -491,7 +493,7 @@ class TestResource:
|
|
|
491
493
|
def post_delete(self):
|
|
492
494
|
class MockSession:
|
|
493
495
|
@staticmethod
|
|
494
|
-
def post(url, data,
|
|
496
|
+
def post(url, data, **kwargs):
|
|
495
497
|
decodedata = data.decode("utf-8")
|
|
496
498
|
datadict = json.loads(decodedata)
|
|
497
499
|
if "show" in url:
|
|
@@ -518,7 +520,7 @@ class TestResource:
|
|
|
518
520
|
def post_datastore(self):
|
|
519
521
|
class MockSession:
|
|
520
522
|
@staticmethod
|
|
521
|
-
def post(url, data,
|
|
523
|
+
def post(url, data, **kwargs):
|
|
522
524
|
decodedata = data.decode("utf-8")
|
|
523
525
|
datadict = json.loads(decodedata)
|
|
524
526
|
if "show" in url:
|
|
@@ -574,11 +576,18 @@ class TestResource:
|
|
|
574
576
|
'{"success": true, "result": {"include_total": true, "resource_id": "_table_metadata", "fields": [{"type": "int", "id": "_id"}, {"type": "name", "id": "name"}, {"type": "oid", "id": "oid"}, {"type": "name", "id": "alias_of"}], "records_format": "objects", "records": [{"_id":"f9cd60f3d7f2f6d0","name":"f9228459-d808-4b51-948f-68a5850abfde","oid":"919290","alias_of":null},{"_id":"7ae63490de9b7d7b","name":"af618a0b-09b8-42c8-836f-2be597e1ea34","oid":"135294","alias_of":null},{"_id":"1dc37f4e89988644","name":"748b40dd-7bd3-40a3-941b-e76f0bfbe0eb","oid":"117144","alias_of":null},{"_id":"2a554a61bd366206","name":"91c78d24-eab3-40b5-ba91-6b29bcda7178","oid":"116963","alias_of":null},{"_id":"fd787575143afe90","name":"9320cfce-4620-489a-bcbe-25c73867d4fc","oid":"107430","alias_of":null},{"_id":"a70093abd230f647","name":"b9d2eb36-e65c-417a-bc28-f4dadb149302","oid":"107409","alias_of":null},{"_id":"95fbdd2d06c07aea","name":"ca6a0891-8395-4d58-9168-6c44e17e0193","oid":"107385","alias_of":null}], "limit": 10000, "_links": {"start": "/api/action/datastore_search?limit=10000&resource_id=_table_metadata", "next": "/api/action/datastore_search?offset=10000&limit=10000&resource_id=_table_metadata"}, "total": 7}}',
|
|
575
577
|
)
|
|
576
578
|
if (
|
|
577
|
-
"
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
579
|
+
"upsert" in url
|
|
580
|
+
and datadict["resource_id"]
|
|
581
|
+
== "de6549d8-268b-4dfe-adaf-a4ae5c8510d5"
|
|
582
|
+
):
|
|
583
|
+
TestResource.datastore = "upsert"
|
|
584
|
+
return MockResponse(
|
|
585
|
+
200,
|
|
586
|
+
'{"success": true, "result": {"method": "upsert", "resource_id": "de6549d8-268b-4dfe-adaf-a4ae5c8510d5"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=datastore_upsert"}',
|
|
587
|
+
)
|
|
588
|
+
if ("create" in url or "insert" in url or "search" in url) and datadict[
|
|
589
|
+
"resource_id"
|
|
590
|
+
] == "de6549d8-268b-4dfe-adaf-a4ae5c8510d5":
|
|
582
591
|
TestResource.datastore = "create"
|
|
583
592
|
return MockResponse(
|
|
584
593
|
200,
|
|
@@ -595,7 +604,7 @@ class TestResource:
|
|
|
595
604
|
def post_patch(self):
|
|
596
605
|
class MockSession:
|
|
597
606
|
@staticmethod
|
|
598
|
-
def post(url, data,
|
|
607
|
+
def post(url, data, **kwargs):
|
|
599
608
|
datadict = json.loads(data.decode("utf-8"))
|
|
600
609
|
if "show" in url:
|
|
601
610
|
return mockshow(url, datadict)
|
|
@@ -607,7 +616,7 @@ class TestResource:
|
|
|
607
616
|
def post_dataset(self):
|
|
608
617
|
class MockSession:
|
|
609
618
|
@staticmethod
|
|
610
|
-
def post(url, data,
|
|
619
|
+
def post(url, data, **kwargs):
|
|
611
620
|
datadict = json.loads(data.decode("utf-8"))
|
|
612
621
|
return mockdataset(url, datadict)
|
|
613
622
|
|
|
@@ -617,7 +626,7 @@ class TestResource:
|
|
|
617
626
|
def search(self):
|
|
618
627
|
class MockSession:
|
|
619
628
|
@staticmethod
|
|
620
|
-
def post(url, data,
|
|
629
|
+
def post(url, data, **kwargs):
|
|
621
630
|
datadict = json.loads(data.decode("utf-8"))
|
|
622
631
|
return mocksearch(url, datadict)
|
|
623
632
|
|
|
@@ -627,7 +636,7 @@ class TestResource:
|
|
|
627
636
|
def post_resourceview(self):
|
|
628
637
|
class MockSession:
|
|
629
638
|
@staticmethod
|
|
630
|
-
def post(url, data,
|
|
639
|
+
def post(url, data, **kwargs):
|
|
631
640
|
decodedata = data.decode("utf-8")
|
|
632
641
|
return mockresourceview(url, decodedata)
|
|
633
642
|
|
|
@@ -637,7 +646,7 @@ class TestResource:
|
|
|
637
646
|
def post_broken(self):
|
|
638
647
|
class MockSession:
|
|
639
648
|
@staticmethod
|
|
640
|
-
def post(url, data,
|
|
649
|
+
def post(url, data, **kwargs):
|
|
641
650
|
if isinstance(data, dict):
|
|
642
651
|
datadict = {
|
|
643
652
|
k.decode("utf8"): v.decode("utf8") for k, v in data.items()
|
|
@@ -977,6 +986,29 @@ class TestResource:
|
|
|
977
986
|
TestResource.datastore = None
|
|
978
987
|
assert resource2.has_datastore() is False
|
|
979
988
|
|
|
989
|
+
schema = [{"id": "code", "type": "text"}, {"id": "value", "type": "float"}]
|
|
990
|
+
records = [{"code": "A", "value": 1.0}, {"code": "B", "value": 2.0}]
|
|
991
|
+
|
|
992
|
+
TestResource.datastore = None
|
|
993
|
+
resource.create_datastore(schema, primary_key="code")
|
|
994
|
+
assert TestResource.datastore == "create"
|
|
995
|
+
|
|
996
|
+
TestResource.datastore = None
|
|
997
|
+
resource.create_datastore(schema, primary_key=["code", "value"])
|
|
998
|
+
assert TestResource.datastore == "create"
|
|
999
|
+
|
|
1000
|
+
TestResource.datastore = None
|
|
1001
|
+
resource.create_datastore(schema)
|
|
1002
|
+
assert TestResource.datastore == "create"
|
|
1003
|
+
|
|
1004
|
+
TestResource.datastore = None
|
|
1005
|
+
resource.update_datastore(records)
|
|
1006
|
+
assert TestResource.datastore == "upsert"
|
|
1007
|
+
|
|
1008
|
+
TestResource.datastore = None
|
|
1009
|
+
resource.update_datastore(records, method="insert")
|
|
1010
|
+
assert TestResource.datastore == "upsert"
|
|
1011
|
+
|
|
980
1012
|
def test_resource_views(self, configuration, post_resourceview):
|
|
981
1013
|
resource = Resource({"id": "25982d1c-f45a-45e1-b14e-87d367413045"})
|
|
982
1014
|
with pytest.raises(HDXError):
|
|
@@ -136,7 +136,7 @@ class TestResourceView:
|
|
|
136
136
|
def read(self):
|
|
137
137
|
class MockSession:
|
|
138
138
|
@staticmethod
|
|
139
|
-
def post(url, data,
|
|
139
|
+
def post(url, data, **kwargs):
|
|
140
140
|
datadict = json.loads(data.decode("utf-8"))
|
|
141
141
|
return resource_view_mockshow(url, datadict)
|
|
142
142
|
|
|
@@ -146,7 +146,7 @@ class TestResourceView:
|
|
|
146
146
|
def post_create(self):
|
|
147
147
|
class MockSession:
|
|
148
148
|
@staticmethod
|
|
149
|
-
def post(url, data,
|
|
149
|
+
def post(url, data, **kwargs):
|
|
150
150
|
datadict = json.loads(data.decode("utf-8"))
|
|
151
151
|
if "show" in url:
|
|
152
152
|
return resource_view_mockshow(url, datadict)
|
|
@@ -165,7 +165,7 @@ class TestResourceView:
|
|
|
165
165
|
def post_update(self):
|
|
166
166
|
class MockSession:
|
|
167
167
|
@staticmethod
|
|
168
|
-
def post(url, data,
|
|
168
|
+
def post(url, data, **kwargs):
|
|
169
169
|
datadict = json.loads(data.decode("utf-8"))
|
|
170
170
|
if "show" in url:
|
|
171
171
|
return resource_view_mockshow(url, datadict)
|
|
@@ -207,7 +207,7 @@ class TestResourceView:
|
|
|
207
207
|
def post_delete(self):
|
|
208
208
|
class MockSession:
|
|
209
209
|
@staticmethod
|
|
210
|
-
def post(url, data,
|
|
210
|
+
def post(url, data, **kwargs):
|
|
211
211
|
decodedata = data.decode("utf-8")
|
|
212
212
|
datadict = json.loads(decodedata)
|
|
213
213
|
if "show" in url:
|
|
@@ -234,7 +234,7 @@ class TestResourceView:
|
|
|
234
234
|
def post_list(self):
|
|
235
235
|
class MockSession:
|
|
236
236
|
@staticmethod
|
|
237
|
-
def post(url, data,
|
|
237
|
+
def post(url, data, **kwargs):
|
|
238
238
|
datadict = json.loads(data.decode("utf-8"))
|
|
239
239
|
return resource_view_mocklist(url, datadict)
|
|
240
240
|
|
|
@@ -185,7 +185,7 @@ class TestShowcase:
|
|
|
185
185
|
def read(self):
|
|
186
186
|
class MockSession:
|
|
187
187
|
@staticmethod
|
|
188
|
-
def post(url, data,
|
|
188
|
+
def post(url, data, **kwargs):
|
|
189
189
|
datadict = json.loads(data.decode("utf-8"))
|
|
190
190
|
if "association_delete" in url:
|
|
191
191
|
TestShowcase.association = "delete"
|
|
@@ -208,7 +208,7 @@ class TestShowcase:
|
|
|
208
208
|
def post_create(self):
|
|
209
209
|
class MockSession:
|
|
210
210
|
@staticmethod
|
|
211
|
-
def post(url, data,
|
|
211
|
+
def post(url, data, **kwargs):
|
|
212
212
|
datadict = json.loads(data.decode("utf-8"))
|
|
213
213
|
if "vocabulary" in url:
|
|
214
214
|
return vocabulary_mockshow(url, datadict)
|
|
@@ -253,7 +253,7 @@ class TestShowcase:
|
|
|
253
253
|
|
|
254
254
|
class MockSession:
|
|
255
255
|
@staticmethod
|
|
256
|
-
def post(url, data,
|
|
256
|
+
def post(url, data, **kwargs):
|
|
257
257
|
datadict = json.loads(data.decode("utf-8"))
|
|
258
258
|
if "vocabulary" in url:
|
|
259
259
|
return vocabulary_mockshow(url, datadict)
|
|
@@ -295,7 +295,7 @@ class TestShowcase:
|
|
|
295
295
|
def post_delete(self):
|
|
296
296
|
class MockSession:
|
|
297
297
|
@staticmethod
|
|
298
|
-
def post(url, data,
|
|
298
|
+
def post(url, data, **kwargs):
|
|
299
299
|
decodedata = data.decode("utf-8")
|
|
300
300
|
datadict = json.loads(decodedata)
|
|
301
301
|
if url.endswith("show") or "list" in url:
|
|
@@ -322,7 +322,7 @@ class TestShowcase:
|
|
|
322
322
|
def allsearch(self):
|
|
323
323
|
class MockSession:
|
|
324
324
|
@staticmethod
|
|
325
|
-
def post(url, data,
|
|
325
|
+
def post(url, data, **kwargs):
|
|
326
326
|
datadict = json.loads(data.decode("utf-8"))
|
|
327
327
|
return mockallsearch(url, datadict)
|
|
328
328
|
|