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_time": "02:00",
|
||||
"retry_delay_hours": 1,
|
||||
"max_concurrent_backups": 2,
|
||||
"backup_timeout_minutes": 60
|
||||
"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,
|
||||
"fallback_to_filesystem_search": true,
|
||||
"search_depth": -1
|
||||
"search_depth": -1,
|
||||
"skip_last_level_for_s7p": true
|
||||
},
|
||||
"web_interface": {
|
||||
"host": "127.0.0.1",
|
||||
|
@ -46,6 +49,12 @@
|
|||
"compression_level": 6,
|
||||
"include_subdirectories": 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",
|
||||
"name": "PLC_MainLine_Example",
|
||||
"path": "C:\\Projects\\Siemens\\PLC_MainLine",
|
||||
"path": "C:\\Projects\\Siemens\\LineA\\Project1",
|
||||
"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",
|
||||
"relative_path": "PLC_MainLine",
|
||||
"relative_path": "LineA\\Project1",
|
||||
"backup_path": "LineA\\Project1",
|
||||
"schedule_config": {
|
||||
"schedule": "daily",
|
||||
"schedule_time": "02:00",
|
||||
|
@ -21,7 +22,7 @@
|
|||
},
|
||||
"backup_history": {
|
||||
"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,
|
||||
"last_successful_backup": "2025-09-01T02:15:30Z"
|
||||
},
|
||||
|
@ -29,8 +30,10 @@
|
|||
"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
|
||||
"file_count": 1247,
|
||||
"total_size_bytes": 125847296
|
||||
},
|
||||
"status": {
|
||||
"current_status": "ready",
|
||||
|
@ -38,6 +41,7 @@
|
|||
"retry_count": 0,
|
||||
"next_retry": null,
|
||||
"files_in_use": false,
|
||||
"exclusivity_check_passed": true,
|
||||
"last_status_update": "2025-09-01T02:15:35Z"
|
||||
},
|
||||
"discovery_info": {
|
||||
|
|
|
@ -5,12 +5,12 @@ Werkzeug==2.3.7
|
|||
# Scheduling
|
||||
APScheduler==3.10.4
|
||||
|
||||
# Everything API Integration (si está disponible)
|
||||
# python-everything==1.0.1 # Descomenta si existe la librería
|
||||
# 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 and process utilities
|
||||
psutil==5.9.5 # System utilities (disk space monitoring)
|
||||
filelock==3.12.4 # File locking for concurrent access
|
||||
|
||||
# Web Interface
|
||||
|
@ -22,11 +22,11 @@ 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
|
||||
|
||||
# Windows-specific dependencies
|
||||
pywin32==306; sys_platform == "win32" # Windows API access
|
||||
|
|
295
src/app.py
295
src/app.py
|
@ -1,38 +1,275 @@
|
|||
from flask import Flask
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
import json
|
||||
"""
|
||||
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
|
||||
|
||||
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
|
||||
with open('config.json') as config_file:
|
||||
config = json.load(config_file)
|
||||
# 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
|
||||
|
||||
# Initialize the scheduler
|
||||
scheduler = BackgroundScheduler()
|
||||
|
||||
def backup_projects():
|
||||
# Logic to scan directories and perform backups
|
||||
pass
|
||||
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
|
||||
|
||||
# Schedule the backup task
|
||||
scheduler.add_job(backup_projects, 'interval', hours=config.get('backup_interval_hours', 24))
|
||||
scheduler.start()
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return "Welcome to the Auto Backup System"
|
||||
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)
|
||||
|
||||
@app.route('/backup', methods=['POST'])
|
||||
def trigger_backup():
|
||||
# Logic to manually trigger a backup
|
||||
return "Backup triggered"
|
||||
|
||||
@app.route('/status', methods=['GET'])
|
||||
def backup_status():
|
||||
# Logic to check the status of backups
|
||||
return "Backup status"
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000)
|
||||
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
|
||||
pass
|
||||
"""
|
||||
AutoBackups - Utilities
|
||||
Funciones de utilidad para hashing, manejo de archivos y otras operaciones
|
||||
"""
|
||||
|
||||
def check_file_access(file_path):
|
||||
# Implement logic to check if a file is in use
|
||||
pass
|
||||
from .hash_utils import HashCalculator
|
||||
from .file_utils import FileChecker, DiskSpaceChecker
|
||||
from .everything_wrapper import EverythingSearcher
|
||||
|
||||
def get_everything_results(query):
|
||||
# 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
|
||||
__all__ = ['HashCalculator', 'FileChecker', 'DiskSpaceChecker', 'EverythingSearcher']
|
||||
|
|
|
@ -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