chap-core 0.0.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.
Files changed (233) hide show
  1. chap_core-0.0.8/HISTORY.rst +8 -0
  2. chap_core-0.0.8/LICENSE +22 -0
  3. chap_core-0.0.8/MANIFEST.in +10 -0
  4. chap_core-0.0.8/PKG-INFO +55 -0
  5. chap_core-0.0.8/README.md +67 -0
  6. chap_core-0.0.8/chap_core/__init__.py +8 -0
  7. chap_core-0.0.8/chap_core/_legacy/__init__.py +0 -0
  8. chap_core-0.0.8/chap_core/_legacy/file_io.py +50 -0
  9. chap_core-0.0.8/chap_core/_legacy_dataset.py +101 -0
  10. chap_core-0.0.8/chap_core/adaptors/__init__.py +0 -0
  11. chap_core-0.0.8/chap_core/adaptors/gluonts.py +50 -0
  12. chap_core-0.0.8/chap_core/alarms.py +18 -0
  13. chap_core-0.0.8/chap_core/api.py +264 -0
  14. chap_core-0.0.8/chap_core/api_types.py +55 -0
  15. chap_core-0.0.8/chap_core/assessment/__init__.py +0 -0
  16. chap_core-0.0.8/chap_core/assessment/dataset_splitting.py +141 -0
  17. chap_core-0.0.8/chap_core/assessment/forecast.py +83 -0
  18. chap_core-0.0.8/chap_core/assessment/multi_location_evaluator.py +124 -0
  19. chap_core-0.0.8/chap_core/assessment/prediction_evaluator.py +353 -0
  20. chap_core-0.0.8/chap_core/chap_cli.py +133 -0
  21. chap_core-0.0.8/chap_core/cli.py +277 -0
  22. chap_core-0.0.8/chap_core/climate_data/__init__.py +14 -0
  23. chap_core-0.0.8/chap_core/climate_data/external.py +4 -0
  24. chap_core-0.0.8/chap_core/climate_data/gee_legacy.py +97 -0
  25. chap_core-0.0.8/chap_core/climate_data/gridded_data.py +45 -0
  26. chap_core-0.0.8/chap_core/climate_data/meteostat_wrapper.py +242 -0
  27. chap_core-0.0.8/chap_core/climate_data/seasonal_forecasts.py +42 -0
  28. chap_core-0.0.8/chap_core/climate_health.py +1 -0
  29. chap_core-0.0.8/chap_core/climate_predictor.py +84 -0
  30. chap_core-0.0.8/chap_core/data/__init__.py +4 -0
  31. chap_core-0.0.8/chap_core/data/adaptors.py +3 -0
  32. chap_core-0.0.8/chap_core/data/datasets.py +7 -0
  33. chap_core-0.0.8/chap_core/data/gluonts_adaptor/__init__.py +0 -0
  34. chap_core-0.0.8/chap_core/data/gluonts_adaptor/dataset.py +147 -0
  35. chap_core-0.0.8/chap_core/data/gluonts_adaptor/model.py +15 -0
  36. chap_core-0.0.8/chap_core/data_wrangling/__init__.py +0 -0
  37. chap_core-0.0.8/chap_core/data_wrangling/flows.py +132 -0
  38. chap_core-0.0.8/chap_core/data_wrangling/tasks.py +35 -0
  39. chap_core-0.0.8/chap_core/database/__init__.py +0 -0
  40. chap_core-0.0.8/chap_core/database/database.py +42 -0
  41. chap_core-0.0.8/chap_core/database/local_db_cache.py +49 -0
  42. chap_core-0.0.8/chap_core/dataset_protocols.py +73 -0
  43. chap_core-0.0.8/chap_core/datatypes.py +386 -0
  44. chap_core-0.0.8/chap_core/dhis2_interface/ChapProgram.py +126 -0
  45. chap_core-0.0.8/chap_core/dhis2_interface/__init__.py +0 -0
  46. chap_core-0.0.8/chap_core/dhis2_interface/json_parsing.py +120 -0
  47. chap_core-0.0.8/chap_core/dhis2_interface/periods.py +14 -0
  48. chap_core-0.0.8/chap_core/dhis2_interface/pydantic_to_spatiotemporal.py +23 -0
  49. chap_core-0.0.8/chap_core/dhis2_interface/src/Config.py +15 -0
  50. chap_core-0.0.8/chap_core/dhis2_interface/src/HttpRequest.py +11 -0
  51. chap_core-0.0.8/chap_core/dhis2_interface/src/PullAnalytics.py +36 -0
  52. chap_core-0.0.8/chap_core/dhis2_interface/src/PullClimateData.py +2 -0
  53. chap_core-0.0.8/chap_core/dhis2_interface/src/PushResult.py +49 -0
  54. chap_core-0.0.8/chap_core/dhis2_interface/src/__init__.py +0 -0
  55. chap_core-0.0.8/chap_core/dhis2_interface/src/create_data_element_if_not_exists.py +79 -0
  56. chap_core-0.0.8/chap_core/dhis2_interface/src/dhis_json_parser.py +0 -0
  57. chap_core-0.0.8/chap_core/docker_helper_functions.py +57 -0
  58. chap_core-0.0.8/chap_core/external/__init__.py +0 -0
  59. chap_core-0.0.8/chap_core/external/external_model.py +497 -0
  60. chap_core-0.0.8/chap_core/external/mlflow.py +259 -0
  61. chap_core-0.0.8/chap_core/external/python_model.py +40 -0
  62. chap_core-0.0.8/chap_core/external/r_description.py +32 -0
  63. chap_core-0.0.8/chap_core/external/r_model.py +60 -0
  64. chap_core-0.0.8/chap_core/external/r_models.py +16 -0
  65. chap_core-0.0.8/chap_core/external/spes.py +2 -0
  66. chap_core-0.0.8/chap_core/fetch/__init__.py +4 -0
  67. chap_core-0.0.8/chap_core/file_io/__init__.py +1 -0
  68. chap_core-0.0.8/chap_core/file_io/cleaners.py +62 -0
  69. chap_core-0.0.8/chap_core/file_io/example_data_set.py +59 -0
  70. chap_core-0.0.8/chap_core/file_io/external_file.py +17 -0
  71. chap_core-0.0.8/chap_core/file_io/file_paths.py +13 -0
  72. chap_core-0.0.8/chap_core/file_io/load.py +6 -0
  73. chap_core-0.0.8/chap_core/geo_coding/__init__.py +0 -0
  74. chap_core-0.0.8/chap_core/geo_coding/location_lookup.py +111 -0
  75. chap_core-0.0.8/chap_core/geojson.py +59 -0
  76. chap_core-0.0.8/chap_core/geometry.py +148 -0
  77. chap_core-0.0.8/chap_core/google_earth_engine/__init__.py +0 -0
  78. chap_core-0.0.8/chap_core/google_earth_engine/_refactor_gee_era5.py +140 -0
  79. chap_core-0.0.8/chap_core/google_earth_engine/gee_era5.py +246 -0
  80. chap_core-0.0.8/chap_core/google_earth_engine/gee_raw.py +131 -0
  81. chap_core-0.0.8/chap_core/internal_state.py +49 -0
  82. chap_core-0.0.8/chap_core/main.py +85 -0
  83. chap_core-0.0.8/chap_core/model_spec.py +97 -0
  84. chap_core-0.0.8/chap_core/omnipy_lib.py +29 -0
  85. chap_core-0.0.8/chap_core/pandas_adaptors.py +12 -0
  86. chap_core-0.0.8/chap_core/plotting/__init__.py +1 -0
  87. chap_core-0.0.8/chap_core/plotting/plotting.py +70 -0
  88. chap_core-0.0.8/chap_core/plotting/prediction_plot.py +179 -0
  89. chap_core-0.0.8/chap_core/predictor/__init__.py +29 -0
  90. chap_core-0.0.8/chap_core/predictor/feature_spec.py +30 -0
  91. chap_core-0.0.8/chap_core/predictor/naive_estimator.py +50 -0
  92. chap_core-0.0.8/chap_core/predictor/naive_predictor.py +131 -0
  93. chap_core-0.0.8/chap_core/predictor/poisson.py +24 -0
  94. chap_core-0.0.8/chap_core/predictor/protocol.py +50 -0
  95. chap_core-0.0.8/chap_core/reports/__init__.py +106 -0
  96. chap_core-0.0.8/chap_core/rest_api.py +169 -0
  97. chap_core-0.0.8/chap_core/rest_api_src/__init__.py +0 -0
  98. chap_core-0.0.8/chap_core/rest_api_src/_legacy.py +57 -0
  99. chap_core-0.0.8/chap_core/rest_api_src/data_models.py +27 -0
  100. chap_core-0.0.8/chap_core/rest_api_src/generate_rest_api.py +57 -0
  101. chap_core-0.0.8/chap_core/rest_api_src/worker_functions.py +139 -0
  102. chap_core-0.0.8/chap_core/runners/__init__.py +0 -0
  103. chap_core-0.0.8/chap_core/runners/command_line_runner.py +44 -0
  104. chap_core-0.0.8/chap_core/runners/conda_runner.py +17 -0
  105. chap_core-0.0.8/chap_core/runners/docker_runner.py +48 -0
  106. chap_core-0.0.8/chap_core/runners/runner.py +20 -0
  107. chap_core-0.0.8/chap_core/services/__init__.py +0 -0
  108. chap_core-0.0.8/chap_core/services/cache_manager.py +18 -0
  109. chap_core-0.0.8/chap_core/simulation/__init__.py +0 -0
  110. chap_core-0.0.8/chap_core/simulation/random_noise_simulator.py +22 -0
  111. chap_core-0.0.8/chap_core/simulation/seasonal_simulator.py +76 -0
  112. chap_core-0.0.8/chap_core/simulation/simulator.py +23 -0
  113. chap_core-0.0.8/chap_core/spatio_temporal_data/__init__.py +0 -0
  114. chap_core-0.0.8/chap_core/spatio_temporal_data/multi_country_dataset.py +88 -0
  115. chap_core-0.0.8/chap_core/spatio_temporal_data/omnipy_spatio_temporal_dataset.py +82 -0
  116. chap_core-0.0.8/chap_core/spatio_temporal_data/temporal_dataclass.py +400 -0
  117. chap_core-0.0.8/chap_core/testing/__init__.py +0 -0
  118. chap_core-0.0.8/chap_core/testing/estimators.py +18 -0
  119. chap_core-0.0.8/chap_core/testing/external_model.py +14 -0
  120. chap_core-0.0.8/chap_core/time_period/__init__.py +15 -0
  121. chap_core-0.0.8/chap_core/time_period/_legacy_implementation.py +90 -0
  122. chap_core-0.0.8/chap_core/time_period/dataclasses.py +78 -0
  123. chap_core-0.0.8/chap_core/time_period/date_util_wrapper.py +706 -0
  124. chap_core-0.0.8/chap_core/time_period/delta.py +10 -0
  125. chap_core-0.0.8/chap_core/time_period/multi_resolution.py +15 -0
  126. chap_core-0.0.8/chap_core/time_period/pandas_wrappers.py +9 -0
  127. chap_core-0.0.8/chap_core/time_period/period_range.py +65 -0
  128. chap_core-0.0.8/chap_core/time_period/protocols.py +42 -0
  129. chap_core-0.0.8/chap_core/time_period/relationships.py +12 -0
  130. chap_core-0.0.8/chap_core/training_control.py +48 -0
  131. chap_core-0.0.8/chap_core/transformations/__init__.py +0 -0
  132. chap_core-0.0.8/chap_core/transformations/covid_mask.py +26 -0
  133. chap_core-0.0.8/chap_core/util.py +39 -0
  134. chap_core-0.0.8/chap_core/worker/__init__.py +0 -0
  135. chap_core-0.0.8/chap_core/worker/background_tasks_worker.py +73 -0
  136. chap_core-0.0.8/chap_core/worker/interface.py +25 -0
  137. chap_core-0.0.8/chap_core/worker/rq_worker.py +76 -0
  138. chap_core-0.0.8/chap_core.egg-info/PKG-INFO +55 -0
  139. chap_core-0.0.8/chap_core.egg-info/SOURCES.txt +232 -0
  140. chap_core-0.0.8/chap_core.egg-info/dependency_links.txt +1 -0
  141. chap_core-0.0.8/chap_core.egg-info/entry_points.txt +3 -0
  142. chap_core-0.0.8/chap_core.egg-info/not-zip-safe +1 -0
  143. chap_core-0.0.8/chap_core.egg-info/requires.txt +36 -0
  144. chap_core-0.0.8/chap_core.egg-info/top_level.txt +1 -0
  145. chap_core-0.0.8/docs/diagrams/uml_use_case_exercise.png +0 -0
  146. chap_core-0.0.8/setup.cfg +22 -0
  147. chap_core-0.0.8/setup.py +85 -0
  148. chap_core-0.0.8/tests/__init__.py +7 -0
  149. chap_core-0.0.8/tests/api/test_json.py +18 -0
  150. chap_core-0.0.8/tests/categorical.py +22 -0
  151. chap_core-0.0.8/tests/climate_data/__init__.py +1 -0
  152. chap_core-0.0.8/tests/climate_data/dhis2/__init__.py +0 -0
  153. chap_core-0.0.8/tests/climate_data/dhis2/test_json_parsing.py +97 -0
  154. chap_core-0.0.8/tests/climate_data/fixtures/test_chapdata-bombali-jan2022-dec2022.zip +0 -0
  155. chap_core-0.0.8/tests/climate_data/test_gee_database.py +35 -0
  156. chap_core-0.0.8/tests/climate_data/test_gee_era5.py +356 -0
  157. chap_core-0.0.8/tests/climate_data/test_mocking.py +12 -0
  158. chap_core-0.0.8/tests/conftest.py +88 -0
  159. chap_core-0.0.8/tests/data_fixtures.py +90 -0
  160. chap_core-0.0.8/tests/data_wrangling/__init__.py +0 -0
  161. chap_core-0.0.8/tests/data_wrangling/test_standardize_data.py +143 -0
  162. chap_core-0.0.8/tests/data_wrangling/test_standardize_laos_data.py +110 -0
  163. chap_core-0.0.8/tests/external/__init__.py +0 -0
  164. chap_core-0.0.8/tests/external/command_line_model_predict.sh +1 -0
  165. chap_core-0.0.8/tests/external/command_line_model_train.sh +0 -0
  166. chap_core-0.0.8/tests/external/conftest.py +34 -0
  167. chap_core-0.0.8/tests/external/external_model_env.yml +6 -0
  168. chap_core-0.0.8/tests/external/test_docker.py +30 -0
  169. chap_core-0.0.8/tests/external/test_external_models.py +89 -0
  170. chap_core-0.0.8/tests/external/util.py +32 -0
  171. chap_core-0.0.8/tests/integration/rest_api/__init__.py +0 -0
  172. chap_core-0.0.8/tests/integration/rest_api/test_integration.py +106 -0
  173. chap_core-0.0.8/tests/integration/rest_api/testdata/traning_prediction_data.zip +0 -0
  174. chap_core-0.0.8/tests/mock_predictor_script.py +52 -0
  175. chap_core-0.0.8/tests/mock_predictor_script_future_climate.csv +240 -0
  176. chap_core-0.0.8/tests/mock_predictor_script_train.csv +240 -0
  177. chap_core-0.0.8/tests/mocks.py +25 -0
  178. chap_core-0.0.8/tests/plotting/__init__.py +0 -0
  179. chap_core-0.0.8/tests/plotting/test_plotting.py +94 -0
  180. chap_core-0.0.8/tests/predictor/__init__.py +0 -0
  181. chap_core-0.0.8/tests/predictor/test_multi_region_predictor.py +18 -0
  182. chap_core-0.0.8/tests/predictor/test_predictor.py +22 -0
  183. chap_core-0.0.8/tests/property_tests/test.py +2 -0
  184. chap_core-0.0.8/tests/runners/docker_example_image/Dockerfile +5 -0
  185. chap_core-0.0.8/tests/runners/test_runners.py +28 -0
  186. chap_core-0.0.8/tests/services/test_cache_manager.py +13 -0
  187. chap_core-0.0.8/tests/simulation/some_tests.py +0 -0
  188. chap_core-0.0.8/tests/simulation/test_random_noise_simulator.py +9 -0
  189. chap_core-0.0.8/tests/simulation/test_seasonal_simulator.py +13 -0
  190. chap_core-0.0.8/tests/simulation/test_simulation_integration.py +13 -0
  191. chap_core-0.0.8/tests/single_assessment/__init__.py +0 -0
  192. chap_core-0.0.8/tests/single_assessment/test.py +47 -0
  193. chap_core-0.0.8/tests/single_assessment/test_multi_location_evaluator.py +58 -0
  194. chap_core-0.0.8/tests/spatio_temporal_data/__init__.py +0 -0
  195. chap_core-0.0.8/tests/spatio_temporal_data/test_omnipy_spatio_temporal_dataset.py +146 -0
  196. chap_core-0.0.8/tests/test_api.py +52 -0
  197. chap_core-0.0.8/tests/test_background_tasks.py +41 -0
  198. chap_core-0.0.8/tests/test_chap_cli.py +43 -0
  199. chap_core-0.0.8/tests/test_cli.py +14 -0
  200. chap_core-0.0.8/tests/test_climate_health.py +21 -0
  201. chap_core-0.0.8/tests/test_climate_predictor.py +52 -0
  202. chap_core-0.0.8/tests/test_dataset.py +6 -0
  203. chap_core-0.0.8/tests/test_dataset_splitting.py +48 -0
  204. chap_core-0.0.8/tests/test_datatypes.py +75 -0
  205. chap_core-0.0.8/tests/test_dhis2_interface.py +55 -0
  206. chap_core-0.0.8/tests/test_external_file.py +18 -0
  207. chap_core-0.0.8/tests/test_external_model_evaluation_acceptance.py +116 -0
  208. chap_core-0.0.8/tests/test_forecast.py +53 -0
  209. chap_core-0.0.8/tests/test_geojson.py +28 -0
  210. chap_core-0.0.8/tests/test_geometry.py +7 -0
  211. chap_core-0.0.8/tests/test_gluonts_adaptor.py +87 -0
  212. chap_core-0.0.8/tests/test_gridded_data.py +23 -0
  213. chap_core-0.0.8/tests/test_integration.py +10 -0
  214. chap_core-0.0.8/tests/test_json_parsing.py +34 -0
  215. chap_core-0.0.8/tests/test_location_lookup.py +110 -0
  216. chap_core-0.0.8/tests/test_meteostat_wrapper.py +219 -0
  217. chap_core-0.0.8/tests/test_metrics.py +1 -0
  218. chap_core-0.0.8/tests/test_mock_predictor_script.py +14 -0
  219. chap_core-0.0.8/tests/test_model_building_acceptance.py +21 -0
  220. chap_core-0.0.8/tests/test_model_spec.py +28 -0
  221. chap_core-0.0.8/tests/test_multi_country_dataset.py +9 -0
  222. chap_core-0.0.8/tests/test_naive_estimator.py +7 -0
  223. chap_core-0.0.8/tests/test_r_description.py +8 -0
  224. chap_core-0.0.8/tests/test_report.py +56 -0
  225. chap_core-0.0.8/tests/test_seasonal_forecast.py +25 -0
  226. chap_core-0.0.8/tests/test_spatio_temporal_dict.py +70 -0
  227. chap_core-0.0.8/tests/test_temporal_dataclass.py +27 -0
  228. chap_core-0.0.8/tests/time_period/__init__.py +1 -0
  229. chap_core-0.0.8/tests/time_period/test_bnpdataclass.py +34 -0
  230. chap_core-0.0.8/tests/time_period/test_data_objects.py +37 -0
  231. chap_core-0.0.8/tests/time_period/test_dateutil_wrapper.py +242 -0
  232. chap_core-0.0.8/tests/time_period/test_multi_resolution.py +48 -0
  233. chap_core-0.0.8/tests/time_period/test_period_range.py +35 -0
