Skip to content

Commit

Permalink
Merge pull request #28 from guivaloz/guivaloz/tests-exhortos-enviar-a…
Browse files Browse the repository at this point in the history
…rchivo

Cambio en recibir archivo
  • Loading branch information
guivaloz authored Jul 11, 2024
2 parents b23863c + 1660b97 commit c9bb9fc
Show file tree
Hide file tree
Showing 13 changed files with 287 additions and 56 deletions.
Empty file.
48 changes: 48 additions & 0 deletions carina/core/exh_externos/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
Exh Externos, modelos
"""

from typing import Optional

from sqlalchemy import JSON, ForeignKey, String
from sqlalchemy.orm import Mapped, mapped_column, relationship

from lib.database import Base
from lib.universal_mixin import UniversalMixin


class ExhExterno(Base, UniversalMixin):
"""ExhExterno"""

# Nombre de la tabla
__tablename__ = "exh_externos"

# Clave primaria
id: Mapped[int] = mapped_column(primary_key=True)

# Clave foránea
estado_id: Mapped[int] = mapped_column(ForeignKey("estados.id"))
estado: Mapped["Estado"] = relationship(back_populates="exh_externos")

# Columnas
clave: Mapped[str] = mapped_column(String(16), unique=True)
descripcion: Mapped[str] = mapped_column(String(256))
api_key: Mapped[Optional[str]] = mapped_column(String(128))

# Columna materias es JSON con clave y nombre para cada una
materias: Mapped[Optional[dict]] = mapped_column(JSON)

# Columnas endpoints
endpoint_consultar_materias: Mapped[Optional[str]] = mapped_column(String(256))
endpoint_recibir_exhorto: Mapped[Optional[str]] = mapped_column(String(256))
endpoint_recibir_exhorto_archivo: Mapped[Optional[str]] = mapped_column(String(256))
endpoint_consultar_exhorto: Mapped[Optional[str]] = mapped_column(String(256))
endpoint_recibir_respuesta_exhorto: Mapped[Optional[str]] = mapped_column(String(256))
endpoint_recibir_respuesta_exhorto_archivo: Mapped[Optional[str]] = mapped_column(String(256))
endpoint_actualizar_exhorto: Mapped[Optional[str]] = mapped_column(String(256))
endpoint_recibir_promocion: Mapped[Optional[str]] = mapped_column(String(256))
endpoint_recibir_promocion_archivo: Mapped[Optional[str]] = mapped_column(String(256))

def __repr__(self):
"""Representación"""
return f"<ExhExterno {self.id}>"
Empty file.
41 changes: 41 additions & 0 deletions carina/v4/exh_externos/crud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""
Exh Externos v4, CRUD (create, read, update, and delete)
"""

from typing import Any

from sqlalchemy.orm import Session

from lib.exceptions import MyIsDeletedError, MyNotExistsError, MyNotValidParamError
from lib.safe_string import safe_clave

from ...core.exh_externos.models import ExhExterno


def get_exh_externos(database: Session) -> Any:
"""Consultar los externos activos"""
return database.query(ExhExterno).filter_by(estatus="A").order_by(ExhExterno.clave)


def get_exh_externo(database: Session, exh_externo_id: int) -> ExhExterno:
"""Consultar un externo por su id"""
exh_externo = database.query(ExhExterno).get(exh_externo_id)
if exh_externo is None:
raise MyNotExistsError("No existe ese externo")
if exh_externo.estatus != "A":
raise MyIsDeletedError("No es activo ese externo, está eliminado")
return exh_externo


