Cliente: Centrica Soluciones
Proyecto: Nebula ERP - Componente DataVault
Version: 1.0
Fecha: 16 de marzo, 2026
Autor: Carlos Torres (Arquitecto de Software)
Estado: VIGENTE
El modelo de datos de DataVault utiliza SQLAlchemy como ORM sobre PostgreSQL (produccion) o SQLite (desarrollo). El esquema soporta el ciclo de vida completo de la preservacion digital OAIS.
Conexion de produccion:
postgresql+psycopg2://egs:{password}@192.168.0.16/datavault
+------------------+ +------------------+ +------------------+
| projects | | ingest | | consulta |
+------------------+ +------------------+ +------------------+
| id (PK) |<---+ | id (PK) | | id (PK) |
| name | | | user_id | | ingest_id |
| description | +--| project_id (FK) | | user_id |
| folder_path | | user_fullname | | duration_hours |
| is_active | | local_path | | status |
| auto_process | | remote_path | | description |
| retention_days | | status | | result_link |
| folder_structure | | upload_id | | created_at |
| custom_subfolders| | file_size_bytes | | available_at |
| created_at | | created_at | | expires_at |
| updated_at | | updated_at | +------------------+
+------------------+ +------------------+
+------------------+
+------------------+ | uploads |
| company | +------------------+
+------------------+ | id (PK, String) |
| id (PK) | | user_id |
| name | | local_path |
+------------------+ | remote_path |
| status |
| created_at |
| updated_at |
+------------------+
projectsGestiona la estructura de proyectos dentro del repositorio OAIS. Cada proyecto tiene su propia jerarquia de directorios SIP/AIP/DIP.
| Columna | Tipo | Nullable | Default | Descripcion |
|---|---|---|---|---|
id |
Integer | NO | autoincrement | Identificador unico |
name |
String | NO | - | Nombre del proyecto (unico) |
description |
String | SI | NULL | Descripcion del proyecto |
folder_path |
String | SI | NULL | Ruta en filesystem |
is_active |
Boolean | NO | True | Si acepta nuevas ingestas |
auto_process |
Boolean | NO | True | Procesamiento automatico |
retention_days |
Integer | SI | NULL | Dias de retencion (politica) |
folder_structure |
String | SI | NULL | Estructura de carpetas JSON |
custom_subfolders |
String | SI | NULL | Subcarpetas personalizadas JSON |
created_at |
DateTime | NO | now() | Fecha de creacion |
updated_at |
DateTime | NO | now() | Ultima modificacion |
Observaciones criticas:
folder_structure y custom_subfolders almacenan JSON como String. En PostgreSQL deberian ser columnas JSONB para queries eficientes.UNIQUE explicito en name a nivel de modelo ORM (solo logico).ingestRegistra cada operacion de ingesta, vinculando el upload del usuario con el paquete OAIS resultante.
| Columna | Tipo | Nullable | Default | Descripcion |
|---|---|---|---|---|
id |
Integer | NO | autoincrement | Identificador unico |
user_id |
String | SI | NULL | ID del usuario que realizo la carga |
user_fullname |
String | SI | NULL | Nombre completo del usuario |
project_id |
Integer (FK) | SI | NULL | Referencia al proyecto |
local_path |
String | SI | NULL | Ruta local del SIP/AIP |
remote_path |
String | SI | NULL | Ruta remota (SFTP/Azure) |
status |
String | NO | 'new' | Estado: new, staged, completed, error |
upload_id |
String | SI | NULL | ID de la sesion de upload |
file_size_bytes |
Integer | SI | NULL | Tamano total en bytes |
created_at |
DateTime | NO | now() | Fecha de ingesta |
updated_at |
DateTime | NO | now() | Ultima actualizacion |
Maquina de estados:
new --> staged --> completed
\ |
+--> error <------+
consultaGestiona solicitudes de acceso a paquetes DIP con duracion temporal.
| Columna | Tipo | Nullable | Default | Descripcion |
|---|---|---|---|---|
id |
Integer | NO | autoincrement | Identificador unico |
ingest_id |
Integer | SI | NULL | Referencia al ingest (no FK formal) |
user_id |
String | SI | NULL | ID del solicitante |
duration_hours |
Integer | NO | 24 | Duracion del acceso (24 o 48h) |
status |
String | NO | 'pendiente' | Estado del ciclo |
description |
String | SI | NULL | Descripcion de la solicitud |
result_link |
String | SI | NULL | URL de acceso al DIP |
created_at |
DateTime | NO | now() | Fecha de solicitud |
available_at |
DateTime | SI | NULL | Cuando se hizo disponible |
expires_at |
DateTime | SI | NULL | Fecha de expiracion |
Maquina de estados:
pendiente --> en_progreso --> respondido --> expirado
| |
+--> cerrado <-+
Observacion: ingest_id no tiene constraint de FK real — es un entero referencial sin integridad relacional. Esto permite consultas huerfanas si el ingest se elimina.
uploadsRastreo de sesiones de upload (antes de que se conviertan en ingestas).
| Columna | Tipo | Nullable | Default | Descripcion |
|---|---|---|---|---|
id |
String (PK) | NO | - | UUID de la sesion |
user_id |
String | SI | NULL | ID del usuario |
local_path |
String | SI | NULL | Ruta local temporal |
remote_path |
String | SI | NULL | Ruta remota destino |
status |
String | NO | 'uploaded' | Estado: uploaded, staged, completed, error |
created_at |
DateTime | NO | now() | Fecha de upload |
updated_at |
DateTime | NO | now() | Ultima actualizacion |
companyModelo simple de organizacion.
| Columna | Tipo | Nullable | Default | Descripcion |
|---|---|---|---|---|
id |
Integer | NO | autoincrement | Identificador unico |
name |
String | NO | - | Nombre de la empresa |
Los paquetes OAIS (SIP, AIP, DIP) no son modelos ORM. Son clases Python que operan sobre el filesystem:
class SIP:
"""Representa un Submission Information Package"""
unique_id: str # ID normalizado
source_path: str # Ruta original
workspace_path: str # Ruta en SIP/
data_path: str # Ruta data/ dentro del SIP
metadata_path: str # Ruta metadata/ dentro del SIP
class AIP:
"""Representa un Archival Information Package"""
unique_id: str # Mismo ID que SIP
zip_path: str # Ruta del ZIP en AIP/
checksum: str # SHA-256 del ZIP
class DIP:
"""Representa un Dissemination Information Package"""
unique_id: str # Mismo ID que SIP/AIP
zip_path: str # Ruta del ZIP en DIP/
Observacion critica: Estas clases deberian tener representacion en base de datos para queries eficientes. Actualmente, para saber si un AIP existe, se verifica el filesystem directamente — operacion costosa con miles de archivos.
Estado global del sistema OAIS. Actualizado por OAISStatusService.
{
"projects": {
"DIGITALIZACION": {
"files": ["id1", "id2"],
"last_activity": "2026-03-15T14:30:00"
}
},
"recent_activity": [
{
"action": "ingest_completed",
"id": "lote_01_20260315_a7b2c1d4",
"project": "DIGITALIZACION",
"timestamp": "2026-03-15T14:30:00"
}
],
"statistics": {
"total_sips": 150,
"total_aips": 148,
"total_dips": 148,
"total_errors": 2
}
}
Estado de preservacion por archivo. Actualizado por PreservationService.
{
"files": {
"lote_01_20260315_a7b2c1d4": {
"status": "GREEN",
"last_verified": "2026-03-15T15:00:00",
"checksum_valid": true,
"metadata_complete": true,
"retention_active": true
}
}
}
Base de datos SQLite independiente usada por los cloud sync daemons para trackear estado de sincronizacion.
| Problema | Severidad | Detalle |
|---|---|---|
| Sin multi-tenancy | CRITICA | No hay campo tenant_id en ninguna tabla. Incompatible con Nebula. |
| FK faltantes | ALTA | consulta.ingest_id sin FK real. ingest.upload_id sin FK a uploads.id. |
| JSON como String | MEDIA | folder_structure y custom_subfolders deberian ser JSONB en PostgreSQL. |
| Sin indices | MEDIA | No hay indices definidos en ORM para status, project_id, created_at. |
| Sin soft-delete | MEDIA | No hay campo deleted_at. Los registros se eliminan fisicamente. |
| Datos OAIS fuera de BD | ALTA | SIP/AIP/DIP solo existen en filesystem. Sin metadata en BD sobre paquetes individuales. |
| Dual storage | MEDIA | oais_status.json duplica informacion que deberia estar en PostgreSQL. |
Para integracion con Nebula, se propone agregar:
-- Tabla de paquetes OAIS (nueva)
CREATE TABLE oais_packages (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL, -- Multi-tenancy Nebula
project_id INTEGER REFERENCES projects(id),
unique_id VARCHAR(255) NOT NULL UNIQUE,
package_type VARCHAR(3) NOT NULL, -- SIP, AIP, DIP
file_path TEXT NOT NULL,
checksum_sha256 VARCHAR(64),
file_size_bytes BIGINT,
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMP DEFAULT NOW(),
verified_at TIMESTAMP,
CONSTRAINT chk_type CHECK (package_type IN ('SIP', 'AIP', 'DIP'))
);
-- Indices recomendados
CREATE INDEX idx_packages_tenant ON oais_packages(tenant_id);
CREATE INDEX idx_packages_project ON oais_packages(project_id);
CREATE INDEX idx_packages_status ON oais_packages(status);
CREATE INDEX idx_ingest_status ON ingest(status);
CREATE INDEX idx_ingest_project ON ingest(project_id);
CREATE INDEX idx_consulta_status ON consulta(status);
Departamento de Arquitectura — Centrica Soluciones