Backup System

The backup system provides automatic backup creation for scene files with per-scene directories, configurable interval, rotation, and expiration policies.

Overview

The backup system creates timestamped backups of scene files in per-scene directories under ~/.local/share/zooui/backups/. Each scene file gets its own backup directory named {scene_filename}_{4char_path_hash}/, containing backups named yy_mm_dd_hh_mm_filename_hash.pzs with chronological sorting.

Scene directories expire after a configurable number of days of inactivity and are automatically garbage-collected.

Key Features

  • Per-scene directories: Each scene file has its own backup directory, isolating rotation and preventing cross-scene interference

  • Timer-based autosave: Creates backups at configurable intervals (default: 5 minutes)

  • File rotation: Keeps last N backups per scene (configurable), deletes oldest automatically

  • Directory expiration: Scene directories expire after expire_days of inactivity and are automatically cleaned up

  • Collision avoidance: Directory name includes path hash; individual backup filenames include a timestamp hash to prevent collisions

  • Error handling: Graceful error recovery with user notifications

  • Configurable: All settings can be configured via UI, CLI, or configuration files

  • Enabled by default: Autosave is active from application start

  • Legacy migration: Old flat backup files are cleaned up automatically on shutdown

Backup Directory Structure

Backups are stored with per-scene subdirectories:

~/.local/share/zooui/backups/
├── myscene_a1b2/
│   ├── 26_05_03_14_30_myscene_c3d4.pzs    # Backup file (timestamp_filename_hash.pzs)
│   ├── 26_05_03_14_35_myscene_e5f6.pzs
│   └── 26_05_03_14_40_myscene_g7h8.pzs
├── project1_h9i0/
│   ├── 26_05_03_14_25_project1_j1k2.pzs
│   └── 26_05_03_14_30_project1_l3m4.pzs
└── test-scene_n5o6/
    └── 26_05_03_14_20_test-scene_p7q8.pzs

Scene Directory Naming

Scene directories follow the format: {filename_stem}_{4char_path_hash}

  • Filename stem: The original scene filename without extension (for human readability)

  • Path hash: 4-character hexadecimal MD5 hash of the absolute file path (for uniqueness)

This ensures that the same scene file at the same location always maps to the same backup directory, while different scenes (even with the same filename at different locations) get separate directories.

File Naming Convention

Backup files within each scene directory follow the format: yy_mm_dd_hh_mm_filename_hash.pzs

  • Timestamp: yy_mm_dd_hh_mm - Year, month, day, hour, minute (24-hour format)

  • Original filename: The original scene filename without extension

  • Hash: 4-character hexadecimal MD5 hash to prevent intra-minute collisions

Examples: - 26_05_03_14_30_myscene_a1b2.pzs - Backup of “myscene.pzs” at 14:30 - 26_05_03_09_15_project_v2_c3d4.pzs - Backup of “project_v2.pzs” at 09:15

Rotation and Expiration

  • Rotation: Each scene directory independently keeps its last max_backups files. When a new backup is created, the oldest excess files in that directory are deleted.

  • Expiration: Scene directories are checked on backup creation and at startup. Directories whose mtime is older than expire_days are deleted entirely. Empty directories (no *.pzs files) are also cleaned up.

Configuration

Autosave behavior can be configured through:

  1. Command-line arguments: - --autosave-interval MINUTES: Set autosave interval in minutes - --autosave-max-backups COUNT: Set maximum backups kept per scene - --backup-expire-days DAYS: Set expiration period for inactive scene directories - --no-autosave: Disable autosave

  2. JSON configuration file:

    {
        "autosave": {
            "enabled": true,
            "interval": 300,
            "max_backups": 20,
            "backup_dir": "~/.local/share/zooui/backups",
            "expire_days": 7
        }
    }
    
  3. UI Settings menu: Settings → Autosave Settings

Default Settings

  • Enabled: True (enabled by default from application start)

  • Interval: 300 seconds (5 minutes)

  • Max backups: 20 backups per scene

  • Expire days: 7 days (inactive scene directories are deleted)

  • Backup location: ~/.local/share/zooui/backups/

Usage Flow

  1. Application start: Autosave is enabled by default. Expired backup directories are cleaned up.

  2. First save: A per-scene backup directory is created (named {scene_filename}_{hash}), and the first backup is stored inside it.

  3. Periodic backups: Additional backups are created at configured intervals in the scene’s directory.

  4. File rotation: When the maximum backup count per scene is reached, oldest backups within that directory are deleted.

  5. Expiration: Backup directories for scenes not saved for expire_days days are deleted.

  6. Shutdown migration: Legacy flat *.pzs files from the old backup system are cleaned up on application shutdown.

Error Handling

The backup system includes comprehensive error handling:

  • Permission errors: Notifies user if backup directory cannot be created

  • Disk space errors: Warns user if disk is full

  • File access errors: Gracefully handles locked or inaccessible files

All errors are displayed to the user via Qt message boxes without blocking application operation.

API Reference

BackupManager

Backup manager for automatic scene file backups with per-scene directories.

