Skip to content

Commit eafb8c7

Browse files
alex-omophubalex-omophubclaude
authored
Back merge (#7)
* CI pipeline * Publish action * Refactor type hinting for vocabulary_ids to use builtins.list for consistency * Import builtins conditionally in type checking for improved clarity * Codecov settings in CI * Update README.md to include Codecov badge and change License badge color * Update CHANGELOG for v0.2.0: add parameters to `concepts.get_by_code()` for synonyms and relationships, and update User-Agent header format. * Update CHANGELOG.md * Add website link to README.md * Readme updates * Documentation * Corrections * v1.3.0 preparation * Release preparation * Integration tests * Improved tests * Examples update * Downloads badge * Sponsorship * Add integration tests for standard concept filtering and multiple filters with pagination * Prepare release v1.3.1 * Refactor tests for API key validation and enhance request handling tests. Updated synchronous and asynchronous client tests to use monkeypatching for API key checks. Added new tests for handling raw requests, including error parsing, rate limits, and JSON decoding issues. * Update vocab_version in tests for consistency across mock responses and client configurations. * Extending mapping method with source_codes option * Add semantic and similar search functionality with corresponding types and integration tests - Introduced `semantic` and `similar` search methods in the search resource, allowing for advanced concept searches using neural embeddings and similarity algorithms. - Added new TypedDicts for `SemanticSearchResult`, `SemanticSearchMeta`, `SimilarConcept`, and `SimilarSearchResult` to structure the response data. - Implemented integration tests for semantic search, including filtering and pagination, as well as tests for finding similar concepts by ID and name. - Updated type imports and ensured compatibility with existing search functionality. * Add semantic search examples to README * Refactor README and integration tests to streamline result extraction - Updated README example to reflect changes in the results structure for semantic search. - Refactored integration tests to utilize a new `extract_data` function for consistent handling of results and similar concepts, improving code clarity and maintainability. * Update CI workflow to support multiple branches and manual triggering * Update minimum similarity threshold in search.py from 0.3 to 0.5 for improved filtering accuracy. * v1.4.0 release * Increase rate limit delay in integration tests from 1 second to 2 seconds for improved test reliability. * Update version handling * Prepare v1.4.1 release Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add retry logic for server errors in SyncHTTPClient and AsyncHTTPClientImpl - Implemented retry mechanism for handling server errors (502, 503, 504) in both synchronous and asynchronous HTTP clients. - Added exponential backoff delay for retries to improve resilience against temporary server issues. * Refactor retry condition formatting in SyncHTTPClient and AsyncHTTPClientImpl * Add bulk search functionality to the API - Introduced `bulk_basic` and `bulk_semantic` methods for executing multiple lexical and semantic searches in a single API call, respectively. - Updated the README to include examples for bulk search usage. - Added corresponding types for bulk search inputs and responses in the type definitions. - Implemented integration and unit tests to validate the new bulk search features. * Update type definitions to include bulk search and semantic search types - Added new types for bulk search and semantic search functionalities to the `__all__` exports in `__init__.py`. - Removed previously commented sections for clarity and organization. * Enhance integration tests for bulk search functionality - Updated assertions in `test_bulk_basic_search` to verify that all expected search IDs are present in the results. - Added a check in `test_bulk_semantic_search` to confirm that the SNOMED vocabulary filter is applied to the results. * Prep for v1.5.0 release * Examples update * Update GitHub Actions workflows to use latest action versions - Upgraded `actions/checkout` from v4 to v6 in both `ci.yml` and `publish.yml`. - Updated `codecov/codecov-action` from v4 to v5 in `ci.yml`. - Changed `actions/upload-artifact` and `actions/download-artifact` from v4 to v5 in `publish.yml`. * Add retry logic with exponential backoff and jitter for rate limits and server errors (#6) - Introduced a new `_calculate_retry_delay` function to handle retry delays based on the Retry-After header and exponential backoff with jitter. - Updated `SyncHTTPClient` and `AsyncHTTPClientImpl` to utilize the new retry delay calculation for handling rate limits (429) and server errors (502, 503, 504). - Enhanced retry mechanism to improve resilience against temporary issues. Co-authored-by: alex-omophub <sdk@omophub.com> * Update CHANGELOG for v1.5.1 release --------- Co-authored-by: alex-omophub <sdk@omophub.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3312860 commit eafb8c7

43 files changed

Lines changed: 5054 additions & 1312 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, master, develop]
6+
pull_request:
7+
branches: [main, master, develop]
8+
workflow_dispatch: # Allow manual triggering
9+
10+
jobs:
11+
lint:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v6
15+
- uses: actions/setup-python@v5
16+
with:
17+
python-version: "3.12"
18+
- name: Install dependencies
19+
run: |
20+
python -m pip install --upgrade pip
21+
pip install ruff mypy
22+
pip install -e ".[dev]"
23+
- name: Run Ruff linter
24+
run: ruff check src/
25+
- name: Run Ruff formatter check
26+
run: ruff format --check src/
27+
- name: Run MyPy
28+
run: mypy src/omophub
29+
30+
test:
31+
runs-on: ubuntu-latest
32+
strategy:
33+
matrix:
34+
python-version: ["3.10", "3.11", "3.12", "3.13"]
35+
steps:
36+
- uses: actions/checkout@v6
37+
- uses: actions/setup-python@v5
38+
with:
39+
python-version: ${{ matrix.python-version }}
40+
- name: Install dependencies
41+
run: |
42+
python -m pip install --upgrade pip
43+
pip install -e ".[dev]"
44+
- name: Run unit tests with coverage
45+
run: |
46+
pytest tests/unit --cov=omophub --cov-report=xml --cov-report=term-missing
47+
- name: Upload coverage to Codecov
48+
if: matrix.python-version == '3.12'
49+
uses: codecov/codecov-action@v5
50+
with:
51+
files: ./coverage.xml
52+
fail_ci_if_error: false
53+
token: ${{ secrets.CODECOV_TOKEN }}
54+
55+
integration:
56+
# Only run on push to main/develop, not on PRs (saves ~7-8 min per PR)
57+
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
58+
runs-on: ubuntu-latest
59+
steps:
60+
- uses: actions/checkout@v6
61+
- uses: actions/setup-python@v5
62+
with:
63+
python-version: "3.12"
64+
- name: Install dependencies
65+
run: |
66+
python -m pip install --upgrade pip
67+
pip install -e ".[dev]"
68+
- name: Run integration tests
69+
env:
70+
TEST_API_KEY: ${{ secrets.TEST_API_KEY }}
71+
run: |
72+
pytest tests/integration -v

.github/workflows/publish.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v6
15+
with:
16+
fetch-depth: 0 # Required for hatch-vcs to get version from tags
17+
- uses: actions/setup-python@v5
18+
with:
19+
python-version: "3.12"
20+
- name: Install build tools
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install build
24+
- name: Build package
25+
run: python -m build
26+
- name: Upload distributions
27+
uses: actions/upload-artifact@v5
28+
with:
29+
name: release-dists
30+
path: dist/
31+
32+
publish:
33+
runs-on: ubuntu-latest
34+
needs: build
35+
environment:
36+
name: pypi
37+
url: https://pypi.org/project/omophub/
38+
permissions:
39+
id-token: write # Required for trusted publishing
40+
steps:
41+
- name: Download distributions
42+
uses: actions/download-artifact@v5
43+
with:
44+
name: release-dists
45+
path: dist/
46+
- name: Publish to PyPI
47+
uses: pypa/gh-action-pypi-publish@release/v1

CHANGELOG.md

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,96 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.5.1] - 2026-04-08
9+
10+
### Fixed
11+
12+
- **Rate-limit handling**: HTTP client now respects the `Retry-After` header on `429 Too Many Requests` responses and applies exponential backoff with jitter on retries. Previous versions retried only on `502/503/504` with a fixed `2^attempt * 0.5s` schedule and did not back off on `429` at all, so a client that hit the server's rate limit at high volume could burn through thousands of failed requests in a tight loop. The new behavior:
13+
- Updated `examples/search_concepts.py` to reflect current API.
14+
15+
## [1.5.0] - 2026-03-26
16+
17+
### Added
18+
19+
- **Bulk lexical search** (`search.bulk_basic()`): Execute up to 50 keyword searches in a single API call. Supports shared defaults for vocabulary, domain, and other filters. Each search is identified by a unique `search_id` for result matching. Maps to `POST /v1/search/bulk`.
20+
- **Bulk semantic search** (`search.bulk_semantic()`): Execute up to 25 natural-language searches using neural embeddings in a single call. Supports per-search similarity thresholds and shared defaults. Includes query enhancement data (abbreviation expansion, misspelling correction). Maps to `POST /v1/search/semantic-bulk`.
21+
- New TypedDict types for bulk search: `BulkSearchInput`, `BulkSearchDefaults`, `BulkSearchResponse`, `BulkSearchResultItem`, `BulkSemanticSearchInput`, `BulkSemanticSearchDefaults`, `BulkSemanticSearchResponse`, `BulkSemanticSearchResultItem`, `QueryEnhancement`.
22+
- Both sync (`OMOPHub`) and async (`AsyncOMOPHub`) clients support bulk search methods.
23+
24+
### Changed
25+
26+
- Updated `__all__` exports to alphabetical order (ruff RUF022 compliance).
27+
- `BulkSearchInput` and `BulkSemanticSearchInput` now use `Required[str]` for `search_id` and `query` fields for proper type checking.
28+
29+
## [1.4.1] - 2026-02-28
30+
31+
### Fixed
32+
33+
- User-Agent header now reports actual SDK version (e.g., `OMOPHub-SDK-Python/1.4.1`) instead of hardcoded `0.1.0`. Version is resolved at runtime via `importlib.metadata`.
34+
35+
## [1.4.0] - 2026-02-23
36+
37+
### Added
38+
39+
- **Semantic search** (`search.semantic()`, `search.semantic_iter()`): Natural language concept search using neural embeddings. Search for clinical intent like "high blood sugar levels" to find diabetes-related concepts. Supports filtering by vocabulary, domain, standard concept, concept class, and minimum similarity threshold. `semantic_iter()` provides automatic pagination.
40+
- **Similarity search** (`search.similar()`): Find concepts similar to a reference concept ID, concept name, or natural language query. Three algorithm options: `'semantic'` (neural embeddings), `'lexical'` (string matching), and `'hybrid'` (combined). Configurable similarity threshold with optional detailed scores and explanations.
41+
42+
## [1.3.1] - 2026-01-24
43+
44+
### Fixed
45+
46+
- Fixed `search.basic_iter()` pagination bug that caused only the first page of results to be returned. The iterator now correctly fetches all pages when iterating through search results.
47+
48+
### Changed
49+
50+
- Added `get_raw()` method to internal request classes for retrieving full API responses with pagination metadata.
51+
- Expanded `search.basic_iter()` method signature to explicitly list all filter parameters instead of using `**kwargs`.
52+
53+
## [1.3.0] - 2026-01-06
54+
55+
### Changes
56+
57+
**Parameter Renames (for API consistency):**
58+
- `search.autocomplete()`: `max_suggestions``page_size`
59+
- `concepts.suggest()`: `vocabulary``vocabulary_ids`, `domain``domain_ids`, `limit``page_size`
60+
- `concepts.related()`: `relatedness_types``relationship_types`
61+
- `concepts.relationships()`: `relationship_type``relationship_ids`
62+
- `relationships.get()`: `relationship_type``relationship_ids`, `target_vocabulary``vocabulary_ids`
63+
- `hierarchy.ancestors()`: `vocabulary_id``vocabulary_ids`, `include_deprecated``include_invalid`
64+
- `hierarchy.descendants()`: `vocabulary_id``vocabulary_ids`, `include_deprecated``include_invalid`
65+
66+
**Simplified APIs (removed parameters):**
67+
- `vocabularies.get()`: Removed `include_stats`, `include_domains` (use `stats()` method instead)
68+
- `vocabularies.domains()`: Removed pagination parameters, now returns all domains
69+
- `domains.list()`: Simplified to single `include_stats` parameter
70+
- `domains.concepts()`: Removed `concept_class_ids`, added `include_invalid`
71+
- `mappings.get()`: Simplified to `target_vocabulary`, `include_invalid`, `vocab_release`
72+
- `relationships.types()`: Removed all filtering parameters
73+
74+
**Default Changes:**
75+
- `vocabularies.list()`: Default `page_size` changed from 100 to 20
76+
- `concepts.batch()`: Default `standard_only` changed from `False` to `True`
77+
78+
### Added
79+
80+
- `vocabularies.domain_stats(vocabulary_id, domain_id)` - Get statistics for a specific domain within a vocabulary
81+
- `vocabularies.concept_classes()` - Get all concept classes
82+
- `hierarchy.get(concept_id)` - Get complete hierarchy (ancestors and descendants) in one call
83+
- `vocab_release` parameter to `concepts.get()`, `concepts.get_by_code()`, `mappings.get()`, `mappings.map()`
84+
- `include_hierarchy` parameter to `concepts.get()` and `concepts.get_by_code()`
85+
- Pagination support to `concepts.suggest()`
86+
- `domain_ids`, `standard_only`, `include_reverse` parameters to `relationships.get()`
87+
88+
## [1.2.0] - 2025-12-09
89+
90+
### Added
91+
92+
- `include_synonyms` and `include_relationships` parameters to `concepts.get_by_code()` method for retrieving concept synonyms and relationships in a single request.
93+
94+
### Changed
95+
96+
- User-Agent header updated to `OMOPHub-SDK-Python/{version}`.
97+
898
## [0.1.0] - 2025-12-01
999

