Arch/utils/file_utils.py

209 lines
5.8 KiB
Python
Raw Normal View History

2025-03-03 15:35:24 -03:00
import os
import json
import shutil
import zipfile
from datetime import datetime
import pytz
from flask import current_app
from werkzeug.utils import secure_filename
def ensure_dir_exists(directory):
"""
Asegurar que un directorio existe, creándolo si es necesario.
Args:
directory (str): Ruta del directorio
"""
if not os.path.exists(directory):
os.makedirs(directory)
def load_json_file(file_path, default=None):
"""
Cargar un archivo JSON.
Args:
file_path (str): Ruta al archivo JSON
default: Valor por defecto si el archivo no existe o no es válido
Returns:
dict: Contenido del archivo JSON o valor por defecto
"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return {} if default is None else default
def save_json_file(file_path, data):
"""
Guardar datos en un archivo JSON.
Args:
file_path (str): Ruta al archivo JSON
data: Datos a guardar (deben ser serializables a JSON)
"""
# Asegurar que el directorio existe
directory = os.path.dirname(file_path)
ensure_dir_exists(directory)
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def get_next_id(id_type):
"""
Obtener el siguiente ID disponible para proyectos o documentos.
Args:
id_type (str): Tipo de ID ('project' o 'document')
Returns:
int: Siguiente ID disponible
"""
storage_path = current_app.config['STORAGE_PATH']
indices_file = os.path.join(storage_path, 'indices.json')
# Cargar índices
indices = load_json_file(indices_file, {"max_project_id": 0, "max_document_id": 0})
# Incrementar y guardar
if id_type == 'project':
indices['max_project_id'] += 1
new_id = indices['max_project_id']
elif id_type == 'document':
indices['max_document_id'] += 1
new_id = indices['max_document_id']
else:
raise ValueError(f"Tipo de ID no válido: {id_type}")
save_json_file(indices_file, indices)
return new_id
def format_project_directory_name(project_id, project_name):
"""
Formatear nombre de directorio para un proyecto.
Args:
project_id (int): ID del proyecto
project_name (str): Nombre del proyecto
Returns:
str: Nombre de directorio formateado
"""
# Sanitizar nombre de proyecto
safe_name = secure_filename(project_name)
safe_name = safe_name.replace(' ', '_')
# Formatear como @id_num_@project_name
return f"@{project_id:03d}_@{safe_name}"
def format_document_directory_name(document_id, document_name):
"""
Formatear nombre de directorio para un documento.
Args:
document_id (int): ID del documento
document_name (str): Nombre del documento
Returns:
str: Nombre de directorio formateado
"""
# Sanitizar nombre de documento
safe_name = secure_filename(document_name)
safe_name = safe_name.replace(' ', '_')
# Formatear como @id_num_@doc_name
return f"@{document_id:03d}_@{safe_name}"
def format_version_filename(version, document_name, extension):
"""
Formatear nombre de archivo para una versión de documento.
Args:
version (int): Número de versión
document_name (str): Nombre base del documento
extension (str): Extensión del archivo
Returns:
str: Nombre de archivo formateado
"""
# Sanitizar nombre
safe_name = secure_filename(document_name)
safe_name = safe_name.replace(' ', '_')
# Asegurar que la extensión no tiene el punto
if extension.startswith('.'):
extension = extension[1:]
# Formatear como v001_doc_name.ext
return f"v{version:03d}_{safe_name}.{extension}"
def create_zip_archive(source_dir, files_to_include, output_path):
"""
Crear un archivo ZIP con los documentos seleccionados.
Args:
source_dir (str): Directorio fuente
files_to_include (list): Lista de archivos a incluir
output_path (str): Ruta de salida para el ZIP
Returns:
str: Ruta al archivo ZIP creado
"""
with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for file_info in files_to_include:
zipf.write(
os.path.join(source_dir, file_info['path']),
arcname=file_info['arcname']
)
return output_path
def get_directory_size(directory):
"""
Calcular el tamaño total de un directorio en bytes.
Args:
directory (str): Ruta al directorio
Returns:
int: Tamaño en bytes
"""
total_size = 0
for dirpath, dirnames, filenames in os.walk(directory):
for filename in filenames:
file_path = os.path.join(dirpath, filename)
total_size += os.path.getsize(file_path)
return total_size
def get_file_info(file_path):
"""
Obtener información básica sobre un archivo.
Args:
file_path (str): Ruta al archivo
Returns:
dict: Información del archivo
"""
stat_info = os.stat(file_path)
return {
'filename': os.path.basename(file_path),
'size': stat_info.st_size,
'created': datetime.fromtimestamp(stat_info.st_ctime, pytz.UTC).isoformat(),
'modified': datetime.fromtimestamp(stat_info.st_mtime, pytz.UTC).isoformat(),
'path': file_path
}
def delete_directory_with_content(directory):
"""
Eliminar un directorio y todo su contenido.
Args:
directory (str): Ruta al directorio a eliminar
"""
if os.path.exists(directory):
shutil.rmtree(directory)