guidelinely 1.0.3__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 (32) hide show
  1. guidelinely-1.0.3/.github/workflows/ci.yml +125 -0
  2. guidelinely-1.0.3/.gitignore +53 -0
  3. guidelinely-1.0.3/.pre-commit-config.yaml +34 -0
  4. guidelinely-1.0.3/LICENSE +21 -0
  5. guidelinely-1.0.3/PKG-INFO +338 -0
  6. guidelinely-1.0.3/README.md +301 -0
  7. guidelinely-1.0.3/examples/01_basic_metadata.py +70 -0
  8. guidelinely-1.0.3/examples/02_calculate_single_parameter.py +73 -0
  9. guidelinely-1.0.3/examples/03_batch_calculations.py +85 -0
  10. guidelinely-1.0.3/examples/04_soil_calculations.py +98 -0
  11. guidelinely-1.0.3/examples/05_advanced_workflow.py +140 -0
  12. guidelinely-1.0.3/examples/README.md +27 -0
  13. guidelinely-1.0.3/guidelinely/__init__.py +60 -0
  14. guidelinely-1.0.3/guidelinely/_version.py +34 -0
  15. guidelinely-1.0.3/guidelinely/auth.py +29 -0
  16. guidelinely-1.0.3/guidelinely/cache.py +46 -0
  17. guidelinely-1.0.3/guidelinely/client.py +469 -0
  18. guidelinely-1.0.3/guidelinely/exceptions.py +82 -0
  19. guidelinely-1.0.3/guidelinely/models.py +138 -0
  20. guidelinely-1.0.3/guidelinely.egg-info/PKG-INFO +338 -0
  21. guidelinely-1.0.3/guidelinely.egg-info/SOURCES.txt +30 -0
  22. guidelinely-1.0.3/guidelinely.egg-info/dependency_links.txt +1 -0
  23. guidelinely-1.0.3/guidelinely.egg-info/requires.txt +14 -0
  24. guidelinely-1.0.3/guidelinely.egg-info/top_level.txt +1 -0
  25. guidelinely-1.0.3/pyproject.toml +84 -0
  26. guidelinely-1.0.3/setup.cfg +4 -0
  27. guidelinely-1.0.3/tests/__init__.py +1 -0
  28. guidelinely-1.0.3/tests/test_auth.py +37 -0
  29. guidelinely-1.0.3/tests/test_cache.py +96 -0
  30. guidelinely-1.0.3/tests/test_client.py +322 -0
  31. guidelinely-1.0.3/tests/test_edge_cases.py +464 -0
  32. guidelinely-1.0.3/tests/test_models.py +202 -0
