mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-14 11:56:05 +08:00
Start implementing backups functionality
This commit is contained in:
parent
32e2723c26
commit
64819d517e
@ -10,8 +10,10 @@ class Backups:
|
|||||||
The backups API provides a version-proof bridge between Cura's BackupManager and plugins that hook into it.
|
The backups API provides a version-proof bridge between Cura's BackupManager and plugins that hook into it.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
cura.Api.backups.createBackup()
|
from cura.Api import CuraApi
|
||||||
cura.Api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})
|
api = CuraApi()
|
||||||
|
api.backups.createBackup()
|
||||||
|
api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})
|
||||||
"""
|
"""
|
||||||
|
|
||||||
manager = BackupsManager() # Re-used instance of the backups manager.
|
manager = BackupsManager() # Re-used instance of the backups manager.
|
||||||
@ -19,9 +21,14 @@ class Backups:
|
|||||||
def createBackup(self) -> ("ZipFile", dict):
|
def createBackup(self) -> ("ZipFile", dict):
|
||||||
"""
|
"""
|
||||||
Create a new backup using the BackupsManager.
|
Create a new backup using the BackupsManager.
|
||||||
:return:
|
:return: Tuple containing a ZIP file with the backup data and a dict with meta data about the backup.
|
||||||
"""
|
"""
|
||||||
return self.manager.createBackup()
|
return self.manager.createBackup()
|
||||||
|
|
||||||
def restoreBackup(self, zip_file: "ZipFile", meta_data: dict) -> None:
|
def restoreBackup(self, zip_file: "ZipFile", meta_data: dict) -> None:
|
||||||
|
"""
|
||||||
|
Restore a backup using the BackupManager.
|
||||||
|
:param zip_file: A ZIP file containing the actual backup data.
|
||||||
|
:param meta_data: Some meta data needed for restoring a backup, like the Cura version number.
|
||||||
|
"""
|
||||||
return self.manager.restoreBackup(zip_file, meta_data)
|
return self.manager.restoreBackup(zip_file, meta_data)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
from UM.PluginRegistry import PluginRegistry
|
||||||
from cura.Api.Backups import Backups
|
from cura.Api.Backups import Backups
|
||||||
|
|
||||||
|
|
||||||
@ -11,5 +12,8 @@ class CuraApi:
|
|||||||
Usage of any other methods than the ones provided in this API can cause plugins to be unstable.
|
Usage of any other methods than the ones provided in this API can cause plugins to be unstable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# For now we use the same API version to be consistent.
|
||||||
|
VERSION = PluginRegistry.APIVersion
|
||||||
|
|
||||||
# Backups API.
|
# Backups API.
|
||||||
backups = Backups()
|
backups = Backups()
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile
|
||||||
|
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from UM.Resources import Resources
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
|
|
||||||
class Backup:
|
class Backup:
|
||||||
@ -8,22 +16,67 @@ class Backup:
|
|||||||
It is also responsible for reading and writing the zip file to the user data folder.
|
It is also responsible for reading and writing the zip file to the user data folder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, zip_file: "ZipFile" = None, meta_data: dict = None):
|
||||||
self.generated = False # type: bool
|
self.zip_file = zip_file # type: Optional[ZipFile]
|
||||||
self.backup_id = None # type: str
|
self.meta_data = meta_data # type: Optional[dict
|
||||||
self.target_cura_version = None # type: str
|
|
||||||
self.zip_file = None
|
|
||||||
self.meta_data = None # type: dict
|
|
||||||
|
|
||||||
def getZipFile(self):
|
def makeFromCurrent(self) -> (bool, Optional[str]):
|
||||||
pass
|
"""
|
||||||
|
Create a backup from the current user config folder.
|
||||||
|
"""
|
||||||
|
cura_release = CuraApplication.getInstance().getVersion()
|
||||||
|
version_data_dir = Resources.getDataStoragePath()
|
||||||
|
timestamp = datetime.now().isoformat()
|
||||||
|
|
||||||
def getMetaData(self):
|
Logger.log("d", "Creating backup for Cura %s, using folder %s", cura_release, version_data_dir)
|
||||||
pass
|
|
||||||
|
|
||||||
def create(self):
|
# We're using an easy to parse filename for when we're restoring edge cases:
|
||||||
self.generated = True
|
# TIMESTAMP.backup.VERSION.cura.zip
|
||||||
pass
|
archive = self._makeArchive("{}.backup.{}.cura.zip".format(timestamp, cura_release), version_data_dir)
|
||||||
|
|
||||||
def restore(self):
|
self.zip_file = archive
|
||||||
pass
|
self.meta_data = {
|
||||||
|
"cura_release": cura_release
|
||||||
|
}
|
||||||
|
# TODO: fill meta data with machine/material/etc counts.
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _makeArchive(root_path: str, archive_name: str) -> Optional[ZipFile]:
|
||||||
|
"""
|
||||||
|
Make a full archive from the given root path with the given name.
|
||||||
|
:param root_path: The root directory to archive recursively.
|
||||||
|
:param archive_name: The name of the archive to create.
|
||||||
|
:return: The archive as ZipFile.
|
||||||
|
"""
|
||||||
|
parent_folder = os.path.dirname(root_path)
|
||||||
|
contents = os.walk(root_path)
|
||||||
|
try:
|
||||||
|
archive = ZipFile(archive_name, "w", ZIP_DEFLATED)
|
||||||
|
for root, folders, files in contents:
|
||||||
|
for folder_name in folders:
|
||||||
|
# Add all folders, even empty ones.
|
||||||
|
absolute_path = os.path.join(root, folder_name)
|
||||||
|
relative_path = absolute_path.replace(parent_folder + '\\', '')
|
||||||
|
archive.write(absolute_path, relative_path)
|
||||||
|
for file_name in files:
|
||||||
|
# Add all files.
|
||||||
|
absolute_path = os.path.join(root, file_name)
|
||||||
|
relative_path = absolute_path.replace(parent_folder + '\\', '')
|
||||||
|
archive.write(absolute_path, relative_path)
|
||||||
|
archive.close()
|
||||||
|
return archive
|
||||||
|
except (IOError, OSError, BadZipfile) as error:
|
||||||
|
Logger.log("e", "Could not create archive from user data directory: %s", error)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def restore(self) -> None:
|
||||||
|
"""
|
||||||
|
Restore this backup.
|
||||||
|
"""
|
||||||
|
if not self.zip_file or not self.meta_data or not self.meta_data.get("cura_release", None):
|
||||||
|
# We can restore without the minimum required information.
|
||||||
|
Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# global_data_dir = os.path.dirname(version_data_dir)
|
||||||
|
# TODO: restore logic.
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from cura.Backups.Backup import Backup
|
||||||
|
|
||||||
|
|
||||||
class BackupsManager:
|
class BackupsManager:
|
||||||
"""
|
"""
|
||||||
@ -9,15 +12,17 @@ class BackupsManager:
|
|||||||
Backups themselves are represented in a different class.
|
Backups themselves are represented in a different class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def createBackup(self) -> ("ZipFile", dict):
|
def createBackup(self) -> ("ZipFile", dict):
|
||||||
"""
|
"""
|
||||||
Get a backup of the current configuration.
|
Get a backup of the current configuration.
|
||||||
:return: A Tuple containing a ZipFile (the actual backup) and a dict containing some meta data (like version).
|
:return: A Tuple containing a ZipFile (the actual backup) and a dict containing some meta data (like version).
|
||||||
"""
|
"""
|
||||||
pass
|
self._disableAutoSave()
|
||||||
|
backup = Backup()
|
||||||
|
backup.makeFromCurrent()
|
||||||
|
self._enableAutoSave()
|
||||||
|
# We don't return a Backup here because we want plugins only to interact with our API and not full objects.
|
||||||
|
return backup.zip_file, backup.meta_data
|
||||||
|
|
||||||
def restoreBackup(self, zip_file: "ZipFile", meta_data: dict) -> None:
|
def restoreBackup(self, zip_file: "ZipFile", meta_data: dict) -> None:
|
||||||
"""
|
"""
|
||||||
@ -25,4 +30,22 @@ class BackupsManager:
|
|||||||
:param zip_file: A ZipFile containing the actual backup.
|
:param zip_file: A ZipFile containing the actual backup.
|
||||||
:param meta_data: A dict containing some meta data that is needed to restore the backup correctly.
|
:param meta_data: A dict containing some meta data that is needed to restore the backup correctly.
|
||||||
"""
|
"""
|
||||||
pass
|
if not meta_data.get("cura_release", None):
|
||||||
|
# If there is no "cura_release" specified in the meta data, we don't execute a backup restore.
|
||||||
|
Logger.log("w", "Tried to restore a backup without specifying a Cura version number.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO: first make a new backup to prevent data loss when restoring fails.
|
||||||
|
|
||||||
|
self._disableAutoSave()
|
||||||
|
|
||||||
|
backup = Backup(zip_file = zip_file, meta_data = meta_data)
|
||||||
|
backup.restore() # At this point, Cura will need to restart for the changes to take effect
|
||||||
|
|
||||||
|
def _disableAutoSave(self):
|
||||||
|
"""Here we try to disable the auto-save plugin as it might interfere with restoring a backup."""
|
||||||
|
# TODO: Disable auto-save if possible.
|
||||||
|
|
||||||
|
def _enableAutoSave(self):
|
||||||
|
"""Re-enable auto-save after we're done."""
|
||||||
|
# TODO: Enable auto-save if possible.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user