feat Creación inicial de una solicitud de proyecto

parent aaf316bd
......@@ -60,6 +60,7 @@ coverage.xml
*.log
local_settings.py
db.sqlite3
settings.py
# Flask stuff:
instance/
......
......@@ -13,6 +13,7 @@ social-auth-app-django = "*"
social-auth-core = {extras = ["saml"],version = "*"}
python3-saml = "*"
cx-oracle = "*"
django-crispy-forms = "==1.7.2"
[requires]
python_version = "3.7"
{
"_meta": {
"hash": {
"sha256": "10726274f0df91f8bc04c1c8f18bd5e615c4cee22a69cd88f4e3d22a5e21e8c0"
"sha256": "d85ca5d04a37c94a2b3bcfd36a8e581e9a46b7880017b6843060dcba9df97075"
},
"pipfile-spec": 6,
"requires": {
......@@ -18,10 +18,10 @@
"default": {
"certifi": {
"hashes": [
"sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7",
"sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
"sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"
],
"version": "==2018.11.29"
"version": "==2019.3.9"
},
"chardet": {
"hashes": [
......@@ -32,23 +32,23 @@
},
"cx-oracle": {
"hashes": [
"sha256:17d760bdf89e364fc7c964c5640c1b38cbb22ab49b53830883f21fda92c59131",
"sha256:1c79be40c1413372062e4a09570ff25dde42111369730c81ca6dc3e5dd9857fe",
"sha256:1e7e161f9855dfa2cb29fb116d1374de966194976cd5a8d8265911b55a1ec9f9",
"sha256:2f4423eb7c652ad7c8352547054784456faed7b61d97a43fcf98f4a61fcc3095",
"sha256:4af28f3060ec26c293921e640ce49b834199ce79d41d771990520a0755d8334b",
"sha256:4ca456fb1889f8e6c34d66af455ef885d3c0390f4be2ef314a811a03d936641e",
"sha256:5895d6db9a7c70e43c48ee53e50a2a6ba647192d881e0689bba5739f9d8e1011",
"sha256:79a5b9c831033cde2bff438b9e87e628c30d160ea97f23bc1350f23af7f8607d",
"sha256:a77f0f3693acf602e59795f544982e127e819fbc6fbd3dda04748367d3883a69",
"sha256:a91f03ab85a8eb5860559c888e6abdba391a119e9bd4fb65c025f689392cc2df",
"sha256:b8e296add6386b56fc468b9e60b8981104c5ff9df1f318454c0a486c1800b3e5",
"sha256:fbbb92cd85d6c0ea91eeec1e82fa7434f8f9cc6089fc908ea9f66f2a93b2e92c",
"sha256:fda68322fe3d2ae77820170059049a0e64efdbb4ed547db96287959b12d975a5",
"sha256:ff44d60f50a03389f723bf7875e3eb8105ed4e344235908573313776af329b8b"
"sha256:00589b59f25518a64ddc10067f2d73a5723b3780119e5ffdeeec7c01910e257e",
"sha256:0409de23fdda6ee687be44e69727f77a9aadba89649c3cfa7655480e2b420c98",
"sha256:1000e0dfa78f77605dd7bf09bfebd2e08c641737c41b032346621413902841c6",
"sha256:2598c1fe9c8dad24f890b117ce1e985e7481dbbe1564af69801ff32de7e18505",
"sha256:2f8839f0d77bdaacea686f935578f2e3c018525f22133b4e78b0617442c0d6f7",
"sha256:4aed0205ee1400ef650d4d95d60b54dd41680a66b349e9c0a98dc8d799ccbe24",
"sha256:4d83c56ccacb5d3f537f2fc8d868aeb752192c5b71984e473db44aec6df4b1ae",
"sha256:5c5e5f2968ff48ff217252887f6de94a3e66ff5a8905a0ffa0437c3df976678e",
"sha256:7d0e30d6ce62bedd41ae4885d43fff727314b67655ec8737b8af5cc0aad7f925",
"sha256:9477983570d961a78c10d70999f508eed105e31c0ca361ccdf0b0211ccd86fab",
"sha256:a3d2505dc430652f1493e99874cedce101ebc41b7811db6372f15348e5b5e69f",
"sha256:b141dc42ee19e580f9821b706d2edd80f4f71377d61aefebde22930c9c92edd2",
"sha256:b1c28e3419e2f0dff2ba96e4724c3db94f99e80558514b9e2485e86caccdbdcb",
"sha256:f3f250e419789f704bc667758a71fdc2253ddaa87fa4a503b2397d9c99ca9afb"
],
"index": "pypi",
"version": "==7.1.1"
"version": "==7.1.2"
},
"defusedxml": {
"hashes": [
......@@ -66,6 +66,14 @@
"index": "pypi",
"version": "==2.1"
},
"django-crispy-forms": {
"hashes": [
"sha256:5952bab971110d0b86c278132dae0aa095beee8f723e625c3d3fa28888f1675f",
"sha256:705ededc554ad8736157c666681165fe22ead2dec0d5446d65fc9dd976a5a876"
],
"index": "pypi",
"version": "==1.7.2"
},
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
......@@ -224,17 +232,17 @@
"develop": {
"astroid": {
"hashes": [
"sha256:39183aecc01d6f74eb54edc6739992e842f3bf3068bdfaaba30b5a1742f44091",
"sha256:62bd1d8d2ace3e812b3a22eb3c4b7856536d8cc0cdb37466f6a53cf881dbad1f"
"sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4",
"sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4"
],
"version": "==2.2.4"
"version": "==2.2.5"
},
"isort": {
"hashes": [
"sha256:89041186651a9a6159683098f337eed0994d9d94e006f891c6e8cbeb8e65f1c7",
"sha256:ba51a651505242b0b37ad94b281e1154301e221a40c623e62334ed863fc1c98c"
"sha256:18c796c2cd35eb1a1d3f012a214a542790a1aed95e29768bdcb9f2197eccbd0b",
"sha256:96151fca2c6e736503981896495d344781b60d18bfda78dc11b290c6125ebdb6"
],
"version": "==4.3.12"
"version": "==4.3.15"
},
"lazy-object-proxy": {
"hashes": [
......
# Generated by Django 2.1 on 2019-03-22 09:58
# Generated by Django 2.1 on 2019-03-29 11:46
from django.conf import settings
from django.db import migrations, models
......@@ -50,20 +50,6 @@ class Migration(migrations.Migration):
('fecha_max_visto_buenos', models.DateField()),
],
),
migrations.CreateModel(
name='Departamento',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False)),
('academico_id_nk', models.IntegerField(blank=True, db_index=True, null=True, verbose_name='cód. académico')),
('rrhh_id_nk', models.CharField(blank=True, max_length=4, null=True, unique=True, verbose_name='cód. RRHH')),
('nombre', models.CharField(blank=True, max_length=255, null=True)),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='email del departamento')),
('email_secretaria', models.EmailField(blank=True, max_length=254, null=True, verbose_name='email de la secretaría')),
('nip_director', models.PositiveIntegerField(blank=True, null=True, verbose_name='NIP del director')),
('nombre_director', models.CharField(blank=True, max_length=255, null=True, verbose_name='nombre del director')),
('email_director', models.EmailField(blank=True, max_length=254, null=True, verbose_name='email del director')),
],
),
migrations.CreateModel(
name='Estudio',
fields=[
......@@ -82,8 +68,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Licencia',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('identificador', models.CharField(help_text='Ver los identificadores estándar en https://spdx.org/licenses/', max_length=255, null=True, unique=True)),
('identificador', models.CharField(help_text='Ver los identificadores estándar en https://spdx.org/licenses/', max_length=255, primary_key=True, serialize=False)),
('nombre', models.CharField(max_length=255)),
('url', models.URLField(blank=True, max_length=255, null=True, verbose_name='URL')),
],
......@@ -130,17 +115,16 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('codigo', models.CharField(max_length=31, null=True)),
('titulo', models.CharField(max_length=255)),
('descripcion', models.TextField(blank=True, null=True, verbose_name='Descripción')),
('titulo', models.CharField(max_length=255, verbose_name='Título')),
('descripcion', models.TextField(help_text='Resumen sucinto del proyecto. Máximo recomendable: un párrafo de 10 líneas.', max_length=4095, null=True, verbose_name='Descripción')),
('financiacion', models.TextField(blank=True, null=True, verbose_name='Financiación solicitada')),
('ayuda', models.PositiveIntegerField(blank=True, null=True, verbose_name='Ayuda solicitada')),
('centro', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='indo.Centro')),
('centro', models.ForeignKey(blank=True, help_text='Sólo obligatorio para PIEC, PRACUZ, PIPOUZ.', null=True, on_delete=django.db.models.deletion.PROTECT, to='indo.Centro')),
('convocatoria', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='indo.Convocatoria')),
('departamento', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='indo.Departamento')),
('estudio', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='indo.Estudio')),
('estudio', models.ForeignKey(blank=True, help_text='Sólo obligatorio para PIET.', null=True, on_delete=django.db.models.deletion.PROTECT, to='indo.Estudio')),
('licencia', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='indo.Licencia')),
('linea', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='indo.Linea')),
('programa', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='indo.Programa')),
('linea', models.ForeignKey(blank=True, help_text='En su caso.', limit_choices_to=models.Q(programa__convocatoria_id=2019), null=True, on_delete=django.db.models.deletion.PROTECT, to='indo.Linea', verbose_name='Línea')),
('programa', models.ForeignKey(limit_choices_to={'convocatoria_id': 2019}, on_delete=django.db.models.deletion.PROTECT, to='indo.Programa')),
],
),
migrations.CreateModel(
......
from datetime import date, datetime
from django.db import models
from django.db.models.query_utils import Q
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
......@@ -69,9 +71,10 @@ class Convocatoria(models.Model):
fecha_max_visto_buenos = models.DateField()
def __str__(self):
return self.id
return str(self.id)
"""
class Departamento(models.Model):
id = models.IntegerField(primary_key=True)
academico_id_nk = models.IntegerField(
......@@ -95,6 +98,7 @@ class Departamento(models.Model):
def __str__(self):
return f"{self.academico_id_nk} {self.rrhh_id_nk} {self.nombre}"
"""
class Estudio(models.Model):
......@@ -121,17 +125,17 @@ class Evento(models.Model):
class Licencia(models.Model):
"""Licencia de publicación de la memoria"""
identificador = models.CharField(
max_length=255,
null=True,
unique=True,
primary_key=True,
help_text=_("Ver los identificadores estándar en https://spdx.org/licenses/"),
)
nombre = models.CharField(max_length=255)
url = models.URLField("URL", max_length=255, blank=True, null=True)
def __str__(self):
return self.nombre
return f"{self.nombre} ({self.identificador})"
class Linea(models.Model):
......@@ -139,7 +143,7 @@ class Linea(models.Model):
programa = models.ForeignKey("Programa", on_delete=models.PROTECT)
def __str__(self):
return f"{self.programa} {self.nombre}"
return f"{self.nombre}"
class Plan(models.Model):
......@@ -153,7 +157,7 @@ class Plan(models.Model):
email_coordinador = models.EmailField(
_("email del coordinador"), blank=True, null=True
)
esta_activo = models.BooleanField("¿Activo?", default=True)
esta_activo = models.BooleanField(_("¿Activo?"), default=True)
centro = models.ForeignKey("Centro", on_delete=models.PROTECT)
estudio = models.ForeignKey("Estudio", on_delete=models.PROTECT)
......@@ -173,43 +177,92 @@ class Programa(models.Model):
convocatoria = models.ForeignKey("Convocatoria", on_delete=models.PROTECT)
def __str__(self):
return f"{self.nombre_corto} {self.convocatoria}"
return f"{self.nombre_corto}"
class ParticipanteProyecto(models.Model):
proyecto = models.ForeignKey("Proyecto", on_delete=models.PROTECT)
tipo_participacion = models.ForeignKey(
"TipoParticipacion", on_delete=models.PROTECT
)
usuario = models.ForeignKey("accounts.CustomUser", on_delete=models.PROTECT)
def get_cargo(self):
if self.tipo_participacion.nombre == "coordinador":
return _("Coordinadora") if self.usuario.sexo == "F" else _("Coordinador")
if self.tipo_participacion.nombre == "coordinador_principal":
if self.usuario.sexo == "F":
return _("Coordinadora principal")
return _("Coordinador principal")
return _("Participante")
class Proyecto(models.Model):
id = models.AutoField(primary_key=True)
codigo = models.CharField(max_length=31, null=True)
titulo = models.CharField(max_length=255)
descripcion = models.TextField(_("Descripción"), blank=True, null=True)
titulo = models.CharField(_("Título"), max_length=255)
descripcion = models.TextField(
_("Descripción"),
null=True,
max_length=4095,
help_text=_(
"Resumen sucinto del proyecto. Máximo recomendable: un párrafo de 10 líneas."
),
)
# publicar_memoria = models.BooleanField("¿Publicar la memoria?", default=True)
financiacion = models.TextField(_("Financiación solicitada"), blank=True, null=True)
ayuda = models.PositiveIntegerField(_("Ayuda solicitada"), blank=True, null=True)
centro = models.ForeignKey("Centro", on_delete=models.PROTECT, null=True)
centro = models.ForeignKey(
"Centro",
on_delete=models.PROTECT,
blank=True,
null=True,
help_text=_("Sólo obligatorio para PIEC, PRACUZ, PIPOUZ."),
)
convocatoria = models.ForeignKey("Convocatoria", on_delete=models.PROTECT)
departamento = models.ForeignKey(
"Departamento", on_delete=models.PROTECT, null=True
# departamento = models.ForeignKey(
# "Departamento", on_delete=models.PROTECT, null=True
# )
estudio = models.ForeignKey(
"Estudio",
on_delete=models.PROTECT,
blank=True,
null=True,
help_text=_("Sólo obligatorio para PIET."),
)
estudio = models.ForeignKey("Estudio", on_delete=models.PROTECT, null=True)
licencia = models.ForeignKey("Licencia", on_delete=models.PROTECT, null=True)
linea = models.ForeignKey("Linea", on_delete=models.PROTECT, null=True)
programa = models.ForeignKey("Programa", on_delete=models.PROTECT)
linea = models.ForeignKey(
"Linea",
on_delete=models.PROTECT,
blank=True,
null=True,
limit_choices_to=Q(programa__convocatoria_id=date.today().year),
verbose_name=_("Línea"),
help_text=_("En su caso."),
)
programa = models.ForeignKey(
"Programa",
on_delete=models.PROTECT,
limit_choices_to={"convocatoria_id": date.today().year},
)
def get_absolute_url(self):
return reverse("proyecto_detail", args=[str(self.id)])
def get_participante_or_none(self, tipo):
try:
return ParticipanteProyecto.objects.get(
proyecto_id=self.id, tipo_participacion_id=tipo
)
except ParticipanteProyecto.DoesNotExist:
return None
def __str__(self):
return self.codigo
class ParticipanteProyecto(models.Model):
proyecto = models.ForeignKey("Proyecto", on_delete=models.PROTECT)
tipo_participacion = models.ForeignKey(
"TipoParticipacion", on_delete=models.PROTECT
)
usuario = models.ForeignKey("accounts.CustomUser", on_delete=models.PROTECT)
""" class Rama(models.Model):
"""
class Rama(models.Model):
id = models.CharField(primary_key=True, max_length=1)
nombre = models.CharField(max_length=63)
"""
......
from django.urls import path
# from . import views
from .views import AyudaView, HomePageView
from .views import AyudaView, HomePageView, ProyectoCreateView, ProyectoDetailView
urlpatterns = [
path("", HomePageView.as_view(), name="home"),
path("ayuda/", AyudaView.as_view(), name="ayuda"),
path("proyecto/new/", ProyectoCreateView.as_view(), name="proyecto_new"),
path("proyecto/<int:pk>/", ProyectoDetailView.as_view(), name="proyecto_detail"),
]
from django.shortcuts import render
from django.views.generic import TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http.response import HttpResponse
from django.shortcuts import render, redirect
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import (
DetailView,
FormView,
ListView,
TemplateView,
View,
FormView,
)
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from .models import (
Convocatoria,
Evento,
ParticipanteProyecto,
Proyecto,
Registro,
TipoParticipacion,
)
# Create your views here.
class AyudaView(TemplateView):
template_name = "ayuda.html"
class HomePageView(TemplateView):
template_name = "home.html"
class ProyectoCreateView(LoginRequiredMixin, CreateView):
"""Crea una nueva solicitud de proyecto"""
model = Proyecto
template_name = "proyecto/new.html"
fields = ["titulo", "descripcion", "programa", "linea", "centro", "estudio"]
def form_valid(self, form):
# Do custom logic on form data that has already been validated here
form.instance.convocatoria = Convocatoria(2019)
proyecto = form.save()
self._guardar_coordinador(proyecto)
self._registrar_creacion(proyecto)
return redirect("proyecto_detail", proyecto.id)
def _guardar_coordinador(self, proyecto):
# Los PIET debe solicitarlos uno de los coordinadores del estudio ("coordinador principal")
# quien podrá nombrar a otro coordinador.
if proyecto.programa.nombre_corto == "PIET":
tipo_participacion = "coordinador_principal"
else:
tipo_participacion = "coordinador"
participanteProyecto = ParticipanteProyecto(
proyecto=proyecto,
tipo_participacion=TipoParticipacion(nombre=tipo_participacion),
usuario=self.request.user,
)
participanteProyecto.save()
def _registrar_creacion(self, proyecto):
evento = Evento.objects.get(nombre="creacion_solicitud")
registro = Registro(
descripcion="Creación inicial de la solicitud",
evento=evento,
proyecto=proyecto,
)
registro.save()
class ProyectoDetailView(DetailView):
model = Proyecto
template_name = "proyecto/detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
coordinador_principal = self.object.get_participante_or_none(
"coordinador_principal"
)
context["coordinador_principal"] = coordinador_principal
coordinador = self.object.get_participante_or_none("coordinador")
context["coordinador"] = coordinador
return context
......@@ -41,6 +41,7 @@ INSTALLED_APPS = [
"indo.apps.IndoConfig",
"accounts.apps.AccountsConfig",
# 3rd Party
'crispy_forms', # https://github.com/django-crispy-forms/django-crispy-forms
"social_django", # https://github.com/python-social-auth/social-app-django
]
......@@ -206,3 +207,6 @@ SOCIAL_AUTH_PIPELINE = (
)
SOCIAL_AUTH_URL_NAMESPACE = "social"
# CRISPY FORMS
CRISPY_TEMPLATE_PACK = 'bootstrap4'
\ No newline at end of file
{% extends 'base.html' %}
{% block content %}
<div class="container-blanco">
<h1>{{ proyecto.titulo }}</h1> {# Our context object, proyecto, is made available by DetailView #}
<hr><br>
<p><strong>Convocatoria</strong>: {{ proyecto.convocatoria_id }}</p>
<p><strong>Programa</strong>: {{ proyecto.programa.nombre_corto }} ({{ proyecto.programa.nombre_largo }})</p>
{% if proyecto.linea %}
<p><strong>Línea</strong>: {{ proyecto.linea.nombre }}</p>
{% endif %}
{% if proyecto.centro %}
<p><strong>Centro</strong>: {{ proyecto.centro.nombre }}</p>
{% endif %}
{% if proyecto.estudio %}
<p><strong>Estudio</strong>: {{ proyecto.estudio.nombre }}</p>
{% endif %}
{% if coordinador_principal %}
<p><strong>{{ coordinador_principal.get_cargo }}</strong>: {{ coordinador_principal.usuario.get_full_name }}</p>
{% endif %}
{% if coordinador %}
<p><strong>{{ coordinador.get_cargo }}</strong>: {{ coordinador.usuario.get_full_name }}</p>
{% endif %}
<h3>Descripción</h3>
<p>{{ proyecto.descripcion }}</p>
</div>
{% endblock content %}
\ No newline at end of file
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %}Nueva solicitud de proyecto{% endblock title %}
{% block description %}Nueva solicitud de proyecto{% endblock description %}
{% block content %}
<div class="container-blanco">
<div class='alert alert-info'>
<i class="fas fa-info-circle"></i>
<div>Introduzca los datos básicos de su solicitud.</br>
En la siguiente página se le solicitará más información.
</div>
</div>
<h1>Nueva solicitud de proyecto</h1>
<hr><br>
<form action="" method="post">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-success" type="submit">Guardar</button>
</form>
</div>
{% endblock content %}
\ No newline at end of file
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