Browse Source

Integração SIGI x Saberes

pull/163/head
Sesostris Vieira 2 years ago
parent
commit
41fe440e5b
  1. 2
      docker/Dockerfile
  2. 1
      requirements/requirements.txt
  3. 209
      sigi/apps/eventos/admin.py
  4. 5
      sigi/apps/eventos/admin_urls.py
  5. 2
      sigi/apps/eventos/forms.py
  6. 22
      sigi/apps/eventos/migrations/0031_evento_moodle_courseid.py
  7. 33
      sigi/apps/eventos/migrations/0032_set_moodle_courseid.py
  8. 16
      sigi/apps/eventos/migrations/0033_remove_evento_link_inscricao.py
  9. 42
      sigi/apps/eventos/migrations/0034_funcao_moodle_roleid_tipoevento_moodle_categoryid_and_more.py
  10. 23
      sigi/apps/eventos/migrations/0035_alter_evento_options.py
  11. 47
      sigi/apps/eventos/models.py
  12. 21
      sigi/apps/eventos/templates/admin/eventos/evento/change_form.html
  13. 52
      sigi/apps/eventos/templates/admin/eventos/evento/createcourse.html
  14. 5
      sigi/apps/eventos/templates/admin/eventos/evento/seleciona_modelo.html
  15. 59
      sigi/apps/eventos/views.py
  16. 1
      sigi/apps/servidores/admin.py
  17. 22
      sigi/apps/servidores/migrations/0013_servidor_moodle_userid.py
  18. 7
      sigi/apps/servidores/models.py
  19. 6
      sigi/apps/utils/__init__.py
  20. 5
      sigi/settings.py

2
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 \

1
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

209
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(
"<path:object_id>/declaracao/",
self.admin_site.admin_view(self.declaracao_report),
name="%s_%s_declaracaoreport" % self.get_model_info(),
),
path(
"<path:object_id>/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(
"<path:object_id>/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)

5
sigi/apps/eventos/admin_urls.py

@ -6,9 +6,4 @@ urlpatterns = [
path(
"alocacaoequipe/", views.alocacao_equipe, name="eventos_alocacaoequipe"
),
path(
"evento/<int:id>/declaracao/",
views.declaracao,
name="evento-declaracao",
),
]

2
sigi/apps/eventos/forms.py

@ -30,7 +30,7 @@ class EventoAdminForm(forms.ModelForm):
"total_participantes",
"status",
"publicar",
"link_inscricao",
"moodle_courseid",
"contato",
"telefone",
"banner",

22
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",
),
),
]

33
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),
]

16
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",
),
]

42
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",
),
),
]

23
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",
},
),
]

47
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",)

21
sigi/apps/eventos/templates/admin/eventos/evento/change_form.html

@ -3,26 +3,39 @@
{% block object-tools-items %}
{% if object_id %}
{% if can_createcourse %}
<li>
<a href="{% url 'evento-declaracao' object_id %}">
{% url opts|admin_urlname:'createcourse' object_id|admin_urlquote as tool_url %}
<a href="{% add_preserved_filters tool_url %}">
<i class="left material-icons" aria-hidden="true">create_new_folder</i>
{% trans "Criar curso no Saberes" %}
</a>
</li>
{% endif %}
<li>
{% url opts|admin_urlname:'declaracaoreport' object_id|admin_urlquote as tool_url %}
<a href="{% add_preserved_filters tool_url %}">
<i class="left material-icons" aria-hidden="true">picture_as_pdf</i>
{% trans "Declaração" %}
</a>
</li>
<li>
<a href="{% url opts|admin_urlname:'gantreport' object_id %}">
{% url opts|admin_urlname:'gantreport' object_id|admin_urlquote as tool_url %}
<a href="{% add_preserved_filters tool_url %}">
<i class="left material-icons" aria-hidden="true">insert_chart</i>
{% trans "Gráfico de Gant" %}
</a>
</li>
<li>
<a href="{% url opts|admin_urlname:'checklistreport' object_id %}">
{% url opts|admin_urlname:'checklistreport' object_id|admin_urlquote as tool_url %}
<a href="{% add_preserved_filters tool_url %}">
<i class="left material-icons" aria-hidden="true">picture_as_pdf</i>
{% trans "Checklist" %}
</a>
</li>
<li>
<a href="{% url opts|admin_urlname:'comunicacaoreport' object_id %}">
{% url opts|admin_urlname:'comunicacaoreport' object_id|admin_urlquote as tool_url %}
<a href="{% add_preserved_filters tool_url %}">
<i class="left material-icons" aria-hidden="true">picture_as_pdf</i>
{% trans "Plano de comunicação" %}
</a>

52
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 }}
<link rel="stylesheet" href="{% static 'material/admin/css/submit_line.min.css' %}">
{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block messages %}
{% if error %}
<ul class="messagelist">
<li class="error">{{ error|capfirst }}</li>
</ul>
{% endif %}
{% endblock messages %}
{% block content %}
<div class="container">
<div class="card">
<div class="card-content">
<span class="card-title">{% trans 'Integração SIGI x Saberes: Criação de curso' %}</span>
<p>
{% 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 %}
</p>
<span class="card-title">{% trans "Os seguintes servidores foram inscritos no curso:" %}</span>
<ul>
{% for membro in membros %}
<li>{{ membro }}</li>
{% endfor %}
</ul>
</div>
<div class="card-action">
<div class="submit-row">
<div class="open-actions">
{% url opts|admin_urlname:'change' evento_id|admin_urlquote as change_url %}
<a class="default waves-effect waves-light btn" role="button" href="{% add_preserved_filters change_url %}">
<i class="material-icons">undo</i>
{% trans "Voltar" %}
</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

5
sigi/apps/eventos/templates/eventos/seleciona_modelo.html → 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 @@
<i class="material-icons">picture_as_pdf</i>
{% trans "Imprimir" %}
</button>
<a class="default waves-effect waves-light btn" role="button" href="{% url 'admin:eventos_evento_change' evento_id %}">
{% url opts|admin_urlname:'change' evento_id|admin_urlquote as change_url %}
<a class="default waves-effect waves-light btn" role="button" href="{% add_preserved_filters change_url %}">
<i class="material-icons">undo</i>
{% trans "Voltar" %}
</a>

59
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"

1
sigi/apps/servidores/admin.py

@ -121,6 +121,7 @@ class ServidorAdmin(admin.ModelAdmin):
"foto",
"servico",
"cargo",
"moodle_userid",
)
},
),

22
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",
),
),
]

7
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)

6
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()

5
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)

Loading…
Cancel
Save