# AGR Curation API Client
A unified Python client for Alliance of Genome Resources (AGR) curation APIs.
## Features
- **Unified Interface**: Single client for all AGR curation API endpoints
- **Type Safety**: Full type hints and Pydantic models for request/response validation
- **Retry Logic**: Automatic retry with exponential backoff for transient failures
- **Authentication**: Support for API key and Okta token authentication
- **Async Support**: Built on httpx for both sync and async operations
- **Comprehensive Error Handling**: Detailed exceptions for different error scenarios
## Installation
```bash
pip install agr-curation-api-client
```
For development:
```bash
git clone https://github.com/alliance-genome/agr_curation_api_client.git
cd agr_curation_api_client
make install-dev
```
## Authentication
The client supports automatic Okta token generation using the same environment variables as other AGR services:
```bash
export OKTA_DOMAIN="your-okta-domain"
export OKTA_API_AUDIENCE="your-api-audience"
export OKTA_CLIENT_ID="your-client-id"
export OKTA_CLIENT_SECRET="your-client-secret"
```
With these environment variables set, the client will automatically obtain an authentication token when initialized.
## Quick Start
### Basic Usage
```python
from agr_curation_api import AGRCurationAPIClient, APIConfig
# Option 1: Automatic authentication (requires OKTA env vars)
client = AGRCurationAPIClient()
# Option 2: Manual token configuration
config = APIConfig(
base_url="https://curation.alliancegenome.org/api",
okta_token="your-okta-token" # Optional - will auto-retrieve if not provided
)
client = AGRCurationAPIClient(config)
# Use the client
with client:
# Get genes from WormBase
genes = client.get_genes(data_provider="WB", limit=10)
for gene in genes:
symbol = gene.gene_symbol.get("displayText", "") if gene.gene_symbol else ""
print(f"{gene.curie}: {symbol}")
```
### Working with Genes
```python
from agr_curation_api import AGRCurationAPIClient, Gene
# Use default configuration
client = AGRCurationAPIClient()
# Get genes from a specific data provider
wb_genes = client.get_genes(data_provider="WB", limit=100)
print(f"Found {len(wb_genes)} WormBase genes")
# Get a specific gene by ID
gene = client.get_gene("WB:WBGene00001234")
if gene:
print(f"Gene: {gene.gene_symbol}")
print(f"Full name: {gene.gene_full_name}")
print(f"Species: {gene.taxon}")
# Get all genes (paginated)
all_genes = client.get_genes(limit=5000, page=0)
```
### Working with Species
```python
# Get all species
species_list = client.get_species()
for species in species_list:
print(f"{species.abbreviation}: {species.display_name}")
# Find a specific species
wb_species = [s for s in species_list if s.abbreviation == "WB"]
if wb_species:
print(f"WormBase: {wb_species[0].full_name}")
```
### Working with Ontology Terms
```python
# Get GO term root nodes
go_roots = client.get_ontology_root_nodes("goterm")
print(f"Found {len(go_roots)} GO root terms")
# Get children of a specific GO term
children = client.get_ontology_node_children("GO:0008150", "goterm") # biological_process
for child in children:
print(f"{child.curie}: {child.name}")
# Get disease ontology terms
disease_roots = client.get_ontology_root_nodes("doterm")
# Get anatomical terms
anatomy_roots = client.get_ontology_root_nodes("anatomicalterm")
```
### Working with Expression Annotations
```python
# Get expression annotations for WormBase
wb_expressions = client.get_expression_annotations(
data_provider="WB",
limit=100
)
for expr in wb_expressions:
if expr.expression_annotation_subject:
gene_id = expr.expression_annotation_subject.get("primaryExternalId")
gene_symbol = expr.expression_annotation_subject.get("geneSymbol", {}).get("displayText")
print(f"Gene: {gene_id} ({gene_symbol})")
if expr.expression_pattern:
anatomy = expr.expression_pattern.get("whereExpressed", {}).get("anatomicalStructure", {}).get("curie")
print(f" Expressed in: {anatomy}")
```
### Working with Alleles
```python
# Get alleles from a specific data provider
wb_alleles = client.get_alleles(data_provider="WB", limit=50)
for allele in wb_alleles:
symbol = allele.allele_symbol.get("displayText", "") if allele.allele_symbol else ""
print(f"{allele.curie}: {symbol}")
# Get a specific allele
allele = client.get_allele("WB:WBVar00001234")
if allele:
print(f"Allele: {allele.allele_symbol}")
print(f"Full name: {allele.allele_full_name}")
```
### Generic Search
```python
# Generic entity search
search_filters = {
"dataProvider.abbreviation": "WB",
"geneSymbol.displayText": "daf-16"
}
results = client.search_entities(
entity_type="gene",
search_filters=search_filters,
limit=10
)
print(f"Total results: {results.total_results}")
print(f"Returned: {results.returned_records}")
for gene_data in results.results:
print(f"Found gene: {gene_data}")
```
### Error Handling
```python
from agr_curation_api import (
AGRAPIError,
AGRAuthenticationError,
AGRConnectionError,
AGRTimeoutError,
AGRValidationError
)
try:
reference = client.get_reference("invalid-id")
except AGRAuthenticationError:
print("Authentication failed - check your credentials")
except AGRValidationError as e:
print(f"Invalid data: {e}")
except AGRTimeoutError:
print("Request timed out - try again later")
except AGRConnectionError:
print("Connection failed - check network")
except AGRAPIError as e:
print(f"API error: {e}")
if e.status_code:
print(f"Status code: {e.status_code}")
```
## Configuration Options
The `APIConfig` class supports the following options:
- `base_url`: Base URL for the A-Team Curation API (default: "https://curation.alliancegenome.org/api")
- `okta_token`: Okta bearer token for authentication (auto-retrieved if not provided)
- `timeout`: Request timeout in seconds (default: 30)
- `max_retries`: Maximum retry attempts (default: 3)
- `retry_delay`: Initial delay between retries in seconds (default: 1)
- `verify_ssl`: Whether to verify SSL certificates (default: True)
- `headers`: Additional headers to include in requests
### Environment Variables
The client uses the following environment variables for configuration:
- `ATEAM_API`: Override the default A-Team API URL (default: uses production curation API)
- `OKTA_DOMAIN`: Your Okta domain (required for automatic authentication)
- `OKTA_API_AUDIENCE`: Your API audience (required for automatic authentication)
- `OKTA_CLIENT_ID`: Your Okta client ID (required for automatic authentication)
- `OKTA_CLIENT_SECRET`: Your Okta client secret (required for automatic authentication)
## Development
### Running Tests
```bash
make test
```
### Code Quality
```bash
# Run linting
make lint
# Run type checking
make type-check
# Format code
make format
# Run all checks
make check
```
### Building Documentation
```bash
cd docs
make html
```
## Contributing
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
## License
This project is licensed under the MIT License - see the LICENSE file for details.
## Support
- **Issues**: [GitHub Issues](https://github.com/alliance-genome/agr_curation_api_client/issues)
- **Documentation**: [API Documentation](https://alliancegenome.org/api-docs)
- **Contact**: software@alliancegenome.org
Raw data
{
"_id": null,
"home_page": null,
"name": "agr-curation-api-client",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": "Alliance Blue Team <valearna@caltech.edu>",
"keywords": "agr, alliance, genome, curation, api, client",
"author": null,
"author_email": "Alliance of Genome Resources <valearna@caltech.edu>",
"download_url": "https://files.pythonhosted.org/packages/09/03/fe15453ae2010ffa220c162279aca7e0377739c37411df7654a18947a0b5/agr_curation_api_client-0.1.0.tar.gz",
"platform": null,
"description": "# AGR Curation API Client\n\nA unified Python client for Alliance of Genome Resources (AGR) curation APIs.\n\n## Features\n\n- **Unified Interface**: Single client for all AGR curation API endpoints\n- **Type Safety**: Full type hints and Pydantic models for request/response validation\n- **Retry Logic**: Automatic retry with exponential backoff for transient failures\n- **Authentication**: Support for API key and Okta token authentication\n- **Async Support**: Built on httpx for both sync and async operations\n- **Comprehensive Error Handling**: Detailed exceptions for different error scenarios\n\n## Installation\n\n```bash\npip install agr-curation-api-client\n```\n\nFor development:\n```bash\ngit clone https://github.com/alliance-genome/agr_curation_api_client.git\ncd agr_curation_api_client\nmake install-dev\n```\n\n## Authentication\n\nThe client supports automatic Okta token generation using the same environment variables as other AGR services:\n\n```bash\nexport OKTA_DOMAIN=\"your-okta-domain\"\nexport OKTA_API_AUDIENCE=\"your-api-audience\"\nexport OKTA_CLIENT_ID=\"your-client-id\"\nexport OKTA_CLIENT_SECRET=\"your-client-secret\"\n```\n\nWith these environment variables set, the client will automatically obtain an authentication token when initialized.\n\n## Quick Start\n\n### Basic Usage\n\n```python\nfrom agr_curation_api import AGRCurationAPIClient, APIConfig\n\n# Option 1: Automatic authentication (requires OKTA env vars)\nclient = AGRCurationAPIClient()\n\n# Option 2: Manual token configuration\nconfig = APIConfig(\n base_url=\"https://curation.alliancegenome.org/api\",\n okta_token=\"your-okta-token\" # Optional - will auto-retrieve if not provided\n)\nclient = AGRCurationAPIClient(config)\n\n# Use the client\nwith client:\n # Get genes from WormBase\n genes = client.get_genes(data_provider=\"WB\", limit=10)\n \n for gene in genes:\n symbol = gene.gene_symbol.get(\"displayText\", \"\") if gene.gene_symbol else \"\"\n print(f\"{gene.curie}: {symbol}\")\n```\n\n### Working with Genes\n\n```python\nfrom agr_curation_api import AGRCurationAPIClient, Gene\n\n# Use default configuration\nclient = AGRCurationAPIClient()\n\n# Get genes from a specific data provider\nwb_genes = client.get_genes(data_provider=\"WB\", limit=100)\nprint(f\"Found {len(wb_genes)} WormBase genes\")\n\n# Get a specific gene by ID\ngene = client.get_gene(\"WB:WBGene00001234\")\nif gene:\n print(f\"Gene: {gene.gene_symbol}\")\n print(f\"Full name: {gene.gene_full_name}\")\n print(f\"Species: {gene.taxon}\")\n\n# Get all genes (paginated)\nall_genes = client.get_genes(limit=5000, page=0)\n```\n\n### Working with Species\n\n```python\n# Get all species\nspecies_list = client.get_species()\n\nfor species in species_list:\n print(f\"{species.abbreviation}: {species.display_name}\")\n\n# Find a specific species\nwb_species = [s for s in species_list if s.abbreviation == \"WB\"]\nif wb_species:\n print(f\"WormBase: {wb_species[0].full_name}\")\n```\n\n### Working with Ontology Terms\n\n```python\n# Get GO term root nodes\ngo_roots = client.get_ontology_root_nodes(\"goterm\")\nprint(f\"Found {len(go_roots)} GO root terms\")\n\n# Get children of a specific GO term\nchildren = client.get_ontology_node_children(\"GO:0008150\", \"goterm\") # biological_process\nfor child in children:\n print(f\"{child.curie}: {child.name}\")\n\n# Get disease ontology terms\ndisease_roots = client.get_ontology_root_nodes(\"doterm\")\n\n# Get anatomical terms\nanatomy_roots = client.get_ontology_root_nodes(\"anatomicalterm\")\n```\n\n### Working with Expression Annotations\n\n```python\n# Get expression annotations for WormBase\nwb_expressions = client.get_expression_annotations(\n data_provider=\"WB\",\n limit=100\n)\n\nfor expr in wb_expressions:\n if expr.expression_annotation_subject:\n gene_id = expr.expression_annotation_subject.get(\"primaryExternalId\")\n gene_symbol = expr.expression_annotation_subject.get(\"geneSymbol\", {}).get(\"displayText\")\n print(f\"Gene: {gene_id} ({gene_symbol})\")\n \n if expr.expression_pattern:\n anatomy = expr.expression_pattern.get(\"whereExpressed\", {}).get(\"anatomicalStructure\", {}).get(\"curie\")\n print(f\" Expressed in: {anatomy}\")\n```\n\n### Working with Alleles\n\n```python\n# Get alleles from a specific data provider\nwb_alleles = client.get_alleles(data_provider=\"WB\", limit=50)\n\nfor allele in wb_alleles:\n symbol = allele.allele_symbol.get(\"displayText\", \"\") if allele.allele_symbol else \"\"\n print(f\"{allele.curie}: {symbol}\")\n\n# Get a specific allele\nallele = client.get_allele(\"WB:WBVar00001234\")\nif allele:\n print(f\"Allele: {allele.allele_symbol}\")\n print(f\"Full name: {allele.allele_full_name}\")\n```\n\n### Generic Search\n\n```python\n# Generic entity search\nsearch_filters = {\n \"dataProvider.abbreviation\": \"WB\",\n \"geneSymbol.displayText\": \"daf-16\"\n}\n\nresults = client.search_entities(\n entity_type=\"gene\",\n search_filters=search_filters,\n limit=10\n)\n\nprint(f\"Total results: {results.total_results}\")\nprint(f\"Returned: {results.returned_records}\")\n\nfor gene_data in results.results:\n print(f\"Found gene: {gene_data}\")\n```\n\n### Error Handling\n\n```python\nfrom agr_curation_api import (\n AGRAPIError,\n AGRAuthenticationError,\n AGRConnectionError,\n AGRTimeoutError,\n AGRValidationError\n)\n\ntry:\n reference = client.get_reference(\"invalid-id\")\nexcept AGRAuthenticationError:\n print(\"Authentication failed - check your credentials\")\nexcept AGRValidationError as e:\n print(f\"Invalid data: {e}\")\nexcept AGRTimeoutError:\n print(\"Request timed out - try again later\")\nexcept AGRConnectionError:\n print(\"Connection failed - check network\")\nexcept AGRAPIError as e:\n print(f\"API error: {e}\")\n if e.status_code:\n print(f\"Status code: {e.status_code}\")\n```\n\n## Configuration Options\n\nThe `APIConfig` class supports the following options:\n\n- `base_url`: Base URL for the A-Team Curation API (default: \"https://curation.alliancegenome.org/api\")\n- `okta_token`: Okta bearer token for authentication (auto-retrieved if not provided)\n- `timeout`: Request timeout in seconds (default: 30)\n- `max_retries`: Maximum retry attempts (default: 3)\n- `retry_delay`: Initial delay between retries in seconds (default: 1)\n- `verify_ssl`: Whether to verify SSL certificates (default: True)\n- `headers`: Additional headers to include in requests\n\n### Environment Variables\n\nThe client uses the following environment variables for configuration:\n\n- `ATEAM_API`: Override the default A-Team API URL (default: uses production curation API)\n- `OKTA_DOMAIN`: Your Okta domain (required for automatic authentication)\n- `OKTA_API_AUDIENCE`: Your API audience (required for automatic authentication)\n- `OKTA_CLIENT_ID`: Your Okta client ID (required for automatic authentication)\n- `OKTA_CLIENT_SECRET`: Your Okta client secret (required for automatic authentication)\n\n## Development\n\n### Running Tests\n\n```bash\nmake test\n```\n\n### Code Quality\n\n```bash\n# Run linting\nmake lint\n\n# Run type checking\nmake type-check\n\n# Format code\nmake format\n\n# Run all checks\nmake check\n```\n\n### Building Documentation\n\n```bash\ncd docs\nmake html\n```\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'feat: add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n\n## Support\n\n- **Issues**: [GitHub Issues](https://github.com/alliance-genome/agr_curation_api_client/issues)\n- **Documentation**: [API Documentation](https://alliancegenome.org/api-docs)\n- **Contact**: software@alliancegenome.org\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Unified Python client for Alliance of Genome Resources (AGR) curation APIs",
"version": "0.1.0",
"project_urls": {
"Bug Reports": "https://github.com/alliance-genome/agr_curation_api_client/issues",
"Documentation": "https://github.com/alliance-genome/agr_curation_api_client#readme",
"Homepage": "https://github.com/alliance-genome/agr_curation_api_client",
"Source": "https://github.com/alliance-genome/agr_curation_api_client"
},
"split_keywords": [
"agr",
" alliance",
" genome",
" curation",
" api",
" client"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "8b012552685a2569f7c2dfa68be0e7dc3fe23115e063140a43f1343a61357992",
"md5": "3347938a7dbbc93e31bf62b5e7d6a54c",
"sha256": "65ed4f819513154f3d0b8a978fa77b7c26e633ea619f857dfdc7806430aa4210"
},
"downloads": -1,
"filename": "agr_curation_api_client-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "3347938a7dbbc93e31bf62b5e7d6a54c",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 10581,
"upload_time": "2025-08-05T16:46:44",
"upload_time_iso_8601": "2025-08-05T16:46:44.391606Z",
"url": "https://files.pythonhosted.org/packages/8b/01/2552685a2569f7c2dfa68be0e7dc3fe23115e063140a43f1343a61357992/agr_curation_api_client-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "0903fe15453ae2010ffa220c162279aca7e0377739c37411df7654a18947a0b5",
"md5": "d19e10243f47a272a9d847d807c3a1bb",
"sha256": "b4363a49a582bf400259e184726aae5ad677a50c2a43401ba9430330d17a715a"
},
"downloads": -1,
"filename": "agr_curation_api_client-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "d19e10243f47a272a9d847d807c3a1bb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 13706,
"upload_time": "2025-08-05T16:46:45",
"upload_time_iso_8601": "2025-08-05T16:46:45.569489Z",
"url": "https://files.pythonhosted.org/packages/09/03/fe15453ae2010ffa220c162279aca7e0377739c37411df7654a18947a0b5/agr_curation_api_client-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-05 16:46:45",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "alliance-genome",
"github_project": "agr_curation_api_client",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "requests",
"specs": [
[
">=",
"2.28.0"
]
]
},
{
"name": "pydantic",
"specs": [
[
">=",
"2.0.0"
]
]
},
{
"name": "httpx",
"specs": [
[
">=",
"0.24.0"
]
]
},
{
"name": "tenacity",
"specs": [
[
">=",
"8.0.0"
]
]
},
{
"name": "python-dateutil",
"specs": [
[
">=",
"2.8.0"
]
]
},
{
"name": "fastapi_okta",
"specs": []
}
],
"lcname": "agr-curation-api-client"
}