feat: Asignar evaluadores a proyectos.

Mostrar a los evaluadores los proyectos que deben evaluar.
parent 6941edac
Pipeline #569 failed with stage
in 6 seconds
[flake8]
exclude=**/settings*.py,**/__init__.py,**/migrations/*.py
ignore = W503
max-line-length = 119
max-complexity = 10
select = B,C,E,F,W
{
"_meta": {
"hash": {
"sha256": "df14818d58d397826ad261de871adf3c7de53cd62b1a61e6ea07c7d9d0c236b4"
"sha256": "f3ec7e8321611c5532d469333bd4c7101d7ea624065306db28c9dc5a3947b3c4"
},
"pipfile-spec": 6,
"requires": {
......@@ -405,12 +405,19 @@
}
},
"develop": {
"asgiref": {
"hashes": [
"sha256:8036f90603c54e93521e5777b2b9a39ba1bad05773fcf2d208f0299d1df58ce5",
"sha256:9ca8b952a0a9afa61d30aa6d3d9b570bb3fd6bafcf7ec9e6bed43b936133db1c"
],
"version": "==3.2.7"
},
"astroid": {
"hashes": [
"sha256:29fa5d46a2404d01c834fcb802a3943685f1fc538eb2a02a161349f5505ac196",
"sha256:2fecea42b20abb1922ed65c7b5be27edfba97211b04b2b6abc6a43549a024ea6"
"sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1",
"sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38"
],
"version": "==2.4.0"
"version": "==2.4.1"
},
"coverage": {
"hashes": [
......@@ -449,6 +456,22 @@
"index": "pypi",
"version": "==5.1"
},
"django": {
"hashes": [
"sha256:051ba55d42daa3eeda3944a8e4df2bc96d4c62f94316dea217248a22563c3621",
"sha256:9aaa6a09678e1b8f0d98a948c56482eac3e3dd2ddbfb8de70a868135ef3b5e01"
],
"index": "pypi",
"version": "==3.0.6"
},
"django-debug-toolbar": {
"hashes": [
"sha256:eabbefe89881bbe4ca7c980ff102e3c35c8e8ad6eb725041f538988f2f39a943",
"sha256:ff94725e7aae74b133d0599b9bf89bd4eb8f5d2c964106e61d11750228c8774c"
],
"index": "pypi",
"version": "==2.2"
},
"isort": {
"hashes": [
"sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
......@@ -491,11 +514,18 @@
},
"pylint": {
"hashes": [
"sha256:588e114e3f9a1630428c35b7dd1c82c1c93e1b0e78ee312ae4724c5e1a1e0245",
"sha256:bd556ba95a4cf55a1fc0004c00cf4560b1e70598a54a74c6904d933c8f3bd5a8"
"sha256:357e30b0cce26b078e58a6a668afdee9b9a04bd25ec982691f2f95c26301e674",
"sha256:f1a99799b1748bc5241e9a6b5a518893b34e2fd6245460207dec71f46c0abc17"
],
"index": "pypi",
"version": "==2.5.0"
"version": "==2.5.1"
},
"pytz": {
"hashes": [
"sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed",
"sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"
],
"version": "==2020.1"
},
"rope": {
"hashes": [
......@@ -513,6 +543,13 @@
],
"version": "==1.14.0"
},
"sqlparse": {
"hashes": [
"sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e",
"sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"
],
"version": "==0.3.1"
},
"toml": {
"hashes": [
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
......
# Standard library
import json
# Third-party
from social_django.models import UserSocialAuth
from social_django.utils import load_strategy
# Django
from django.contrib.auth.models import AbstractUser, UserManager
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
# local Django
from .pipeline import get_identidad
class CustomUserManager(UserManager):
def get_or_none(self, **kwargs):
......@@ -19,28 +29,42 @@ class CustomUserManager(UserManager):
class CustomUser(AbstractUser):
AbstractUser._meta.get_field('username').verbose_name = _('NIP') # Cambio verbose_name
AbstractUser._meta.get_field('last_name').verbose_name = _('primer apellido') # Cambio verbose_name
AbstractUser._meta.get_field('last_name').verbose_name = _(
'primer apellido'
) # Cambio verbose_name
# Campos sobrescritos
first_name = models.CharField(_('first name'), max_length=50, blank=True) # era: max_length=30
# Campos adicionales
numero_documento = models.CharField(
_('número de documento'), max_length=16, blank=True, null=True, help_text=_('DNI, NIE o pasaporte.')
_('número de documento'),
max_length=16,
blank=True,
null=True,
help_text=_('DNI, NIE o pasaporte.'),
)
tipo_documento = models.CharField(
_('tipo de documento'), max_length=3, blank=True, null=True, help_text=_('DNI, NIE o pasaporte.')
_('tipo de documento'),
max_length=3,
blank=True,
null=True,
help_text=_('DNI, NIE o pasaporte.'),
)
last_name_2 = models.CharField(_('segundo apellido'), max_length=150, blank=True, null=True)
sexo = models.CharField(max_length=1, blank=True, null=True)
sexo_oficial = models.CharField(max_length=1, blank=True, null=True)
nombre_oficial = models.CharField(max_length=50, blank=True, null=True)
centro_id_nks = models.CharField(_('Cód. centros'), max_length=127, blank=True, null=True)
departamento_id_nks = models.CharField(_('Cód. departamentos'), max_length=127, blank=True, null=True)
departamento_id_nks = models.CharField(
_('Cód. departamentos'), max_length=127, blank=True, null=True
)
colectivos = models.CharField(max_length=127, blank=True, null=True)
@property
def full_name(self):
"""Devuelve el nombre completo (nombre y los dos apellidos)."""
return ' '.join(part.strip() for part in (self.first_name, self.last_name, self.last_name_2) if part)
return ' '.join(
part.strip() for part in (self.first_name, self.last_name, self.last_name_2) if part
)
# Metodos sobrescritos
def get_full_name(self):
......@@ -75,5 +99,26 @@ class CustomUser(AbstractUser):
num_equipos = num_como_participante + num_como_coordinador
return num_equipos
@classmethod
def crear_usuario(cls, request, nip):
"""Crea un registro de usuario con el NIP indicado y los datos de Gestión de Identidades."""
usuario = cls.objects.create_user(username=nip)
try:
get_identidad(load_strategy(request), None, usuario)
except Exception as ex:
# Si Gestión de Identidades devuelve un error, borramos el usuario
# y finalizamos mostrando el mensaje de error.
usuario.delete()
raise ValidationError('ERROR: ' + str(ex))
# HACK - Indicamos que la autenticación es vía Single Sign On con SAML.
usuario_social = UserSocialAuth(
uid=f'lord:{usuario.username}', provider='saml', user=usuario
)
usuario_social.save()
return usuario
# Custom Manager
objects = CustomUserManager()
# Standard library
from datetime import date
from accounts.models import CustomUser
from accounts.pipeline import get_identidad
# Third-party
from social_django.models import UserSocialAuth
from social_django.utils import load_strategy
# Django
from django import forms
from django.contrib.auth.models import Group
from django.db.models import BLANK_CHOICE_DASH
from django.utils.translation import gettext_lazy as _
from social_django.models import UserSocialAuth
from social_django.utils import load_strategy
# Local Django
from accounts.models import CustomUser
from accounts.pipeline import get_identidad
from .models import Linea, ParticipanteProyecto, Programa, Proyecto, TipoParticipacion
class InvitacionForm(forms.ModelForm):
nip = forms.IntegerField(
label=_('NIP'),
help_text=_('Número de Identificación Personal en la Universidad de Zaragoza de la persona a invitar.'),
help_text=_(
'Número de Identificación Personal en la Universidad de Zaragoza de la persona a invitar.'
),
)
def __init__(self, *args, **kwargs):
......@@ -37,7 +45,9 @@ class InvitacionForm(forms.ModelForm):
raise forms.ValidationError('ERROR: ' + str(ex))
# HACK - Indicamos que la autenticación es vía Single Sign On con SAML.
usuario_social = UserSocialAuth(uid=f'lord:{usuario.username}', provider='saml', user=usuario)
usuario_social = UserSocialAuth(
uid=f'lord:{usuario.username}', provider='saml', user=usuario
)
usuario_social.save()
return usuario
......@@ -61,7 +71,9 @@ class InvitacionForm(forms.ModelForm):
# Si el usuario no está activo, finalizamos explicando esta circunstancia.
if not usuario.is_active:
raise forms.ValidationError(_('Usuario inactivo en el sistema de Gestión de Identidades'))
raise forms.ValidationError(
_('Usuario inactivo en el sistema de Gestión de Identidades')
)
# Si el usuario no tiene un email válido, finalizamos explicando esta circunstancia.
if not usuario.email:
......@@ -78,14 +90,26 @@ class InvitacionForm(forms.ModelForm):
vinculados = self.proyecto.get_usuarios_vinculados()
if usuario in vinculados:
raise forms.ValidationError(
_(f'No puede invitar a {usuario.get_full_name()} ' 'porque ya está vinculado a este proyecto.')
_(
f'No puede invitar a {usuario.get_full_name()} '
'porque ya está vinculado a este proyecto.'
)
)
# La participación de los estudiantes estará limitada a dos por proyecto
# (excepto en los PIPOUZ).
if self.proyecto.programa.nombre_corto != 'PIPOUZ' and usuario.get_colectivo_principal() == 'EST':
estudiantes = [vinculado for vinculado in vinculados if vinculado.get_colectivo_principal() == 'EST']
if (
self.proyecto.programa.nombre_corto != 'PIPOUZ'
and usuario.get_colectivo_principal() == 'EST'
):
estudiantes = [
vinculado
for vinculado in vinculados
if vinculado.get_colectivo_principal() == 'EST'
]
if len(estudiantes) >= self.proyecto.programa.max_estudiantes:
nombres_estudiantes = ', '.join(list(map(lambda e: e.get_full_name(), estudiantes)))
nombres_estudiantes = ', '.join(
list(map(lambda e: e.get_full_name(), estudiantes))
)
raise forms.ValidationError(
_(
'Ya se ha alcanzado el máximo de participación de '
......@@ -116,7 +140,9 @@ class ProyectoForm(forms.ModelForm):
estudio = cleaned_data.get('estudio')
if linea and linea.programa_id != programa.id:
self.add_error('linea', _('La línea seleccionada no pertenece al programa seleccionado.'))
self.add_error(
'linea', _('La línea seleccionada no pertenece al programa seleccionado.')
)
if lineas_del_programa and not linea:
self.add_error('linea', _('Este programa requiere seleccionar una línea.'))
......@@ -131,13 +157,34 @@ class ProyectoForm(forms.ModelForm):
super().__init__(*args, **kwargs)
self.fields['programa'].widget.choices = tuple(
BLANK_CHOICE_DASH
+ list(Programa.objects.filter(convocatoria_id=date.today().year).values_list('id', 'nombre_corto').all())
+ list(
Programa.objects.filter(convocatoria_id=date.today().year)
.values_list('id', 'nombre_corto')
.all()
)
)
self.fields['linea'].widget.choices = tuple(
BLANK_CHOICE_DASH
+ list(Linea.objects.filter(programa__convocatoria_id=date.today().year).values_list('id', 'nombre').all())
+ list(
Linea.objects.filter(programa__convocatoria_id=date.today().year)
.values_list('id', 'nombre')
.all()
)
)
class Meta:
fields = ['titulo', 'descripcion', 'programa', 'linea', 'centro', 'estudio']
model = Proyecto
class EvaluadorForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['evaluador'].widget.choices = tuple(
BLANK_CHOICE_DASH
+ [(u.id, u.full_name) for u in Group.objects.get(name="Evaluadores").user_set.all()]
)
class Meta:
fields = ('evaluador',)
model = Proyecto
......@@ -9,8 +9,8 @@ def add_permission_to_group(apps, schema_editor):
permission = apps.get_model('auth', 'Permission')
gestores = group.objects.get(name='Gestores')
ver_proyecto = permission.objects.get(codename='editar_proyecto')
gestores.permissions.add(ver_proyecto)
permiso = permission.objects.get(codename='editar_proyecto')
gestores.permissions.add(permiso)
def geo_post_migrate_signal(apps, schema_editor):
......
# Generated by Django 3.0.6 on 2020-05-28 09:43
from django.apps import apps as django_apps
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
def add_permissions_to_group(apps, schema_editor):
group = apps.get_model('auth', 'Group')
permission = apps.get_model('auth', 'Permission')
gestores = group.objects.get(name='Gestores')
permiso = permission.objects.get(codename='listar_evaluadores')
gestores.permissions.add(permiso)
permiso = permission.objects.get(codename='editar_evaluadores')
gestores.permissions.add(permiso)
def create_group(apps, schema_editor):
group = apps.get_model('auth', 'Group')
group, created = group.objects.get_or_create(name='Evaluadores')
def geo_post_migrate_signal(apps, schema_editor):
'''Emit the post-migrate signal during the migration.
Permissions are not actually created during or after an individual migration,
but are triggered by a post-migrate signal which is sent after the
`python manage.py migrate` command completes successfully.
This is necessary because this permission is used later in this migration.
'''
indo_config = django_apps.get_app_config('indo')
models.signals.post_migrate.send(
sender=indo_config,
app_config=indo_config,
verbosity=2,
interactive=False,
using=schema_editor.connection.alias,
)
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('indo', '0006_auto_20200207_0919'),
]
operations = [
migrations.AlterModelOptions(
name='proyecto',
options={
'permissions': [
('listar_proyectos', 'Puede ver el listado de todos los proyectos.'),
('ver_proyecto', 'Puede ver cualquier proyecto.'),
('editar_proyecto', 'Puede editar cualquier proyecto en cualquier momento.'),
('listar_evaluadores', 'Puede ver el listado de evaluadores.'),
('editar_evaluador', 'Puede editar el evaluador de un proyecto.'),
]
},
),
migrations.AddField(
model_name='proyecto',
name='evaluador',
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name='proyectos_evaluados',
to=settings.AUTH_USER_MODEL,
),
),
migrations.RunPython(geo_post_migrate_signal),
migrations.RunPython(add_permissions_to_group),
migrations.RunPython(create_group),
migrations.RunPython(geo_post_migrate_signal),
]
This diff is collapsed.
......@@ -6,12 +6,71 @@ from django.utils.translation import gettext_lazy as _
from .models import Proyecto
class EvaluadoresTable(tables.Table):
"""Muestra los proyectos solicitados y el evaluador asignado a ellos."""
def render_titulo(self, record):
enlace = reverse('proyecto_detail', args=[record.id])
return mark_safe(f'<a href="{enlace}">{record.titulo}</a>')
editar = tables.Column(empty_values=(), orderable=False, verbose_name='')
def render_editar(self, record):
enlace = reverse('evaluador_update', args=[record.id])
return mark_safe(
f'''<a href="{enlace}" title={_("Editar el evaluador")}
aria-label={_("Editar el evaluador")}>
<span class="fas fa-pencil-alt"></span>
</a>'''
)
class Meta:
attrs = {'class': 'table table-striped table-hover cabecera-azul'}
model = Proyecto
fields = ('programa', 'linea', 'titulo', 'evaluador.full_name', 'editar')
empty_text = _('Por el momento no se ha presentado ninguna solicitud de proyecto.')
template_name = 'django_tables2/bootstrap4.html'
per_page = 20
class ProyectosEvaluadosTable(tables.Table):
"""Muestra los proyectos asignados a un usuario evaluador."""
def render_titulo(self, record):
enlace = reverse('proyecto_detail', args=[record.id])
return mark_safe(f"<a href='{enlace}'>{record.titulo}</a>")
boton_evaluar = tables.Column(empty_values=(), orderable=False, verbose_name='')
def render_boton_evaluar(self, record):
enlace = reverse('proyecto_detail', args=[record.id]) # FIXME Hacer página de evaluación
return mark_safe(
f'''<a href="{enlace}" title={_("Evaluar el proyecto")}
aria-label={_('Evaluar el proyecto')} class="btn btn-info btn-sm">
<span class="fas fa-balance-scale" aria-hidden="true" style="display: inline;"></span>
&nbsp;{_('Evaluar')}
</a>'''
)
class Meta:
attrs = {'class': 'table table-striped table-hover cabecera-azul'}
model = Proyecto
fields = ('programa', 'linea', 'titulo', 'boton_evaluar')
empty_text = _('Por el momento no se le ha asignado ningún proyecto.')
template_name = 'django_tables2/bootstrap4.html'
per_page = 20
class ProyectosTable(tables.Table):
"""Muestra las solicitudes de proyecto presentadas."""
def render_titulo(self, record):
enlace = reverse('proyecto_detail', args=[record.id])
return mark_safe(f"<a href='{enlace}'>{record.titulo}</a>")
coordinadores = tables.Column(empty_values=(), orderable=False, verbose_name=_('Coordinador(es)'))
coordinadores = tables.Column(
empty_values=(), orderable=False, verbose_name=_('Coordinador(es)')
)
def render_coordinadores(self, record):
coordinadores = record.get_coordinadores()
......
......@@ -13,9 +13,12 @@ from .views import (
ProyectoAnularView,
ProyectoCreateView,
ProyectoDetailView,
ProyectoListView,
ProyectoEvaluadorTableView,
ProyectoEvaluadorUpdateView,
ProyectoTableView,
ProyectoPresentarView,
ProyectoUpdateFieldView,
ProyectosEvaluadosTableView,
ProyectosUsuarioView,
)
......@@ -24,24 +27,61 @@ urlpatterns = [
path('', HomePageView.as_view(), name='home'),
path('summernote/', include('django_summernote.urls')),
path('ayuda/', AyudaView.as_view(), name='ayuda'),
path('gestion/proyecto/list/<int:anyo>', ProyectoListView.as_view(), name='proyecto_list'),
path(
'participante-proyecto/aceptar_invitacion/<int:proyecto_id>',
'evaluador/mis-proyectos/<int:anyo>/',
ProyectosEvaluadosTableView.as_view(),
name='proyectos_evaluados_table',
),
path('gestion/proyecto/<int:anyo>/', ProyectoTableView.as_view(), name='proyectos_table'),
path(
'gestion/proyecto/<int:anyo>/evaluadores',
ProyectoEvaluadorTableView.as_view(),
name='evaluadores_table',
),
path(
'gestion/proyecto/<int:pk>/editar_evaluador/',
ProyectoEvaluadorUpdateView.as_view(),
name='evaluador_update',
),
path(
'participante-proyecto/aceptar_invitacion/<int:proyecto_id>/',
ParticipanteAceptarView.as_view(),
name='participante_aceptar',
),
path(
'participante-proyecto/declinar_invitacion', ParticipanteDeclinarView.as_view(), name='participante_declinar'
'participante-proyecto/declinar_invitacion/',
ParticipanteDeclinarView.as_view(),
name='participante_declinar',
),
path(
'participante-proyecto/invitar/<int:proyecto_id>/',
InvitacionView.as_view(),
name='participante_invitar',
),
path(
'participante-proyecto/renunciar/',
ParticipanteRenunciarView.as_view(),
name='participante_renunciar',
),
path(
'participante-proyecto/<int:pk>/delete/',
ParticipanteDeleteView.as_view(),
name='participante_delete',
),
path('participante-proyecto/invitar/<int:proyecto_id>', InvitacionView.as_view(), name='participante_invitar'),
path('participante-proyecto/renunciar', ParticipanteRenunciarView.as_view(), name='participante_renunciar'),
path('participante-proyecto/<int:pk>/delete/', ParticipanteDeleteView.as_view(), name='participante_delete'),
path('proyecto/new/', ProyectoCreateView.as_view(), name='proyecto_new'),
path('proyecto/<int:pk>/', ProyectoDetailView.as_view(), name='proyecto_detail'),
path('proyecto/<int:pk>/edit/<campo>', ProyectoUpdateFieldView.as_view(), name='proyecto_update_field'),
path(
'proyecto/<int:pk>/edit/<campo>/',
ProyectoUpdateFieldView.as_view(),
name='proyecto_update_field',
),
path('proyecto/<int:pk>/anular/', ProyectoAnularView.as_view(), name='proyecto_anular'),
path('proyecto/<int:pk>/presentar', ProyectoPresentarView.as_view(), name='proyecto_presentar'),
path('proyecto/mis-proyectos/<int:anyo>', ProyectosUsuarioView.as_view(), name='mis_proyectos'),
path(
'proyecto/<int:pk>/presentar/', ProyectoPresentarView.as_view(), name='proyecto_presentar'
),
path(
'proyecto/mis-proyectos/<int:anyo>/', ProyectosUsuarioView.as_view(), name='mis_proyectos'
),
]
if settings.DEBUG:
......
This diff is collapsed.
[tool.black]
skip-string-normalization = true
line-length = 119
line-length = 99
target-version = ['py37']
include = '\.pyi?$'
exclude = '''
......@@ -22,3 +22,16 @@ exclude = '''
# the root of the project
)
'''
[tool.isort]
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
line_length = 99
[tool.pylint.messages_control]
disable = "C0330, C0326"
[tool.pylint.format]
max-line-length = "99"
......@@ -92,14 +92,24 @@
<span class="fas fa-cog"></span>&nbsp; {% trans "Gestión" %}
</a>
<div class="dropdown-menu" aria-labelledby="gestionMenu">
<a class="dropdown-item" href="{% url 'proyecto_list' anyo_actual %}">
<a class="dropdown-item" href="{% url 'proyectos_table' anyo_actual %}">
<span class="fas fa-th-list"></span>&nbsp; {% trans "Proyectos presentados" %}
</a>
<a class="dropdown-item" href="{% url 'evaluadores_table' anyo_actual %}">
<span class="fas fa-th-list"></span>&nbsp; {% trans "Evaluadores de los proyectos" %}
</a>
</div>
</li>
{% endif %}
{% if user|has_group:"Evaluadores" %}
<li class="nav-item">
<a class="nav-link" href="{% url 'proyectos_evaluados_table' anyo_actual %}">
<span class="fas fa-balance-scale"></span>&nbsp; {% trans "Evaluaciones" %}
</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{% url 'mis_proyectos' anyo_actual %}">
<span class="fas fa-project-diagram"></span> &nbsp;{% trans "Mis proyectos" %}
......
{% extends 'base.html' %}
{% load i18n %}
{% load render_table from django_tables2 %}
{% block title %}{% trans "Proyectos a evaluar" %}{% endblock title %}
{% block content %}
<div class='container-blanco'>
<h1 id="presentados">{% trans "Proyectos a evaluar" %} <small>{{ anyo }}</small></h1>
<hr />
<br />
<div class="alert-info alert fade-in">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<span class="fas fa-info-circle"></span>
{% trans "Aquí puede ver los proyectos que se le han asignado para su evaluación." %}
</div><br />
{% render_table table %}
</div>
{% endblock content %}
\ No newline at end of file
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans 'Actualizar evaluador del proyecto' %}{% endblock title %}
{% block description %}{% trans 'Actualizar evaluador de proyecto' %}{% endblock description %}
{% block extracss %}<style>
label {
display: block;
font-size: 1.75rem;
font-weight: 500;
line-height: 1.2;
margin-bottom: 0.5rem;
margin-top: 0;
}
.helptext {
color: #6c757d !important;
display: block;
}
</style>
{% endblock extracss %}
{% block content %}
<div class="container-blanco">
<h1>{% trans 'Actualizar evaluador del proyecto' %}</h1>
<hr />
<br />
<div class="alert-info alert fade-in">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<span class="fas fa-info-circle"></span>
{% blocktrans %}
<p>Aquí puede asignar un evaluador a un proyecto.</p>
<p>Para que una persona aparezca en el desplegable de evaluadores, debe:</p>
<ol>
<li>
<strong>Tener un NIP (identificador de usuario)</strong><br />
Si el usuario nunca ha estado vinculado a la Universad de Zaragoza,
puede obtener un NIP visitando la
<a href="https://identidad.unizar.es/autoregistro" target="_blank" rel="noopener noreferrer">
página de autorregistro</a>
<span class="fas fa-link" style="float: none; font-size: 100%;"></span>.
</li>
<li>
<strong>Tener la vinculación «Evaluador externo innovación ACPUA»</strong><br />
Una vez que el usuario disponga de un NIP, debe comunicárselo al Vicerrectorado
de Política Académica para asignarle dicha vinculación en el Sistema de Gestión de
Identidades.
</li>
</ol>
{% endblocktrans %}
</div><br />
<p><label>Proyecto:</label> {{ proyecto.titulo }}</p>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<br style="clear: both;" />
<div class="btn-group" role="group" aria-label="Botones">
<a href="{% url 'evaluadores_table' proyecto.convocatoria %}" class="btn btn-info">
<span class="fas fa-step-backward"></span> {% trans 'Retroceder' %}
</a>
<button class="btn btn-warning" type="submit">
<span class="fas fa-check"></span> {% trans 'Actualizar' %}
</button>
</div>
</form>
</div>
{% endblock content %}
\ No newline at end of file
{% extends 'base.html' %}
{% load i18n %}
{% load render_table from django_tables2 %}