anga-grid 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. anga_grid-0.5.0/.dockerignore +27 -0
  2. anga_grid-0.5.0/.editorconfig +15 -0
  3. anga_grid-0.5.0/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
  4. anga_grid-0.5.0/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
  5. anga_grid-0.5.0/.github/PULL_REQUEST_TEMPLATE.md +29 -0
  6. anga_grid-0.5.0/.github/dependabot.yml +30 -0
  7. anga_grid-0.5.0/.github/workflows/ci.yml +42 -0
  8. anga_grid-0.5.0/.gitignore +54 -0
  9. anga_grid-0.5.0/.pre-commit-config.yaml +29 -0
  10. anga_grid-0.5.0/CHANGELOG.md +81 -0
  11. anga_grid-0.5.0/CITATION.cff +49 -0
  12. anga_grid-0.5.0/CODE_OF_CONDUCT.md +44 -0
  13. anga_grid-0.5.0/CONTRIBUTING.md +110 -0
  14. anga_grid-0.5.0/Dockerfile +22 -0
  15. anga_grid-0.5.0/LICENSE +21 -0
  16. anga_grid-0.5.0/Makefile +41 -0
  17. anga_grid-0.5.0/PKG-INFO +310 -0
  18. anga_grid-0.5.0/README.md +253 -0
  19. anga_grid-0.5.0/SECURITY.md +28 -0
  20. anga_grid-0.5.0/doc/adr/0001-seasonal-calendars.md +148 -0
  21. anga_grid-0.5.0/doc/adr/0002-no-default-network-fetch.md +77 -0
  22. anga_grid-0.5.0/doc/adr/0003-manifest-as-attrs.md +77 -0
  23. anga_grid-0.5.0/doc/adr/0004-test-layer-taxonomy.md +68 -0
  24. anga_grid-0.5.0/doc/architecture.md +41 -0
  25. anga_grid-0.5.0/doc/calendars.md +65 -0
  26. anga_grid-0.5.0/doc/cli.md +93 -0
  27. anga_grid-0.5.0/doc/contributing-calendars.md +81 -0
  28. anga_grid-0.5.0/doc/data-sources.md +68 -0
  29. anga_grid-0.5.0/doc/datasets.md +65 -0
  30. anga_grid-0.5.0/doc/glossary.md +71 -0
  31. anga_grid-0.5.0/doc/index.md +41 -0
  32. anga_grid-0.5.0/doc/installation.md +84 -0
  33. anga_grid-0.5.0/doc/operations.md +80 -0
  34. anga_grid-0.5.0/doc/roadmap.md +79 -0
  35. anga_grid-0.5.0/doc/troubleshooting.md +80 -0
  36. anga_grid-0.5.0/examples/01_chirps_njoro_spi.py +48 -0
  37. anga_grid-0.5.0/examples/02_onset_detection_long_rains.py +45 -0
  38. anga_grid-0.5.0/examples/03_wrsi_maize_long_rains.py +73 -0
  39. anga_grid-0.5.0/examples/04_long_rains_trend_and_quintile.py +66 -0
  40. anga_grid-0.5.0/pyproject.toml +100 -0
  41. anga_grid-0.5.0/src/anga_grid/__init__.py +25 -0
  42. anga_grid-0.5.0/src/anga_grid/_version.py +9 -0
  43. anga_grid-0.5.0/src/anga_grid/aggregation/__init__.py +4 -0
  44. anga_grid-0.5.0/src/anga_grid/aggregation/reducers.py +34 -0
  45. anga_grid-0.5.0/src/anga_grid/aggregation/seasonal.py +24 -0
  46. anga_grid-0.5.0/src/anga_grid/cli/__init__.py +11 -0
  47. anga_grid-0.5.0/src/anga_grid/cli/classify.py +59 -0
  48. anga_grid-0.5.0/src/anga_grid/cli/compute.py +104 -0
  49. anga_grid-0.5.0/src/anga_grid/cli/fetch.py +64 -0
  50. anga_grid-0.5.0/src/anga_grid/cli/main.py +45 -0
  51. anga_grid-0.5.0/src/anga_grid/cli/quintile.py +60 -0
  52. anga_grid-0.5.0/src/anga_grid/cli/rollup.py +66 -0
  53. anga_grid-0.5.0/src/anga_grid/cli/seasons.py +42 -0
  54. anga_grid-0.5.0/src/anga_grid/cli/trend.py +58 -0
  55. anga_grid-0.5.0/src/anga_grid/correction/__init__.py +5 -0
  56. anga_grid-0.5.0/src/anga_grid/correction/base.py +31 -0
  57. anga_grid-0.5.0/src/anga_grid/correction/delta.py +36 -0
  58. anga_grid-0.5.0/src/anga_grid/correction/linear.py +44 -0
  59. anga_grid-0.5.0/src/anga_grid/correction/monthly.py +54 -0
  60. anga_grid-0.5.0/src/anga_grid/cropping/__init__.py +59 -0
  61. anga_grid-0.5.0/src/anga_grid/cropping/calendar.py +220 -0
  62. anga_grid-0.5.0/src/anga_grid/cropping/windows.py +68 -0
  63. anga_grid-0.5.0/src/anga_grid/exceptions/__init__.py +47 -0
  64. anga_grid-0.5.0/src/anga_grid/indicators/__init__.py +35 -0
  65. anga_grid-0.5.0/src/anga_grid/indicators/dry_spell/__init__.py +7 -0
  66. anga_grid-0.5.0/src/anga_grid/indicators/dry_spell/count.py +113 -0
  67. anga_grid-0.5.0/src/anga_grid/indicators/evapotranspiration/__init__.py +11 -0
  68. anga_grid-0.5.0/src/anga_grid/indicators/evapotranspiration/constants.py +7 -0
  69. anga_grid-0.5.0/src/anga_grid/indicators/evapotranspiration/fao56.py +100 -0
  70. anga_grid-0.5.0/src/anga_grid/indicators/gdd/__init__.py +15 -0
  71. anga_grid-0.5.0/src/anga_grid/indicators/gdd/accumulate.py +102 -0
  72. anga_grid-0.5.0/src/anga_grid/indicators/onset/__init__.py +7 -0
  73. anga_grid-0.5.0/src/anga_grid/indicators/onset/detect.py +133 -0
  74. anga_grid-0.5.0/src/anga_grid/indicators/spi/__init__.py +3 -0
  75. anga_grid-0.5.0/src/anga_grid/indicators/spi/compute.py +129 -0
  76. anga_grid-0.5.0/src/anga_grid/indicators/temperature_extremes/__init__.py +8 -0
  77. anga_grid-0.5.0/src/anga_grid/indicators/temperature_extremes/counts.py +103 -0
  78. anga_grid-0.5.0/src/anga_grid/indicators/trend/__init__.py +6 -0
  79. anga_grid-0.5.0/src/anga_grid/indicators/trend/linear.py +117 -0
  80. anga_grid-0.5.0/src/anga_grid/indicators/wrsi/__init__.py +13 -0
  81. anga_grid-0.5.0/src/anga_grid/indicators/wrsi/compute.py +127 -0
  82. anga_grid-0.5.0/src/anga_grid/indicators/wrsi/crops.py +53 -0
  83. anga_grid-0.5.0/src/anga_grid/logging/__init__.py +4 -0
  84. anga_grid-0.5.0/src/anga_grid/logging/config.py +30 -0
  85. anga_grid-0.5.0/src/anga_grid/logging/formatters.py +33 -0
  86. anga_grid-0.5.0/src/anga_grid/provenance/__init__.py +17 -0
  87. anga_grid-0.5.0/src/anga_grid/provenance/manifest.py +74 -0
  88. anga_grid-0.5.0/src/anga_grid/provenance/ops.py +34 -0
  89. anga_grid-0.5.0/src/anga_grid/provenance/steps.py +37 -0
  90. anga_grid-0.5.0/src/anga_grid/providers/__init__.py +13 -0
  91. anga_grid-0.5.0/src/anga_grid/providers/agera5/__init__.py +7 -0
  92. anga_grid-0.5.0/src/anga_grid/providers/agera5/manifest.py +52 -0
  93. anga_grid-0.5.0/src/anga_grid/providers/agera5/provider.py +121 -0
  94. anga_grid-0.5.0/src/anga_grid/providers/agera5/schema.py +37 -0
  95. anga_grid-0.5.0/src/anga_grid/providers/agera5/variables.py +21 -0
  96. anga_grid-0.5.0/src/anga_grid/providers/base.py +24 -0
  97. anga_grid-0.5.0/src/anga_grid/providers/chirps/__init__.py +13 -0
  98. anga_grid-0.5.0/src/anga_grid/providers/chirps/manifest.py +46 -0
  99. anga_grid-0.5.0/src/anga_grid/providers/chirps/provider.py +104 -0
  100. anga_grid-0.5.0/src/anga_grid/providers/chirps/schema.py +41 -0
  101. anga_grid-0.5.0/src/anga_grid/providers/nex_gddp/__init__.py +19 -0
  102. anga_grid-0.5.0/src/anga_grid/providers/nex_gddp/manifest.py +63 -0
  103. anga_grid-0.5.0/src/anga_grid/providers/nex_gddp/provider.py +133 -0
  104. anga_grid-0.5.0/src/anga_grid/providers/nex_gddp/scenarios.py +66 -0
  105. anga_grid-0.5.0/src/anga_grid/providers/nex_gddp/schema.py +84 -0
  106. anga_grid-0.5.0/src/anga_grid/providers/tamsat/__init__.py +3 -0
  107. anga_grid-0.5.0/src/anga_grid/providers/tamsat/manifest.py +52 -0
  108. anga_grid-0.5.0/src/anga_grid/providers/tamsat/provider.py +98 -0
  109. anga_grid-0.5.0/src/anga_grid/providers/tamsat/schema.py +45 -0
  110. anga_grid-0.5.0/src/anga_grid/rollup/__init__.py +21 -0
  111. anga_grid-0.5.0/src/anga_grid/rollup/bbox.py +146 -0
  112. anga_grid-0.5.0/src/anga_grid/rollup/polygon.py +144 -0
  113. anga_grid-0.5.0/src/anga_grid/season/__init__.py +4 -0
  114. anga_grid-0.5.0/src/anga_grid/season/catalog.py +86 -0
  115. anga_grid-0.5.0/src/anga_grid/season/types.py +65 -0
  116. anga_grid-0.5.0/src/anga_grid/severity/__init__.py +24 -0
  117. anga_grid-0.5.0/src/anga_grid/severity/bands.py +76 -0
  118. anga_grid-0.5.0/src/anga_grid/severity/phases.py +44 -0
  119. anga_grid-0.5.0/src/anga_grid/severity/quintile.py +118 -0
  120. anga_grid-0.5.0/src/anga_grid/severity/summary.py +29 -0
  121. anga_grid-0.5.0/src/anga_grid/storage/__init__.py +22 -0
  122. anga_grid-0.5.0/src/anga_grid/storage/format.py +23 -0
  123. anga_grid-0.5.0/src/anga_grid/storage/io.py +56 -0
  124. anga_grid-0.5.0/src/anga_grid/storage/manifest_io.py +35 -0
  125. anga_grid-0.5.0/src/anga_grid/synthetic/__init__.py +26 -0
  126. anga_grid-0.5.0/src/anga_grid/synthetic/agera5.py +97 -0
  127. anga_grid-0.5.0/src/anga_grid/synthetic/chirps.py +79 -0
  128. anga_grid-0.5.0/src/anga_grid/synthetic/nex_gddp.py +65 -0
  129. anga_grid-0.5.0/src/anga_grid/synthetic/tamsat.py +67 -0
  130. anga_grid-0.5.0/src/anga_grid/types/__init__.py +20 -0
  131. anga_grid-0.5.0/src/anga_grid/types/geometry.py +66 -0
  132. anga_grid-0.5.0/src/anga_grid/types/regions.py +29 -0
  133. anga_grid-0.5.0/src/anga_grid/types/time.py +36 -0
  134. anga_grid-0.5.0/tests/__init__.py +0 -0
  135. anga_grid-0.5.0/tests/conftest.py +27 -0
  136. anga_grid-0.5.0/tests/fixtures/README.md +55 -0
  137. anga_grid-0.5.0/tests/fixtures/__init__.py +0 -0
  138. anga_grid-0.5.0/tests/fixtures/data/agera5_njoro_1991_full_year.nc +0 -0
  139. anga_grid-0.5.0/tests/fixtures/data/agera5_njoro_1991_long_rains.nc +0 -0
  140. anga_grid-0.5.0/tests/fixtures/data/chirps_kisumu_1991.nc +0 -0
  141. anga_grid-0.5.0/tests/fixtures/data/chirps_mombasa_1991.nc +0 -0
  142. anga_grid-0.5.0/tests/fixtures/data/chirps_njoro_1991.nc +0 -0
  143. anga_grid-0.5.0/tests/fixtures/data/chirps_njoro_1991_1995.nc +0 -0
  144. anga_grid-0.5.0/tests/fixtures/data/chirps_njoro_1991_2020.nc +0 -0
  145. anga_grid-0.5.0/tests/fixtures/data/nex_gddp_njoro_ssp126_2030.nc +0 -0
  146. anga_grid-0.5.0/tests/fixtures/data/nex_gddp_njoro_ssp245_2030.nc +0 -0
  147. anga_grid-0.5.0/tests/fixtures/data/nex_gddp_njoro_ssp585_2030.nc +0 -0
  148. anga_grid-0.5.0/tests/fixtures/data/tamsat_njoro_1991.nc +0 -0
  149. anga_grid-0.5.0/tests/fixtures/data/tamsat_njoro_1991_1995.nc +0 -0
  150. anga_grid-0.5.0/tests/fixtures/regenerate.py +125 -0
  151. anga_grid-0.5.0/tests/integration/__init__.py +0 -0
  152. anga_grid-0.5.0/tests/integration/test_cli_end_to_end.py +186 -0
  153. anga_grid-0.5.0/tests/integration/test_cli_rollup_classify.py +233 -0
  154. anga_grid-0.5.0/tests/integration/test_cli_trend_quintile.py +144 -0
  155. anga_grid-0.5.0/tests/integration/test_committed_fixtures.py +205 -0
  156. anga_grid-0.5.0/tests/integration/test_synthetic_data_consistency.py +82 -0
  157. anga_grid-0.5.0/tests/performance/__init__.py +0 -0
  158. anga_grid-0.5.0/tests/performance/test_performance_indicators.py +111 -0
  159. anga_grid-0.5.0/tests/performance/test_performance_v05.py +124 -0
  160. anga_grid-0.5.0/tests/property/__init__.py +0 -0
  161. anga_grid-0.5.0/tests/property/test_property.py +93 -0
  162. anga_grid-0.5.0/tests/property/test_property_v05.py +87 -0
  163. anga_grid-0.5.0/tests/providers/__init__.py +0 -0
  164. anga_grid-0.5.0/tests/providers/test_agera5.py +147 -0
  165. anga_grid-0.5.0/tests/providers/test_chirps.py +164 -0
  166. anga_grid-0.5.0/tests/providers/test_nex_gddp.py +186 -0
  167. anga_grid-0.5.0/tests/providers/test_tamsat.py +139 -0
  168. anga_grid-0.5.0/tests/regression/__init__.py +0 -0
  169. anga_grid-0.5.0/tests/regression/test_regression_canonicalize.py +75 -0
  170. anga_grid-0.5.0/tests/regression/test_regression_provenance.py +105 -0
  171. anga_grid-0.5.0/tests/regression/test_regression_v05.py +122 -0
  172. anga_grid-0.5.0/tests/roundtrip/__init__.py +0 -0
  173. anga_grid-0.5.0/tests/roundtrip/test_roundtrip_storage.py +127 -0
  174. anga_grid-0.5.0/tests/roundtrip/test_roundtrip_v05.py +104 -0
  175. anga_grid-0.5.0/tests/smoke/__init__.py +0 -0
  176. anga_grid-0.5.0/tests/smoke/test_smoke_cli.py +47 -0
  177. anga_grid-0.5.0/tests/smoke/test_smoke_imports.py +75 -0
  178. anga_grid-0.5.0/tests/smoke/test_smoke_v05.py +149 -0
  179. anga_grid-0.5.0/tests/unit/__init__.py +0 -0
  180. anga_grid-0.5.0/tests/unit/test_aggregation.py +107 -0
  181. anga_grid-0.5.0/tests/unit/test_correction.py +141 -0
  182. anga_grid-0.5.0/tests/unit/test_cropping.py +103 -0
  183. anga_grid-0.5.0/tests/unit/test_cropping_regions.py +88 -0
  184. anga_grid-0.5.0/tests/unit/test_indicators_dry_spell.py +121 -0
  185. anga_grid-0.5.0/tests/unit/test_indicators_evapotranspiration.py +160 -0
  186. anga_grid-0.5.0/tests/unit/test_indicators_gdd.py +146 -0
  187. anga_grid-0.5.0/tests/unit/test_indicators_onset.py +133 -0
  188. anga_grid-0.5.0/tests/unit/test_indicators_spi.py +72 -0
  189. anga_grid-0.5.0/tests/unit/test_indicators_temperature_extremes.py +118 -0
  190. anga_grid-0.5.0/tests/unit/test_indicators_trend.py +116 -0
  191. anga_grid-0.5.0/tests/unit/test_indicators_wrsi.py +163 -0
  192. anga_grid-0.5.0/tests/unit/test_logging.py +53 -0
  193. anga_grid-0.5.0/tests/unit/test_provenance.py +102 -0
  194. anga_grid-0.5.0/tests/unit/test_provenance_chain.py +74 -0
  195. anga_grid-0.5.0/tests/unit/test_rollup.py +111 -0
  196. anga_grid-0.5.0/tests/unit/test_rollup_polygon.py +143 -0
  197. anga_grid-0.5.0/tests/unit/test_season.py +120 -0
  198. anga_grid-0.5.0/tests/unit/test_severity.py +132 -0
  199. anga_grid-0.5.0/tests/unit/test_severity_quintile.py +111 -0
  200. anga_grid-0.5.0/tests/unit/test_storage.py +110 -0
  201. anga_grid-0.5.0/tests/unit/test_types.py +111 -0
