Showing
10 changed files
with
227 additions
and
5 deletions
@@ -4,6 +4,20 @@ from rest_framework.decorators import api_view | @@ -4,6 +4,20 @@ from rest_framework.decorators import api_view | ||
4 | 4 | ||
5 | from django.conf import settings | 5 | from django.conf import settings |
6 | 6 | ||
7 | +import datetime | ||
8 | + | ||
9 | +from actstream.models import Action | ||
10 | +from django.http import Http404 | ||
11 | +from django_filters.rest_framework import DjangoFilterBackend | ||
12 | +from rest_framework import filters | ||
13 | +from rest_framework.generics import get_object_or_404 | ||
14 | +from rest_framework.permissions import IsAuthenticated | ||
15 | +from rest_framework_json_api.views import ReadOnlyModelViewSet | ||
16 | + | ||
17 | +from core.permissions import CustomModelPermissions | ||
18 | +from core.serializers import ActionSerializer | ||
19 | +from usuario.models import Usuario | ||
20 | + | ||
7 | 21 | ||
8 | @api_view(['POST']) | 22 | @api_view(['POST']) |
9 | def recaptcha(request): | 23 | def recaptcha(request): |
@@ -16,3 +30,39 @@ def recaptcha(request): | @@ -16,3 +30,39 @@ def recaptcha(request): | ||
16 | ) | 30 | ) |
17 | 31 | ||
18 | return Response({'captcha': r.json()}) | 32 | return Response({'captcha': r.json()}) |
33 | + | ||
34 | + | ||
35 | +class AuditoriaViewSet(ReadOnlyModelViewSet): | ||
36 | + queryset = Action.objects.all() | ||
37 | + permission_classes = (IsAuthenticated, CustomModelPermissions) | ||
38 | + serializer_class = ActionSerializer | ||
39 | + filter_backends = (DjangoFilterBackend, filters.OrderingFilter) | ||
40 | + ordering = '-timestamp' | ||
41 | + # | ||
42 | + # def get_queryset(self): | ||
43 | + # queryset = super().get_queryset() | ||
44 | + # | ||
45 | + # if not self.action == 'list': | ||
46 | + # return queryset | ||
47 | + # | ||
48 | + # # en el caso de que la accion sea listar, controlar que se filtre por fecha obligatoriamente | ||
49 | + # usuario_id = self.request.GET.get('u_id', None) | ||
50 | + # fecha_desde = self.request.GET.get('fecha_desde', None) | ||
51 | + # fecha_hasta = self.request.GET.get('fecha_hasta', None) | ||
52 | + # | ||
53 | + # if not fecha_desde: | ||
54 | + # return queryset.none() | ||
55 | + # | ||
56 | + # if not fecha_hasta or fecha_hasta < fecha_desde: | ||
57 | + # fecha_hasta = datetime.datetime.now() | ||
58 | + # | ||
59 | + # if usuario_id: | ||
60 | + # try: | ||
61 | + # usuario = get_object_or_404(Usuario, id=usuario_id) | ||
62 | + # queryset = usuario.actor_actions.public(timestamp__date__range=(fecha_desde, fecha_hasta)) | ||
63 | + # except Http404: | ||
64 | + # return queryset.none() | ||
65 | + # else: | ||
66 | + # queryset = Action.objects.public(timestamp__date__range=(fecha_desde, fecha_hasta)) | ||
67 | + # | ||
68 | + # return queryset |
project/apps/core/constants.py
0 → 100644
project/apps/core/mixins.py
0 → 100644
1 | +from typing import Optional | ||
2 | +from actstream import action | ||
3 | +from actstream.models import Action | ||
4 | +from django.contrib.contenttypes.models import ContentType | ||
5 | +from rest_framework.response import Response | ||
6 | +from rest_framework import status | ||
7 | + | ||
8 | +from core.constants import iniciado | ||
9 | +from core.serializers import ActionSerializer | ||
10 | + | ||
11 | + | ||
12 | +# class FiltroObligatorioMixin(object): | ||
13 | +# nombre_filtro_obligatorio: Optional[str] = None | ||
14 | +# | ||
15 | +# def get_queryset(self): | ||
16 | +# queryset = super().get_queryset() | ||
17 | +# | ||
18 | +# if not self.action == 'list': | ||
19 | +# return queryset | ||
20 | +# | ||
21 | +# # en el caso de que la accion sea listar, controlar que vengan los datos del filtro indicado | ||
22 | +# filtro = self.request.GET.get(self.get_nombre_filtro_obligatorio(), None) | ||
23 | +# if filtro: | ||
24 | +# return queryset | ||
25 | +# | ||
26 | +# return queryset.none() | ||
27 | +# | ||
28 | +# def get_nombre_filtro_obligatorio(self): | ||
29 | +# assert self.nombre_filtro_obligatorio is not None, ( | ||
30 | +# "Debe definir el atributo nombre_filtro_obligatorio a la clase '%s'" | ||
31 | +# % self.__class__.__name__ | ||
32 | +# ) | ||
33 | +# | ||
34 | +# return self.nombre_filtro_obligatorio | ||
35 | + | ||
36 | + | ||
37 | +class AuditoriaMixin: | ||
38 | + | ||
39 | + def create(self, request, *args, **kwargs): | ||
40 | + serializer = self.get_serializer(data=request.data) | ||
41 | + serializer.is_valid(raise_exception=True) | ||
42 | + self.perform_create(serializer) | ||
43 | + action_object = serializer.instance | ||
44 | + | ||
45 | + self.registrar_accion(usuario=self.request.user, verb=iniciado, instance=serializer.instance, | ||
46 | + data=serializer.data) | ||
47 | + headers = self.get_success_headers(serializer.data) | ||
48 | + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) | ||
49 | + | ||
50 | + def perform_update(self, serializer): | ||
51 | + serializer_modificado = self.get_serializer(data=self.request.data) | ||
52 | + serializer_modificado.is_valid() | ||
53 | + serializer_accion = self.get_serializer(instance=serializer.instance) | ||
54 | + | ||
55 | + data = {'data_inicial': serializer_accion.data, 'data_modificada': serializer_modificado.data} | ||
56 | + | ||
57 | + self.registrar_accion(usuario=self.request.user, verb=serializer_modificado.data["estado"], instance=serializer.instance, | ||
58 | + data=data) | ||
59 | + serializer.save() | ||
60 | + | ||
61 | + @staticmethod | ||
62 | + def registrar_accion(usuario, verb, instance, data=None, target=None): | ||
63 | + action.send(usuario, verb=verb, action_object=instance, target=target, data=data) | ||
64 | + | ||
65 | + @staticmethod | ||
66 | + def obtener_historial_acciones(content_type, action_objects_id, request): | ||
67 | + content_type = ContentType.objects.get(model=content_type) | ||
68 | + historial_actividades = Action.objects.filter( | ||
69 | + action_object_object_id__in=action_objects_id, | ||
70 | + action_object_content_type_id=content_type.id | ||
71 | + ) | ||
72 | + serializer = ActionSerializer(historial_actividades, many=True, context={'request': request}) | ||
73 | + return serializer |
project/apps/core/serializers.py
0 → 100644
1 | +from actstream.models import Action | ||
2 | +from rest_framework import serializers | ||
3 | + | ||
4 | +from usuario.serializers import UsuarioSerializer | ||
5 | +# from persona.serializers import ( | ||
6 | +# DomicilioSerializer, | ||
7 | +# FotoComplementariaPersonaSerializer, | ||
8 | +# IdentificacionCromaticaSerializer, | ||
9 | +# PersonaSerializer, | ||
10 | +# VinculoFamiliarSerializer, | ||
11 | +# HuellaDactilarSerializer, | ||
12 | +# VinculoFamiliarSinIdentificarSerializer, | ||
13 | +# IdentificacionDecadactilarSerializer, AutorizacionMenorSerializer, | ||
14 | +# ) | ||
15 | +from usuario.models import Usuario | ||
16 | + | ||
17 | + | ||
18 | +class ActivityGenericRelatedField(serializers.Field): | ||
19 | + """ | ||
20 | + DRF Serializer field that serializers GenericForeignKey fields on the :class:`~activity.models.Action` | ||
21 | + of known model types to their respective ActionSerializer implementation. | ||
22 | + """ | ||
23 | + | ||
24 | + def to_representation(self, value): | ||
25 | + from edicto.serializers import EdictoSerializer, PrecioSerializer | ||
26 | + | ||
27 | + MAPEO_SERIALIZADORES_POR_MODELO = { | ||
28 | + "Usuario": UsuarioSerializer, | ||
29 | + "Edicto": EdictoSerializer, | ||
30 | + "Precio": PrecioSerializer | ||
31 | + } | ||
32 | + | ||
33 | + nombre_modelo = type(value).__name__ | ||
34 | + serializer_cls = MAPEO_SERIALIZADORES_POR_MODELO.get(nombre_modelo, None) | ||
35 | + | ||
36 | + if serializer_cls: | ||
37 | + # se genera un nuevo diccionario donde se le suma otro diccionario conteniendo el elemento "type" | ||
38 | + data = {**{'type': nombre_modelo}, **{'id': value.id}, **serializer_cls(value, context=self.context).data} | ||
39 | + else: | ||
40 | + data = str(value) | ||
41 | + | ||
42 | + return data | ||
43 | + | ||
44 | + | ||
45 | +class ActorSerializer(serializers.ModelSerializer): | ||
46 | + class Meta: | ||
47 | + model = Usuario | ||
48 | + fields = ( | ||
49 | + 'username', | ||
50 | + 'first_name', | ||
51 | + 'last_name', | ||
52 | + 'cuil' | ||
53 | + ) | ||
54 | + | ||
55 | + | ||
56 | +class ActionSerializer(serializers.Serializer): | ||
57 | + """ | ||
58 | + DRF serializer for :class:`~activity.models.Action`. | ||
59 | + """ | ||
60 | + actor = ActorSerializer(read_only=True) | ||
61 | + verb = serializers.CharField(read_only=True) | ||
62 | + action_object = ActivityGenericRelatedField(read_only=True) | ||
63 | + target = ActivityGenericRelatedField(read_only=True) | ||
64 | + timestamp = serializers.DateTimeField(read_only=True) | ||
65 | + data = serializers.DictField(read_only=True) | ||
66 | + | ||
67 | + class Meta: | ||
68 | + model = Action | ||
69 | + fields = ('id', 'actor', 'verb', 'action_object', 'target', 'timestamp', 'data') |
@@ -2,13 +2,15 @@ from django_filters.rest_framework import DjangoFilterBackend | @@ -2,13 +2,15 @@ from django_filters.rest_framework import DjangoFilterBackend | ||
2 | from rest_framework import viewsets, filters, mixins | 2 | from rest_framework import viewsets, filters, mixins |
3 | from rest_framework.permissions import IsAuthenticated | 3 | from rest_framework.permissions import IsAuthenticated |
4 | 4 | ||
5 | +from core.mixins import AuditoriaMixin | ||
6 | + | ||
5 | from .filters import EdictoFilter, PrecioFilter | 7 | from .filters import EdictoFilter, PrecioFilter |
6 | from .models import Edicto, Precio | 8 | from .models import Edicto, Precio |
7 | from .permissions import IsAdminOrAuthorized | 9 | from .permissions import IsAdminOrAuthorized |
8 | -from .serializer import EdictoSerializer, PrecioSerializer | 10 | +from .serializers import EdictoSerializer, PrecioSerializer |
9 | 11 | ||
10 | 12 | ||
11 | -class EdictoViewSet(mixins.CreateModelMixin, | 13 | +class EdictoViewSet(AuditoriaMixin, mixins.CreateModelMixin, |
12 | mixins.RetrieveModelMixin, | 14 | mixins.RetrieveModelMixin, |
13 | mixins.UpdateModelMixin, | 15 | mixins.UpdateModelMixin, |
14 | mixins.ListModelMixin, | 16 | mixins.ListModelMixin, |
@@ -24,7 +26,7 @@ class EdictoViewSet(mixins.CreateModelMixin, | @@ -24,7 +26,7 @@ class EdictoViewSet(mixins.CreateModelMixin, | ||
24 | lookup_field = 'uuid' | 26 | lookup_field = 'uuid' |
25 | 27 | ||
26 | 28 | ||
27 | -class PrecioViewSet(viewsets.ReadOnlyModelViewSet): | 29 | +class PrecioViewSet(AuditoriaMixin, viewsets.ReadOnlyModelViewSet): |
28 | serializer_class = PrecioSerializer | 30 | serializer_class = PrecioSerializer |
29 | permission_classes = [IsAuthenticated, IsAdminOrAuthorized] | 31 | permission_classes = [IsAuthenticated, IsAdminOrAuthorized] |
30 | filter_backends = (DjangoFilterBackend, filters.OrderingFilter) | 32 | filter_backends = (DjangoFilterBackend, filters.OrderingFilter) |
@@ -4,3 +4,13 @@ from django.apps import AppConfig | @@ -4,3 +4,13 @@ from django.apps import AppConfig | ||
4 | class EdictoConfig(AppConfig): | 4 | class EdictoConfig(AppConfig): |
5 | default_auto_field = 'django.db.models.BigAutoField' | 5 | default_auto_field = 'django.db.models.BigAutoField' |
6 | name = 'edicto' | 6 | name = 'edicto' |
7 | + | ||
8 | + def ready(self): | ||
9 | + from actstream import registry | ||
10 | + registry.register( | ||
11 | + self.get_model('Edicto'), | ||
12 | + self.get_model('Precio'), | ||
13 | + ) | ||
14 | + | ||
15 | + | ||
16 | +default_app_config = 'edicto.apps.EdictoConfig' |
@@ -4,6 +4,8 @@ from organismo import api as organismo_api | @@ -4,6 +4,8 @@ from organismo import api as organismo_api | ||
4 | from usuario import api as usuario_api | 4 | from usuario import api as usuario_api |
5 | from edicto.api import EdictoViewSet | 5 | from edicto.api import EdictoViewSet |
6 | from edicto import api as edicto_api | 6 | from edicto import api as edicto_api |
7 | +from core import api as core_api | ||
8 | + | ||
7 | # Define routes | 9 | # Define routes |
8 | router = routers.DefaultRouter() | 10 | router = routers.DefaultRouter() |
9 | 11 | ||
@@ -11,5 +13,4 @@ router.register(prefix='usuario', viewset=usuario_api.UsuarioViewSet) | @@ -11,5 +13,4 @@ router.register(prefix='usuario', viewset=usuario_api.UsuarioViewSet) | ||
11 | router.register(prefix='organismo', viewset=organismo_api.OrganismoViewSet) | 13 | router.register(prefix='organismo', viewset=organismo_api.OrganismoViewSet) |
12 | router.register(r'edicto', EdictoViewSet, basename='edicto') | 14 | router.register(r'edicto', EdictoViewSet, basename='edicto') |
13 | router.register(prefix='precio', viewset=edicto_api.PrecioViewSet) | 15 | router.register(prefix='precio', viewset=edicto_api.PrecioViewSet) |
14 | - | ||
15 | - | 16 | +router.register(prefix='auditoria', viewset=core_api.AuditoriaViewSet) |
@@ -52,6 +52,7 @@ THIRD_PARTY_APPS = ( | @@ -52,6 +52,7 @@ THIRD_PARTY_APPS = ( | ||
52 | 'django_filters', | 52 | 'django_filters', |
53 | 'corsheaders', | 53 | 'corsheaders', |
54 | 'oauth2_provider', | 54 | 'oauth2_provider', |
55 | + 'actstream', | ||
55 | ) | 56 | ) |
56 | 57 | ||
57 | PROJECT_APPS = ( | 58 | PROJECT_APPS = ( |
@@ -61,6 +62,8 @@ PROJECT_APPS = ( | @@ -61,6 +62,8 @@ PROJECT_APPS = ( | ||
61 | 'edicto', | 62 | 'edicto', |
62 | ) | 63 | ) |
63 | 64 | ||
65 | +SITE_ID = 1 | ||
66 | + | ||
64 | INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + PROJECT_APPS | 67 | INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + PROJECT_APPS |
65 | 68 | ||
66 | MIDDLEWARE = ( | 69 | MIDDLEWARE = ( |
@@ -192,3 +195,7 @@ AUTHENTICATION_BACKENDS = ( | @@ -192,3 +195,7 @@ AUTHENTICATION_BACKENDS = ( | ||
192 | 195 | ||
193 | # Secret Key para Captcha. | 196 | # Secret Key para Captcha. |
194 | SECRET_KEY_CAPTCHA = env.str('SECRET_KEY_CAPTCHA', default="") | 197 | SECRET_KEY_CAPTCHA = env.str('SECRET_KEY_CAPTCHA', default="") |
198 | + | ||
199 | +ACTSTREAM_SETTINGS = { | ||
200 | + 'USE_JSONFIELD': True, | ||
201 | +} |
-
Please register or login to post a comment