Arch/services/index_service.py

277 lines
9.5 KiB
Python

import os
import json
import re
from datetime import datetime
import pytz
from flask import current_app
from utils.file_utils import load_json_file, save_json_file, ensure_dir_exists
from services.project_service import get_all_projects
from services.document_service import get_project_documents
def build_search_index():
"""
Construir un índice de búsqueda para proyectos y documentos.
Returns:
tuple: (success, message)
"""
try:
# Obtener todos los proyectos (incluyendo inactivos)
all_projects = get_all_projects(include_inactive=True)
# Inicializar índice
index = {
'projects': {},
'documents': {},
'clients': {},
'years': {},
'users': {},
'last_updated': datetime.now(pytz.UTC).isoformat()
}
# Indexar proyectos
for project in all_projects:
project_id = int(project.get('codigo', '').replace('PROJ', ''))
if not project_id:
continue
# Datos básicos del proyecto
project_data = {
'id': project_id,
'code': project.get('codigo'),
'description': project.get('descripcion', ''),
'client': project.get('cliente', ''),
'destination': project.get('destinacion', ''),
'year': project.get('ano_creacion'),
'status': project.get('estado', 'inactivo'),
'created_by': project.get('creado_por', ''),
'modified_by': project.get('modificado_por', ''),
'directory': project.get('directory', '')
}
# Añadir al índice de proyectos
index['projects'][str(project_id)] = project_data
# Añadir a índice de clientes
client = project.get('cliente', 'Sin cliente')
if client not in index['clients']:
index['clients'][client] = []
index['clients'][client].append(project_id)
# Añadir a índice de años
year = str(project.get('ano_creacion', 'Sin año'))
if year not in index['years']:
index['years'][year] = []
index['years'][year].append(project_id)
# Añadir a índice de usuarios
creator = project.get('creado_por', 'desconocido')
if creator not in index['users']:
index['users'][creator] = {'created': [], 'modified': []}
index['users'][creator]['created'].append(project_id)
modifier = project.get('modificado_por')
if modifier and modifier not in index['users']:
index['users'][modifier] = {'created': [], 'modified': []}
if modifier:
index['users'][modifier]['modified'].append(project_id)
# Indexar documentos del proyecto
documents = get_project_documents(project_id)
for doc in documents:
doc_id = doc.get('id')
if not doc_id:
continue
# Extraer nombre del documento
doc_name = doc.get('document_id', '').split('_', 1)[1] if '_' in doc.get('document_id', '') else f'doc_{doc_id}'
# Datos básicos del documento
doc_data = {
'id': doc_id,
'name': doc_name,
'project_id': project_id,
'original_filename': doc.get('original_filename', ''),
'versions': len(doc.get('versions', [])),
'latest_version': max([v.get('version', 0) for v in doc.get('versions', [])]) if doc.get('versions') else 0,
'directory': doc.get('directory', '')
}
# Añadir al índice de documentos
index['documents'][f"{project_id}_{doc_id}"] = doc_data
# Guardar índice
storage_path = current_app.config['STORAGE_PATH']
index_file = os.path.join(storage_path, 'indices.json')
save_json_file(index_file, index)
return True, "Índice construido correctamente."
except Exception as e:
current_app.logger.error(f"Error al construir índice: {str(e)}")
return False, f"Error al construir índice: {str(e)}"
def search_projects(query, filters=None):
"""
Buscar proyectos en el índice.
Args:
query (str): Término de búsqueda
filters (dict, optional): Filtros adicionales
- client: Cliente específico
- year: Año de creación
- status: Estado del proyecto
- created_by: Usuario creador
Returns:
list: Lista de proyectos que coinciden con la búsqueda
"""
# Cargar índice
storage_path = current_app.config['STORAGE_PATH']
index_file = os.path.join(storage_path, 'indices.json')
index = load_json_file(index_file, {})
if not index or 'projects' not in index:
return []
# Preparar resultados
results = []
# Convertir query a minúsculas para búsqueda insensible a mayúsculas
query = query.lower()
# Buscar en proyectos
for project_id, project_data in index.get('projects', {}).items():
# Aplicar filtros si existen
if filters:
# Filtrar por cliente
if 'client' in filters and filters['client'] and project_data.get('client') != filters['client']:
continue
# Filtrar por año
if 'year' in filters and filters['year'] and str(project_data.get('year')) != str(filters['year']):
continue
# Filtrar por estado
if 'status' in filters and filters['status'] and project_data.get('status') != filters['status']:
continue
# Filtrar por creador
if 'created_by' in filters and filters['created_by'] and project_data.get('created_by') != filters['created_by']:
continue
# Si no hay query, incluir todos los proyectos que pasen los filtros
if not query:
results.append(project_data)
continue
# Buscar coincidencias en campos relevantes
if (query in project_data.get('code', '').lower() or
query in project_data.get('description', '').lower() or
query in project_data.get('client', '').lower() or
query in project_data.get('destination', '').lower()):
results.append(project_data)
return results
def search_documents(query, project_id=None):
"""
Buscar documentos en el índice.
Args:
query (str): Término de búsqueda
project_id (int, optional): ID del proyecto para limitar la búsqueda
Returns:
list: Lista de documentos que coinciden con la búsqueda
"""
# Cargar índice
storage_path = current_app.config['STORAGE_PATH']
index_file = os.path.join(storage_path, 'indices.json')
index = load_json_file(index_file, {})
if not index or 'documents' not in index:
return []
# Preparar resultados
results = []
# Convertir query a minúsculas para búsqueda insensible a mayúsculas
query = query.lower()
# Buscar en documentos
for doc_key, doc_data in index.get('documents', {}).items():
# Si se especifica proyecto, filtrar por él
if project_id is not None and doc_data.get('project_id') != int(project_id):
continue
# Si no hay query, incluir todos los documentos del proyecto
if not query:
results.append(doc_data)
continue
# Buscar coincidencias en campos relevantes
if (query in doc_data.get('name', '').lower() or
query in doc_data.get('original_filename', '').lower()):
results.append(doc_data)
return results
def get_index_stats():
"""
Obtener estadísticas del índice.
Returns:
dict: Estadísticas del índice
"""
# Cargar índice
storage_path = current_app.config['STORAGE_PATH']
index_file = os.path.join(storage_path, 'indices.json')
index = load_json_file(index_file, {})
if not index:
return {
'projects': 0,
'documents': 0,
'clients': 0,
'years': 0,
'users': 0,
'last_updated': None
}
# Calcular estadísticas
stats = {
'projects': len(index.get('projects', {})),
'documents': len(index.get('documents', {})),
'clients': len(index.get('clients', {})),
'years': len(index.get('years', {})),
'users': len(index.get('users', {})),
'last_updated': index.get('last_updated')
}
return stats
def schedule_index_update():
"""
Programar actualización periódica del índice.
"""
from apscheduler.schedulers.background import BackgroundScheduler
scheduler = BackgroundScheduler()
# Programar actualización diaria a las 3:00 AM
scheduler.add_job(
build_search_index,
'cron',
hour=3,
minute=0,
id='index_update'
)
scheduler.start()
current_app.logger.info("Actualización de índice programada.")