class zooui.backup.backupmanager.BackupManager(config)[source]

Bases: object

Constructor :

BackupManager(config: Dict[str, Any])

Parameters :
configDict[str, Any]

Configuration dictionary with autosave settings

BackupManager(config) –> None

Backup manager that creates per-scene backup directories under the XDG data directory with naming convention: {scene_filename}_{4char_path_hash}/

Each scene directory contains backups named: yy_mm_dd_hh_mm_filename_hash.pzs

Rotation is per-scene (keeps last N backups per scene). Scene directories expire after a configurable number of days since the last backup was written.

Method :

BackupManager.__init__(config)

Parameters :
configDict[str, Any]

Configuration dictionary

BackupManager.__init__(config) –> None

Initialize backup manager with configuration.

Parameters:

config – Configuration dictionary with ‘max_backups’ (default: 20), ‘expire_days’ (default: 7), ‘backup_dir’ (default: XDG data dir / backups)

_get_scene_dir(source_path)[source]

Get the per-scene backup directory for a given source file.

Directory name format: {filename_stem}_{4char_hash_of_absolute_path}

Parameters:

source_path – Path to the scene file

Returns:

Per-scene backup directory path

Return type:

Path

_ensure_backup_dir()[source]

Ensure backup root directory exists.

_show_error_dialog(message)[source]

Show error dialog without parent widget.

_generate_backup_filename(source_path)[source]

Generate backup filename with format: yy_mm_dd_hh_mm_filename_hash.pzs

Parameters:

source_path – Path to original file

Returns:

Generated backup filename

Return type:

str

create_backup(source_path)[source]
Method :

BackupManager.create_backup(source_path)

Parameters :
source_pathstr

Path to the scene file to backup

BackupManager.create_backup(source_path) –> Optional[str]

Create a backup of the scene file in its per-scene directory. Rotation and expiration cleanup are triggered automatically.

Parameters:

source_path – Path to the scene file to backup

Returns:

Path to created backup file, or None if failed

Return type:

Optional[str]

_rotate_backups(scene_dir)[source]

Keep only the last N backups within a scene directory, delete oldest ones.

Parameters:

scene_dir – The per-scene backup directory to rotate

_cleanup_expired()[source]

Delete expired backup scene directories.

Directories are considered expired if their mtime is older than expire_days (default 7). Empty or non-backup directories are also cleaned up.

Returns:

Number of directories deleted

Return type:

int

cleanup_expired_dirs()[source]
Method :

BackupManager.cleanup_expired_dirs()

Parameters :

None

BackupManager.cleanup_expired_dirs() –> int

Public method to delete expired backup scene directories.

Returns:

Number of directories deleted

Return type:

int

cleanup_flat_backups()[source]
Method :

BackupManager.cleanup_flat_backups()

Parameters :

None

BackupManager.cleanup_flat_backups() –> int

Delete legacy flat backup files from the root backup directory. Used for migrating from the old flat structure to the new per-scene directory structure. Only deletes *.pzs files directly in the backup root (not in subdirectories).

Returns:

Number of flat backup files deleted

Return type:

int

get_backup_count(source_path=None)[source]
Method :

BackupManager.get_backup_count(source_path=None)

Parameters :
source_pathOptional[str]

Path to the scene file (counts backups for specific scene), or None (counts backups across all scenes)

BackupManager.get_backup_count(source_path=None) –> int

Get current number of backup files.

Parameters:

source_path – Optional scene path to count backups for a specific scene. If None, counts all backups across all scenes.

Returns:

Number of backup files

Return type:

int

list_backups(source_path=None)[source]
Method :

BackupManager.list_backups(source_path=None)

Parameters :
source_pathOptional[str]

Path to the scene file (lists backups for specific scene), or None (lists backups across all scenes)

BackupManager.list_backups(source_path=None) –> List[str]

List all backup files sorted by modification time (newest first).

Parameters:

source_path – Optional scene path to list backups for a specific scene. If None, lists all backups across all scenes.

Returns:

List of backup file paths relative to backup root

Return type:

List[str]

cleanup_all()[source]
Method :

BackupManager.cleanup_all()

Parameters :

None

BackupManager.cleanup_all() –> int

Delete all backup files and directories.

Returns:

Number of items (files + directories) deleted

Return type:

int

Main Methods

  • create_backup(scene_path: str) -> Optional[str]: Create backup of scene file in its per-scene directory. Returns backup path. Triggers rotation and expiration cleanup.

  • _get_scene_dir(source_path: str) -> Path: Get per-scene backup directory path from source file path

  • _rotate_backups(scene_dir: Path) -> None: Rotate backups within a scene directory

  • _cleanup_expired() -> int: Delete expired backup directories

  • cleanup_expired_dirs() -> int: Public method to delete expired backup directories

  • cleanup_flat_backups() -> int: Delete legacy flat backup files (migration)

  • get_backup_count(source_path=None) -> int: Get backup count (per scene or global)

  • list_backups(source_path=None) -> List[str]: List backup files (per scene or global)

  • cleanup_all() -> int: Delete all backup files and directories

