feat Visto bueno de los directores/decanos

parent a7d88737
Pipeline #373 passed with stage
in 6 seconds
# Generated by Django 2.2.1 on 2019-06-11 08:19
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [("indo", "0005_auto_20190606_1412")]
operations = [
migrations.AddField(
model_name="programa",
name="requiere_visto_bueno",
field=models.BooleanField(
default="False",
verbose_name="¿Requiere el visto bueno del director o decano?",
),
),
migrations.AddField(
model_name="proyecto",
name="visto_bueno",
field=models.BooleanField(null=True, verbose_name="Visto bueno"),
),
migrations.AlterField(
model_name="proyecto",
name="centro",
field=models.ForeignKey(
blank=True,
help_text="Sólo obligatorio para PIEC, PRACUZ, PIPOUZ.",
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="proyectos",
to="indo.Centro",
),
),
]
......@@ -118,7 +118,7 @@ class Estudio(models.Model):
)
id = models.PositiveSmallIntegerField(_("Cód. estudio"), primary_key=True)
nombre = models.CharField(max_length=255)
esta_activo = models.BooleanField("¿Activo?", default=True)
esta_activo = models.BooleanField(_("¿Activo?"), default=True)
rama = models.CharField(max_length=1, choices=OPCIONES_RAMA)
tipo_estudio = models.ForeignKey("TipoEstudio", on_delete=models.PROTECT)
......@@ -206,6 +206,9 @@ class Programa(models.Model):
)
campos = models.TextField(null=True)
convocatoria = models.ForeignKey("Convocatoria", on_delete=models.PROTECT)
requiere_visto_bueno = models.BooleanField(
_("¿Requiere el visto bueno del director o decano?"), default="False"
)
def __str__(self):
return f"{self.nombre_corto}"
......@@ -439,6 +442,7 @@ class Proyecto(models.Model):
blank=True,
null=True,
help_text=_("Sólo obligatorio para PIEC, PRACUZ, PIPOUZ."),
related_name="proyectos",
)
convocatoria = models.ForeignKey("Convocatoria", on_delete=models.PROTECT)
departamento = models.ForeignKey(
......@@ -466,6 +470,7 @@ class Proyecto(models.Model):
on_delete=models.PROTECT,
limit_choices_to={"convocatoria_id": date.today().year},
)
visto_bueno = models.BooleanField(_("Visto bueno"), null=True)
def en_borrador(self):
return self.estado == "BORRADOR"
......
......@@ -19,6 +19,7 @@ from templated_email import send_templated_mail
from .forms import InvitacionForm, ProyectoForm
from .models import (
Centro,
Convocatoria,
Evento,
ParticipanteProyecto,
......@@ -93,6 +94,20 @@ class ChecksMixin(UserPassesTestMixin):
for col_autorizado in ["PAS", "ADS", "PDI"]
)
def es_decano_o_director(self, proyecto_id):
"""Devuelve si el usuario actual es decano o director del proyecto indicado."""
usuario_actual = self.request.user
proyecto = get_object_or_404(Proyecto, pk=proyecto_id)
centro = proyecto.centro
if not centro:
return False
nip_decano = centro.nip_decano
self.permission_denied_message = _(
"Usted no es decano/director del centro del proyecto."
)
return usuario_actual.username == str(nip_decano)
class AyudaView(TemplateView):
template_name = "ayuda.html"
......@@ -144,7 +159,7 @@ class ParticipanteAceptarView(LoginRequiredMixin, RedirectView):
proyecto = get_object_or_404(Proyecto, pk=proyecto_id)
num_equipos = usuario_actual.get_num_equipos(proyecto.convocatoria_id)
num_max_equipos = Convocatoria.objects.order_by("-id").first().num_max_equipos
num_max_equipos = proyecto.convocatoria.num_max_equipos
if num_equipos >= num_max_equipos:
messages.error(
request,
......@@ -335,7 +350,10 @@ class ProyectoDetailView(LoginRequiredMixin, ChecksMixin, DetailView):
def test_func(self):
# TODO: Los evaluadores y gestores también tendrán que tener acceso.
return self.esta_vinculado(self.kwargs["pk"])
proyecto_id = self.kwargs["pk"]
return self.esta_vinculado(proyecto_id) or self.es_decano_o_director(
proyecto_id
)
class ProyectoPresentarView(LoginRequiredMixin, ChecksMixin, RedirectView):
......@@ -355,7 +373,7 @@ class ProyectoPresentarView(LoginRequiredMixin, ChecksMixin, RedirectView):
# TODO ¿Chequear el estado actual del proyecto?
num_equipos = self.request.user.get_num_equipos(proyecto.convocatoria_id)
num_max_equipos = Convocatoria.objects.order_by("-id").first().num_max_equipos
num_max_equipos = proyecto.convocatoria.num_max_equipos
if num_equipos >= num_max_equipos:
messages.error(
request,
......@@ -485,7 +503,7 @@ class ProyectoUpdateFieldView(LoginRequiredMixin, ChecksMixin, UpdateView):
):
raise Http404(_("No puede editar ese campo."))
if campo not in ("titulo", "departamento", "licencia", "ayuda"):
if campo not in ("titulo", "departamento", "licencia", "ayuda", "visto_bueno"):
formulario = modelform_factory(
Proyecto, fields=(campo,), widgets={campo: SummernoteWidget()}
)
......@@ -513,7 +531,10 @@ class ProyectoUpdateFieldView(LoginRequiredMixin, ChecksMixin, UpdateView):
return super().get_form_class()
def test_func(self):
return self.es_coordinador(self.kwargs["pk"])
return self.es_coordinador(self.kwargs["pk"]) or (
self.kwargs["campo"] == "visto_bueno"
and self.es_decano_o_director(self.kwargs["pk"])
)
class ProyectosUsuarioView(LoginRequiredMixin, TemplateView):
......@@ -556,4 +577,17 @@ class ProyectosUsuarioView(LoginRequiredMixin, TemplateView):
.all()
)
try:
nip_decano = int(usuario.username)
except ValueError:
nip_decano = 0
centros_dirigidos = Centro.objects.filter(nip_decano=nip_decano).all()
if centros_dirigidos:
context["proyectos_centros_dirigidos"] = Proyecto.objects.filter(
convocatoria_id=anyo,
programa__requiere_visto_bueno=True,
centro__in=centros_dirigidos,
).all()
return context
......@@ -44,6 +44,9 @@
<tr><td><strong>{{ coordinador.get_cargo }}</strong>:</td><td>{{ coordinador.usuario.get_full_name }}</td></tr>
{% endif %}
<tr><td><strong>{% trans 'Estado' %}</strong>:</td><td>{{ proyecto.get_estado_display }}</td></tr>
{% if proyecto.programa.requiere_visto_bueno %}
<tr><td><strong>{% trans 'Visto bueno' %}</strong>:</td><td>{{ proyecto.visto_bueno | yesno:"Sí,No,—" }}</td></tr>
{% endif %}
</table>
</div><br />
......
......@@ -113,6 +113,44 @@
{% else %}
<p>{% trans 'No está invitado a ningún proyecto.' %}</p>
{% endif %}
<br /><br />
{% if proyectos_centros_dirigidos %}
<h1>{% trans "Proyectos impulsados por mi centro" %}</h1>
<hr />
<br />
<div class="table-responsive">
<table class="table table-hover table-striped table-sm">
<tr>
<th>{% trans 'Programa' %}</th>
<th>{% trans 'Línea' %}</th>
<th>{% trans 'Título' %}</th>
<th>{% trans 'Coordinador' %}</th>
<th>{% trans 'Estado' %}</th>
<th>{% trans 'Visto bueno '%}</th>
<th>{% trans 'Acción '%}</th>
</tr>
{% for proyecto in proyectos_centros_dirigidos %}
<tr>
<td>{{proyecto.programa.nombre_corto}}</td>
<td>{{proyecto.linea.nombre}}</td>
<td><a href="{% url 'proyecto_detail' proyecto.id %}">{{ proyecto.titulo }}</a></td>
<td>{{proyecto.get_usuario_coordinador.get_full_name}}</td>
<td>{{proyecto.get_estado_display}}</td>
<td>{{proyecto.visto_bueno | yesno:"Sí,No,—"}}</td>
<td>
{% if proyecto.estado == 'SOLICITADO' %}
<a href="{% url 'proyecto_update_field' proyecto.id "visto_bueno" %}" class="btn btn-info btn-sm">
<span class="fas fa-pencil-alt" aria-hidden="true"></span>&nbsp; {% trans 'Editar' %}
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
</div>
<div class="modal fade" id="declinarModal" tabindex="-1" role="dialog" aria-labelledby="declinarModalLabel" aria-hidden="true">
......
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