hdx-python-api 6.7.2__tar.gz → 6.7.4__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 (151) hide show
  1. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/PKG-INFO +1 -1
  2. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/documentation/index.md +159 -5
  3. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/_version.py +2 -2
  4. hdx_python_api-6.7.4/src/hdx/api/utilities/hdx_stats.py +86 -0
  5. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/activity.py +3 -1
  6. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/dataset.py +158 -7
  7. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/hdxobject.py +28 -1
  8. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/location.py +44 -7
  9. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/organization.py +1 -7
  10. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/resource.py +337 -18
  11. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/user.py +21 -8
  12. hdx_python_api-6.7.4/tests/hdx/api/utilities/test_hdx_stats.py +89 -0
  13. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_activity.py +6 -1
  14. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_dataset_noncore.py +142 -0
  15. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_location.py +65 -0
  16. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_resource.py +339 -1
  17. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_user.py +28 -0
  18. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/.github/workflows/publish.yaml +0 -0
  19. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/.github/workflows/run-python-tests.yaml +0 -0
  20. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/.gitignore +0 -0
  21. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/.pre-commit-config.yaml +0 -0
  22. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/CLAUDE.md +0 -0
  23. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/LICENSE +0 -0
  24. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/README.md +0 -0
  25. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/documentation/.readthedocs.yaml +0 -0
  26. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/documentation/mkdocs.yaml +0 -0
  27. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/pyproject.toml +0 -0
  28. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/__init__.py +0 -0
  29. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/configuration.py +0 -0
  30. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/hdx_base_configuration.yaml +0 -0
  31. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/locations.py +0 -0
  32. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/remotehdx.py +0 -0
  33. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/utilities/__init__.py +0 -0
  34. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/utilities/date_helper.py +0 -0
  35. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/utilities/filestore_helper.py +0 -0
  36. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/utilities/hdx_error_handler.py +0 -0
  37. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/utilities/hdx_state.py +0 -0
  38. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/api/utilities/url_utils.py +0 -0
  39. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/__init__.py +0 -0
  40. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/resource_matcher.py +0 -0
  41. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/resource_view.py +0 -0
  42. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/showcase.py +0 -0
  43. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/data/vocabulary.py +0 -0
  44. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/facades/__init__.py +0 -0
  45. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/facades/infer_arguments.py +0 -0
  46. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/facades/keyword_arguments.py +0 -0
  47. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/src/hdx/facades/simple.py +0 -0
  48. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/__init__.py +0 -0
  49. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/Accepted_Tags.csv +0 -0
  50. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/CKAN/hdx_dataset_static.yaml +0 -0
  51. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/Tag_Mapping.csv +0 -0
  52. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/Tag_Mapping_ChainRuleError.csv +0 -0
  53. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/empty.yaml +0 -0
  54. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_base_config.json +0 -0
  55. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_base_config.yaml +0 -0
  56. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_config.json +0 -0
  57. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_config.yaml +0 -0
  58. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_dataset_static.json +0 -0
  59. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_dataset_static.yaml +0 -0
  60. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_datasource_topline.json +0 -0
  61. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_datasource_topline.yaml +0 -0
  62. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_email_configuration.json +0 -0
  63. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_email_configuration.yaml +0 -0
  64. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_missing_site_config.json +0 -0
  65. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_organization_static.json +0 -0
  66. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_organization_static.yaml +0 -0
  67. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_resource_static.json +0 -0
  68. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_resource_static.yaml +0 -0
  69. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_resource_view_static.json +0 -0
  70. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_resource_view_static.yaml +0 -0
  71. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_showcase_static.json +0 -0
  72. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_showcase_static.yaml +0 -0
  73. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_user_static.json +0 -0
  74. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_user_static.yaml +0 -0
  75. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_vocabulary_static.json +0 -0
  76. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/hdx_vocabulary_static.yaml +0 -0
  77. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/project_configuration.json +0 -0
  78. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/project_configuration.yaml +0 -0
  79. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/user_agent_config.yaml +0 -0
  80. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/user_agent_config2.yaml +0 -0
  81. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/user_agent_config3.yaml +0 -0
  82. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/config/user_agent_config_wrong.yaml +0 -0
  83. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/dataset_search_results.yaml +0 -0
  84. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/download_gen_resource/conflict_data_alg.csv +0 -0
  85. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/download_gen_resource/min_qc_conflict_data_alg.csv +0 -0
  86. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/download_gen_resource/qc_conflict_data_alg.csv +0 -0
  87. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/download_gen_resource/test_data_no_data.csv +0 -0
  88. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/download_gen_resource/test_data_no_years.csv +0 -0
  89. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/empty.csv +0 -0
  90. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/gen_resource/conflict_data_alg.csv +0 -0
  91. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/gen_resource/min_qc_conflict_data_alg.csv +0 -0
  92. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/gen_resource/qc_conflict_data_alg.csv +0 -0
  93. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/gen_resource/test_data_no_data.csv +0 -0
  94. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/gen_resource/test_data_no_years.csv +0 -0
  95. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/location/location_list_all_fields.yaml +0 -0
  96. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/location/location_show_results.yaml +0 -0
  97. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/organization/organization_list_all_fields.yaml +0 -0
  98. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/organization/organization_show_results.yaml +0 -0
  99. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/qc_from_rows/qc_conflict_data_alg.csv +0 -0
  100. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/qc_from_rows/qc_conflict_data_alg_one_col.csv +0 -0
  101. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/resource_formats.json +0 -0
  102. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/showcase_all_search_results.yaml +0 -0
  103. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/size_hash/ACLED-All-Africa-File_20170101-to-20170708.xlsx +0 -0
  104. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/state/analysis_dates.txt +0 -0
  105. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/state/last_build_date.txt +0 -0
  106. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/test_data.csv +0 -0
  107. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/test_data.zip +0 -0
  108. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/dem_data_zwe.csv +0 -0
  109. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/dem_indicatorlist_zwe.csv +0 -0
  110. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/expected_resources_to_update.json +0 -0
  111. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/opri_data_zwe.csv +0 -0
  112. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/opri_indicatorlist_zwe.csv +0 -0
  113. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/opri_metadata_zwe.csv +0 -0
  114. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/qc_sdg_data_zwe.csv +0 -0
  115. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/sdg_data_zwe.csv +0 -0
  116. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/sdg_indicatorlist_zwe.csv +0 -0
  117. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/sdg_metadata_zwe.csv +0 -0
  118. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/unesco_dataset.json +0 -0
  119. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_dataset_resources/unesco_update_dataset.json +0 -0
  120. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_logic/update_logic_resources.yaml +0 -0
  121. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/fixtures/update_logic/update_logic_resources_new.yaml +0 -0
  122. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/__init__.py +0 -0
  123. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/api/__init__.py +0 -0
  124. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/api/test_ckan.py +0 -0
  125. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/api/test_configuration.py +0 -0
  126. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/api/test_locations.py +0 -0
  127. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/api/utilities/__init__.py +0 -0
  128. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/api/utilities/test_filestore_helper.py +0 -0
  129. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/api/utilities/test_hdx_error_handler.py +0 -0
  130. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/api/utilities/test_hdx_state.py +0 -0
  131. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/api/utilities/test_url_utils.py +0 -0
  132. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/conftest.py +0 -0
  133. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/__init__.py +0 -0
  134. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_dataset_add_hapi_error.py +0 -0
  135. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_dataset_core.py +0 -0
  136. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_dataset_resource_generation.py +0 -0
  137. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_organization.py +0 -0
  138. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_resource_view.py +0 -0
  139. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_showcase.py +0 -0
  140. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_update_dataset_resources.py +0 -0
  141. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_update_logic.py +0 -0
  142. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/data/test_vocabulary.py +0 -0
  143. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/facades/__init__.py +0 -0
  144. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/facades/test_infer_arguments.py +0 -0
  145. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/facades/test_keyword_arguments.py +0 -0
  146. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/tests/hdx/facades/test_simple.py +0 -0
  147. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/uv.lock +0 -0
  148. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/workingexample/my_code.py +0 -0
  149. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/workingexample/my_resource.csv +0 -0
  150. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/workingexample/my_resource.xlsx +0 -0
  151. {hdx_python_api-6.7.2 → hdx_python_api-6.7.4}/workingexample/run.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hdx-python-api
