Modificada directorio base del proyecto
This commit is contained in:
parent
581aa551c4
commit
c8ceb8fddd
|
@ -1,72 +0,0 @@
|
||||||
# AutoBackups Project
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
AutoBackups is a Python application designed to automate the backup of Simatic S7 projects and other user-defined directories. The application leverages the Everything API to efficiently locate files and manage backups based on user-defined configurations.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
- Monitors specified directories for Simatic S7 files (*.s7p) and other user-defined directories.
|
|
||||||
- Automatically compresses and backs up projects on a configurable schedule (daily, hourly, 3-hour, 7-hour intervals, startup, or manual).
|
|
||||||
- Supports manual backup triggers.
|
|
||||||
- Skips projects with files in use and retries after one hour.
|
|
||||||
- Maintains a two-stage hash system: first checking *.s7p files, then all files to avoid unnecessary backups.
|
|
||||||
- User-friendly web interface built with Flask for configuration and status monitoring.
|
|
||||||
- Comprehensive logging system with timestamped log files.
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
```
|
|
||||||
autobackups
|
|
||||||
├── .logs/ # Application logs with startup timestamps
|
|
||||||
├── src
|
|
||||||
│ ├── app.py # Main entry point of the Flask application
|
|
||||||
│ ├── models
|
|
||||||
│ │ └── __init__.py # Data models for configuration and directories
|
|
||||||
│ ├── routes
|
|
||||||
│ │ └── __init__.py # API routes for backup operations
|
|
||||||
│ ├── services
|
|
||||||
│ │ └── __init__.py # Business logic for backups
|
|
||||||
│ └── utils
|
|
||||||
│ └── __init__.py # Utility functions for hashing and file access
|
|
||||||
├── static
|
|
||||||
│ ├── css # CSS files for styling
|
|
||||||
│ └── js # JavaScript files for client-side functionality
|
|
||||||
├── templates
|
|
||||||
│ └── index.html # Main HTML template for the web interface
|
|
||||||
├── config.json # Global configuration settings for the application
|
|
||||||
├── projects.json # Per-project configuration, schedules, and hash storage
|
|
||||||
├── requirements.txt # Python dependencies
|
|
||||||
└── README.md # Project documentation
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
1. Clone the repository:
|
|
||||||
```
|
|
||||||
git clone <repository-url>
|
|
||||||
cd autobackups
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Create a Miniconda environment:
|
|
||||||
```
|
|
||||||
conda create --name autobackups python=3.12
|
|
||||||
conda activate autobackups
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Install the required packages:
|
|
||||||
```
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Configure the application by editing `config.json` to specify directories to monitor and backup settings.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
- Start the Flask application:
|
|
||||||
```
|
|
||||||
python src/app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
- Access the web interface at `http://localhost:5000` to configure settings and monitor backup statuses.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
Contributions are welcome! Please submit a pull request or open an issue for any enhancements or bug fixes.
|
|
||||||
|
|
||||||
## License
|
|
||||||
This project is licensed under the MIT License. See the LICENSE file for details.
|
|
|
@ -1,258 +0,0 @@
|
||||||
# AutoBackups - Especificación Técnica Detallada
|
|
||||||
|
|
||||||
## Resumen Ejecutivo
|
|
||||||
AutoBackups es una aplicación Python que automatiza el backup de proyectos Simatic S7 y directorios definidos por el usuario, utilizando la API de Everything para búsqueda eficiente de archivos y un sistema de hash inteligente para evitar backups innecesarios.
|
|
||||||
|
|
||||||
## Arquitectura del Sistema
|
|
||||||
|
|
||||||
### 1. Componentes Principales
|
|
||||||
|
|
||||||
#### 1.1 Motor de Búsqueda (Everything API)
|
|
||||||
- **Librería**: PyEverything wrapper
|
|
||||||
- **DLL Local**: `Everything-SDK\dll\Everything64.dll`
|
|
||||||
- **Función**: Localizar archivos *.s7p en directorios de observación
|
|
||||||
- **Optimización**: Evitar último nivel del árbol para archivos .s7p
|
|
||||||
- **Ventaja**: Aprovecha el índice existente de Everything para búsquedas instantáneas
|
|
||||||
|
|
||||||
#### 1.2 Sistema de Hash en Dos Etapas
|
|
||||||
**Etapa 1**: Hash rápido de archivos *.s7p
|
|
||||||
- Solo verifica MD5 de (timestamp + tamaño) de archivos .s7p
|
|
||||||
- Si no hay cambios → no se procede con backup
|
|
||||||
- Si hay cambios → procede a Etapa 2
|
|
||||||
|
|
||||||
**Etapa 2**: Hash completo del proyecto
|
|
||||||
- Calcula MD5 de (timestamp + tamaño) de todos los archivos del directorio
|
|
||||||
- Compara con último hash almacenado
|
|
||||||
- Solo hace backup si hay diferencias
|
|
||||||
|
|
||||||
#### 1.3 Scheduler de Background
|
|
||||||
- **daily**: Una vez al día a hora configurada
|
|
||||||
- **hourly**: Cada hora
|
|
||||||
- **3-hour**: Cada 3 horas
|
|
||||||
- **7-hour**: Cada 7 horas
|
|
||||||
- **startup**: Al iniciar la aplicación
|
|
||||||
- **manual**: Solo bajo demanda del usuario
|
|
||||||
- **Escaneos**: Automáticos cada 1 hora
|
|
||||||
- **Backups**: Mínimo cada 10 minutos
|
|
||||||
- **Prioridad**: Baja prioridad de sistema
|
|
||||||
|
|
||||||
### 2. Estructura de Datos
|
|
||||||
|
|
||||||
#### 2.1 config.json (Configuración Global)
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"observation_directories": [
|
|
||||||
{
|
|
||||||
"path": "C:\\Projects\\Siemens",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "D:\\Engineering\\Projects",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "C:\\Important\\Docs",
|
|
||||||
"type": "manual",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"backup_destination": "D:\\Backups\\AutoBackups",
|
|
||||||
"default_schedule": "daily",
|
|
||||||
"default_schedule_time": "02:00",
|
|
||||||
"retry_delay_hours": 1,
|
|
||||||
"web_interface": {
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"port": 5000
|
|
||||||
},
|
|
||||||
"logging": {
|
|
||||||
"level": "INFO",
|
|
||||||
"max_log_files": 30
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2.2 projects.json (Estado de Proyectos)
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"projects": [
|
|
||||||
{
|
|
||||||
"id": "project_001",
|
|
||||||
"name": "PLC_MainLine",
|
|
||||||
"path": "C:\\Projects\\Siemens\\PLC_MainLine",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"s7p_file": "C:\\Projects\\Siemens\\PLC_MainLine\\PLC_MainLine.s7p",
|
|
||||||
"schedule": "daily",
|
|
||||||
"schedule_time": "02:00",
|
|
||||||
"enabled": true,
|
|
||||||
"last_backup": "2025-09-01T02:15:30",
|
|
||||||
"last_s7p_hash": "abc123def456",
|
|
||||||
"last_full_hash": "def789ghi012",
|
|
||||||
"last_s7p_timestamp": "2025-08-31T14:30:00",
|
|
||||||
"status": "ready",
|
|
||||||
"retry_count": 0,
|
|
||||||
"next_retry": null,
|
|
||||||
"error_message": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Flujo de Operación
|
|
||||||
|
|
||||||
#### 3.1 Inicio de Aplicación
|
|
||||||
1. Cargar configuración desde `config.json`
|
|
||||||
2. Cargar estado de proyectos desde `projects.json`
|
|
||||||
3. Inicializar logger con timestamp de inicio
|
|
||||||
4. Escanear directorios de observación usando Everything API
|
|
||||||
5. Actualizar lista de proyectos detectados
|
|
||||||
6. Iniciar scheduler
|
|
||||||
7. Iniciar interfaz web Flask
|
|
||||||
|
|
||||||
#### 4.2 Proceso de Backup
|
|
||||||
1. **Verificación de Espacio**: Verificar mínimo 100MB libres
|
|
||||||
2. **Verificación de Acceso .s7p**: Intentar comprimir solo archivo .s7p
|
|
||||||
3. **Hash Etapa 1**: Verificar MD5 de (timestamp + tamaño) del archivo .s7p
|
|
||||||
4. **Hash Etapa 2**: Si Etapa 1 detecta cambios, verificar hash completo
|
|
||||||
5. **Compresión**: Crear archivo ZIP con estructura específica preservada
|
|
||||||
6. **Almacenamiento**: Guardar en `backup_destination/ProjectPath/YYYY-MM-DD/HH-MM-SS_projects.zip`
|
|
||||||
7. **Actualización**: Actualizar hashes y timestamps en `projects.json`
|
|
||||||
|
|
||||||
#### 3.3 Gestión de Errores
|
|
||||||
- **Archivos en uso**: Marcar proyecto para reintento en 1 hora
|
|
||||||
- **Errores de permisos**: Log de error y notificación en interfaz
|
|
||||||
- **Errores de espacio**: Verificar espacio disponible antes de backup
|
|
||||||
|
|
||||||
### 4. Interfaz Web
|
|
||||||
|
|
||||||
#### 4.1 Dashboard Principal
|
|
||||||
- **Lista de Proyectos**: Tabla con estado, último backup, próximo backup
|
|
||||||
- **Controles Globales**: Backup manual global, pausar/reanudar scheduler
|
|
||||||
- **Estadísticas**: Total de proyectos, backups exitosos, errores
|
|
||||||
|
|
||||||
#### 4.2 Configuración de Proyectos
|
|
||||||
- **Habilitación/Deshabilitación**: Toggle por proyecto
|
|
||||||
- **Configuración de Schedule**: Dropdown con opciones de frecuencia
|
|
||||||
- **Backup Manual**: Botón de backup inmediato por proyecto
|
|
||||||
|
|
||||||
#### 4.3 Logs y Monitoreo
|
|
||||||
- **Log Viewer**: Mostrar logs recientes con filtros por nivel
|
|
||||||
- **Estado del Sistema**: Información de Everything API, espacio en disco
|
|
||||||
|
|
||||||
### 5. Estructura de Backup
|
|
||||||
|
|
||||||
#### 5.1 Nomenclatura de Archivos (Estructura Específica)
|
|
||||||
```
|
|
||||||
backup_destination/
|
|
||||||
├── LineA/
|
|
||||||
│ └── Project1/
|
|
||||||
│ ├── 2025-09-01/
|
|
||||||
│ │ ├── 02-15-30_projects.zip
|
|
||||||
│ │ └── 14-30-15_projects.zip
|
|
||||||
│ └── 2025-09-02/
|
|
||||||
│ └── 02-15-45_projects.zip
|
|
||||||
└── LineB/
|
|
||||||
└── Project2/
|
|
||||||
└── 2025-09-01/
|
|
||||||
└── 08-20-10_projects.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5.2 Contenido del ZIP
|
|
||||||
```
|
|
||||||
02-15-30_projects.zip
|
|
||||||
└── LineA/
|
|
||||||
└── Project1/
|
|
||||||
├── project.s7p
|
|
||||||
├── subfolder1/
|
|
||||||
└── subfolder2/
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Tecnologías y Librerías
|
|
||||||
|
|
||||||
#### 6.1 Python Core
|
|
||||||
- **Python**: 3.12
|
|
||||||
- **Framework Web**: Flask
|
|
||||||
- **Scheduler**: APScheduler
|
|
||||||
- **Compresión**: zipfile (biblioteca estándar)
|
|
||||||
|
|
||||||
#### 6.2 Librerías Específicas
|
|
||||||
- **Everything API**: PyEverything wrapper con DLL local
|
|
||||||
- **Hashing**: hashlib MD5 para (timestamp + tamaño)
|
|
||||||
- **Config**: json (biblioteca estándar)
|
|
||||||
- **Logging**: logging (biblioteca estándar)
|
|
||||||
- **File Access Check**: Intento de compresión + verificación .s7p
|
|
||||||
- **Process Priority**: pywin32 para baja prioridad
|
|
||||||
- **Disk Space**: psutil para monitoreo de espacio libre
|
|
||||||
|
|
||||||
#### 6.3 Frontend
|
|
||||||
- **HTML/CSS/JavaScript**: Vanilla (sin React por simplicidad)
|
|
||||||
- **AJAX**: Para comunicación con Flask API
|
|
||||||
- **Bootstrap**: Para estilizado responsivo
|
|
||||||
|
|
||||||
### 7. Consideraciones de Seguridad
|
|
||||||
|
|
||||||
#### 7.1 Acceso a Archivos
|
|
||||||
- Verificación de permisos antes de backup
|
|
||||||
- Manejo seguro de rutas para evitar path traversal
|
|
||||||
- Logs de todos los accesos a archivos
|
|
||||||
|
|
||||||
#### 7.2 Interfaz Web
|
|
||||||
- Solo acceso local (127.0.0.1)
|
|
||||||
- No autenticación requerida (uso local únicamente)
|
|
||||||
- Validación de inputs en formularios
|
|
||||||
|
|
||||||
### 8. Logging y Monitoreo
|
|
||||||
|
|
||||||
#### 8.1 Estructura de Logs
|
|
||||||
```
|
|
||||||
.logs/
|
|
||||||
├── autobackups_2025-09-01_08-30-15.log
|
|
||||||
├── autobackups_2025-09-02_08-30-22.log
|
|
||||||
└── ...
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 8.2 Niveles de Log
|
|
||||||
- **INFO**: Inicio/fin de backups, descubrimiento de proyectos
|
|
||||||
- **WARNING**: Archivos en uso, reintentos
|
|
||||||
- **ERROR**: Fallos de backup, errores de configuración
|
|
||||||
- **DEBUG**: Detalles de hashes, operaciones de archivos
|
|
||||||
|
|
||||||
### 9. Instalación y Despliegue
|
|
||||||
|
|
||||||
#### 9.1 Entorno Miniconda
|
|
||||||
```bash
|
|
||||||
conda create --name autobackups python=3.12
|
|
||||||
conda activate autobackups
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 9.2 Ejecución
|
|
||||||
- **Desarrollo**: `python src/app.py`
|
|
||||||
- **Producción**: `pythonw.exe src/app.py` (sin consola)
|
|
||||||
- **Como servicio**: Futuro enhancement con `python-windows-service`
|
|
||||||
|
|
||||||
### 10. Roadmap de Desarrollo
|
|
||||||
|
|
||||||
#### Fase 1: Core Backend
|
|
||||||
- [ ] Integración con Everything API
|
|
||||||
- [ ] Sistema de hash en dos etapas
|
|
||||||
- [ ] Motor de backup básico
|
|
||||||
- [ ] Scheduler con APScheduler
|
|
||||||
|
|
||||||
#### Fase 2: Interfaz Web
|
|
||||||
- [ ] Dashboard básico con Flask
|
|
||||||
- [ ] API REST para operaciones CRUD
|
|
||||||
- [ ] Frontend con HTML/JS/Bootstrap
|
|
||||||
|
|
||||||
#### Fase 3: Características Avanzadas
|
|
||||||
- [ ] Logs web viewer
|
|
||||||
- [ ] Configuración avanzada de directorios
|
|
||||||
- [ ] Estadísticas y reportes
|
|
||||||
|
|
||||||
#### Fase 4: Optimizaciones
|
|
||||||
- [ ] Ejecución como servicio Windows
|
|
||||||
- [ ] Notificaciones por email
|
|
||||||
- [ ] Backup incremental
|
|
||||||
|
|
||||||
Este documento será la base para el desarrollo del proyecto AutoBackups.
|
|
|
@ -1,124 +0,0 @@
|
||||||
# AutoBackups - Preguntas Técnicas Pendientes
|
|
||||||
|
|
||||||
## Preguntas Críticas para el Desarrollo
|
|
||||||
|
|
||||||
### 1. Everything API - Integración
|
|
||||||
**Pregunta**: ¿Everything está instalado y funcionando en el sistema objetivo?
|
|
||||||
**Impacto**: Si no está disponible, necesitaremos un método alternativo de búsqueda de archivos.
|
|
||||||
**Opciones**:
|
|
||||||
- Usar `python-everything` si Everything está disponible
|
|
||||||
- Implementar búsqueda recursiva con `os.walk()` como fallback
|
|
||||||
- Usar `pathlib` con `glob` patterns
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Podemos asumir que Everything estará disponible o necesitamos fallback?
|
|
||||||
|
|
||||||
### 2. Estructura de Backup - Clarificación de Rutas
|
|
||||||
**Pregunta**: Si tenemos un archivo en `C:\Projects\Siemens\LineA\Project1\project.s7p`, ¿el backup debe ser?
|
|
||||||
**Opción A**:
|
|
||||||
```
|
|
||||||
backup_destination/2025-09-01_14-30-15/Projects_Siemens_LineA_Project1.zip
|
|
||||||
└── Projects/Siemens/LineA/Project1/...
|
|
||||||
```
|
|
||||||
**Opción B**:
|
|
||||||
```
|
|
||||||
backup_destination/2025-09-01_14-30-15/LineA_Project1.zip
|
|
||||||
└── LineA/Project1/...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué estructura prefieres?
|
|
||||||
|
|
||||||
### 3. Configuración de Schedules - Granularidad
|
|
||||||
**Pregunta**: ¿Los schedules deben ser configurables a nivel de:
|
|
||||||
- Por directorio de observación (todos los proyectos de un directorio)
|
|
||||||
- Por proyecto individual
|
|
||||||
- Ambos (global con override por proyecto)
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué nivel de granularidad necesitas?
|
|
||||||
|
|
||||||
### 4. Hash de Archivos - Algoritmo y Almacenamiento
|
|
||||||
**Pregunta**: Para el sistema de hash en dos etapas:
|
|
||||||
- ¿Usamos MD5 (rápido) o SHA256 (más seguro) para los hashes?
|
|
||||||
- ¿El hash incluye solo timestamp o también tamaño de archivo?
|
|
||||||
- ¿Cómo manejamos archivos que se mueven dentro del proyecto?
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué balance entre velocidad y precisión prefieres?
|
|
||||||
|
|
||||||
### 5. Detección de Archivos en Uso - Método
|
|
||||||
**Pregunta**: Para detectar archivos bloqueados, ¿prefieres:
|
|
||||||
**Opción A**: Intentar abrir cada archivo en modo escritura exclusiva
|
|
||||||
**Opción B**: Usar `lsof` (Linux) / `handle.exe` (Windows) para listar archivos abiertos
|
|
||||||
**Opción C**: Solo verificar el archivo .s7p principal
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué método consideras más apropiado?
|
|
||||||
|
|
||||||
### 6. Manejo de Proyectos Grandes - Performance
|
|
||||||
**Pregunta**: ¿Hay límites que debamos considerar?
|
|
||||||
- Tamaño máximo de proyecto para backup
|
|
||||||
- Tiempo máximo de backup
|
|
||||||
- Número máximo de archivos por proyecto
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Necesitamos algún tipo de throttling o límites?
|
|
||||||
|
|
||||||
### 7. Configuración de Directorios - Recursividad
|
|
||||||
**Pregunta**: Cuando especificamos un directorio de observación como `C:\Projects`, ¿debemos:
|
|
||||||
- Buscar solo en subdirectorios inmediatos
|
|
||||||
- Buscar recursivamente en toda la jerarquía
|
|
||||||
- Permitir configurar la profundidad de búsqueda
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué comportamiento prefieres por defecto?
|
|
||||||
|
|
||||||
### 8. Backup Incremental vs Completo
|
|
||||||
**Pregunta**: ¿Todos los backups deben ser completos o consideras backup incremental?
|
|
||||||
- Backup completo: Todo el proyecto cada vez
|
|
||||||
- Backup incremental: Solo archivos modificados
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Preferencia por simplicidad o eficiencia de espacio?
|
|
||||||
|
|
||||||
### 9. Web Interface - Características Específicas
|
|
||||||
**Pregunta**: ¿Qué funcionalidades son prioritarias en la interfaz web?
|
|
||||||
**Must-have**:
|
|
||||||
- Lista de proyectos con estado
|
|
||||||
- Trigger manual de backup
|
|
||||||
- Ver logs básicos
|
|
||||||
|
|
||||||
**Nice-to-have**:
|
|
||||||
- Configurar schedules por proyecto
|
|
||||||
- Ver progreso de backup en tiempo real
|
|
||||||
- Estadísticas históricas
|
|
||||||
- Configurar nuevos directorios
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Cuáles son must-have vs nice-to-have?
|
|
||||||
|
|
||||||
### 10. Error Handling - Estrategias de Recuperación
|
|
||||||
**Pregunta**: ¿Cómo manejar errores específicos?
|
|
||||||
- Espacio insuficiente en destino de backup
|
|
||||||
- Pérdida de conexión con Everything
|
|
||||||
- Corrupción de archivos de configuración
|
|
||||||
- Falla durante compresión
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué nivel de robustez necesitas?
|
|
||||||
|
|
||||||
### 11. Startup Behavior - Inicialización
|
|
||||||
**Pregunta**: Al iniciar la aplicación:
|
|
||||||
- ¿Debe hacer un escaneo completo inmediatamente?
|
|
||||||
- ¿Debe esperar al primer schedule programado?
|
|
||||||
- ¿Debe permitir configurar el comportamiento de startup?
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué comportamiento prefieres al iniciar?
|
|
||||||
|
|
||||||
### 12. Multi-threading - Concurrencia
|
|
||||||
**Pregunta**: ¿Los backups deben ejecutarse:
|
|
||||||
- Secuencialmente (uno por vez)
|
|
||||||
- En paralelo (múltiples simultáneos)
|
|
||||||
- Con límite configurable de concurrencia
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué balance entre velocidad y recursos del sistema?
|
|
||||||
|
|
||||||
## Próximos Pasos
|
|
||||||
Una vez que tengas respuestas a estas preguntas, podremos:
|
|
||||||
1. Finalizar el diseño técnico
|
|
||||||
2. Crear el `requirements.txt` definitivo
|
|
||||||
3. Comenzar con la implementación por fases
|
|
||||||
4. Definir el plan de testing
|
|
||||||
|
|
||||||
¿Podrías revisar estas preguntas y darme tus preferencias para cada una?
|
|
|
@ -1,60 +0,0 @@
|
||||||
{
|
|
||||||
"observation_directories": [
|
|
||||||
{
|
|
||||||
"path": "C:\\Projects\\Siemens",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"enabled": true,
|
|
||||||
"description": "Directorio principal de proyectos Siemens"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "D:\\Engineering\\Projects",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"enabled": true,
|
|
||||||
"description": "Proyectos de ingeniería adicionales"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "C:\\Important\\Documentation",
|
|
||||||
"type": "manual",
|
|
||||||
"enabled": true,
|
|
||||||
"description": "Documentación importante para backup manual"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"backup_destination": "D:\\Backups\\AutoBackups",
|
|
||||||
"global_settings": {
|
|
||||||
"default_schedule": "daily",
|
|
||||||
"default_schedule_time": "02:00",
|
|
||||||
"retry_delay_hours": 1,
|
|
||||||
"backup_timeout_minutes": 0,
|
|
||||||
"min_free_space_mb": 100,
|
|
||||||
"scan_interval_minutes": 60,
|
|
||||||
"min_backup_interval_minutes": 10
|
|
||||||
},
|
|
||||||
"everything_api": {
|
|
||||||
"dll_path": "Everything-SDK\\dll\\Everything64.dll",
|
|
||||||
"enabled": true,
|
|
||||||
"search_depth": -1,
|
|
||||||
"skip_last_level_for_s7p": true
|
|
||||||
},
|
|
||||||
"web_interface": {
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"port": 5000,
|
|
||||||
"debug": false
|
|
||||||
},
|
|
||||||
"logging": {
|
|
||||||
"level": "INFO",
|
|
||||||
"max_log_files": 30,
|
|
||||||
"log_rotation_days": 30
|
|
||||||
},
|
|
||||||
"backup_options": {
|
|
||||||
"compression_level": 6,
|
|
||||||
"include_subdirectories": true,
|
|
||||||
"preserve_directory_structure": true,
|
|
||||||
"hash_algorithm": "md5",
|
|
||||||
"hash_includes": ["timestamp", "size"],
|
|
||||||
"backup_type": "complete",
|
|
||||||
"process_priority": "low",
|
|
||||||
"sequential_execution": true,
|
|
||||||
"filename_format": "HH-MM-SS_projects.zip",
|
|
||||||
"date_format": "YYYY-MM-DD"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
{
|
|
||||||
"metadata": {
|
|
||||||
"version": "1.0",
|
|
||||||
"last_updated": "2025-09-01T08:30:15Z",
|
|
||||||
"total_projects": 0
|
|
||||||
},
|
|
||||||
"projects": [
|
|
||||||
{
|
|
||||||
"id": "example_project_001",
|
|
||||||
"name": "PLC_MainLine_Example",
|
|
||||||
"path": "C:\\Projects\\Siemens\\LineA\\Project1",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"s7p_file": "C:\\Projects\\Siemens\\LineA\\Project1\\project.s7p",
|
|
||||||
"observation_directory": "C:\\Projects\\Siemens",
|
|
||||||
"relative_path": "LineA\\Project1",
|
|
||||||
"backup_path": "LineA\\Project1",
|
|
||||||
"schedule_config": {
|
|
||||||
"schedule": "daily",
|
|
||||||
"schedule_time": "02:00",
|
|
||||||
"enabled": true,
|
|
||||||
"next_scheduled_backup": "2025-09-02T02:00:00Z"
|
|
||||||
},
|
|
||||||
"backup_history": {
|
|
||||||
"last_backup_date": "2025-09-01T02:15:30Z",
|
|
||||||
"last_backup_file": "D:\\Backups\\AutoBackups\\LineA\\Project1\\2025-09-01\\02-15-30_projects.zip",
|
|
||||||
"backup_count": 5,
|
|
||||||
"last_successful_backup": "2025-09-01T02:15:30Z"
|
|
||||||
},
|
|
||||||
"hash_info": {
|
|
||||||
"last_s7p_hash": "abc123def456789",
|
|
||||||
"last_full_hash": "def789ghi012345",
|
|
||||||
"last_s7p_timestamp": "2025-08-31T14:30:00Z",
|
|
||||||
"last_s7p_size": 2048576,
|
|
||||||
"last_scan_timestamp": "2025-09-01T02:10:00Z",
|
|
||||||
"file_count": 1247,
|
|
||||||
"total_size_bytes": 125847296
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"current_status": "ready",
|
|
||||||
"last_error": null,
|
|
||||||
"retry_count": 0,
|
|
||||||
"next_retry": null,
|
|
||||||
"files_in_use": false,
|
|
||||||
"exclusivity_check_passed": true,
|
|
||||||
"last_status_update": "2025-09-01T02:15:35Z"
|
|
||||||
},
|
|
||||||
"discovery_info": {
|
|
||||||
"discovered_date": "2025-09-01T08:30:15Z",
|
|
||||||
"discovery_method": "everything_api",
|
|
||||||
"auto_discovered": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"statistics": {
|
|
||||||
"total_backups_created": 15,
|
|
||||||
"total_backup_size_mb": 2450.5,
|
|
||||||
"average_backup_time_seconds": 45.2,
|
|
||||||
"last_global_scan": "2025-09-01T08:30:15Z",
|
|
||||||
"projects_with_errors": 0,
|
|
||||||
"projects_pending_retry": 0
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
# Core Framework
|
|
||||||
Flask==2.3.3
|
|
||||||
Werkzeug==2.3.7
|
|
||||||
|
|
||||||
# Scheduling
|
|
||||||
APScheduler==3.10.4
|
|
||||||
|
|
||||||
# Everything API Integration
|
|
||||||
PyEverything==1.0.1 # Wrapper para Everything SDK
|
|
||||||
|
|
||||||
# File Operations and Utilities
|
|
||||||
pathlib2==2.3.7.post1 # Enhanced pathlib for Python 3.12
|
|
||||||
psutil==5.9.5 # System utilities (disk space monitoring)
|
|
||||||
filelock==3.12.4 # File locking for concurrent access
|
|
||||||
|
|
||||||
# Web Interface
|
|
||||||
Jinja2==3.1.2 # Template engine for Flask
|
|
||||||
|
|
||||||
# Configuration and Data
|
|
||||||
jsonschema==4.19.1 # JSON schema validation
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
colorlog==6.7.0 # Colored logging output
|
|
||||||
|
|
||||||
# Windows-specific dependencies
|
|
||||||
pywin32==306; sys_platform == "win32" # Windows API access for low priority
|
|
||||||
|
|
||||||
# Development and Testing (opcional, remove for production)
|
|
||||||
pytest==7.4.2
|
|
||||||
pytest-flask==1.2.0
|
|
||||||
black==23.9.1 # Code formatting
|
|
||||||
flake8==6.1.0 # Code linting
|
|
|
@ -0,0 +1,525 @@
|
||||||
|
# AutoBackups Project
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
AutoBackups is a Python application designed to automate the backup of Simatic S7 projects and other user-defined directories. The application leverages the Everything API to efficiently locate files and manage backups based on user-defined configurations.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Monitors specified directories for Simatic S7 files (*.s7p) and other user-defined directories.
|
||||||
|
- Automatically compresses and backs up projects on a configurable schedule (daily, hourly, 3-hour, 7-hour intervals, startup, or manual).
|
||||||
|
- Supports manual backup triggers.
|
||||||
|
- Skips projects with files in use and retries after one hour.
|
||||||
|
- Maintains a two-stage hash system: first checking *.s7p files, then all files to avoid unnecessary backups.
|
||||||
|
- User-friendly web interface built with Flask for configuration and status monitoring.
|
||||||
|
- Comprehensive logging system with timestamped log files.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
```
|
||||||
|
├── .logs/ # Application logs with startup timestamps
|
||||||
|
├── src
|
||||||
|
│ ├── app.py # Main entry point of the Flask application
|
||||||
|
│ ├── models
|
||||||
|
│ │ └── __init__.py # Data models for configuration and directories
|
||||||
|
│ ├── routes
|
||||||
|
│ │ └── __init__.py # API routes for backup operations
|
||||||
|
│ ├── services
|
||||||
|
│ │ └── __init__.py # Business logic for backups
|
||||||
|
│ └── utils
|
||||||
|
│ └── __init__.py # Utility functions for hashing and file access
|
||||||
|
├── static
|
||||||
|
│ ├── css # CSS files for styling
|
||||||
|
│ └── js # JavaScript files for client-side functionality
|
||||||
|
├── templates
|
||||||
|
│ └── index.html # Main HTML template for the web interface
|
||||||
|
├── config.json # Global configuration settings for the application
|
||||||
|
├── projects.json # Per-project configuration, schedules, and hash storage
|
||||||
|
├── requirements.txt # Python dependencies
|
||||||
|
└── README.md # Project documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
1. Clone the repository:
|
||||||
|
```
|
||||||
|
git clone <repository-url>
|
||||||
|
cd autobackups
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create a Miniconda environment:
|
||||||
|
```
|
||||||
|
conda create --name autobackups python=3.12
|
||||||
|
conda activate autobackups
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Install the required packages:
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Configure the application by editing `config.json` to specify directories to monitor and backup settings.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
- Start the Flask application:
|
||||||
|
```
|
||||||
|
python src/app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
- Access the web interface at `http://localhost:5120` to configure settings and monitor backup statuses.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
Contributions are welcome! Please submit a pull request or open an issue for any enhancements or bug fixes.
|
||||||
|
|
||||||
|
## License
|
||||||
|
This project is licensed under the MIT License. See the LICENSE file for details.
|
||||||
|
|
||||||
|
# AutoBackups - Especificación Técnica Detallada
|
||||||
|
|
||||||
|
## Resumen Ejecutivo
|
||||||
|
AutoBackups es una aplicación Python que automatiza el backup de proyectos Simatic S7 y directorios definidos por el usuario, utilizando la API de Everything para búsqueda eficiente de archivos y un sistema de hash inteligente para evitar backups innecesarios.
|
||||||
|
|
||||||
|
## Arquitectura del Sistema
|
||||||
|
|
||||||
|
### 1. Componentes Principales
|
||||||
|
|
||||||
|
#### 1.1 Motor de Búsqueda (Everything API)
|
||||||
|
- **Librería**: PyEverything wrapper
|
||||||
|
- **DLL Local**: `Everything-SDK\dll\Everything64.dll`
|
||||||
|
- **Función**: Localizar archivos *.s7p en directorios de observación
|
||||||
|
- **Optimización**: Evitar último nivel del árbol para archivos .s7p
|
||||||
|
- **Ventaja**: Aprovecha el índice existente de Everything para búsquedas instantáneas
|
||||||
|
|
||||||
|
#### 1.2 Sistema de Hash en Dos Etapas
|
||||||
|
**Etapa 1**: Hash rápido de archivos *.s7p
|
||||||
|
- Solo verifica MD5 de (timestamp + tamaño) de archivos .s7p
|
||||||
|
- Si no hay cambios → no se procede con backup
|
||||||
|
- Si hay cambios → procede a Etapa 2
|
||||||
|
|
||||||
|
**Etapa 2**: Hash completo del proyecto
|
||||||
|
- Calcula MD5 de (timestamp + tamaño) de todos los archivos del directorio
|
||||||
|
- Compara con último hash almacenado
|
||||||
|
- Solo hace backup si hay diferencias
|
||||||
|
|
||||||
|
#### 1.3 Scheduler de Background
|
||||||
|
- **daily**: Una vez al día a hora configurada
|
||||||
|
- **hourly**: Cada hora
|
||||||
|
- **3-hour**: Cada 3 horas
|
||||||
|
- **7-hour**: Cada 7 horas
|
||||||
|
- **startup**: Al iniciar la aplicación
|
||||||
|
- **manual**: Solo bajo demanda del usuario
|
||||||
|
- **Escaneos**: Automáticos cada 1 hora
|
||||||
|
- **Backups**: Mínimo cada 10 minutos
|
||||||
|
- **Prioridad**: Baja prioridad de sistema
|
||||||
|
|
||||||
|
### 2. Estructura de Datos
|
||||||
|
|
||||||
|
#### 2.1 config.json (Configuración Global)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"observation_directories": [
|
||||||
|
{
|
||||||
|
"path": "C:\\Projects\\Siemens",
|
||||||
|
"type": "siemens_s7",
|
||||||
|
"enabled": true,
|
||||||
|
"description": "Directorio principal de proyectos Siemens"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "D:\\Engineering\\Projects",
|
||||||
|
"type": "siemens_s7",
|
||||||
|
"enabled": true,
|
||||||
|
"description": "Proyectos de ingeniería adicionales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "C:\\Important\\Documentation",
|
||||||
|
"type": "manual",
|
||||||
|
"enabled": true,
|
||||||
|
"description": "Documentación importante para backup manual"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"backup_destination": "D:\\Backups\\AutoBackups",
|
||||||
|
"global_settings": {
|
||||||
|
"default_schedule": "daily",
|
||||||
|
"default_schedule_time": "02:00",
|
||||||
|
"retry_delay_hours": 1,
|
||||||
|
"backup_timeout_minutes": 0,
|
||||||
|
"min_free_space_mb": 100,
|
||||||
|
"scan_interval_minutes": 60,
|
||||||
|
"min_backup_interval_minutes": 10
|
||||||
|
},
|
||||||
|
"everything_api": {
|
||||||
|
"dll_path": "Everything-SDK\\dll\\Everything64.dll",
|
||||||
|
"enabled": true,
|
||||||
|
"search_depth": -1,
|
||||||
|
"skip_last_level_for_s7p": true
|
||||||
|
},
|
||||||
|
"web_interface": {
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 5000,
|
||||||
|
"debug": false
|
||||||
|
},
|
||||||
|
"logging": {
|
||||||
|
"level": "INFO",
|
||||||
|
"max_log_files": 30,
|
||||||
|
"log_rotation_days": 30
|
||||||
|
},
|
||||||
|
"backup_options": {
|
||||||
|
"compression_level": 6,
|
||||||
|
"include_subdirectories": true,
|
||||||
|
"preserve_directory_structure": true,
|
||||||
|
"hash_algorithm": "md5",
|
||||||
|
"hash_includes": ["timestamp", "size"],
|
||||||
|
"backup_type": "complete",
|
||||||
|
"process_priority": "low",
|
||||||
|
"sequential_execution": true,
|
||||||
|
"filename_format": "HH-MM-SS_projects.zip",
|
||||||
|
"date_format": "YYYY-MM-DD"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.2 projects.json (Estado de Proyectos)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"version": "1.0",
|
||||||
|
"last_updated": "2025-09-01T08:30:15Z",
|
||||||
|
"total_projects": 0
|
||||||
|
},
|
||||||
|
"projects": [
|
||||||
|
{
|
||||||
|
"id": "example_project_001",
|
||||||
|
"name": "PLC_MainLine_Example",
|
||||||
|
"path": "C:\\Projects\\Siemens\\LineA\\Project1",
|
||||||
|
"type": "siemens_s7",
|
||||||
|
"s7p_file": "C:\\Projects\\Siemens\\LineA\\Project1\\project.s7p",
|
||||||
|
"observation_directory": "C:\\Projects\\Siemens",
|
||||||
|
"relative_path": "LineA\\Project1",
|
||||||
|
"backup_path": "LineA\\Project1",
|
||||||
|
"schedule_config": {
|
||||||
|
"schedule": "daily",
|
||||||
|
"schedule_time": "02:00",
|
||||||
|
"enabled": true,
|
||||||
|
"next_scheduled_backup": "2025-09-02T02:00:00Z"
|
||||||
|
},
|
||||||
|
"backup_history": {
|
||||||
|
"last_backup_date": "2025-09-01T02:15:30Z",
|
||||||
|
"last_backup_file": "D:\\Backups\\AutoBackups\\LineA\\Project1\\2025-09-01\\02-15-30_projects.zip",
|
||||||
|
"backup_count": 5,
|
||||||
|
"last_successful_backup": "2025-09-01T02:15:30Z"
|
||||||
|
},
|
||||||
|
"hash_info": {
|
||||||
|
"last_s7p_hash": "abc123def456789",
|
||||||
|
"last_full_hash": "def789ghi012345",
|
||||||
|
"last_s7p_timestamp": "2025-08-31T14:30:00Z",
|
||||||
|
"last_s7p_size": 2048576,
|
||||||
|
"last_scan_timestamp": "2025-09-01T02:10:00Z",
|
||||||
|
"file_count": 1247,
|
||||||
|
"total_size_bytes": 125847296
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"current_status": "ready",
|
||||||
|
"last_error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"next_retry": null,
|
||||||
|
"files_in_use": false,
|
||||||
|
"exclusivity_check_passed": true,
|
||||||
|
"last_status_update": "2025-09-01T02:15:35Z"
|
||||||
|
},
|
||||||
|
"discovery_info": {
|
||||||
|
"discovered_date": "2025-09-01T08:30:15Z",
|
||||||
|
"discovery_method": "everything_api",
|
||||||
|
"auto_discovered": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"statistics": {
|
||||||
|
"total_backups_created": 15,
|
||||||
|
"total_backup_size_mb": 2450.5,
|
||||||
|
"average_backup_time_seconds": 45.2,
|
||||||
|
"last_global_scan": "2025-09-01T08:30:15Z",
|
||||||
|
"projects_with_errors": 0,
|
||||||
|
"projects_pending_retry": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Flujo de Operación
|
||||||
|
|
||||||
|
#### 3.1 Inicio de Aplicación
|
||||||
|
1. Cargar configuración desde `config.json`
|
||||||
|
2. Cargar estado de proyectos desde `projects.json`
|
||||||
|
3. Inicializar logger con timestamp de inicio
|
||||||
|
4. Escanear directorios de observación usando Everything API
|
||||||
|
5. Actualizar lista de proyectos detectados
|
||||||
|
6. Iniciar scheduler
|
||||||
|
7. Iniciar interfaz web Flask
|
||||||
|
|
||||||
|
#### 4.2 Proceso de Backup
|
||||||
|
1. **Verificación de Espacio**: Verificar mínimo 100MB libres
|
||||||
|
2. **Verificación de Acceso .s7p**: Intentar comprimir solo archivo .s7p
|
||||||
|
3. **Hash Etapa 1**: Verificar MD5 de (timestamp + tamaño) del archivo .s7p
|
||||||
|
4. **Hash Etapa 2**: Si Etapa 1 detecta cambios, verificar hash completo
|
||||||
|
5. **Compresión**: Crear archivo ZIP con estructura específica preservada
|
||||||
|
6. **Almacenamiento**: Guardar en `backup_destination/ProjectPath/YYYY-MM-DD/HH-MM-SS_projects.zip`
|
||||||
|
7. **Actualización**: Actualizar hashes y timestamps en `projects.json`
|
||||||
|
|
||||||
|
#### 3.3 Gestión de Errores
|
||||||
|
- **Archivos en uso**: Marcar proyecto para reintento en 1 hora
|
||||||
|
- **Errores de permisos**: Log de error y notificación en interfaz
|
||||||
|
- **Errores de espacio**: Verificar espacio disponible antes de backup
|
||||||
|
|
||||||
|
### 4. Interfaz Web
|
||||||
|
|
||||||
|
#### 4.1 Dashboard Principal
|
||||||
|
- **Lista de Proyectos**: Tabla con estado, último backup, próximo backup
|
||||||
|
- **Controles Globales**: Backup manual global, pausar/reanudar scheduler
|
||||||
|
- **Estadísticas**: Total de proyectos, backups exitosos, errores
|
||||||
|
|
||||||
|
#### 4.2 Configuración de Proyectos
|
||||||
|
- **Habilitación/Deshabilitación**: Toggle por proyecto
|
||||||
|
- **Configuración de Schedule**: Dropdown con opciones de frecuencia
|
||||||
|
- **Backup Manual**: Botón de backup inmediato por proyecto
|
||||||
|
|
||||||
|
#### 4.3 Logs y Monitoreo
|
||||||
|
- **Log Viewer**: Mostrar logs recientes con filtros por nivel
|
||||||
|
- **Estado del Sistema**: Información de Everything API, espacio en disco
|
||||||
|
|
||||||
|
### 5. Estructura de Backup
|
||||||
|
|
||||||
|
#### 5.1 Nomenclatura de Archivos (Estructura Específica)
|
||||||
|
```
|
||||||
|
backup_destination/
|
||||||
|
├── LineA/
|
||||||
|
│ └── Project1/
|
||||||
|
│ ├── 2025-09-01/
|
||||||
|
│ │ ├── 02-15-30_projects.zip
|
||||||
|
│ │ └── 14-30-15_projects.zip
|
||||||
|
│ └── 2025-09-02/
|
||||||
|
│ └── 02-15-45_projects.zip
|
||||||
|
└── LineB/
|
||||||
|
└── Project2/
|
||||||
|
└── 2025-09-01/
|
||||||
|
└── 08-20-10_projects.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.2 Contenido del ZIP
|
||||||
|
```
|
||||||
|
02-15-30_projects.zip
|
||||||
|
└── LineA/
|
||||||
|
└── Project1/
|
||||||
|
├── project.s7p
|
||||||
|
├── subfolder1/
|
||||||
|
└── subfolder2/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Tecnologías y Librerías
|
||||||
|
|
||||||
|
#### 6.1 Python Core
|
||||||
|
- **Python**: 3.12
|
||||||
|
- **Framework Web**: Flask
|
||||||
|
- **Scheduler**: APScheduler
|
||||||
|
- **Compresión**: zipfile (biblioteca estándar)
|
||||||
|
|
||||||
|
#### 6.2 Librerías Específicas
|
||||||
|
- **Everything API**: PyEverything wrapper con DLL local
|
||||||
|
- **Hashing**: hashlib MD5 para (timestamp + tamaño)
|
||||||
|
- **Config**: json (biblioteca estándar)
|
||||||
|
- **Logging**: logging (biblioteca estándar)
|
||||||
|
- **File Access Check**: Intento de compresión + verificación .s7p
|
||||||
|
- **Process Priority**: pywin32 para baja prioridad
|
||||||
|
- **Disk Space**: psutil para monitoreo de espacio libre
|
||||||
|
|
||||||
|
#### 6.3 Frontend
|
||||||
|
- **HTML/CSS/JavaScript**: Vanilla (sin React por simplicidad)
|
||||||
|
- **AJAX**: Para comunicación con Flask API
|
||||||
|
- **Bootstrap**: Para estilizado responsivo
|
||||||
|
|
||||||
|
### 7. Consideraciones de Seguridad
|
||||||
|
|
||||||
|
#### 7.1 Acceso a Archivos
|
||||||
|
- Verificación de permisos antes de backup
|
||||||
|
- Manejo seguro de rutas para evitar path traversal
|
||||||
|
- Logs de todos los accesos a archivos
|
||||||
|
|
||||||
|
#### 7.2 Interfaz Web
|
||||||
|
- Solo acceso local (127.0.0.1)
|
||||||
|
- No autenticación requerida (uso local únicamente)
|
||||||
|
- Validación de inputs en formularios
|
||||||
|
|
||||||
|
### 8. Logging y Monitoreo
|
||||||
|
|
||||||
|
#### 8.1 Estructura de Logs
|
||||||
|
```
|
||||||
|
.logs/
|
||||||
|
├── autobackups_2025-09-01_08-30-15.log
|
||||||
|
├── autobackups_2025-09-02_08-30-22.log
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 8.2 Niveles de Log
|
||||||
|
- **INFO**: Inicio/fin de backups, descubrimiento de proyectos
|
||||||
|
- **WARNING**: Archivos en uso, reintentos
|
||||||
|
- **ERROR**: Fallos de backup, errores de configuración
|
||||||
|
- **DEBUG**: Detalles de hashes, operaciones de archivos
|
||||||
|
|
||||||
|
### 9. Instalación y Despliegue
|
||||||
|
|
||||||
|
#### 9.1 Entorno Miniconda
|
||||||
|
```bash
|
||||||
|
conda create --name autobackups python=3.12
|
||||||
|
conda activate autobackups
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9.2 Ejecución
|
||||||
|
- **Desarrollo**: `python src/app.py`
|
||||||
|
- **Producción**: `pythonw.exe src/app.py` (sin consola)
|
||||||
|
- **Como servicio**: Futuro enhancement con `python-windows-service`
|
||||||
|
|
||||||
|
### 10. Roadmap de Desarrollo
|
||||||
|
|
||||||
|
#### Fase 1: Core Backend
|
||||||
|
- [ ] Integración con Everything API
|
||||||
|
- [ ] Sistema de hash en dos etapas
|
||||||
|
- [ ] Motor de backup básico
|
||||||
|
- [ ] Scheduler con APScheduler
|
||||||
|
|
||||||
|
#### Fase 2: Interfaz Web
|
||||||
|
- [ ] Dashboard básico con Flask
|
||||||
|
- [ ] API REST para operaciones CRUD
|
||||||
|
- [ ] Frontend con HTML/JS/Bootstrap
|
||||||
|
|
||||||
|
#### Fase 3: Características Avanzadas
|
||||||
|
- [ ] Logs web viewer
|
||||||
|
- [ ] Configuración avanzada de directorios
|
||||||
|
- [ ] Estadísticas y reportes
|
||||||
|
|
||||||
|
#### Fase 4: Optimizaciones
|
||||||
|
- [ ] Ejecución como servicio Windows
|
||||||
|
- [ ] Notificaciones por email
|
||||||
|
- [ ] Backup incremental
|
||||||
|
|
||||||
|
Este documento será la base para el desarrollo del proyecto AutoBackups.
|
||||||
|
|
||||||
|
|
||||||
|
# AutoBackups - Preguntas Técnicas Pendientes
|
||||||
|
|
||||||
|
## Preguntas Críticas para el Desarrollo
|
||||||
|
|
||||||
|
### 1. Everything API - Integración
|
||||||
|
**Pregunta**: ¿Everything está instalado y funcionando en el sistema objetivo?
|
||||||
|
**Impacto**: Si no está disponible, necesitaremos un método alternativo de búsqueda de archivos.
|
||||||
|
**Opciones**:
|
||||||
|
- Usar `python-everything` si Everything está disponible
|
||||||
|
- Implementar búsqueda recursiva con `os.walk()` como fallback
|
||||||
|
- Usar `pathlib` con `glob` patterns
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Podemos asumir que Everything estará disponible o necesitamos fallback?
|
||||||
|
|
||||||
|
### 2. Estructura de Backup - Clarificación de Rutas
|
||||||
|
**Pregunta**: Si tenemos un archivo en `C:\Projects\Siemens\LineA\Project1\project.s7p`, ¿el backup debe ser?
|
||||||
|
**Opción A**:
|
||||||
|
```
|
||||||
|
backup_destination/2025-09-01_14-30-15/Projects_Siemens_LineA_Project1.zip
|
||||||
|
└── Projects/Siemens/LineA/Project1/...
|
||||||
|
```
|
||||||
|
**Opción B**:
|
||||||
|
```
|
||||||
|
backup_destination/2025-09-01_14-30-15/LineA_Project1.zip
|
||||||
|
└── LineA/Project1/...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Qué estructura prefieres?
|
||||||
|
|
||||||
|
### 3. Configuración de Schedules - Granularidad
|
||||||
|
**Pregunta**: ¿Los schedules deben ser configurables a nivel de:
|
||||||
|
- Por directorio de observación (todos los proyectos de un directorio)
|
||||||
|
- Por proyecto individual
|
||||||
|
- Ambos (global con override por proyecto)
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Qué nivel de granularidad necesitas?
|
||||||
|
|
||||||
|
### 4. Hash de Archivos - Algoritmo y Almacenamiento
|
||||||
|
**Pregunta**: Para el sistema de hash en dos etapas:
|
||||||
|
- ¿Usamos MD5 (rápido) o SHA256 (más seguro) para los hashes?
|
||||||
|
- ¿El hash incluye solo timestamp o también tamaño de archivo?
|
||||||
|
- ¿Cómo manejamos archivos que se mueven dentro del proyecto?
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Qué balance entre velocidad y precisión prefieres?
|
||||||
|
|
||||||
|
### 5. Detección de Archivos en Uso - Método
|
||||||
|
**Pregunta**: Para detectar archivos bloqueados, ¿prefieres:
|
||||||
|
**Opción A**: Intentar abrir cada archivo en modo escritura exclusiva
|
||||||
|
**Opción B**: Usar `lsof` (Linux) / `handle.exe` (Windows) para listar archivos abiertos
|
||||||
|
**Opción C**: Solo verificar el archivo .s7p principal
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Qué método consideras más apropiado?
|
||||||
|
|
||||||
|
### 6. Manejo de Proyectos Grandes - Performance
|
||||||
|
**Pregunta**: ¿Hay límites que debamos considerar?
|
||||||
|
- Tamaño máximo de proyecto para backup
|
||||||
|
- Tiempo máximo de backup
|
||||||
|
- Número máximo de archivos por proyecto
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Necesitamos algún tipo de throttling o límites?
|
||||||
|
|
||||||
|
### 7. Configuración de Directorios - Recursividad
|
||||||
|
**Pregunta**: Cuando especificamos un directorio de observación como `C:\Projects`, ¿debemos:
|
||||||
|
- Buscar solo en subdirectorios inmediatos
|
||||||
|
- Buscar recursivamente en toda la jerarquía
|
||||||
|
- Permitir configurar la profundidad de búsqueda
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Qué comportamiento prefieres por defecto?
|
||||||
|
|
||||||
|
### 8. Backup Incremental vs Completo
|
||||||
|
**Pregunta**: ¿Todos los backups deben ser completos o consideras backup incremental?
|
||||||
|
- Backup completo: Todo el proyecto cada vez
|
||||||
|
- Backup incremental: Solo archivos modificados
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Preferencia por simplicidad o eficiencia de espacio?
|
||||||
|
|
||||||
|
### 9. Web Interface - Características Específicas
|
||||||
|
**Pregunta**: ¿Qué funcionalidades son prioritarias en la interfaz web?
|
||||||
|
**Must-have**:
|
||||||
|
- Lista de proyectos con estado
|
||||||
|
- Trigger manual de backup
|
||||||
|
- Ver logs básicos
|
||||||
|
|
||||||
|
**Nice-to-have**:
|
||||||
|
- Configurar schedules por proyecto
|
||||||
|
- Ver progreso de backup en tiempo real
|
||||||
|
- Estadísticas históricas
|
||||||
|
- Configurar nuevos directorios
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Cuáles son must-have vs nice-to-have?
|
||||||
|
|
||||||
|
### 10. Error Handling - Estrategias de Recuperación
|
||||||
|
**Pregunta**: ¿Cómo manejar errores específicos?
|
||||||
|
- Espacio insuficiente en destino de backup
|
||||||
|
- Pérdida de conexión con Everything
|
||||||
|
- Corrupción de archivos de configuración
|
||||||
|
- Falla durante compresión
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Qué nivel de robustez necesitas?
|
||||||
|
|
||||||
|
### 11. Startup Behavior - Inicialización
|
||||||
|
**Pregunta**: Al iniciar la aplicación:
|
||||||
|
- ¿Debe hacer un escaneo completo inmediatamente?
|
||||||
|
- ¿Debe esperar al primer schedule programado?
|
||||||
|
- ¿Debe permitir configurar el comportamiento de startup?
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Qué comportamiento prefieres al iniciar?
|
||||||
|
|
||||||
|
### 12. Multi-threading - Concurrencia
|
||||||
|
**Pregunta**: ¿Los backups deben ejecutarse:
|
||||||
|
- Secuencialmente (uno por vez)
|
||||||
|
- En paralelo (múltiples simultáneos)
|
||||||
|
- Con límite configurable de concurrencia
|
||||||
|
|
||||||
|
**Decisión necesaria**: ¿Qué balance entre velocidad y recursos del sistema?
|
||||||
|
|
||||||
|
## Próximos Pasos
|
||||||
|
Una vez que tengas respuestas a estas preguntas, podremos:
|
||||||
|
1. Finalizar el diseño técnico
|
||||||
|
2. Crear el `requirements.txt` definitivo
|
||||||
|
3. Comenzar con la implementación por fases
|
||||||
|
4. Definir el plan de testing
|
||||||
|
|
||||||
|
¿Podrías revisar estas preguntas y darme tus preferencias para cada una?
|
245
SPECIFICATION.md
245
SPECIFICATION.md
|
@ -1,245 +0,0 @@
|
||||||
# AutoBackups - Especificación Técnica Detallada
|
|
||||||
|
|
||||||
## Resumen Ejecutivo
|
|
||||||
AutoBackups es una aplicación Python que automatiza el backup de proyectos Simatic S7 y directorios definidos por el usuario, utilizando la API de Everything para búsqueda eficiente de archivos y un sistema de hash inteligente para evitar backups innecesarios.
|
|
||||||
|
|
||||||
## Arquitectura del Sistema
|
|
||||||
|
|
||||||
### 1. Componentes Principales
|
|
||||||
|
|
||||||
#### 1.1 Motor de Búsqueda (Everything API)
|
|
||||||
- **Librería**: `python-everything` o integración directa con Everything SDK
|
|
||||||
- **Función**: Localizar archivos *.s7p en directorios de observación
|
|
||||||
- **Ventaja**: Aprovecha el índice existente de Everything para búsquedas instantáneas
|
|
||||||
|
|
||||||
#### 1.2 Sistema de Hash en Dos Etapas
|
|
||||||
**Etapa 1**: Hash rápido de archivos *.s7p
|
|
||||||
- Solo verifica timestamps de archivos .s7p
|
|
||||||
- Si no hay cambios → no se procede con backup
|
|
||||||
- Si hay cambios → procede a Etapa 2
|
|
||||||
|
|
||||||
**Etapa 2**: Hash completo del proyecto
|
|
||||||
- Calcula hash de todos los archivos del directorio del proyecto
|
|
||||||
- Compara con último hash almacenado
|
|
||||||
- Solo hace backup si hay diferencias
|
|
||||||
|
|
||||||
#### 1.3 Scheduler Flexible
|
|
||||||
- **daily**: Una vez al día a hora configurada
|
|
||||||
- **hourly**: Cada hora
|
|
||||||
- **3-hour**: Cada 3 horas
|
|
||||||
- **7-hour**: Cada 7 horas
|
|
||||||
- **startup**: Al iniciar la aplicación
|
|
||||||
- **manual**: Solo bajo demanda del usuario
|
|
||||||
|
|
||||||
### 2. Estructura de Datos
|
|
||||||
|
|
||||||
#### 2.1 config.json (Configuración Global)
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"observation_directories": [
|
|
||||||
{
|
|
||||||
"path": "C:\\Projects\\Siemens",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "D:\\Engineering\\Projects",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "C:\\Important\\Docs",
|
|
||||||
"type": "manual",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"backup_destination": "D:\\Backups\\AutoBackups",
|
|
||||||
"default_schedule": "daily",
|
|
||||||
"default_schedule_time": "02:00",
|
|
||||||
"retry_delay_hours": 1,
|
|
||||||
"web_interface": {
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"port": 5000
|
|
||||||
},
|
|
||||||
"logging": {
|
|
||||||
"level": "INFO",
|
|
||||||
"max_log_files": 30
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2.2 projects.json (Estado de Proyectos)
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"projects": [
|
|
||||||
{
|
|
||||||
"id": "project_001",
|
|
||||||
"name": "PLC_MainLine",
|
|
||||||
"path": "C:\\Projects\\Siemens\\PLC_MainLine",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"s7p_file": "C:\\Projects\\Siemens\\PLC_MainLine\\PLC_MainLine.s7p",
|
|
||||||
"schedule": "daily",
|
|
||||||
"schedule_time": "02:00",
|
|
||||||
"enabled": true,
|
|
||||||
"last_backup": "2025-09-01T02:15:30",
|
|
||||||
"last_s7p_hash": "abc123def456",
|
|
||||||
"last_full_hash": "def789ghi012",
|
|
||||||
"last_s7p_timestamp": "2025-08-31T14:30:00",
|
|
||||||
"status": "ready",
|
|
||||||
"retry_count": 0,
|
|
||||||
"next_retry": null,
|
|
||||||
"error_message": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Flujo de Operación
|
|
||||||
|
|
||||||
#### 3.1 Inicio de Aplicación
|
|
||||||
1. Cargar configuración desde `config.json`
|
|
||||||
2. Cargar estado de proyectos desde `projects.json`
|
|
||||||
3. Inicializar logger con timestamp de inicio
|
|
||||||
4. Escanear directorios de observación usando Everything API
|
|
||||||
5. Actualizar lista de proyectos detectados
|
|
||||||
6. Iniciar scheduler
|
|
||||||
7. Iniciar interfaz web Flask
|
|
||||||
|
|
||||||
#### 3.2 Proceso de Backup
|
|
||||||
1. **Verificación de Acceso**: Intentar abrir archivos en modo lectura
|
|
||||||
2. **Hash Etapa 1**: Verificar timestamp del archivo .s7p
|
|
||||||
3. **Hash Etapa 2**: Si Etapa 1 detecta cambios, verificar hash completo
|
|
||||||
4. **Compresión**: Crear archivo ZIP con estructura de directorios preservada
|
|
||||||
5. **Almacenamiento**: Guardar en `backup_destination/YYYY-MM-DD_HH-MM-SS/`
|
|
||||||
6. **Actualización**: Actualizar hashes y timestamps en `projects.json`
|
|
||||||
|
|
||||||
#### 3.3 Gestión de Errores
|
|
||||||
- **Archivos en uso**: Marcar proyecto para reintento en 1 hora
|
|
||||||
- **Errores de permisos**: Log de error y notificación en interfaz
|
|
||||||
- **Errores de espacio**: Verificar espacio disponible antes de backup
|
|
||||||
|
|
||||||
### 4. Interfaz Web
|
|
||||||
|
|
||||||
#### 4.1 Dashboard Principal
|
|
||||||
- **Lista de Proyectos**: Tabla con estado, último backup, próximo backup
|
|
||||||
- **Controles Globales**: Backup manual global, pausar/reanudar scheduler
|
|
||||||
- **Estadísticas**: Total de proyectos, backups exitosos, errores
|
|
||||||
|
|
||||||
#### 4.2 Configuración de Proyectos
|
|
||||||
- **Habilitación/Deshabilitación**: Toggle por proyecto
|
|
||||||
- **Configuración de Schedule**: Dropdown con opciones de frecuencia
|
|
||||||
- **Backup Manual**: Botón de backup inmediato por proyecto
|
|
||||||
|
|
||||||
#### 4.3 Logs y Monitoreo
|
|
||||||
- **Log Viewer**: Mostrar logs recientes con filtros por nivel
|
|
||||||
- **Estado del Sistema**: Información de Everything API, espacio en disco
|
|
||||||
|
|
||||||
### 5. Estructura de Backup
|
|
||||||
|
|
||||||
#### 5.1 Nomenclatura de Archivos
|
|
||||||
```
|
|
||||||
backup_destination/
|
|
||||||
├── 2025-09-01_02-15-30/
|
|
||||||
│ ├── Projects_Siemens_PLC_MainLine.zip
|
|
||||||
│ └── Important_Docs_Documentation.zip
|
|
||||||
└── 2025-09-02_02-15-45/
|
|
||||||
└── Projects_Siemens_PLC_SecondaryLine.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5.2 Contenido del ZIP
|
|
||||||
```
|
|
||||||
PLC_MainLine.zip
|
|
||||||
└── Projects/
|
|
||||||
└── Siemens/
|
|
||||||
└── PLC_MainLine/
|
|
||||||
├── PLC_MainLine.s7p
|
|
||||||
├── subfolder1/
|
|
||||||
└── subfolder2/
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Tecnologías y Librerías
|
|
||||||
|
|
||||||
#### 6.1 Python Core
|
|
||||||
- **Python**: 3.12
|
|
||||||
- **Framework Web**: Flask
|
|
||||||
- **Scheduler**: APScheduler
|
|
||||||
- **Compresión**: zipfile (biblioteca estándar)
|
|
||||||
|
|
||||||
#### 6.2 Librerías Específicas
|
|
||||||
- **Everything API**: `python-everything` o implementación custom
|
|
||||||
- **Hashing**: hashlib (biblioteca estándar)
|
|
||||||
- **Config**: json (biblioteca estándar)
|
|
||||||
- **Logging**: logging (biblioteca estándar)
|
|
||||||
- **File Access Check**: Implementación custom con try/except
|
|
||||||
|
|
||||||
#### 6.3 Frontend
|
|
||||||
- **HTML/CSS/JavaScript**: Vanilla (sin React por simplicidad)
|
|
||||||
- **AJAX**: Para comunicación con Flask API
|
|
||||||
- **Bootstrap**: Para estilizado responsivo
|
|
||||||
|
|
||||||
### 7. Consideraciones de Seguridad
|
|
||||||
|
|
||||||
#### 7.1 Acceso a Archivos
|
|
||||||
- Verificación de permisos antes de backup
|
|
||||||
- Manejo seguro de rutas para evitar path traversal
|
|
||||||
- Logs de todos los accesos a archivos
|
|
||||||
|
|
||||||
#### 7.2 Interfaz Web
|
|
||||||
- Solo acceso local (127.0.0.1)
|
|
||||||
- No autenticación requerida (uso local únicamente)
|
|
||||||
- Validación de inputs en formularios
|
|
||||||
|
|
||||||
### 8. Logging y Monitoreo
|
|
||||||
|
|
||||||
#### 8.1 Estructura de Logs
|
|
||||||
```
|
|
||||||
.logs/
|
|
||||||
├── autobackups_2025-09-01_08-30-15.log
|
|
||||||
├── autobackups_2025-09-02_08-30-22.log
|
|
||||||
└── ...
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 8.2 Niveles de Log
|
|
||||||
- **INFO**: Inicio/fin de backups, descubrimiento de proyectos
|
|
||||||
- **WARNING**: Archivos en uso, reintentos
|
|
||||||
- **ERROR**: Fallos de backup, errores de configuración
|
|
||||||
- **DEBUG**: Detalles de hashes, operaciones de archivos
|
|
||||||
|
|
||||||
### 9. Instalación y Despliegue
|
|
||||||
|
|
||||||
#### 9.1 Entorno Miniconda
|
|
||||||
```bash
|
|
||||||
conda create --name autobackups python=3.12
|
|
||||||
conda activate autobackups
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 9.2 Ejecución
|
|
||||||
- **Desarrollo**: `python src/app.py`
|
|
||||||
- **Producción**: `pythonw.exe src/app.py` (sin consola)
|
|
||||||
- **Como servicio**: Futuro enhancement con `python-windows-service`
|
|
||||||
|
|
||||||
### 10. Roadmap de Desarrollo
|
|
||||||
|
|
||||||
#### Fase 1: Core Backend
|
|
||||||
- [ ] Integración con Everything API
|
|
||||||
- [ ] Sistema de hash en dos etapas
|
|
||||||
- [ ] Motor de backup básico
|
|
||||||
- [ ] Scheduler con APScheduler
|
|
||||||
|
|
||||||
#### Fase 2: Interfaz Web
|
|
||||||
- [ ] Dashboard básico con Flask
|
|
||||||
- [ ] API REST para operaciones CRUD
|
|
||||||
- [ ] Frontend con HTML/JS/Bootstrap
|
|
||||||
|
|
||||||
#### Fase 3: Características Avanzadas
|
|
||||||
- [ ] Logs web viewer
|
|
||||||
- [ ] Configuración avanzada de directorios
|
|
||||||
- [ ] Estadísticas y reportes
|
|
||||||
|
|
||||||
#### Fase 4: Optimizaciones
|
|
||||||
- [ ] Ejecución como servicio Windows
|
|
||||||
- [ ] Notificaciones por email
|
|
||||||
- [ ] Backup incremental
|
|
||||||
|
|
||||||
Este documento será la base para el desarrollo del proyecto AutoBackups.
|
|
|
@ -1,111 +0,0 @@
|
||||||
# AutoBackups - Decisiones Técnicas Finalizadas
|
|
||||||
|
|
||||||
## Decisiones Técnicas Confirmadas
|
|
||||||
|
|
||||||
### 1. Everything API - Integración ✅
|
|
||||||
**Decisión**: Everything SDK está instalado bajo `Everything-SDK\dll\Everything64.dll`
|
|
||||||
**Implementación**: Usar PyEverything (Wrapper) sin fallback
|
|
||||||
**Ruta DLL**: `Everything-SDK\dll\Everything64.dll`
|
|
||||||
|
|
||||||
### 2. Estructura de Backup - Clarificación de Rutas ✅
|
|
||||||
**Decisión**: Estructura específica con formato de fecha personalizado
|
|
||||||
**Ejemplo**:
|
|
||||||
- Directorio base: `C:\Projects\Siemens`
|
|
||||||
- Proyecto: `C:\Projects\Siemens\LineA\Project1\project.s7p`
|
|
||||||
- Backup: `backup_destination/LineA/Project1/2025-09-01/14-30-15_projects.zip`
|
|
||||||
**Formato**: YYYY-MM-DD para fechas, HH-MM-SS formato 24 horas
|
|
||||||
|
|
||||||
### 3. Configuración de Schedules - Granularidad ✅
|
|
||||||
**Decisión**: Ambos niveles (global con override por proyecto)
|
|
||||||
**Implementación**:
|
|
||||||
- Configuración global por defecto
|
|
||||||
- Override individual por proyecto
|
|
||||||
- Interfaz web para modificar ambos niveles
|
|
||||||
|
|
||||||
### 4. Hash de Archivos - Algoritmo y Almacenamiento ✅
|
|
||||||
**Decisión**: Hash de timestamps + tamaño (NO contenido)
|
|
||||||
**Algoritmo**: MD5 para velocidad
|
|
||||||
**Incluye**: timestamp + tamaño de archivo
|
|
||||||
**Prioridad**: Velocidad sobre seguridad criptográfica
|
|
||||||
|
|
||||||
### 5. Detección de Archivos en Uso - Método ✅
|
|
||||||
**Decisión**: Detección por intento de compresión
|
|
||||||
**Método Primario**: Intentar comprimir y detectar errores de exclusividad
|
|
||||||
**Método Rápido**: Verificar archivo .s7p antes de comprimir proyecto completo
|
|
||||||
**Manejo de Errores**: Marcar como problema de exclusividad si falla
|
|
||||||
|
|
||||||
### 6. Manejo de Proyectos Grandes - Performance ✅
|
|
||||||
**Decisión**: Sin límites de tamaño, tiempo o archivos
|
|
||||||
**Implementación**: Sin throttling, procesar cualquier tamaño de proyecto
|
|
||||||
**Prioridad**: Baja prioridad de sistema para no interferir
|
|
||||||
|
|
||||||
### 7. Configuración de Directorios - Recursividad ✅
|
|
||||||
**Decisión**: Búsqueda recursiva con optimización para .s7p
|
|
||||||
**Implementación**:
|
|
||||||
- Búsqueda recursiva en toda la jerarquía
|
|
||||||
- Optimización: evitar último nivel para archivos .s7p
|
|
||||||
- Razón: archivos .s7p no pueden estar en el último nivel del árbol
|
|
||||||
|
|
||||||
### 8. Backup Incremental vs Completo ✅
|
|
||||||
**Decisión**: Siempre backup completo
|
|
||||||
**Implementación**: Todo el proyecto cada vez
|
|
||||||
**Razón**: Simplicidad y asegurar integridad completa
|
|
||||||
|
|
||||||
### 9. Web Interface - Características Específicas ✅
|
|
||||||
**Must-have**:
|
|
||||||
- Lista de proyectos con estado ✅
|
|
||||||
- Trigger manual de backup ✅
|
|
||||||
- Ver logs básicos ✅
|
|
||||||
- Configurar schedules por proyecto ✅
|
|
||||||
- **Modificar configuración general (config.json) ✅**
|
|
||||||
|
|
||||||
**Nice-to-have**:
|
|
||||||
- Ver progreso de backup en tiempo real
|
|
||||||
- Estadísticas históricas
|
|
||||||
- Configurar nuevos directorios
|
|
||||||
|
|
||||||
### 10. Error Handling - Estrategias de Recuperación ✅
|
|
||||||
**Espacio Insuficiente**:
|
|
||||||
- Área de status en página web mostrando espacio libre
|
|
||||||
- Límite mínimo: 100MB libres para ejecutar backups
|
|
||||||
- Sin backups si espacio < 100MB
|
|
||||||
|
|
||||||
**Otros Errores**:
|
|
||||||
- Marcar para reintento en 1 hora
|
|
||||||
- Log de errores para diagnóstico
|
|
||||||
|
|
||||||
### 11. Startup Behavior - Inicialización ✅
|
|
||||||
**Decisión**: Aplicación de background con timers específicos
|
|
||||||
**Comportamiento**:
|
|
||||||
- Escaneos programados: cada 1 hora
|
|
||||||
- Backups mínimo: cada 10 minutos
|
|
||||||
- Aplicación siempre en background
|
|
||||||
- Baja prioridad de sistema
|
|
||||||
|
|
||||||
### 12. Multi-threading - Concurrencia ✅
|
|
||||||
**Decisión**: Ejecución secuencial (uno por vez)
|
|
||||||
**Implementación**:
|
|
||||||
- Backups secuenciales para economizar recursos
|
|
||||||
- Baja prioridad de sistema
|
|
||||||
- Orientado a operación en background sin interferir
|
|
||||||
|
|
||||||
## Especificación Técnica Finalizada ✅
|
|
||||||
|
|
||||||
### Resumen de Decisiones Clave:
|
|
||||||
1. **Everything SDK**: PyEverything wrapper con DLL local
|
|
||||||
2. **Estructura de Backup**: `backup_destination/ProjectPath/YYYY-MM-DD/HH-MM-SS_projects.zip`
|
|
||||||
3. **Hash**: MD5 de (timestamp + tamaño) para velocidad
|
|
||||||
4. **Detección de Uso**: Por intento de compresión + verificación .s7p
|
|
||||||
5. **Performance**: Sin límites, baja prioridad, secuencial
|
|
||||||
6. **Búsqueda**: Recursiva evitando último nivel para .s7p
|
|
||||||
7. **Backups**: Siempre completos
|
|
||||||
8. **Interfaz**: Flask con configuración de config.json
|
|
||||||
9. **Espacio**: Mínimo 100MB libres, status visible
|
|
||||||
10. **Background**: Escaneos 1h, backups min 10min
|
|
||||||
11. **Concurrencia**: Secuencial, baja prioridad
|
|
||||||
|
|
||||||
### Próximos Pasos:
|
|
||||||
1. ✅ Finalizar diseño técnico basado en decisiones
|
|
||||||
2. ⏳ Actualizar `requirements.txt` con PyEverything
|
|
||||||
3. ⏳ Comenzar implementación por fases
|
|
||||||
4. ⏳ Crear estructura básica del proyecto
|
|
|
@ -1,216 +0,0 @@
|
||||||
# Byte-compiled / optimized / DLL files
|
|
||||||
__pycache__/
|
|
||||||
*.py[codz]
|
|
||||||
*$py.class
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
|
||||||
.nox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.py.cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
cover/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
|
||||||
instance/
|
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
|
||||||
.scrapy
|
|
||||||
|
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
.pybuilder/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
# For a library or package, you might want to ignore these files since the code is
|
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
|
||||||
# .python-version
|
|
||||||
|
|
||||||
# pipenv
|
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# UV
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
||||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
||||||
# commonly ignored for libraries.
|
|
||||||
#uv.lock
|
|
||||||
|
|
||||||
# poetry
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
||||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
||||||
# commonly ignored for libraries.
|
|
||||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
||||||
#poetry.lock
|
|
||||||
#poetry.toml
|
|
||||||
|
|
||||||
# pdm
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
||||||
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
||||||
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
||||||
#pdm.lock
|
|
||||||
#pdm.toml
|
|
||||||
.pdm-python
|
|
||||||
.pdm-build/
|
|
||||||
|
|
||||||
# pixi
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
||||||
#pixi.lock
|
|
||||||
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
||||||
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
||||||
.pixi
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# Redis
|
|
||||||
*.rdb
|
|
||||||
*.aof
|
|
||||||
*.pid
|
|
||||||
|
|
||||||
# RabbitMQ
|
|
||||||
mnesia/
|
|
||||||
rabbitmq/
|
|
||||||
rabbitmq-data/
|
|
||||||
|
|
||||||
# ActiveMQ
|
|
||||||
activemq-data/
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.envrc
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|
||||||
# pytype static type analyzer
|
|
||||||
.pytype/
|
|
||||||
|
|
||||||
# Cython debug symbols
|
|
||||||
cython_debug/
|
|
||||||
|
|
||||||
# PyCharm
|
|
||||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
||||||
#.idea/
|
|
||||||
|
|
||||||
# Abstra
|
|
||||||
# Abstra is an AI-powered process automation framework.
|
|
||||||
# Ignore directories containing user credentials, local state, and settings.
|
|
||||||
# Learn more at https://abstra.io/docs
|
|
||||||
.abstra/
|
|
||||||
|
|
||||||
# Visual Studio Code
|
|
||||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
||||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
||||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
||||||
# you could uncomment the following to ignore the entire vscode folder
|
|
||||||
# .vscode/
|
|
||||||
|
|
||||||
# Ruff stuff:
|
|
||||||
.ruff_cache/
|
|
||||||
|
|
||||||
# PyPI configuration file
|
|
||||||
.pypirc
|
|
||||||
|
|
||||||
# Marimo
|
|
||||||
marimo/_static/
|
|
||||||
marimo/_lsp/
|
|
||||||
__marimo__/
|
|
||||||
|
|
||||||
# Streamlit
|
|
||||||
.streamlit/secrets.toml
|
|
|
@ -1,72 +0,0 @@
|
||||||
# AutoBackups Project
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
AutoBackups is a Python application designed to automate the backup of Simatic S7 projects and other user-defined directories. The application leverages the Everything API to efficiently locate files and manage backups based on user-defined configurations.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
- Monitors specified directories for Simatic S7 files (*.s7p) and other user-defined directories.
|
|
||||||
- Automatically compresses and backs up projects on a configurable schedule (daily, hourly, 3-hour, 7-hour intervals, startup, or manual).
|
|
||||||
- Supports manual backup triggers.
|
|
||||||
- Skips projects with files in use and retries after one hour.
|
|
||||||
- Maintains a two-stage hash system: first checking *.s7p files, then all files to avoid unnecessary backups.
|
|
||||||
- User-friendly web interface built with Flask for configuration and status monitoring.
|
|
||||||
- Comprehensive logging system with timestamped log files.
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
```
|
|
||||||
autobackups
|
|
||||||
├── .logs/ # Application logs with startup timestamps
|
|
||||||
├── src
|
|
||||||
│ ├── app.py # Main entry point of the Flask application
|
|
||||||
│ ├── models
|
|
||||||
│ │ └── __init__.py # Data models for configuration and directories
|
|
||||||
│ ├── routes
|
|
||||||
│ │ └── __init__.py # API routes for backup operations
|
|
||||||
│ ├── services
|
|
||||||
│ │ └── __init__.py # Business logic for backups
|
|
||||||
│ └── utils
|
|
||||||
│ └── __init__.py # Utility functions for hashing and file access
|
|
||||||
├── static
|
|
||||||
│ ├── css # CSS files for styling
|
|
||||||
│ └── js # JavaScript files for client-side functionality
|
|
||||||
├── templates
|
|
||||||
│ └── index.html # Main HTML template for the web interface
|
|
||||||
├── config.json # Global configuration settings for the application
|
|
||||||
├── projects.json # Per-project configuration, schedules, and hash storage
|
|
||||||
├── requirements.txt # Python dependencies
|
|
||||||
└── README.md # Project documentation
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
1. Clone the repository:
|
|
||||||
```
|
|
||||||
git clone <repository-url>
|
|
||||||
cd autobackups
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Create a Miniconda environment:
|
|
||||||
```
|
|
||||||
conda create --name autobackups python=3.12
|
|
||||||
conda activate autobackups
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Install the required packages:
|
|
||||||
```
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Configure the application by editing `config.json` to specify directories to monitor and backup settings.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
- Start the Flask application:
|
|
||||||
```
|
|
||||||
python src/app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
- Access the web interface at `http://localhost:5000` to configure settings and monitor backup statuses.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
Contributions are welcome! Please submit a pull request or open an issue for any enhancements or bug fixes.
|
|
||||||
|
|
||||||
## License
|
|
||||||
This project is licensed under the MIT License. See the LICENSE file for details.
|
|
|
@ -1,258 +0,0 @@
|
||||||
# AutoBackups - Especificación Técnica Detallada
|
|
||||||
|
|
||||||
## Resumen Ejecutivo
|
|
||||||
AutoBackups es una aplicación Python que automatiza el backup de proyectos Simatic S7 y directorios definidos por el usuario, utilizando la API de Everything para búsqueda eficiente de archivos y un sistema de hash inteligente para evitar backups innecesarios.
|
|
||||||
|
|
||||||
## Arquitectura del Sistema
|
|
||||||
|
|
||||||
### 1. Componentes Principales
|
|
||||||
|
|
||||||
#### 1.1 Motor de Búsqueda (Everything API)
|
|
||||||
- **Librería**: PyEverything wrapper
|
|
||||||
- **DLL Local**: `Everything-SDK\dll\Everything64.dll`
|
|
||||||
- **Función**: Localizar archivos *.s7p en directorios de observación
|
|
||||||
- **Optimización**: Evitar último nivel del árbol para archivos .s7p
|
|
||||||
- **Ventaja**: Aprovecha el índice existente de Everything para búsquedas instantáneas
|
|
||||||
|
|
||||||
#### 1.2 Sistema de Hash en Dos Etapas
|
|
||||||
**Etapa 1**: Hash rápido de archivos *.s7p
|
|
||||||
- Solo verifica MD5 de (timestamp + tamaño) de archivos .s7p
|
|
||||||
- Si no hay cambios → no se procede con backup
|
|
||||||
- Si hay cambios → procede a Etapa 2
|
|
||||||
|
|
||||||
**Etapa 2**: Hash completo del proyecto
|
|
||||||
- Calcula MD5 de (timestamp + tamaño) de todos los archivos del directorio
|
|
||||||
- Compara con último hash almacenado
|
|
||||||
- Solo hace backup si hay diferencias
|
|
||||||
|
|
||||||
#### 1.3 Scheduler de Background
|
|
||||||
- **daily**: Una vez al día a hora configurada
|
|
||||||
- **hourly**: Cada hora
|
|
||||||
- **3-hour**: Cada 3 horas
|
|
||||||
- **7-hour**: Cada 7 horas
|
|
||||||
- **startup**: Al iniciar la aplicación
|
|
||||||
- **manual**: Solo bajo demanda del usuario
|
|
||||||
- **Escaneos**: Automáticos cada 1 hora
|
|
||||||
- **Backups**: Mínimo cada 10 minutos
|
|
||||||
- **Prioridad**: Baja prioridad de sistema
|
|
||||||
|
|
||||||
### 2. Estructura de Datos
|
|
||||||
|
|
||||||
#### 2.1 config.json (Configuración Global)
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"observation_directories": [
|
|
||||||
{
|
|
||||||
"path": "C:\\Projects\\Siemens",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "D:\\Engineering\\Projects",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "C:\\Important\\Docs",
|
|
||||||
"type": "manual",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"backup_destination": "D:\\Backups\\AutoBackups",
|
|
||||||
"default_schedule": "daily",
|
|
||||||
"default_schedule_time": "02:00",
|
|
||||||
"retry_delay_hours": 1,
|
|
||||||
"web_interface": {
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"port": 5000
|
|
||||||
},
|
|
||||||
"logging": {
|
|
||||||
"level": "INFO",
|
|
||||||
"max_log_files": 30
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2.2 projects.json (Estado de Proyectos)
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"projects": [
|
|
||||||
{
|
|
||||||
"id": "project_001",
|
|
||||||
"name": "PLC_MainLine",
|
|
||||||
"path": "C:\\Projects\\Siemens\\PLC_MainLine",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"s7p_file": "C:\\Projects\\Siemens\\PLC_MainLine\\PLC_MainLine.s7p",
|
|
||||||
"schedule": "daily",
|
|
||||||
"schedule_time": "02:00",
|
|
||||||
"enabled": true,
|
|
||||||
"last_backup": "2025-09-01T02:15:30",
|
|
||||||
"last_s7p_hash": "abc123def456",
|
|
||||||
"last_full_hash": "def789ghi012",
|
|
||||||
"last_s7p_timestamp": "2025-08-31T14:30:00",
|
|
||||||
"status": "ready",
|
|
||||||
"retry_count": 0,
|
|
||||||
"next_retry": null,
|
|
||||||
"error_message": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Flujo de Operación
|
|
||||||
|
|
||||||
#### 3.1 Inicio de Aplicación
|
|
||||||
1. Cargar configuración desde `config.json`
|
|
||||||
2. Cargar estado de proyectos desde `projects.json`
|
|
||||||
3. Inicializar logger con timestamp de inicio
|
|
||||||
4. Escanear directorios de observación usando Everything API
|
|
||||||
5. Actualizar lista de proyectos detectados
|
|
||||||
6. Iniciar scheduler
|
|
||||||
7. Iniciar interfaz web Flask
|
|
||||||
|
|
||||||
#### 4.2 Proceso de Backup
|
|
||||||
1. **Verificación de Espacio**: Verificar mínimo 100MB libres
|
|
||||||
2. **Verificación de Acceso .s7p**: Intentar comprimir solo archivo .s7p
|
|
||||||
3. **Hash Etapa 1**: Verificar MD5 de (timestamp + tamaño) del archivo .s7p
|
|
||||||
4. **Hash Etapa 2**: Si Etapa 1 detecta cambios, verificar hash completo
|
|
||||||
5. **Compresión**: Crear archivo ZIP con estructura específica preservada
|
|
||||||
6. **Almacenamiento**: Guardar en `backup_destination/ProjectPath/YYYY-MM-DD/HH-MM-SS_projects.zip`
|
|
||||||
7. **Actualización**: Actualizar hashes y timestamps en `projects.json`
|
|
||||||
|
|
||||||
#### 3.3 Gestión de Errores
|
|
||||||
- **Archivos en uso**: Marcar proyecto para reintento en 1 hora
|
|
||||||
- **Errores de permisos**: Log de error y notificación en interfaz
|
|
||||||
- **Errores de espacio**: Verificar espacio disponible antes de backup
|
|
||||||
|
|
||||||
### 4. Interfaz Web
|
|
||||||
|
|
||||||
#### 4.1 Dashboard Principal
|
|
||||||
- **Lista de Proyectos**: Tabla con estado, último backup, próximo backup
|
|
||||||
- **Controles Globales**: Backup manual global, pausar/reanudar scheduler
|
|
||||||
- **Estadísticas**: Total de proyectos, backups exitosos, errores
|
|
||||||
|
|
||||||
#### 4.2 Configuración de Proyectos
|
|
||||||
- **Habilitación/Deshabilitación**: Toggle por proyecto
|
|
||||||
- **Configuración de Schedule**: Dropdown con opciones de frecuencia
|
|
||||||
- **Backup Manual**: Botón de backup inmediato por proyecto
|
|
||||||
|
|
||||||
#### 4.3 Logs y Monitoreo
|
|
||||||
- **Log Viewer**: Mostrar logs recientes con filtros por nivel
|
|
||||||
- **Estado del Sistema**: Información de Everything API, espacio en disco
|
|
||||||
|
|
||||||
### 5. Estructura de Backup
|
|
||||||
|
|
||||||
#### 5.1 Nomenclatura de Archivos (Estructura Específica)
|
|
||||||
```
|
|
||||||
backup_destination/
|
|
||||||
├── LineA/
|
|
||||||
│ └── Project1/
|
|
||||||
│ ├── 2025-09-01/
|
|
||||||
│ │ ├── 02-15-30_projects.zip
|
|
||||||
│ │ └── 14-30-15_projects.zip
|
|
||||||
│ └── 2025-09-02/
|
|
||||||
│ └── 02-15-45_projects.zip
|
|
||||||
└── LineB/
|
|
||||||
└── Project2/
|
|
||||||
└── 2025-09-01/
|
|
||||||
└── 08-20-10_projects.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5.2 Contenido del ZIP
|
|
||||||
```
|
|
||||||
02-15-30_projects.zip
|
|
||||||
└── LineA/
|
|
||||||
└── Project1/
|
|
||||||
├── project.s7p
|
|
||||||
├── subfolder1/
|
|
||||||
└── subfolder2/
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Tecnologías y Librerías
|
|
||||||
|
|
||||||
#### 6.1 Python Core
|
|
||||||
- **Python**: 3.12
|
|
||||||
- **Framework Web**: Flask
|
|
||||||
- **Scheduler**: APScheduler
|
|
||||||
- **Compresión**: zipfile (biblioteca estándar)
|
|
||||||
|
|
||||||
#### 6.2 Librerías Específicas
|
|
||||||
- **Everything API**: PyEverything wrapper con DLL local
|
|
||||||
- **Hashing**: hashlib MD5 para (timestamp + tamaño)
|
|
||||||
- **Config**: json (biblioteca estándar)
|
|
||||||
- **Logging**: logging (biblioteca estándar)
|
|
||||||
- **File Access Check**: Intento de compresión + verificación .s7p
|
|
||||||
- **Process Priority**: pywin32 para baja prioridad
|
|
||||||
- **Disk Space**: psutil para monitoreo de espacio libre
|
|
||||||
|
|
||||||
#### 6.3 Frontend
|
|
||||||
- **HTML/CSS/JavaScript**: Vanilla (sin React por simplicidad)
|
|
||||||
- **AJAX**: Para comunicación con Flask API
|
|
||||||
- **Bootstrap**: Para estilizado responsivo
|
|
||||||
|
|
||||||
### 7. Consideraciones de Seguridad
|
|
||||||
|
|
||||||
#### 7.1 Acceso a Archivos
|
|
||||||
- Verificación de permisos antes de backup
|
|
||||||
- Manejo seguro de rutas para evitar path traversal
|
|
||||||
- Logs de todos los accesos a archivos
|
|
||||||
|
|
||||||
#### 7.2 Interfaz Web
|
|
||||||
- Solo acceso local (127.0.0.1)
|
|
||||||
- No autenticación requerida (uso local únicamente)
|
|
||||||
- Validación de inputs en formularios
|
|
||||||
|
|
||||||
### 8. Logging y Monitoreo
|
|
||||||
|
|
||||||
#### 8.1 Estructura de Logs
|
|
||||||
```
|
|
||||||
.logs/
|
|
||||||
├── autobackups_2025-09-01_08-30-15.log
|
|
||||||
├── autobackups_2025-09-02_08-30-22.log
|
|
||||||
└── ...
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 8.2 Niveles de Log
|
|
||||||
- **INFO**: Inicio/fin de backups, descubrimiento de proyectos
|
|
||||||
- **WARNING**: Archivos en uso, reintentos
|
|
||||||
- **ERROR**: Fallos de backup, errores de configuración
|
|
||||||
- **DEBUG**: Detalles de hashes, operaciones de archivos
|
|
||||||
|
|
||||||
### 9. Instalación y Despliegue
|
|
||||||
|
|
||||||
#### 9.1 Entorno Miniconda
|
|
||||||
```bash
|
|
||||||
conda create --name autobackups python=3.12
|
|
||||||
conda activate autobackups
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 9.2 Ejecución
|
|
||||||
- **Desarrollo**: `python src/app.py`
|
|
||||||
- **Producción**: `pythonw.exe src/app.py` (sin consola)
|
|
||||||
- **Como servicio**: Futuro enhancement con `python-windows-service`
|
|
||||||
|
|
||||||
### 10. Roadmap de Desarrollo
|
|
||||||
|
|
||||||
#### Fase 1: Core Backend
|
|
||||||
- [ ] Integración con Everything API
|
|
||||||
- [ ] Sistema de hash en dos etapas
|
|
||||||
- [ ] Motor de backup básico
|
|
||||||
- [ ] Scheduler con APScheduler
|
|
||||||
|
|
||||||
#### Fase 2: Interfaz Web
|
|
||||||
- [ ] Dashboard básico con Flask
|
|
||||||
- [ ] API REST para operaciones CRUD
|
|
||||||
- [ ] Frontend con HTML/JS/Bootstrap
|
|
||||||
|
|
||||||
#### Fase 3: Características Avanzadas
|
|
||||||
- [ ] Logs web viewer
|
|
||||||
- [ ] Configuración avanzada de directorios
|
|
||||||
- [ ] Estadísticas y reportes
|
|
||||||
|
|
||||||
#### Fase 4: Optimizaciones
|
|
||||||
- [ ] Ejecución como servicio Windows
|
|
||||||
- [ ] Notificaciones por email
|
|
||||||
- [ ] Backup incremental
|
|
||||||
|
|
||||||
Este documento será la base para el desarrollo del proyecto AutoBackups.
|
|
|
@ -1,124 +0,0 @@
|
||||||
# AutoBackups - Preguntas Técnicas Pendientes
|
|
||||||
|
|
||||||
## Preguntas Críticas para el Desarrollo
|
|
||||||
|
|
||||||
### 1. Everything API - Integración
|
|
||||||
**Pregunta**: ¿Everything está instalado y funcionando en el sistema objetivo?
|
|
||||||
**Impacto**: Si no está disponible, necesitaremos un método alternativo de búsqueda de archivos.
|
|
||||||
**Opciones**:
|
|
||||||
- Usar `python-everything` si Everything está disponible
|
|
||||||
- Implementar búsqueda recursiva con `os.walk()` como fallback
|
|
||||||
- Usar `pathlib` con `glob` patterns
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Podemos asumir que Everything estará disponible o necesitamos fallback?
|
|
||||||
|
|
||||||
### 2. Estructura de Backup - Clarificación de Rutas
|
|
||||||
**Pregunta**: Si tenemos un archivo en `C:\Projects\Siemens\LineA\Project1\project.s7p`, ¿el backup debe ser?
|
|
||||||
**Opción A**:
|
|
||||||
```
|
|
||||||
backup_destination/2025-09-01_14-30-15/Projects_Siemens_LineA_Project1.zip
|
|
||||||
└── Projects/Siemens/LineA/Project1/...
|
|
||||||
```
|
|
||||||
**Opción B**:
|
|
||||||
```
|
|
||||||
backup_destination/2025-09-01_14-30-15/LineA_Project1.zip
|
|
||||||
└── LineA/Project1/...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué estructura prefieres?
|
|
||||||
|
|
||||||
### 3. Configuración de Schedules - Granularidad
|
|
||||||
**Pregunta**: ¿Los schedules deben ser configurables a nivel de:
|
|
||||||
- Por directorio de observación (todos los proyectos de un directorio)
|
|
||||||
- Por proyecto individual
|
|
||||||
- Ambos (global con override por proyecto)
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué nivel de granularidad necesitas?
|
|
||||||
|
|
||||||
### 4. Hash de Archivos - Algoritmo y Almacenamiento
|
|
||||||
**Pregunta**: Para el sistema de hash en dos etapas:
|
|
||||||
- ¿Usamos MD5 (rápido) o SHA256 (más seguro) para los hashes?
|
|
||||||
- ¿El hash incluye solo timestamp o también tamaño de archivo?
|
|
||||||
- ¿Cómo manejamos archivos que se mueven dentro del proyecto?
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué balance entre velocidad y precisión prefieres?
|
|
||||||
|
|
||||||
### 5. Detección de Archivos en Uso - Método
|
|
||||||
**Pregunta**: Para detectar archivos bloqueados, ¿prefieres:
|
|
||||||
**Opción A**: Intentar abrir cada archivo en modo escritura exclusiva
|
|
||||||
**Opción B**: Usar `lsof` (Linux) / `handle.exe` (Windows) para listar archivos abiertos
|
|
||||||
**Opción C**: Solo verificar el archivo .s7p principal
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué método consideras más apropiado?
|
|
||||||
|
|
||||||
### 6. Manejo de Proyectos Grandes - Performance
|
|
||||||
**Pregunta**: ¿Hay límites que debamos considerar?
|
|
||||||
- Tamaño máximo de proyecto para backup
|
|
||||||
- Tiempo máximo de backup
|
|
||||||
- Número máximo de archivos por proyecto
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Necesitamos algún tipo de throttling o límites?
|
|
||||||
|
|
||||||
### 7. Configuración de Directorios - Recursividad
|
|
||||||
**Pregunta**: Cuando especificamos un directorio de observación como `C:\Projects`, ¿debemos:
|
|
||||||
- Buscar solo en subdirectorios inmediatos
|
|
||||||
- Buscar recursivamente en toda la jerarquía
|
|
||||||
- Permitir configurar la profundidad de búsqueda
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué comportamiento prefieres por defecto?
|
|
||||||
|
|
||||||
### 8. Backup Incremental vs Completo
|
|
||||||
**Pregunta**: ¿Todos los backups deben ser completos o consideras backup incremental?
|
|
||||||
- Backup completo: Todo el proyecto cada vez
|
|
||||||
- Backup incremental: Solo archivos modificados
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Preferencia por simplicidad o eficiencia de espacio?
|
|
||||||
|
|
||||||
### 9. Web Interface - Características Específicas
|
|
||||||
**Pregunta**: ¿Qué funcionalidades son prioritarias en la interfaz web?
|
|
||||||
**Must-have**:
|
|
||||||
- Lista de proyectos con estado
|
|
||||||
- Trigger manual de backup
|
|
||||||
- Ver logs básicos
|
|
||||||
|
|
||||||
**Nice-to-have**:
|
|
||||||
- Configurar schedules por proyecto
|
|
||||||
- Ver progreso de backup en tiempo real
|
|
||||||
- Estadísticas históricas
|
|
||||||
- Configurar nuevos directorios
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Cuáles son must-have vs nice-to-have?
|
|
||||||
|
|
||||||
### 10. Error Handling - Estrategias de Recuperación
|
|
||||||
**Pregunta**: ¿Cómo manejar errores específicos?
|
|
||||||
- Espacio insuficiente en destino de backup
|
|
||||||
- Pérdida de conexión con Everything
|
|
||||||
- Corrupción de archivos de configuración
|
|
||||||
- Falla durante compresión
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué nivel de robustez necesitas?
|
|
||||||
|
|
||||||
### 11. Startup Behavior - Inicialización
|
|
||||||
**Pregunta**: Al iniciar la aplicación:
|
|
||||||
- ¿Debe hacer un escaneo completo inmediatamente?
|
|
||||||
- ¿Debe esperar al primer schedule programado?
|
|
||||||
- ¿Debe permitir configurar el comportamiento de startup?
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué comportamiento prefieres al iniciar?
|
|
||||||
|
|
||||||
### 12. Multi-threading - Concurrencia
|
|
||||||
**Pregunta**: ¿Los backups deben ejecutarse:
|
|
||||||
- Secuencialmente (uno por vez)
|
|
||||||
- En paralelo (múltiples simultáneos)
|
|
||||||
- Con límite configurable de concurrencia
|
|
||||||
|
|
||||||
**Decisión necesaria**: ¿Qué balance entre velocidad y recursos del sistema?
|
|
||||||
|
|
||||||
## Próximos Pasos
|
|
||||||
Una vez que tengas respuestas a estas preguntas, podremos:
|
|
||||||
1. Finalizar el diseño técnico
|
|
||||||
2. Crear el `requirements.txt` definitivo
|
|
||||||
3. Comenzar con la implementación por fases
|
|
||||||
4. Definir el plan de testing
|
|
||||||
|
|
||||||
¿Podrías revisar estas preguntas y darme tus preferencias para cada una?
|
|
|
@ -1,60 +0,0 @@
|
||||||
{
|
|
||||||
"observation_directories": [
|
|
||||||
{
|
|
||||||
"path": "C:\\Projects\\Siemens",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"enabled": true,
|
|
||||||
"description": "Directorio principal de proyectos Siemens"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "D:\\Engineering\\Projects",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"enabled": true,
|
|
||||||
"description": "Proyectos de ingeniería adicionales"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "C:\\Important\\Documentation",
|
|
||||||
"type": "manual",
|
|
||||||
"enabled": true,
|
|
||||||
"description": "Documentación importante para backup manual"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"backup_destination": "D:\\Backups\\AutoBackups",
|
|
||||||
"global_settings": {
|
|
||||||
"default_schedule": "daily",
|
|
||||||
"default_schedule_time": "02:00",
|
|
||||||
"retry_delay_hours": 1,
|
|
||||||
"backup_timeout_minutes": 0,
|
|
||||||
"min_free_space_mb": 100,
|
|
||||||
"scan_interval_minutes": 60,
|
|
||||||
"min_backup_interval_minutes": 10
|
|
||||||
},
|
|
||||||
"everything_api": {
|
|
||||||
"dll_path": "Everything-SDK\\dll\\Everything64.dll",
|
|
||||||
"enabled": true,
|
|
||||||
"search_depth": -1,
|
|
||||||
"skip_last_level_for_s7p": true
|
|
||||||
},
|
|
||||||
"web_interface": {
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"port": 5000,
|
|
||||||
"debug": false
|
|
||||||
},
|
|
||||||
"logging": {
|
|
||||||
"level": "INFO",
|
|
||||||
"max_log_files": 30,
|
|
||||||
"log_rotation_days": 30
|
|
||||||
},
|
|
||||||
"backup_options": {
|
|
||||||
"compression_level": 6,
|
|
||||||
"include_subdirectories": true,
|
|
||||||
"preserve_directory_structure": true,
|
|
||||||
"hash_algorithm": "md5",
|
|
||||||
"hash_includes": ["timestamp", "size"],
|
|
||||||
"backup_type": "complete",
|
|
||||||
"process_priority": "low",
|
|
||||||
"sequential_execution": true,
|
|
||||||
"filename_format": "HH-MM-SS_projects.zip",
|
|
||||||
"date_format": "YYYY-MM-DD"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
{
|
|
||||||
"metadata": {
|
|
||||||
"version": "1.0",
|
|
||||||
"last_updated": "2025-09-01T08:30:15Z",
|
|
||||||
"total_projects": 0
|
|
||||||
},
|
|
||||||
"projects": [
|
|
||||||
{
|
|
||||||
"id": "example_project_001",
|
|
||||||
"name": "PLC_MainLine_Example",
|
|
||||||
"path": "C:\\Projects\\Siemens\\LineA\\Project1",
|
|
||||||
"type": "siemens_s7",
|
|
||||||
"s7p_file": "C:\\Projects\\Siemens\\LineA\\Project1\\project.s7p",
|
|
||||||
"observation_directory": "C:\\Projects\\Siemens",
|
|
||||||
"relative_path": "LineA\\Project1",
|
|
||||||
"backup_path": "LineA\\Project1",
|
|
||||||
"schedule_config": {
|
|
||||||
"schedule": "daily",
|
|
||||||
"schedule_time": "02:00",
|
|
||||||
"enabled": true,
|
|
||||||
"next_scheduled_backup": "2025-09-02T02:00:00Z"
|
|
||||||
},
|
|
||||||
"backup_history": {
|
|
||||||
"last_backup_date": "2025-09-01T02:15:30Z",
|
|
||||||
"last_backup_file": "D:\\Backups\\AutoBackups\\LineA\\Project1\\2025-09-01\\02-15-30_projects.zip",
|
|
||||||
"backup_count": 5,
|
|
||||||
"last_successful_backup": "2025-09-01T02:15:30Z"
|
|
||||||
},
|
|
||||||
"hash_info": {
|
|
||||||
"last_s7p_hash": "abc123def456789",
|
|
||||||
"last_full_hash": "def789ghi012345",
|
|
||||||
"last_s7p_timestamp": "2025-08-31T14:30:00Z",
|
|
||||||
"last_s7p_size": 2048576,
|
|
||||||
"last_scan_timestamp": "2025-09-01T02:10:00Z",
|
|
||||||
"file_count": 1247,
|
|
||||||
"total_size_bytes": 125847296
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"current_status": "ready",
|
|
||||||
"last_error": null,
|
|
||||||
"retry_count": 0,
|
|
||||||
"next_retry": null,
|
|
||||||
"files_in_use": false,
|
|
||||||
"exclusivity_check_passed": true,
|
|
||||||
"last_status_update": "2025-09-01T02:15:35Z"
|
|
||||||
},
|
|
||||||
"discovery_info": {
|
|
||||||
"discovered_date": "2025-09-01T08:30:15Z",
|
|
||||||
"discovery_method": "everything_api",
|
|
||||||
"auto_discovered": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"statistics": {
|
|
||||||
"total_backups_created": 15,
|
|
||||||
"total_backup_size_mb": 2450.5,
|
|
||||||
"average_backup_time_seconds": 45.2,
|
|
||||||
"last_global_scan": "2025-09-01T08:30:15Z",
|
|
||||||
"projects_with_errors": 0,
|
|
||||||
"projects_pending_retry": 0
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
# Core Framework
|
|
||||||
Flask==2.3.3
|
|
||||||
Werkzeug==2.3.7
|
|
||||||
|
|
||||||
# Scheduling
|
|
||||||
APScheduler==3.10.4
|
|
||||||
|
|
||||||
# Everything API Integration
|
|
||||||
PyEverything==1.0.1 # Wrapper para Everything SDK
|
|
||||||
|
|
||||||
# File Operations and Utilities
|
|
||||||
pathlib2==2.3.7.post1 # Enhanced pathlib for Python 3.12
|
|
||||||
psutil==5.9.5 # System utilities (disk space monitoring)
|
|
||||||
filelock==3.12.4 # File locking for concurrent access
|
|
||||||
|
|
||||||
# Web Interface
|
|
||||||
Jinja2==3.1.2 # Template engine for Flask
|
|
||||||
|
|
||||||
# Configuration and Data
|
|
||||||
jsonschema==4.19.1 # JSON schema validation
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
colorlog==6.7.0 # Colored logging output
|
|
||||||
|
|
||||||
# Windows-specific dependencies
|
|
||||||
pywin32==306; sys_platform == "win32" # Windows API access for low priority
|
|
||||||
|
|
||||||
# Development and Testing (opcional, remove for production)
|
|
||||||
pytest==7.4.2
|
|
||||||
pytest-flask==1.2.0
|
|
||||||
black==23.9.1 # Code formatting
|
|
||||||
flake8==6.1.0 # Code linting
|
|
|
@ -1,275 +0,0 @@
|
||||||
"""
|
|
||||||
AutoBackups - Aplicación Principal
|
|
||||||
Sistema automatizado de backup para proyectos Simatic S7
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
from pathlib import Path
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
# Agregar el directorio src al path para imports
|
|
||||||
current_dir = Path(__file__).parent
|
|
||||||
src_dir = current_dir
|
|
||||||
sys.path.insert(0, str(src_dir))
|
|
||||||
|
|
||||||
# Imports de módulos propios
|
|
||||||
from models.config_model import Config
|
|
||||||
from models.project_model import ProjectManager
|
|
||||||
from utils.file_utils import DiskSpaceChecker
|
|
||||||
from services.basic_backup_service import BasicBackupService
|
|
||||||
|
|
||||||
|
|
||||||
class AutoBackupsApp:
|
|
||||||
"""Aplicación principal de AutoBackups"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.config = None
|
|
||||||
self.project_manager = None
|
|
||||||
self.discovery_service = None
|
|
||||||
self.disk_checker = None
|
|
||||||
self.logger = None
|
|
||||||
|
|
||||||
# Inicializar aplicación
|
|
||||||
self._setup_logging()
|
|
||||||
self._load_configuration()
|
|
||||||
self._initialize_services()
|
|
||||||
|
|
||||||
def _setup_logging(self):
|
|
||||||
"""Configurar sistema de logging"""
|
|
||||||
try:
|
|
||||||
# Crear directorio de logs si no existe
|
|
||||||
logs_dir = Path(__file__).parent / ".logs"
|
|
||||||
logs_dir.mkdir(exist_ok=True)
|
|
||||||
|
|
||||||
# Nombre del archivo de log con timestamp
|
|
||||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
||||||
log_filename = logs_dir / f"autobackups_{timestamp}.log"
|
|
||||||
|
|
||||||
# Configurar logging
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
||||||
handlers=[
|
|
||||||
logging.FileHandler(log_filename, encoding='utf-8'),
|
|
||||||
logging.StreamHandler(sys.stdout)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.logger = logging.getLogger(__name__)
|
|
||||||
self.logger.info(f"AutoBackups iniciado - Log: {log_filename}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error configurando logging: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def _load_configuration(self):
|
|
||||||
"""Cargar configuración del sistema"""
|
|
||||||
try:
|
|
||||||
self.logger.info("Cargando configuración...")
|
|
||||||
self.config = Config()
|
|
||||||
self.logger.info("Configuración cargada exitosamente")
|
|
||||||
|
|
||||||
# Mostrar información básica de configuración
|
|
||||||
self.logger.info(f"Directorios de observación: {len(self.config.observation_directories)}")
|
|
||||||
self.logger.info(f"Destino de backups: {self.config.backup_destination}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Error cargando configuración: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def _initialize_services(self):
|
|
||||||
"""Inicializar servicios principales"""
|
|
||||||
try:
|
|
||||||
self.logger.info("Inicializando servicios...")
|
|
||||||
|
|
||||||
# Project Manager
|
|
||||||
self.project_manager = ProjectManager()
|
|
||||||
self.logger.info("Project Manager inicializado")
|
|
||||||
|
|
||||||
# Disk Space Checker
|
|
||||||
self.disk_checker = DiskSpaceChecker()
|
|
||||||
self.logger.info("Disk Space Checker inicializado")
|
|
||||||
|
|
||||||
# Basic Backup Service (Fase 1)
|
|
||||||
self.backup_service = BasicBackupService(self.config)
|
|
||||||
self.logger.info("Basic Backup Service inicializado")
|
|
||||||
|
|
||||||
# Project Discovery Service (temporalmente comentado)
|
|
||||||
# self.discovery_service = ProjectDiscoveryService(self.config, self.project_manager)
|
|
||||||
# self.logger.info("Project Discovery Service inicializado")
|
|
||||||
|
|
||||||
self.logger.info("Todos los servicios inicializados correctamente")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Error inicializando servicios: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def check_system_requirements(self) -> bool:
|
|
||||||
"""Verificar requerimientos del sistema"""
|
|
||||||
try:
|
|
||||||
self.logger.info("Verificando requerimientos del sistema...")
|
|
||||||
|
|
||||||
# Verificar espacio en disco
|
|
||||||
backup_destination = self.config.backup_destination
|
|
||||||
min_space_mb = self.config.get_min_free_space_mb()
|
|
||||||
|
|
||||||
free_space_mb = self.disk_checker.get_free_space_mb(backup_destination)
|
|
||||||
|
|
||||||
if free_space_mb < min_space_mb:
|
|
||||||
self.logger.error(
|
|
||||||
f"Espacio insuficiente en destino de backup. "
|
|
||||||
f"Disponible: {free_space_mb:.1f}MB, "
|
|
||||||
f"Requerido: {min_space_mb}MB"
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.logger.info(f"Espacio en disco OK: {free_space_mb:.1f}MB disponibles")
|
|
||||||
|
|
||||||
# Verificar Everything API (opcional en Fase 1)
|
|
||||||
if hasattr(self, 'backup_service'):
|
|
||||||
if self.backup_service.check_system_requirements():
|
|
||||||
self.logger.info("Requerimientos del sistema verificados")
|
|
||||||
else:
|
|
||||||
self.logger.warning("Algunos requerimientos fallaron")
|
|
||||||
else:
|
|
||||||
self.logger.warning("Backup service no disponible")
|
|
||||||
|
|
||||||
# Verificar directorios de observación
|
|
||||||
missing_dirs = []
|
|
||||||
for obs_dir in self.config.observation_directories:
|
|
||||||
if not Path(obs_dir["path"]).exists():
|
|
||||||
missing_dirs.append(obs_dir["path"])
|
|
||||||
|
|
||||||
if missing_dirs:
|
|
||||||
self.logger.warning(f"Directorios de observación no encontrados: {missing_dirs}")
|
|
||||||
|
|
||||||
self.logger.info("Verificación de requerimientos completada")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Error verificando requerimientos del sistema: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def discover_projects(self) -> int:
|
|
||||||
"""Descubrir proyectos en directorios de observación"""
|
|
||||||
try:
|
|
||||||
self.logger.info("Iniciando descubrimiento de proyectos...")
|
|
||||||
|
|
||||||
# Usar el servicio básico de backup
|
|
||||||
if hasattr(self, 'backup_service'):
|
|
||||||
projects = self.backup_service.discover_projects_basic()
|
|
||||||
else:
|
|
||||||
projects = []
|
|
||||||
|
|
||||||
# Agregar proyectos al manager
|
|
||||||
for project_info in projects:
|
|
||||||
self.project_manager.add_or_update_project(project_info)
|
|
||||||
|
|
||||||
msg = f"Descubrimiento completado: {len(projects)} proyectos"
|
|
||||||
self.logger.info(msg)
|
|
||||||
|
|
||||||
return len(projects)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Error en descubrimiento de proyectos: {e}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def show_system_status(self):
|
|
||||||
"""Mostrar estado actual del sistema"""
|
|
||||||
try:
|
|
||||||
self.logger.info("=== ESTADO DEL SISTEMA ===")
|
|
||||||
|
|
||||||
# Información de configuración
|
|
||||||
self.logger.info(f"Directorios de observación configurados: {len(self.config.observation_directories)}")
|
|
||||||
self.logger.info(f"Destino de backups: {self.config.backup_destination}")
|
|
||||||
|
|
||||||
# Información de proyectos
|
|
||||||
all_projects = self.project_manager.get_all_projects()
|
|
||||||
enabled_projects = self.project_manager.get_enabled_projects()
|
|
||||||
|
|
||||||
self.logger.info(f"Total de proyectos: {len(all_projects)}")
|
|
||||||
self.logger.info(f"Proyectos habilitados: {len(enabled_projects)}")
|
|
||||||
|
|
||||||
# Información de espacio en disco
|
|
||||||
backup_dest = self.config.backup_destination
|
|
||||||
total_mb, used_mb, free_mb = self.disk_checker.get_disk_usage_info(backup_dest)
|
|
||||||
|
|
||||||
self.logger.info(f"Espacio en disco (destino backup):")
|
|
||||||
self.logger.info(f" Total: {total_mb:.1f}MB")
|
|
||||||
self.logger.info(f" Usado: {used_mb:.1f}MB")
|
|
||||||
self.logger.info(f" Libre: {free_mb:.1f}MB")
|
|
||||||
|
|
||||||
# Información de Everything API
|
|
||||||
if self.discovery_service.everything_searcher:
|
|
||||||
self.logger.info("Everything API: Disponible")
|
|
||||||
else:
|
|
||||||
self.logger.info("Everything API: No disponible")
|
|
||||||
|
|
||||||
self.logger.info("=== FIN ESTADO DEL SISTEMA ===")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Error mostrando estado del sistema: {e}")
|
|
||||||
|
|
||||||
def run_initial_setup(self):
|
|
||||||
"""Ejecutar configuración inicial de la aplicación"""
|
|
||||||
try:
|
|
||||||
self.logger.info("=== CONFIGURACIÓN INICIAL DE AUTOBACKUPS ===")
|
|
||||||
|
|
||||||
# Verificar requerimientos del sistema
|
|
||||||
if not self.check_system_requirements():
|
|
||||||
self.logger.error("Los requerimientos del sistema no se cumplen")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Descubrir proyectos
|
|
||||||
projects_found = self.discover_projects()
|
|
||||||
|
|
||||||
if projects_found == 0:
|
|
||||||
self.logger.warning("No se encontraron proyectos para backup")
|
|
||||||
|
|
||||||
# Mostrar estado del sistema
|
|
||||||
self.show_system_status()
|
|
||||||
|
|
||||||
self.logger.info("=== CONFIGURACIÓN INICIAL COMPLETADA ===")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Error en configuración inicial: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Función principal"""
|
|
||||||
try:
|
|
||||||
print("AutoBackups - Sistema de Backup Automatizado")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Crear y configurar aplicación
|
|
||||||
app = AutoBackupsApp()
|
|
||||||
|
|
||||||
# Ejecutar configuración inicial
|
|
||||||
if app.run_initial_setup():
|
|
||||||
app.logger.info("AutoBackups configurado correctamente")
|
|
||||||
|
|
||||||
# TODO: En las siguientes fases:
|
|
||||||
# - Iniciar scheduler de backups
|
|
||||||
# - Iniciar interfaz web Flask
|
|
||||||
# - Configurar tareas en background
|
|
||||||
|
|
||||||
print("\nConfiguración inicial completada.")
|
|
||||||
print("Revisa el archivo de log para más detalles.")
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("Error en la configuración inicial. Revisa los logs.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("\nAplicación interrumpida por el usuario")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error inesperado: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,9 +0,0 @@
|
||||||
"""
|
|
||||||
AutoBackups - Data Models
|
|
||||||
Modelos de datos para configuración y manejo de proyectos
|
|
||||||
"""
|
|
||||||
|
|
||||||
from .config_model import Config
|
|
||||||
from .project_model import Project, ProjectStatus
|
|
||||||
|
|
||||||
__all__ = ['Config', 'Project', 'ProjectStatus']
|
|
|
@ -1,12 +0,0 @@
|
||||||
"""
|
|
||||||
AutoBackups - Services
|
|
||||||
Servicios principales para backup y descubrimiento de proyectos
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Imports comentados temporalmente para evitar problemas de dependencias circulares
|
|
||||||
# from .project_discovery_service import ProjectDiscoveryService
|
|
||||||
# from .backup_service import BackupService
|
|
||||||
# from .scheduler_service import SchedulerService
|
|
||||||
from .basic_backup_service import BasicBackupService
|
|
||||||
|
|
||||||
__all__ = ['BasicBackupService']
|
|
|
@ -1,10 +0,0 @@
|
||||||
"""
|
|
||||||
AutoBackups - Utilities
|
|
||||||
Funciones de utilidad para hashing, manejo de archivos y otras operaciones
|
|
||||||
"""
|
|
||||||
|
|
||||||
from .hash_utils import HashCalculator
|
|
||||||
from .file_utils import FileChecker, DiskSpaceChecker
|
|
||||||
from .everything_wrapper import EverythingSearcher
|
|
||||||
|
|
||||||
__all__ = ['HashCalculator', 'FileChecker', 'DiskSpaceChecker', 'EverythingSearcher']
|
|
19
config.json
19
config.json
|
@ -24,13 +24,16 @@
|
||||||
"default_schedule": "daily",
|
"default_schedule": "daily",
|
||||||
"default_schedule_time": "02:00",
|
"default_schedule_time": "02:00",
|
||||||
"retry_delay_hours": 1,
|
"retry_delay_hours": 1,
|
||||||
"max_concurrent_backups": 2,
|
"backup_timeout_minutes": 0,
|
||||||
"backup_timeout_minutes": 60
|
"min_free_space_mb": 100,
|
||||||
|
"scan_interval_minutes": 60,
|
||||||
|
"min_backup_interval_minutes": 10
|
||||||
},
|
},
|
||||||
"everything_api": {
|
"everything_api": {
|
||||||
|
"dll_path": "Everything-SDK\\dll\\Everything64.dll",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"fallback_to_filesystem_search": true,
|
"search_depth": -1,
|
||||||
"search_depth": -1
|
"skip_last_level_for_s7p": true
|
||||||
},
|
},
|
||||||
"web_interface": {
|
"web_interface": {
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
|
@ -46,6 +49,12 @@
|
||||||
"compression_level": 6,
|
"compression_level": 6,
|
||||||
"include_subdirectories": true,
|
"include_subdirectories": true,
|
||||||
"preserve_directory_structure": true,
|
"preserve_directory_structure": true,
|
||||||
"hash_algorithm": "md5"
|
"hash_algorithm": "md5",
|
||||||
|
"hash_includes": ["timestamp", "size"],
|
||||||
|
"backup_type": "complete",
|
||||||
|
"process_priority": "low",
|
||||||
|
"sequential_execution": true,
|
||||||
|
"filename_format": "HH-MM-SS_projects.zip",
|
||||||
|
"date_format": "YYYY-MM-DD"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,12 @@
|
||||||
{
|
{
|
||||||
"id": "example_project_001",
|
"id": "example_project_001",
|
||||||
"name": "PLC_MainLine_Example",
|
"name": "PLC_MainLine_Example",
|
||||||
"path": "C:\\Projects\\Siemens\\PLC_MainLine",
|
"path": "C:\\Projects\\Siemens\\LineA\\Project1",
|
||||||
"type": "siemens_s7",
|
"type": "siemens_s7",
|
||||||
"s7p_file": "C:\\Projects\\Siemens\\PLC_MainLine\\PLC_MainLine.s7p",
|
"s7p_file": "C:\\Projects\\Siemens\\LineA\\Project1\\project.s7p",
|
||||||
"observation_directory": "C:\\Projects\\Siemens",
|
"observation_directory": "C:\\Projects\\Siemens",
|
||||||
"relative_path": "PLC_MainLine",
|
"relative_path": "LineA\\Project1",
|
||||||
|
"backup_path": "LineA\\Project1",
|
||||||
"schedule_config": {
|
"schedule_config": {
|
||||||
"schedule": "daily",
|
"schedule": "daily",
|
||||||
"schedule_time": "02:00",
|
"schedule_time": "02:00",
|
||||||
|
@ -21,7 +22,7 @@
|
||||||
},
|
},
|
||||||
"backup_history": {
|
"backup_history": {
|
||||||
"last_backup_date": "2025-09-01T02:15:30Z",
|
"last_backup_date": "2025-09-01T02:15:30Z",
|
||||||
"last_backup_file": "D:\\Backups\\AutoBackups\\2025-09-01_02-15-30\\Projects_Siemens_PLC_MainLine.zip",
|
"last_backup_file": "D:\\Backups\\AutoBackups\\LineA\\Project1\\2025-09-01\\02-15-30_projects.zip",
|
||||||
"backup_count": 5,
|
"backup_count": 5,
|
||||||
"last_successful_backup": "2025-09-01T02:15:30Z"
|
"last_successful_backup": "2025-09-01T02:15:30Z"
|
||||||
},
|
},
|
||||||
|
@ -29,8 +30,10 @@
|
||||||
"last_s7p_hash": "abc123def456789",
|
"last_s7p_hash": "abc123def456789",
|
||||||
"last_full_hash": "def789ghi012345",
|
"last_full_hash": "def789ghi012345",
|
||||||
"last_s7p_timestamp": "2025-08-31T14:30:00Z",
|
"last_s7p_timestamp": "2025-08-31T14:30:00Z",
|
||||||
|
"last_s7p_size": 2048576,
|
||||||
"last_scan_timestamp": "2025-09-01T02:10:00Z",
|
"last_scan_timestamp": "2025-09-01T02:10:00Z",
|
||||||
"file_count": 1247
|
"file_count": 1247,
|
||||||
|
"total_size_bytes": 125847296
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"current_status": "ready",
|
"current_status": "ready",
|
||||||
|
@ -38,6 +41,7 @@
|
||||||
"retry_count": 0,
|
"retry_count": 0,
|
||||||
"next_retry": null,
|
"next_retry": null,
|
||||||
"files_in_use": false,
|
"files_in_use": false,
|
||||||
|
"exclusivity_check_passed": true,
|
||||||
"last_status_update": "2025-09-01T02:15:35Z"
|
"last_status_update": "2025-09-01T02:15:35Z"
|
||||||
},
|
},
|
||||||
"discovery_info": {
|
"discovery_info": {
|
||||||
|
|
|
@ -5,12 +5,12 @@ Werkzeug==2.3.7
|
||||||
# Scheduling
|
# Scheduling
|
||||||
APScheduler==3.10.4
|
APScheduler==3.10.4
|
||||||
|
|
||||||
# Everything API Integration (si está disponible)
|
# Everything API Integration
|
||||||
# python-everything==1.0.1 # Descomenta si existe la librería
|
PyEverything==1.0.1 # Wrapper para Everything SDK
|
||||||
|
|
||||||
# File Operations and Utilities
|
# File Operations and Utilities
|
||||||
pathlib2==2.3.7.post1 # Enhanced pathlib for Python 3.12
|
pathlib2==2.3.7.post1 # Enhanced pathlib for Python 3.12
|
||||||
psutil==5.9.5 # System and process utilities
|
psutil==5.9.5 # System utilities (disk space monitoring)
|
||||||
filelock==3.12.4 # File locking for concurrent access
|
filelock==3.12.4 # File locking for concurrent access
|
||||||
|
|
||||||
# Web Interface
|
# Web Interface
|
||||||
|
@ -22,11 +22,11 @@ jsonschema==4.19.1 # JSON schema validation
|
||||||
# Logging
|
# Logging
|
||||||
colorlog==6.7.0 # Colored logging output
|
colorlog==6.7.0 # Colored logging output
|
||||||
|
|
||||||
|
# Windows-specific dependencies
|
||||||
|
pywin32==306; sys_platform == "win32" # Windows API access for low priority
|
||||||
|
|
||||||
# Development and Testing (opcional, remove for production)
|
# Development and Testing (opcional, remove for production)
|
||||||
pytest==7.4.2
|
pytest==7.4.2
|
||||||
pytest-flask==1.2.0
|
pytest-flask==1.2.0
|
||||||
black==23.9.1 # Code formatting
|
black==23.9.1 # Code formatting
|
||||||
flake8==6.1.0 # Code linting
|
flake8==6.1.0 # Code linting
|
||||||
|
|
||||||
# Windows-specific dependencies
|
|
||||||
pywin32==306; sys_platform == "win32" # Windows API access
|
|
||||||
|
|
293
src/app.py
293
src/app.py
|
@ -1,38 +1,275 @@
|
||||||
from flask import Flask
|
"""
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
AutoBackups - Aplicación Principal
|
||||||
import json
|
Sistema automatizado de backup para proyectos Simatic S7
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
app = Flask(__name__)
|
# Agregar el directorio src al path para imports
|
||||||
|
current_dir = Path(__file__).parent
|
||||||
|
src_dir = current_dir
|
||||||
|
sys.path.insert(0, str(src_dir))
|
||||||
|
|
||||||
# Load configuration from config.json
|
# Imports de módulos propios
|
||||||
with open('config.json') as config_file:
|
from models.config_model import Config
|
||||||
config = json.load(config_file)
|
from models.project_model import ProjectManager
|
||||||
|
from utils.file_utils import DiskSpaceChecker
|
||||||
|
from services.basic_backup_service import BasicBackupService
|
||||||
|
|
||||||
# Initialize the scheduler
|
|
||||||
scheduler = BackgroundScheduler()
|
|
||||||
|
|
||||||
def backup_projects():
|
class AutoBackupsApp:
|
||||||
# Logic to scan directories and perform backups
|
"""Aplicación principal de AutoBackups"""
|
||||||
pass
|
|
||||||
|
|
||||||
# Schedule the backup task
|
def __init__(self):
|
||||||
scheduler.add_job(backup_projects, 'interval', hours=config.get('backup_interval_hours', 24))
|
self.config = None
|
||||||
scheduler.start()
|
self.project_manager = None
|
||||||
|
self.discovery_service = None
|
||||||
|
self.disk_checker = None
|
||||||
|
self.logger = None
|
||||||
|
|
||||||
@app.route('/')
|
# Inicializar aplicación
|
||||||
def index():
|
self._setup_logging()
|
||||||
return "Welcome to the Auto Backup System"
|
self._load_configuration()
|
||||||
|
self._initialize_services()
|
||||||
|
|
||||||
@app.route('/backup', methods=['POST'])
|
def _setup_logging(self):
|
||||||
def trigger_backup():
|
"""Configurar sistema de logging"""
|
||||||
# Logic to manually trigger a backup
|
try:
|
||||||
return "Backup triggered"
|
# Crear directorio de logs si no existe
|
||||||
|
logs_dir = Path(__file__).parent / ".logs"
|
||||||
|
logs_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
@app.route('/status', methods=['GET'])
|
# Nombre del archivo de log con timestamp
|
||||||
def backup_status():
|
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
# Logic to check the status of backups
|
log_filename = logs_dir / f"autobackups_{timestamp}.log"
|
||||||
return "Backup status"
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
# Configurar logging
|
||||||
app.run(host='0.0.0.0', port=5000)
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
|
handlers=[
|
||||||
|
logging.FileHandler(log_filename, encoding='utf-8'),
|
||||||
|
logging.StreamHandler(sys.stdout)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger = logging.getLogger(__name__)
|
||||||
|
self.logger.info(f"AutoBackups iniciado - Log: {log_filename}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error configurando logging: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def _load_configuration(self):
|
||||||
|
"""Cargar configuración del sistema"""
|
||||||
|
try:
|
||||||
|
self.logger.info("Cargando configuración...")
|
||||||
|
self.config = Config()
|
||||||
|
self.logger.info("Configuración cargada exitosamente")
|
||||||
|
|
||||||
|
# Mostrar información básica de configuración
|
||||||
|
self.logger.info(f"Directorios de observación: {len(self.config.observation_directories)}")
|
||||||
|
self.logger.info(f"Destino de backups: {self.config.backup_destination}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error cargando configuración: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def _initialize_services(self):
|
||||||
|
"""Inicializar servicios principales"""
|
||||||
|
try:
|
||||||
|
self.logger.info("Inicializando servicios...")
|
||||||
|
|
||||||
|
# Project Manager
|
||||||
|
self.project_manager = ProjectManager()
|
||||||
|
self.logger.info("Project Manager inicializado")
|
||||||
|
|
||||||
|
# Disk Space Checker
|
||||||
|
self.disk_checker = DiskSpaceChecker()
|
||||||
|
self.logger.info("Disk Space Checker inicializado")
|
||||||
|
|
||||||
|
# Basic Backup Service (Fase 1)
|
||||||
|
self.backup_service = BasicBackupService(self.config)
|
||||||
|
self.logger.info("Basic Backup Service inicializado")
|
||||||
|
|
||||||
|
# Project Discovery Service (temporalmente comentado)
|
||||||
|
# self.discovery_service = ProjectDiscoveryService(self.config, self.project_manager)
|
||||||
|
# self.logger.info("Project Discovery Service inicializado")
|
||||||
|
|
||||||
|
self.logger.info("Todos los servicios inicializados correctamente")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error inicializando servicios: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def check_system_requirements(self) -> bool:
|
||||||
|
"""Verificar requerimientos del sistema"""
|
||||||
|
try:
|
||||||
|
self.logger.info("Verificando requerimientos del sistema...")
|
||||||
|
|
||||||
|
# Verificar espacio en disco
|
||||||
|
backup_destination = self.config.backup_destination
|
||||||
|
min_space_mb = self.config.get_min_free_space_mb()
|
||||||
|
|
||||||
|
free_space_mb = self.disk_checker.get_free_space_mb(backup_destination)
|
||||||
|
|
||||||
|
if free_space_mb < min_space_mb:
|
||||||
|
self.logger.error(
|
||||||
|
f"Espacio insuficiente en destino de backup. "
|
||||||
|
f"Disponible: {free_space_mb:.1f}MB, "
|
||||||
|
f"Requerido: {min_space_mb}MB"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.logger.info(f"Espacio en disco OK: {free_space_mb:.1f}MB disponibles")
|
||||||
|
|
||||||
|
# Verificar Everything API (opcional en Fase 1)
|
||||||
|
if hasattr(self, 'backup_service'):
|
||||||
|
if self.backup_service.check_system_requirements():
|
||||||
|
self.logger.info("Requerimientos del sistema verificados")
|
||||||
|
else:
|
||||||
|
self.logger.warning("Algunos requerimientos fallaron")
|
||||||
|
else:
|
||||||
|
self.logger.warning("Backup service no disponible")
|
||||||
|
|
||||||
|
# Verificar directorios de observación
|
||||||
|
missing_dirs = []
|
||||||
|
for obs_dir in self.config.observation_directories:
|
||||||
|
if not Path(obs_dir["path"]).exists():
|
||||||
|
missing_dirs.append(obs_dir["path"])
|
||||||
|
|
||||||
|
if missing_dirs:
|
||||||
|
self.logger.warning(f"Directorios de observación no encontrados: {missing_dirs}")
|
||||||
|
|
||||||
|
self.logger.info("Verificación de requerimientos completada")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error verificando requerimientos del sistema: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def discover_projects(self) -> int:
|
||||||
|
"""Descubrir proyectos en directorios de observación"""
|
||||||
|
try:
|
||||||
|
self.logger.info("Iniciando descubrimiento de proyectos...")
|
||||||
|
|
||||||
|
# Usar el servicio básico de backup
|
||||||
|
if hasattr(self, 'backup_service'):
|
||||||
|
projects = self.backup_service.discover_projects_basic()
|
||||||
|
else:
|
||||||
|
projects = []
|
||||||
|
|
||||||
|
# Agregar proyectos al manager
|
||||||
|
for project_info in projects:
|
||||||
|
self.project_manager.add_or_update_project(project_info)
|
||||||
|
|
||||||
|
msg = f"Descubrimiento completado: {len(projects)} proyectos"
|
||||||
|
self.logger.info(msg)
|
||||||
|
|
||||||
|
return len(projects)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error en descubrimiento de proyectos: {e}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def show_system_status(self):
|
||||||
|
"""Mostrar estado actual del sistema"""
|
||||||
|
try:
|
||||||
|
self.logger.info("=== ESTADO DEL SISTEMA ===")
|
||||||
|
|
||||||
|
# Información de configuración
|
||||||
|
self.logger.info(f"Directorios de observación configurados: {len(self.config.observation_directories)}")
|
||||||
|
self.logger.info(f"Destino de backups: {self.config.backup_destination}")
|
||||||
|
|
||||||
|
# Información de proyectos
|
||||||
|
all_projects = self.project_manager.get_all_projects()
|
||||||
|
enabled_projects = self.project_manager.get_enabled_projects()
|
||||||
|
|
||||||
|
self.logger.info(f"Total de proyectos: {len(all_projects)}")
|
||||||
|
self.logger.info(f"Proyectos habilitados: {len(enabled_projects)}")
|
||||||
|
|
||||||
|
# Información de espacio en disco
|
||||||
|
backup_dest = self.config.backup_destination
|
||||||
|
total_mb, used_mb, free_mb = self.disk_checker.get_disk_usage_info(backup_dest)
|
||||||
|
|
||||||
|
self.logger.info(f"Espacio en disco (destino backup):")
|
||||||
|
self.logger.info(f" Total: {total_mb:.1f}MB")
|
||||||
|
self.logger.info(f" Usado: {used_mb:.1f}MB")
|
||||||
|
self.logger.info(f" Libre: {free_mb:.1f}MB")
|
||||||
|
|
||||||
|
# Información de Everything API
|
||||||
|
if self.discovery_service.everything_searcher:
|
||||||
|
self.logger.info("Everything API: Disponible")
|
||||||
|
else:
|
||||||
|
self.logger.info("Everything API: No disponible")
|
||||||
|
|
||||||
|
self.logger.info("=== FIN ESTADO DEL SISTEMA ===")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error mostrando estado del sistema: {e}")
|
||||||
|
|
||||||
|
def run_initial_setup(self):
|
||||||
|
"""Ejecutar configuración inicial de la aplicación"""
|
||||||
|
try:
|
||||||
|
self.logger.info("=== CONFIGURACIÓN INICIAL DE AUTOBACKUPS ===")
|
||||||
|
|
||||||
|
# Verificar requerimientos del sistema
|
||||||
|
if not self.check_system_requirements():
|
||||||
|
self.logger.error("Los requerimientos del sistema no se cumplen")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Descubrir proyectos
|
||||||
|
projects_found = self.discover_projects()
|
||||||
|
|
||||||
|
if projects_found == 0:
|
||||||
|
self.logger.warning("No se encontraron proyectos para backup")
|
||||||
|
|
||||||
|
# Mostrar estado del sistema
|
||||||
|
self.show_system_status()
|
||||||
|
|
||||||
|
self.logger.info("=== CONFIGURACIÓN INICIAL COMPLETADA ===")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error en configuración inicial: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Función principal"""
|
||||||
|
try:
|
||||||
|
print("AutoBackups - Sistema de Backup Automatizado")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
# Crear y configurar aplicación
|
||||||
|
app = AutoBackupsApp()
|
||||||
|
|
||||||
|
# Ejecutar configuración inicial
|
||||||
|
if app.run_initial_setup():
|
||||||
|
app.logger.info("AutoBackups configurado correctamente")
|
||||||
|
|
||||||
|
# TODO: En las siguientes fases:
|
||||||
|
# - Iniciar scheduler de backups
|
||||||
|
# - Iniciar interfaz web Flask
|
||||||
|
# - Configurar tareas en background
|
||||||
|
|
||||||
|
print("\nConfiguración inicial completada.")
|
||||||
|
print("Revisa el archivo de log para más detalles.")
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Error en la configuración inicial. Revisa los logs.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nAplicación interrumpida por el usuario")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error inesperado: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
|
@ -1 +1,9 @@
|
||||||
# This file is intentionally left blank.
|
"""
|
||||||
|
AutoBackups - Data Models
|
||||||
|
Modelos de datos para configuración y manejo de proyectos
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .config_model import Config
|
||||||
|
from .project_model import Project, ProjectStatus
|
||||||
|
|
||||||
|
__all__ = ['Config', 'Project', 'ProjectStatus']
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
from flask import Blueprint
|
|
||||||
|
|
||||||
bp = Blueprint('routes', __name__)
|
|
||||||
|
|
||||||
from . import backup_routes, status_routes, config_routes
|
|
|
@ -1 +1,12 @@
|
||||||
# This file is intentionally left blank.
|
"""
|
||||||
|
AutoBackups - Services
|
||||||
|
Servicios principales para backup y descubrimiento de proyectos
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Imports comentados temporalmente para evitar problemas de dependencias circulares
|
||||||
|
# from .project_discovery_service import ProjectDiscoveryService
|
||||||
|
# from .backup_service import BackupService
|
||||||
|
# from .scheduler_service import SchedulerService
|
||||||
|
from .basic_backup_service import BasicBackupService
|
||||||
|
|
||||||
|
__all__ = ['BasicBackupService']
|
||||||
|
|
|
@ -1,19 +1,10 @@
|
||||||
def hash_timestamps(file_paths):
|
"""
|
||||||
# Implement hashing logic for file timestamps
|
AutoBackups - Utilities
|
||||||
pass
|
Funciones de utilidad para hashing, manejo de archivos y otras operaciones
|
||||||
|
"""
|
||||||
|
|
||||||
def check_file_access(file_path):
|
from .hash_utils import HashCalculator
|
||||||
# Implement logic to check if a file is in use
|
from .file_utils import FileChecker, DiskSpaceChecker
|
||||||
pass
|
from .everything_wrapper import EverythingSearcher
|
||||||
|
|
||||||
def get_everything_results(query):
|
__all__ = ['HashCalculator', 'FileChecker', 'DiskSpaceChecker', 'EverythingSearcher']
|
||||||
# Implement interaction with the Everything API
|
|
||||||
pass
|
|
||||||
|
|
||||||
def compress_files(file_paths, destination):
|
|
||||||
# Implement file compression logic
|
|
||||||
pass
|
|
||||||
|
|
||||||
def log_backup_status(project_name, status):
|
|
||||||
# Implement logging of backup status
|
|
||||||
pass
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
/* This file is intentionally left blank. */
|
|
|
@ -1,32 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Auto Backups</title>
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>Auto Backups</h1>
|
|
||||||
<div id="backup-status">
|
|
||||||
<h2>Backup Status</h2>
|
|
||||||
<ul id="status-list">
|
|
||||||
<!-- Backup status items will be dynamically inserted here -->
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div id="backup-controls">
|
|
||||||
<h2>Backup Controls</h2>
|
|
||||||
<button id="manual-backup">Force Backup Now</button>
|
|
||||||
<label for="backup-interval">Backup Interval:</label>
|
|
||||||
<select id="backup-interval">
|
|
||||||
<option value="hourly">Hourly</option>
|
|
||||||
<option value="daily" selected>Daily</option>
|
|
||||||
<option value="weekly">Weekly</option>
|
|
||||||
</select>
|
|
||||||
<button id="save-settings">Save Settings</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in New Issue