eco-back 0.2.1__py3-none-any.whl → 0.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
eco_back/__init__.py CHANGED
@@ -2,12 +2,13 @@
2
2
  eco-back - Librería Python
3
3
  """
4
4
 
5
- __version__ = "0.2.1"
5
+ __version__ = "0.2.3"
6
6
 
7
7
  # Módulos principales
8
8
  from . import database
9
9
  from . import api
10
10
  from . import documento
11
11
  from . import registro
12
+ from . import utils
12
13
 
13
- __all__ = ["database", "api", "documento", "registro", "__version__"]
14
+ __all__ = ["database", "api", "documento", "registro", "utils", "__version__"]
@@ -2,12 +2,12 @@ from rest_framework.decorators import api_view, renderer_classes
2
2
  from django.views.decorators.csrf import csrf_exempt
3
3
  from rest_framework.renderers import JSONRenderer
4
4
  from rest_framework import status
5
+ from rest_framework.response import Response
5
6
  from registro.models.models_global import Trazabilidad
6
7
  from registro.serializer.serializer_get import TrazabilidadSerializer
7
- from rest_framework.response import Response
8
+
9
+ from eco_back.utils import ServiceValidationError, handle_service_validation_error, _log_warn
8
10
 
9
- from utils.guardado_datos.exceptions import ServiceValidationError, ServiceValidationError, handle_service_validation_error
10
- from utils.logger_generico import _log_warn
11
11
 
12
12
 
13
13
  @csrf_exempt
@@ -0,0 +1,13 @@
1
+ """
2
+ Módulo de utilidades para eco-back
3
+ Incluye manejo de excepciones y logging
4
+ """
5
+
6
+ from .exceptions import ServiceValidationError, handle_service_validation_error
7
+ from .logger_generico import _log_warn
8
+
9
+ __all__ = [
10
+ "ServiceValidationError",
11
+ "handle_service_validation_error",
12
+ "_log_warn"
13
+ ]
@@ -0,0 +1,46 @@
1
+ # Raise custom exception for service validation errors
2
+ # - Envía un mensaje y un código HTTP asociado
3
+ import datetime
4
+ from functools import wraps
5
+ from django.http import JsonResponse
6
+
7
+ from eco_back.utils.logger_generico import _log_warn
8
+
9
+ class ServiceValidationError(Exception):
10
+ def __init__(self, message: str, code: int = 400, errors=None, extra=None, context=None, user_id=None, request_path=None):
11
+ super().__init__(message)
12
+ self.message = message
13
+ self.code = code
14
+ self.errors = errors
15
+ self.extra = extra
16
+ self.context = context
17
+ self.user_id = user_id
18
+ self.request_path = request_path
19
+ self.timestamp = datetime.datetime.now().isoformat()
20
+
21
+ def handle_service_validation_error(view_func):
22
+ @wraps(view_func)
23
+ def _wrapped_view(request, *args, **kwargs):
24
+ try:
25
+ return view_func(request, *args, **kwargs)
26
+ except ServiceValidationError as e:
27
+ _log_warn(f"Error: {e.message}", level="warning", extra={"errors": e.errors, "extra": e.extra, "context": e.context}, exc_info=True)
28
+ resp = {"status": "error", "message": e.message}
29
+ if e.errors:
30
+ resp["errors"] = e.errors
31
+ if e.extra:
32
+ resp["extra"] = e.extra
33
+ if e.context:
34
+ resp["context"] = e.context
35
+ if e.user_id:
36
+ resp["user_id"] = e.user_id
37
+ if e.request_path:
38
+ resp["request_path"] = e.request_path
39
+
40
+ resp["timestamp"] = e.timestamp
41
+ return JsonResponse(resp, status=e.code)
42
+ except Exception as e:
43
+ _log_warn(f"Error inesperado en decorator: {e}", level="error", exc_info=True, extra={"view": view_func.__name__})
44
+ print(f"handle_service_validation_error atrapó excepción: {e}")
45
+ return JsonResponse({"status": "error", "message": "Error interno"}, status=500)
46
+ return _wrapped_view
@@ -0,0 +1,107 @@
1
+ import logging
2
+ import inspect
3
+ logger = logging.getLogger(__name__)
4
+
5
+ # Mensajes
6
+ def _log_warn(msg, level='info', exc_info=False, extra=None):
7
+ """
8
+ Registrar mensajes usando el logger configurado.
9
+
10
+ Parámetros:
11
+ msg: mensaje (se convierte a str).
12
+ level: nivel de log ('debug','info','warning','error','critical' o int). Default 'info'.
13
+ exc_info: bool o excepción para incluir traceback. Default False.
14
+ extra: dict opcional para pasar como extra al logger.
15
+
16
+ Llamadas anteriores _log_warn(msg) siguen funcionando y producirán un INFO por defecto.
17
+ """
18
+ # compatibilidad: si se llama con un solo argumento, funciona como antes (INFO)
19
+ try:
20
+ # obtener frame llamante de forma ligera
21
+ frm = inspect.currentframe()
22
+ caller = frm.f_back if frm is not None else None
23
+ mod = inspect.getmodule(caller) if caller is not None else None
24
+ logger_name = mod.__name__ if mod and hasattr(mod, '__name__') else __name__
25
+ logger = logging.getLogger(logger_name)
26
+
27
+ extra = extra or {}
28
+ lvl = level.lower() if isinstance(level, str) else None
29
+
30
+ if isinstance(level, str):
31
+ lvl = level.lower()
32
+ if lvl == 'debug':
33
+ logger.debug("%s", str(msg), exc_info=exc_info, **({'extra': extra} if extra else {}))
34
+ elif lvl == 'info':
35
+ logger.info("%s", str(msg), exc_info=exc_info, **({'extra': extra} if extra else {}))
36
+ elif lvl in ('warn', 'warning'):
37
+ logger.warning("%s", str(msg), exc_info=exc_info, **({'extra': extra} if extra else {}))
38
+ elif lvl == 'error':
39
+ logger.error("%s", str(msg), exc_info=exc_info, **({'extra': extra} if extra else {}))
40
+ elif lvl == 'critical':
41
+ logger.critical("%s", str(msg), exc_info=exc_info, **({'extra': extra} if extra else {}))
42
+ else:
43
+ logger.info("%s", str(msg), exc_info=exc_info, **({'extra': extra} if extra else {}))
44
+ else:
45
+ # nivel numérico
46
+ logger.log(int(level), "%s", str(msg), exc_info=exc_info, **({'extra': extra} if extra else {}))
47
+ except Exception:
48
+ try:
49
+ logging.getLogger(__name__).error("Error al loggear mensaje: %s", str(msg), exc_info=True)
50
+ except Exception:
51
+ # fallback: siempre imprimir por stdout/stderr para depuración inmediata
52
+ try:
53
+ import sys, traceback
54
+ print("[LOGGER-ERROR] Error al loggear mensaje:", str(msg), file=sys.stderr)
55
+ traceback.print_exc()
56
+ except Exception:
57
+ pass
58
+ pass
59
+
60
+ # Usos de _log_warn
61
+ '''
62
+ # Mensaje simple (por compatibilidad -> level='info')
63
+ _log_warn("Inicio del proceso")
64
+
65
+
66
+ # Nivel explícito
67
+ _log_warn("Campo faltante en payload", level="warning")
68
+ _log_warn("Operación completada", level="debug")
69
+
70
+
71
+ # Con exception traceback dentro de un except
72
+ try:
73
+ # hacer_algo()
74
+ print(1) # ejemplo que lanza excepción
75
+ except Exception as e:
76
+ _log_warn(f"Error al ejecutar hacer_algo: {e}", level="error", exc_info=True)
77
+
78
+
79
+ # Loggear estructuras (dict/list) de forma legible
80
+ import json
81
+ data = {"usuario": "juan", "roles": ["admin", "analista"]}
82
+ _log_warn(json.dumps(data, ensure_ascii=False), level="info")
83
+
84
+
85
+ # o directamente (se convierte a str internamente)
86
+ _log_warn(data, level="debug")
87
+
88
+
89
+ # Loggear una instancia Django (recomendado convertir a dict)
90
+ from django.forms.models import model_to_dict
91
+ instancia_modelo = ... # instancia de un modelo Django
92
+ _log_warn(model_to_dict(instancia_modelo), level="debug")
93
+
94
+
95
+ # Loggear una instancia Django (recomendado convertir a dict)
96
+ from django.forms.models import model_to_dict
97
+ _log_warn(model_to_dict(instancia_modelo), level="debug")
98
+
99
+ # Usar extra para pasar metadata (si el handler/formatter lo soporta)
100
+ user = ... # instancia de usuario
101
+ ot = ... # alguna otra variable relevante
102
+ _log_warn("Acceso a recurso", level="info", extra={"user_id": str(user.id), "ot": ot})
103
+
104
+ # Nivel numérico
105
+ _log_warn("Mensaje crítico numérico", level=50) # equivale a CRITICAL
106
+
107
+ '''
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eco-back
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Librería Python eco-back
5
5
  Author-email: EI UNP <ecosistema@unp.gov.co>
6
6
  License: MIT
@@ -38,18 +38,20 @@ Dynamic: license-file
38
38
 
39
39
  # eco-back
40
40
 
41
- Librería Python para backend con soporte para PostgreSQL/PostGIS y clientes API
41
+ Librería Python para backend con soporte para PostgreSQL/PostGIS, clientes API, manejo de excepciones y logging
42
42
 
43
43
  ## Características
44
44
 
45
- - **Base de datos**: Conexión y operaciones con PostgreSQL
46
- - **PostGIS**: Operaciones geoespaciales (puntos, polígonos, búsquedas por proximidad)
47
- - **Cliente API**: Cliente HTTP genérico y cliente específico UNP
48
- - **Patrón Repository**: Implementación de patrones de diseño para acceso a datos
45
+ - **Base de datos**: Conexión y operaciones con PostgreSQL
46
+ - **PostGIS**: Operaciones geoespaciales (puntos, polígonos, búsquedas por proximidad)
47
+ - **Cliente API**: Cliente HTTP genérico y cliente específico UNP
48
+ - **Patrón Repository**: Implementación de patrones de diseño para acceso a datos
49
+ - **Utils**: Sistema de excepciones y logging para Django
50
+ - **Registro**: Funciones de trazabilidad para Django REST Framework
49
51
 
50
52
  ## Descripción
51
53
 
52
- eco-back es una librería modular que facilita el desarrollo de aplicaciones backend, proporcionando abstracciones para bases de datos geoespaciales y consumo de APIs REST.
54
+ eco-back es una librería modular que facilita el desarrollo de aplicaciones backend, proporcionando abstracciones para bases de datos geoespaciales, consumo de APIs REST, manejo de excepciones y logging estructurado.
53
55
 
54
56
  ## Instalación
55
57
 
@@ -194,6 +196,58 @@ flake8 src/ tests/
194
196
  mypy src/
195
197
  ```
196
198
 
199
+ ### Manejo de excepciones y logging (Django)
200
+
201
+ ```python
202
+ from eco_back.utils import (
203
+ ServiceValidationError,
204
+ handle_service_validation_error,
205
+ _log_warn
206
+ )
207
+ from rest_framework.decorators import api_view
208
+ from rest_framework.response import Response
209
+
210
+ @api_view(['GET'])
211
+ @handle_service_validation_error
212
+ def mi_vista(request, user_id):
213
+ try:
214
+ _log_warn(f"Buscando usuario {user_id}", level="info")
215
+
216
+ usuario = Usuario.objects.get(id=user_id)
217
+ return Response({"usuario": usuario.username})
218
+
219
+ except Usuario.DoesNotExist:
220
+ _log_warn(f"Usuario {user_id} no encontrado", level="warning")
221
+ raise ServiceValidationError(
222
+ "Usuario no encontrado",
223
+ code=404,
224
+ context={"user_id": user_id}
225
+ )
226
+ ```
227
+
228
+ ### Trazabilidad de registros (Django)
229
+
230
+ ```python
231
+ from eco_back.registro import TrazabilidadRegistroPermiso
232
+ from django.urls import path
233
+
234
+ urlpatterns = [
235
+ path('api/trazabilidad/<int:registro_id>/', TrazabilidadRegistroPermiso),
236
+ ]
237
+ ```
238
+
239
+ ## Módulos disponibles
240
+
241
+ - `eco_back.database` - Conexiones y operaciones con PostgreSQL/PostGIS
242
+ - `eco_back.api` - Clientes HTTP y consumo de APIs
243
+ - `eco_back.documento` - Manejo de documentos y anexos
244
+ - `eco_back.utils` - Excepciones y logging para Django
245
+ - `eco_back.registro` - Funciones de trazabilidad (Django REST Framework)
246
+
247
+ Para más información, consulta los README de cada módulo:
248
+ - [Utils (excepciones y logging)](src/eco_back/utils/README.md)
249
+ - [Registro (trazabilidad)](src/eco_back/registro/README.md)
250
+
197
251
  ## Licencia
198
252
 
199
253
  MIT
@@ -1,4 +1,4 @@
1
- eco_back/__init__.py,sha256=-RSbnbAjw4et97vl6xBoZkLlKL5gsOVn4t6iW-0zzEA,255
1
+ eco_back/__init__.py,sha256=9N4_nRaEpRuXLvPwkDksFgFFRu80eqH7gVXq4gP-3-c,285
2
2
  eco_back/api/__init__.py,sha256=9moiU5H8jGlva8k65bUK49LRIkE7bcLH1SK1dk23wcM,202
3
3
  eco_back/api/client.py,sha256=b2J9ty7mRtPUsMD-YJui60B9GEa-85VAAb-sS5y1Rp8,8537
4
4
  eco_back/api/config.py,sha256=dyERZY_4lXtRjhqLaHI0F4WlgPOQ_sw35eHNWGkQNLo,1590
@@ -12,9 +12,12 @@ eco_back/database/repository.py,sha256=NFh0pfvf7Pw878qOoVhR8ARMFC0CiJBQ_EwKshY8l
12
12
  eco_back/documento/__init__.py,sha256=0rIgJ52in_BMg7VYr2pVlu9EnTPftgdqJ4hOZU5I4j0,146
13
13
  eco_back/documento/anexos.py,sha256=KIzGNEVJ6tV3awhq4WGiqb8eSV2PluRRZ0PRbqeO5VE,17511
14
14
  eco_back/registro/__init__.py,sha256=CRDv6OSKzt1r-RnKSfv86Ceb7yfL2Fn-OhHGCx467lE,148
15
- eco_back/registro/trazabilidad.py,sha256=nj0DRCqSkJ57GVnw8kAYVpCxr1JZ1nb3n-zmTQ8QlBQ,1258
16
- eco_back-0.2.1.dist-info/licenses/LICENSE,sha256=XKKSDU9WlUEAyPNlRhq6e2xhVNpJc097JwPZJ1rUnRE,1077
17
- eco_back-0.2.1.dist-info/METADATA,sha256=9iudxyPMgsc09sle7BE_bisz3k9N1hzhRIHnzvBkwrw,4736
18
- eco_back-0.2.1.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
19
- eco_back-0.2.1.dist-info/top_level.txt,sha256=ViJZv023782XNNFMcAsRC9iN1CU3tb8lP9wDAHacMyc,9
20
- eco_back-0.2.1.dist-info/RECORD,,
15
+ eco_back/registro/trazabilidad.py,sha256=XiOxAJCDGIkRjG-Lznko9ztmVxDhEBUYYwQiUxYOuxg,1185
16
+ eco_back/utils/__init__.py,sha256=DM0Y6fMeoRH6tG0skpkfEUVR71OAIFuVKQDC3_XBHvc,317
17
+ eco_back/utils/exceptions.py,sha256=woKrhxkncGIG_rCgRFT6B9ZAgV2TdaCH4Vcusyj3aX4,2010
18
+ eco_back/utils/logger_generico.py,sha256=1FIalQDkxzu0NBj-L7SzTuKEWcZuh25ZcIEux5WCB2c,4227
19
+ eco_back-0.2.3.dist-info/licenses/LICENSE,sha256=XKKSDU9WlUEAyPNlRhq6e2xhVNpJc097JwPZJ1rUnRE,1077
20
+ eco_back-0.2.3.dist-info/METADATA,sha256=CZY_ssPUJRBFpMcX5Ee7q_fvyIEKAr06AZAkgGk1Pu8,6544
21
+ eco_back-0.2.3.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
22
+ eco_back-0.2.3.dist-info/top_level.txt,sha256=ViJZv023782XNNFMcAsRC9iN1CU3tb8lP9wDAHacMyc,9
23
+ eco_back-0.2.3.dist-info/RECORD,,