Arch/routes/document_routes.py

271 lines
9.4 KiB
Python

import os
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, send_file, abort
from flask_login import login_required, current_user
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired
from wtforms import StringField, TextAreaField, SubmitField, HiddenField
from wtforms.validators import DataRequired, Length
from werkzeug.utils import secure_filename
from services.document_service import (
add_document, add_version, get_document, get_project_documents,
get_document_version, get_latest_version, register_download,
delete_document
)
from services.project_service import get_project
from services.schema_service import get_schema_document_types
from utils.security import permission_required
# Definir Blueprint
documents_bp = Blueprint('documents', __name__, url_prefix='/documents')
# Formularios
class DocumentUploadForm(FlaskForm):
"""Formulario para subir documento."""
nombre = StringField('Nombre del documento', validators=[DataRequired(), Length(1, 100)])
description = TextAreaField('Descripción', validators=[Length(0, 500)])
file = FileField('Archivo', validators=[FileRequired()])
submit = SubmitField('Subir documento')
class DocumentVersionForm(FlaskForm):
"""Formulario para nueva versión de documento."""
description = TextAreaField('Descripción de la versión', validators=[Length(0, 500)])
file = FileField('Archivo', validators=[FileRequired()])
document_id = HiddenField('ID de documento', validators=[DataRequired()])
submit = SubmitField('Subir nueva versión')
# Rutas
@documents_bp.route('/<int:project_id>')
@login_required
def list(project_id):
"""Listar documentos de un proyecto."""
project = get_project(project_id)
if not project:
flash('Proyecto no encontrado.', 'danger')
return redirect(url_for('projects.list'))
documents = get_project_documents(project_id)
return render_template('documents/list.html',
project=project,
documents=documents)
@documents_bp.route('/<int:project_id>/upload', methods=['GET', 'POST'])
@login_required
@permission_required(1000) # Nivel mínimo para subir documentos
def upload(project_id):
"""Subir un nuevo documento."""
project = get_project(project_id)
if not project:
flash('Proyecto no encontrado.', 'danger')
return redirect(url_for('projects.list'))
# Verificar que el proyecto esté activo
if project['estado'] != 'activo':
flash('No se pueden añadir documentos a un proyecto inactivo.', 'warning')
return redirect(url_for('projects.view', project_id=project_id))
form = DocumentUploadForm()
if form.validate_on_submit():
# Añadir documento
success, message, document_id = add_document(
project_id,
{
'nombre': form.nombre.data,
'description': form.description.data
},
form.file.data,
current_user.username
)
if success:
flash(message, 'success')
return redirect(url_for('documents.versions', project_id=project_id, document_id=document_id))
else:
flash(message, 'danger')
return render_template('documents/upload.html',
form=form,
project=project)
@documents_bp.route('/<int:project_id>/<int:document_id>')
@login_required
def versions(project_id, document_id):
"""Ver versiones de un documento."""
project = get_project(project_id)
if not project:
flash('Proyecto no encontrado.', 'danger')
return redirect(url_for('projects.list'))
document = get_document(project_id, document_id)
if not document:
flash('Documento no encontrado.', 'danger')
return redirect(url_for('projects.view', project_id=project_id))
form = DocumentVersionForm()
form.document_id.data = document_id
return render_template('documents/versions.html',
project=project,
document=document,
form=form)
@documents_bp.route('/<int:project_id>/<int:document_id>/upload', methods=['POST'])
@login_required
@permission_required(1000) # Nivel mínimo para subir versiones
def upload_version(project_id, document_id):
"""Subir una nueva versión de documento."""
project = get_project(project_id)
if not project:
flash('Proyecto no encontrado.', 'danger')
return redirect(url_for('projects.list'))
# Verificar que el proyecto esté activo
if project['estado'] != 'activo':
flash('No se pueden añadir versiones a un proyecto inactivo.', 'warning')
return redirect(url_for('projects.view', project_id=project_id))
document = get_document(project_id, document_id)
if not document:
flash('Documento no encontrado.', 'danger')
return redirect(url_for('projects.view', project_id=project_id))
form = DocumentVersionForm()
if form.validate_on_submit():
# Añadir versión
success, message, version = add_version(
project_id,
document_id,
{
'description': form.description.data
},
form.file.data,
current_user.username
)
if success:
flash(message, 'success')
else:
flash(message, 'danger')
else:
for field, errors in form.errors.items():
for error in errors:
flash(f"{getattr(form, field).label.text}: {error}", 'danger')
return redirect(url_for('documents.versions', project_id=project_id, document_id=document_id))
@documents_bp.route('/<int:project_id>/<int:document_id>/download/<int:version>')
@login_required
def download(project_id, document_id, version):
"""Descargar una versión específica de un documento."""
project = get_project(project_id)
if not project:
flash('Proyecto no encontrado.', 'danger')
return redirect(url_for('projects.list'))
# Obtener versión solicitada
version_meta, file_path = get_document_version(project_id, document_id, version)
if not version_meta or not file_path:
flash('Versión de documento no encontrada.', 'danger')
return redirect(url_for('projects.view', project_id=project_id))
# Registrar descarga
register_download(project_id, document_id, version, current_user.username)
# Enviar archivo
try:
return send_file(
file_path,
mimetype=version_meta['mime_type'],
as_attachment=True,
download_name=os.path.basename(file_path)
)
except Exception as e:
flash(f'Error al descargar el archivo: {str(e)}', 'danger')
return redirect(url_for('documents.versions', project_id=project_id, document_id=document_id))
@documents_bp.route('/<int:project_id>/<int:document_id>/latest')
@login_required
def download_latest(project_id, document_id):
"""Descargar la última versión de un documento."""
project = get_project(project_id)
if not project:
flash('Proyecto no encontrado.', 'danger')
return redirect(url_for('projects.list'))
# Obtener última versión
version_meta, file_path = get_latest_version(project_id, document_id)
if not version_meta or not file_path:
flash('Documento no encontrado.', 'danger')
return redirect(url_for('projects.view', project_id=project_id))
# Registrar descarga
register_download(project_id, document_id, version_meta['version'], current_user.username)
# Enviar archivo
try:
return send_file(
file_path,
mimetype=version_meta['mime_type'],
as_attachment=True,
download_name=os.path.basename(file_path)
)
except Exception as e:
flash(f'Error al descargar el archivo: {str(e)}', 'danger')
return redirect(url_for('documents.versions', project_id=project_id, document_id=document_id))
@documents_bp.route('/<int:project_id>/<int:document_id>/delete', methods=['POST'])
@login_required
@permission_required(9000) # Nivel alto para eliminar documentos
def delete(project_id, document_id):
"""Eliminar un documento."""
project = get_project(project_id)
if not project:
flash('Proyecto no encontrado.', 'danger')
return redirect(url_for('projects.list'))
success, message = delete_document(project_id, document_id)
if success:
flash(message, 'success')
else:
flash(message, 'danger')
return redirect(url_for('projects.view', project_id=project_id))
@documents_bp.route('/<int:project_id>/export')
@login_required
def export(project_id):
"""Exportar documentos de un proyecto."""
project = get_project(project_id)
if not project:
flash('Proyecto no encontrado.', 'danger')
return redirect(url_for('projects.list'))
documents = get_project_documents(project_id)
return render_template('documents/export.html',
project=project,
documents=documents)
@documents_bp.route('/<int:project_id>/api/list')
@login_required
def api_list(project_id):
"""API para listar documentos de un proyecto."""
documents = get_project_documents(project_id)
return jsonify(documents)