diff --git a/README.md b/README.md index 3b31253..df583c1 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,12 @@ If you want to filter by more than one value, simply concatenate all the desired ```python from aempsconn.aemps import AempsConn from aempsconn.filter import ( + MaterialFilter, MedicamentoFilter, MedicamentosFilter, + NotaFilter, PresentacionesFilter, + RegistroCambiosFilter, VmppFilter, ) @@ -44,6 +47,11 @@ for med in aemps.medicamentos.get( ): print(med.nombre) +for med in aemps.medicamentos.get( + filter=MedicamentosFilter().nombre.startswith(value="") +): + print(med.nombre) + for med in aemps.presentaciones.get( filter=PresentacionesFilter().vmp.equals("270671000140106") ): @@ -52,6 +60,19 @@ for med in aemps.presentaciones.get( for desc_cli in aemps.vmpp.get(filter=VmppFilter().nombre.contains("metotrexato")): print(desc_cli.vmpDesc) +for change in aemps.registro_cambios.get( + filter=RegistroCambiosFilter().fecha.equals(value="20/12/2023") +): + print(change) + +for note in aemps.notas.get(filter=NotaFilter().nregistro.equals("69223")): + print(note) + +for material in aemps.materiales.get( + filter=MaterialFilter().nregistro.equals("78632") +): + print(material) + ``` ## Contributing diff --git a/pyproject.toml b/pyproject.toml index c78056e..2a15007 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "aempsconn" -version = "1.0.1" +version = "1.0.2" description = "Library designed for interacting with the CIMA API (AEMPS)" license = "MIT" authors = ["fqlenos "] diff --git a/src/aempsconn/aemps.py b/src/aempsconn/aemps.py index 0121709..715e8c0 100644 --- a/src/aempsconn/aemps.py +++ b/src/aempsconn/aemps.py @@ -4,7 +4,15 @@ from pydantic import HttpUrl -from .modules import Medicamento, Medicamentos, Presentaciones, Vmpp +from .modules import ( + Material, + Medicamento, + Medicamentos, + Notas, + Presentaciones, + RegistroCambios, + Vmpp, +) from .utils.request_handler import ReqHandler @@ -46,3 +54,15 @@ def presentaciones(self) -> Presentaciones: @property def vmpp(self) -> Vmpp: return Vmpp(req_handler=self.__req_handler) + + @property + def registro_cambios(self) -> RegistroCambios: + return RegistroCambios(req_handler=self.__req_handler) + + @property + def notas(self) -> Notas: + return Notas(req_handler=self.__req_handler) + + @property + def materiales(self) -> Material: + return Material(req_handler=self.__req_handler) diff --git a/src/aempsconn/filter/__init__.py b/src/aempsconn/filter/__init__.py index ffd0c02..5e008bf 100644 --- a/src/aempsconn/filter/__init__.py +++ b/src/aempsconn/filter/__init__.py @@ -1,7 +1,10 @@ """Package: Filters for different modules.""" from .maestras import MaestrasFilter +from .material import MaterialFilter from .medicamento import MedicamentoFilter from .medicamentos import MedicamentosFilter +from .nota import NotaFilter from .presentaciones import PresentacionesFilter +from .registro_cambios import RegistroCambiosFilter from .vmpp import VmppFilter diff --git a/src/aempsconn/filter/material.py b/src/aempsconn/filter/material.py new file mode 100644 index 0000000..a3a59f4 --- /dev/null +++ b/src/aempsconn/filter/material.py @@ -0,0 +1,19 @@ +"""Module for filtering the 'Materiales'.""" + +from ..utils.filter import Filterable, Text + + +class NumRegistro(Text["MaterialFilter"]): + def __init__(self, instance: "MaterialFilter") -> None: + key: str = "nregistro" + super().__init__(key=key, instance=instance) + + +class MaterialFilter(Filterable): + + def __init__(self) -> None: + super().__init__() + + @property + def nregistro(self) -> "NumRegistro": + return NumRegistro(instance=self) diff --git a/src/aempsconn/filter/nota.py b/src/aempsconn/filter/nota.py new file mode 100644 index 0000000..e45c6de --- /dev/null +++ b/src/aempsconn/filter/nota.py @@ -0,0 +1,19 @@ +"""Module for filtering the 'Notas'.""" + +from ..utils.filter import Filterable, Text + + +class NumRegistro(Text["NotaFilter"]): + def __init__(self, instance: "NotaFilter") -> None: + key: str = "nregistro" + super().__init__(key=key, instance=instance) + + +class NotaFilter(Filterable): + + def __init__(self) -> None: + super().__init__() + + @property + def nregistro(self) -> "NumRegistro": + return NumRegistro(instance=self) diff --git a/src/aempsconn/filter/registro_cambios.py b/src/aempsconn/filter/registro_cambios.py new file mode 100644 index 0000000..9afb8ae --- /dev/null +++ b/src/aempsconn/filter/registro_cambios.py @@ -0,0 +1,39 @@ +"""Module for filtering the 'Registro Cambios'.""" + +from ..utils.filter import Date, Filterable, Integer, Text + + +class Fecha(Date["RegistroCambiosFilter"]): + def __init__(self, instance: "RegistroCambiosFilter") -> None: + key: str = "fecha" + super().__init__(key=key, instance=instance) + + +class NumRegistro(Text["RegistroCambiosFilter"]): + def __init__(self, instance: "RegistroCambiosFilter") -> None: + key: str = "nregistro" + super().__init__(key=key, instance=instance) + + +class Pagina(Integer["RegistroCambiosFilter"]): + def __init__(self, instance: "RegistroCambiosFilter") -> None: + key: str = "pagina" + super().__init__(key=key, instance=instance) + + +class RegistroCambiosFilter(Filterable): + + def __init__(self) -> None: + super().__init__() + + @property + def fecha(self) -> "Fecha": + return Fecha(instance=self) + + @property + def nregistro(self) -> "NumRegistro": + return NumRegistro(instance=self) + + @property + def pagina(self) -> "Pagina": + return Pagina(instance=self) diff --git a/src/aempsconn/modules/__init__.py b/src/aempsconn/modules/__init__.py index 9bd28cd..d8fd42d 100644 --- a/src/aempsconn/modules/__init__.py +++ b/src/aempsconn/modules/__init__.py @@ -1,6 +1,9 @@ """Package: CIMA-related modules.""" +from .material import Material from .medicamento import Medicamento from .medicamentos import Medicamentos +from .notas import Notas from .presentaciones import Presentaciones +from .registro_cambios import RegistroCambios from .vmpp import Vmpp diff --git a/src/aempsconn/modules/material.py b/src/aempsconn/modules/material.py new file mode 100644 index 0000000..b8ad508 --- /dev/null +++ b/src/aempsconn/modules/material.py @@ -0,0 +1,21 @@ +"""Module for retrieving information from 'Materiales' endpoint.""" + +from typing import Any + +from ..filter.material import MaterialFilter +from ..objects.material import MaterialModel +from ..utils.module import BaseModule +from ..utils.request_handler import ReqHandler + + +class Material(BaseModule[MaterialFilter, MaterialModel]): + + def __init__(self, req_handler: ReqHandler) -> None: + super().__init__(req_handler) + + @property + def endpoint(self) -> str: + return "materiales" + + def parse_result(self, data: dict[str, Any]) -> MaterialModel: + return MaterialModel(**data) diff --git a/src/aempsconn/modules/notas.py b/src/aempsconn/modules/notas.py new file mode 100644 index 0000000..2bbbf55 --- /dev/null +++ b/src/aempsconn/modules/notas.py @@ -0,0 +1,21 @@ +"""Module for retrieving information from 'Notas' endpoint.""" + +from typing import Any + +from ..filter.nota import NotaFilter +from ..objects.nota import NotaModel +from ..utils.module import BaseModule +from ..utils.request_handler import ReqHandler + + +class Notas(BaseModule[NotaFilter, NotaModel]): + + def __init__(self, req_handler: ReqHandler) -> None: + super().__init__(req_handler) + + @property + def endpoint(self) -> str: + return "notas" + + def parse_result(self, data: list[dict[str, Any]]) -> list[NotaModel]: + return [NotaModel(**d) for d in data] diff --git a/src/aempsconn/modules/registro_cambios.py b/src/aempsconn/modules/registro_cambios.py new file mode 100644 index 0000000..3ef51e6 --- /dev/null +++ b/src/aempsconn/modules/registro_cambios.py @@ -0,0 +1,23 @@ +"""Module for retrieving information from 'Registro Cambios' endpoint.""" + +from typing import Any + +from ..filter.registro_cambios import RegistroCambiosFilter +from ..objects.registro_cambios import RegistroCambiosModel +from ..utils.module import BaseModule +from ..utils.request_handler import ReqHandler + + +class RegistroCambios(BaseModule[RegistroCambiosFilter, RegistroCambiosModel]): + + def __init__(self, req_handler: ReqHandler) -> None: + super().__init__(req_handler) + + @property + def endpoint(self) -> str: + return "registroCambios" + + def parse_result(self, data: dict[str, Any]) -> RegistroCambiosModel: + if "Es necesario indicar la fecha" in data.values(): + raise KeyError("You must filter, at least, by `fecha`") + return RegistroCambiosModel(**data) diff --git a/src/aempsconn/objects/documento.py b/src/aempsconn/objects/documento.py index 682a6e9..888a3f2 100644 --- a/src/aempsconn/objects/documento.py +++ b/src/aempsconn/objects/documento.py @@ -2,22 +2,14 @@ from datetime import datetime -from pydantic import HttpUrl, field_validator +from pydantic import Field, HttpUrl from pydantic.dataclasses import dataclass @dataclass class DocumentoModel: - tipo: int secc: bool + tipo: int = Field(ge=1, le=4) url: HttpUrl | None = None urlHtml: HttpUrl | None = None fecha: datetime | None = None - - @field_validator("tipo") - @classmethod - def check_tipo_range(cls, tipo: int) -> int: - if tipo not in range(1, 5): - raise ValueError("'Tipo' field must be between 1-4 (both included)") - - return tipo diff --git a/src/aempsconn/objects/material.py b/src/aempsconn/objects/material.py index afdc477..f26cff6 100644 --- a/src/aempsconn/objects/material.py +++ b/src/aempsconn/objects/material.py @@ -1,14 +1,14 @@ """Module for the 'Material' object.""" -from pydantic import HttpUrl from pydantic.dataclasses import dataclass from .documento_material import DocumentoMaterialModel +from .videos import VideoModel @dataclass class MaterialModel: - titulo: str - listaDocsPaciente: list[DocumentoMaterialModel] - listaDocsProfesional: list[DocumentoMaterialModel] - video: HttpUrl + titulo: str | None = None + listaDocsPaciente: list[DocumentoMaterialModel] | None = None + listaDocsProfesional: list[DocumentoMaterialModel] | None = None + videos: list[VideoModel] | None = None diff --git a/src/aempsconn/objects/nota.py b/src/aempsconn/objects/nota.py index 3d56090..ac3a552 100644 --- a/src/aempsconn/objects/nota.py +++ b/src/aempsconn/objects/nota.py @@ -10,7 +10,7 @@ class NotaModel: tipo: int num: str - ref: str + referencia: str asunto: str fecha: datetime url: HttpUrl diff --git a/src/aempsconn/objects/registro_cambios.py b/src/aempsconn/objects/registro_cambios.py index a8a0ce9..9a1d8d8 100644 --- a/src/aempsconn/objects/registro_cambios.py +++ b/src/aempsconn/objects/registro_cambios.py @@ -3,7 +3,7 @@ from datetime import datetime from typing import Literal -from pydantic import field_validator +from pydantic import Field from pydantic.dataclasses import dataclass @@ -11,8 +11,7 @@ class RegistroCambiosModel: nregistro: str fecha: datetime - tipoCambio: int - cambios: list[ + cambio: list[ Literal[ "estado", "comerc", @@ -22,13 +21,7 @@ class RegistroCambiosModel: "notasSeguridad", "matinf", "otros", + "fotos", ] ] - - @field_validator("tipoCambio") - @classmethod - def check_tipo_cambio_range(cls, tipo_cambio: int) -> int: - if tipo_cambio not in range(1, 4): - raise ValueError("'TipoCambio' field must be between 1-4 (both included)") - - return tipo_cambio + tipoCambio: int = Field(ge=1, le=4) diff --git a/src/aempsconn/objects/videos.py b/src/aempsconn/objects/videos.py new file mode 100644 index 0000000..247ff59 --- /dev/null +++ b/src/aempsconn/objects/videos.py @@ -0,0 +1,14 @@ +"""Module for the 'Videos' object.""" + +from datetime import datetime + +from pydantic import HttpUrl +from pydantic.dataclasses import dataclass + + +@dataclass +class VideoModel: + titulo: str + url: HttpUrl + video: HttpUrl + fecha: datetime diff --git a/src/aempsconn/utils/__init__.py b/src/aempsconn/utils/__init__.py deleted file mode 100644 index 19f917f..0000000 --- a/src/aempsconn/utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Package: General utils for the AEMPSconn.""" diff --git a/src/aempsconn/utils/filter/__init__.py b/src/aempsconn/utils/filter/__init__.py index 584a770..51b4035 100644 --- a/src/aempsconn/utils/filter/__init__.py +++ b/src/aempsconn/utils/filter/__init__.py @@ -1,4 +1,4 @@ """Package: Utils of the filtering methods for AEMPSconn.""" from .filter import Filterable -from .operator import Bool, Integer, Text +from .operator import Bool, Date, Integer, Text diff --git a/src/aempsconn/utils/filter/base_operator.py b/src/aempsconn/utils/filter/base_operator.py index e090270..1a835e2 100644 --- a/src/aempsconn/utils/filter/base_operator.py +++ b/src/aempsconn/utils/filter/base_operator.py @@ -1,5 +1,6 @@ """Module for the base operators of the filtering.""" +from datetime import datetime from typing import Generic, TypeVar from .filter import Filter, Filterable @@ -21,6 +22,10 @@ def __init__( class Equals(BaseOperator[T, R], Generic[T, R]): def equals(self, value: T) -> R: + if self.key == "fecha": + # checks datetime format and raises a value error if needed + datetime.strptime(str(value), "%d/%m/%Y").strftime("%d/%m/%Y") + self.instance.query.append(Filter(key=self.key, value=value)) return self.instance diff --git a/src/aempsconn/utils/filter/operator.py b/src/aempsconn/utils/filter/operator.py index c8e2ded..62562bc 100644 --- a/src/aempsconn/utils/filter/operator.py +++ b/src/aempsconn/utils/filter/operator.py @@ -26,3 +26,10 @@ class Integer( Generic[R], ): pass + + +class Date( + Equals[str, R], + Generic[R], +): + pass diff --git a/src/aempsconn/utils/request_handler.py b/src/aempsconn/utils/request_handler.py index be7a885..1daa951 100644 --- a/src/aempsconn/utils/request_handler.py +++ b/src/aempsconn/utils/request_handler.py @@ -115,6 +115,12 @@ def _request( initial_response: dict[str, Any] = self._raw_request( method=method, endpoint=endpoint, params=params, **kwargs ) + + if isinstance(initial_response, list): + # When requesting "notas", AEMPS returns a list + yield initial_response + return + if initial_response.get(RESULTS, None) is None: yield initial_response