eco-back 0.2.0__tar.gz → 0.2.2__tar.gz

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.
Files changed (35) hide show
  1. {eco_back-0.2.0/src/eco_back.egg-info → eco_back-0.2.2}/PKG-INFO +61 -7
  2. {eco_back-0.2.0 → eco_back-0.2.2}/README.md +60 -6
  3. {eco_back-0.2.0 → eco_back-0.2.2}/pyproject.toml +1 -1
  4. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/__init__.py +3 -2
  5. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/api/registro.py +3 -0
  6. eco_back-0.2.2/src/eco_back/registro/trazabilidad.py +27 -0
  7. eco_back-0.2.2/src/eco_back/utils/__init__.py +13 -0
  8. eco_back-0.2.2/src/eco_back/utils/exceptions.py +46 -0
  9. eco_back-0.2.2/src/eco_back/utils/logger_generico.py +107 -0
  10. {eco_back-0.2.0 → eco_back-0.2.2/src/eco_back.egg-info}/PKG-INFO +61 -7
  11. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back.egg-info/SOURCES.txt +3 -0
  12. eco_back-0.2.0/src/eco_back/registro/trazabilidad.py +0 -0
  13. {eco_back-0.2.0 → eco_back-0.2.2}/LICENSE +0 -0
  14. {eco_back-0.2.0 → eco_back-0.2.2}/MANIFEST.in +0 -0
  15. {eco_back-0.2.0 → eco_back-0.2.2}/setup.cfg +0 -0
  16. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/api/__init__.py +0 -0
  17. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/api/client.py +0 -0
  18. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/api/config.py +0 -0
  19. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/database/__init__.py +0 -0
  20. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/database/config.py +0 -0
  21. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/database/connection.py +0 -0
  22. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/database/models.py +0 -0
  23. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/database/postgis.py +0 -0
  24. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/database/repository.py +0 -0
  25. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/documento/__init__.py +0 -0
  26. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/documento/anexos.py +0 -0
  27. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back/registro/__init__.py +0 -0
  28. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back.egg-info/dependency_links.txt +0 -0
  29. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back.egg-info/requires.txt +0 -0
  30. {eco_back-0.2.0 → eco_back-0.2.2}/src/eco_back.egg-info/top_level.txt +0 -0
  31. {eco_back-0.2.0 → eco_back-0.2.2}/tests/test_anexos.py +0 -0
  32. {eco_back-0.2.0 → eco_back-0.2.2}/tests/test_api.py +0 -0
  33. {eco_back-0.2.0 → eco_back-0.2.2}/tests/test_database.py +0 -0
  34. {eco_back-0.2.0 → eco_back-0.2.2}/tests/test_example.py +0 -0
  35. {eco_back-0.2.0 → eco_back-0.2.2}/tests/test_postgis.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eco-back
3
- Version: 0.2.0
3
+ Version: 0.2.2
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,17 +1,19 @@
1
1
  # eco-back
2
2
 
3
- Librería Python para backend con soporte para PostgreSQL/PostGIS y clientes API
3
+ Librería Python para backend con soporte para PostgreSQL/PostGIS, clientes API, manejo de excepciones y logging
4
4
 
5
5
  ## Características
6
6
 
7
- - **Base de datos**: Conexión y operaciones con PostgreSQL
8
- - **PostGIS**: Operaciones geoespaciales (puntos, polígonos, búsquedas por proximidad)
9
- - **Cliente API**: Cliente HTTP genérico y cliente específico UNP
10
- - **Patrón Repository**: Implementación de patrones de diseño para acceso a datos
7
+ - **Base de datos**: Conexión y operaciones con PostgreSQL
8
+ - **PostGIS**: Operaciones geoespaciales (puntos, polígonos, búsquedas por proximidad)
9
+ - **Cliente API**: Cliente HTTP genérico y cliente específico UNP
10
+ - **Patrón Repository**: Implementación de patrones de diseño para acceso a datos
11
+ - **Utils**: Sistema de excepciones y logging para Django
12
+ - **Registro**: Funciones de trazabilidad para Django REST Framework
11
13
 
12
14
  ## Descripción
13
15
 
14
- 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.
16
+ 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.
15
17
 
16
18
  ## Instalación
17
19
 
@@ -156,6 +158,58 @@ flake8 src/ tests/
156
158
  mypy src/
