Browse Source

Adiciona API rest para expor eventos. Gertiq #160534

pull/166/head
Sesóstris Vieira 1 year ago
parent
commit
9a7a78cbd4
  1. 3
      requirements/requirements.txt
  2. 56
      sigi/apps/eventos/admin.py
  3. 15
      sigi/apps/eventos/api_urls.py
  4. 14
      sigi/apps/eventos/forms.py
  5. 77
      sigi/apps/eventos/migrations/0044_evento_chave_inscricao_evento_contato_inscricao_and_more.py
  6. 47
      sigi/apps/eventos/models.py
  7. 67
      sigi/apps/eventos/serializers.py
  8. 224
      sigi/apps/eventos/views.py
  9. 7
      sigi/apps/utils/pagination.py
  10. 11
      sigi/settings.py
  11. 11
      sigi/urls.py

3
requirements/requirements.txt

@ -7,8 +7,10 @@ moodlepy==0.23.10
pandas==2.0.2
Pillow==9.5.0
psycopg2-binary==2.9.6
PyYAML==6.0.1
python-docx==0.8.11
requests==2.31.0
uritemplate==4.1.1
weasyprint==58.0
XlsxWriter==3.1.2
Django==4.2.4
@ -20,5 +22,6 @@ django-filter==23.2
django-import-export==3.2.0
django-localflavor==4.0
django-material-admin==1.8.6
djangorestframework==3.14.0
django-tinymce==3.6.1
django-weasyprint==2.2.0

56
sigi/apps/eventos/admin.py