@@ -0,0 +1,8 @@
1
+ =======
2
+ History
3
+ =======
4
+
5
+ 0.0.1 (2024-01-23)
6
+ ------------------
7
+
8
+ * First release on PyPI.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024, Sandvelab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,10 @@
1
+ include CONTRIBUTING.rst
2
+ include HISTORY.rst
3
+ include LICENSE
4
+ include README.rst
5
+
6
+ recursive-include tests *
7
+ recursive-exclude * __pycache__
8
+ recursive-exclude * *.py[co]
9
+
10
+ recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.1
2
+ Name: chap_core
3
+ Version: 0.0.8
4
+ Summary: Main repository for joint programming project on climate health
5
+ Home-page: https://github.com/dhis2/chap-core
6
+ Author: Sandvelab
7
+ Author-email: knutdrand@gmail.com
8
+ License: MIT license
9
+ Keywords: chap_core
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Natural Language :: English
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Requires-Python: >=3.9
17
+ License-File: LICENSE
18
+ Requires-Dist: virtualenv
19
+ Requires-Dist: numpy<2.0
20
+ Requires-Dist: bionumpy
21
+ Requires-Dist: pandas
22
+ Requires-Dist: plotly
23
+ Requires-Dist: scikit-learn
24
+ Requires-Dist: matplotlib
25
+ Requires-Dist: diskcache
26
+ Requires-Dist: geopy
27
+ Requires-Dist: pooch
28
+ Requires-Dist: python-dateutil
29
+ Requires-Dist: meteostat
30
+ Requires-Dist: cyclopts
31
+ Requires-Dist: requests
32
+ Requires-Dist: fastapi
33
+ Requires-Dist: pydantic>=2.0
34
+ Requires-Dist: pyyaml
35
+ Requires-Dist: geopandas
36
+ Requires-Dist: libpysal
37
+ Requires-Dist: docker
38
+ Requires-Dist: scipy
39
+ Requires-Dist: gitpython
40
+ Requires-Dist: earthengine-api
41
+ Requires-Dist: python-dotenv
42
+ Requires-Dist: rq
43
+ Requires-Dist: python-multipart
44
+ Requires-Dist: uvicorn
45
+ Requires-Dist: pydantic-geojson
46
+ Requires-Dist: annotated_types
47
+ Requires-Dist: pycountry
48
+ Requires-Dist: unidecode
49
+ Requires-Dist: httpx
50
+ Requires-Dist: earthengine-api
51
+ Requires-Dist: mlflow
52
+ Requires-Dist: gluonts
53
+ Requires-Dist: xarray
54
+
55
+ Chap Core
@@ -0,0 +1,67 @@
1
+ # Climate Health Analysis Platform (CHAP)
2
+ CHAP offers a platform for analysing the relationship between climate and health. The platform is designed to be modular and flexible, allowing for easy integration of new models and data sources. The platform is designed to be used by researchers and public health professionals to forecast and assess the impact of climate on health outcomes.
3
+
4
+ # Installation
5
+
6
+ Basic installation can be done using pip:
7
+
8
+ $ pip install git+https://github.com/dhis2/chap-core.git
9
+
10
+ If running models, you may also need to install:
11
+
12
+ - Docker, if running a model that runs through docker
13
+ - [pyenv](https://github.com/pyenv/pyenv?tab=readme-ov-file#installation), if running a model that uses Python virtual environments
14
+
15
+ # Documentation
16
+
17
+ The main documentation is located at [https://dhis2.github.io/chap-core/](https://dhis2.github.io/chap-core/).
18
+
19
+ # Usage
20
+
21
+ The following shows basic usage of the platform. Follow the link to the documentation above for more details.
22
+
23
+ ## Evaluate a public model on public data
24
+ CHAP supports evaluating models that are defined using the MLflow specification for machine learning models (link coming). Such models can e.g. exist in Github repositories. CHAP also has some built-in example data that can be used to evaluate models. The following example shows how to evaluate an Ewars model located on Github ([https://github.com/sandvelab/chap_auto_ewars](https://github.com/sandvelab/chap_auto_ewars)) using the ISMIP dataset:
25
+
26
+ ```bash
27
+ chap evaluate --model-name https://github.com/sandvelab/chap_auto_ewars --dataset-name ISIMIP_dengue_harmonized --dataset-country brazil
28
+ ```
29
+
30
+ The above example requires that you have installed chap with pip and also that you have Docker available.
31
+
32
+
33
+ ## Fetching Polygon Data
34
+ Fetch polygons for regions of interest in a country (on admin1 level). The following example fetches polygons for two regions in Norway
35
+
36
+ ```python
37
+ import chap_core.fetch
38
+
39
+ polygons = chap_core.fetch.get_area_polygons('Norway', ['Oslo', 'Akershus'])
40
+ assert [feature.id for feature in polygons.features] == ['Oslo', 'Akershus']
41
+ ```
42
+ Region names that are not recognized are skipped:
43
+
44
+ ```python
45
+ polygons = climate_health.fetch.get_area_polygons('Norway', ['Oslo', 'Akershus', 'Unknown'])
46
+ assert [feature.id for feature in polygons.features] == ['Oslo', 'Akershus']
47
+ ```
48
+
49
+ ## Fetching climate data
50
+ Fetching climate data through Google Earth Engine. The following example fetches temperature data from the ERA5 dataset for the regions of interest. You need to have an an account at gee, and pass the credentials to the method to make this work: https://developers.google.com/earth-engine/guides/auth
51
+
52
+ ```python
53
+
54
+ import chap_core.fetch
55
+
56
+ credentials = dict(account='demoaccount@demo.gserviceaccount.com', private_key='private_key')
57
+ polygons = open("polygon_file.geojson").read()
58
+ start_period = '202001' # January 2020
59
+ end_period = '202011' # December 2020
60
+ band_names = ['temperature_2m', 'total_precipitation_sum']
61
+ data = chap_core.fetch.gee_era5(credentials, polygons, start_period, end_period, band_names)
62
+ print(data)
63
+ start_week = '2020W03' # Week 3 of 2020
64
+ end_week = '2020W05' # Week 5 of 2020
65
+ data = chap_core.fetch.gee_era5(credentials, polygons, start_week, end_week, band_names)
66
+ print(data)
67
+ ```
@@ -0,0 +1,8 @@
1
+ """Top-level package for chap-core."""
2
+
3
+ __author__ = """Sandvelab"""
4
+ __email__ = "knutdrand@gmail.com"
5
+ __version__ = "0.0.8"
6
+
7
+ from . import fetch
8
+ from . import data
File without changes
@@ -0,0 +1,50 @@
1
+ from typing import List
2
+
3
+ import chap_core.time_period.dataclasses as dc
4
+ from chap_core.time_period import TimePeriod, Month, Day, Year
5
+
6
+
7
+ def parse_period_string(time_string: str) -> TimePeriod:
8
+ period = TimePeriod.parse(time_string)
9
+ return period
10
+
11
+
12
+ def write_time_series_data(data):
13
+ def topandas(self):
14
+ data = pd.DataFrame(
15
+ {
16
+ "time_period": self.time_period.topandas(),
17
+ "rainfall": self.rainfall,
18
+ "mean_temperature": self.mean_temperature,
19
+ "disease_cases": self.disease_cases,
20
+ }
21
+ )
22
+ return data
23
+
24
+ to_pandas = topandas
25
+
26
+ def to_csv(self, csv_file: str, **kwargs):
27
+ """Write data to a csv file."""
28
+ data = self.to_pandas()
29
+ data.to_csv(csv_file, index=False, **kwargs)
30
+
31
+
32
+ def parse_periods_strings(time_strings: List[str]) -> dc.Period:
33
+ periods = [parse_period_string(time_string) for time_string in time_strings]
34
+ if not periods:
35
+ return dc.Period.empty()
36
+ t = type(periods[0])
37
+ assert all(type(period) == t for period in periods), periods
38
+
39
+ if t == Year:
40
+ return dc.Year([period.year for period in periods])
41
+ if t == Month:
42
+ return dc.Month(
43
+ [period.year for period in periods], [period.month for period in periods]
44
+ )
45
+ elif t == Day:
46
+ return dc.Day(
47
+ [period.year for period in periods],
48
+ [period.month for period in periods],
49
+ [period.day for period in periods],
50
+ )
@@ -0,0 +1,101 @@
1
+ from typing import Protocol, TypeAlias, Union, Iterable, TypeVar, Tuple
2
+
3
+ import pandas as pd
4
+ from pydantic import BaseModel
5
+
6
+ from chap_core.datatypes import Location
7
+ from chap_core.time_period.dataclasses import Period
8
+
9
+ SpatialIndexType: TypeAlias = Union[str, Location]
10
+ TemporalIndexType: TypeAlias = Union[Period, Iterable[Period], slice]
11
+
12
+
13
+ FeaturesT = TypeVar("FeaturesT")
14
+
15
+
16
+ class ClimateData(BaseModel):
17
+ temperature: float
18
+ rainfall: float
19
+ humidity: float
20
+
21
+
22
+ class DataType(BaseModel):
23
+ disease_cases: int
24
+ climate_data: ClimateData
25
+
26
+
27
+ class IsTemporalDataSet(Protocol[FeaturesT]):
28
+ def restrict_time_period(
29
+ self, start_period: Period = None, end_period: Period = None
30
+ ) -> "IsTemporalDataSet[FeaturesT]": ...
31
+
32
+ def to_tidy_dataframe(self) -> pd.DataFrame: ...
33
+
34
+ @classmethod
35
+ def from_tidy_dataframe(cls, df: pd.DataFrame) -> "IsSpatialDataSet[FeaturesT]": ...
36
+
37
+
38
+ class TemporalArray:
39
+ def __init__(self, time_index, data):
40
+ self._time_index = time_index
41
+ self._data = data
42
+
43
+ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
44
+ return ufunc
45
+
46
+
47
+ class IsSpatialDataSet(Protocol[FeaturesT]):
48
+ def get_locations(
49
+ self, location: Iterable[SpatialIndexType]
50
+ ) -> "IsSpatialDataSet[FeaturesT]": ...
51
+
52
+ def get_location(self, location: SpatialIndexType) -> FeaturesT: ...
53
+
54
+ def locations(self) -> Iterable[SpatialIndexType]: ...
55
+
56
+ def data(self) -> Iterable[FeaturesT]: ...
57
+
58
+ def location_items(self) -> Iterable[Tuple[SpatialIndexType, FeaturesT]]: ...
59
+
60
+ def to_tidy_dataframe(self) -> pd.DataFrame: ...
61
+
62
+ @classmethod
63
+ def from_tidy_dataframe(cls, df: pd.DataFrame) -> "IsSpatialDataSet[FeaturesT]": ...
64
+
65
+
66
+ class IsSpatioTemporalDataSet(Protocol[FeaturesT]):
67
+ dataclass = ...
68
+
69
+ def get_data_for_locations(
70
+ self, location: Iterable[SpatialIndexType]
71
+ ) -> "IsSpatioTemporalDataSet[FeaturesT]": ...
72
+
73
+ def get_data_for_location(self, location: SpatialIndexType) -> FeaturesT: ...
74
+
75
+ def restrict_time_period(
76
+ self, start_period: Period = None, end_period: Period = None
77
+ ) -> "IsSpatioTemporalDataSet[FeaturesT]": ...
78
+
79
+ def start_time(self) -> Period: ...
80
+
81
+ def end_time(self) -> Period: ...
82
+
83
+ def locations(self) -> Iterable[SpatialIndexType]: ...
84
+
85
+ def data(self) -> Iterable[FeaturesT]: ...
86
+
87
+ def location_items(
88
+ self,
89
+ ) -> Iterable[Tuple[SpatialIndexType, IsTemporalDataSet[FeaturesT]]]: ...
90
+
91
+ def to_tidy_dataframe(self) -> pd.DataFrame: ...
92
+
93
+ @classmethod
94
+ def from_tidy_dataframe(
95
+ cls, df: pd.DataFrame
96
+ ) -> "IsSpatioTemporalDataSet[FeaturesT]": ...
97
+
98
+ def to_csv(self, file_name: str): ...
99
+
100
+ @classmethod
101
+ def from_csv(self, file_name: str) -> "IsSpatioTemporalDataSet[FeaturesT]": ...
File without changes
@@ -0,0 +1,50 @@
1
+ from gluonts.model.estimator import Estimator
2
+ from gluonts.dataset.common import ListDataset
3
+ from gluonts.model.predictor import Predictor
4
+ from ..data import DataSet
5
+ from ..data.gluonts_adaptor.dataset import DataSetAdaptor
6
+ from ..datatypes import Samples
7
+ from ..time_period import PeriodRange
8
+ from pathlib import Path
9
+ from dataclasses import dataclass
10
+
11
+
12
+ @dataclass
13
+ class GluonTSPredictor:
14
+ gluonts_predictor: Predictor
15
+
16
+ def predict(
17
+ self, history: DataSet, future_data: DataSet, num_samples=100
18
+ ) -> DataSet:
19
+ gluonts_dataset = DataSetAdaptor.to_gluonts_testinstances(
20
+ history, future_data, self.gluonts_predictor.prediction_length
21
+ )
22
+ forecasts = self.gluonts_predictor.predict(
23
+ gluonts_dataset, num_samples=num_samples
24
+ )
25
+ data = {
26
+ location: Samples(
27
+ PeriodRange.from_pandas(forecast.index), forecast.samples.T
28
+ )
29
+ for location, forecast in zip(history.keys(), forecasts)
30
+ }
31
+ return DataSet(data)
32
+
33
+ def save(self, filename: str):
34
+ filepath = Path(filename)
35
+ filepath.mkdir(exist_ok=True, parents=True)
36
+ self.gluonts_predictor.serialize(filepath)
37
+
38
+ @classmethod
39
+ def load(cls, filename: str):
40
+ return GluonTSPredictor(Predictor.deserialize(Path(filename)))
41
+
42
+
43
+ @dataclass
44
+ class GluonTSEstimator:
45
+ gluont_ts_estimator: Estimator
46
+
47
+ def train(self, dataset: DataSet) -> GluonTSPredictor:
48
+ gluonts_dataset = DataSetAdaptor.to_gluonts(dataset)
49
+ ds = ListDataset(gluonts_dataset, freq="m")
50
+ return GluonTSPredictor(self.gluont_ts_estimator.train(ds))
@@ -0,0 +1,18 @@
1
+ from typing import Iterable
2
+
3
+ import numpy as np
4
+ from pydantic import BaseModel
5
+
6
+ # Epidemioglical week: different from calendar week
7
+ # https://www.cdc.gov/flu/weekly/overview.htm
8
+
9
+
10
+ class OutbreakParameters(BaseModel):
11
+ endemic_factor: float
12
+ probability_threshold: float
13
+
14
+
15
+ def outbreak_prediction(
16
+ parameters: OutbreakParameters, case_samples: Iterable[float]
17
+ ) -> bool:
18
+ return np.mean()
@@ -0,0 +1,264 @@
1
+ import logging
2
+ import json
3
+
4
+ from .assessment.forecast import forecast as do_forecast
5
+ import zipfile
6
+ from pathlib import Path
7
+ from typing import Optional, List
8
+
9
+ import numpy as np
10
+
11
+ from .assessment.dataset_splitting import train_test_split_with_weather
12
+ from .datatypes import (
13
+ HealthData,
14
+ ClimateData,
15
+ HealthPopulationData,
16
+ SimpleClimateData,
17
+ FullData,
18
+ )
19
+ from .dhis2_interface.json_parsing import (
20
+ predictions_to_datavalue,
21
+ parse_disease_data,
22
+ json_to_pandas,
23
+ parse_population_data,
24
+ )
25
+ from .external.external_model import get_model_from_directory_or_github_url
26
+ from .file_io.example_data_set import DataSetType, datasets
27
+ from .geojson import geojson_to_graph, NeighbourGraph
28
+ from .plotting.prediction_plot import plot_forecast_from_summaries
29
+ from .predictor import get_model
30
+ from .spatio_temporal_data.temporal_dataclass import DataSet
31
+ import dataclasses
32
+
33
+ from .time_period.date_util_wrapper import delta_month, Month
34
+
35
+ from .transformations.covid_mask import mask_covid_data
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ class DummyControl:
41
+ def set_status(self, status):
42
+ pass
43
+
44
+ @property
45
+ def current_control(self):
46
+ return None
47
+
48
+
49
+ @dataclasses.dataclass
50
+ class AreaPolygons:
51
+ shape_file: str
52
+
53
+
54
+ @dataclasses.dataclass
55
+ class PredictionData:
56
+ area_polygons: AreaPolygons = None
57
+ health_data: DataSet[HealthData] = None
58
+ climate_data: DataSet[ClimateData] = None
59
+ population_data: DataSet[HealthPopulationData] = None
60
+ disease_id: Optional[str] = None
61
+ features: List[object] = None
62
+
63
+
64
+ def extract_disease_name(health_data: dict) -> str:
65
+ return health_data["rows"][0][0]
66
+
67
+
68
+ def read_zip_folder(zip_file_path: str) -> PredictionData:
69
+ # read zipfile, create PredictionData
70
+ print(zip_file_path)
71
+ ziparchive = zipfile.ZipFile(zip_file_path)
72
+ expected_files = {
73
+ "area_polygons": "orgUnits.geojson",
74
+ "disease": "disease.json",
75
+ "population": "population.json",
76
+ "temperature": "temperature.json",
77
+ "precipitation": "precipitation.json",
78
+ }
79
+ json_data = json.load(ziparchive.open(expected_files["disease"]))
80
+ name_mapping = {"time_period": 2, "disease_cases": 3, "location": 1}
81
+ disease = parse_disease_data(json_data, name_mapping=name_mapping)
82
+ disease_id = extract_disease_name(json_data)
83
+ temperature_json = json.load(ziparchive.open(expected_files["temperature"]))
84
+ name_mapping = {"time_period": 2, "mean_temperature": 3, "location": 1}
85
+ temperature = json_to_pandas(temperature_json, name_mapping)
86
+
87
+ precipitation_json = json.load(ziparchive.open(expected_files["temperature"]))
88
+ name_mapping = {"time_period": 2, "precipitation": 3, "location": 1}
89
+ precipitation = json_to_pandas(precipitation_json, name_mapping)
90
+
91
+ assert np.all(precipitation.time_period == temperature.time_period)
92
+ assert np.all(precipitation.location == temperature.location)
93
+
94
+ temperature["rainfall"] = precipitation["precipitation"]
95
+ temperature["rainfall"] = temperature["rainfall"].astype(float)
96
+ temperature["mean_temperature"] = temperature["mean_temperature"].astype(float)
97
+
98
+ features = json.load(ziparchive.open(expected_files["area_polygons"]))["features"]
99
+ climate = DataSet.from_pandas(temperature, dataclass=SimpleClimateData)
100
+
101
+ population_json = json.load(ziparchive.open(expected_files["population"]))
102
+ population = parse_population_data(population_json)
103
+ graph_file_name = ""
104
+ graph = NeighbourGraph.from_geojson_file(
105
+ ziparchive.open(expected_files["area_polygons"])
106
+ )
107
+ print(graph)
108
+ if False:
109
+ graph_file_name = Path(zip_file_path).with_suffix(".graph")
110
+ area_polygons_file = ziparchive.open(expected_files["area_polygons"])
111
+ geojson_to_graph(area_polygons_file, graph_file_name)
112
+ # geojson_to_shape(area_polygons_file, shape_file_name)
113
+
114
+ # geojson_to_shape(str(zip_file_path) + "!area_polygons", shape_file_name)
115
+
116
+ return PredictionData(
117
+ health_data=disease,
118
+ climate_data=climate,
119
+ population_data=population,
120
+ area_polygons=graph,
121
+ disease_id=disease_id,
122
+ features=features,
123
+ )
124
+
125
+ out_data = {}
126
+
127
+
128
+ # ...
129
+
130
+
131
+ def dhis_zip_flow(
132
+ zip_file_path: str,
133
+ out_json: Optional[str] = None,
134
+ model_name=None,
135
+ n_months=4,
136
+ docker_filename: Optional[str] = None,
137
+ ) -> List[dict] | None:
138
+ data: PredictionData = read_zip_folder(zip_file_path)
139
+ json_body = train_on_prediction_data(data, model_name, n_months, docker_filename)
140
+ if out_json is not None:
141
+ with open(out_json, "w") as f:
142
+ json.dump(json_body, f)
143
+ return None
144
+ else:
145
+ return json_body
146
+
147
+
148
+ def train_on_prediction_data(
149
+ data,
150
+ model_name=None,
151
+ n_months=4,
152
+ docker_filename=None,
153
+ model_path=None,
154
+ control=None,
155
+ ):
156
+ if control is None:
157
+ control = DummyControl()
158
+ control.set_status("Preprocessing")
159
+ if model_name == "external":
160
+ model = get_model_from_directory_or_github_url(model_path)
161
+ else:
162
+ model = get_model(model_name)()
163
+ start_timestamp = min(
164
+ data.health_data.start_timestamp, data.climate_data.start_timestamp
165
+ )
166
+ end_timestamp = max(data.health_data.end_timestamp, data.climate_data.end_timestamp)
167
+ new_dict = {}
168
+ for location in data.health_data.locations():
169
+ health = data.health_data.get_location(location).fill_to_range(
170
+ start_timestamp, end_timestamp
171
+ )
172
+ climate = data.climate_data.get_location(location).fill_to_range(
173
+ start_timestamp, end_timestamp
174
+ )
175
+ assert (
176
+ location in data.population_data
177
+ ), f"Location {location} not in population data: {data.population_data.keys()}"
178
+ population = data.population_data[location]
179
+ new_dict[location] = FullData.combine(health.data(), climate.data(), population)
180
+
181
+ climate_health_data = DataSet(new_dict)
182
+ prediction_start = Month(climate_health_data.end_timestamp) - n_months * delta_month
183
+ train_data, _, future_weather = train_test_split_with_weather(
184
+ climate_health_data, prediction_start
185
+ )
186
+ logger.info(f"Training model {model_name} on {len(train_data.items())} locations")
187
+ control.set_status("Training")
188
+ if hasattr(model, "set_training_control"):
189
+ model.set_training_control(control.current_control)
190
+ if hasattr(model, "set_graph"):
191
+ model.set_graph(data.area_polygons)
192
+
193
+ model.train(train_data) # , extra_args=data.area_polygons)
194
+ logger.info(
195
+ f"Forecasting using {model_name} on {len(train_data.items())} locations"
196
+ )
197
+ control.set_status("Forecasting")
198
+ predictions = model.forecast(future_weather, forecast_delta=n_months * delta_month)
199
+ attrs = ["median", "quantile_high", "quantile_low"]
200
+ logger.info("Converting predictions to json")
201
+ control.set_status("Postprocessing")
202
+ data_values = predictions_to_datavalue(
203
+ predictions, attribute_mapping=dict(zip(attrs, attrs))
204
+ )
205
+ json_body = [dataclasses.asdict(element) for element in data_values]
206
+ diseaseId = data.disease_id
207
+ return {"diseaseId": diseaseId, "dataValues": json_body}
208
+ # return json_body
209
+
210
+
211
+ def train_with_validation(model_name, dataset_name, n_months=12):
212
+ dataset = datasets[dataset_name].load()
213
+ # assert not np.any(np.any(np.isnan(data.to_array()[:, 1:])) for data in dataset.values()), "Dataset contains NaN values"
214
+ # assert not any(np.any(np.isnan(data.mean_temperature) | np.isnan(data.rainfall)) for data in dataset.values()), "Dataset contains NaN values"
215
+ dataset = mask_covid_data(dataset)
216
+ model = get_model(model_name)(n_iter=32000)
217
+ # split_point = dataset.end_timestamp - n_months * delta_month
218
+ # train_data, test_data, future_weather = train_test_split_with_weather(dataset, split_point)
219
+ prediction_length = n_months * delta_month
220
+ split_point = dataset.end_timestamp - prediction_length
221
+ split_period = Month(split_point.year, split_point.month)
222
+ train_data, test_set, future_weather = train_test_split_with_weather(
223
+ dataset, split_period
224
+ )
225
+ model.set_validation_data(test_set)
226
+ model.train(train_data)
227
+ predictions = model.forecast(
228
+ future_weather, forecast_delta=n_months * delta_month, n_samples=100
229
+ )
230
+ # plot predictions
231
+ figs = []
232
+ for location, prediction in predictions.items():
233
+ fig = plot_forecast_from_summaries(
234
+ prediction.data(), dataset.get_location(location).data()
235
+ ) # , lambda x: np.log(x+1))
236
+ figs.append(fig)
237
+ return figs
238
+
239
+
240
+ def forecast(
241
+ model_name: str,
242
+ dataset_name: DataSetType,
243
+ n_months: int,
244
+ model_path: Optional[str] = None,
245
+ ):
246
+ logging.basicConfig(level=logging.INFO)
247
+ dataset = datasets[dataset_name].load()
248
+
249
+ if model_name == "external":
250
+ model = get_model_from_directory_or_github_url(model_path)
251
+ else:
252
+ model = get_model(model_name)
253
+ model = model()
254
+
255
+ # model = get_model(model_name)()
256
+ predictions = do_forecast(model, dataset, n_months * delta_month)
257
+
258
+ figs = []
259
+ for location, prediction in predictions.items():
260
+ fig = plot_forecast_from_summaries(
261
+ prediction.data(), dataset.get_location(location).data()
262
+ ) # , lambda x: np.log(x+1))
263
+ figs.append(fig)
264
+ return figs