From 41fe440e5b169ec130b52db338607ba553b8843c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ses=C3=B3stris=20Vieira?= Date: Mon, 24 Apr 2023 18:13:53 -0300 Subject: [PATCH] =?UTF-8?q?Integra=C3=A7=C3=A3o=20SIGI=20x=20Saberes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/Dockerfile | 2 + requirements/requirements.txt | 1 + sigi/apps/eventos/admin.py | 209 +++++++++++++++++- sigi/apps/eventos/admin_urls.py | 5 - sigi/apps/eventos/forms.py | 2 +- .../migrations/0031_evento_moodle_courseid.py | 22 ++ .../migrations/0032_set_moodle_courseid.py | 33 +++ .../0033_remove_evento_link_inscricao.py | 16 ++ ...d_tipoevento_moodle_categoryid_and_more.py | 42 ++++ .../migrations/0035_alter_evento_options.py | 23 ++ sigi/apps/eventos/models.py | 47 +++- .../admin/eventos/evento/change_form.html | 21 +- .../admin/eventos/evento/createcourse.html | 52 +++++ .../eventos/evento}/seleciona_modelo.html | 5 +- sigi/apps/eventos/views.py | 59 +---- sigi/apps/servidores/admin.py | 1 + .../migrations/0013_servidor_moodle_userid.py | 22 ++ sigi/apps/servidores/models.py | 7 + sigi/apps/utils/__init__.py | 6 + sigi/settings.py | 5 + 20 files changed, 504 insertions(+), 76 deletions(-) create mode 100644 sigi/apps/eventos/migrations/0031_evento_moodle_courseid.py create mode 100644 sigi/apps/eventos/migrations/0032_set_moodle_courseid.py create mode 100644 sigi/apps/eventos/migrations/0033_remove_evento_link_inscricao.py create mode 100644 sigi/apps/eventos/migrations/0034_funcao_moodle_roleid_tipoevento_moodle_categoryid_and_more.py create mode 100644 sigi/apps/eventos/migrations/0035_alter_evento_options.py create mode 100644 sigi/apps/eventos/templates/admin/eventos/evento/createcourse.html rename sigi/apps/eventos/templates/{eventos => admin/eventos/evento}/seleciona_modelo.html (88%) create mode 100644 sigi/apps/servidores/migrations/0013_servidor_moodle_userid.py diff --git a/docker/Dockerfile b/docker/Dockerfile index fd53b72..2534ff8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -39,6 +39,8 @@ ENV AUTH_LDAP_MIRROR_GROUPS='' ENV AUTH_LDAP_CACHE_GROUPS='' ENV AUTH_LDAP_GROUP_CACHE_TIMEOUT='' ENV AUTH_PROFILE_MODULE='' +ENV MOODLE_BASE_URL='' +ENV MOODLE_API_TOKEN='' # Install env ENV RUN_PACKAGES gcc locales build-essential python3-dev graphviz \ diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 58d50e5..4534d4d 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -3,6 +3,7 @@ docutils==0.19 gunicorn==20.1.0 ibge==0.0.5 ipython==8.11.0 +moodlepy==0.23.10 pandas==1.5.3 Pillow==9.4.0 psycopg2-binary==2.9.5 diff --git a/sigi/apps/eventos/admin.py b/sigi/apps/eventos/admin.py index 6036e14..7d6921d 100644 --- a/sigi/apps/eventos/admin.py +++ b/sigi/apps/eventos/admin.py @@ -1,14 +1,20 @@ import datetime -from django.contrib import admin -from django.http import HttpResponseRedirect -from django.shortcuts import get_object_or_404, render -from django.urls import path +import time +from moodle import Moodle +from django.conf import settings +from django.contrib import admin, messages +from django.http import HttpResponse +from django.shortcuts import get_object_or_404, render, redirect +from django.template import Template, Context +from django.urls import path, reverse from django.utils.safestring import mark_safe from django.utils.translation import gettext as _ +from django_weasyprint.utils import django_url_fetcher from django_weasyprint.views import WeasyTemplateResponse from import_export.fields import Field from tinymce.models import HTMLField from tinymce.widgets import AdminTinyMCE +from weasyprint import HTML from sigi.apps.eventos.models import ( Checklist, Cronograma, @@ -21,7 +27,8 @@ from sigi.apps.eventos.models import ( Convite, Anexo, ) -from sigi.apps.eventos.forms import EventoAdminForm +from sigi.apps.eventos.forms import EventoAdminForm, SelecionaModeloForm +from sigi.apps.utils import abreviatura from sigi.apps.utils.filters import EmptyFilter, DateRangeFilter from sigi.apps.utils.mixins import CartExportMixin, ValueLabeledResource @@ -205,6 +212,21 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin): else: return "" + def render_change_form(self, request, context, add, change, form_url, obj): + context.update( + { + "can_createcourse": request.user.has_perm( + "eventos.createcourse_evento" + ) + and obj.moodle_courseid is None + and obj.tipo_evento.moodle_template_courseid is not None + and obj.tipo_evento.moodle_categoryid is not None + } + ) + return super().render_change_form( + request, context, add, change, form_url, obj + ) + def lookup_allowed(self, lookup, value): return super(EventoAdmin, self).lookup_allowed( lookup, value @@ -216,6 +238,11 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin): def get_urls(self): urls = super().get_urls() my_urls = [ + path( + "/declaracao/", + self.admin_site.admin_view(self.declaracao_report), + name="%s_%s_declaracaoreport" % self.get_model_info(), + ), path( "/gant/", self.admin_site.admin_view(self.gant_report), @@ -231,9 +258,75 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin): self.admin_site.admin_view(self.plano_comunicacao), name="%s_%s_comunicacaoreport" % self.get_model_info(), ), + path( + "/createcourse/", + self.admin_site.admin_view(self.create_course), + name="%s_%s_createcourse" % self.get_model_info(), + ), ] return my_urls + urls + def declaracao_report(self, request, object_id): + if request.method == "POST": + form = SelecionaModeloForm(request.POST) + if form.is_valid(): + evento = get_object_or_404(Evento, id=object_id) + modelo = form.cleaned_data["modelo"] + membro = ( + evento.equipe_set.filter(assina_oficio=True).first() + or evento.equipe_set.first() + ) + if membro: + servidor = membro.membro + else: + servidor = None + template_string = ( + """ + {% extends "eventos/declaracao_pdf.html" %} + {% block text_body %}""" + + modelo.texto + + """ + {% endblock %} + """ + ) + context = Context( + { + "pagesize": modelo.formato, + "pagemargin": modelo.margem, + "evento": evento, + "servidor": servidor, + "data": evento.data_inicio.date(), + } + ) + string = Template(template_string).render(context) + # return HttpResponse(string) + response = HttpResponse( + headers={ + "Content-Type": "application/pdf", + "Content-Disposition": 'attachment; filename="declaração.pdf"', + } + ) + pdf = HTML( + string=string, + url_fetcher=django_url_fetcher, + encoding="utf-8", + base_url=request.build_absolute_uri("/"), + ) + pdf.write_pdf(target=response) + return response + else: + form = SelecionaModeloForm() + + context = { + "form": form, + "evento_id": object_id, + "opts": self.model._meta, + "preserved_filters": self.get_preserved_filters(request), + } + return render( + request, "admin/eventos/evento/seleciona_modelo.html", context + ) + def gant_report(self, request, object_id): evento = get_object_or_404(Evento, id=object_id) cronograma = list( @@ -324,3 +417,109 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin): context=context, content_type="application/pdf", ) + + def create_course(self, request, object_id): + evento = get_object_or_404(Evento, id=object_id) + change_url = ( + reverse( + "admin:%s_%s_change" % self.get_model_info(), args=[object_id] + ) + + "?" + + self.get_preserved_filters(request) + ) + if evento.moodle_courseid is not None: + self.message_user( + request, + _("Este evento já tem curso associado no Saberes"), + level=messages.ERROR, + ) + return redirect(change_url) + if ( + evento.tipo_evento.moodle_template_courseid is None + or evento.tipo_evento.moodle_categoryid is None + ): + self.message_user( + request, + _("Este tipo de evento não possui template no Saberes"), + level=messages.ERROR, + ) + return redirect(change_url) + if evento.data_inicio is None or evento.data_termino is None: + self.message_user( + request, + _( + "O evento precisa ter datas de início e término para criar " + "curso no Saberes." + ), + level=messages.ERROR, + ) + return redirect(change_url) + + if evento.turma == "": + self.message_user( + request, + _( + "Preencha (e salve!) o campo Turma para poder criar o " + "curso no Saberes" + ), + level=messages.ERROR, + ) + return redirect(change_url) + + api_url = f"{settings.MOODLE_BASE_URL}/webservice/rest/server.php" + mws = Moodle(api_url, settings.MOODLE_API_TOKEN) + fullname = f"{evento.nome} - {evento.turma}" + shortname = f"{evento.tipo_evento.nome} - {evento.turma}" + inicio = int(time.mktime(evento.data_inicio.astimezone().timetuple())) + fim = int(time.mktime(evento.data_termino.astimezone().timetuple())) + try: + novo_curso = mws.core.course.duplicate_course( + evento.tipo_evento.moodle_template_courseid, + fullname=fullname, + shortname=shortname, + categoryid=evento.tipo_evento.moodle_categoryid, + visible=0, + ) + evento.moodle_courseid = novo_curso.id + evento.save() + changes = { + "id": novo_curso.id, + "summary": evento.descricao, + "startdate": inicio, + "enddate": fim, + } + res = mws.core.course.update_courses([changes]) + membros = evento.equipe_set.exclude( + membro__moodle_userid=None + ).exclude(funcao__moodle_roleid=None) + equipe = [] + for membro in membros: + equipe.append( + { + "roleid": membro.funcao.moodle_roleid, + "userid": membro.membro.moodle_userid, + "courseid": evento.moodle_courseid, + } + ) + mws.enrol.manual.enrol_users(equipe) + context = { + "evento": evento, + "fullname": fullname, + "shortname": shortname, + "membros": membros, + "opts": self.model._meta, + "preserved_filters": self.get_preserved_filters(request), + } + return render( + request, "admin/eventos/evento/createcourse.html", context + ) + except Exception as e: + self.message_user( + request, + _( + "Ocorreu um erro ao criar o curso no Saberes com " + f"a mensagem {e.message}" + ), + level=messages.ERROR, + ) + return redirect(change_url) diff --git a/sigi/apps/eventos/admin_urls.py b/sigi/apps/eventos/admin_urls.py index 9255f11..6496b3b 100644 --- a/sigi/apps/eventos/admin_urls.py +++ b/sigi/apps/eventos/admin_urls.py @@ -6,9 +6,4 @@ urlpatterns = [ path( "alocacaoequipe/", views.alocacao_equipe, name="eventos_alocacaoequipe" ), - path( - "evento//declaracao/", - views.declaracao, - name="evento-declaracao", - ), ] diff --git a/sigi/apps/eventos/forms.py b/sigi/apps/eventos/forms.py index 19dbb33..ff92fe6 100644 --- a/sigi/apps/eventos/forms.py +++ b/sigi/apps/eventos/forms.py @@ -30,7 +30,7 @@ class EventoAdminForm(forms.ModelForm): "total_participantes", "status", "publicar", - "link_inscricao", + "moodle_courseid", "contato", "telefone", "banner", diff --git a/sigi/apps/eventos/migrations/0031_evento_moodle_courseid.py b/sigi/apps/eventos/migrations/0031_evento_moodle_courseid.py new file mode 100644 index 0000000..b4bd01a --- /dev/null +++ b/sigi/apps/eventos/migrations/0031_evento_moodle_courseid.py @@ -0,0 +1,22 @@ +# Generated by Django 4.1.7 on 2023-04-14 15:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("eventos", "0030_alter_evento_descricao_alter_tipoevento_categoria"), + ] + + operations = [ + migrations.AddField( + model_name="evento", + name="moodle_courseid", + field=models.PositiveBigIntegerField( + blank=True, + help_text="ID do curso no Saberes. Este campo é preenchido automaticamente quando o curso é criado no Saberes.", + null=True, + verbose_name="ID do curso", + ), + ), + ] diff --git a/sigi/apps/eventos/migrations/0032_set_moodle_courseid.py b/sigi/apps/eventos/migrations/0032_set_moodle_courseid.py new file mode 100644 index 0000000..e26181c --- /dev/null +++ b/sigi/apps/eventos/migrations/0032_set_moodle_courseid.py @@ -0,0 +1,33 @@ +# Generated by Django 4.1.7 on 2023-04-14 15:02 + +from django.db import migrations + + +def set_courseid(apps, schema_editor): + Evento = apps.get_model("eventos", "Evento") + for evento in Evento.objects.exclude(link_inscricao=""): + if "=" in evento.link_inscricao: + evento.moodle_courseid = int(evento.link_inscricao.split("=")[1]) + evento.save() + + +def reset_courseid(apps, schema_editor): + from django.conf import settings + + Evento = apps.get_model("eventos", "Evento") + for evento in Evento.objects.exclude(moodle_courseid=None): + evento.link_inscricao = ( + settings.MOODLE_BASE_URL + + f"/course/view.php?id={evento.moodle_courseid}" + ) + evento.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("eventos", "0031_evento_moodle_courseid"), + ] + + operations = [ + migrations.RunPython(set_courseid, reset_courseid), + ] diff --git a/sigi/apps/eventos/migrations/0033_remove_evento_link_inscricao.py b/sigi/apps/eventos/migrations/0033_remove_evento_link_inscricao.py new file mode 100644 index 0000000..a24ffee --- /dev/null +++ b/sigi/apps/eventos/migrations/0033_remove_evento_link_inscricao.py @@ -0,0 +1,16 @@ +# Generated by Django 4.1.7 on 2023-04-14 15:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("eventos", "0032_set_moodle_courseid"), + ] + + operations = [ + migrations.RemoveField( + model_name="evento", + name="link_inscricao", + ), + ] diff --git a/sigi/apps/eventos/migrations/0034_funcao_moodle_roleid_tipoevento_moodle_categoryid_and_more.py b/sigi/apps/eventos/migrations/0034_funcao_moodle_roleid_tipoevento_moodle_categoryid_and_more.py new file mode 100644 index 0000000..326f449 --- /dev/null +++ b/sigi/apps/eventos/migrations/0034_funcao_moodle_roleid_tipoevento_moodle_categoryid_and_more.py @@ -0,0 +1,42 @@ +# Generated by Django 4.1.7 on 2023-04-14 17:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("eventos", "0033_remove_evento_link_inscricao"), + ] + + operations = [ + migrations.AddField( + model_name="funcao", + name="moodle_roleid", + field=models.PositiveBigIntegerField( + blank=True, + help_text="Código do papel do membro da equipe no Saberes", + null=True, + verbose_name="Papel Saberes", + ), + ), + migrations.AddField( + model_name="tipoevento", + name="moodle_categoryid", + field=models.PositiveBigIntegerField( + blank=True, + help_text="Código da categoria no Saberes onde o curso deve ser criado.", + null=True, + verbose_name="Categoria do curso", + ), + ), + migrations.AddField( + model_name="tipoevento", + name="moodle_template_courseid", + field=models.PositiveBigIntegerField( + blank=True, + help_text="Código do curso que serve de protótipo no Saberes para criação de novos eventos desse tipo.", + null=True, + verbose_name="Curso protótipo", + ), + ), + ] diff --git a/sigi/apps/eventos/migrations/0035_alter_evento_options.py b/sigi/apps/eventos/migrations/0035_alter_evento_options.py new file mode 100644 index 0000000..eaa0145 --- /dev/null +++ b/sigi/apps/eventos/migrations/0035_alter_evento_options.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.7 on 2023-04-18 19:57 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("eventos", "0034_funcao_moodle_roleid_tipoevento_moodle_categoryid_and_more"), + ] + + operations = [ + migrations.AlterModelOptions( + name="evento", + options={ + "ordering": ("-data_inicio",), + "permissions": [ + ("createcourse_evento", "Can create courses in Saberes platform") + ], + "verbose_name": "Evento", + "verbose_name_plural": "Eventos", + }, + ), + ] diff --git a/sigi/apps/eventos/models.py b/sigi/apps/eventos/models.py index cae3d21..28c4bb1 100644 --- a/sigi/apps/eventos/models.py +++ b/sigi/apps/eventos/models.py @@ -32,6 +32,23 @@ class TipoEvento(models.Model): _("Categoria"), max_length=1, choices=CATEGORIA_CHOICES ) casa_solicita = models.BooleanField(_("casa pode solicitar"), default=False) + moodle_template_courseid = models.PositiveBigIntegerField( + _("Curso protótipo"), + blank=True, + null=True, + help_text=_( + "Código do curso que serve de protótipo no Saberes para criação de " + "novos eventos desse tipo." + ), + ) + moodle_categoryid = models.PositiveBigIntegerField( + _("Categoria do curso"), + blank=True, + null=True, + help_text=_( + "Código da categoria no Saberes onde o curso deve ser criado." + ), + ) class Meta: ordering = ("nome",) @@ -129,7 +146,15 @@ class Evento(models.Model): ) status = models.CharField(_("Status"), max_length=1, choices=STATUS_CHOICES) publicar = models.BooleanField(_("publicar no site"), default=False) - link_inscricao = models.URLField(_("link de inscrição"), blank=True) + moodle_courseid = models.PositiveBigIntegerField( + _("ID do curso"), + blank=True, + null=True, + help_text=_( + "ID do curso no Saberes. Este campo é preenchido automaticamente " + "quando o curso é criado no Saberes." + ), + ) contato = models.CharField(_("contato"), max_length=100, blank=True) telefone = models.CharField( _("tefone de contato"), max_length=30, blank=True @@ -145,6 +170,9 @@ class Evento(models.Model): class Meta: ordering = ("-data_inicio",) verbose_name, verbose_name_plural = _("Evento"), _("Eventos") + permissions = [ + ("createcourse_evento", "Can create courses in Saberes platform"), + ] def __str__(self): return _( @@ -172,6 +200,17 @@ class Evento(models.Model): ).format(processo=self.num_processo, **m.groupdict()) return self.num_processo + @property + def link_inscricao(self): + if self.moodle_courseid is None: + return "" + from django.conf import settings + + return ( + settings.MOODLE_BASE_URL + + f"/course/view.php?id={self.moodle_courseid}" + ) + def save(self, *args, **kwargs): if self.status != Evento.STATUS_CANCELADO: self.data_cancelamento = None @@ -262,6 +301,12 @@ class Evento(models.Model): class Funcao(models.Model): nome = models.CharField(_("Função na equipe de evento"), max_length=100) descricao = models.TextField(_("Descrição da função")) + moodle_roleid = models.PositiveBigIntegerField( + _("Papel Saberes"), + blank=True, + null=True, + help_text=_("Código do papel do membro da equipe no Saberes"), + ) class Meta: ordering = ("nome",) diff --git a/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html b/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html index b72d9f4..1a2a1e2 100644 --- a/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html +++ b/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html @@ -3,26 +3,39 @@ {% block object-tools-items %} {% if object_id %} + {% if can_createcourse %} +
  • + {% url opts|admin_urlname:'createcourse' object_id|admin_urlquote as tool_url %} + + + {% trans "Criar curso no Saberes" %} + +
  • + {% endif %}
  • - + {% url opts|admin_urlname:'declaracaoreport' object_id|admin_urlquote as tool_url %} + {% trans "Declaração" %}
  • - + {% url opts|admin_urlname:'gantreport' object_id|admin_urlquote as tool_url %} + {% trans "Gráfico de Gant" %}
  • - + {% url opts|admin_urlname:'checklistreport' object_id|admin_urlquote as tool_url %} + {% trans "Checklist" %}
  • - + {% url opts|admin_urlname:'comunicacaoreport' object_id|admin_urlquote as tool_url %} + {% trans "Plano de comunicação" %} diff --git a/sigi/apps/eventos/templates/admin/eventos/evento/createcourse.html b/sigi/apps/eventos/templates/admin/eventos/evento/createcourse.html new file mode 100644 index 0000000..a59d87f --- /dev/null +++ b/sigi/apps/eventos/templates/admin/eventos/evento/createcourse.html @@ -0,0 +1,52 @@ +{% extends "admin/base_site.html" %} +{% load i18n static admin_urls %} + +{% block extrastyle %} + {{ block.super }} + +{% endblock %} + +{% block breadcrumbs %}{% endblock %} + +{% block messages %} + {% if error %} +
      +
    • {{ error|capfirst }}
    • +
    + {% endif %} +{% endblock messages %} + +{% block content %} +
    +
    +
    + {% trans 'Integração SIGI x Saberes: Criação de curso' %} +

    + {% blocktrans with courseid=evento.moodle_courseid courseurl=evento.link_inscricao|urlize trimmed %} + Criado curso no ambiente Saberes com ID {{ courseid }}, que pode ser + acessado em {{ courseurl }}. O nome completo do curso é "{{ fullname }}" + e o nome breve é "{{ shortname }}". + {% endblocktrans %} +

    + {% trans "Os seguintes servidores foram inscritos no curso:" %} +
      + {% for membro in membros %} +
    • {{ membro }}
    • + {% endfor %} +
    +
    +
    +
    +
    + {% url opts|admin_urlname:'change' evento_id|admin_urlquote as change_url %} + + undo + {% trans "Voltar" %} + +
    +
    +
    +
    +
    +{% endblock %} + diff --git a/sigi/apps/eventos/templates/eventos/seleciona_modelo.html b/sigi/apps/eventos/templates/admin/eventos/evento/seleciona_modelo.html similarity index 88% rename from sigi/apps/eventos/templates/eventos/seleciona_modelo.html rename to sigi/apps/eventos/templates/admin/eventos/evento/seleciona_modelo.html index e733c1c..ec5200f 100644 --- a/sigi/apps/eventos/templates/eventos/seleciona_modelo.html +++ b/sigi/apps/eventos/templates/admin/eventos/evento/seleciona_modelo.html @@ -1,5 +1,5 @@ {% extends "admin/base_site.html" %} -{% load i18n static %} +{% load i18n static admin_urls %} {% block extrastyle %} {{ block.super }} @@ -35,7 +35,8 @@ picture_as_pdf {% trans "Imprimir" %} - + {% url opts|admin_urlname:'change' evento_id|admin_urlquote as change_url %} + undo {% trans "Voltar" %} diff --git a/sigi/apps/eventos/views.py b/sigi/apps/eventos/views.py index c73923f..a3afaf4 100644 --- a/sigi/apps/eventos/views.py +++ b/sigi/apps/eventos/views.py @@ -114,64 +114,7 @@ def calendario(request): return render(request, "eventos/calendario.html", context) -@login_required -@staff_member_required -def declaracao(request, id): - if request.method == "POST": - form = SelecionaModeloForm(request.POST) - if form.is_valid(): - evento = get_object_or_404(Evento, id=id) - modelo = form.cleaned_data["modelo"] - membro = ( - evento.equipe_set.filter(assina_oficio=True).first() - or evento.equipe_set.first() - ) - if membro: - servidor = membro.membro - else: - servidor = None - template_string = ( - """ - {% extends "eventos/declaracao_pdf.html" %} - {% block text_body %}""" - + modelo.texto - + """ - {% endblock %} - """ - ) - context = Context( - { - "pagesize": modelo.formato, - "pagemargin": modelo.margem, - "evento": evento, - "servidor": servidor, - "data": timezone.localdate(), - } - ) - string = Template(template_string).render(context) - # return HttpResponse(string) - response = HttpResponse( - headers={ - "Content-Type": "application/pdf", - "Content-Disposition": 'attachment; filename="declaração.pdf"', - } - ) - pdf = HTML( - string=string, - url_fetcher=django_url_fetcher, - encoding="utf-8", - base_url=request.build_absolute_uri("/"), - ) - pdf.write_pdf(target=response) - return response - else: - form = SelecionaModeloForm() - - context = {"form": form, "evento_id": id} - return render(request, "eventos/seleciona_modelo.html", context) - - -class eventoListView(ListView): +class EventoListView(ListView): model = Evento paginate_by = 100 template_name = "eventos/lista.html" diff --git a/sigi/apps/servidores/admin.py b/sigi/apps/servidores/admin.py index 45b9564..1b0fee6 100644 --- a/sigi/apps/servidores/admin.py +++ b/sigi/apps/servidores/admin.py @@ -121,6 +121,7 @@ class ServidorAdmin(admin.ModelAdmin): "foto", "servico", "cargo", + "moodle_userid", ) }, ), diff --git a/sigi/apps/servidores/migrations/0013_servidor_moodle_userid.py b/sigi/apps/servidores/migrations/0013_servidor_moodle_userid.py new file mode 100644 index 0000000..0420d05 --- /dev/null +++ b/sigi/apps/servidores/migrations/0013_servidor_moodle_userid.py @@ -0,0 +1,22 @@ +# Generated by Django 4.1.7 on 2023-04-14 17:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("servidores", "0012_add_user_interlegis"), + ] + + operations = [ + migrations.AddField( + model_name="servidor", + name="moodle_userid", + field=models.PositiveBigIntegerField( + blank=True, + help_text="Código do usuário no Saberes", + null=True, + verbose_name="ID usuário Saberes", + ), + ), + ] diff --git a/sigi/apps/servidores/models.py b/sigi/apps/servidores/models.py index c7eccfc..8456a84 100644 --- a/sigi/apps/servidores/models.py +++ b/sigi/apps/servidores/models.py @@ -55,6 +55,12 @@ class Servidor(models.Model): Servico, on_delete=models.SET_NULL, blank=True, null=True ) cargo = models.CharField(max_length=100, blank=True) + moodle_userid = models.PositiveBigIntegerField( + _("ID usuário Saberes"), + blank=True, + null=True, + help_text=_("Código do usuário no Saberes"), + ) externo = models.BooleanField(_("colaborador externo"), default=False) orgao_origem = models.CharField( _("órgão de origem, "), max_length=100, blank=True @@ -93,6 +99,7 @@ User.servidor = property( else None ) + # Sinal para ao criar um usuário criar um servidor # baseado no nome contido no LDAP @receiver(post_save, sender=User) diff --git a/sigi/apps/utils/__init__.py b/sigi/apps/utils/__init__.py index a7acb48..6b89a69 100644 --- a/sigi/apps/utils/__init__.py +++ b/sigi/apps/utils/__init__.py @@ -94,3 +94,9 @@ def editor_help(field_name, Field_list): "home/editor_help_snippet.html", {"field_name": field_name, "placeholders": placeholders}, ) + + +def abreviatura(name): + for conector in [" da ", " de ", " do ", " das ", " dos ", " e "]: + name = name.replace(conector, " ") + return ("".join([w[0] for w in name.split()])).upper() diff --git a/sigi/settings.py b/sigi/settings.py index fea6d31..223b86b 100644 --- a/sigi/settings.py +++ b/sigi/settings.py @@ -261,3 +261,8 @@ TINYMCE_DEFAULT_CONFIG = { MENU_FILE = BASE_DIR / "menu_conf.yaml" HOSPEDAGEM_PATH = Path(env("HOSPEDAGEM_PATH", default="/tmp/HOSP/")) REGISTRO_PATH = Path(env("REGISTRO_PATH", default="/tmp/DNS/")) + +# Integração com Moodle + +MOODLE_BASE_URL = env("MOODLE_BASE_URL", default=None) +MOODLE_API_TOKEN = env("MOODLE_API_TOKEN", default=None)