@ -547,7 +547,60 @@ class ModeloDeclaracaoAdmin(admin.ModelAdmin):
class EventoAdmin(CartExportMixin, admin.ModelAdmin):
form = EventoAdminForm
resource_class = EventoResource
date_hierarchy = "data_inicio"
fieldsets = (
(
None,
{
"fields": (
"tipo_evento",
"nome",
"turma",
"descricao",
"virtual",
"solicitante",
"num_processo",
"data_pedido",
"data_recebido_coperi",
"data_inicio",
"data_termino",
"carga_horaria",
"casa_anfitria",
"contato",
"telefone",
"observacao",
)
},
),
(
_("Status"),
{
"fields": (
"status",
"total_participantes",
"data_cancelamento",
"motivo_cancelamento",
)
},
),
(
_("Portal/Saberes"),
{
"fields": (
"publicar",
"publico_alvo",
"local",
"moodle_courseid",
"chave_inscricao",
"perfil_aluno",
"observacao_inscricao",
"contato_inscricao",
"telefone_inscricao",
"banner",
)
},
),
)
list_display = (
"get_banner",
"publicar",
@ -578,6 +631,7 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin):
"virtual",
"solicitante",
)
date_hierarchy = "data_inicio"
autocomplete_fields = (
"tipo_evento",
"casa_anfitria",

15
sigi/apps/eventos/api_urls.py

@ -0,0 +1,15 @@
from django.urls import path, include
from sigi.apps.eventos import views
urlpatterns = [
path(
"evento/<int:pk>/",
views.ApiEventoRetrieve.as_view(),
name="api_eventos_evento_view",
),
path(
"evento/",
views.ApiEventoList.as_view(),
name="api_eventos_evento_list",
),
]

14
sigi/apps/eventos/forms.py

@ -30,6 +30,11 @@ class EventoAdminForm(forms.ModelForm):
"status",
"publicar",
"moodle_courseid",
"chave_inscricao",
"perfil_aluno",
"observacao_inscricao",
"contato_inscricao",
"telefone_inscricao",
"contato",
"telefone",
"banner",
@ -41,6 +46,7 @@ class EventoAdminForm(forms.ModelForm):
cleaned_data = super(EventoAdminForm, self).clean()
data_inicio = cleaned_data.get("data_inicio")
data_termino = cleaned_data.get("data_termino")
publicar = cleaned_data.get("publicar")
if data_inicio and data_termino and data_inicio > data_termino:
raise forms.ValidationError(
@ -48,6 +54,14 @@ class EventoAdminForm(forms.ModelForm):
code="invalid_period",
)
if publicar and (data_inicio is None or data_termino is None):
raise forms.ValidationError(
_(
"Para publicar no site é preciso ter data início e data término"
),
code="cannot_publish",
)
class SelecionaModeloForm(forms.Form):
modelo = forms.ModelChoiceField(

77
sigi/apps/eventos/migrations/0044_evento_chave_inscricao_evento_contato_inscricao_and_more.py

@ -0,0 +1,77 @@
# Generated by Django 4.2.4 on 2023-09-19 11:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("eventos", "0043_alter_solicitacao_estimativa_casas_and_more"),
]
operations = [
migrations.AddField(
model_name="evento",
name="chave_inscricao",
field=models.CharField(
blank=True, max_length=100, verbose_name="chave de inscrição"
),
),
migrations.AddField(
model_name="evento",
name="contato_inscricao",
field=models.CharField(
blank=True,
help_text="pessoa ou setor responsável por dar suporte aos alunos no processo de inscrição",
max_length=100,
verbose_name="contato para inscrição",
),
),
migrations.AddField(
model_name="evento",
name="observacao_inscricao",
field=models.TextField(
blank=True,
help_text="Mais detalhes para ajudar o aluno a se inscrever no curso",
verbose_name="Observações para inscrição",
),
),
migrations.AddField(
model_name="evento",
name="perfil_aluno",
field=models.URLField(
blank=True,
help_text="Link completo da página de perfil do aluno deste curso no Saberes",
verbose_name="Link do perfil do aluno",
),
),
migrations.AddField(
model_name="evento",
name="telefone_inscricao",
field=models.CharField(
blank=True,
help_text="telefone da pessoa ou setor responsável por dar suporte aos alunos no processo de inscrição",
max_length=30,
verbose_name="telefone do contato",
),
),
migrations.AlterField(
model_name="evento",
name="contato",
field=models.CharField(
blank=True,
help_text="pessoa de contato na casa anfitriã",
max_length=100,
verbose_name="contato",
),
),
migrations.AlterField(
model_name="evento",
name="telefone",
field=models.CharField(
blank=True,
help_text="telefone da pessoa de contato na casa anfitriã",
max_length=30,
verbose_name="tefone de contato",
),
),
]

47
sigi/apps/eventos/models.py

@ -411,9 +411,52 @@ class Evento(models.Model):
"quando o curso é criado no Saberes."
),
)
contato = models.CharField(_("contato"), max_length=100, blank=True)
chave_inscricao = models.CharField(
_("chave de inscrição"), max_length=100, blank=True
)
perfil_aluno = models.URLField(
_("Link do perfil do aluno"),
blank=True,
help_text=_(
"Link completo da página de perfil do aluno deste curso no Saberes"
),
)
observacao_inscricao = models.TextField(
_("Observações para inscrição"),
blank=True,
help_text=_(
"Mais detalhes para ajudar o aluno a se inscrever no curso"
),
)
contato_inscricao = models.CharField(
_("contato para inscrição"),
max_length=100,
blank=True,
help_text=_(
"pessoa ou setor responsável por dar suporte aos alunos no "
"processo de inscrição"
),
)
telefone_inscricao = models.CharField(
_("telefone do contato"),
max_length=30,
blank=True,
help_text=_(
"telefone da pessoa ou setor responsável por dar suporte aos "
"alunos no processo de inscrição"
),
)
contato = models.CharField(
_("contato"),
max_length=100,
blank=True,
help_text=_("pessoa de contato na casa anfitriã"),
)
telefone = models.CharField(
_("tefone de contato"), max_length=30, blank=True
_("tefone de contato"),
max_length=30,
blank=True,
help_text=_("telefone da pessoa de contato na casa anfitriã"),
)
banner = models.ImageField(_("banner do evento"), blank=True, null=True)
data_cancelamento = models.DateField(

67
sigi/apps/eventos/serializers.py

@ -0,0 +1,67 @@
from rest_framework import serializers
from sigi.apps.eventos.models import Evento
class EventoSerializer(serializers.ModelSerializer):
casa_nome = serializers.SerializerMethodField("get_casa_nome")
casa_logradouro = serializers.SerializerMethodField("get_casa_logradouro")
casa_bairro = serializers.SerializerMethodField("get_casa_bairro")
casa_municipio = serializers.SerializerMethodField("get_casa_municipio")
casa_uf = serializers.SerializerMethodField("get_casa_uf")
casa_cep = serializers.SerializerMethodField("get_casa_cep")
class Meta:
model = Evento
fields = [
"id",
"nome",
"turma",
"publico_alvo",
"data_inicio",
"data_termino",
"carga_horaria",
"local",
"casa_nome",
"casa_logradouro",
"casa_bairro",
"casa_municipio",
"casa_uf",
"casa_cep",
"link_inscricao",
"chave_inscricao",
"perfil_aluno",
"observacao_inscricao",
"contato_inscricao",
"telefone_inscricao",
"banner",
]
def get_casa_nome(self, obj):
if obj.casa_anfitria:
return obj.casa_anfitria.nome
return ""
def get_casa_logradouro(self, obj):
if obj.casa_anfitria:
return obj.casa_anfitria.logradouro
return ""
def get_casa_bairro(self, obj):
if obj.casa_anfitria:
return obj.casa_anfitria.bairro
return ""
def get_casa_municipio(self, obj):
if obj.casa_anfitria:
return obj.casa_anfitria.municipio.nome
return ""
def get_casa_uf(self, obj):
if obj.casa_anfitria:
return obj.casa_anfitria.municipio.uf.nome
return ""
def get_casa_cep(self, obj):
if obj.casa_anfitria:
return obj.casa_anfitria.cep
return ""

224
sigi/apps/eventos/views.py

@ -2,6 +2,7 @@ import calendar
import csv
import locale
from functools import reduce
from rest_framework import mixins, generics
from typing import OrderedDict
from django import forms
from django.contrib import messages
@ -35,6 +36,7 @@ from sigi.apps.eventos.forms import (
FuncionarioForm,
ParlamentarForm,
)
from sigi.apps.eventos.serializers import EventoSerializer
from sigi.apps.parlamentares.models import Parlamentar
from sigi.apps.servidores.models import Servidor
@ -362,201 +364,27 @@ def alocacao_equipe(request):
return render(request, "eventos/alocacao_equipe.html", context)
# # Views e functions para carrinho de exportação
# def query_ordena(qs, o):
# from sigi.apps.eventos.admin import EventoAdmin
# list_display = EventoAdmin.list_display
# order_fields = []
# for order_number in o.split('.'):
# order_number = int(order_number)
# order = ''
# if order_number != abs(order_number):
# order_number = abs(order_number)
# order = '-'
# order_fields.append(order + list_display[order_number - 1])
# qs = qs.order_by(*order_fields)
# return qs
# def get_for_qs(get, qs):
# kwargs = {}
# for k, v in get.iteritems():
# if str(k) not in ('page', 'pop', 'q', '_popup', 'o', 'ot'):
# kwargs[str(k)] = v
# qs = qs.filter(**kwargs)
# if 'o' in get:
# qs = query_ordena(qs, get['o'])
# return qs
# def carrinhoOrGet_for_qs(request):
# if 'carrinho_eventos' in request.session:
# ids = request.session['carrinho_eventos']
# qs = Evento.objects.filter(pk__in=ids)
# else:
# qs = Evento.objects.all()
# if request.GET:
# qs = get_for_qs(request.GET, qs)
# return qs
# def adicionar_eventos_carrinho(request, queryset=None, id=None):
# if request.method == 'POST':
# ids_selecionados = request.POST.getlist('_selected_action')
# if 'carrinho_eventos' not in request.session:
# request.session['carrinho_eventos'] = ids_selecionados
# else:
# lista = request.session['carrinho_eventos']
# # Verifica se id já não está adicionado
# for id in ids_selecionados:
# if id not in lista:
# lista.append(id)
# request.session['carrinho_eventos'] = lista
# @login_required
# def visualizar_carrinho(request):
# qs = carrinhoOrGet_for_qs(request)
# paginator = Paginator(qs, 100)
# try:
# page = int(request.GET.get('page', '1'))
# except ValueError:
# page = 1
# try:
# paginas = paginator.page(page)
# except (EmptyPage, InvalidPage):
# paginas = paginator.page(paginator.num_pages)
# carrinhoIsEmpty = not('carrinho_eventos' in request.session)
# return render(
# request,
# 'eventos/carrinho.html',
# {
# 'carIsEmpty': carrinhoIsEmpty,
# 'paginas': paginas,
# 'query_str': '?' + request.META['QUERY_STRING']
# }
# )
# @login_required
# def excluir_carrinho(request):
# if 'carrinho_eventos' in request.session:
# del request.session['carrinho_eventos']
# messages.info(request, 'O carrinho foi esvaziado')
# return HttpResponseRedirect('../../')
# @login_required
# def deleta_itens_carrinho(request):
# if request.method == 'POST':
# ids_selecionados = request.POST.getlist('_selected_action')
# removed = 0
# if 'carrinho_eventos' in request.session:
# lista = request.session['carrinho_eventos']
# for item in ids_selecionados:
# lista.remove(item)
# removed += 1
# if lista:
# request.session['carrinho_eventos'] = lista
# else:
# del lista
# del request.session['carrinho_eventos']
# messages.info(request, "{0} itens removidos do carrinho".format(removed))
# return HttpResponseRedirect('.')
# @login_required
# def export_csv(request):
# def rm_rows(lista,reg):
# for a in lista:
# if a in lista:
# reg.pop(a,None)
# else:
# pass
# def serialize(r, field):
# value = (getattr(r, 'get_{0}_display'.format(field.name), None) or
# getattr(r, field.name, ""))
# if callable(value):
# value = value()
# if value is None:
# value = ""
# return unicode(value).encode('utf8')
# eventos = carrinhoOrGet_for_qs(request)
# eventos.select_related('equipe', 'convite')
# if not eventos:
# messages.info(request, _("Nenhum evento a exportar"))
# return HttpResponseRedirect('../')
# max_equipe = max([e.equipe_set.count() for e in eventos])
# mun_casa = 'Município da Casa Anfitriã'.encode('utf8')
# uf_casa = 'UF da Casa Anfitriã'.encode('utf8')
# reg_casa = 'Região da Casa Anfitriã'.encode('utf8')
# head = [f.verbose_name.encode('utf8') for f in Evento._meta.fields]
# head.extend([mun_casa, uf_casa, reg_casa])
# head.extend([f.verbose_name.encode('utf8')+"_{0}".format(i+1)
# for i in range(max_equipe) for f in Equipe._meta.fields
# if f.name not in ('id', 'evento')])
# head.extend([f.verbose_name.encode('utf8') for f in Convite._meta.fields
# if f.name not in ('id', 'evento')])
# head.extend([f.verbose_name.encode('utf8') for f in Modulo._meta.fields
# if f.name not in ('id', 'evento')])
# response = HttpResponse(content_type='text/csv')
# response['Content-Disposition'] = 'attachment; filename=eventos.csv'
# rm_list = ['Descrição do evento', 'Local do evento', 'Público alvo', 'Motivo do cancelamento', 'Descrição do módulo']
# for a in head:
# if 'Observações_' in a:
# rm_list.append(a)
# for a in rm_list:
# if a in head:
# head.remove(a)
# else:
# pass
# writer = csv.DictWriter(response, fieldnames=head)
# writer.writeheader()
# for evento in eventos:
# reg = {f.verbose_name.encode('utf8'): serialize(evento, f)
# for f in Evento._meta.fields}
# if evento.casa_anfitria is None:
# reg[mun_casa] = ""
# reg[uf_casa] = ""
# reg[reg_casa] = ""
# else:
# reg[mun_casa] = evento.casa_anfitria.municipio.nome.encode('utf8')
# reg[uf_casa] = evento.casa_anfitria.municipio.uf.sigla.\
# encode('utf8')
# reg[reg_casa] = evento.casa_anfitria.municipio.uf.\
# get_regiao_display().encode('utf8')
# idx = 1
# for membro in evento.equipe_set.all():
# reg.update(
# {
# "{0}_{1}".format(f.verbose_name.encode('utf8'), idx):
# serialize(membro, f) for f in Equipe._meta.fields
# if f.name not in ('id', 'evento')
# }
# )
# idx += 1
# for convite in evento.convite_set.all():
# reg.update(
# {f.verbose_name.encode('utf8'): serialize(convite, f)
# for f in Convite._meta.fields
# if f.name not in ('id', 'evento')}
# )
# rm_rows(rm_list,reg)
# writer.writerow(reg)
# if evento.convite_set.count() == 0:
# rm_rows(rm_list,reg)
# writer.writerow(reg)
# return response
class ApiEventoAbstract:
queryset = (
Evento.objects.filter(publicar=True)
.exclude(data_inicio=None)
.exclude(data_termino=None)
.order_by("-data_inicio")
)
serializer_class = EventoSerializer
class ApiEventoList(ApiEventoAbstract, generics.ListAPIView):
"""
Lista de eventos, oficinas e cursos realizados pelo ILB / Interlegis
"""
pass
class ApiEventoRetrieve(ApiEventoAbstract, generics.RetrieveAPIView):
"""
Recupera um evento pelo id
"""
pass

7
sigi/apps/utils/pagination.py

@ -0,0 +1,7 @@
from rest_framework.pagination import PageNumberPagination
from rest_framework.settings import api_settings
class SigiPageNumberPagination(PageNumberPagination):
page_size_query_param = "page_size"
max_page_size = 100

11
sigi/settings.py

@ -56,6 +56,7 @@ INSTALLED_APPS = [
"import_export",
"tinymce",
"django.forms",
"rest_framework",
"material",
"material.admin",
"django.contrib.auth",
@ -258,6 +259,16 @@ TINYMCE_DEFAULT_CONFIG = {
"toolbar3": "table tabledelete | tableprops tablerowprops tablecellprops | tableinsertrowbefore tableinsertrowafter tabledeleterow tablerowheader | tableinsertcolbefore tableinsertcolafter tabledeletecol tablecolheader | tablemergecells tablesplitcells | tablecellbackgroundcolor tablecellbordercolor tablecellborderwidth tablecellborderstyle",
}
# Rest Framework settings
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": [
"rest_framework.renderers.JSONRenderer",
],
"DEFAULT_PAGINATION_CLASS": "sigi.apps.utils.pagination.SigiPageNumberPagination",
"PAGE_SIZE": 100,
}
# SIGI specific settings
MENU_FILE = BASE_DIR / "menu_conf.yaml"

11
sigi/urls.py

@ -13,6 +13,7 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from rest_framework.schemas import get_schema_view
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
@ -28,6 +29,16 @@ urlpatterns = [
path("admin/convenios/", include("sigi.apps.convenios.urls")),
path("admin/ocorrencias/", include("sigi.apps.ocorrencias.admin_urls")),
path("admin/", admin.site.urls),
path(
"api/",
get_schema_view(
title="SIGI Open API Schema",
description="API for SIGI opendata",
version="1.0.0",
),
name="openapi-schema",
),
path("api/eventos/", include("sigi.apps.eventos.api_urls")),
path("tinymce/", include("tinymce.urls")),
path("accounts/", include("sigi.apps.home.accounts_urls")),
path("", include("sigi.apps.home.urls")),

Loading…
Cancel
Save