feat Elaboración de las memorias (1)

parent ffd5b910
Pipeline #612 failed with stage
in 0 seconds
......@@ -14,10 +14,12 @@ import zeep
from django.contrib.auth.models import AbstractUser, UserManager
from django.core.exceptions import ValidationError
from django.db import models
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
# local Django
from .pipeline import get_identidad
from indo.models import Centro, Departamento
class CustomUserManager(UserManager):
......@@ -64,6 +66,22 @@ class CustomUser(AbstractUser):
)
colectivos = models.CharField(max_length=127, blank=True, null=True)
@property
def nombres_centros(self):
"""Devuelve una cadena con los nombres de los centros del usuario."""
id_nk_centros = json.loads(self.centro_id_nks) if self.centro_id_nks else []
centros = [get_object_or_404(Centro, academico_id_nk=id_nk) for id_nk in id_nk_centros]
return ', '.join([centro.nombre for centro in centros])
@property
def nombres_departamentos(self):
"""Devuelve una cadena con los nombres de los departamentos del usuario."""
id_nk_depts = json.loads(self.departamento_id_nks) if self.departamento_id_nks else []
departamentos = [
get_object_or_404(Departamento, academico_id_nk=id_nk) for id_nk in id_nk_depts
]
return ', '.join([departamento.nombre for departamento in departamentos])
@property
def full_name(self):
"""Devuelve el nombre completo (nombre y los dos apellidos)."""
......
......@@ -522,25 +522,49 @@ class Proyecto(models.Model):
except ParticipanteProyecto.DoesNotExist:
return None
def get_coordinador(self):
@property
def coordinador(self):
"""Devuelve el usuario coordinador del proyecto"""
coordinador = self.get_participante_or_none('coordinador')
return coordinador.usuario if coordinador else None
def get_coordinador_2(self):
def get_coordinador(self):
"""Devuelve el usuario coordinador del proyecto"""
return self.coordinador
@property
def coordinador_2(self):
"""Devuelve el segundo coordinador del proyecto (los PIET pueden tener 2)."""
coordinador_2 = self.get_participante_or_none('coordinador_2')
return coordinador_2.usuario if coordinador_2 else None
def get_coordinador_2(self):
"""Devuelve el segundo coordinador del proyecto (los PIET pueden tener 2)."""
return self.coordinador_2
def get_coordinadores(self):
"""Devuelve los usuarios coordinadores del proyecto."""
coordinadores = [self.get_coordinador(), self.get_coordinador_2()]
coordinadores = [self.coordinador, self.coordinador_2]
return list(filter(None, coordinadores))
def get_dict_valoraciones(self):
"""Devuelve un diccionario criterio_id => valoración con las valoraciones del proyecto."""
return {valoracion.criterio_id: valoracion for valoracion in self.valoraciones.all()}
def get_dict_respuestas_memoria(self):
"""Devuelve un diccionario subapartado_id => respuesta de la memoria del proyecto."""
return {respuesta.subapartado_id: respuesta for respuesta in self.respuestas_memoria.all()}
@property
def usuarios_participantes(self):
"""Devuelve una lista de los usuarios participantes en el proyecto."""
participantes_proyecto = (
self.participantes.filter(tipo_participacion='participante')
.order_by('usuario__first_name', 'usuario__last_name')
.all()
)
return [pp.usuario for pp in participantes_proyecto]
def get_usuarios_vinculados(self):
"""
Devuelve todos los usuarios vinculados al proyecto
......
......@@ -8,6 +8,8 @@ from .views import (
EvaluacionView,
HomePageView,
InvitacionView,
MemoriaDetailView,
MemoriaPresentarView,
ParticipanteAceptarView,
ParticipanteDeclinarView,
ParticipanteDeleteView,
......@@ -99,6 +101,8 @@ urlpatterns = [
ParticipanteDeleteView.as_view(),
name='participante_delete',
),
path('memoria/<int:pk>/', MemoriaDetailView.as_view(), name='memoria_detail'),
path('memoria/<int:pk>/presentar/', MemoriaPresentarView.as_view(), name='memoria_presentar'),
path('proyecto/new/', ProyectoCreateView.as_view(), name='proyecto_new'),
path('proyecto/<int:pk>/', ProyectoDetailView.as_view(), name='proyecto_detail'),
path(
......
......@@ -41,6 +41,7 @@ from .models import (
Convocatoria,
Criterio,
Evento,
MemoriaApartado,
ParticipanteProyecto,
Plan,
Proyecto,
......@@ -352,7 +353,7 @@ class ProyectoEvaluadorUpdateView(LoginRequiredMixin, PermissionRequiredMixin, U
return super().get(request, *args, **kwargs)
def get_success_url(self):
return reverse('evaluadores_table', kwargs={'anyo': self.object.convocatoria})
return reverse('evaluadores_table', kwargs={'anyo': self.object.convocatoria.id})
class ProyectoResolucionUpdateView(
......@@ -597,6 +598,35 @@ class ProyectoAnularView(LoginRequiredMixin, ChecksMixin, RedirectView):
return self.es_coordinador(self.kwargs['pk'])
class MemoriaDetailView(LoginRequiredMixin, TemplateView): # TODO: permisos
"""Muestra la memoria del proyecto indicado."""
template_name = 'memoria/detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
proyecto = get_object_or_404(Proyecto, pk=kwargs['pk'])
context['proyecto'] = proyecto
context['apartados'] = proyecto.convocatoria.apartados_memoria.all()
context['dict_respuestas'] = proyecto.get_dict_respuestas_memoria()
return context
class MemoriaPresentarView(LoginRequiredMixin, ChecksMixin, RedirectView):
"""Presenta la memoria final de proyecto.
El proyecto pasa de estado «Aceptado» a estado «Memoria presentada».
TODO: ¿Enviar correos al corrector y al coordinador?
"""
pass
def test_func(self):
# TODO: Comprobar fecha y estado
return self.es_coordinador(self.kwargs['pk'])
class ProyectoDetailView(LoginRequiredMixin, ChecksMixin, DetailView):
"""Muestra una solicitud de proyecto."""
......
......@@ -14,6 +14,10 @@ h1 > small {
color: gray;
}
h2 > small {
color: gray;
}
/*** Navbar ***/
.bg-azul {
background-color: #213c71;
......@@ -239,3 +243,21 @@ h1 > small {
.dataTables_length {
margin-bottom: 1em;
}
@media print {
.nav, .breadcrumb, .footer, .navbar, .noprint {
display: none !important;
}
/* Sobrescribo esta propiedad de Bootstrap para que
* al imprimir no se añada la URL a los enlaces.
*/
a[href]:after {
content: "";
}
@page {
size: A4;
}
}
{% extends 'base.html' %}
{% load custom_tags i18n %}
{% block title %}{% trans 'Memoria final' %}: {{ proyecto.titulo }}{% endblock title %}
{% block content %}
<div class="container-blanco">
<h1 id="cabecera">{% trans 'Memoria final' %}</h1>
<h2><small>{% trans 'Proyectos de Innovación Docente' %} {{proyecto.convocatoria}}</small></h2>
<hr />
<br />
<div class="alert-info alert fade-in noprint">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<span class="fas fa-info-circle"></span>
{% trans 'Aquí puede ver y editar la memoria del proyecto.' %}<br />
{% trans 'Pulse el botón «<strong>Guardar</strong>» para almacenar sus cambios.' %}
{% trans 'Puede editar su memoria tantas veces como desee.' %}<br />
{% trans 'Cuando esté satisfecho, pulse el botón «<strong>Presentar</strong>».' %}
{% trans 'Una vez haya presentado la memoria, ya no podrá modificarla.' %}
</div>
<br />
<h3>1. {% trans 'Identificación del proyecto' %}</h3>
<div class="table-responsive">
<table class="table table-hover table-striped table-sm" aria-describedby="datos del proyecto">
<tr>
<th scope="row"><strong>{% trans 'Título' %}</strong>:</th>
<td>{{ proyecto.titulo }}</td>
</tr>
<tr>
<th scope="row"><strong>{% trans 'Programa' %}</strong>:</th>
<td>{{ proyecto.programa.nombre_corto }} ({{ proyecto.programa.nombre_largo }})</td>
</tr>
{% if proyecto.linea %}
<tr>
<th scope="row"><strong>{% trans 'Línea' %}</strong>:</th>
<td>{{ proyecto.linea.nombre }}</td>
</tr>
{% endif %}
{% if proyecto.centro %}
<tr>
<th scope="row"><strong>{% trans 'Centro' %}</strong>:</th>
<td>{{ proyecto.centro.nombre }}</td>
</tr>
{% endif %}
{% if proyecto.estudio %}
<tr>
<th scope="row"><strong>{% trans 'Estudio' %}</strong>:</th>
<td>{{ proyecto.estudio.nombre }}</td>
</tr>
{% endif %}
</table>
</div>
<br />
<h3>2. {% trans 'Coordinadores del proyecto' %}</h3>
<div class="table-responsive">
{% if proyecto.coordinador %}
<table class="table table-hover table-striped table-sm"
aria-describedby="coordinador">
<tr>
<th scope="row" style="font-weight: bold;">{% trans 'Coordinador' %}</th>
<td>{{ proyecto.coordinador.full_name }}</td>
</tr>
<tr>
<th scope="row" style="font-weight: bold;">{% trans 'Correo electrónico' %}</th>
<td>{{ proyecto.coordinador.email }}</td>
</tr>
<tr>
<th scope="row" style="font-weight: bold;">{% trans 'Departamento' %}</th>
<td>{{ proyecto.coordinador.nombres_departamentos }}</td>
</tr>
<tr>
<th scope="row" style="font-weight: bold;">{% trans 'Centro' %}</th>
<td>{{ proyecto.coordinador.nombres_centros }}</td>
</tr>
</table>
{% endif %}
{% if proyecto.coordinador_2 %}
<table class="table table-hover table-striped table-sm"
aria-describedby="coordinador 2">
<tr>
<th scope="row" style="font-weight: bold;">{% trans 'Coordinador 2' %}</th>
<td>{{ proyecto.coordinador_2.full_name }}</td>
</tr>
<tr>
<th scope="row" style="font-weight: bold;">{% trans 'Correo electrónico' %}</th>
<td>{{ proyecto.coordinador_2.email }}</td>
</tr>
<tr>
<th scope="row" style="font-weight: bold;">{% trans 'Departamento' %}</th>
<td>{{ proyecto.coordinador_2.nombres_departamentos }}</td>
</tr>
<tr>
<th scope="row" style="font-weight: bold;">{% trans 'Centro' %}</th>
<td>{{ proyecto.coordinador_2.nombres_centros }}</td>
</tr>
</table>
{% endif %}
</div>
<br />
<h3>3. {% trans 'Resumen del proyecto' %}</h3>
<div>{{ proyecto.descripcion | limpiar }}</div>
<br />
<h3>4. {% trans 'Participantes en el proyecto' %}</h3>
<div class="table-responsive">
<table class="table table-hover table-striped table-sm cabecera-azul"
aria-describedby="participantes">
<thead>
<tr>
<th scope="col">{% trans "Nombre y apellidos" %}</th>
<th scope="col">{% trans "Correo electrónico" %}</th>
<th scope="col">{% trans "Departamento" %}</th>
<th scope="col">{% trans "Centro" %}</th>
</tr>
</thead>
{% for participante in proyecto.usuarios_participantes %}
<tr>
<td>{{ participante.full_name }}</td>
<td>{{ participante.email }}</td>
<td>{{ participante.nombres_departamentos }}</td>
<td>{{ participante.nombres_centros }}</td>
</tr>
{% endfor %}
</table>
</div>
<br />
<form action="" method="post">
{% csrf_token %}
{% for apartado in apartados %}
<h3>{{ apartado.numero }}. {{ apartado.descripcion }}</h3>
{% for subapartado in apartado.subapartados.all %}
<p><strong>{{ subapartado.descripcion }}</strong></p>
<p class="noprint" style="color: gray;">{{ subapartado.ayuda }}</p>
{% with respuesta=dict_respuestas|get_item:subapartado.id %}
{% if subapartado.tipo == 'texto' %}
<textarea class="textarea form-control" name="{{ subapartado.id }}"
placeholder="{% trans 'Introduzca sus comentarios.' %}" rows="10"
cols="80">{% if respuesta %}{{ respuesta.texto }}{% endif %}</textarea>
{% elif subapartado.tipo == 'fichero' %}
TODO: Enviar PDF
{% endif %}
{% endwith %}
<br />
{% endfor %}
{% endfor %}
<!-- Botones -->
<br style="clear: both;" />
<div class="btn-group noprint" role="group" aria-label="Botones">
<a href="{% url 'proyecto_detail' proyecto.id %}" class="btn btn-info"
title="{% trans 'Cancelar' %}">
<span class="fas fa-times"></span> {% trans 'Cancelar' %}
</a>
{# TODO if permitir_edicion #}
<button class="btn btn-success" type="button">
<span class="far fa-save"></span> {% trans 'Guardar' %}
</button>
{# endif #}
{# TODO if es_coordinador #}
<button class="btn btn-warning" data-toggle="modal" data-target="#presentarModal"
type="button">
<span class="fas fa-file-export"></span> {% trans 'Presentar' %}
</button>
{# endif #}
</div>
</form>
</div>
<div class="modal fade" id="presentarModal" tabindex="-1" role="dialog"
aria-labelledby="presentarModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="presentarModalLabel">
{% trans "¿Seguro que desea presentar la memoria?" %}
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Cerrar">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>{% trans "Una vez presentada ya no podrá hacer ningún cambio." %}</p>
<p>
{% blocktrans %}Se generará el PDF final de la memoria, y el corrector
podrá aprobarla o rechazarla.{% endblocktrans %}
</p>
</div>
<div class="modal-footer">
<form action="{% url 'memoria_presentar' pk=proyecto.id %}"
id="presentar-form" method="post">
{% csrf_token %}
<button type="button" class="btn btn-info" data-dismiss="modal">
<span class="fas fa-times"></span> {% trans "Cancelar" %}
</button>
<button type="submit" class="btn btn-warning">
<span class="fas fa-check"></span> {% trans "Presentar" %}
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock content %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment