Enzo Yair

test-enpoint

from rest_framework import viewsets
import json
from rest_framework import viewsets,mixins
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import action
from rest_framework import status
from rest_framework.response import Response
from rest_framework_api_key.permissions import HasAPIKey
from datetime import datetime
from .models import Evento
from .serializers import EventoSerializer
from .service import GenerarFechas
class EventoViewSets(viewsets.ReadOnlyModelViewSet):
class EventoViewSets(
mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet,
):
queryset = Evento.objects.all().order_by('id')
serializer_class = EventoSerializer
permission_classes = [IsAuthenticated,]
permission_classes = [HasAPIKey | IsAuthenticated]
lookup_field = 'id'
@action(methods=['POST'], detail=False, url_path='obtener-mes')
def obtener_mes(self, request):
mes_query = request.data.get('mes', None)
if mes_query is None:
return Response(
data={
'error':
'El mes ingresado no es válido. '
'Por favor, ingrese el mes correspondiente a la fecha actual o posterior.'
},
status=status.HTTP_400_BAD_REQUEST
)
eventos = Evento.objects.filter(fecha_inicio__month=mes_query)
evento_json = []
for evento in eventos:
generar_fechas = GenerarFechas(evento=evento, mes=mes_query)
generar_fechas.procesar_fechas_evento() # Cambiado a 'procesar_fechas_evento()'
evento_json.append(generar_fechas.obtener_datos_evento())
return Response(data=evento_json, status=status.HTTP_200_OK)
... ...
DIAS_SEMANA = (
(1, 'Lunes'),
(2, 'Martes'),
(3, 'Miércoles'),
(4, 'Jueves'),
(5, 'Viernes'),
(6, 'Sábado'),
(7, 'Domingo'),
)
... ...
# Generated by Django 4.2.9 on 2024-10-08 01:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evento', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='fechaevento',
name='dia_evento',
),
migrations.AddField(
model_name='fechaevento',
name='dias',
field=models.DateField(blank=True, editable=False, null=True),
),
migrations.AddField(
model_name='fechaevento',
name='duracion_evento',
field=models.IntegerField(choices=[(1, 'Lunes'), (2, 'Martes'), (3, 'Miércoles'), (4, 'Jueves'), (5, 'Viernes'), (6, 'Sábado'), (7, 'Domingo')], default=1, verbose_name='Días de la semana'),
preserve_default=False,
),
]
... ...
... ... @@ -3,6 +3,7 @@ from django.db import models
from django.core.validators import FileExtensionValidator
from organismo.models import Organismo, Dependencia
from .constant import DIAS_SEMANA
# Create your models here.
... ... @@ -62,8 +63,8 @@ class FechaEvento(models.Model):
class Meta:
verbose_name = 'Fecha del Eventos'
verbose_name_plural = 'Fechas del Eventos'
dia_evento = models.DateField(verbose_name='Días del evento')
duracion_evento = models.IntegerField(choices=DIAS_SEMANA, blank=False, verbose_name='Días de la semana')
dias = models.DateField(editable=False, blank=True, null=True)
def __str__(self):
return f'{self.dia_evento}'
\ No newline at end of file
return f'{self.duracion_evento}'
... ...
import json
import calendar
import pytz
from django.core.serializers.json import DjangoJSONEncoder
from datetime import datetime, timedelta, time
from .models import Evento, FechaEvento
class GenerarFechas:
def __init__(self, evento: Evento, mes: int):
self.fechas_generadas = {}
self.evento = evento
self.mes = mes
def obtener_rango_del_mes(self):
"""Obtiene el rango de fechas para el mes específico."""
year = datetime.now().year
self.mes = int(self.mes)
primer_dia, ultimo_dia = calendar.monthrange(year, self.mes)
fecha_inicio_mes = datetime(year, self.mes, 1)
fecha_fin_mes = datetime(year, self.mes, ultimo_dia)
return fecha_inicio_mes, fecha_fin_mes
def ajustar_fechas_a_rango_mes(self):
"""Ajusta las fechas del evento al rango del mes especificado."""
zona_horaria = pytz.timezone('America/Argentina/Buenos_Aires')
fecha_inicio_evento = self.combinar_fecha_hora(self.evento.fecha_inicio, self.evento.hora_inicio, zona_horaria)
fecha_fin_evento = self.combinar_fecha_hora(self.evento.fecha_final, self.evento.hora_fin, zona_horaria)
fecha_inicio_mes, fecha_fin_mes = self.obtener_rango_del_mes()
fecha_inicio_evento = max(fecha_inicio_evento, zona_horaria.localize(fecha_inicio_mes))
fecha_fin_evento = min(fecha_fin_evento, zona_horaria.localize(fecha_fin_mes))
return fecha_inicio_evento, fecha_fin_evento
@staticmethod
def combinar_fecha_hora(fecha, hora, zona_horaria):
"""Combina la fecha y hora en un solo objeto datetime."""
if fecha is None or hora is None:
return None
fecha_hora = datetime.combine(fecha, hora)
return zona_horaria.localize(fecha_hora) if fecha_hora.tzinfo is None else fecha_hora
def obtener_dias_del_evento(self):
"""Obtiene los días de la semana en los que se realiza el evento (en formato 1 a 7)."""
dias_evento = []
for fecha in self.evento.fechas.all():
if fecha.duracion_evento is not None: # Solo considerar fechas con duracion_evento definido
dias_evento.append(int(fecha.duracion_evento)) # Convertir a entero y agregar a la lista
return dias_evento
def procesar_fechas_evento(self):
"""Genera las fechas del evento ajustadas al mes especificado."""
fecha_inicio, fecha_fin = self.ajustar_fechas_a_rango_mes()
dias_evento = self.obtener_dias_del_evento()
fechas_null = self.evento.fechas.filter(dias__isnull=True)
self.asignar_fechas_nulas(fechas_null, dias_evento, fecha_inicio)
self.generar_fechas_para_dias_especificos(fecha_inicio, fecha_fin, dias_evento)
return self.fechas_generadas
def asignar_fechas_nulas(self, fechas_null, dias_evento, fecha_inicio):
"""Asigna fechas a las entradas que no tienen días especificados."""
for fecha_obj in fechas_null:
dia_semana = fecha_inicio.weekday() + 1 # Ajustar de 0-6 a 1-7
if dia_semana in dias_evento:
fecha_obj.dias = fecha_inicio.date()
fecha_obj.save(update_fields=['dias'])
self.fechas_generadas[fecha_inicio.strftime('%Y-%m-%d')] = fecha_obj.pk
def generar_fechas_para_dias_especificos(self, fecha_inicio, fecha_fin, dias_evento):
"""Genera fechas adicionales dentro del rango si coinciden con los días seleccionados."""
fecha_actual = fecha_inicio
while fecha_actual <= fecha_fin:
dia_semana = fecha_actual.weekday() + 1 # Ajustar de 0-6 a 1-7
if dia_semana in dias_evento:
self.agregar_fecha(fecha_actual, dia_semana)
fecha_actual += timedelta(days=1)
def agregar_fecha(self, fecha_actual, dia_semana):
"""Agrega una fecha al evento si no existe ya en el sistema."""
if not FechaEvento.objects.filter(dias=fecha_actual, duracion_evento=str(dia_semana)).exists():
nueva_fecha = FechaEvento.objects.create(dias=fecha_actual, duracion_evento=str(dia_semana))
self.evento.fechas.add(nueva_fecha)
self.fechas_generadas[fecha_actual.strftime('%Y-%m-%d')] = nueva_fecha.pk
def obtener_datos_evento(self):
"""Devuelve los datos del evento en formato serializado."""
return {
'titulo': self.evento.titulo,
'categoria': self.evento.categoria,
'descripcion': self.evento.descripcion,
'direccion': self.evento.direccion,
'fecha_inicio': self.evento.fecha_inicio.isoformat() if self.evento.fecha_inicio else None,
'hora_inicio': self.evento.hora_inicio.isoformat() if self.evento.hora_inicio else None,
'fecha_final': self.evento.fecha_final.isoformat() if self.evento.fecha_final else None,
'hora_fin': self.evento.hora_fin.isoformat() if self.evento.hora_fin else None,
'url': self.evento.url,
'imagen': self.evento.imagen.url if self.evento.imagen else None,
'fechas': list(self.evento.fechas.values('id', 'duracion_evento', 'dias')),
'organismo': list(self.evento.organismo.values('short_name')),
'dependencia': list(self.evento.dependencia.values('short_name')),
}
... ...
import random
from factory import faker, django, post_generation
from evento.models import Evento, FechaEvento
class FechaEventoFactory(django.DjangoModelFactory):
class Meta:
model = FechaEvento
duracion_evento = random.randint(1, 7)
class EventoFactory(django.DjangoModelFactory):
class Meta:
model = Evento
skip_postgeneration_save = True
titulo = faker.Faker(provider='sentence', nb_words=50)
categoria = faker.Faker(provider='sentence', nb_words=30)
direccion = 'https://maps.app.goo.gl/CNwbHBx5zq1VDje57'
descripcion = faker.Faker(provider='sentence', nb_words=30)
fecha_inicio = '2024-10-04'
fecha_fin = '2024-10-04'
@post_generation
def add_fechas(self, create, extracted, **kwargs):
if not create:
return
if extracted:
for dias in extracted:
self.fechas.add(dias)
... ...
import pytest
import json
from datetime import time, datetime
from rest_framework import status
from django.contrib.auth.models import User
from django.urls import reverse
from rest_framework.test import APIClient
from evento.tests.factories import EventoFactory, FechaEventoFactory
from evento.models import Evento
@pytest.mark.django_db
@pytest.mark.parametrize('mes, fecha_inicio, fecha_final', [
(10, '2024-10-07', '2024-10-07'), # Evento en octubre
(11, '2024-11-04', '2024-11-04') # Evento en noviembre
])
def test_obtener_mes_get(mes, fecha_inicio, fecha_final):
cliente = APIClient()
user = User.objects.create_user(
username='admin',
email='admin@example.com',
password='password123',
is_superuser=True,
)
cliente.force_authenticate(user=user)
calendario = FechaEventoFactory.create(duracion_evento=1)
data = {
'titulo': f'Event test for month {mes}',
'categoria': 'Cultural',
'descripcion': f'Durante el evento test para el mes {mes}',
'direccion': 'https://maps.app.goo.gl/RJExHoGFyg8Ska7CA',
'fecha_inicio': fecha_inicio,
'hora_inicio': str(time(8, 30)),
'fecha_final': fecha_final,
'hora_fin': str(time(12, 00)),
'url': 'google.com',
'fechas': json.dumps([
{
'id': calendario.pk,
'duracion_evento': 1,
'dias': None,
}
]),
}
evento = Evento.objects.create(
titulo=data['titulo'],
categoria=data['categoria'],
descripcion=data['descripcion'],
direccion=data['direccion'],
fecha_inicio=data['fecha_inicio'],
hora_inicio=data['hora_inicio'],
fecha_final=data['fecha_final'],
hora_fin=data['hora_fin'],
url=data['url']
)
evento.fechas.set([calendario])
url = reverse('evento-obtener-mes')
response = cliente.post(url, {'mes': mes}, format='multipart')
print("Response JSON:", response.json())
evento_fechas = evento.fechas.all().values()
print("Fechas asociadas al evento:", list(evento_fechas))
if response.status_code == status.HTTP_400_BAD_REQUEST:
assert response.data['error'] == (
'El mes ingresado no es válido. Por favor, ingrese el mes correspondiente a la fecha actual o posterior.'
)
else:
assert response.status_code == 200
# Verificar que solo existe una fecha asociada con el evento
assert evento.fechas.count() == 1, "Debe existir solo una fecha asociada al evento"
evento_data = response.json()['data'][0]
assert evento_data['titulo'] == f'Event test for month {mes}'
assert evento_data['categoria'] == 'Cultural'
assert evento_data['descripcion'] == f'Durante el evento test para el mes {mes}'
assert evento_data['direccion'] == 'https://maps.app.goo.gl/RJExHoGFyg8Ska7CA'
assert evento_data['fecha_inicio'] == fecha_inicio
assert evento_data['hora_inicio'] == str(time(8, 30))
assert evento_data['fecha_final'] == fecha_final
assert evento_data['hora_fin'] == str(time(12, 0))
assert evento_data['url'] == 'google.com'
assert evento_data['fechas'] == [{
'id': calendario.pk,
'duracion_evento': 1,
'dias': fecha_inicio,
}]
... ...
... ... @@ -57,6 +57,7 @@ THIRD_PARTY_APPS = (
'corsheaders',
'oauth2_provider',
'mozilla_django_oidc',
"rest_framework_api_key",
)
... ... @@ -86,7 +87,7 @@ ROOT_URLCONF = 'project.urls'
WSGI_APPLICATION = 'project.wsgi.application'
LANGUAGE_CODE = 'es-AR'
TIME_ZONE = 'America/Argentina/Catamarca'
TIME_ZONE = 'America/Argentina/Buenos_Aires'
USE_I18N = True
USE_TZ = True
... ... @@ -158,6 +159,10 @@ REST_FRAMEWORK = {
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
),
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework_api_key.permissions.HasAPIKey",
'rest_framework.permissions.AllowAny',
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
'rest_framework.authentication.BasicAuthentication',
... ...
... ... @@ -6,6 +6,7 @@ django-filter==23.3
djangorestframework==3.14.0
django-environ==0.11.2
djangorestframework-jsonapi==6.1.0
djangorestframework-api-key==2.3.0
django-oauth-toolkit==2.3.0
mozilla-django-oidc==3.0.0
... ...