157
159
  ```
158
160
 
161
+ ### Manejo de excepciones y logging (Django)
162
+
163
+ ```python
164
+ from eco_back.utils import (
165
+ ServiceValidationError,
166
+ handle_service_validation_error,
167
+ _log_warn
168
+ )
169
+ from rest_framework.decorators import api_view
170
+ from rest_framework.response import Response
171
+
172
+ @api_view(['GET'])
173
+ @handle_service_validation_error
174
+ def mi_vista(request, user_id):
175
+ try:
176
+ _log_warn(f"Buscando usuario {user_id}", level="info")
177
+
178
+ usuario = Usuario.objects.get(id=user_id)
179
+ return Response({"usuario": usuario.username})
180
+
181
+ except Usuario.DoesNotExist:
182
+ _log_warn(f"Usuario {user_id} no encontrado", level="warning")
183
+ raise ServiceValidationError(
184
+ "Usuario no encontrado",
185
+ code=404,
186
+ context={"user_id": user_id}
187
+ )
188
+ ```
189
+
190
+ ### Trazabilidad de registros (Django)
191
+
192
+ ```python
193
+ from eco_back.registro import TrazabilidadRegistroPermiso
194
+ from django.urls import path
195
+
196
+ urlpatterns = [
197
+ path('api/trazabilidad/<int:registro_id>/', TrazabilidadRegistroPermiso),
198
+ ]
199
+ ```
200
+
201
+ ## Módulos disponibles
202
+
203
+ - `eco_back.database` - Conexiones y operaciones con PostgreSQL/PostGIS
204
+ - `eco_back.api` - Clientes HTTP y consumo de APIs
205
+ - `eco_back.documento` - Manejo de documentos y anexos
206
+ - `eco_back.utils` - Excepciones y logging para Django
207
+ - `eco_back.registro` - Funciones de trazabilidad (Django REST Framework)
208
+
209
+ Para más información, consulta los README de cada módulo:
210
+ - [Utils (excepciones y logging)](src/eco_back/utils/README.md)
211
+ - [Registro (trazabilidad)](src/eco_back/registro/README.md)
212
+
159
213
  ## Licencia
160
214
 
161
215
  MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "eco-back"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "Librería Python eco-back"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -2,12 +2,13 @@
2
2
  eco-back - Librería Python
3
3
  """
4
4
 
5
- __version__ = "0.2.0"
5
+ __version__ = "0.2.2"
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__"]
@@ -89,3 +89,6 @@ class Consecutivo:
89
89
  """Context manager exit"""
90
90
  self.close()
91
91
  return False
92
+
93
+
94
+
@@ -0,0 +1,27 @@
1
+ from eco_back.utils.exceptions import ServiceValidationError, handle_service_validation_error
2
+ from eco_back.utils.logger_generico import _log_warn
3
+ from rest_framework.decorators import api_view, renderer_classes
4
+ from django.views.decorators.csrf import csrf_exempt
5
+ from rest_framework.renderers import JSONRenderer
6
+ from rest_framework import status
7
+ from registro.models.models_global import Trazabilidad
8
+ from registro.serializer.serializer_get import TrazabilidadSerializer
9
+ from rest_framework.response import Response
10
+
11
+
12
+
13
+ @csrf_exempt
14
+ @api_view(['GET'])
15
+ @renderer_classes([JSONRenderer])
16
+ @handle_service_validation_error
17
+ def TrazabilidadRegistroPermiso(request, registro_id):
18
+ try:
19
+ trazabilidades = Trazabilidad.objects.filter(
20
+ registro=int(registro_id)
21
+ ).order_by('fecha_recibido')
22
+
23
+ serializer = TrazabilidadSerializer(trazabilidades, many=True)
24
+ return Response(serializer.data, status=status.HTTP_200_OK)
25
+ except Exception as e:
26
+ _log_warn(f"Consulta trazabilidades - error consultando trazabilidades: {e}", level='error', exc_info=True)
27
+ raise ServiceValidationError("Trazabilidades no encontradas", code=400, context={"exception": str(e)})
@@ -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.0
3
+ Version: 0.2.2
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
@@ -22,6 +22,9 @@ src/eco_back/documento/__init__.py
22
22
  src/eco_back/documento/anexos.py
23
23
  src/eco_back/registro/__init__.py
24
24
  src/eco_back/registro/trazabilidad.py
25
+ src/eco_back/utils/__init__.py
26
+ src/eco_back/utils/exceptions.py
27
+ src/eco_back/utils/logger_generico.py
25
28
  tests/test_anexos.py
26
29
  tests/test_api.py
27
30
  tests/test_database.py
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes