Utiliser l’API Mon Suivi Addiction : Guide complet pour les praticiens et développeurs

Publié le 19/06/2025

Dans le contexte actuel de la santé publique, le suivi des patients souffrant d’addictions est devenu une priorité. L’API Mon Suivi Addiction a été conçue pour simplifier et sécuriser la gestion des données des praticiens et de leurs patients. Cet article vous présente comment utiliser cette API, ses avantages, et vous propose un exemple concret d’intégration.


Qu’est-ce que l’API Mon Suivi Addiction ?

L’API Mon Suivi Addiction est une interface de programmation qui permet aux applications et aux plateformes médicales de communiquer facilement avec la base de données officielle des praticiens enregistrés via l’API RPPS de Santé Publique France. Elle facilite la gestion des accès et la mise à jour sécurisée des données de suivi des patients concernés par les addictions.


Pourquoi utiliser l’API Mon Suivi Addiction ?

  • Sécurité et conformité : Accès protégé avec authentification renforcée, respect des normes RGPD.
  • Base à jour : Utilisation des données officielles RPPS, garantissant une information fiable sur les praticiens.
  • Interopérabilité : Connectez vos outils de gestion de patients directement avec MonSuiviAddiction.fr pour un suivi fluide.
  • Gain de temps : Automatisez la gestion des comptes praticiens et la synchronisation des données.

Fonctionnalités principales

  • Authentification des praticiens via leurs identifiants RPPS.
  • Récupération des informations professionnelles.
  • Mise à jour sécurisée des suivis et des notes cliniques.
  • Gestion des accès et réinitialisation des mots de passe.


Langage : Symfony 6.4
<?php
namespace App\Controller\Api;