SceneAutosaveManager

SceneAutosaveManager - Autosave functionality for Scene class.

This class manages automatic backup creation for scene files with configurable interval and rotation. It was extracted from the Scene class to improve modularity and maintainability.

zooui.objects.scene.sceneutils.autosave.stop_timer_safely(timer)[source]

Safely stop a QTimer, ensuring thread safety.

This function ensures that QTimer.stop() is called from the same thread that created the timer, preventing Qt threading errors.

Parameters:

timer – QTimer instance to stop, or None

class zooui.objects.scene.sceneutils.autosave.SceneAutosaveManager(scene, config=None)[source]

Bases: object

Manager for scene autosave functionality.

This class handles automatic backup creation for scene files with configurable interval and rotation. It manages the backup timer, configuration, and integration with the BackupManager.

_scene

Reference to the parent Scene object

_logger

Logger instance for autosave operations

_autosave_enabled

Whether autosave is enabled

_autosave_interval

Autosave interval in seconds

_autosave_config

Autosave configuration dictionary

_autosave_timer

QTimer for periodic autosave

_autosave_active

Whether autosave timer is active

_backup_manager

BackupManager instance for creating backups

_last_save_path

Path of last saved scene file

Initialize SceneAutosaveManager.

Parameters:
  • scene – Parent Scene object

  • config – Optional configuration dictionary with autosave settings

_enable_autosave_if_configured()[source]

Enable autosave if configured in settings.

Creates backup manager, runs expiration cleanup, and starts autosave timer.

_trigger_autosave_backup(filename)[source]

Trigger autosave backup creation via BackupManager.

Parameters:

filename – Path to the scene file to backup

_autosave_timeout()[source]

Timer callback for periodic autosave.

Called when autosave timer expires to create a backup.

enable_autosave(interval_minutes)[source]

Enable autosave with specified interval.

Starts QTimer for periodic autosave.

Parameters:

interval_minutes – Autosave interval in minutes (minimum 1)

cleanup_legacy_backups()[source]

Clean up legacy flat backup files from old backup structure.

Called during application shutdown to migrate from the old flat backup system to the new per-scene directory structure.

Returns:

Number of legacy backup files deleted

Return type:

int

disable_autosave()[source]

Disable autosave and stop timer safely.

Idempotent: if autosave is already disabled, this is a no-op.

is_autosave_enabled()[source]

Check if autosave is currently enabled.

Returns:

True if autosave is enabled, False otherwise

get_autosave_interval()[source]

Get current autosave interval in seconds.

Returns:

Autosave interval in seconds

get_autosave_config()[source]

Get autosave configuration.

Returns:

Dictionary with autosave configuration

set_autosave_config(config)[source]

Update autosave configuration.

Parameters:

config – Dictionary with autosave configuration updates

update_last_save_path(filename)[source]

Update the last save path and trigger autosave if enabled.

Parameters:

filename – Path to the saved scene file

UI Integration

The backup system is accessible through:

  • Settings menu: Settings → Autosave Settings dialog

  • Configuration: User preferences stored in ~/.config/zooui/config.json

Testing

The backup system includes comprehensive tests:

  • Unit tests: test/unittest/backup/test_backupmanager.py (30 tests)

  • Scene integration tests: test/unittest/objects/scene/test_scene_autosave.py

  • UI tests: test/unittest/windows/dialogwindows/test_autosavesettingsdialog.py

  • Integration tests: test/integrationtest/test_autosave_integration.py

Example Usage

from zooui.backup.backupmanager import BackupManager

# Create backup manager with custom configuration
config = {
    "max_backups": 10,
    "expire_days": 14,
    "enabled": True,
    "interval": 60
}
backup_manager = BackupManager(config)

# Create backup of scene file
backup_path = backup_manager.create_backup("/path/to/scene.pzs")

if backup_path:
    print(f"Backup created successfully: {backup_path}")

    # List backups for this scene
    backups = backup_manager.list_backups("/path/to/scene.pzs")
    print(f"Total backups for this scene: {len(backups)}")
    for backup in backups[:5]:
        print(f"  - {backup}")

    # Check backup count for specific scene
    count = backup_manager.get_backup_count("/path/to/scene.pzs")
    print(f"Backups for scene: {count}")

    # Clean up expired directories
    expired = backup_manager.cleanup_expired_dirs()
    print(f"Cleaned up {expired} expired directories")
else:
    print("Backup failed")

Command-Line Examples

# Default: autosave enabled, 5-minute interval, keep 20 backups, 7-day expiration
python main.py

# 1-minute interval, keep 50 backups per scene, 14-day expiration
python main.py --autosave-interval 1 --autosave-max-backups 50 --backup-expire-days 14

# Disable autosave
python main.py --no-autosave

# Configure via settings file
python main.py --config zooui_config_example.json

# 10-minute interval only (uses defaults for other settings)
python main.py --autosave-interval 10

# Keep backups for 30 days before expiration
python main.py --backup-expire-days 30