@@ -0,0 +1,125 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ tags: [ 'v*' ] # Run on version tags
7
+ pull_request:
8
+ branches: [ main ]
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Cache pip dependencies
26
+ uses: actions/cache@v4
27
+ with:
28
+ path: ~/.cache/pip
29
+ key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }}
30
+ restore-keys: |
31
+ ${{ runner.os }}-pip-${{ matrix.python-version }}-
32
+ ${{ runner.os }}-pip-
33
+
34
+ - name: Install dependencies
35
+ run: |
36
+ python -m pip install --upgrade pip
37
+ pip install -e ".[dev]"
38
+
39
+ - name: Run tests with coverage
40
+ run: |
41
+ pytest --cov=guidelinely --cov-report=xml --cov-report=term-missing
42
+
43
+ - name: Upload coverage to Codecov
44
+ uses: codecov/codecov-action@v4
45
+ with:
46
+ file: ./coverage.xml
47
+ flags: unittests
48
+ name: codecov-umbrella
49
+ fail_ci_if_error: false
50
+
51
+ lint:
52
+ runs-on: ubuntu-latest
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+
56
+ - name: Set up Python
57
+ uses: actions/setup-python@v5
58
+ with:
59
+ python-version: "3.11"
60
+
61
+ - name: Cache pip dependencies
62
+ uses: actions/cache@v4
63
+ with:
64
+ path: ~/.cache/pip
65
+ key: ${{ runner.os }}-pip-lint-${{ hashFiles('**/pyproject.toml') }}
66
+ restore-keys: |
67
+ ${{ runner.os }}-pip-lint-
68
+ ${{ runner.os }}-pip-
69
+
70
+ - name: Install dependencies
71
+ run: |
72
+ python -m pip install --upgrade pip
73
+ pip install -e ".[dev]"
74
+
75
+ - name: Check formatting with Black
76
+ run: black --check guidelinely/ tests/
77
+
78
+ - name: Lint with Ruff
79
+ run: ruff check guidelinely/ tests/
80
+
81
+ - name: Type check with MyPy
82
+ run: mypy guidelinely/
83
+
84
+ build:
85
+ runs-on: ubuntu-latest
86
+ steps:
87
+ - uses: actions/checkout@v4
88
+
89
+ - name: Set up Python
90
+ uses: actions/setup-python@v5
91
+ with:
92
+ python-version: "3.11"
93
+
94
+ - name: Install build dependencies
95
+ run: |
96
+ python -m pip install --upgrade pip
97
+ pip install build
98
+
99
+ - name: Build package
100
+ run: python -m build
101
+
102
+ - name: Check build artifacts
103
+ run: ls -la dist/
104
+
105
+ - name: Upload build artifacts
106
+ uses: actions/upload-artifact@v4
107
+ with:
108
+ name: dist
109
+ path: dist/
110
+
111
+ publish:
112
+ runs-on: ubuntu-latest
113
+ needs: [test, lint, build]
114
+ if: startsWith(github.ref, 'refs/tags/v') # Only publish on version tags
115
+ permissions:
116
+ id-token: write # Required for trusted publishing
117
+ steps:
118
+ - name: Download build artifacts
119
+ uses: actions/download-artifact@v4
120
+ with:
121
+ name: dist
122
+ path: dist/
123
+
124
+ - name: Publish to PyPI
125
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,53 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ venv/
25
+ env/
26
+ ENV/
27
+ .venv
28
+
29
+ # Testing
30
+ .pytest_cache/
31
+ .coverage
32
+ htmlcov/
33
+ .tox/
34
+
35
+ # IDE
36
+ .vscode/
37
+ .idea/
38
+ *.swp
39
+ *.swo
40
+ *~
41
+
42
+ # Type checking
43
+ .mypy_cache/
44
+ .dmypy.json
45
+ dmypy.json
46
+
47
+ # R reference (temporary)
48
+ r_reference/
49
+
50
+ # OS
51
+ .DS_Store
52
+ Thumbs.db
53
+ *ipynb
@@ -0,0 +1,34 @@
1
+ repos:
2
+ - repo: https://github.com/psf/black
3
+ rev: 24.10.0
4
+ hooks:
5
+ - id: black
6
+ args: [--check, --line-length=100]
7
+ files: ^(guidelinely|tests)/
8
+ exclude: ^guidelinely/_version\.py$
9
+
10
+ - repo: https://github.com/astral-sh/ruff-pre-commit
11
+ rev: v0.8.1
12
+ hooks:
13
+ - id: ruff
14
+ files: ^(guidelinely|tests)/
15
+
16
+ - repo: https://github.com/pre-commit/mirrors-mypy
17
+ rev: v1.13.0
18
+ hooks:
19
+ - id: mypy
20
+ files: ^guidelinely/
21
+ additional_dependencies:
22
+ - httpx>=0.25.0
23
+ - pydantic>=2.0.0
24
+ - diskcache>=5.6.0
25
+
26
+ - repo: local
27
+ hooks:
28
+ - id: pytest
29
+ name: pytest
30
+ entry: pytest --cov=guidelinely --cov-report=term-missing
31
+ language: system
32
+ types: [python]
33
+ pass_filenames: false
34
+ always_run: true
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Guidelinely Contributors
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,338 @@
1
+ Metadata-Version: 2.4
2
+ Name: guidelinely
3
+ Version: 1.0.3
4
+ Summary: Python client for the Guidelinely Environmental Guidelines API
5
+ Author-email: Michael Davison <michael.davison@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/mpdavison/guidelinely-python
8
+ Project-URL: Documentation, https://guidelines.1681248.com/docs
9
+ Project-URL: Repository, https://github.com/mpdavison/guidelinely-python
10
+ Project-URL: Bug Tracker, https://github.com/mpdavison/guidelinely-python/issues
11
+ Keywords: environmental,guidelines,api,water quality,soil,sediment
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: httpx>=0.25.0
24
+ Requires-Dist: pydantic>=2.0.0
25
+ Requires-Dist: diskcache>=5.6.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
28
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
29
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
30
+ Requires-Dist: pytest-httpx>=0.30.0; extra == "dev"
31
+ Requires-Dist: responses>=0.23.0; extra == "dev"
32
+ Requires-Dist: black>=23.0.0; extra == "dev"
33
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
34
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
35
+ Requires-Dist: pre-commit>=3.0.0; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ # Guidelinely Python Client
39
+
40
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
42
+
43
+ Python client library for the [Guidelinely API](https://guidelines.1681248.com/docs) - an environmental guideline calculation and search API.
44
+
45
+ Calculate context-dependent environmental guideline values for chemical parameters (Aluminum, Copper, Lead, etc.) in various media (water, soil, sediment) based on environmental conditions (pH, hardness, temperature, etc.).
46
+
47
+ This Python client mirrors the functionality of the [R client](https://github.com/mpdavison/envguidelines).
48
+
49
+ ## Features
50
+
51
+
52
+ - ✅ **Metadata Queries**: List parameters, search, explore media types and sources
53
+ - ✅ **Single Calculations**: Calculate guidelines for individual parameters
54
+ - ✅ **Batch Calculations**: Efficiently calculate multiple parameters (up to 50)
55
+ - ✅ **Context-Aware**: Support for pH, hardness, temperature, and other environmental factors
56
+ - ✅ **Unit Conversion**: Optional target unit specification
57
+ - ✅ **Type Safety**: Full Pydantic model support for request/response validation
58
+ - ✅ **Comprehensive Tests**: Mock-based test suite with responses library
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ # Clone the repository
64
+ git clone https://github.com/mpdavison/guidelinely-python.git
65
+ cd guidelinely-python
66
+
67
+ # Install in development mode
68
+ pip install -e .
69
+
70
+ # Or install with dev dependencies
71
+ pip install -e ".[dev]"
72
+ ```
73
+
74
+ ## Quick Start
75
+
76
+ ```python
77
+ from guidelinely import calculate_guidelines
78
+
79
+ # Calculate aluminum guidelines in surface water
80
+ result = calculate_guidelines(
81
+ parameter="Aluminum",
82
+ media="surface_water",
83
+ context={
84
+ "pH": "7.0 1", # pH 7.0 (dimensionless)
85
+ "hardness": "100 mg/L" # Water hardness as CaCO3
86
+ }
87
+ )
88
+
89
+ print(f"Found {result.total_count} guidelines")
90
+ for guideline in result.results:
91
+ print(f"{guideline.parameter}: {guideline.value} ({guideline.source})")
92
+ ```
93
+
94
+ ## API Key
95
+
96
+ Calculation endpoints optionally accept an API key. Set it as an environment variable:
97
+
98
+ ```bash
99
+ export GUIDELINELY_API_KEY="your_api_key_here"
100
+ ```
101
+
102
+ Metadata endpoints (list parameters, search, etc.) work without authentication.
103
+
104
+ ## Usage Examples
105
+
106
+ ### Basic Metadata Queries
107
+
108
+ ```python
109
+ from guidelinely import (
110
+ health_check,
111
+ list_parameters,
112
+ search_parameters,
113
+ list_media,
114
+ list_sources,
115
+ get_stats,
116
+ )
117
+
118
+ # Check API health
119
+ status = health_check()
120
+
121
+ # List all chemical parameters
122
+ params = list_parameters()
123
+ print(f"Available parameters: {len(params)}")
124
+
125
+ # Search for ammonia-related parameters
126
+ ammonia = search_parameters("ammon")
127
+ print(ammonia) # ['Ammonia', 'Ammonium', ...]
128
+
129
+ # Get available media types
130
+ media = list_media()
131
+ # {'surface_water': 'Surface Water', 'soil': 'Soil', ...}
132
+
133
+ # View guideline sources
134
+ sources = list_sources()
135
+
136
+ # Database statistics
137
+ stats = get_stats()
138
+ ```
139
+
140
+ ### Single Parameter Calculation
141
+
142
+ ```python
143
+ from guidelinely import calculate_guidelines
144
+ import os
145
+
146
+ os.environ["GUIDELINELY_API_KEY"] = "your_key"
147
+
148
+ result = calculate_guidelines(
149
+ parameter="Copper",
150
+ media="surface_water",
151
+ context={
152
+ "pH": "7.5 1",
153
+ "hardness": "150 mg/L",
154
+ "temperature": "15 °C",
155
+ "chloride": "75 mg/L"
156
+ },
157
+ target_unit="μg/L" # Optional unit conversion
158
+ )
159
+
160
+ # Filter results
161
+ chronic_aquatic = [
162
+ g for g in result.results
163
+ if g.receptor == "Aquatic Life" and g.exposure_duration == "chronic"
164
+ ]
165
+ ```
166
+
167
+ ### Batch Calculations
168
+
169
+ ```python
170
+ from guidelinely import calculate_batch
171
+
172
+ # Calculate multiple parameters at once (more efficient)
173
+ result = calculate_batch(
174
+ parameters=["Aluminum", "Copper", "Lead", "Zinc", "Cadmium"],
175
+ media="surface_water",
176
+ context={
177
+ "pH": "7.5 1",
178
+ "hardness": "150 mg/L",
179
+ "temperature": "15 °C"
180
+ }
181
+ )
182
+
183
+ print(f"Total: {result.total_count} guidelines")
184
+
185
+ # With per-parameter unit conversion
186
+ result = calculate_batch(
187
+ parameters=[
188
+ "Aluminum",
189
+ {"name": "Copper", "target_unit": "μg/L"},
190
+ {"name": "Lead", "target_unit": "mg/L"}
191
+ ],
192
+ media="surface_water",
193
+ context={"pH": "7.0 1", "hardness": "100 mg/L"}
194
+ )
195
+ ```
196
+
197
+ ### Soil Calculations
198
+
199
+ ```python
200
+ result = calculate_guidelines(
201
+ parameter="Lead",
202
+ media="soil",
203
+ context={
204
+ "pH": "6.5 1",
205
+ "organic_matter": "3.5 %",
206
+ "cation_exchange_capacity": "15 meq/100g"
207
+ }
208
+ )
209
+ ```
210
+
211
+ ## Environmental Context Parameters
212
+
213
+ All context parameters **must be strings with units** (Pint format):
214
+
215
+ ### Water (surface_water, groundwater)
216
+ ```python
217
+ context = {
218
+ "pH": "7.0 1", # Dimensionless - use "1" as unit
219
+ "hardness": "100 mg/L", # mg/L as CaCO3
220
+ "temperature": "20 °C",
221
+ "chloride": "50 mg/L"
222
+ }
223
+ ```
224
+
225
+ ### Soil
226
+ ```python
227
+ context = {
228
+ "pH": "6.5 1",
229
+ "organic_matter": "3.5 %",
230
+ "cation_exchange_capacity": "15 meq/100g"
231
+ }
232
+ ```
233
+
234
+ ### Sediment
235
+ ```python
236
+ context = {
237
+ "pH": "7.0 1",
238
+ "organic_matter": "2.5 %",
239
+ "grain_size": "0.5 mm"
240
+ }
241
+ ```
242
+
243
+ ## Data Models
244
+
245
+ The library uses Pydantic for type-safe data handling:
246
+
247
+ ```python
248
+ from guidelinely.models import GuidelineResponse, CalculationResponse
249
+
250
+ # All API responses are strongly typed
251
+ result: CalculationResponse = calculate_guidelines(...)
252
+
253
+ # Access typed fields
254
+ for guideline in result.results:
255
+ guideline.parameter # str
256
+ guideline.value # str (PostgreSQL unitrange format)
257
+ guideline.lower # Optional[float]
258
+ guideline.upper # Optional[float]
259
+ guideline.unit # str
260
+ guideline.is_calculated # bool
261
+ ```
262
+
263
+ ## Guideline Value Format
264
+
265
+ Guidelines use PostgreSQL `unitrange` format:
266
+ - `[10 μg/L,100 μg/L]` - Range from 10 to 100 μg/L
267
+ - `(,87.0 μg/L]` - Upper limit only (≤87.0 μg/L)
268
+ - `[5.0 mg/L,)` - Lower limit only (≥5.0 mg/L)
269
+
270
+ Parsed into `lower`, `upper`, and `unit` fields.
271
+
272
+ ## Development
273
+
274
+ ```bash
275
+ # Install development dependencies
276
+ pip install -e ".[dev]"
277
+
278
+ # Run tests
279
+ pytest
280
+
281
+ # Run tests with coverage
282
+ pytest --cov=guidelinely --cov-report=html
283
+
284
+ # Format code
285
+ black guidelinely/ tests/ examples/
286
+
287
+ # Type checking
288
+ mypy guidelinely/
289
+
290
+ # Linting
291
+ ruff check guidelinely/ tests/
292
+ ```
293
+
294
+ ## API Reference
295
+
296
+ ### Client Functions
297
+
298
+ - `health_check()` - Service health check
299
+ - `readiness_check()` - Database readiness check
300
+ - `list_parameters()` - List all chemical parameters
301
+ - `search_parameters(q, media)` - Search parameters
302
+ - `list_media()` - List media types
303
+ - `list_sources()` - List guideline sources
304
+ - `get_stats()` - Database statistics
305
+ - `calculate_guidelines(parameter, media, context, target_unit, api_key)` - Calculate single parameter
306
+ - `calculate_batch(parameters, media, context, api_key)` - Batch calculate (max 50 parameters)
307
+
308
+ ### Models
309
+
310
+ - `GuidelineResponse` - Single guideline result
311
+ - `CalculationResponse` - Calculation endpoint response
312
+ - `CalculateRequest` - Single calculation request body
313
+ - `BatchCalculateRequest` - Batch calculation request body
314
+
315
+ ## Examples
316
+
317
+ See the `examples/` directory for complete working examples:
318
+
319
+ 1. `01_basic_metadata.py` - Basic metadata queries
320
+ 2. `02_calculate_single_parameter.py` - Single parameter calculations
321
+ 3. `03_batch_calculations.py` - Batch calculations
322
+ 4. `04_soil_calculations.py` - Soil-specific calculations
323
+ 5. `05_advanced_workflow.py` - Advanced filtering and analysis
324
+
325
+ ## Resources
326
+
327
+ - **API Documentation**: https://guidelines.1681248.com/docs
328
+ - **OpenAPI Spec**: https://guidelines.1681248.com/openapi.json
329
+ - **R Client**: https://github.com/mpdavison/envguidelines
330
+ - **Issue Tracker**: https://github.com/mpdavison/guidelinely-python/issues
331
+
332
+ ## License
333
+
334
+ MIT License - see [LICENSE](LICENSE) file for details.
335
+
336
+ ## Contributing
337
+
338
+ Contributions are welcome! Please feel free to submit a Pull Request.