Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.7.1] - 2026-05-20

Maintenance release. Dependency and lock-file updates only — there are no
source code or public API changes. The published runtime dependencies
(`httpx`, `typing_extensions`) are unchanged; the updates below affect the
pinned development, testing, and optional-extra dependencies in `uv.lock`
and are not shipped in the wheel/sdist.

### Security

- Updated pinned development and transitive dependencies in `uv.lock` to
resolve reported advisories. None of these affect the published runtime
dependencies:
- `idna` 3.11 → 3.15 — fixes a bypass of the CVE-2024-3651 mitigation in
`idna.encode()` (transitive via `httpx`/`anyio`).
- `pytest` 9.0.2 → 9.0.3 — fixes vulnerable tmpdir handling (dev only).
- `python-dotenv` 1.2.1 → 1.2.2 — fixes symlink following in `set_key`
that allowed arbitrary file overwrite (dev only).
- `pygments` 2.19.2 → 2.20.0 — fixes a ReDoS in GUID matching
(transitive, dev only).

### Changed

- Bumped `aiohttp` and added `aiohappyeyeballs` in `uv.lock` (#17).
- Locked the optional FHIR extras (`fhirpy`, `fhir-resources`) and their
transitive dependencies in `uv.lock`. These were already declared in
`pyproject.toml` but had never been captured in the lock file.

## [1.7.0] - 2026-04-14

### Added
Expand Down Expand Up @@ -216,7 +244,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Full type hints and PEP 561 compliance
- HTTP/2 support via httpx

[Unreleased]: https://github.com/omopHub/omophub-python/compare/v1.6.0...HEAD
[Unreleased]: https://github.com/omopHub/omophub-python/compare/v1.7.1...HEAD
[1.7.1]: https://github.com/omopHub/omophub-python/compare/v1.7.0...v1.7.1
[1.7.0]: https://github.com/omopHub/omophub-python/compare/v1.6.0...v1.7.0
[1.6.0]: https://github.com/omopHub/omophub-python/compare/v1.5.1...v1.6.0
[1.5.1]: https://github.com/omopHub/omophub-python/compare/v1.5.0...v1.5.1
[1.5.0]: https://github.com/omopHub/omophub-python/compare/v1.4.1...v1.5.0
Expand Down
30 changes: 17 additions & 13 deletions examples/search_concepts.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ def basic_search() -> None:
"""Demonstrate basic concept search."""
print("=== Basic Search ===")

# Simple text search - returns a flat list of concept dicts
concepts = client.search.basic("heart attack")
# ``search.basic`` returns a dict with a ``concepts`` list plus pagination
# metadata - read the list from ``result["concepts"]``.
result = client.search.basic("heart attack")
concepts = result["concepts"]
print(f"Found {len(concepts)} concepts for 'heart attack'")

for c in concepts[:3]:
Expand All @@ -28,14 +30,15 @@ def filtered_search() -> None:
"""Demonstrate search with filters."""
print("\n=== Filtered Search ===")

# Search in specific vocabularies
concepts = client.search.basic(
# Search in specific vocabularies. Same dict-shaped response as above.
result = client.search.basic(
"myocardial infarction",
vocabulary_ids=["SNOMED", "ICD10CM"],
domain_ids=["Condition"],
standard_concept="S", # Only standard concepts
page_size=10,
)
concepts = result["concepts"]
print(f"Found {len(concepts)} standard condition concepts")

for c in concepts[:5]:
Expand All @@ -45,14 +48,15 @@ def filtered_search() -> None:
def bulk_lexical_search() -> None:
"""Demonstrate bulk lexical search — multiple queries in one call.

``bulk_basic`` returns a list of per-query result objects. Each item
has ``search_id``, ``query``, ``results`` (a nested list), ``status``,
and ``duration``.
``bulk_basic`` returns a dict with a ``results`` list (plus
``total_searches`` / ``completed_searches`` / ``failed_searches``
counts). Each item in ``results`` has ``search_id``, ``query``,
``results`` (a nested list of concepts), ``status``, and ``duration``.
"""
print("\n=== Bulk Lexical Search ===")

# Search for multiple terms at once (up to 50)
items = client.search.bulk_basic(
response = client.search.bulk_basic(
[
{"search_id": "q1", "query": "diabetes mellitus"},
{"search_id": "q2", "query": "hypertension"},
Expand All @@ -61,13 +65,13 @@ def bulk_lexical_search() -> None:
defaults={"vocabulary_ids": ["SNOMED"], "page_size": 5},
)

for item in items:
for item in response["results"]:
print(
f" {item['search_id']}: {len(item['results'])} results ({item['status']})"
)

# Per-query overrides — different domains per query
items = client.search.bulk_basic(
response = client.search.bulk_basic(
[
{
"search_id": "conditions",
Expand All @@ -80,7 +84,7 @@ def bulk_lexical_search() -> None:
)

print("\n Per-query domain overrides:")
for item in items:
for item in response["results"]:
print(f" {item['search_id']}:")
for c in item["results"][:3]:
print(f" {c['concept_name']} ({c['vocabulary_id']}/{c['domain_id']})")
Expand Down Expand Up @@ -124,8 +128,8 @@ def bulk_semantic_search() -> None:
def autocomplete_example() -> None:
"""Demonstrate autocomplete suggestions.

``concepts.suggest`` returns a flat list of concept dicts - the same
shape as ``search.basic`` - so you read ``concept_name`` directly.
``concepts.suggest`` returns its own response shape (distinct from
``search.basic``); read suggestion fields off each item directly.
"""
print("\n=== Autocomplete ===")

Expand Down
Loading
Loading