From c7935b6f8a563a85f721f03681f3a733cec1e90b Mon Sep 17 00:00:00 2001 From: ricval Date: Thu, 15 Aug 2024 15:06:56 -0600 Subject: [PATCH] =?UTF-8?q?Distritos=20casi=20completo,=20falta=20conexi?= =?UTF-8?q?=C3=B3n=20con=20CT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orion/app.py | 2 + orion/blueprints/distritos/__init__.py | 0 orion/blueprints/distritos/forms.py | 15 ++ orion/blueprints/distritos/models.py | 37 ++++ .../templates/distritos/detail.jinja2 | 96 +++++++++ .../distritos/templates/distritos/edit.jinja2 | 22 ++ .../distritos/templates/distritos/list.jinja2 | 84 ++++++++ .../distritos/templates/distritos/new.jinja2 | 21 ++ orion/blueprints/distritos/views.py | 197 ++++++++++++++++++ orion/blueprints/personas/views.py | 1 + 10 files changed, 475 insertions(+) create mode 100644 orion/blueprints/distritos/__init__.py create mode 100644 orion/blueprints/distritos/forms.py create mode 100644 orion/blueprints/distritos/models.py create mode 100644 orion/blueprints/distritos/templates/distritos/detail.jinja2 create mode 100644 orion/blueprints/distritos/templates/distritos/edit.jinja2 create mode 100644 orion/blueprints/distritos/templates/distritos/list.jinja2 create mode 100644 orion/blueprints/distritos/templates/distritos/new.jinja2 create mode 100644 orion/blueprints/distritos/views.py diff --git a/orion/app.py b/orion/app.py index 6eb4166..6ba37a4 100644 --- a/orion/app.py +++ b/orion/app.py @@ -10,6 +10,7 @@ from orion.blueprints.areas.views import areas from orion.blueprints.bitacoras.views import bitacoras from orion.blueprints.carreras.views import carreras +from orion.blueprints.distritos.views import distritos from orion.blueprints.entradas_salidas.views import entradas_salidas from orion.blueprints.modulos.views import modulos from orion.blueprints.niveles_academicos.views import niveles_academicos @@ -40,6 +41,7 @@ def create_app(): app.register_blueprint(areas) app.register_blueprint(bitacoras) app.register_blueprint(carreras) + app.register_blueprint(distritos) app.register_blueprint(entradas_salidas) app.register_blueprint(modulos) app.register_blueprint(niveles_academicos) diff --git a/orion/blueprints/distritos/__init__.py b/orion/blueprints/distritos/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/orion/blueprints/distritos/forms.py b/orion/blueprints/distritos/forms.py new file mode 100644 index 0000000..8059785 --- /dev/null +++ b/orion/blueprints/distritos/forms.py @@ -0,0 +1,15 @@ +""" +Distritos, formularios +""" + +from flask_wtf import FlaskForm +from wtforms import StringField, SubmitField +from wtforms.validators import DataRequired, Length + + +class DistritoForm(FlaskForm): + """Formulario Distrito""" + + clave = StringField("Clave", validators=[DataRequired(), Length(max=16)]) + nombre = StringField("Nombre", validators=[DataRequired(), Length(max=256)]) + guardar = SubmitField("Guardar") diff --git a/orion/blueprints/distritos/models.py b/orion/blueprints/distritos/models.py new file mode 100644 index 0000000..fa97e97 --- /dev/null +++ b/orion/blueprints/distritos/models.py @@ -0,0 +1,37 @@ +""" +Distritos, modelos +""" + +from typing import List + +from sqlalchemy import ForeignKey, String +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from lib.universal_mixin import UniversalMixin +from orion.extensions import database + + +class Distrito(database.Model, UniversalMixin): + """Distrito""" + + # Nombre de la tabla + __tablename__ = "distritos" + + # Clave primaria + id: Mapped[int] = mapped_column(primary_key=True) + + # Columnas + clave: Mapped[str] = mapped_column(String(16), unique=True) + nombre: Mapped[str] = mapped_column(String(256)) + + # Hijos + # centros_trabajos = db.relationship("CentroTrabajo", back_populates="distrito") + + @property + def nombre_descriptivo(self): + """Junta nombre del curso y su descripción""" + return self.clave + ": " + self.nombre + + def __repr__(self): + """Representación""" + return f"" diff --git a/orion/blueprints/distritos/templates/distritos/detail.jinja2 b/orion/blueprints/distritos/templates/distritos/detail.jinja2 new file mode 100644 index 0000000..18d0027 --- /dev/null +++ b/orion/blueprints/distritos/templates/distritos/detail.jinja2 @@ -0,0 +1,96 @@ +{% extends 'layouts/app.jinja2' %} +{% import 'macros/detail.jinja2' as detail %} +{% import 'macros/modals.jinja2' as modals %} +{% import 'macros/topbar.jinja2' as topbar %} +{% import 'macros/list.jinja2' as list %} + +{% block title %}Distrito {{ distrito.clave }}{% endblock %} + +{% block topbar_actions %} + {% call topbar.page_buttons('Distrito ' + distrito.clave) %} + {{ topbar.button_previous('Distritos', url_for('distritos.list_active')) }} + {% if current_user.can_edit('DISTRITOS') %} + {{ topbar.button_edit('Editar', url_for('distritos.edit', distrito_id=distrito.id)) }} + {% endif %} + {% if current_user.can_admin('DISTRITOS') %} + {% if distrito.estatus == 'A' %}{{ topbar.button_delete('Eliminar', url_for('distritos.delete', distrito_id=distrito.id)) }}{% endif %} + {% if distrito.estatus == 'B' %}{{ topbar.button_recover('Recuperar', url_for('distritos.recover', distrito_id=distrito.id)) }}{% endif %} + {% endif %} + {% endcall %} +{% endblock %} + +{% block content %} + {% call detail.card(estatus=distrito.estatus) %} + {{ detail.label_value_big('Clave', distrito.clave) }} + {{ detail.label_value('Nombre', distrito.nombre) }} + {% endcall %} + + {% call detail.card('Centros de Trabajos') %} + {% endcall %} + + {% call detail.card('Personas') %} +

