Arch/routes/document_routes.py

310 lines
9.3 KiB
Python
Raw Normal View History

2025-03-03 15:35:24 -03:00
import os
from flask import (
Blueprint,
render_template,
redirect,
url_for,
flash,
request,
jsonify,
send_file,
abort,
)
2025-03-03 15:35:24 -03:00
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,
2025-03-03 15:35:24 -03:00
)
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")
2025-03-03 15:35:24 -03:00
# 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")
2025-03-03 15:35:24 -03:00
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")
2025-03-03 15:35:24 -03:00
# Rutas
@documents_bp.route("/<project_id>")
2025-03-03 15:35:24 -03:00
@login_required
def list(project_id):
"""Listar documentos de un proyecto."""
project = get_project(project_id)
2025-03-03 15:35:24 -03:00
if not project:
flash("Proyecto no encontrado.", "danger")
return redirect(url_for("projects.list"))
2025-03-03 15:35:24 -03:00
documents = get_project_documents(project_id)
return render_template("documents/list.html", project=project, documents=documents)
@documents_bp.route("/<project_id>/upload", methods=["GET", "POST"])
2025-03-03 15:35:24 -03:00
@login_required
@permission_required(1000) # Nivel mínimo para subir documentos
def upload(project_id):
"""Subir un nuevo documento."""
project = get_project(project_id)
2025-03-03 15:35:24 -03:00
if not project:
flash("Proyecto no encontrado.", "danger")
return redirect(url_for("projects.list"))
2025-03-03 15:35:24 -03:00
# 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))
2025-03-03 15:35:24 -03:00
form = DocumentUploadForm()
2025-03-03 15:35:24 -03:00
if form.validate_on_submit():
# Añadir documento
success, message, document_id = add_document(
project_id,
{"nombre": form.nombre.data, "description": form.description.data},
2025-03-03 15:35:24 -03:00
form.file.data,
current_user.username,
2025-03-03 15:35:24 -03:00
)
2025-03-03 15:35:24 -03:00
if success:
flash(message, "success")
return redirect(
url_for(
"documents.versions", project_id=project_id, document_id=document_id
)
)
2025-03-03 15:35:24 -03:00
else:
flash(message, "danger")
return render_template("documents/upload.html", form=form, project=project)
2025-03-03 15:35:24 -03:00
@documents_bp.route("/<project_id>/<int:document_id>")
2025-03-03 15:35:24 -03:00
@login_required
def versions(project_id, document_id):
"""Ver versiones de un documento."""
project = get_project(project_id)
2025-03-03 15:35:24 -03:00
if not project:
flash("Proyecto no encontrado.", "danger")
return redirect(url_for("projects.list"))
2025-03-03 15:35:24 -03:00
document = get_document(project_id, document_id)
2025-03-03 15:35:24 -03:00
if not document:
flash("Documento no encontrado.", "danger")
return redirect(url_for("projects.view", project_id=project_id))
2025-03-03 15:35:24 -03:00
form = DocumentVersionForm()
form.document_id.data = document_id
return render_template(
"documents/versions.html", project=project, document=document, form=form
)
@documents_bp.route("/<project_id>/<int:document_id>/upload", methods=["POST"])
2025-03-03 15:35:24 -03:00
@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)
2025-03-03 15:35:24 -03:00
if not project:
flash("Proyecto no encontrado.", "danger")
return redirect(url_for("projects.list"))
2025-03-03 15:35:24 -03:00
# 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))
2025-03-03 15:35:24 -03:00
document = get_document(project_id, document_id)
2025-03-03 15:35:24 -03:00
if not document:
flash("Documento no encontrado.", "danger")
return redirect(url_for("projects.view", project_id=project_id))
2025-03-03 15:35:24 -03:00
form = DocumentVersionForm()
2025-03-03 15:35:24 -03:00
if form.validate_on_submit():
# Añadir versión
success, message, version = add_version(
project_id,
document_id,
{"description": form.description.data},
2025-03-03 15:35:24 -03:00
form.file.data,
current_user.username,
2025-03-03 15:35:24 -03:00
)
2025-03-03 15:35:24 -03:00
if success:
flash(message, "success")
2025-03-03 15:35:24 -03:00
else:
flash(message, "danger")
2025-03-03 15:35:24 -03:00
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)
)
2025-03-03 15:35:24 -03:00
@documents_bp.route("/<project_id>/<int:document_id>/download/<int:version>")
2025-03-03 15:35:24 -03:00
@login_required
def download(project_id, document_id, version):
"""Descargar una versión específica de un documento."""
project = get_project(project_id)
2025-03-03 15:35:24 -03:00
if not project:
flash("Proyecto no encontrado.", "danger")
return redirect(url_for("projects.list"))
2025-03-03 15:35:24 -03:00
# Obtener versión solicitada
version_meta, file_path = get_document_version(project_id, document_id, version)
2025-03-03 15:35:24 -03:00
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))
2025-03-03 15:35:24 -03:00
# Registrar descarga
register_download(project_id, document_id, version, current_user.username)
2025-03-03 15:35:24 -03:00
# Enviar archivo
try:
return send_file(
file_path,
mimetype=version_meta["mime_type"],
2025-03-03 15:35:24 -03:00
as_attachment=True,
download_name=os.path.basename(file_path),
2025-03-03 15:35:24 -03:00
)
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
)
)
2025-03-03 15:35:24 -03:00
@documents_bp.route("/<project_id>/<int:document_id>/latest")
2025-03-03 15:35:24 -03:00
@login_required
def download_latest(project_id, document_id):
"""Descargar la última versión de un documento."""
project = get_project(project_id)
2025-03-03 15:35:24 -03:00
if not project:
flash("Proyecto no encontrado.", "danger")
return redirect(url_for("projects.list"))
2025-03-03 15:35:24 -03:00
# Obtener última versión
version_meta, file_path = get_latest_version(project_id, document_id)
2025-03-03 15:35:24 -03:00
if not version_meta or not file_path:
flash("Documento no encontrado.", "danger")
return redirect(url_for("projects.view", project_id=project_id))
2025-03-03 15:35:24 -03:00
# Registrar descarga
register_download(
project_id, document_id, version_meta["version"], current_user.username
)
2025-03-03 15:35:24 -03:00
# Enviar archivo
try:
return send_file(
file_path,
mimetype=version_meta["mime_type"],
2025-03-03 15:35:24 -03:00
as_attachment=True,
download_name=os.path.basename(file_path),
2025-03-03 15:35:24 -03:00
)
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
)
)
2025-03-03 15:35:24 -03:00
@documents_bp.route("/<project_id>/<int:document_id>/delete", methods=["POST"])
2025-03-03 15:35:24 -03:00
@login_required
@permission_required(9000) # Nivel alto para eliminar documentos
def delete(project_id, document_id):
"""Eliminar un documento."""
project = get_project(project_id)
2025-03-03 15:35:24 -03:00
if not project:
flash("Proyecto no encontrado.", "danger")
return redirect(url_for("projects.list"))
2025-03-03 15:35:24 -03:00
success, message = delete_document(project_id, document_id)
2025-03-03 15:35:24 -03:00
if success:
flash(message, "success")
2025-03-03 15:35:24 -03:00
else:
flash(message, "danger")
2025-03-03 15:35:24 -03:00
return redirect(url_for("projects.view", project_id=project_id))
@documents_bp.route("/<project_id>/export")
2025-03-03 15:35:24 -03:00
@login_required
def export(project_id):
"""Exportar documentos de un proyecto."""
project = get_project(project_id)
2025-03-03 15:35:24 -03:00
if not project:
flash("Proyecto no encontrado.", "danger")
return redirect(url_for("projects.list"))
2025-03-03 15:35:24 -03:00
documents = get_project_documents(project_id)
return render_template(
"documents/export.html", project=project, documents=documents
)
@documents_bp.route("/<project_id>/api/list")
2025-03-03 15:35:24 -03:00
@login_required
def api_list(project_id):
"""API para listar documentos de un proyecto."""
documents = get_project_documents(project_id)
return jsonify(documents)