# Annuaire Santé FHIR - Client Python Moderne
[](https://badge.fury.io/py/annuaire-sante-fhir)
[](https://pypi.org/project/annuaire-sante-fhir/)
[](https://opensource.org/licenses/MIT)
Client Python moderne et type-safe pour l'API FHIR de l'Annuaire Santé avec modèles JSON propres, sans artefacts FHIR, prêts pour injection en base de données.
## Caractéristiques
- **Modèles Pydantic v2** propres et DB-ready sans artefacts FHIR
- **Résolution automatique des codes MOS** (Modèle des Objets de Santé)
- **Transformateurs FHIR → JSON** pour chaque ressource
- **Type hints complets** pour une excellente expérience développeur
- **Support complet des profils**:
- FR Core v2.1.0 (Practitioner, Organization, PractitionerRole, HealthcareService)
- AS DP v1.1.0 (Annuaire Santé Données Publiques)
## Installation
```bash
pip install annuaire-sante-fhir
```
**Note**: Le nom du package sur PyPI est `annuaire-sante-fhir`, mais l'import dans votre code reste `annuairesante`:
```python
from annuairesante import AnnuaireSanteClient, transform_practitioner
```
## Configuration de la clé API
Pour utiliser l'API Annuaire Santé, vous devez obtenir une clé API :
1. **Obtenir une clé** : Rendez-vous sur [https://ansforge.github.io/annuaire-sante-fhir-documentation/pages/guide/version-2/getting-started/get-api-key.html](https://ansforge.github.io/annuaire-sante-fhir-documentation/pages/guide/version-2/getting-started/get-api-key.html) et créez un compte pour obtenir votre clé API
2. **Configurer la clé** : Trois méthodes disponibles
### Méthode 1 : Variable d'environnement (recommandé)
```bash
export ANNUAIRE_SANTE_API_KEY="votre-cle-api"
```
### Méthode 2 : Fichier .env
Créez un fichier `.env` à la racine de votre projet :
```env
ANNUAIRE_SANTE_API_KEY=votre-cle-api
```
### Méthode 3 : Paramètre direct
```python
from annuairesante import AnnuaireSanteClient
client = AnnuaireSanteClient(api_key="votre-cle-api")
```
⚠️ **Sécurité** : Ne commitez jamais votre clé API dans Git ! Ajoutez `.env` à votre `.gitignore`.
## Utilisation rapide
### Recherche simple (quelques résultats)
```python
from annuairesante import AnnuaireSanteClient, transform_practitioner
# Initialiser le client
client = AnnuaireSanteClient() # Utilise ANNUAIRE_SANTE_API_KEY
# Rechercher des professionnels
bundle = client.practitioner.search(family="MARTIN", given="Jean")
print(f"Total trouvé: {bundle.total}")
# Parcourir les résultats
for resource in bundle.entries:
practitioner = transform_practitioner(resource)
print(f"Nom: {practitioner.name.full_text}")
print(f"RPPS: {practitioner.identifiers.rpps}")
# Exporter en JSON pour base de données
db_ready_json = practitioner.model_dump(mode='json')
```
### Synchronisation de masse (pagination automatique)
```python
from annuairesante import AnnuaireSanteClient, transform_organization
client = AnnuaireSanteClient()
# Synchroniser toutes les organisations d'une région
count = 0
for org_fhir in client.organization.search_all(
**{"address-postalcode": "69"}, # Département du Rhône
active=True
):
org = transform_organization(org_fhir)
database.save(org.model_dump(mode='json'))
count += 1
if count % 100 == 0:
print(f"{count} organisations synchronisées...")
print(f"Total: {count} organisations")
```
### Synchronisation incrémentale
```python
from annuairesante import AnnuaireSanteClient
from datetime import datetime
client = AnnuaireSanteClient()
# Synchroniser uniquement les modifications depuis la dernière synchro
last_sync = "2025-01-01T00:00:00Z"
for practitioner in client.practitioner.search_all(
_lastUpdated=f"ge{last_sync}", # ge = greater or equal
**{"address-city": "Lyon"}
):
database.upsert(practitioner) # Mise à jour ou insertion
# Sauvegarder la date de synchro
database.set_last_sync(datetime.utcnow().isoformat() + "Z")
```
## Modèles disponibles
### Practitioner (Professionnel de santé)
```python
{
"identifiers": {
"idnps": "810101205564", # Obligatoire
"rpps": "10101205564", # Obligatoire
"adeli": None
},
"name": {
"family": "VERDIER",
"given": ["Pauline"],
"prefix": "MME", # Civilité (JDV_J78)
"suffix": ["DR"], # Titre exercice (JDV_J79)
"full_text": "VERDIER Pauline"
},
"gender": "female",
"birth_date": "1985-03-15",
"contacts": {
"phones": ["+33612345678"],
"emails": ["contact@example.com"],
"mssante": [{
"email": "pauline.verdier@aura.mssante.fr",
"type": "PER", # ORG, APP, PER, CAB
"digitization": false,
"liste_rouge": false
}]
},
"addresses": [{
"lines": ["2 RUE CLAUDE BERNARD"],
"city": "PARIS",
"postal_code": "75005",
"insee_code": "75105"
}],
"qualifications": { # Indexé par type puis nom de système
"profession": {
"ProfessionSante": {
"code": "21",
"display": "Médecin" # Résolu via MOS
},
"CategorieProfessionnelle": {
"code": "C",
"display": "Civil"
}
},
"diplome": {
"DiplomeEtatFrancais": {
"code": "DE28",
"display": "DE Docteur en médecine"
}
}
},
"smartcards": [{
"type": "CPS",
"number": "3100434368",
"period": {"start": "2024-02-21", "end": "2027-02-21"},
"is_valid": true
}],
"metadata": {
"id": "003-3014698-3057235",
"version_id": "1",
"last_updated": "2025-04-28T18:19:26.335+02:00",
"profiles": ["https://hl7.fr/ig/fhir/core/StructureDefinition/fr-core-practitioner"],
"data_trace": {
"systeme_information": "RPPS"
}
},
"active": true
}
```
### Organization (Structure de santé)
```python
{
"identifiers": {
"finess": "750010753", # FINEJ ou FINEG
"idnst": "1750010753",
"siret": "12345678901234"
},
"name": "PHARMACIE BLONDEEL",
"aliases": ["LA GRANDE PHARMACIE DU 5"],
"types_by_category": { # Indexé par catégorie
"categorieEtablissement": {
"code": "620",
"display": "Pharmacie d'officine", # Résolu via MOS
"category": "categorieEtablissement"
},
"secteurActiviteRASS": {
"code": "SA33",
"display": "Secteur privé commercial",
"category": "secteurActiviteRASS"
},
"statutJuridiqueINSEE": {
"code": "101",
"display": "SELAS",
"category": "statutJuridiqueINSEE"
}
},
"primary_type": { # Type sans catégorie spécifique
"code": "620",
"display": "Pharmacie d'Officine",
"category": null
},
"pharmacy_licence": "75#000283",
"addresses": [...]
}
```
### Autres ressources
- **PractitionerRole**: Situation d'exercice (genre, mode, fonction)
- **HealthcareService**: Service/activité de santé (modalité, type, forme)
- **Device**: Équipement matériel lourd
## Accès simplifié aux données
Les structures JSON sont optimisées pour un accès direct sans boucles :
```python
# Practitioner - Accès aux qualifications
pract.qualifications["profession"]["ProfessionSante"]
# → {"code": "21", "display": "Pharmacien"}
# Helpers disponibles
pract.get_profession() # Code profession principal
pract.get_diploma() # Diplôme principal
pract.get_category() # Catégorie professionnelle
# Organization - Accès aux types
org.types_by_category["secteurActiviteRASS"]
# → {"code": "SA33", "display": "Pharmacie d'officine"}
org.primary_type # Type principal (sans catégorie)
# Helpers disponibles
org.get_secteur_activite()
org.get_statut_juridique()
```
**Note** : Les clés des dictionnaires sont des **noms lisibles** ("ProfessionSante", "DiplomeEtatFrancais") extraits des référentiels MOS. Si l'index MOS n'est pas encore construit avec le nouveau format, les codes de table sont utilisés comme fallback ("TRE-G15", "TRE-R48").
Pour construire l'index avec les noms lisibles :
```bash
python examples/update_mos_cache.py
```
## Résolution MOS
Tous les codes MOS (TRE_*, JDV_*) sont automatiquement résolus en libellés lisibles :
```python
from annuairesante.mos import MOSResolver
resolver = MOSResolver()
display = resolver.resolve(
"https://mos.esante.gouv.fr/NOS/TRE_G15-ProfessionSante/FHIR/TRE-G15-ProfessionSante",
"21"
)
# → "Médecin"
```
## API et ressources disponibles
### Ressources FHIR supportées
| Ressource | Description | Exemples |
|-----------|-------------|----------|
| **Practitioner** | Professionnels de santé | `client.practitioner.search(family="MARTIN")` |
| **Organization** | Structures de santé | `client.organization.search(type="620")` |
| **PractitionerRole** | Situations d'exercice | `client.practitioner_role.search(practitioner="003-123456")` |
| **HealthcareService** | Services/activités de santé | `client.healthcare_service.search(organization="001-01-174986")` |
| **Device** | Équipements matériels lourds | `client.device.search(type="05602")` |
### Méthodes disponibles
Pour chaque ressource, trois méthodes sont disponibles :
```python
# 1. search() - Recherche avec résultat paginé (Bundle)
bundle = client.practitioner.search(family="MARTIN", _count=20)
print(f"Page courante: {len(bundle.entries)} résultats")
# Note: bundle.total est toujours 0 (l'API ne fournit pas ce champ)
for entry in bundle.entries:
process(entry)
# Pagination manuelle
while bundle.has_next():
bundle = bundle.next()
for entry in bundle.entries:
process(entry)
# 2. search_all() - Générateur avec pagination automatique
for practitioner in client.practitioner.search_all(family="MARTIN"):
database.save(practitioner)
# 3. get() - Récupérer par ID
practitioner = client.practitioner.get("003-123456")
```
### Paramètres de recherche
Consultez la [documentation complète des paramètres](docs/API_PARAMETERS.md) pour la liste exhaustive.
**Exemples courants :**
```python
# Practitioner
client.practitioner.search(
family="MARTIN", # Nom de famille
given="Jean", # Prénom
**{"qualification-code": "10"}, # Code profession (10=Médecin dans TRE-G15)
**{"mailbox-mss": "jean@mssante.fr"}, # Boîte MSS
active=True, # Actif uniquement
_lastUpdated="ge2025-01-01" # Modifié depuis le 1er janvier
)
# Organization
client.organization.search(
name="hopital", # Nom (recherche partielle)
identifier="750010753", # FINESS, SIRET, etc.
type="620", # Type (620 = Pharmacie)
**{"address-city": "Paris"}, # Ville
**{"address-postalcode": "75"}, # Code postal / département
active=True
)
# PractitionerRole
client.practitioner_role.search(
practitioner="003-123456", # ID du professionnel
organization="001-01-879996", # ID de l'organisation
role="204", # Code fonction/activité
active=True
)
```
## Exemples complets
### 1. Recherche et affichage
```python
from annuairesante import AnnuaireSanteClient, transform_practitioner
client = AnnuaireSanteClient()
# Rechercher des médecins à Lyon
bundle = client.practitioner.search(
**{"qualification-code": "10"}, # Médecin (code TRE-G15)
_count=10
)
print(f"Médecins (page courante): {len(bundle.entries)} résultats")
for resource in bundle.entries:
practitioner = transform_practitioner(resource)
print(f"\n{practitioner.name.full_text}")
print(f" RPPS: {practitioner.identifiers.rpps}")
if practitioner.contacts.mssante:
print(f" MSSanté: {practitioner.contacts.mssante[0].email}")
```
### 2. Synchronisation régionale complète
```python
from annuairesante import AnnuaireSanteClient, transform_organization
import json
client = AnnuaireSanteClient()
# Exporter toutes les pharmacies du département 69 en JSON Lines
with open("pharmacies_69.jsonl", "w") as f:
for org_fhir in client.organization.search_all(
type="620", # Pharmacie d'officine
**{"address-postalcode": "69"}, # Rhône
active=True,
_count=100 # 100 par page
):
org = transform_organization(org_fhir)
json.dump(org.model_dump(mode='json'), f, ensure_ascii=False)
f.write("\n")
```
### 3. Mise à jour incrémentale quotidienne
```python
from annuairesante import AnnuaireSanteClient
from datetime import datetime
client = AnnuaireSanteClient()
# Lire la dernière date de synchro
last_sync = load_last_sync_date() # Ex: "2025-10-08T00:00:00Z"
# Synchroniser uniquement les modifications
modified_count = 0
for org in client.organization.search_all(
_lastUpdated=f"ge{last_sync}",
**{"address-postalcode": "69"}
):
database.upsert(org)
modified_count += 1
# Sauvegarder la nouvelle date
save_last_sync_date(datetime.utcnow().isoformat() + "Z")
print(f"{modified_count} organisations mises à jour")
```
Voir aussi les exemples complets dans le dossier [`examples/`](examples/) :
- `basic_usage.py` - Utilisation basique
- `api_search.py` - Recherches avancées et pagination
- `sync_region.py` - Synchronisation de masse d'une région
- `incremental_sync.py` - Synchronisation incrémentale avec gestion d'état
## Développement
```bash
# Installer en mode développement
pip install -e ".[dev]"
# Tests
pytest
# Formatage
black src/
ruff check src/
# Type checking
mypy src/
```
## Standards et conformité
Cette bibliothèque implémente:
- **FR Core v2.1.0**: Profils FHIR français (HL7 France)
- FR Core Practitioner
- FR Core Organization
- FR Core PractitionerRole
- FR Core HealthcareService
- FR Core Address, ContactPoint, HumanName
- **AS DP v1.1.0**: Profils Annuaire Santé Données Publiques
- AS DP Practitioner (extensions: smartcard, mailbox-mss-metadata)
- AS DP Organization (extensions: organization-types, pharmacy-licence)
- AS DP PractitionerRole
- AS DP HealthcareService Healthcare Activity (extension: authorization)
- AS DP Device (extension: authorization)
## Licence
MIT
## Contributions
Les contributions sont les bienvenues ! Consultez [CONTRIBUTING.md](CONTRIBUTING.md) pour les guidelines.
Raw data
{
"_id": null,
"home_page": null,
"name": "annuaire-sante-fhir",
"maintainer": "Olivier Roch Vilato",
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "fhir, health, annuaire-sante, healthcare, france, mos, annuaire, sante, esante, pydantic, fhir-to-json, rpps, finess, medical-directory, french-healthcare",
"author": "Olivier Roch Vilato",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/a2/8f/27d0917646f3d5792f6f9b0f525723a7ae66acc7ab7e9c7557ef14ec3709/annuaire_sante_fhir-0.0.3.tar.gz",
"platform": null,
"description": "# Annuaire Sant\u00e9 FHIR - Client Python Moderne\n\n[](https://badge.fury.io/py/annuaire-sante-fhir)\n[](https://pypi.org/project/annuaire-sante-fhir/)\n[](https://opensource.org/licenses/MIT)\n\nClient Python moderne et type-safe pour l'API FHIR de l'Annuaire Sant\u00e9 avec mod\u00e8les JSON propres, sans artefacts FHIR, pr\u00eats pour injection en base de donn\u00e9es.\n\n## Caract\u00e9ristiques\n\n- **Mod\u00e8les Pydantic v2** propres et DB-ready sans artefacts FHIR\n- **R\u00e9solution automatique des codes MOS** (Mod\u00e8le des Objets de Sant\u00e9)\n- **Transformateurs FHIR \u2192 JSON** pour chaque ressource\n- **Type hints complets** pour une excellente exp\u00e9rience d\u00e9veloppeur\n- **Support complet des profils**:\n - FR Core v2.1.0 (Practitioner, Organization, PractitionerRole, HealthcareService)\n - AS DP v1.1.0 (Annuaire Sant\u00e9 Donn\u00e9es Publiques)\n\n## Installation\n\n```bash\npip install annuaire-sante-fhir\n```\n\n**Note**: Le nom du package sur PyPI est `annuaire-sante-fhir`, mais l'import dans votre code reste `annuairesante`:\n\n```python\nfrom annuairesante import AnnuaireSanteClient, transform_practitioner\n```\n\n## Configuration de la cl\u00e9 API\n\nPour utiliser l'API Annuaire Sant\u00e9, vous devez obtenir une cl\u00e9 API :\n\n1. **Obtenir une cl\u00e9** : Rendez-vous sur [https://ansforge.github.io/annuaire-sante-fhir-documentation/pages/guide/version-2/getting-started/get-api-key.html](https://ansforge.github.io/annuaire-sante-fhir-documentation/pages/guide/version-2/getting-started/get-api-key.html) et cr\u00e9ez un compte pour obtenir votre cl\u00e9 API\n2. **Configurer la cl\u00e9** : Trois m\u00e9thodes disponibles\n\n### M\u00e9thode 1 : Variable d'environnement (recommand\u00e9)\n\n```bash\nexport ANNUAIRE_SANTE_API_KEY=\"votre-cle-api\"\n```\n\n### M\u00e9thode 2 : Fichier .env\n\nCr\u00e9ez un fichier `.env` \u00e0 la racine de votre projet :\n\n```env\nANNUAIRE_SANTE_API_KEY=votre-cle-api\n```\n\n### M\u00e9thode 3 : Param\u00e8tre direct\n\n```python\nfrom annuairesante import AnnuaireSanteClient\n\nclient = AnnuaireSanteClient(api_key=\"votre-cle-api\")\n```\n\n\u26a0\ufe0f **S\u00e9curit\u00e9** : Ne commitez jamais votre cl\u00e9 API dans Git ! Ajoutez `.env` \u00e0 votre `.gitignore`.\n\n## Utilisation rapide\n\n### Recherche simple (quelques r\u00e9sultats)\n\n```python\nfrom annuairesante import AnnuaireSanteClient, transform_practitioner\n\n# Initialiser le client\nclient = AnnuaireSanteClient() # Utilise ANNUAIRE_SANTE_API_KEY\n\n# Rechercher des professionnels\nbundle = client.practitioner.search(family=\"MARTIN\", given=\"Jean\")\n\nprint(f\"Total trouv\u00e9: {bundle.total}\")\n\n# Parcourir les r\u00e9sultats\nfor resource in bundle.entries:\n practitioner = transform_practitioner(resource)\n\n print(f\"Nom: {practitioner.name.full_text}\")\n print(f\"RPPS: {practitioner.identifiers.rpps}\")\n\n # Exporter en JSON pour base de donn\u00e9es\n db_ready_json = practitioner.model_dump(mode='json')\n```\n\n### Synchronisation de masse (pagination automatique)\n\n```python\nfrom annuairesante import AnnuaireSanteClient, transform_organization\n\nclient = AnnuaireSanteClient()\n\n# Synchroniser toutes les organisations d'une r\u00e9gion\ncount = 0\nfor org_fhir in client.organization.search_all(\n **{\"address-postalcode\": \"69\"}, # D\u00e9partement du Rh\u00f4ne\n active=True\n):\n org = transform_organization(org_fhir)\n database.save(org.model_dump(mode='json'))\n\n count += 1\n if count % 100 == 0:\n print(f\"{count} organisations synchronis\u00e9es...\")\n\nprint(f\"Total: {count} organisations\")\n```\n\n### Synchronisation incr\u00e9mentale\n\n```python\nfrom annuairesante import AnnuaireSanteClient\nfrom datetime import datetime\n\nclient = AnnuaireSanteClient()\n\n# Synchroniser uniquement les modifications depuis la derni\u00e8re synchro\nlast_sync = \"2025-01-01T00:00:00Z\"\n\nfor practitioner in client.practitioner.search_all(\n _lastUpdated=f\"ge{last_sync}\", # ge = greater or equal\n **{\"address-city\": \"Lyon\"}\n):\n database.upsert(practitioner) # Mise \u00e0 jour ou insertion\n\n# Sauvegarder la date de synchro\ndatabase.set_last_sync(datetime.utcnow().isoformat() + \"Z\")\n```\n\n## Mod\u00e8les disponibles\n\n### Practitioner (Professionnel de sant\u00e9)\n\n```python\n{\n \"identifiers\": {\n \"idnps\": \"810101205564\", # Obligatoire\n \"rpps\": \"10101205564\", # Obligatoire\n \"adeli\": None\n },\n \"name\": {\n \"family\": \"VERDIER\",\n \"given\": [\"Pauline\"],\n \"prefix\": \"MME\", # Civilit\u00e9 (JDV_J78)\n \"suffix\": [\"DR\"], # Titre exercice (JDV_J79)\n \"full_text\": \"VERDIER Pauline\"\n },\n \"gender\": \"female\",\n \"birth_date\": \"1985-03-15\",\n \"contacts\": {\n \"phones\": [\"+33612345678\"],\n \"emails\": [\"contact@example.com\"],\n \"mssante\": [{\n \"email\": \"pauline.verdier@aura.mssante.fr\",\n \"type\": \"PER\", # ORG, APP, PER, CAB\n \"digitization\": false,\n \"liste_rouge\": false\n }]\n },\n \"addresses\": [{\n \"lines\": [\"2 RUE CLAUDE BERNARD\"],\n \"city\": \"PARIS\",\n \"postal_code\": \"75005\",\n \"insee_code\": \"75105\"\n }],\n \"qualifications\": { # Index\u00e9 par type puis nom de syst\u00e8me\n \"profession\": {\n \"ProfessionSante\": {\n \"code\": \"21\",\n \"display\": \"M\u00e9decin\" # R\u00e9solu via MOS\n },\n \"CategorieProfessionnelle\": {\n \"code\": \"C\",\n \"display\": \"Civil\"\n }\n },\n \"diplome\": {\n \"DiplomeEtatFrancais\": {\n \"code\": \"DE28\",\n \"display\": \"DE Docteur en m\u00e9decine\"\n }\n }\n },\n \"smartcards\": [{\n \"type\": \"CPS\",\n \"number\": \"3100434368\",\n \"period\": {\"start\": \"2024-02-21\", \"end\": \"2027-02-21\"},\n \"is_valid\": true\n }],\n \"metadata\": {\n \"id\": \"003-3014698-3057235\",\n \"version_id\": \"1\",\n \"last_updated\": \"2025-04-28T18:19:26.335+02:00\",\n \"profiles\": [\"https://hl7.fr/ig/fhir/core/StructureDefinition/fr-core-practitioner\"],\n \"data_trace\": {\n \"systeme_information\": \"RPPS\"\n }\n },\n \"active\": true\n}\n```\n\n### Organization (Structure de sant\u00e9)\n\n```python\n{\n \"identifiers\": {\n \"finess\": \"750010753\", # FINEJ ou FINEG\n \"idnst\": \"1750010753\",\n \"siret\": \"12345678901234\"\n },\n \"name\": \"PHARMACIE BLONDEEL\",\n \"aliases\": [\"LA GRANDE PHARMACIE DU 5\"],\n \"types_by_category\": { # Index\u00e9 par cat\u00e9gorie\n \"categorieEtablissement\": {\n \"code\": \"620\",\n \"display\": \"Pharmacie d'officine\", # R\u00e9solu via MOS\n \"category\": \"categorieEtablissement\"\n },\n \"secteurActiviteRASS\": {\n \"code\": \"SA33\",\n \"display\": \"Secteur priv\u00e9 commercial\",\n \"category\": \"secteurActiviteRASS\"\n },\n \"statutJuridiqueINSEE\": {\n \"code\": \"101\",\n \"display\": \"SELAS\",\n \"category\": \"statutJuridiqueINSEE\"\n }\n },\n \"primary_type\": { # Type sans cat\u00e9gorie sp\u00e9cifique\n \"code\": \"620\",\n \"display\": \"Pharmacie d'Officine\",\n \"category\": null\n },\n \"pharmacy_licence\": \"75#000283\",\n \"addresses\": [...]\n}\n```\n\n### Autres ressources\n\n- **PractitionerRole**: Situation d'exercice (genre, mode, fonction)\n- **HealthcareService**: Service/activit\u00e9 de sant\u00e9 (modalit\u00e9, type, forme)\n- **Device**: \u00c9quipement mat\u00e9riel lourd\n\n## Acc\u00e8s simplifi\u00e9 aux donn\u00e9es\n\nLes structures JSON sont optimis\u00e9es pour un acc\u00e8s direct sans boucles :\n\n```python\n# Practitioner - Acc\u00e8s aux qualifications\npract.qualifications[\"profession\"][\"ProfessionSante\"]\n# \u2192 {\"code\": \"21\", \"display\": \"Pharmacien\"}\n\n# Helpers disponibles\npract.get_profession() # Code profession principal\npract.get_diploma() # Dipl\u00f4me principal\npract.get_category() # Cat\u00e9gorie professionnelle\n\n# Organization - Acc\u00e8s aux types\norg.types_by_category[\"secteurActiviteRASS\"]\n# \u2192 {\"code\": \"SA33\", \"display\": \"Pharmacie d'officine\"}\n\norg.primary_type # Type principal (sans cat\u00e9gorie)\n\n# Helpers disponibles\norg.get_secteur_activite()\norg.get_statut_juridique()\n```\n\n**Note** : Les cl\u00e9s des dictionnaires sont des **noms lisibles** (\"ProfessionSante\", \"DiplomeEtatFrancais\") extraits des r\u00e9f\u00e9rentiels MOS. Si l'index MOS n'est pas encore construit avec le nouveau format, les codes de table sont utilis\u00e9s comme fallback (\"TRE-G15\", \"TRE-R48\").\n\nPour construire l'index avec les noms lisibles :\n```bash\npython examples/update_mos_cache.py\n```\n\n## R\u00e9solution MOS\n\nTous les codes MOS (TRE_*, JDV_*) sont automatiquement r\u00e9solus en libell\u00e9s lisibles :\n\n```python\nfrom annuairesante.mos import MOSResolver\n\nresolver = MOSResolver()\ndisplay = resolver.resolve(\n \"https://mos.esante.gouv.fr/NOS/TRE_G15-ProfessionSante/FHIR/TRE-G15-ProfessionSante\",\n \"21\"\n)\n# \u2192 \"M\u00e9decin\"\n```\n\n## API et ressources disponibles\n\n### Ressources FHIR support\u00e9es\n\n| Ressource | Description | Exemples |\n|-----------|-------------|----------|\n| **Practitioner** | Professionnels de sant\u00e9 | `client.practitioner.search(family=\"MARTIN\")` |\n| **Organization** | Structures de sant\u00e9 | `client.organization.search(type=\"620\")` |\n| **PractitionerRole** | Situations d'exercice | `client.practitioner_role.search(practitioner=\"003-123456\")` |\n| **HealthcareService** | Services/activit\u00e9s de sant\u00e9 | `client.healthcare_service.search(organization=\"001-01-174986\")` |\n| **Device** | \u00c9quipements mat\u00e9riels lourds | `client.device.search(type=\"05602\")` |\n\n### M\u00e9thodes disponibles\n\nPour chaque ressource, trois m\u00e9thodes sont disponibles :\n\n```python\n# 1. search() - Recherche avec r\u00e9sultat pagin\u00e9 (Bundle)\nbundle = client.practitioner.search(family=\"MARTIN\", _count=20)\nprint(f\"Page courante: {len(bundle.entries)} r\u00e9sultats\")\n# Note: bundle.total est toujours 0 (l'API ne fournit pas ce champ)\nfor entry in bundle.entries:\n process(entry)\n\n# Pagination manuelle\nwhile bundle.has_next():\n bundle = bundle.next()\n for entry in bundle.entries:\n process(entry)\n\n# 2. search_all() - G\u00e9n\u00e9rateur avec pagination automatique\nfor practitioner in client.practitioner.search_all(family=\"MARTIN\"):\n database.save(practitioner)\n\n# 3. get() - R\u00e9cup\u00e9rer par ID\npractitioner = client.practitioner.get(\"003-123456\")\n```\n\n### Param\u00e8tres de recherche\n\nConsultez la [documentation compl\u00e8te des param\u00e8tres](docs/API_PARAMETERS.md) pour la liste exhaustive.\n\n**Exemples courants :**\n\n```python\n# Practitioner\nclient.practitioner.search(\n family=\"MARTIN\", # Nom de famille\n given=\"Jean\", # Pr\u00e9nom\n **{\"qualification-code\": \"10\"}, # Code profession (10=M\u00e9decin dans TRE-G15)\n **{\"mailbox-mss\": \"jean@mssante.fr\"}, # Bo\u00eete MSS\n active=True, # Actif uniquement\n _lastUpdated=\"ge2025-01-01\" # Modifi\u00e9 depuis le 1er janvier\n)\n\n# Organization\nclient.organization.search(\n name=\"hopital\", # Nom (recherche partielle)\n identifier=\"750010753\", # FINESS, SIRET, etc.\n type=\"620\", # Type (620 = Pharmacie)\n **{\"address-city\": \"Paris\"}, # Ville\n **{\"address-postalcode\": \"75\"}, # Code postal / d\u00e9partement\n active=True\n)\n\n# PractitionerRole\nclient.practitioner_role.search(\n practitioner=\"003-123456\", # ID du professionnel\n organization=\"001-01-879996\", # ID de l'organisation\n role=\"204\", # Code fonction/activit\u00e9\n active=True\n)\n```\n\n## Exemples complets\n\n### 1. Recherche et affichage\n\n```python\nfrom annuairesante import AnnuaireSanteClient, transform_practitioner\n\nclient = AnnuaireSanteClient()\n\n# Rechercher des m\u00e9decins \u00e0 Lyon\nbundle = client.practitioner.search(\n **{\"qualification-code\": \"10\"}, # M\u00e9decin (code TRE-G15)\n _count=10\n)\n\nprint(f\"M\u00e9decins (page courante): {len(bundle.entries)} r\u00e9sultats\")\n\nfor resource in bundle.entries:\n practitioner = transform_practitioner(resource)\n\n print(f\"\\n{practitioner.name.full_text}\")\n print(f\" RPPS: {practitioner.identifiers.rpps}\")\n\n if practitioner.contacts.mssante:\n print(f\" MSSant\u00e9: {practitioner.contacts.mssante[0].email}\")\n```\n\n### 2. Synchronisation r\u00e9gionale compl\u00e8te\n\n```python\nfrom annuairesante import AnnuaireSanteClient, transform_organization\nimport json\n\nclient = AnnuaireSanteClient()\n\n# Exporter toutes les pharmacies du d\u00e9partement 69 en JSON Lines\nwith open(\"pharmacies_69.jsonl\", \"w\") as f:\n for org_fhir in client.organization.search_all(\n type=\"620\", # Pharmacie d'officine\n **{\"address-postalcode\": \"69\"}, # Rh\u00f4ne\n active=True,\n _count=100 # 100 par page\n ):\n org = transform_organization(org_fhir)\n json.dump(org.model_dump(mode='json'), f, ensure_ascii=False)\n f.write(\"\\n\")\n```\n\n### 3. Mise \u00e0 jour incr\u00e9mentale quotidienne\n\n```python\nfrom annuairesante import AnnuaireSanteClient\nfrom datetime import datetime\n\nclient = AnnuaireSanteClient()\n\n# Lire la derni\u00e8re date de synchro\nlast_sync = load_last_sync_date() # Ex: \"2025-10-08T00:00:00Z\"\n\n# Synchroniser uniquement les modifications\nmodified_count = 0\nfor org in client.organization.search_all(\n _lastUpdated=f\"ge{last_sync}\",\n **{\"address-postalcode\": \"69\"}\n):\n database.upsert(org)\n modified_count += 1\n\n# Sauvegarder la nouvelle date\nsave_last_sync_date(datetime.utcnow().isoformat() + \"Z\")\nprint(f\"{modified_count} organisations mises \u00e0 jour\")\n```\n\nVoir aussi les exemples complets dans le dossier [`examples/`](examples/) :\n- `basic_usage.py` - Utilisation basique\n- `api_search.py` - Recherches avanc\u00e9es et pagination\n- `sync_region.py` - Synchronisation de masse d'une r\u00e9gion\n- `incremental_sync.py` - Synchronisation incr\u00e9mentale avec gestion d'\u00e9tat\n\n## D\u00e9veloppement\n\n```bash\n# Installer en mode d\u00e9veloppement\npip install -e \".[dev]\"\n\n# Tests\npytest\n\n# Formatage\nblack src/\nruff check src/\n\n# Type checking\nmypy src/\n```\n\n## Standards et conformit\u00e9\n\nCette biblioth\u00e8que impl\u00e9mente:\n\n- **FR Core v2.1.0**: Profils FHIR fran\u00e7ais (HL7 France)\n - FR Core Practitioner\n - FR Core Organization\n - FR Core PractitionerRole\n - FR Core HealthcareService\n - FR Core Address, ContactPoint, HumanName\n\n- **AS DP v1.1.0**: Profils Annuaire Sant\u00e9 Donn\u00e9es Publiques\n - AS DP Practitioner (extensions: smartcard, mailbox-mss-metadata)\n - AS DP Organization (extensions: organization-types, pharmacy-licence)\n - AS DP PractitionerRole\n - AS DP HealthcareService Healthcare Activity (extension: authorization)\n - AS DP Device (extension: authorization)\n\n## Licence\n\nMIT\n\n## Contributions\n\nLes contributions sont les bienvenues ! Consultez [CONTRIBUTING.md](CONTRIBUTING.md) pour les guidelines.\n",
"bugtrack_url": null,
"license": null,
"summary": "Client Python type-safe pour l'API Annuaire Sant\u00e9 : FHIR vers JSON DB-ready avec r\u00e9solution MOS automatique",
"version": "0.0.3",
"project_urls": {
"Changelog": "https://github.com/orochvilato/annuaire-sante-fhir/releases",
"Documentation": "https://github.com/orochvilato/annuaire-sante-fhir#readme",
"Homepage": "https://github.com/orochvilato/annuaire-sante-fhir",
"Issues": "https://github.com/orochvilato/annuaire-sante-fhir/issues",
"Repository": "https://github.com/orochvilato/annuaire-sante-fhir",
"Source Code": "https://github.com/orochvilato/annuaire-sante-fhir"
},
"split_keywords": [
"fhir",
" health",
" annuaire-sante",
" healthcare",
" france",
" mos",
" annuaire",
" sante",
" esante",
" pydantic",
" fhir-to-json",
" rpps",
" finess",
" medical-directory",
" french-healthcare"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "39a014b24682f3160898d796818368bedd312106a958792e34d3064a01ea66ee",
"md5": "b2b4efbdaa83526c4a7b1dc6d4393d2d",
"sha256": "79025c55d0b0cb10601c9e193cbe079190203f2355a148489be25518190faf41"
},
"downloads": -1,
"filename": "annuaire_sante_fhir-0.0.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "b2b4efbdaa83526c4a7b1dc6d4393d2d",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 50159,
"upload_time": "2025-10-21T17:51:04",
"upload_time_iso_8601": "2025-10-21T17:51:04.786353Z",
"url": "https://files.pythonhosted.org/packages/39/a0/14b24682f3160898d796818368bedd312106a958792e34d3064a01ea66ee/annuaire_sante_fhir-0.0.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "a28f27d0917646f3d5792f6f9b0f525723a7ae66acc7ab7e9c7557ef14ec3709",
"md5": "a4ed094c6d74739796d958e5806211a8",
"sha256": "cc6f7ece865a87dc6e45f48e955816ea2f007b3d341950a7d01a6aaed20ffa10"
},
"downloads": -1,
"filename": "annuaire_sante_fhir-0.0.3.tar.gz",
"has_sig": false,
"md5_digest": "a4ed094c6d74739796d958e5806211a8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 38018,
"upload_time": "2025-10-21T17:51:06",
"upload_time_iso_8601": "2025-10-21T17:51:06.390253Z",
"url": "https://files.pythonhosted.org/packages/a2/8f/27d0917646f3d5792f6f9b0f525723a7ae66acc7ab7e9c7557ef14ec3709/annuaire_sante_fhir-0.0.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-21 17:51:06",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "orochvilato",
"github_project": "annuaire-sante-fhir",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "annuaire-sante-fhir"
}