457 lines
22 KiB
Python
457 lines
22 KiB
Python
from django.shortcuts import render
|
|
from django.contrib.auth import get_user_model
|
|
from django.utils import timezone
|
|
from django.db.models import F
|
|
from datetime import timedelta
|
|
from rest_framework.views import APIView
|
|
from rest_framework.response import Response
|
|
from rest_framework.permissions import IsAuthenticated
|
|
|
|
from core.permissions import (
|
|
IsSameOrganization,
|
|
IsSameOrganizationDeveloper,
|
|
IsSameOrganizationAndAdmin,
|
|
IsSuperUser
|
|
)
|
|
|
|
from api.organization.models import UsoAlmacenamiento, Organizacion
|
|
from api.record.models import Document
|
|
from api.customs.models import ProcesamientoPedimento
|
|
from api.logger.models import UserActivity, RequestLog
|
|
|
|
from api.logger.mixins import LoggingMixin
|
|
from mixins.filtrado_organizacion import FiltroPorOrganizacionMixin, DocumentosFiltradosMixin
|
|
|
|
from drf_yasg.utils import swagger_auto_schema
|
|
from drf_yasg import openapi
|
|
|
|
from api.logger.models import UserActivity, RequestLog, UserActivity
|
|
|
|
|
|
# Create your views here.
|
|
class DocumentUtilInformation(LoggingMixin, APIView, FiltroPorOrganizacionMixin):
|
|
"""
|
|
View to get the total storage used by the organization and stats of documents added in last 1, 7, and 30 days.
|
|
Permite filtrar por fecha usando los parámetros ?fecha_inicio=YYYY-MM-DD&fecha_fin=YYYY-MM-DD
|
|
"""
|
|
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
|
model = Document
|
|
|
|
my_tags = ['Cards']
|
|
|
|
@swagger_auto_schema(
|
|
operation_description="Get total storage used and document stats. Permite filtrar por fecha de documentos.",
|
|
manual_parameters=[
|
|
openapi.Parameter('fecha_inicio', openapi.IN_QUERY, description="Fecha de inicio (YYYY-MM-DD)", type=openapi.TYPE_STRING),
|
|
openapi.Parameter('fecha_fin', openapi.IN_QUERY, description="Fecha de fin (YYYY-MM-DD)", type=openapi.TYPE_STRING),
|
|
],
|
|
responses={
|
|
200: openapi.Response(
|
|
description="Document stats",
|
|
schema=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
"archivos_ultimas_1_dia": openapi.Schema(type=openapi.TYPE_INTEGER, description="Archivos en el último día"),
|
|
"archivos_ultimos_7_dias": openapi.Schema(type=openapi.TYPE_INTEGER, description="Archivos en los últimos 7 días"),
|
|
"archivos_ultimos_30_dias": openapi.Schema(type=openapi.TYPE_INTEGER, description="Archivos en los últimos 30 días"),
|
|
"archivos_filtrados": openapi.Schema(type=openapi.TYPE_INTEGER, description="Archivos en el rango de fechas")
|
|
}
|
|
),
|
|
examples={
|
|
"application/json": {
|
|
"archivos_ultimas_1_dia": 5,
|
|
"archivos_ultimos_7_dias": 20,
|
|
"archivos_ultimos_30_dias": 50,
|
|
"archivos_filtrados": 10
|
|
}
|
|
}
|
|
)
|
|
}
|
|
)
|
|
|
|
def get_queryset(self):
|
|
return self.get_queryset_filtrado()
|
|
|
|
def get(self, request):
|
|
queryset = self.get_queryset()
|
|
now = timezone.now()
|
|
count_1 = queryset.filter(created_at__gte=now - timedelta(days=1)).count()
|
|
count_7 = queryset.filter(created_at__gte=now - timedelta(days=7)).count()
|
|
count_30 = queryset.filter(created_at__gte=now - timedelta(days=30)).count()
|
|
|
|
fecha_inicio = request.query_params.get('fecha_inicio')
|
|
fecha_fin = request.query_params.get('fecha_fin')
|
|
docs_filtrados = queryset
|
|
|
|
if fecha_inicio:
|
|
docs_filtrados = docs_filtrados.filter(created_at__gte=fecha_inicio)
|
|
if fecha_fin:
|
|
docs_filtrados = docs_filtrados.filter(created_at__lte=fecha_fin)
|
|
count_filtrados = docs_filtrados.count()
|
|
return Response({
|
|
"archivos_ultimas_1_dia": count_1,
|
|
"archivos_ultimos_7_dias": count_7,
|
|
"archivos_ultimos_30_dias": count_30,
|
|
"archivos_filtrados": count_filtrados
|
|
})
|
|
|
|
class ViewPedimentoServicesUtilInformation(LoggingMixin, APIView, FiltroPorOrganizacionMixin):
|
|
"""
|
|
View para obtener información de uso de servicios relacionados con pedimentos.
|
|
Devuelve la cantidad de procesos por estado (1: espera, 2: proceso, 3: finalizado, 4: error) para la organización.
|
|
"""
|
|
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
|
model = Document
|
|
my_tags = ['Cards']
|
|
|
|
@swagger_auto_schema(
|
|
operation_description="Get services stats. Permite filtrar por fecha de procesos.",
|
|
manual_parameters=[
|
|
openapi.Parameter('fecha_inicio', openapi.IN_QUERY, description="Fecha de inicio (YYYY-MM-DD)", type=openapi.TYPE_STRING),
|
|
openapi.Parameter('fecha_fin', openapi.IN_QUERY, description="Fecha de fin (YYYY-MM-DD)", type=openapi.TYPE_STRING),
|
|
],
|
|
responses={
|
|
200: openapi.Response(
|
|
description="Services stats",
|
|
schema=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
"en_espera": openapi.Schema(type=openapi.TYPE_INTEGER, description="Cantidad de procesos en espera"),
|
|
"en_proceso": openapi.Schema(type=openapi.TYPE_INTEGER, description="Cantidad de procesos en proceso"),
|
|
"finalizados": openapi.Schema(type=openapi.TYPE_INTEGER, description="Cantidad de procesos finalizados"),
|
|
"con_error": openapi.Schema(type=openapi.TYPE_INTEGER, description="Cantidad de procesos con error"),
|
|
"procesos_filtrados": openapi.Schema(type=openapi.TYPE_INTEGER, description="Procesos en el rango de fechas")
|
|
}
|
|
),
|
|
examples={
|
|
"application/json": {
|
|
"en_espera": 1,
|
|
"en_proceso": 2,
|
|
"finalizados": 3,
|
|
"con_error": 4,
|
|
"procesos_filtrados": 5
|
|
}
|
|
}
|
|
)
|
|
}
|
|
)
|
|
|
|
def get_queryset(self):
|
|
if not self.request.user.is_authenticated or not hasattr(self.request.user, 'organizacion'):
|
|
return None
|
|
|
|
# Si es super usuario, devuelve todos los procesos
|
|
if self.request.user.is_superuser:
|
|
return ProcesamientoPedimento.objects.all()
|
|
|
|
# Si es Administrador de la organizacion devuelve todos los servicios de la organizacion
|
|
if self.request.user.is_authenticated and self.request.user.groups.filter(name='admin').exists() and self.request.user.groups.filter(name='Agente Aduanal').exists():
|
|
return ProcesamientoPedimento.objects.filter(pedimento__organizacion=self.request.user.organizacion)
|
|
|
|
# Si es Desarrollador de la organizacion devuelve todos los servicios de la organizacion
|
|
if self.request.user.is_authenticated and self.request.user.groups.filter(name='developer').exists() and self.request.user.groups.filter(name='Agente Aduanal').exists():
|
|
return self.request.user.organizacion.procesamiento_pedimentos.all()
|
|
|
|
if self.request.user.is_authenticated and self.request.user.groups.filter(name='user').exists() and self.request.user.groups.filter(name='Agente Aduanal').exists():
|
|
return self.request.user.organizacion.procesamiento_pedimentos.all()
|
|
|
|
# Si es importador de la organizacion, devuelve los servicios relacionados con sus pedimentos
|
|
if self.request.user.is_authenticated and self.request.user.groups.filter(name='importador').exists() and self.request.user.is_importador and self.request.user.groups.filter(name='user').exists():
|
|
return self.request.user.organizacion.procesamiento_pedimentos.filter(pedimento__contribuyente=self.request.user.rfc)
|
|
|
|
|
|
|
|
# Si es parte de una organización, filtrar por esa organización
|
|
return ProcesamientoPedimento.objects.filter(pedimento__organizacion=self.request.user.organizacion)
|
|
|
|
def get(self, request):
|
|
queryset = self.get_queryset()
|
|
if queryset is None:
|
|
return Response({"error": "Usuario no autenticado o sin organización"}, status=401)
|
|
en_espera = queryset.filter(estado=1).count()
|
|
en_proceso = queryset.filter(estado=2).count()
|
|
finalizados = queryset.filter(estado=3).count()
|
|
con_error = queryset.filter(estado=4).count()
|
|
fecha_inicio = request.query_params.get('fecha_inicio')
|
|
fecha_fin = request.query_params.get('fecha_fin')
|
|
procesos_filtrados = queryset
|
|
if fecha_inicio:
|
|
procesos_filtrados = procesos_filtrados.filter(created_at__gte=fecha_inicio)
|
|
if fecha_fin:
|
|
procesos_filtrados = procesos_filtrados.filter(created_at__lte=fecha_fin)
|
|
count_filtrados = procesos_filtrados.count()
|
|
return Response({
|
|
"en_espera": en_espera,
|
|
"en_proceso": en_proceso,
|
|
"finalizados": finalizados,
|
|
"con_error": con_error,
|
|
"procesos_filtrados": count_filtrados
|
|
})
|
|
|
|
class UserActivityAnalysis(LoggingMixin, APIView, FiltroPorOrganizacionMixin):
|
|
"""
|
|
Endpoint para análisis de actividades de usuario.
|
|
Devuelve el conteo de acciones por tipo y los 5 usuarios más activos.
|
|
"""
|
|
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
|
|
|
model = UserActivity
|
|
|
|
my_tags = ['Cards']
|
|
|
|
@swagger_auto_schema(
|
|
operation_description="Get analysis of user activities. Permite filtrar por fecha de actividades.",
|
|
manual_parameters=[
|
|
openapi.Parameter('fecha_inicio', openapi.IN_QUERY, description="Fecha de inicio (YYYY-MM-DD)", type=openapi.TYPE_STRING),
|
|
openapi.Parameter('fecha_fin', openapi.IN_QUERY, description="Fecha de fin (YYYY-MM-DD)", type=openapi.TYPE_STRING),
|
|
],
|
|
responses={
|
|
200: openapi.Response(
|
|
description="User activity analysis",
|
|
schema=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
"actions_count": openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
additional_properties=openapi.Schema(type=openapi.TYPE_INTEGER, description="Cantidad por acción")
|
|
),
|
|
"top_users": openapi.Schema(
|
|
type=openapi.TYPE_ARRAY,
|
|
items=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
"username": openapi.Schema(type=openapi.TYPE_STRING),
|
|
"activity_count": openapi.Schema(type=openapi.TYPE_INTEGER)
|
|
}
|
|
),
|
|
description="Top 5 usuarios más activos"
|
|
),
|
|
"actividades_filtradas": openapi.Schema(type=openapi.TYPE_INTEGER, description="Actividades en el rango de fechas")
|
|
}
|
|
),
|
|
examples={
|
|
"application/json": {
|
|
"actions_count": {
|
|
"login": 20,
|
|
"logout": 18,
|
|
"create": 15,
|
|
"update": 10,
|
|
"delete": 5,
|
|
"view": 30,
|
|
"search": 12,
|
|
"export": 3,
|
|
"import": 2
|
|
},
|
|
"top_users": [
|
|
{"username": "admin", "activity_count": 25},
|
|
{"username": "user1", "activity_count": 20}
|
|
],
|
|
"actividades_filtradas": 10
|
|
}
|
|
}
|
|
)
|
|
}
|
|
)
|
|
def get_queryset(self):
|
|
return self.get_queryset_filtrado()
|
|
|
|
def get(self, request):
|
|
queryset = self.get_queryset()
|
|
User = get_user_model()
|
|
actions = [a[0] for a in UserActivity.ACTIONS]
|
|
actions_count = {a: queryset.filter(action=a).count() for a in actions}
|
|
# Top 5 usuarios más activos
|
|
top_users = []
|
|
from django.db.models import Count
|
|
top_users_qs = queryset.values('user').annotate(activity_count=Count('id')).order_by('-activity_count')[:5]
|
|
for entry in top_users_qs:
|
|
try:
|
|
user_obj = User.objects.get(pk=entry['user'])
|
|
top_users.append({"username": user_obj.username, "activity_count": entry['activity_count']})
|
|
except User.DoesNotExist:
|
|
continue
|
|
fecha_inicio = request.query_params.get('fecha_inicio')
|
|
fecha_fin = request.query_params.get('fecha_fin')
|
|
actividades_filtradas = queryset
|
|
if fecha_inicio:
|
|
actividades_filtradas = actividades_filtradas.filter(timestamp__gte=fecha_inicio)
|
|
if fecha_fin:
|
|
actividades_filtradas = actividades_filtradas.filter(timestamp__lte=fecha_fin)
|
|
count_filtrados = actividades_filtradas.count()
|
|
return Response({
|
|
"actions_count": actions_count,
|
|
"top_users": top_users,
|
|
"actividades_filtradas": count_filtrados
|
|
})
|
|
|
|
class RequestLogAnalysis(LoggingMixin, APIView, FiltroPorOrganizacionMixin):
|
|
"""
|
|
Endpoint para análisis de logs de peticiones.
|
|
Devuelve el conteo por método, los paths más solicitados y el promedio de tiempo de respuesta.
|
|
"""
|
|
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
|
model = RequestLog
|
|
|
|
my_tags = ['Cards']
|
|
|
|
@swagger_auto_schema(
|
|
operation_description="Get analysis of request logs. Permite filtrar por fecha de logs.",
|
|
manual_parameters=[
|
|
openapi.Parameter('fecha_inicio', openapi.IN_QUERY, description="Fecha de inicio (YYYY-MM-DD)", type=openapi.TYPE_STRING),
|
|
openapi.Parameter('fecha_fin', openapi.IN_QUERY, description="Fecha de fin (YYYY-MM-DD)", type=openapi.TYPE_STRING),
|
|
],
|
|
responses={
|
|
200: openapi.Response(
|
|
description="Request log analysis",
|
|
schema=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
"methods_count": openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
additional_properties=openapi.Schema(type=openapi.TYPE_INTEGER, description="Cantidad por método")
|
|
),
|
|
"top_paths": openapi.Schema(
|
|
type=openapi.TYPE_ARRAY,
|
|
items=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
"path": openapi.Schema(type=openapi.TYPE_STRING),
|
|
"count": openapi.Schema(type=openapi.TYPE_INTEGER)
|
|
}
|
|
),
|
|
description="Top 5 paths más solicitados"
|
|
),
|
|
"avg_response_time": openapi.Schema(type=openapi.TYPE_NUMBER, format='float', description="Promedio de tiempo de respuesta (ms)"),
|
|
"logs_filtrados": openapi.Schema(type=openapi.TYPE_INTEGER, description="Logs en el rango de fechas")
|
|
}
|
|
),
|
|
examples={
|
|
"application/json": {
|
|
"methods_count": {
|
|
"GET": 120,
|
|
"POST": 30,
|
|
"PUT": 10,
|
|
"DELETE": 5
|
|
},
|
|
"top_paths": [
|
|
{"path": "/api/v1/record/documents/", "count": 50},
|
|
{"path": "/api/v1/customs/pedimentos/", "count": 40}
|
|
],
|
|
"avg_response_time": 120.5,
|
|
"logs_filtrados": 15
|
|
}
|
|
}
|
|
)
|
|
}
|
|
)
|
|
def get_queryset(self):
|
|
return self.get_queryset_filtrado()
|
|
|
|
def get(self, request):
|
|
queryset = self.get_queryset()
|
|
from django.db.models import Count, Avg
|
|
from api.logger.models import RequestLog
|
|
methods = [m[0] for m in RequestLog.METHODS]
|
|
methods_count = {m: queryset.filter(method=m).count() for m in methods}
|
|
top_paths_qs = queryset.values('path').annotate(count=Count('id')).order_by('-count')[:5]
|
|
top_paths = [{"path": entry['path'], "count": entry['count']} for entry in top_paths_qs]
|
|
avg_response_time = queryset.aggregate(avg=Avg('response_time'))['avg'] or 0.0
|
|
fecha_inicio = request.query_params.get('fecha_inicio')
|
|
fecha_fin = request.query_params.get('fecha_fin')
|
|
logs_filtrados = queryset
|
|
if fecha_inicio:
|
|
logs_filtrados = logs_filtrados.filter(timestamp__gte=fecha_inicio)
|
|
if fecha_fin:
|
|
logs_filtrados = logs_filtrados.filter(timestamp__lte=fecha_fin)
|
|
count_filtrados = logs_filtrados.count()
|
|
return Response({
|
|
"methods_count": methods_count,
|
|
"top_paths": top_paths,
|
|
"avg_response_time": round(avg_response_time, 2),
|
|
"logs_filtrados": count_filtrados
|
|
})
|
|
|
|
class LastDocumentView(LoggingMixin, APIView, DocumentosFiltradosMixin):
|
|
"""
|
|
View que obtiene los ultimos 10 documentos agregados.
|
|
Permite filtrar por fecha usando los parámetros ?fecha_inicio=YYYY-MM-DD&fecha_fin=YYYY-MM-DD
|
|
"""
|
|
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
|
model = Document
|
|
|
|
my_tags = ['Cards']
|
|
|
|
@swagger_auto_schema(
|
|
operation_description="Obtiene los últimos 10 documentos agregados. Permite filtrar por fecha de creación.",
|
|
manual_parameters=[
|
|
openapi.Parameter('fecha_inicio', openapi.IN_QUERY, description="Fecha de inicio (YYYY-MM-DD)", type=openapi.TYPE_STRING),
|
|
openapi.Parameter('fecha_fin', openapi.IN_QUERY, description="Fecha de fin (YYYY-MM-DD)", type=openapi.TYPE_STRING),
|
|
],
|
|
responses={
|
|
200: openapi.Response(
|
|
description="Lista de los últimos 10 documentos",
|
|
schema=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
"documentos": openapi.Schema(
|
|
type=openapi.TYPE_ARRAY,
|
|
items=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
"id": openapi.Schema(type=openapi.TYPE_STRING, format="uuid"),
|
|
"archivo": openapi.Schema(type=openapi.TYPE_STRING, description="Ruta del archivo"),
|
|
"extension": openapi.Schema(type=openapi.TYPE_STRING),
|
|
"size": openapi.Schema(type=openapi.TYPE_INTEGER),
|
|
"created_at": openapi.Schema(type=openapi.TYPE_STRING, format="date-time"),
|
|
"updated_at": openapi.Schema(type=openapi.TYPE_STRING, format="date-time"),
|
|
"organizacion": openapi.Schema(type=openapi.TYPE_STRING),
|
|
"pedimento": openapi.Schema(type=openapi.TYPE_STRING)
|
|
}
|
|
),
|
|
description="Últimos 10 documentos agregados"
|
|
),
|
|
"total_filtrados": openapi.Schema(type=openapi.TYPE_INTEGER, description="Total de documentos en el rango de fechas")
|
|
}
|
|
),
|
|
examples={
|
|
"application/json": {
|
|
"documentos": [
|
|
{"id": "b7e6c8e2-1a2b-4c3d-8e9f-123456789abc", "archivo": "documents/doc1.pdf", "extension": "pdf", "size": 123456, "created_at": "2025-07-14T10:00:00Z", "updated_at": "2025-07-14T10:00:00Z", "organizacion": "Org1", "pedimento": "Ped1"}
|
|
],
|
|
"total_filtrados": 1
|
|
}
|
|
}
|
|
)
|
|
}
|
|
)
|
|
def get_queryset(self):
|
|
return self.get_queryset_filtrado_por_organizacion()
|
|
|
|
def get(self, request):
|
|
queryset = self.get_queryset()
|
|
fecha_inicio = request.query_params.get('fecha_inicio')
|
|
fecha_fin = request.query_params.get('fecha_fin')
|
|
documentos = queryset
|
|
if fecha_inicio:
|
|
documentos = documentos.filter(created_at__gte=fecha_inicio)
|
|
if fecha_fin:
|
|
documentos = documentos.filter(created_at__lte=fecha_fin)
|
|
total_filtrados = documentos.count()
|
|
ultimos = documentos[:10]
|
|
docs_serializados = []
|
|
for doc in ultimos:
|
|
docs_serializados.append({
|
|
"id": str(doc.id),
|
|
"archivo": doc.archivo.name if doc.archivo else '',
|
|
"extension": doc.extension,
|
|
"size": doc.size,
|
|
"created_at": doc.created_at.isoformat() if doc.created_at else '',
|
|
"updated_at": doc.updated_at.isoformat() if doc.updated_at else '',
|
|
"organizacion": str(doc.organizacion) if doc.organizacion else '',
|
|
"pedimento": str(doc.pedimento) if doc.pedimento else ''
|
|
})
|
|
return Response({
|
|
"documentos": docs_serializados,
|
|
"total_filtrados": total_filtrados
|
|
})
|
|
|