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

56
sigi/apps/eventos/admin.py

@ -547,7 +547,60 @@ class ModeloDeclaracaoAdmin(admin.ModelAdmin):
class EventoAdmin(CartExportMixin, admin.ModelAdmin): class EventoAdmin(CartExportMixin, admin.ModelAdmin):
form = EventoAdminForm form = EventoAdminForm
resource_class = EventoResource 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 = ( list_display = (
"get_banner", "get_banner",
"publicar", "publicar",
@ -578,6 +631,7 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin):
"virtual", "virtual",
"solicitante", "solicitante",
) )
date_hierarchy = "data_inicio"
autocomplete_fields = ( autocomplete_fields = (
"tipo_evento", "tipo_evento",
"casa_anfitria", "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", "status",
"publicar", "publicar",
"moodle_courseid", "moodle_courseid",
"chave_inscricao",
"perfil_aluno",
"observacao_inscricao",
"contato_inscricao",
"telefone_inscricao",
"contato", "contato",
"telefone", "telefone",
"banner", "banner",
@ -41,6 +46,7 @@ class EventoAdminForm(forms.ModelForm):
cleaned_data = super(EventoAdminForm, self).clean() cleaned_data = super(EventoAdminForm, self).clean()
data_inicio = cleaned_data.get("data_inicio") data_inicio = cleaned_data.get("data_inicio")
data_termino = cleaned_data.get("data_termino") data_termino = cleaned_data.get("data_termino")
publicar = cleaned_data.get("publicar")
if data_inicio and data_termino and data_inicio > data_termino: if data_inicio and data_termino and data_inicio > data_termino:
raise forms.ValidationError( raise forms.ValidationError(
@ -48,6 +54,14 @@ class EventoAdminForm(forms.ModelForm):
code="invalid_period", 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): class SelecionaModeloForm(forms.Form):
modelo = forms.ModelChoiceField( 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." "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( 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) banner = models.ImageField(_("banner do evento"), blank=True, null=True)
data_cancelamento = models.DateField( 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 csv
import locale import locale
from functools import reduce from functools import reduce
from rest_framework import mixins, generics
from typing import OrderedDict from typing import OrderedDict
from django import forms from django import forms
from django.contrib import messages from django.contrib import messages
@ -35,6 +36,7 @@ from sigi.apps.eventos.forms import (
FuncionarioForm, FuncionarioForm,
ParlamentarForm, ParlamentarForm,
) )
from sigi.apps.eventos.serializers import EventoSerializer
from sigi.apps.parlamentares.models import Parlamentar from sigi.apps.parlamentares.models import Parlamentar
from sigi.apps.servidores.models import Servidor from sigi.apps.servidores.models import Servidor
@ -362,201 +364,27 @@ def alocacao_equipe(request):
return render(request, "eventos/alocacao_equipe.html", context) return render(request, "eventos/alocacao_equipe.html", context)
# # Views e functions para carrinho de exportação class ApiEventoAbstract:
queryset = (
# def query_ordena(qs, o): Evento.objects.filter(publicar=True)
# from sigi.apps.eventos.admin import EventoAdmin .exclude(data_inicio=None)
# list_display = EventoAdmin.list_display .exclude(data_termino=None)
# order_fields = [] .order_by("-data_inicio")
)
# for order_number in o.split('.'): serializer_class = EventoSerializer
# order_number = int(order_number)
# order = ''
# if order_number != abs(order_number): class ApiEventoList(ApiEventoAbstract, generics.ListAPIView):
# order_number = abs(order_number) """
# order = '-' Lista de eventos, oficinas e cursos realizados pelo ILB / Interlegis
# order_fields.append(order + list_display[order_number - 1]) """
# qs = qs.order_by(*order_fields)
# return qs pass
# def get_for_qs(get, qs):
# kwargs = {} class ApiEventoRetrieve(ApiEventoAbstract, generics.RetrieveAPIView):
# for k, v in get.iteritems(): """
# if str(k) not in ('page', 'pop', 'q', '_popup', 'o', 'ot'): Recupera um evento pelo id
# kwargs[str(k)] = v """
# qs = qs.filter(**kwargs)
# if 'o' in get: pass
# 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

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", "import_export",
"tinymce", "tinymce",
"django.forms", "django.forms",
"rest_framework",
"material", "material",
"material.admin", "material.admin",
"django.contrib.auth", "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", "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 # SIGI specific settings
MENU_FILE = BASE_DIR / "menu_conf.yaml" 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 1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 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.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf import settings from django.conf import settings
@ -28,6 +29,16 @@ urlpatterns = [
path("admin/convenios/", include("sigi.apps.convenios.urls")), path("admin/convenios/", include("sigi.apps.convenios.urls")),
path("admin/ocorrencias/", include("sigi.apps.ocorrencias.admin_urls")), path("admin/ocorrencias/", include("sigi.apps.ocorrencias.admin_urls")),
path("admin/", admin.site.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("tinymce/", include("tinymce.urls")),
path("accounts/", include("sigi.apps.home.accounts_urls")), path("accounts/", include("sigi.apps.home.accounts_urls")),
path("", include("sigi.apps.home.urls")), path("", include("sigi.apps.home.urls")),

Loading…
Cancel
Save