3
- Version: 6.7.2
3
+ Version: 6.7.4
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
@@ -36,6 +36,7 @@ upload your datasets to HDX.
36
36
  - [Location Management](#location-management)
37
37
  - [Vocabulary Management](#vocabulary-management)
38
38
  - [Activity Streams](#activity-streams)
39
+ - [HDX Statistics](#hdx-statistics)
39
40
  - [Pipeline State](#pipeline-state)
40
41
  - [Working Examples](#working-examples)
41
42
  - [Project Framework](#project-framework)
@@ -636,6 +637,45 @@ function, for example:
636
637
 
637
638
  dataset.remove_showcase(showcase)
638
639
 
640
+ HDX runs file structure checks on resources, recording schema changes (eg.
641
+ column names, order or types) and whether sheets have merged cells. You can get
642
+ these checks for all of a dataset's resources from HDX:
643
+
644
+ fs_check_info = dataset.get_fs_check_info()
645
+
646
+ This returns a list of dictionaries, each from a resource name to a list of
647
+ check events with keys such as `state`, `timestamp`, `sheet_changes` and
648
+ `hxl_proxy_response`.
649
+
650
+ Similarly, you can get the geopreview shape information of a dataset's
651
+ resources:
652
+
653
+ shape_info = dataset.get_shape_info()
654
+
655
+ HDX can recommend tags for a dataset based on its title and organization
656
+ (`owner_org`). Tags are ordered from most to least recommended:
657
+
658
+ tags = dataset.get_recommended_tags()
659
+
660
+ You can check if the dataset or its organization is in HDX's allowlist for
661
+ automatic loading of resources into the datastore:
662
+
663
+ allowed = dataset.is_allowed_for_datastore()
664
+
665
+ HDX sysadmins can submit all of a dataset's CSV resources to DataPusher+
666
+ (returning False if there are none):
667
+
668
+ submitted = dataset.push_to_datastore()
669
+
670
+ Users who are sysadmins or have permission to manage QA can mark QA of a
671
+ dataset as completed, set a QA field of all its resources or clear a file
672
+ structure check field (`fs_check_info` by default) of all its resources. The
673
+ dataset is updated from the result:
674
+
675
+ dataset.set_qa_completed(True)
676
+ dataset.qa_revise_resources("pii", "false")
677
+ dataset.reset_fs_check()
678
+
639
679
  ### Time Period
640
680
 
641
681
  Time Period is a mandatory field in HDX. It is the earliest start date and
@@ -947,6 +987,38 @@ If the method **set_file_to_upload** is used to supply a file, the resource
947
987
  `last_modified` field is set to now automatically regardless of the value of
948
988
  `data_updated` or whether **mark_data_updated** has been called.
949
989
 
990
+ The file structure check events of a resource (see Dataset Specific
991
+ Operations) can be obtained from its `fs_check_info` field without a call to
992
+ HDX:
993
+
994
+ events = resource.get_fs_check_info()
995
+
996
+ Users who are sysadmins or have permission to manage QA can set or clear a
997
+ file structure check field of a resource. If the key is `fs_check_info` and the
998
+ value is a dictionary, it is appended to the existing check events (the last 20
999
+ are kept):
1000
+
1001
+ resource.revise_fs_check("fs_check_info", {"state": "success", ...})
1002
+ resource.reset_fs_check() # key defaults to fs_check_info
1003
+
1004
+ They can also quarantine a resource (which removes its `shape_info` and
1005
+ geopreview) or take it out of quarantine, and patch other QA fields:
1006
+
1007
+ in_quarantine = resource.is_in_quarantine()
1008
+ resource.set_in_quarantine(True)
1009
+ resource.qa_patch(pii="true", sdc_report_flag="false")
1010
+
1011
+ A resource can be flagged as containing p-codes or as being in HAPI. Setting
1012
+ these flags requires sysadmin or permission to manage p-codes or the in HAPI
1013
+ flag respectively:
1014
+
1015
+ p_coded = resource.is_p_coded() # None if not set
1016
+ resource.set_p_coded(True)
1017
+ in_hapi = resource.is_in_hapi()
1018
+ resource.set_in_hapi(True)
1019
+
1020
+ These methods update the resource from the result.
1021
+
950
1022
  ### Data Dictionary
951
1023
 
952
1024
  A resource can have a data dictionary describing the columns in the data it
@@ -974,11 +1046,36 @@ using the getter:
974
1046
 
975
1047
  data_dictionary = resource.get_hdx_data_dictionary()
976
1048
 
1049
+ To change the data dictionary of a resource already in HDX without sending
1050
+ dataset update notifications, use **update_hdx_data_dictionary**. It patches
1051
+ only the `hdx_data_dictionary` field (so `last_modified` is unchanged), then,
1052
+ if the resource has a datastore, updates the datastore field labels and
1053
+ descriptions without reloading the data. The given columns are merged by
1054
+ `field` into the existing data dictionary in the resource metadata, so read
1055
+ the resource from HDX first. Pass `replace=True` to replace the data
1056
+ dictionary instead and `update_datastore=False` to leave the datastore alone:
1057
+
1058
+ resource = Resource.read_from_hdx(RESOURCE_ID)
1059
+ resource.update_hdx_data_dictionary([
1060
+ {
1061
+ "field": "col_name",
1062
+ "label": "Column Label",
1063
+ "description": "New description.",
1064
+ },
1065
+ ])
1066
+
1067
+ Columns are matched to datastore fields by `field`. A warning is logged for
1068
+ any column with no matching datastore field.
1069
+
977
1070
  ### Datastore
978
1071
 
979
- A resource's data can be loaded into the HDX datastore and queried. HDX
980
- requires an API key for all datastore actions and dumps, including reads of
981
- public resources. To create, update or delete the datastore for a resource:
1072
+ A resource's data can be loaded into the HDX datastore and queried. HDX calls this
1073
+ [Tabular Data Endpoints (TDE)](https://docs.humdata.org/build/hdx-apis/tabular-data-endpoints),
1074
+ and it is available only for resources with TDE enabled (their `datastore_active`
1075
+ field is true). Contact hdx@un.org to request TDE for your datasets. HDX requires an
1076
+ API key for all datastore actions and dumps, including reads of public resources,
1077
+ and limits each IP address to 60 requests per minute. To create, update or delete
1078
+ the datastore for a resource:
982
1079
 
983
1080
  resource.create_datastore(
984
1081
  [{"id": "code", "type": "text"}, {"id": "value", "type": "numeric"}],
@@ -988,6 +1085,22 @@ public resources. To create, update or delete the datastore for a resource:
988
1085
  has_datastore = resource.has_datastore()
989
1086
  resource.delete_datastore()
990
1087
 
1088
+ HDX loads uploaded files into the datastore using DataPusher+. To reload a
1089
+ resource's file into the datastore without uploading it again (so
1090
+ `last_modified` is unchanged), use **submit_to_datapusher**. It returns False
1091
+ if the job was not submitted, eg. because one is already pending. HDX's
1092
+ datastore allowlist and format checks, which apply to automatic submissions
1093
+ on upload, are not applied. **get_datapusher_status** returns the status of
1094
+ the latest job or None if there is none:
1095
+
1096
+ submitted = resource.submit_to_datapusher(ignore_hash=True)
1097
+ status = resource.get_datapusher_status() # eg. status["status"] == "complete"
1098
+
1099
+ HDX sysadmins can instead use **push_to_datastore**, which submits the
1100
+ resource through HDX's wrapper action:
1101
+
1102
+ submitted = resource.push_to_datastore()
1103
+
991
1104
  To download the whole datastore (rather than the resource's file) use
992
1105
  **download_datastore**, which returns the URL and path like **download**.
993
1106
  `file_format` can be `csv` (default), `tsv`, `json` or `xml`, and `filename`
@@ -1008,7 +1121,8 @@ and `include_total`, and passes any other keyword arguments to CKAN:
1008
1121
  records = result["records"]
1009
1122
 
1010
1123
  **get_datastore_records** takes the same query parameters but pages through
1011
- all matching rows, yielding each record (`page_size` defaults to 10000):
1124
+ all matching rows, yielding each record (`page_size` defaults to 10000).
1125
+ `records_format` csv and tsv are not supported; use `search_datastore` for those:
1012
1126
 
1013
1127
  for record in resource.get_datastore_records(filters={"code": "A"}):
1014
1128
  ...
@@ -1020,7 +1134,9 @@ internal `_id` field:
1020
1134
 
1021
1135
  **search_datastore_sql** wraps CKAN's `datastore_search_sql`, running a
1022
1136
  SELECT statement where tables are named by double quoted resource id and
1023
- returning the list of records:
1137
+ returning the list of records. HDX returns at most 32,000 rows per query and
1138
+ cuts off larger results, in which case a warning is logged (see
1139
+ [paging in the TDE docs](https://docs.humdata.org/build/hdx-apis/tabular-data-endpoints/troubleshooting-and-error-handling#pagination)):
1024
1140
 
1025
1141
  records = Resource.search_datastore_sql(
1026
1142
  'SELECT code, SUM(value) AS total FROM "RESOURCE_ID" GROUP BY code'
@@ -1067,6 +1183,11 @@ configuration):
1067
1183
 
1068
1184
  user = User.get_current_user()
1069
1185
 
1186
+ You can get the name of the API token in the configuration and the md5 hash
1187
+ of its owner's email:
1188
+
1189
+ token_info = User.get_current_token_info() # {"email_hash": ..., "token_name": ...}
1190
+
1070
1191
  You can check that the current user has a particular permission to a specific
1071
1192
  organization:
1072
1193
 
@@ -1152,6 +1273,17 @@ since Data Grid membership changes over time, rather than a fixed list):
1152
1273
 
1153
1274
  This returns a sorted list of 3-letter location codes.
1154
1275
 
1276
+ HDX sysadmins can get the Data Grid of a country and clear HDX's cached copy of
1277
+ it:
1278
+
1279
+ data_grid = Location.get_data_grid("AFG")
1280
+ Location.invalidate_data_grid("AFG")
1281
+
1282
+ **get_data_grid** returns a dictionary with keys including `title`, `date`,
1283
+ `stats` and `categories` (or None if not found). Each category has
1284
+ `subcategories`, each with a list of `datasets`. Complementary datasets have
1285
+ `is_complementary` set to True.
1286
+
1155
1287
  You can get the datasets belonging to a location as follows:
1156
1288
 
1157
1289
  datasets = location.get_datasets(**kwargs)
@@ -1223,6 +1355,28 @@ permission to edit the object the activity relates to for these:
1223
1355
  data = activity.get_data()
1224
1356
  diff = activity.get_diff(diff_type="unified") # or "context" or "html"
1225
1357
 
1358
+ ## HDX Statistics
1359
+
1360
+ General statistics about datasets, organizations and users on HDX can be
1361
+ obtained as follows:
1362
+
1363
+ from hdx.api.utilities.hdx_stats import get_general_statistics, push_general_stats
1364
+
1365
+ stats = get_general_statistics()
1366
+
1367
+ Users with permission to manage basic scheduled tasks can compute the statistics
1368
+ and push them to analytics:
1369
+
1370
+ pushed_stats = push_general_stats()
1371
+
1372
+ HDX sysadmins can get the number of activities of each active user (optionally
1373
+ between start and end dates) and lists of active and inactive organizations:
1374
+
1375
+ from hdx.api.utilities.hdx_stats import get_organization_statistics, get_user_statistics
1376
+
1377
+ user_stats = get_user_statistics(start_date="2026-01-01", end_date="2026-06-30")
1378
+ org_stats = get_organization_statistics()
1379
+
1226
1380
  ## Pipeline State
1227
1381
 
1228
1382
  The HDXState class allows the reading and writing of state to a given dataset. Input
@@ -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.7.2'
22
- __version_tuple__ = version_tuple = (6, 7, 2)
21
+ __version__ = version = '6.7.4'
22
+ __version_tuple__ = version_tuple = (6, 7, 4)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -0,0 +1,86 @@
1
+ """HDX site-wide statistics"""
2
+
3
+ from hdx.api.configuration import Configuration
4
+ from hdx.data.hdxobject import HDXError
5
+
6
+
7
+ def _call_stats_action(
8
+ action: str, configuration: Configuration | None, data: dict | None = None
9
+ ) -> dict:
10
+ """Call an HDX statistics action
11
+
12
+ Args:
13
+ action: CKAN action name
14
+ configuration: HDX configuration. Defaults to global configuration.
15
+ data: Parameters to pass to the action. Defaults to None.
16
+
17
+ Returns:
18
+ Statistics dictionary
19
+ """
20
+ if configuration is None:
21
+ configuration = Configuration.read()
22
+ try:
23
+ return configuration.call_remoteckan(action, data or {})
24
+ except Exception as e:
25
+ raise HDXError(f"Failed when trying to call {action}!") from e
26
+
27
+
28
+ def get_general_statistics(configuration: Configuration | None = None) -> dict:
29
+ """Get general statistics about datasets, organizations and users on HDX
30
+
31
+ Args:
32
+ configuration: HDX configuration. Defaults to global configuration.
33
+
34
+ Returns:
35
+ Dictionary with keys datasets, organizations and users
36
+ """
37
+ return _call_stats_action("hdx_general_statistics", configuration)
38
+
39
+
40
+ def push_general_stats(configuration: Configuration | None = None) -> dict:
41
+ """Compute general statistics on HDX and push them to analytics. Requires
42
+ permission to manage basic scheduled tasks.
43
+
44
+ Args:
45
+ configuration: HDX configuration. Defaults to global configuration.
46
+
47
+ Returns:
48
+ Statistics that were pushed
49
+ """
50
+ return _call_stats_action("hdx_push_general_stats", configuration)
51
+
52
+
53
+ def get_user_statistics(
54
+ start_date: str | None = None,
55
+ end_date: str | None = None,
56
+ configuration: Configuration | None = None,
57
+ ) -> dict:
58
+ """Get the number of activities of each active user on HDX, optionally
59
+ between start and end dates. Requires sysadmin.
60
+
61
+ Args:
62
+ start_date: Count activities on or after this date eg. 2026-01-01. Defaults to None.
63
+ end_date: Count activities on or before this date. Defaults to None.
64
+ configuration: HDX configuration. Defaults to global configuration.
65
+
66
+ Returns:
67
+ Dictionary from user id to dictionary with keys id, name and no_of_activities
68
+ """
69
+ data = {}
70
+ if start_date:
71
+ data["start_date"] = start_date
72
+ if end_date:
73
+ data["end_date"] = end_date
74
+ return _call_stats_action("hdx_user_statistics", configuration, data)
75
+
76
+
77
+ def get_organization_statistics(configuration: Configuration | None = None) -> dict:
78
+ """Get active and inactive organizations on HDX. Requires sysadmin.
79
+
80
+ Args:
81
+ configuration: HDX configuration. Defaults to global configuration.
82
+
83
+ Returns:
84
+ Dictionary with keys active_organizations and inactive_organizations
85
+ """
86
+ return _call_stats_action("hdx_organization_statistics", configuration)
@@ -129,7 +129,9 @@ class Activity(HDXObject):
129
129
  result = activity._write_to_hdx(action, data)
130
130
  return [cls(x, configuration=configuration) for x in result]
131
131
  data = dict(data)
132
- limit = data.setdefault("limit", 100)
132
+ # HDX silently caps limit at ckan.activity_list_limit_max
133
+ limit = min(data.get("limit", 100), 100)
134
+ data["limit"] = limit
133
135
  offset = data.get("offset", 0)
134
136
  activities = []
135
137
  while True:
@@ -147,6 +147,14 @@ class Dataset(HDXObject):
147
147
  "list": "package_list",
148
148
  "autocomplete": "package_autocomplete",
149
149
  "create_default_views": "package_create_default_resource_views",
150
+ "fs_check_info": "fs_check_info_show",
151
+ "fs_check_reset": "hdx_fs_check_package_reset",
152
+ "shape_info": "shape_info_show",
153
+ "recommend_tags": "hdx_recommend_tags",
154
+ "allowed_for_datastore": "hdx_is_package_allowed_for_datastore",
155
+ "push_to_datastore": "hdx_push_resource_to_datastore",
156
+ "qa_completed": "hdx_mark_qa_completed",
157
+ "qa_revise_resources": "hdx_qa_package_revise_resource",
150
158
  }
151
159
 
152
160
  def __setitem__(self, key: Any, value: Any) -> None:
@@ -1447,13 +1455,7 @@ class Dataset(HDXObject):
1447
1455
  Returns:
1448
1456
  List of Activity objects
1449
1457
  """
1450
- return Activity.get_activities(
1451
- "package",
1452
- self.data.get("id") or self.data.get("name"),
1453
- self.configuration,
1454
- all_pages,
1455
- **kwargs,
1456
- )
1458
+ return self._get_activities("package", all_pages, **kwargs)
1457
1459
 
1458
1460
  @classmethod
1459
1461
  def autocomplete(
@@ -2333,6 +2335,155 @@ class Dataset(HDXObject):
2333
2335
  return None
2334
2336
  return f"{self.configuration.get_hdx_site_url()}/api/3/action/package_show?id={name_or_id}"
2335
2337
 
2338
+ def get_fs_check_info(self) -> list[dict]:
2339
+ """Get file structure check information for the dataset's resources
2340
+ from HDX. Each list item is a dictionary from resource name to a list
2341
+ of check events, each of which can have keys such as state, timestamp,
2342
+ sheet_changes and hxl_proxy_response.
2343
+
2344
+ Returns:
2345
+ List of dictionaries from resource name to file structure checks
2346
+ """
2347
+ success, result = self._read_from_hdx(
2348
+ "dataset",
2349
+ self.get_name_or_id(prefer_name=False),
2350
+ "id",
2351
+ self.actions()["fs_check_info"],
2352
+ )
2353
+ if success:
2354
+ return result
2355
+ return []
2356
+
2357
+ def _set_from_package(self, package: dict) -> None:
2358
+ """Set the dataset's metadata and resources from a package metadata
2359
+ dictionary returned by HDX
2360
+
2361
+ Args:
2362
+ package: Package metadata dictionary
2363
+
2364
+ Returns:
2365
+ None
2366
+ """
2367
+ self.data = package
2368
+ self.init_resources()
2369
+ self.separate_resources()
2370
+
2371
+ def reset_fs_check(self, key: str = "fs_check_info") -> None:
2372
+ """Clear a file structure check field of all the dataset's resources
2373
+ in HDX. Requires sysadmin or permission to manage QA.
2374
+
2375
+ Args:
2376
+ key: Resource field to clear. Defaults to fs_check_info.
2377
+
2378
+ Returns:
2379
+ None
2380
+ """
2381
+ result = self._write_to_hdx(
2382
+ "fs_check_reset", {"package_id": self.data["id"], "key": key}, "package_id"
2383
+ )
2384
+ self._set_from_package(result["package"])
2385
+
2386
+ def set_qa_completed(self, qa_completed: bool) -> None:
2387
+ """Mark QA of the dataset in HDX as completed or not. Requires
2388
+ sysadmin or permission to manage QA.
2389
+
2390
+ Args:
2391
+ qa_completed: Whether QA is completed
2392
+
2393
+ Returns:
2394
+ None
2395
+ """
2396
+ result = self._write_to_hdx(
2397
+ "qa_completed", {"id": self.data["id"], "qa_completed": qa_completed}, "id"
2398
+ )
2399
+ self._set_from_package(result["package"])
2400
+
2401
+ def qa_revise_resources(self, key: str, value: Any) -> None:
2402
+ """Set a QA field of all the dataset's resources in HDX. Requires
2403
+ sysadmin or permission to manage QA.
2404
+
2405
+ Args:
2406
+ key: Resource field eg. pii
2407
+ value: Value to set
2408
+
2409
+ Returns:
2410
+ None
2411
+ """
2412
+ result = self._write_to_hdx(
2413
+ "qa_revise_resources",
2414
+ {"id": self.data["id"], "key": key, "value": value},
2415
+ "id",
2416
+ )
2417
+ self._set_from_package(result["package"])
2418
+
2419
+ def get_shape_info(self) -> list[dict]:
2420
+ """Get geopreview shape information for the dataset's resources from
2421
+ HDX
2422
+
2423
+ Returns:
2424
+ List of dictionaries from resource name to shape information
2425
+ """
2426
+ success, result = self._read_from_hdx(
2427
+ "dataset",
2428
+ self.get_name_or_id(prefer_name=False),
2429
+ "id",
2430
+ self.actions()["shape_info"],
2431
+ )
2432
+ if success:
2433
+ return result
2434
+ return []
2435
+
2436
+ def get_recommended_tags(self) -> list[str]:
2437
+ """Get tags recommended by HDX for the dataset based on its title and
2438
+ organization. Tags are ordered from most to least recommended.
2439
+
2440
+ Returns:
2441
+ List of recommended tags
2442
+ """
2443
+ data = {"title": self.data.get("title")}
2444
+ organization = self.data.get("owner_org")
2445
+ if organization:
2446
+ data["organization"] = organization
2447
+ try:
2448
+ result = self.configuration.call_remoteckan(
2449
+ self.actions()["recommend_tags"], data
2450
+ )
2451
+ except Exception as e:
2452
+ raise HDXError("Failed when trying to get recommended tags!") from e
2453
+ return [tag["name"] for tag in result]
2454
+
2455
+ def is_allowed_for_datastore(self) -> bool:
2456
+ """Return if the dataset or its organization is in HDX's allowlist for
2457
+ automatic loading of resources into the datastore
2458
+
2459
+ Returns:
2460
+ Whether dataset is allowed for datastore
2461
+ """
2462
+ success, result = self._read_from_hdx(
2463
+ "dataset",
2464
+ self.data["id"],
2465
+ "package_id",
2466
+ self.actions()["allowed_for_datastore"],
2467
+ )
2468
+ if success:
2469
+ return result
2470
+ logger.debug(result)
2471
+ return False
2472
+
2473
+ def push_to_datastore(self) -> bool:
2474
+ """Submit all the dataset's CSV resources to DataPusher+ using HDX's
2475
+ wrapper action. Requires sysadmin.
2476
+
2477
+ Returns:
2478
+ True if submitted, False if not (eg. no CSV resources)
2479
+ """
2480
+ result = self._write_to_hdx(
2481
+ "push_to_datastore", {"dataset_id": self.data["id"]}, "dataset_id"
2482
+ )
2483
+ if not result["success"]:
2484
+ logger.debug(result["message"])
2485
+ return result["success"]
2486
+
2336
2487
  def generate_resource(
2337
2488
  self,
2338
2489
  folder: Path | str,
@@ -9,7 +9,7 @@ from collections import UserDict
9
9
  from collections.abc import Sequence
10
10
  from os.path import isfile
11
11
  from pathlib import Path
12
- from typing import Any, Optional, Union
12
+ from typing import TYPE_CHECKING, Any, Optional, Union
13
13
 
14
14
  from ckanapi.errors import NotFound
15
15
  from hdx.utilities.dictandlist import merge_two_dictionaries
@@ -20,6 +20,9 @@ from hdx.utilities.loader import (
20
20
 
21
21
  from hdx.api.configuration import Configuration
22
22
 
23
+ if TYPE_CHECKING:
24
+ from hdx.data.activity import Activity
25
+
23
26
  logger = logging.getLogger(__name__)
24
27
 
25
28
 
@@ -376,6 +379,30 @@ class HDXObject(UserDict, ABC):
376
379
  **kwargs,
377
380
  )
378
381
 
382
+ def _get_activities(
383
+ self, object_type: str, all_pages: bool = False, **kwargs: Any
384
+ ) -> list["Activity"]:
385
+ """Helper method to get the activity stream of this object
386
+
387
+ Args:
388
+ object_type: One of package, organization, group or user
389
+ all_pages: Whether to get all pages of results. Defaults to False.
390
+ **kwargs: Parameters to pass to Activity.get_activities
391
+
392
+ Returns:
393
+ List of Activity objects
394
+ """
395
+ # Imported here as hdx.data.activity imports this module
396
+ from hdx.data.activity import Activity
397
+
398
+ return Activity.get_activities(
399
+ object_type,
400
+ self.data.get("id") or self.data.get("name"),
401
+ self.configuration,
402
+ all_pages,
403
+ **kwargs,
404
+ )
405
+
379
406
  def _write_to_hdx(
380
407
  self,
381
408
  action: str,
@@ -43,6 +43,8 @@ class Location(HDXObject):
43
43
  "delete": "group_delete",
44
44
  "list": "group_list",
45
45
  "autocomplete": "group_autocomplete",
46
+ "data_grid": "hdx_datagrid_show",
47
+ "invalidate_data_grid": "invalidate_data_completeness_for_location",
46
48
  }
47
49
 
48
50
  @classmethod
@@ -218,6 +220,47 @@ class Location(HDXObject):
218
220
  if len(group["name"]) == 3 and group.get("data_completeness") == "active"
219
221
  )
220
222
 
223
+ @staticmethod
224
+ def get_data_grid(
225
+ iso3: str, configuration: Configuration | None = None
226
+ ) -> dict | None:
227
+ """Get the Data Grid of a country. The dictionary has keys including
228
+ title, date, stats and categories. Each category has subcategories,
229
+ each with a list of datasets. Complementary datasets have
230
+ is_complementary set to True. Requires sysadmin.
231
+
232
+ Args:
233
+ iso3: Country ISO 3-letter code
234
+ configuration: HDX configuration. Defaults to global configuration.
235
+
236
+ Returns:
237
+ Data Grid dictionary or None if not found
238
+ """
239
+ location = Location(configuration=configuration)
240
+ success, result = location._read_from_hdx(
241
+ "location", iso3.lower(), "id", location.actions()["data_grid"]
242
+ )
243
+ if success:
244
+ return result
245
+ logger.debug(result)
246
+ return None
247
+
248
+ @staticmethod
249
+ def invalidate_data_grid(
250
+ iso3: str, configuration: Configuration | None = None
251
+ ) -> None:
252
+ """Clear HDX's cached Data Grid of a country. Requires sysadmin.
253
+
254
+ Args:
255
+ iso3: Country ISO 3-letter code
256
+ configuration: HDX configuration. Defaults to global configuration.
257
+
258
+ Returns:
259
+ None
260
+ """
261
+ location = Location(configuration=configuration)
262
+ location._write_to_hdx("invalidate_data_grid", {"name": iso3.lower()}, "name")
263
+
221
264
  def get_activities(self, all_pages: bool = False, **kwargs: Any) -> list[Activity]:
222
265
  """Get the activity stream of this location. One page of results (whose size
223
266
  is given by limit) is returned unless all_pages is True.
@@ -232,13 +275,7 @@ class Location(HDXObject):
232
275
  Returns:
233
276
  List of Activity objects
234
277
  """
235
- return Activity.get_activities(
236
- "group",
237
- self.data.get("id") or self.data.get("name"),
238
- self.configuration,
239
- all_pages,
240
- **kwargs,
241
- )
278
+ return self._get_activities("group", all_pages, **kwargs)
242
279
 
243
280
  @classmethod
244
281
  def autocomplete(
@@ -326,13 +326,7 @@ class Organization(HDXObject):
326
326
  Returns:
327
327
  List of Activity objects
328
328
  """
329
- return Activity.get_activities(
330
- "organization",
331
- self.data.get("id") or self.data.get("name"),
332
- self.configuration,
333
- all_pages,
334
- **kwargs,
335
- )
329
+ return self._get_activities("organization", all_pages, **kwargs)
336
330
 
337
331
  @classmethod
338
332
  def autocomplete(