271 lines
9.4 KiB
Python
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)
|