def get_exh_externo_with_clave(database: Session, exh_externo_clave: str) -> ExhExterno:
"""Consultar un externo por su clave"""
try:
exh_externo_clave = safe_clave(exh_externo_clave)
except ValueError as error:
raise MyNotValidParamError(str(error)) from error
exh_externo = database.query(ExhExterno).filter_by(clave=exh_externo_clave).first()
if exh_externo is None:
raise MyNotExistsError("No existe ese externo")
if exh_externo.estatus != "A":
raise MyIsDeletedError("No es activo ese externo, está eliminado")
return exh_externo
34 changes: 34 additions & 0 deletions carina/v4/exh_externos/paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""
Exh Externos v4, rutas (paths)
"""

from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, status
from fastapi_pagination.ext.sqlalchemy import paginate

from lib.database import Session, get_db
from lib.exceptions import MyAnyError
from lib.fastapi_pagination_custom_page import CustomPage

from ...core.permisos.models import Permiso
from ..usuarios.authentications import UsuarioInDB, get_current_active_user
from .crud import get_exh_externos
from .schemas import ExhExternoOut

exh_externos = APIRouter(prefix="/v4/exh_externos", tags=["externos"])


@exh_externos.get("", response_model=CustomPage[ExhExternoOut])
def paginado_exh_externos(
current_user: Annotated[UsuarioInDB, Depends(get_current_active_user)],
database: Annotated[Session, Depends(get_db)],
):
"""Paginado de externos"""
if current_user.permissions.get("EXH EXTERNOS", 0) < Permiso.VER:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden")
try:
resultados = get_exh_externos(database)
except MyAnyError as error:
return CustomPage(success=False, errors=[str(error)])
return paginate(resultados)
32 changes: 32 additions & 0 deletions carina/v4/exh_externos/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
Exh Externos v4, esquemas de pydantic
"""

from pydantic import BaseModel, ConfigDict

from lib.schemas_base import OneBaseOut


class ExhExternoOut(BaseModel):
"""Esquema para entregar externos"""

id: int | None = None
clave: str | None = None
descripcion: str | None = None
materias: dict | None = None
endpoint_consultar_materias: str | None = None
endpoint_recibir_exhorto: str | None = None
endpoint_recibir_exhorto_archivo: str | None = None
endpoint_consultar_exhorto: str | None = None
endpoint_recibir_respuesta_exhorto: str | None = None
endpoint_recibir_respuesta_exhorto_archivo: str | None = None
endpoint_actualizar_exhorto: str | None = None
endpoint_recibir_promocion: str | None = None
endpoint_recibir_promocion_archivo: str | None = None
model_config = ConfigDict(from_attributes=True)


class OneExhExternoOut(OneBaseOut):
"""Esquema para entregar un externo"""