10100
### Added
@@ -27,5 +117,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
27117
- Full type hints and PEP 561 compliance
28118
- HTTP/2 support via httpx
29119

30-
[Unreleased]: https://github.com/omopHub/omophub-python/compare/v0.1.0...HEAD
120+
[Unreleased]: https://github.com/omopHub/omophub-python/compare/v1.4.1...HEAD
121+
[1.4.1]: https://github.com/omopHub/omophub-python/compare/v1.4.0...v1.4.1
122+
[1.4.0]: https://github.com/omopHub/omophub-python/compare/v1.3.1...v1.4.0
123+
[1.3.1]: https://github.com/omopHub/omophub-python/compare/v1.3.0...v1.3.1
124+
[1.3.0]: https://github.com/omopHub/omophub-python/compare/v1.2.0...v1.3.0
125+
[1.2.0]: https://github.com/omopHub/omophub-python/compare/v0.1.0...v1.2.0
31126
[0.1.0]: https://github.com/omopHub/omophub-python/releases/tag/v0.1.0

CONTRIBUTING.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# Contributing to OMOPHub Python SDK
2+
3+
First off, thank you for considering contributing to OMOPHub!
4+
5+
## How Can I Contribute?
6+
7+
### Reporting Bugs
8+
9+
Before creating bug reports, please check the [existing issues](https://github.com/OMOPHub/omophub-python/issues) to avoid duplicates.
10+
11+
When creating a bug report, please include:
12+
13+
- **Python version** (`python --version`)
14+
- **SDK version** (`pip show omophub`)
15+
- **Operating system**
16+
- **Minimal code example** that reproduces the issue
17+
- **Full error traceback**
18+
- **Expected vs actual behavior**
19+
20+
### Suggesting Features
21+
22+
Feature requests are welcome! Please open an issue with:
23+
24+
- Clear description of the feature
25+
- Use case: why would this be useful?
26+
- Possible implementation approach (optional)
27+
28+
### Pull Requests
29+
30+
1. **Fork the repository** and create your branch from `main`
31+
2. **Install development dependencies:**
32+
```bash
33+
git clone https://github.com/YOUR_USERNAME/omophub-python.git
34+
cd omophub-python
35+
pip install -e ".[dev]"
36+
```
37+
3. **Make your changes** with clear, descriptive commits
38+
4. **Add tests** for new functionality
39+
5. **Run the test suite:**
40+
```bash
41+
pytest
42+
```
43+
6. **Ensure code style compliance:**
44+
```bash
45+
ruff check .
46+
ruff format .
47+
mypy src/
48+
```
49+
7. **Update documentation** if needed
50+
8. **Submit a pull request** with a clear description
51+
52+
## Development Setup
53+
54+
### Prerequisites
55+
56+
- Python 3.9+
57+
- pip
58+
59+
### Installation
60+
61+
```bash
62+
# Clone your fork
63+
git clone https://github.com/YOUR_USERNAME/omophub-python.git
64+
cd omophub-python
65+
66+
# Create virtual environment
67+
python -m venv venv
68+
source venv/bin/activate # On Windows: venv\Scripts\activate
69+
70+
# Install in development mode
71+
pip install -e ".[dev]"
72+
```
73+
74+
### Running Tests
75+
76+
```bash
77+
# Run all tests
78+
pytest
79+
80+
# Run with coverage
81+
pytest --cov=omophub --cov-report=html
82+
83+
# Run specific test file
84+
pytest tests/test_concepts.py
85+
86+
# Run tests matching a pattern
87+
pytest -k "test_search"
88+
```
89+
90+
### Code Style
91+
92+
We use:
93+
- **Ruff** for linting and formatting
94+
- **mypy** for type checking
95+
96+
```bash
97+
# Check linting
98+
ruff check .
99+
100+
# Auto-format code
101+
ruff format .
102+
103+
# Type checking
104+
mypy src/
105+
```
106+
107+
## Project Structure
108+
109+
```
110+
omophub-python/
111+
├── src/omophub/
112+
│ ├── __init__.py # Public API exports
113+
│ ├── client.py # OMOPHub client class
114+
│ ├── resources/ # API resource classes
115+
│ │ ├── concepts.py
116+
│ │ ├── search.py
117+
│ │ ├── hierarchy.py
118+
│ │ └── ...
119+
│ ├── types.py # TypedDict definitions
120+
│ └── exceptions.py # Custom exceptions
121+
├── tests/
122+
│ ├── test_concepts.py
123+
│ ├── test_search.py
124+
│ └── ...
125+
├── examples/
126+
│ └── ...
127+
└── pyproject.toml
128+
```
129+
130+
## Commit Messages
131+
132+
We follow [Conventional Commits](https://www.conventionalcommits.org/):
133+
134+
- `feat:` New feature
135+
- `fix:` Bug fix
136+
- `docs:` Documentation changes
137+
- `test:` Adding or updating tests
138+
- `refactor:` Code refactoring
139+
- `chore:` Maintenance tasks
140+
141+
Examples:
142+
```
143+
feat: add semantic search endpoint
144+
fix: handle rate limit errors correctly
145+
docs: update README with new examples
146+
test: add tests for batch concept lookup
147+
```
148+
149+
## Questions?
150+
151+
- Open a [GitHub Discussion](https://github.com/OMOPHub/omophub-python/discussions)
152+
- Email: support@omophub.com
153+
154+
## License
155+
156+
By contributing, you agree that your contributions will be licensed under the MIT License.
157+
158+
---
159+
160+
Thank you for helping make OMOPHub better!

0 commit comments

Comments
 (0)