First implementation API for PIP (Proyecto: Pharmacy Integration Platform)
Run Tests on Branches / Backend Tests (push) Successful in 3m32s
Run Tests on Branches / Frontend Tests (push) Successful in 3m27s

This commit is contained in:
Antoni Nuñez Romeu
2026-07-01 16:46:52 +02:00
parent d935db4b3e
commit c5df0ec0b2
121 changed files with 9575 additions and 0 deletions
@@ -0,0 +1,189 @@
from datetime import datetime
from typing import Any
from uuid import UUID
from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession
from src.api.v1.dependencies.auth import get_current_user, require_role
from src.core.exceptions import ConflictException, NotFoundException
from src.domain.entities import MedicationEntity, UserRole
from src.infrastructure.database.repositories import MedicationCatalogRepository, MedicationRepository
from src.infrastructure.database.session import get_db_session
from src.services.auth.security import TokenData
from src.services.medication.medication_service import MedicationService
router = APIRouter(prefix="/medications", tags=["Medications"])
class MedicationCreateRequest(BaseModel):
nregistro: str = Field(..., min_length=1, max_length=50)
name: str = Field(..., min_length=1, max_length=500)
active_ingredient: str | None = Field(None, max_length=500)
dosage: str | None = Field(None, max_length=100)
form: str | None = Field(None, max_length=100)
atc_code: str | None = Field(None, max_length=20)
prescription_required: bool = False
manufacturer: str | None = Field(None, max_length=255)
metadata: dict[str, Any] | None = None
class MedicationUpdateRequest(BaseModel):
name: str | None = Field(None, min_length=1, max_length=500)
active_ingredient: str | None = Field(None, max_length=500)
dosage: str | None = Field(None, max_length=100)
form: str | None = Field(None, max_length=100)
atc_code: str | None = Field(None, max_length=20)
prescription_required: bool | None = None
manufacturer: str | None = Field(None, max_length=255)
metadata: dict[str, Any] | None = None
class MedicationResponse(BaseModel):
id: UUID
nregistro: str
name: str
active_ingredient: str | None
dosage: str | None
form: str | None
atc_code: str | None
prescription_required: bool
manufacturer: str | None
metadata: dict[str, Any] | None
created_at: datetime | None
updated_at: datetime | None
class MedicationListResponse(BaseModel):
items: list[MedicationResponse]
total: int
offset: int
limit: int
def _entity_to_response(e: MedicationEntity) -> MedicationResponse:
return MedicationResponse(
id=e.id, nregistro=e.nregistro, name=e.name,
active_ingredient=e.active_ingredient, dosage=e.dosage,
form=e.form, atc_code=e.atc_code,
prescription_required=e.prescription_required,
manufacturer=e.manufacturer, metadata=e.metadata_,
created_at=e.created_at, updated_at=e.updated_at,
)
def _get_service(session: AsyncSession) -> MedicationService:
return MedicationService(
medication_repo=MedicationRepository(session),
catalog_repo=MedicationCatalogRepository(session),
)
@router.get("", response_model=MedicationListResponse)
async def list_medications(
q: str | None = None,
offset: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=200),
session: AsyncSession = Depends(get_db_session),
current_user: TokenData = Depends(get_current_user),
):
service = _get_service(session)
if q:
entities, total = await service.search_medications(q, offset=offset, limit=limit)
else:
from src.domain.entities import MedicationEntity
all_entities, total = await service.search_medications("", offset=offset, limit=limit)
entities = all_entities
return MedicationListResponse(items=[_entity_to_response(e) for e in entities], total=total, offset=offset, limit=limit)
@router.get("/search", response_model=MedicationListResponse)
async def search_medications(
q: str = Query(..., min_length=1),
offset: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=200),
session: AsyncSession = Depends(get_db_session),
current_user: TokenData = Depends(get_current_user),
):
service = _get_service(session)
entities, total = await service.search_medications(q, offset=offset, limit=limit)
return MedicationListResponse(items=[_entity_to_response(e) for e in entities], total=total, offset=offset, limit=limit)
@router.get("/nregistro/{nregistro}", response_model=MedicationResponse)
async def get_medication_by_nregistro(
nregistro: str,
session: AsyncSession = Depends(get_db_session),
current_user: TokenData = Depends(get_current_user),
):
service = _get_service(session)
entity = await service.get_by_nregistro(nregistro)
if not entity:
raise NotFoundException("Medication", nregistro)
return _entity_to_response(entity)
@router.get("/{medication_id}", response_model=MedicationResponse)
async def get_medication(
medication_id: UUID,
session: AsyncSession = Depends(get_db_session),
current_user: TokenData = Depends(get_current_user),
):
service = _get_service(session)
entity = await service.get_medication(medication_id)
if not entity:
raise NotFoundException("Medication", str(medication_id))
return _entity_to_response(entity)
@router.post("", response_model=MedicationResponse, status_code=201)
async def create_medication(
body: MedicationCreateRequest,
session: AsyncSession = Depends(get_db_session),
current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
service = _get_service(session)
entity = MedicationEntity(
nregistro=body.nregistro, name=body.name,
active_ingredient=body.active_ingredient, dosage=body.dosage,
form=body.form, atc_code=body.atc_code,
prescription_required=body.prescription_required,
manufacturer=body.manufacturer, metadata_=body.metadata,
)
created = await service.create_medication(entity)
return _entity_to_response(created)
@router.put("/{medication_id}", response_model=MedicationResponse)
async def update_medication(
medication_id: UUID,
body: MedicationUpdateRequest,
session: AsyncSession = Depends(get_db_session),
current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
service = _get_service(session)
entity = await service.get_medication(medication_id)
if not entity:
raise NotFoundException("Medication", str(medication_id))
update_data = body.model_dump(exclude_unset=True)
if "metadata" in update_data:
entity.metadata_ = update_data.pop("metadata")
for key, value in update_data.items():
setattr(entity, key, value)
updated = await service.update_medication(entity)
return _entity_to_response(updated)
@router.delete("/{medication_id}", status_code=204)
async def delete_medication(
medication_id: UUID,
session: AsyncSession = Depends(get_db_session),
current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
service = _get_service(session)
deleted = await service.delete_medication(medication_id)
if not deleted:
raise NotFoundException("Medication", str(medication_id))