use App\Entity\User;
use App\Entity\Practitioner;
use Psr\Log\LoggerInterface;
use Doctrine\ORM\AbstractQuery;
use Symfony\Component\Uid\Uuid;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class AddictionController extends AbstractController
{
    private const MEMORY_LIMIT = '512M';
    private const DATE_FORMAT = 'Y-m-d H:i:s';
    private const JSON_OPTIONS = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT;

    public function __construct(
        private EntityManagerInterface $em,
        private SerializerInterface $serializer,
        private LoggerInterface $logger
    ) {}

    #[Route('/api/addiction/suivi', name: 'api_addiction_suivi_praticien', methods: ['GET'])]
    public function getSuivi(Request $request): JsonResponse
    {
        ini_set('memory_limit', self::MEMORY_LIMIT);

        $practitionerId = $request->query->get('practitioner_id');

        $this->logger->debug('Recherche du praticien', [
            'practitioner_id' => $practitionerId,
            // 'normalized_id' and 'uuid' are not yet defined here
        ]);

        if (empty($practitionerId)) {
            $this->logger->error('Identifiant praticien manquant', ['practitioner_id' => $practitionerId]);
            return $this->json([
                'error' => 'Identifiant praticien manquant'
            ], JsonResponse::HTTP_BAD_REQUEST);
        }

        // Accept both UUID with and without hyphens (32 or 36 chars)
        $normalizedId = $practitionerId;
        if (preg_match('/^[a-f0-9]{32}$/i', $practitionerId)) {
            // Insert hyphens to match UUID format
            $normalizedId = substr($practitionerId, 0, 8) . '-' .
                            substr($practitionerId, 8, 4) . '-' .
                            substr($practitionerId, 12, 4) . '-' .
                            substr($practitionerId, 16, 4) . '-' .
                            substr($practitionerId, 20, 12);
        }

        try {
            $uuid = Uuid::fromString($normalizedId);
        } catch (\Throwable $e) {
            $this->logger->error('Identifiant praticien invalide', ['practitioner_id' => $practitionerId]);
            return $this->json([
                'error' => 'Identifiant praticien invalide'
            ], JsonResponse::HTTP_BAD_REQUEST);
        }

        $practitioner = $this->em->getRepository(Practitioner::class)->find($uuid);
        if (!$practitioner) {
            return $this->json(['error' => 'Praticien non trouvé'], JsonResponse::HTTP_NOT_FOUND);
        }

        $data = $this->buildResponseData($practitioner);
        return $this->createJsonResponse($data);
    }

    private function buildResponseData(Practitioner $practitioner): array
    {
        return [
            'practitioner' => $this->getPractitionerData($practitioner),
            'metadata' => [
                'qualifications' => $this->getQualificationCodes(),
                'specialties' => $this->getPopularSpecialties(),
                'last_updated' => (new \DateTime())->format(self::DATE_FORMAT)
            ]
        ];
    }

    private function getPractitionerData(Practitioner $practitioner): array
    {
        $query = $this->em->createQueryBuilder()
            ->select('p', 'org', 'u', 'uOrg', 'ds', 'ua')
            ->from(Practitioner::class, 'p')
            ->leftJoin('p.organization', 'org')
            ->leftJoin('p.users', 'u') // Utilisez la relation inverse définie dans Practitioner
            ->leftJoin('u.organization', 'uOrg')
            ->leftJoin('u.dailyStates', 'ds')
            ->leftJoin('ds.userAddiction', 'ua')
            ->where('p.id = :id')
            ->setParameter('id', $practitioner->getId())
            ->getQuery();

        $result = $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY);
        
        if (!$result) {
            $this->logger->warning('Aucun praticien trouvé avec cet ID', ['id' => $practitioner->getId()]);
            return [];
        }

        // Formatage manuel des données
        return [
            'id' => $result['id'],
            'identifier' => $result['identifier'],
            'firstName' => $result['firstName'],
            'lastName' => $result['lastName'],
            'fullName' => $result['fullName'],
            'telecom' => $result['telecom'],
            'qualification' => $result['qualification'],
            'organization' => $result['organization'] ?? null,
            'users' => array_map(function($user) {
                return [
                    'id' => $user['id'],
                    'email' => $user['email'],
                    'dailyStates' => $user['dailyStates']
                ];
            }, $result['users'] ?? [])
        ];
    }

    private function createJsonResponse(array $data): JsonResponse
    {
        $context = [
            'groups' => ['practitioner:read', 'user:read', 'daily_state:read'],
            'datetime_format' => self::DATE_FORMAT,
            'json_encode_options' => self::JSON_OPTIONS
        ];

        return JsonResponse::fromJsonString(
            $this->serializer->serialize($data, 'json', $context),
            JsonResponse::HTTP_OK
        );
    }

     private function getQualificationCodes(): array
    {
        return [
            '10'    => 'Médecin',
            'C'     => 'Médecin généraliste',
            'C10'   => 'Chirurgie maxillo-faciale',
            'C11'   => 'Chirurgie thoracique',
            'C12'   => 'Chirurgie orthopédique',
            'C13'   => 'Urologie',
            'C15'   => 'Dermatologie - Vénérologie',
            'C20'   => 'Hémobiologie',
            'C23'   => 'Gynécologie médicale et Obstétrique',
            'C25'   => 'Gynécologie médicale',
            'C27'   => 'Obstétrique',
            'C76'   => 'Orthopédie dento-maxillo-faciale',
            'C83'   => 'Chirurgie face et cou',
            'CAPA01'=> 'Addictologie clinique',
            'CAPA02'=> 'Aide médicale urgente',
            'CAPA03'=> 'Allergologie',
            'CAPA04'=> 'Angéiologie',
            'CAPA05'=> 'Évaluation et traitement de la douleur',
            'CAPA06'=> 'Gérontologie',
            'CAPA07'=> 'Hydrologie et climatologie médicales',
            '40'    => 'Chirurgien-dentiste',
            'DE'    => 'Chirurgien-dentiste',
            'DE09'  => 'Orthopédie dento-faciale',
            'DE28'  => 'Chirurgie orale',
            'SCD01' => 'Orthopédie dento-faciale',
            'SCD02' => 'Chirurgie orale',
            'SCD03' => 'Médecine bucco-dentaire',
            '60'    => 'Infirmier',
            '69'    => 'Infirmier psychiatrique',
            '50'    => 'Sage-femme',
            '70'    => 'Masseur-Kinésithérapeute',
            '80'    => 'Pédicure-Podologue',
            '81'    => 'Orthoprothésiste',
            '82'    => 'Podo-Orthésiste',
            '83'    => 'Orthopédiste-Orthésiste',
            '84'    => 'Oculariste',
            '85'    => 'Épithésiste',
            '86'    => 'Technicien de laboratoire médical',
            '91'    => 'Orthophoniste',
            '92'    => 'Orthoptiste',
            '94'    => 'Ergothérapeute',
            '95'    => 'Diététicien',
            '96'    => 'Psychomotricien',
            '98'    => 'Manipulateur en électroradiologie médicale',
            '21'    => 'Pharmacien',
            'GENR01' => 'Génétique médicale',
            'S'      => 'Spécialiste',
            'FON-05' => 'Médecine fonctionnelle',
            'DA'     => 'Addictologie',
            'MCO'    => 'Médecine et Chirurgie Ostéopathique',
            'RAD'    => 'Radiologie',
            'L'       => 'Non spécifié ou inconnu',
            'FON-01'  => 'Titulaire de cabinet',
            'FON-33'  => 'Salarié en poste fixe',
            'GEN12'   => 'Activité de professionnel du social et médico-social exerçant des fonctions d\'encadrement et d\'organisation de l\'accompagnement',
            '312'     => 'Professeur de lycée option imprimerie',
            '317'     => 'Professeur de lycée option musique',
            '320'     => 'Professeur de lycée option sciences physiques',
            'GENR10'  => 'Activité de professionnel du social et médico-social exerçant des fonctions d\'encadrement et d\'organisation de l\'accompagnement',
            'GENR04'  => 'Activité de professionnel du social et médico-social exerçant des fonctions d\'encadrement et d\'organisation de l\'accompagnement',
        ];
    }

    private function getPopularSpecialties(): array
    {
    $popularSpecialties = [
        'Addictologie',
        'Tabacologie',
        'Alcoolisme',
        'Toxicomanie',
        'Dépendance numérique',
        'Jeu pathologique',
        'Psychothérapie',
        'Thérapie cognitive',
        'Médecin généraliste',
        'Psychologue',
        'Thérapeute'
    ];
    return $popularSpecialties;
    }
}
Retour à la liste