Enzo Yair

Merge branch 'feature/evento_admin' into 'develop'

Feature/evento admin

feature:
-Creacion de modelos Evento
-Creacion de modelos Fecha
-Validacion de campo direccion
-Validacion de campo fecha_inicio y fecha_final
-Registro de modelo Evento en admin
-Registro de modelo Fecha en admin
Test resultados:
================================================================ 2 passed, 1 warning in 2.64s ================================================================= 

See merge request !2
@@ -110,5 +110,5 @@ deployment/logs/ @@ -110,5 +110,5 @@ deployment/logs/
110 .pytest_cache/ 110 .pytest_cache/
111 111
112 project/settings/production.py 112 project/settings/production.py
113 -project/static/* 113 +project/assets/static/*
114 project/media/* 114 project/media/*
  1 +from django.contrib import admin
  2 +
  3 +from .models import Evento, FechaEvento
  4 +from .forms import EventoForms
  5 +
  6 +# Register your models here.
  7 +
  8 +
  9 +@admin.register(Evento)
  10 +class EventoAdmin(admin.ModelAdmin):
  11 + form = EventoForms
  12 + list_display = (
  13 + 'titulo',
  14 + 'categoria',
  15 + 'descripcion',
  16 + 'direccion',
  17 + )
  18 + search_fields = (
  19 + 'titulo',
  20 + 'categoria',
  21 + 'fecha_inicio',
  22 + 'fecha_fin',
  23 + )
  24 +
  25 +
  26 +@admin.register(FechaEvento)
  27 +class FechaEventoAdmin(admin.ModelAdmin):
  28 + model = FechaEvento
  1 +from django.apps import AppConfig
  2 +
  3 +
  4 +class EventoConfig(AppConfig):
  5 + default_auto_field = 'django.db.models.BigAutoField'
  6 + name = 'evento'
  1 +import re
  2 +
  3 +from django import forms
  4 +from django.core.exceptions import ValidationError
  5 +from django.utils.translation import gettext_lazy as _
  6 +
  7 +from .models import Evento
  8 +
  9 +
  10 +class EventoForms(forms.ModelForm):
  11 + class Meta:
  12 + model = Evento
  13 + fields = (
  14 + 'titulo',
  15 + 'categoria',
  16 + 'fecha_inicio',
  17 + 'hora_inicio',
  18 + 'fecha_final',
  19 + 'hora_fin',
  20 + 'fechas',
  21 + 'descripcion',
  22 + 'direccion',
  23 + 'url',
  24 + 'organismo',
  25 + 'dependencia',
  26 + 'imagen',
  27 + )
  28 +
  29 + def clean_direccion(self):
  30 + clean = super().clean()
  31 + direccion = clean.get('direccion')
  32 +
  33 + maps_regex = re.compile(
  34 + r'^(https?://)?(www\.)?(google\.com/maps|goo\.gl|maps\.app\.goo\.gl)/\S+$',
  35 + re.IGNORECASE
  36 + )
  37 + if direccion:
  38 + if not maps_regex.match(direccion):
  39 + raise ValidationError(
  40 + _('La dirección no es un enlace válido de Google Maps.')
  41 + )
  42 + return direccion
  43 +
  44 + def clean_fecha_final(self):
  45 + fecha_inicio = self.cleaned_data.get('fecha_inicio')
  46 + fecha_final = self.cleaned_data.get('fecha_final')
  47 +
  48 + if fecha_inicio and fecha_final:
  49 + if fecha_final < fecha_inicio:
  50 + raise ValidationError(
  51 + _('La fecha final no puede ser anterior a la fecha de inicio.'
  52 + )
  53 + )
  54 +
  55 + return fecha_final
  56 +
  57 + def clean_hora_fin(self):
  58 + clean = super().clean()
  59 +
  60 + fecha_inicio = clean.get('fecha_inicio')
  61 + hora_inicio = clean.get('hora_inicio')
  62 + fecha_final = clean.get('fecha_final')
  63 + hora_fin = clean.get('hora_fin')
  64 +
  65 + if fecha_final == fecha_inicio:
  66 + if hora_fin <= hora_inicio:
  67 + raise ValidationError(
  68 + _('La hora de finalización debe ser posterior a la hora de inicio.'
  69 + )
  70 + )
  71 +
  72 + return hora_fin
  1 +# Generated by Django 4.2.9 on 2024-10-02 00:54
  2 +
  3 +import django.core.validators
  4 +from django.db import migrations, models
  5 +
  6 +
  7 +class Migration(migrations.Migration):
  8 +
  9 + initial = True
  10 +
  11 + dependencies = [
  12 + ('organismo', '0001_initial'),
  13 + ]
  14 +
  15 + operations = [
  16 + migrations.CreateModel(
  17 + name='FechaEvento',
  18 + fields=[
  19 + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
  20 + ('dia_evento', models.DateField(verbose_name='Días del evento')),
  21 + ],
  22 + options={
  23 + 'verbose_name': 'Fecha del Eventos',
  24 + 'verbose_name_plural': 'Fechas del Eventos',
  25 + },
  26 + ),
  27 + migrations.CreateModel(
  28 + name='Evento',
  29 + fields=[
  30 + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
  31 + ('fecha_inicio', models.DateField(verbose_name='Fecha de inicio del evento')),
  32 + ('hora_inicio', models.TimeField(verbose_name='Hora de apertura')),
  33 + ('fecha_final', models.DateField(verbose_name='Fecha de cierre del evento')),
  34 + ('hora_fin', models.TimeField(verbose_name='Hora de cierre')),
  35 + ('titulo', models.CharField(max_length=350)),
  36 + ('categoria', models.CharField(max_length=150)),
  37 + ('direccion', models.URLField(help_text='Ingrese la url obtenida por google maps', max_length=150, verbose_name='Ubicación del evento')),
  38 + ('descripcion', models.TextField(verbose_name='Descripción')),
  39 + ('url', models.URLField(blank=True, max_length=300, verbose_name='Dirección Web')),
  40 + ('imagen', models.ImageField(blank=True, upload_to='static/eventos', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['jpg', 'png'])], verbose_name='Banner promocional')),
  41 + ('dependencia', models.ManyToManyField(blank=True, related_name='evento_dependencia', to='organismo.dependencia')),
  42 + ('fechas', models.ManyToManyField(blank=True, help_text='Por favor, indique los días disponibles para el evento', related_name='evento_fechas', to='evento.fechaevento', verbose_name='Días disponibles')),
  43 + ('organismo', models.ManyToManyField(blank=True, related_name='evento_organismo', to='organismo.organismo')),
  44 + ],
  45 + options={
  46 + 'verbose_name': 'Evento',
  47 + 'verbose_name_plural': 'Eventos',
  48 + 'ordering': ('titulo', 'categoria'),
  49 + },
  50 + ),
  51 + ]
  1 +from django.db import models
  2 +
  3 +from django.core.validators import FileExtensionValidator
  4 +
  5 +from organismo.models import Organismo, Dependencia
  6 +
  7 +# Create your models here.
  8 +
  9 +
  10 +class Evento(models.Model):
  11 + class Meta:
  12 + ordering = 'titulo', 'categoria'
  13 + verbose_name = 'Evento'
  14 + verbose_name_plural = 'Eventos'
  15 +
  16 + organismo = models.ManyToManyField(
  17 + Organismo,
  18 + related_name='evento_organismo',
  19 + blank=True
  20 + )
  21 + dependencia = models.ManyToManyField(
  22 + Dependencia,
  23 + related_name='evento_dependencia',
  24 + blank=True
  25 + )
  26 + fecha_inicio = models.DateField(verbose_name='Fecha de inicio del evento')
  27 + hora_inicio = models.TimeField(blank=False, verbose_name='Hora de apertura')
  28 + fecha_final = models.DateField(verbose_name='Fecha de cierre del evento')
  29 + hora_fin = models.TimeField(blank=False, verbose_name='Hora de cierre')
  30 + fechas = models.ManyToManyField(
  31 + 'FechaEvento',
  32 + related_name='evento_fechas',
  33 + blank=True,
  34 + verbose_name='Días disponibles',
  35 + help_text='Por favor, indique los días disponibles para el evento'
  36 + )
  37 + titulo = models.CharField(max_length=350, null=False)
  38 + categoria = models.CharField(max_length=150, null=False)
  39 + direccion = models.URLField(
  40 + max_length=150,
  41 + verbose_name='Ubicación del evento',
  42 + help_text='Ingrese la url obtenida por google maps'
  43 + )
  44 + descripcion = models.TextField(null=False, verbose_name='Descripción')
  45 + url = models.URLField(
  46 + max_length=300,
  47 + blank=True,
  48 + verbose_name='Dirección Web'
  49 + )
  50 + imagen = models.ImageField(
  51 + upload_to='static/eventos',
  52 + validators=[FileExtensionValidator(allowed_extensions=['jpg', 'png'])],
  53 + blank=True,
  54 + verbose_name='Banner promocional'
  55 + )
  56 +
  57 + def __str__(self):
  58 + return f'{self.titulo} - {self.descripcion}'
  59 +
  60 +
  61 +class FechaEvento(models.Model):
  62 + class Meta:
  63 + verbose_name = 'Fecha del Eventos'
  64 + verbose_name_plural = 'Fechas del Eventos'
  65 +
  66 + dia_evento = models.DateField(verbose_name='Días del evento')
  67 +
  68 + def __str__(self):
  69 + return f'{self.dia_evento}'
  1 +from django.test import TestCase
  2 +
  3 +# Create your tests here.
  1 +from django.shortcuts import render
  2 +
  3 +# Create your views here.
@@ -63,6 +63,7 @@ THIRD_PARTY_APPS = ( @@ -63,6 +63,7 @@ THIRD_PARTY_APPS = (
63 PROJECT_APPS = ( 63 PROJECT_APPS = (
64 'core', 64 'core',
65 'organismo', 65 'organismo',
  66 + 'evento',
66 ) 67 )
67 68
68 INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + PROJECT_APPS 69 INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + PROJECT_APPS
@@ -99,7 +100,8 @@ LANGUAGES = [ @@ -99,7 +100,8 @@ LANGUAGES = [
99 # https://docs.djangoproject.com/en/1.7/howto/static-files/ 100 # https://docs.djangoproject.com/en/1.7/howto/static-files/
100 101
101 STATIC_ROOT = str(PROJECT_DIR.path('static')) 102 STATIC_ROOT = str(PROJECT_DIR.path('static'))
102 -STATIC_URL = '/static/' 103 +STATIC_URL = env.str('DJANGO_STATIC_URL', default='/static/')
  104 +STATICFILES_DIRS = [str(PROJECT_DIR.path('assets')), ]
103 105
104 MEDIA_ROOT = str(PROJECT_DIR.path('media')) 106 MEDIA_ROOT = str(PROJECT_DIR.path('media'))
105 MEDIA_URL = env.str('MEDIA_URL', default='/media/') 107 MEDIA_URL = env.str('MEDIA_URL', default='/media/')
@@ -16,6 +16,7 @@ Including another URLconf @@ -16,6 +16,7 @@ Including another URLconf
16 from django.contrib import admin 16 from django.contrib import admin
17 from django.urls import path, include, reverse_lazy 17 from django.urls import path, include, reverse_lazy
18 from django.conf import settings 18 from django.conf import settings
  19 +from django.conf.urls.static import static
19 from django.views.generic.base import RedirectView 20 from django.views.generic.base import RedirectView
20 21
21 from .router import router 22 from .router import router
@@ -28,4 +29,4 @@ urlpatterns = [ @@ -28,4 +29,4 @@ urlpatterns = [
28 path('', RedirectView.as_view(url=reverse_lazy('admin:index'))), 29 path('', RedirectView.as_view(url=reverse_lazy('admin:index'))),
29 path('admin/', admin.site.urls), 30 path('admin/', admin.site.urls),
30 path('api/v1/', include(router.urls)), 31 path('api/v1/', include(router.urls)),
31 -] 32 +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
@@ -9,6 +9,9 @@ djangorestframework-jsonapi==6.1.0 @@ -9,6 +9,9 @@ djangorestframework-jsonapi==6.1.0
9 django-oauth-toolkit==2.3.0 9 django-oauth-toolkit==2.3.0
10 mozilla-django-oidc==3.0.0 10 mozilla-django-oidc==3.0.0
11 11
  12 +# visualizar imagenes.
  13 +pillow==10.4.0
  14 +
12 15
13 # database 16 # database
14 psycopg2==2.9.9 17 psycopg2==2.9.9