data: ExhExternoOut | None = None
48 changes: 48 additions & 0 deletions lib/pwgen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
Generadores de contraseñas
"""
import random
import string
import time

from hashids import Hashids

from config.settings import get_settings

settings = get_settings()


def generar_api_key(id: int, email: str, random_length: int = 24) -> str:
"""Generar API key a partir de un ID, un e-mail y una cadena aleatoria"""
aleatorio = "".join(random.sample(string.ascii_letters + string.digits, k=random_length))
hash_email = Hashids(salt=email, min_length=8).encode(1)
hash_id = Hashids(salt=settings.SALT, min_length=8).encode(id)
return f"{hash_id}.{hash_email}.{aleatorio}"


def generar_contrasena(largo=16):
"""Generar contraseña con minúsculas, mayúculas, dígitos y signos"""
minusculas = string.ascii_lowercase
mayusculas = string.ascii_uppercase
digitos = string.digits
simbolos = string.punctuation
todos = minusculas + mayusculas + digitos + simbolos
temp = random.sample(todos, largo)
return "".join(temp)


def generar_aleatorio(largo=16):
"""Generar cadena de texto aleatorio con minúsculas, mayúculas y dígitos"""
minusculas = string.ascii_lowercase
mayusculas = string.ascii_uppercase
digitos = string.digits
todos = minusculas + mayusculas + digitos
temp = random.sample(todos, largo)
return "".join(temp)


def generar_identificador(largo: int = 16) -> str:
"""Generar identificador con el tiempo actual y algo aleatorio, todo con letras en mayúsculas y dígitos"""
timestamp_unique = str(int(time.time() * 1000))
random_characters = "".join(random.sample(string.ascii_uppercase + string.digits, k=largo))
return f"{timestamp_unique}{random_characters}"[:largo]
51 changes: 26 additions & 25 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,36 @@ readme = "README.md"

[tool.poetry.dependencies]
python = "^3.11"
cryptography = "^41.0.7"
fastapi = {extras = ["sqlalchemy"], version = "^0.109.0"}
fastapi-pagination = "^0.12.14"
google-auth = "^2.26.2"
google-cloud = "^0.34.0"
google-cloud-secret-manager = "^2.17.0"
google-cloud-storage = "^2.14.0"
gunicorn = "^21.2.0"
hashids = "^1.3.1"
psycopg2-binary = "^2.9.9"
pydantic = "^2.5.3"
pydantic-settings = "^2.1.0"
python-dotenv = "^1.0.0"
cryptography = "^42.0"
fastapi = {extras = ["sqlalchemy"], version = "^0.111"}
fastapi-pagination = "^0.12"
google-auth = "^2.32"
google-cloud = "^0.34"
google-cloud-secret-manager = "^2.20"
google-cloud-storage = "^2.14"
gunicorn = "^22.0"
hashids = "^1.3"
psycopg2-binary = "^2.9"
pydantic = "^2.5"
pydantic-settings = "^2.1"
python-dotenv = "^1.0"
python-multipart = "^0.0.9"
pytz = "^2023.3.post1"
sqlalchemy = "^2.0.25"
sqlalchemy-utils = "^0.41.1"
unidecode = "^1.3.8"
uvicorn = "^0.25.0"
pytz = "^2024.1"
sqlalchemy = "^2.0"
sqlalchemy-utils = "^0.41"
unidecode = "^1.3"
uvicorn = "^0.30"


[tool.poetry.group.dev.dependencies]
isort = "^5.13.2"
black = "^23.12.1"
pylint = "^3.0.3"
pylint-sqlalchemy = "^0.3.0"
pytest = "^7.4.4"
pre-commit = "^3.6.0"
faker = "^25.2.0"
black = "^24.4"
faker = "^26.0"
isort = "^5.13"
poetry-plugin-export = "^1.8"
pylint = "^3.0"
pylint-sqlalchemy = "^0.3"
pytest = "^8.2"
pre-commit = "^3.6"

[build-system]
requires = ["poetry-core"]
Expand Down
67 changes: 44 additions & 23 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,53 +1,74 @@
annotated-types==0.7.0 ; python_version >= "3.11" and python_version < "4.0"
anyio==4.4.0 ; python_version >= "3.11" and python_version < "4.0"
cachetools==5.3.3 ; python_version >= "3.11" and python_version < "4.0"
certifi==2024.6.2 ; python_version >= "3.11" and python_version < "4.0"
cffi==1.16.0 ; python_version >= "3.11" and python_version < "4.0"
certifi==2024.7.4 ; python_version >= "3.11" and python_version < "4.0"
cffi==1.16.0 ; python_version >= "3.11" and python_version < "4.0" and platform_python_implementation != "PyPy"
charset-normalizer==3.3.2 ; python_version >= "3.11" and python_version < "4.0"
click==8.1.7 ; python_version >= "3.11" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and platform_system == "Windows"
cryptography==41.0.7 ; python_version >= "3.11" and python_version < "4.0"
fastapi-pagination==0.12.25 ; python_version >= "3.11" and python_version < "4.0"
fastapi[sqlalchemy]==0.109.2 ; python_version >= "3.11" and python_version < "4.0"
google-api-core==2.19.0 ; python_version >= "3.11" and python_version < "4.0"
google-api-core[grpc]==2.19.0 ; python_version >= "3.11" and python_version < "4.0"
google-auth==2.30.0 ; python_version >= "3.11" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows")
cryptography==42.0.8 ; python_version >= "3.11" and python_version < "4.0"
dnspython==2.6.1 ; python_version >= "3.11" and python_version < "4.0"
email-validator==2.2.0 ; python_version >= "3.11" and python_version < "4.0"
fastapi-cli==0.0.4 ; python_version >= "3.11" and python_version < "4.0"
fastapi-pagination==0.12.26 ; python_version >= "3.11" and python_version < "4.0"
fastapi[sqlalchemy]==0.111.0 ; python_version >= "3.11" and python_version < "4.0"
google-api-core==2.19.1 ; python_version >= "3.11" and python_version < "4.0"
google-api-core[grpc]==2.19.1 ; python_version >= "3.11" and python_version < "4.0"
google-auth==2.32.0 ; python_version >= "3.11" and python_version < "4.0"
google-cloud-core==2.4.1 ; python_version >= "3.11" and python_version < "4.0"
google-cloud-secret-manager==2.20.0 ; python_version >= "3.11" and python_version < "4.0"
google-cloud-secret-manager==2.20.1 ; python_version >= "3.11" and python_version < "4.0"
google-cloud-storage==2.17.0 ; python_version >= "3.11" and python_version < "4.0"
google-cloud==0.34.0 ; python_version >= "3.11" and python_version < "4.0"
google-crc32c==1.5.0 ; python_version >= "3.11" and python_version < "4.0"
google-resumable-media==2.7.1 ; python_version >= "3.11" and python_version < "4.0"
googleapis-common-protos==1.63.1 ; python_version >= "3.11" and python_version < "4.0"
googleapis-common-protos[grpc]==1.63.1 ; python_version >= "3.11" and python_version < "4.0"
googleapis-common-protos==1.63.2 ; python_version >= "3.11" and python_version < "4.0"
googleapis-common-protos[grpc]==1.63.2 ; python_version >= "3.11" and python_version < "4.0"
greenlet==3.0.3 ; python_version < "3.13" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") and python_version >= "3.11"
grpc-google-iam-v1==0.13.0 ; python_version >= "3.11" and python_version < "4.0"
grpcio-status==1.62.2 ; python_version >= "3.11" and python_version < "4.0"
grpc-google-iam-v1==0.13.1 ; python_version >= "3.11" and python_version < "4.0"
grpcio-status==1.64.1 ; python_version >= "3.11" and python_version < "4.0"
grpcio==1.64.1 ; python_version >= "3.11" and python_version < "4.0"
gunicorn==21.2.0 ; python_version >= "3.11" and python_version < "4.0"
gunicorn==22.0.0 ; python_version >= "3.11" and python_version < "4.0"
h11==0.14.0 ; python_version >= "3.11" and python_version < "4.0"
hashids==1.3.1 ; python_version >= "3.11" and python_version < "4.0"
httpcore==1.0.5 ; python_version >= "3.11" and python_version < "4.0"
httptools==0.6.1 ; python_version >= "3.11" and python_version < "4.0"
httpx==0.27.0 ; python_version >= "3.11" and python_version < "4.0"
idna==3.7 ; python_version >= "3.11" and python_version < "4.0"
jinja2==3.1.4 ; python_version >= "3.11" and python_version < "4.0"
markdown-it-py==3.0.0 ; python_version >= "3.11" and python_version < "4.0"
markupsafe==2.1.5 ; python_version >= "3.11" and python_version < "4.0"
mdurl==0.1.2 ; python_version >= "3.11" and python_version < "4.0"
orjson==3.10.6 ; python_version >= "3.11" and python_version < "4.0"
packaging==24.1 ; python_version >= "3.11" and python_version < "4.0"
proto-plus==1.24.0 ; python_version >= "3.11" and python_version < "4.0"
protobuf==4.25.3 ; python_version >= "3.11" and python_version < "4.0"
protobuf==5.27.2 ; python_version >= "3.11" and python_version < "4.0"
psycopg2-binary==2.9.9 ; python_version >= "3.11" and python_version < "4.0"
pyasn1-modules==0.4.0 ; python_version >= "3.11" and python_version < "4.0"
pyasn1==0.6.0 ; python_version >= "3.11" and python_version < "4.0"
pycparser==2.22 ; python_version >= "3.11" and python_version < "4.0"
pydantic-core==2.18.4 ; python_version >= "3.11" and python_version < "4.0"
pydantic-settings==2.3.3 ; python_version >= "3.11" and python_version < "4.0"
pydantic==2.7.4 ; python_version >= "3.11" and python_version < "4.0"
pycparser==2.22 ; python_version >= "3.11" and python_version < "4.0" and platform_python_implementation != "PyPy"
pydantic-core==2.20.1 ; python_version >= "3.11" and python_version < "4.0"
pydantic-settings==2.3.4 ; python_version >= "3.11" and python_version < "4.0"
pydantic==2.8.2 ; python_version >= "3.11" and python_version < "4.0"
pygments==2.18.0 ; python_version >= "3.11" and python_version < "4.0"
python-dotenv==1.0.1 ; python_version >= "3.11" and python_version < "4.0"
python-multipart==0.0.9 ; python_version >= "3.11" and python_version < "4.0"
pytz==2023.4 ; python_version >= "3.11" and python_version < "4.0"
pytz==2024.1 ; python_version >= "3.11" and python_version < "4.0"
pyyaml==6.0.1 ; python_version >= "3.11" and python_version < "4.0"
requests==2.32.3 ; python_version >= "3.11" and python_version < "4.0"
rich==13.7.1 ; python_version >= "3.11" and python_version < "4.0"
rsa==4.9 ; python_version >= "3.11" and python_version < "4"
shellingham==1.5.4 ; python_version >= "3.11" and python_version < "4.0"
sniffio==1.3.1 ; python_version >= "3.11" and python_version < "4.0"
sqlalchemy-utils==0.41.2 ; python_version >= "3.11" and python_version < "4.0"
sqlalchemy==2.0.31 ; python_version >= "3.11" and python_version < "4.0"
starlette==0.36.3 ; python_version >= "3.11" and python_version < "4.0"
starlette==0.37.2 ; python_version >= "3.11" and python_version < "4.0"
typer==0.12.3 ; python_version >= "3.11" and python_version < "4.0"
typing-extensions==4.12.2 ; python_version >= "3.11" and python_version < "4.0"
ujson==5.10.0 ; python_version >= "3.11" and python_version < "4.0"
unidecode==1.3.8 ; python_version >= "3.11" and python_version < "4.0"
urllib3==2.2.2 ; python_version >= "3.11" and python_version < "4.0"
uvicorn==0.25.0 ; python_version >= "3.11" and python_version < "4.0"
uvicorn==0.30.1 ; python_version >= "3.11" and python_version < "4.0"
uvicorn[standard]==0.30.1 ; python_version >= "3.11" and python_version < "4.0"
uvloop==0.19.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_version >= "3.11" and python_version < "4.0"
watchfiles==0.22.0 ; python_version >= "3.11" and python_version < "4.0"
websockets==12.0 ; python_version >= "3.11" and python_version < "4.0"
6 changes: 4 additions & 2 deletions tests/test_estados.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"""

import unittest

import requests

from tests.load_env import config


Expand All @@ -13,7 +15,7 @@ class TestEstados(unittest.TestCase):
def test_get_estados(self):
"""Test GET method for estados"""
response = requests.get(
f"{config['api_base_url']}/estados",
url=f"{config['api_base_url']}/estados",
headers={"X-Api-Key": config["api_key"]},
timeout=config["timeout"],
)
Expand All @@ -22,7 +24,7 @@ def test_get_estados(self):
def test_get_estado_by_clave_inegi(self):
"""Test GET method for estado by clave INEGI"""
response = requests.get(
f"{config['api_base_url']}/estados/05",
url=f"{config['api_base_url']}/estados/05",
headers={"X-Api-Key": config["api_key"]},
timeout=config["timeout"],
)
Expand Down
Loading

0 comments on commit c9bb9fc

Please sign in to comment.