TODO: FALTA: filtrar personas en este distrito

+ +
+
+
+
+
+ + +
+
+
+ + +
+
+
+
+ + + + + + + + + +
Nombre completoSexoSituación
+ {% endcall %} +{% endblock %} + +{% block custom_javascript %} + {% if current_user.can_admin('DISTRITOS') %} + {% if distrito.estatus == 'A' %}{{ modals.custom_javascript_delete('Eliminar', '¿Eliminar a ' + distrito.clave + '?') }}{% endif %} + {% if distrito.estatus == 'B' %}{{ modals.custom_javascript_recover('Recuperar', '¿Recuperar a ' + distrito.clave + '?') }}{% endif %} + {% endif %} + {{ detail.moment_js(moment) }} + + + +{% endblock %} diff --git a/orion/blueprints/distritos/templates/distritos/edit.jinja2 b/orion/blueprints/distritos/templates/distritos/edit.jinja2 new file mode 100644 index 0000000..00dbb4d --- /dev/null +++ b/orion/blueprints/distritos/templates/distritos/edit.jinja2 @@ -0,0 +1,22 @@ +{% extends 'layouts/app.jinja2' %} +{% import 'macros/form.jinja2' as f with context %} +{% import 'macros/topbar.jinja2' as topbar %} + +{% block title %}Editar Distrito {{ distrito.clave }}{% endblock %} + +{% block topbar_actions %} + {% call topbar.page_buttons('Editar ' + distrito.clave) %} + {{ topbar.button_cancel() }} + {% endcall %} +{% endblock %} + +{% block content %} + {% call f.card() %} + {% set form_kwargs = {'distrito_id': distrito.id} %} + {% call f.form_tag('distritos.edit', fid='distrito_form', **form_kwargs) %} + {% call f.form_group(form.clave) %}{% endcall %} + {% call f.form_group(form.nombre) %}{% endcall %} + {% call f.form_group(form.guardar) %}{% endcall %} + {% endcall %} + {% endcall %} +{% endblock %} diff --git a/orion/blueprints/distritos/templates/distritos/list.jinja2 b/orion/blueprints/distritos/templates/distritos/list.jinja2 new file mode 100644 index 0000000..b23ae80 --- /dev/null +++ b/orion/blueprints/distritos/templates/distritos/list.jinja2 @@ -0,0 +1,84 @@ +{% extends 'layouts/app.jinja2' %} +{% import 'macros/list.jinja2' as list %} +{% import 'macros/topbar.jinja2' as topbar %} + +{% block title %}{{ titulo }}{% endblock %} + +{% block topbar_actions %} + {% call topbar.page_buttons(titulo) %} + {% if current_user.can_admin('DISTRITOS') %} + {% if estatus == 'A' %}{{ topbar.button_list_inactive('Inactivos', url_for('distritos.list_inactive')) }}{% endif %} + {% if estatus == 'B' %}{{ topbar.button_list_active('Activos', url_for('distritos.list_active')) }}{% endif %} + {% endif %} + {% if current_user.can_insert('DISTRITOS') %} + {{ topbar.button_new('Nuevo Distrito', url_for('distritos.new')) }} + {% endif %} + {% endcall %} +{% endblock %} + +{% block content %} + {% call list.card() %} + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
+
+
+ + + + + + + + +
ClaveNombre
+ {% endcall %} +{% endblock %} + +{% block custom_javascript %} + + + +{% endblock %} diff --git a/orion/blueprints/distritos/templates/distritos/new.jinja2 b/orion/blueprints/distritos/templates/distritos/new.jinja2 new file mode 100644 index 0000000..e9a900e --- /dev/null +++ b/orion/blueprints/distritos/templates/distritos/new.jinja2 @@ -0,0 +1,21 @@ +{% extends 'layouts/app.jinja2' %} +{% import 'macros/form.jinja2' as f with context %} +{% import 'macros/topbar.jinja2' as topbar %} + +{% block title %}Nuevo Distrito{% endblock %} + +{% block topbar_actions %} + {% call topbar.page_buttons('Nuevo Distrito') %} + {{ topbar.button_cancel() }} + {% endcall %} +{% endblock %} + +{% block content %} + {% call f.card() %} + {% call f.form_tag('distritos.new', fid='distritos_form') %} + {% call f.form_group(form.clave) %}{% endcall %} + {% call f.form_group(form.nombre) %}{% endcall %} + {% call f.form_group(form.guardar) %}{% endcall %} + {% endcall %} + {% endcall %} +{% endblock %} diff --git a/orion/blueprints/distritos/views.py b/orion/blueprints/distritos/views.py new file mode 100644 index 0000000..90743ec --- /dev/null +++ b/orion/blueprints/distritos/views.py @@ -0,0 +1,197 @@ +""" +Distritos, vistas +""" + +import json +from flask import Blueprint, flash, redirect, render_template, request, url_for +from flask_login import current_user, login_required + +from lib.datatables import get_datatable_parameters, output_datatable_json +from lib.safe_string import safe_string, safe_message + +from orion.blueprints.bitacoras.models import Bitacora +from orion.blueprints.distritos.forms import DistritoForm +from orion.blueprints.modulos.models import Modulo +from orion.blueprints.permisos.models import Permiso +from orion.blueprints.usuarios.decorators import permission_required +from orion.blueprints.distritos.models import Distrito + +MODULO = "DISTRITOS" + +distritos = Blueprint("distritos", __name__, template_folder="templates") + + +@distritos.before_request +@login_required +@permission_required(MODULO, Permiso.VER) +def before_request(): + """Permiso por defecto""" + + +@distritos.route("/distritos/datatable_json", methods=["GET", "POST"]) +def datatable_json(): + """DataTable JSON para listado de Distrito""" + # Tomar parámetros de Datatables + draw, start, rows_per_page = get_datatable_parameters() + # Consultar + consulta = Distrito.query + # Primero filtrar por columnas propias + if "estatus" in request.form: + consulta = consulta.filter_by(estatus=request.form["estatus"]) + else: + consulta = consulta.filter_by(estatus="A") + if "clave" in request.form: + clave = safe_string(request.form["clave"]) + if clave != "": + consulta = consulta.filter(Distrito.clave.contains(clave)) + if "nombre" in request.form: + nombre = safe_string(request.form["nombre"], save_enie=True) + if nombre != "": + consulta = consulta.filter(Distrito.nombre.contains(nombre)) + # Ordenar y paginar + registros = consulta.order_by(Distrito.clave).offset(start).limit(rows_per_page).all() + total = consulta.count() + # Elaborar datos para DataTable + data = [] + for resultado in registros: + data.append( + { + "detalle": { + "clave": resultado.clave, + "url": url_for("distritos.detail", distrito_id=resultado.id), + }, + "nombre": resultado.nombre, + } + ) + # Entregar JSON + return output_datatable_json(draw, total, data) + + +@distritos.route("/distritos") +def list_active(): + """Listado de Distritos activos""" + return render_template( + "distritos/list.jinja2", + filtros=json.dumps({"estatus": "A"}), + titulo="Distritos", + estatus="A", + ) + + +@distritos.route("/distritos/inactivos") +@permission_required(MODULO, Permiso.ADMINISTRAR) +def list_inactive(): + """Listado de Distritos inactivos""" + return render_template( + "distritos/list.jinja2", + filtros=json.dumps({"estatus": "B"}), + titulo="Distritos inactivos", + estatus="B", + ) + + +@distritos.route("/distritos/") +def detail(distrito_id): + """Detalle de un Distrito""" + distrito = Distrito.query.get_or_404(distrito_id) + filtros_personas = json.dumps({"estatus": "A"}) + return render_template("distritos/detail.jinja2", distrito=distrito, filtros_personas=filtros_personas) + + +@distritos.route("/distritos/nuevo", methods=["GET", "POST"]) +@permission_required(MODULO, Permiso.CREAR) +def new(): + """Nuevo Distrito""" + form = DistritoForm() + if form.validate_on_submit(): + # Validar que el nombre no se repita + clave = safe_string(form.clave.data, save_enie=True) + if Distrito.query.filter_by(clave=clave).first(): + flash("La clave ya está en uso. Debe de ser única.", "warning") + return render_template("distritos/new.jinja2", form=form) + # Guardar + distrito = Distrito( + clave=clave, + nombre=safe_string(form.nombre.data, save_enie=True), + ) + distrito.save() + bitacora = Bitacora( + modulo=Modulo.query.filter_by(nombre=MODULO).first(), + usuario=current_user, + descripcion=safe_message(f"Nuevo Distrito {distrito.nombre}"), + url=url_for("distritos.detail", distrito_id=distrito.id), + ) + bitacora.save() + flash(bitacora.descripcion, "success") + return redirect(bitacora.url) + return render_template("distritos/new.jinja2", form=form) + + +@distritos.route("/distritos/edicion/", methods=["GET", "POST"]) +@permission_required(MODULO, Permiso.MODIFICAR) +def edit(distrito_id): + """Editar Distrito""" + distrito = Distrito.query.get_or_404(distrito_id) + form = DistritoForm() + if form.validate_on_submit(): + es_valido = True + # Si cambia el nombre verificar que no este en uso + clave = safe_string(form.clave.data) + if distrito.clave != clave: + distrito_existente = Distrito.query.filter_by(clave=clave).first() + if distrito_existente and distrito_existente.id != distrito.id: + es_valido = False + flash("La clave ya está en uso. Debe de ser única.", "warning") + # Si es valido actualizar + if es_valido: + distrito.clave = clave + distrito.nombre = safe_string(form.nombre.data, save_enie=True) + distrito.save() + bitacora = Bitacora( + modulo=Modulo.query.filter_by(nombre=MODULO).first(), + usuario=current_user, + descripcion=safe_message(f"Editado Distrito {distrito.clave}"), + url=url_for("distritos.detail", distrito_id=distrito.id), + ) + bitacora.save() + flash(bitacora.descripcion, "success") + return redirect(bitacora.url) + form.clave.data = distrito.clave + form.nombre.data = distrito.nombre + return render_template("distritos/edit.jinja2", form=form, distrito=distrito) + + +@distritos.route("/distritos/eliminar/") +@permission_required(MODULO, Permiso.ADMINISTRAR) +def delete(distrito_id): + """Eliminar Distrito""" + distrito = Distrito.query.get_or_404(distrito_id) + if distrito.estatus == "A": + distrito.delete() + bitacora = Bitacora( + modulo=Modulo.query.filter_by(nombre=MODULO).first(), + usuario=current_user, + descripcion=safe_message(f"Eliminado Distrito {distrito.clave}"), + url=url_for("distritos.detail", distrito_id=distrito.id), + ) + bitacora.save() + flash(bitacora.descripcion, "success") + return redirect(url_for("distritos.detail", distrito_id=distrito.id)) + + +@distritos.route("/distritos/recuperar/") +@permission_required(MODULO, Permiso.ADMINISTRAR) +def recover(distrito_id): + """Recuperar Distrito""" + distrito = Distrito.query.get_or_404(distrito_id) + if distrito.estatus == "B": + distrito.recover() + bitacora = Bitacora( + modulo=Modulo.query.filter_by(nombre=MODULO).first(), + usuario=current_user, + descripcion=safe_message(f"Recuperado Distrito {distrito.clave}"), + url=url_for("distritos.detail", distrito_id=distrito.id), + ) + bitacora.save() + flash(bitacora.descripcion, "success") + return redirect(url_for("distritos.detail", distrito_id=distrito.id)) diff --git a/orion/blueprints/personas/views.py b/orion/blueprints/personas/views.py index 735fc1a..89628d5 100644 --- a/orion/blueprints/personas/views.py +++ b/orion/blueprints/personas/views.py @@ -63,6 +63,7 @@ def datatable_json(): "url": url_for("personas.detail", persona_id=resultado.id), }, "situacion": resultado.situacion, + "sexo": "HOMBRE" if resultado.sexo == "H" else "MUJER", } ) # Entregar JSON