Arch/routes/project_routes.py

185 lines
6.7 KiB
Python
Raw Normal View History

2025-03-03 15:35:24 -03:00
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
from flask_login import login_required, current_user
from flask_wtf import FlaskForm
from wtforms import StringField, SelectField, TextAreaField, SubmitField
from wtforms.validators import DataRequired, Length
from services.project_service import (
create_project, update_project, get_project, delete_project,
get_all_projects, get_project_children, get_project_document_count,
filter_projects
)
from services.schema_service import get_all_schemas
from utils.security import permission_required
# Definir Blueprint
projects_bp = Blueprint('projects', __name__, url_prefix='/projects')
# Formularios
class ProjectForm(FlaskForm):
"""Formulario de proyecto."""
descripcion = StringField('Descripción', validators=[DataRequired(), Length(1, 100)])
cliente = StringField('Cliente', validators=[DataRequired(), Length(1, 100)])
destinacion = StringField('Destinación', validators=[Length(0, 100)])
esquema = SelectField('Esquema', validators=[DataRequired()])
proyecto_padre = SelectField('Proyecto Padre', validators=[])
submit = SubmitField('Guardar')
class ProjectFilterForm(FlaskForm):
"""Formulario de filtrado de proyectos."""
cliente = StringField('Cliente')
estado = SelectField('Estado', choices=[('', 'Todos'), ('activo', 'Activo'), ('inactivo', 'Inactivo')])
ano_inicio = StringField('Año Inicio')
ano_fin = StringField('Año Fin')
descripcion = StringField('Descripción')
submit = SubmitField('Filtrar')
# Rutas
@projects_bp.route('/')
@login_required
def list():
"""Listar proyectos."""
filter_form = ProjectFilterForm(request.args)
# Obtener proyectos según filtros
if request.args:
projects = filter_projects(request.args)
else:
projects = get_all_projects()
return render_template('projects/list.html',
projects=projects,
filter_form=filter_form)
@projects_bp.route('/create', methods=['GET', 'POST'])
@login_required
@permission_required(1000) # Nivel mínimo para crear proyectos
def create():
"""Crear nuevo proyecto."""
form = ProjectForm()
# Cargar opciones para esquemas
schemas = get_all_schemas()
form.esquema.choices = [(code, schema['descripcion']) for code, schema in schemas.items()]
# Cargar opciones para proyectos padre
projects = [(p['codigo'], p['descripcion']) for p in get_all_projects()]
form.proyecto_padre.choices = [('', 'Ninguno')] + projects
if form.validate_on_submit():
# Preparar datos del proyecto
project_data = {
'descripcion': form.descripcion.data,
'cliente': form.cliente.data,
'destinacion': form.destinacion.data,
'esquema': form.esquema.data,
'proyecto_padre': form.proyecto_padre.data if form.proyecto_padre.data else None
}
# Crear proyecto
success, message, project_id = create_project(project_data, current_user.username)
if success:
flash(message, 'success')
return redirect(url_for('projects.view', project_id=project_id))
else:
flash(message, 'danger')
return render_template('projects/create.html', form=form)
@projects_bp.route('/<int:project_id>')
@login_required
def view(project_id):
"""Ver detalles de un proyecto."""
project = get_project(project_id)
if not project:
flash('Proyecto no encontrado.', 'danger')
return redirect(url_for('projects.list'))
# Obtener información adicional
children = get_project_children(project_id)
document_count = get_project_document_count(project_id)
return render_template('projects/view.html',
project=project,
children=children,
document_count=document_count)
@projects_bp.route('/<int:project_id>/edit', methods=['GET', 'POST'])
@login_required
@permission_required(5000) # Nivel mínimo para editar proyectos
def edit(project_id):
"""Editar un proyecto."""
project = get_project(project_id)
if not project:
flash('Proyecto no encontrado.', 'danger')
return redirect(url_for('projects.list'))
form = ProjectForm()
# Cargar opciones para esquemas
schemas = get_all_schemas()
form.esquema.choices = [(code, schema['descripcion']) for code, schema in schemas.items()]
# Cargar opciones para proyectos padre (excluyendo este proyecto y sus hijos)
all_projects = get_all_projects()
children_codes = [child['codigo'] for child in get_project_children(project_id)]
available_projects = [(p['codigo'], p['descripcion']) for p in all_projects
if p['codigo'] != project['codigo'] and p['codigo'] not in children_codes]
form.proyecto_padre.choices = [('', 'Ninguno')] + available_projects
if request.method == 'GET':
# Cargar datos actuales
form.descripcion.data = project['descripcion']
form.cliente.data = project['cliente']
form.destinacion.data = project.get('destinacion', '')
form.esquema.data = project['esquema']
form.proyecto_padre.data = project.get('proyecto_padre', '')
if form.validate_on_submit():
# Preparar datos actualizados
project_data = {
'descripcion': form.descripcion.data,
'cliente': form.cliente.data,
'destinacion': form.destinacion.data,
'esquema': form.esquema.data,
'proyecto_padre': form.proyecto_padre.data if form.proyecto_padre.data else None
}
# Actualizar proyecto
success, message = update_project(project_id, project_data, current_user.username)
if success:
flash(message, 'success')
return redirect(url_for('projects.view', project_id=project_id))
else:
flash(message, 'danger')
return render_template('projects/edit.html', form=form, project=project)
@projects_bp.route('/<int:project_id>/delete', methods=['POST'])
@login_required
@permission_required(9000) # Nivel alto para eliminar proyectos
def delete(project_id):
"""Eliminar un proyecto (marcar como inactivo)."""
success, message = delete_project(project_id)
if success:
flash(message, 'success')
else:
flash(message, 'danger')
return redirect(url_for('projects.list'))
@projects_bp.route('/api/list')
@login_required
def api_list():
"""API para listar proyectos (para selects dinámicos)."""
projects = get_all_projects()
return jsonify([{
'id': p['codigo'],
'text': p['descripcion']
} for p in projects])