@@ -0,0 +1,27 @@
1
+ .github
2
+ .venv
3
+ .pytest_cache
4
+ .mypy_cache
5
+ .ruff_cache
6
+ .hypothesis
7
+ __pycache__
8
+ *.pyc
9
+ *.pyo
10
+ .coverage
11
+ htmlcov
12
+ build
13
+ dist
14
+ *.egg-info
15
+ tests
16
+ doc
17
+ examples
18
+ .editorconfig
19
+ .pre-commit-config.yaml
20
+ CHANGELOG.md
21
+ CONTRIBUTING.md
22
+ SECURITY.md
23
+ CODE_OF_CONDUCT.md
24
+ CLAUDE.md
25
+ data
26
+ *.zarr
27
+ *.nc
@@ -0,0 +1,15 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+ indent_style = space
9
+ indent_size = 4
10
+
11
+ [*.{md,yml,yaml,toml,json}]
12
+ indent_size = 2
13
+
14
+ [Makefile]
15
+ indent_style = tab
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: Bug report
3
+ about: Report unexpected behaviour with a synthetic-data reproducer
4
+ labels: bug
5
+ ---
6
+
7
+ ## What you expected
8
+
9
+ <!-- What anga-grid was supposed to do. -->
10
+
11
+ ## What actually happened
12
+
13
+ <!-- What it did instead. Include the exception trace if any. -->
14
+
15
+ ## Reproducer
16
+
17
+ ```python
18
+ from anga_grid.synthetic import synthetic_chirps
19
+ # minimal code that triggers the issue against synthetic data
20
+ ```
21
+
22
+ ## Environment
23
+
24
+ - anga-grid version:
25
+ - Python version:
26
+ - OS:
27
+ - xarray / xclim versions (from `uv pip list | grep -E 'xarray|xclim'`):
28
+
29
+ ## Manifest contents
30
+
31
+ If the issue is about provenance / output attrs, paste the relevant
32
+ attrs of the offending dataset:
33
+
34
+ ```
35
+ ds.attrs
36
+ ```
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: Feature request
3
+ about: Propose a new indicator, provider, calendar, or operational hook
4
+ labels: enhancement
5
+ ---
6
+
7
+ ## Operational context
8
+
9
+ <!-- Which operational workflow needs this. Bulletin generation,
10
+ early-warning trigger, county dashboard, research analysis,
11
+ etc. -->
12
+
13
+ ## What's missing today
14
+
15
+ <!-- How are people working around the absence of this feature? -->
16
+
17
+ ## Proposed shape
18
+
19
+ <!-- Either an API sketch (function signature, CLI invocation) or a
20
+ reference to an existing implementation we could model on
21
+ (FEWS NET, ICPAC, KMD, an xclim indicator, a paper). -->
22
+
23
+ ## Citations / authoritative sources
24
+
25
+ <!-- KMD bulletin, ICPAC GHACOF statement, peer-reviewed paper,
26
+ county extension service report. The default thresholds in this
27
+ library are anchored on cited sources; new ones should be too. -->
@@ -0,0 +1,29 @@
1
+ ## What changed
2
+
3
+ <!-- One sentence per logical change. If this PR bundles unrelated
4
+ changes, please split it. -->
5
+
6
+ ## Why it matters
7
+
8
+ <!-- The operational or analytical context. Who notices this and
9
+ when? -->
10
+
11
+ ## Local check
12
+
13
+ - [ ] `uv run ruff check src/ tests/` clean
14
+ - [ ] `uv run mypy src/` clean
15
+ - [ ] `uv run pytest --cov=anga_grid --cov-fail-under=80` passes
16
+ - [ ] If touching defaults / thresholds: citation included in
17
+ commit body or doc/
18
+
19
+ ## Provenance impact
20
+
21
+ - [ ] Output `Manifest` history records the new operation
22
+ - [ ] Caveats list updated if the change affects bias notes
23
+ - [ ] No-op if this PR doesn't touch indicator outputs
24
+
25
+ ## Docs touched
26
+
27
+ - [ ] CHANGELOG updated
28
+ - [ ] README Quick Look still accurate
29
+ - [ ] doc/ files affected by the change updated
@@ -0,0 +1,30 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ day: "monday"
8
+ time: "06:00"
9
+ timezone: "Africa/Nairobi"
10
+ open-pull-requests-limit: 5
11
+ labels:
12
+ - "dependencies"
13
+ commit-message:
14
+ prefix: "deps"
15
+ include: "scope"
16
+ ignore:
17
+ # zarr 3.x is a breaking rewrite; pinned <3 deliberately until
18
+ # the upstream xarray + zarr-3 stack is proven on this codebase.
19
+ - dependency-name: "zarr"
20
+ update-types: ["version-update:semver-major"]
21
+
22
+ - package-ecosystem: "github-actions"
23
+ directory: "/"
24
+ schedule:
25
+ interval: "monthly"
26
+ labels:
27
+ - "ci"
28
+ commit-message:
29
+ prefix: "ci"
30
+ include: "scope"
@@ -0,0 +1,42 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ test:
14
+ name: test (py${{ matrix.python }})
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ python: ["3.11", "3.12", "3.13"]
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: install uv
24
+ uses: astral-sh/setup-uv@v3
25
+ with:
26
+ enable-cache: true
27
+ cache-dependency-glob: "pyproject.toml"
28
+
29
+ - name: pin python
30
+ run: uv python install ${{ matrix.python }}
31
+
32
+ - name: install
33
+ run: uv sync --extra dev --python ${{ matrix.python }}
34
+
35
+ - name: ruff
36
+ run: uv run --python ${{ matrix.python }} ruff check src/ tests/
37
+
38
+ - name: mypy
39
+ run: uv run --python ${{ matrix.python }} mypy src/
40
+
41
+ - name: pytest
42
+ run: uv run --python ${{ matrix.python }} pytest --cov=anga_grid --cov-report=term-missing --cov-fail-under=80
@@ -0,0 +1,54 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ *.egg
11
+ wheels/
12
+ .eggs/
13
+
14
+ # Virtual environments
15
+ .venv/
16
+ venv/
17
+ env/
18
+ .python-version
19
+
20
+ # uv
21
+ uv.lock
22
+
23
+ # Testing / coverage
24
+ .pytest_cache/
25
+ .coverage
26
+ .coverage.*
27
+ htmlcov/
28
+ coverage.xml
29
+ .hypothesis/
30
+
31
+ # Type checkers
32
+ .mypy_cache/
33
+ .ruff_cache/
34
+
35
+ # IDE
36
+ .idea/
37
+ .vscode/
38
+ *.swp
39
+ *.swo
40
+ .DS_Store
41
+
42
+ # Local data caches
43
+ /data/
44
+ *.zarr/
45
+ *.nc
46
+ !tests/fixtures/**/*.nc
47
+
48
+ # Editor / system droppings
49
+ *~
50
+ *.bak
51
+ .~lock.*
52
+
53
+ # Jupyter checkpoints picked up by users running examples in notebooks
54
+ .ipynb_checkpoints/
@@ -0,0 +1,29 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.6.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-toml
9
+ - id: check-added-large-files
10
+ args: ['--maxkb=500']
11
+ - id: check-merge-conflict
12
+ - id: mixed-line-ending
13
+ args: ['--fix=lf']
14
+
15
+ - repo: https://github.com/astral-sh/ruff-pre-commit
16
+ rev: v0.4.10
17
+ hooks:
18
+ - id: ruff
19
+ args: [--fix, --exit-non-zero-on-fix]
20
+ - id: ruff-format
21
+
22
+ - repo: local
23
+ hooks:
24
+ - id: mypy
25
+ name: mypy on src/
26
+ entry: uv run mypy src/
27
+ language: system
28
+ pass_filenames: false
29
+ types_or: [python, pyi]
@@ -0,0 +1,81 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project go here. Format follows Keep a
4
+ Changelog; this project does not yet ship pinned releases - everything
5
+ below is on `main`.
6
+
7
+ ## Unreleased — v0.5 work in progress
8
+
9
+ ### Added
10
+
11
+ - `temperature_extremes` indicator: hot_days, cold_days, frost_days,
12
+ tropical_nights with WMO-ETCCDI default thresholds.
13
+ - `severity.quintile` module: tercile and quintile classification with
14
+ optional baseline-period reference.
15
+ - `severity.summary` module: per-band fraction breakdown of a
16
+ classified output.
17
+ - Per-region cropping calendars: Embu, Kisumu, Mombasa, Garissa.
18
+ - CLI: `anga trend` and `anga quintile` subcommands.
19
+ - README badges: CI, Python, license, ruff, coverage, region.
20
+ - Project setup docs: CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md,
21
+ CHANGELOG.md, CITATION.cff, doc/installation.md, doc/cli.md,
22
+ doc/operations.md, doc/data-sources.md, doc/troubleshooting.md,
23
+ doc/glossary.md, doc/roadmap.md, doc/index.md,
24
+ doc/contributing-calendars.md.
25
+ - ADRs 0002 (no default network fetch), 0003 (manifest as attrs),
26
+ 0004 (eight test layers).
27
+ - Build infra: Dockerfile, .dockerignore, Makefile, .editorconfig,
28
+ .pre-commit-config.yaml, .github/dependabot.yml,
29
+ .github/ISSUE_TEMPLATE/{bug_report,feature_request}.md,
30
+ .github/PULL_REQUEST_TEMPLATE.md, .github/FUNDING.yml.
31
+ - CI: Python 3.13 added to the test matrix.
32
+ - Storage: `.cdf` accepted as a NetCDF suffix.
33
+ - Tests: hypothesis property tests for cropping windows + temperature
34
+ extremes monotonicity; synthetic-data builder consistency contract.
35
+
36
+ ### Changed
37
+
38
+ - README install instructions now use `uv tool install` as the
39
+ primary path; the previous `pip install` line broke on PEP 668
40
+ systems.
41
+ - README Quick Look fetch example flags `--source-override` since
42
+ network fetch is still roadmap in v0.5.
43
+ - `severity.quintile` now drops the stray `quantile` coord that
44
+ `xr.quantile` adds, fixing apply_ufunc dimension errors on
45
+ spatial inputs.
46
+
47
+ ## v0.4 (2026-01)
48
+
49
+ - NEX-GDDP-CMIP6 provider with five-scenario catalog and unit conversion.
50
+ - Polygon-based rollups via vectorised ray-casting (no shapely dep).
51
+ - `cropping/` module: phenological windows + Nakuru calendars.
52
+ - `anga_grid.synthetic` public package; runnable examples/.
53
+ - Top-level files reshaped into themed sub-packages: types, exceptions,
54
+ logging, aggregation, correction, severity, storage, provenance,
55
+ season.
56
+
57
+ ## v0.3 (2025-12)
58
+
59
+ - TAMSAT-3.1 provider as the third precipitation reader.
60
+ - Provider sub-package refactor: chirps/, agera5/ split into
61
+ provider/schema/manifest/variables modules.
62
+ - Indicator sub-package refactor: spi/, onset/, dry_spell/, gdd/.
63
+ - Reference ET (FAO-56 Penman-Monteith).
64
+ - WRSI for maize, sorghum, beans.
65
+ - Four new test layers: smoke, regression, performance, roundtrip.
66
+
67
+ ## v0.2 (2025-11)
68
+
69
+ - Provenance Manifest as a first-class concept.
70
+ - Storage helpers (zarr/netcdf write/read with manifest preservation).
71
+ - AgERA5 provider for ECMWF/Copernicus reanalysis.
72
+ - Dry-spell count and growing degree days indicators.
73
+ - Bias correction: linear scaling, delta change, monthly linear scaling.
74
+ - County/ward rollups with the Nakuru ward catalog.
75
+ - KMD severity bands and NDMA drought phase classification.
76
+ - CLI: `anga rollup` and `anga classify` subcommands.
77
+
78
+ ## v0.1 (2025-09 → 2025-10)
79
+
80
+ - Initial release: CHIRPS provider, Season type with KMD/ICPAC catalog,
81
+ SPI and onset indicators, basic CLI, behavioural test suite, CI.
@@ -0,0 +1,49 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use anga-grid in published work, please cite it."
3
+ title: "anga-grid"
4
+ abstract: >
5
+ Gridded climate indicators for East African agriculture, with
6
+ KMD/ICPAC seasonal calendars, FAO-56 reference ET, WRSI, and
7
+ KMD/NDMA/FEWS-NET drought classification baked in.
8
+ authors:
9
+ - family-names: Osoro
10
+ given-names: Preston
11
+ email: prestonosoro56@gmail.com
12
+ license: MIT
13
+ repository-code: "https://github.com/preston-56/anga-grid"
14
+ type: software
15
+ keywords:
16
+ - climate
17
+ - agriculture
18
+ - east-africa
19
+ - drought
20
+ - chirps
21
+ - xarray
22
+ references:
23
+ - type: article
24
+ authors:
25
+ - family-names: Funk
26
+ given-names: Chris
27
+ title: >
28
+ The climate hazards infrared precipitation with stations - a new
29
+ environmental record for monitoring extremes
30
+ journal: "Scientific Data"
31
+ year: 2015
32
+ volume: 2
33
+ number: 150066
34
+ doi: 10.1038/sdata.2015.66
35
+ - type: article
36
+ authors:
37
+ - family-names: Camberlin
38
+ given-names: Pierre
39
+ - family-names: Okoola
40
+ given-names: Raphael E.
41
+ title: >
42
+ The onset and cessation of the long rains in eastern Africa
43
+ and their interannual variability
44
+ journal: "Theoretical and Applied Climatology"
45
+ year: 2003
46
+ volume: 75
47
+ issue: 1
48
+ start: 43
49
+ end: 54
@@ -0,0 +1,44 @@
1
+ # Code of conduct
2
+
3
+ anga-grid is a small project but its contributors include researchers,
4
+ operational meteorologists, and extension officers across multiple
5
+ institutions and countries. We expect everyone interacting in project
6
+ spaces (issues, pull requests, discussions, email) to behave the way
7
+ they'd want a colleague at a regional climate forum to behave.
8
+
9
+ ## Concretely
10
+
11
+ - Be respectful of different domain expertise. A peer-reviewed
12
+ climatologist and an extension officer running monthly drought
13
+ bulletins both have things to teach each other.
14
+ - Critique ideas, not people. If you disagree with a proposed default
15
+ threshold or season window, say why with citations rather than
16
+ dismissing the contributor.
17
+ - Assume good faith on language differences. Many contributors won't
18
+ have English as a first language; phrasing that reads tersely or
19
+ abruptly is rarely intended that way.
20
+ - No harassment, sexualised content, ad hominem attacks, or
21
+ political-affiliation flame wars in project spaces.
22
+
23
+ ## Enforcement
24
+
25
+ If you experience or observe unacceptable behaviour, please email
26
+ **prestonosoro56@gmail.com**. Reports will be handled confidentially.
27
+
28
+ For minor issues, the maintainer may simply ask the offending party
29
+ to adjust their behaviour. For repeated or severe issues, the
30
+ maintainer may temporarily or permanently ban the contributor from
31
+ project spaces.
32
+
33
+ ## Scope
34
+
35
+ This Code of Conduct applies to all project spaces (the GitHub
36
+ repository, related issue trackers, mailing lists, and any
37
+ project-affiliated communication channels) and to public spaces when
38
+ an individual is representing the project.
39
+
40
+ ## Attribution
41
+
42
+ Inspired by the Contributor Covenant, adapted for a smaller-scale
43
+ project where individual judgement is preferred over a long
44
+ prescriptive ruleset.
@@ -0,0 +1,110 @@
1
+ # Contributing to anga-grid
2
+
3
+ Thanks for considering a contribution. anga-grid is built for
4
+ operational use by East African agronomy and drought-monitoring
5
+ services, and the project particularly welcomes input from people
6
+ working in those domains.
7
+
8
+ ## What we want
9
+
10
+ The most useful contributions, in order of impact:
11
+
12
+ 1. **Domain corrections.** If a default season window, planting DOY,
13
+ bias caveat, or threshold is wrong for the region you work in,
14
+ open an issue with the correction and a citation we can ground
15
+ the change in (KMD bulletin, ICPAC GHACOF statement, peer-reviewed
16
+ paper, county-level extension service report).
17
+ 2. **New regional cropping calendars.** The catalog in
18
+ `src/anga_grid/cropping/calendar.py` covers Nakuru, Embu, Kisumu,
19
+ Mombasa, and Garissa. Adding a calendar for your county requires
20
+ planting DOY, days-to-flower / fill / harvest for each crop, and
21
+ the season key it anchors to.
22
+ 3. **New dataset providers.** If your operational pipeline uses a
23
+ dataset we don't ship a reader for, the fastest path is a PR
24
+ modelled on `providers/chirps/` or `providers/agera5/`.
25
+ 4. **Bug reports.** Especially ones that include a synthetic-data
26
+ reproducer using `anga_grid.synthetic.*`.
27
+
28
+ ## What we don't want
29
+
30
+ - Generalisation to other regions at the cost of East African
31
+ correctness. The library is opinionated for a reason; the
32
+ README's "Not a global library" line is load-bearing.
33
+ - New abstractions for hypothetical future flexibility. Three
34
+ similar lines is better than a premature abstraction.
35
+ - Changes to default thresholds without a citation. The defaults
36
+ match operational KMD/ICPAC/NDMA practice; if you change them
37
+ you owe an explanation.
38
+
39
+ ## How to contribute
40
+
41
+ ### Bug reports and feature requests
42
+
43
+ Open an issue on
44
+ [GitHub](https://github.com/preston-56/anga-grid/issues). Please
45
+ include:
46
+
47
+ - What you expected to happen
48
+ - What actually happened
49
+ - A minimal reproducer (synthetic input is fine; see
50
+ `anga_grid.synthetic.*` for ready-made data builders)
51
+ - Your Python and `anga-grid` versions
52
+
53
+ ### Code contributions
54
+
55
+ 1. Fork the repo and create a topic branch.
56
+ 2. Set up a dev environment: `uv sync --extra dev`.
57
+ 3. Make your change with tests. Every new module needs a
58
+ corresponding `tests/unit/test_*.py`.
59
+ 4. Run the full local check before opening a PR:
60
+
61
+ ```bash
62
+ uv run ruff check src/ tests/
63
+ uv run mypy src/
64
+ uv run pytest --cov=anga_grid --cov-fail-under=80
65
+ ```
66
+
67
+ 5. Open a PR with a description of *why* the change matters
68
+ (operational context > technical motivation).
69
+
70
+ ### Commit conventions
71
+
72
+ - Use a leading scope tag: `feat(providers): ...`, `fix(rollup): ...`,
73
+ `doc: ...`, `test: ...`, `ci: ...`, `refactor(season): ...`.
74
+ - Keep each commit to one logical change. The history reads better
75
+ when each commit's diff matches one claim in its subject line.
76
+ - Don't bundle code + tests + docs into one giant commit when they
77
+ can land separately.
78
+
79
+ ## Code style
80
+
81
+ - `ruff check` and `mypy --strict` must pass; CI enforces this.
82
+ - Default to no comments. Only add one when the *why* of the code is
83
+ non-obvious. Avoid godoc-style `// FuncName does X` blocks on every
84
+ function; well-named identifiers should carry the load.
85
+ - New modules go inside themed sub-packages, not flat at the root.
86
+ See `src/anga_grid/correction/` and `src/anga_grid/severity/` for
87
+ the two-file split pattern.
88
+ - Public API symbols are exported via the package `__init__.py`
89
+ re-exports. Internal helpers stay underscore-prefixed.
90
+
91
+ ## Testing layers
92
+
93
+ The test tree has eight layers, each with its own pytest marker:
94
+
95
+ - `tests/unit/` — per-module behavioural tests
96
+ - `tests/providers/` — per-provider integration against synthetic data
97
+ - `tests/integration/` — cross-module end-to-end (CLI etc.)
98
+ - `tests/property/` — hypothesis-driven invariants
99
+ - `tests/smoke/` — minimum import / wiring sanity
100
+ - `tests/regression/` — pinned past-bug behaviours
101
+ - `tests/performance/` — bounded-time benchmark checks
102
+ - `tests/roundtrip/` — write-then-read symmetry tests
103
+
104
+ When adding a substantive new module, please touch the relevant
105
+ layer(s) — at minimum `tests/unit/` and `tests/smoke/`.
106
+
107
+ ## License
108
+
109
+ By contributing you agree your changes are released under the MIT
110
+ License (see `LICENSE`).
@@ -0,0 +1,22 @@
1
+ FROM python:3.12-slim-bookworm
2
+
3
+ ENV PYTHONDONTWRITEBYTECODE=1 \
4
+ PYTHONUNBUFFERED=1 \
5
+ PIP_DISABLE_PIP_VERSION_CHECK=1
6
+
7
+ RUN apt-get update && apt-get install -y --no-install-recommends \
8
+ libnetcdf-dev \
9
+ libhdf5-dev \
10
+ ca-certificates \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ RUN pip install --no-cache-dir uv==0.4.18
14
+
15
+ WORKDIR /app
16
+
17
+ COPY . .
18
+
19
+ RUN uv pip install --system --no-cache .
20
+
21
+ ENTRYPOINT ["anga"]
22
+ CMD ["--help"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Preston Osoro
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.
@@ -0,0 +1,41 @@
1
+ .PHONY: help install test lint typecheck cover clean
2
+
3
+ help:
4
+ @echo "make install - uv sync with dev extras"
5
+ @echo "make test - run the full pytest suite"
6
+ @echo "make lint - ruff check"
7
+ @echo "make typecheck - mypy on src/"
8
+ @echo "make cover - pytest with coverage report"
9
+ @echo "make smoke - just the smoke test layer"
10
+ @echo "make clean - remove caches and build artifacts"
11
+
12
+ install:
13
+ uv sync --extra dev
14
+
15
+ test:
16
+ uv run pytest
17
+
18
+ lint:
19
+ uv run ruff check src/ tests/
20
+
21
+ typecheck:
22
+ uv run mypy src/
23
+
24
+ cover:
25
+ uv run pytest --cov=anga_grid --cov-report=term-missing --cov-fail-under=80
26
+
27
+ smoke:
28
+ uv run pytest -m smoke -q
29
+
30
+ regression:
31
+ uv run pytest -m regression -q
32
+
33
+ performance:
34
+ uv run pytest -m performance -q
35
+
36
+ roundtrip:
37
+ uv run pytest -m roundtrip -q
38
+
39
+ clean:
40
+ rm -rf build/ dist/ *.egg-info .pytest_cache .mypy_cache .ruff_cache .coverage htmlcov .hypothesis
41
+ find . -type d -name __pycache__ -exec rm -rf {} +