Source code for tools.project.incrementalArchive
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
""" Module incrementalArchive: This module ... """
import logging
# noinspection PyUnresolvedReferences
import mari
import os
import traceback
from PySide.QtGui import QMessageBox, QFileDialog, QApplication
from stkMariTools.lib.ui_utils import MariToolsMenuItem
[docs]class IncrementalArchiveMenuItem(MariToolsMenuItem):
"""
This class adds a Clear History action.
"""
logger = logging.getLogger(__name__)
def __init__(self):
"""
The constructor.
:return:
"""
super(IncrementalArchiveMenuItem, self).__init__()
# Set the class instance
mari.IncrementalArchiveMenuItem = self
# Action item is to be called in the Menu
self.actionIdentifier = 'Archive current project (incremental)'
# Python command to be run when action is executed from the Menu
self.actionCommand = 'mari.IncrementalArchiveMenuItem.archiveProject()'
# Path to the action in the Mari Menu
self.actionPath = 'MainWindow/&Scripts/&Project/&Archives'
# Icon to use for the action
self.actionIcon = 'Folder'
# Define metadata common attributes
self.archive_location_metaId = 'archiveSavePath'
self.archive_location_metaDescription = 'This attribute defines a common location ' \
'where the archive is to be saved to ' \
'on the local filesystem.'
self.archive_version_metaId = 'archiveVersion'
self.archive_version_metaDescription = 'This attribute defines the current archive version.'
# Define amount of padding to use when formatting version strings
self.version_padding = 3
# Add the item to the Mari Menu
self.addMariToolsMenuItem()
[docs] def archiveProject(self):
"""
This method incrementally archives the current project.
:return:
"""
current_project = mari.projects.current()
if not current_project:
self.logger.error('### No current project to archive could be returned!!!')
mari.utils.message(
text='You need to have a project open first in order to archive it!',
title='No project currently open!',
icon=QMessageBox.Icon.Warning,
)
return
# Store reference to UUID for being able to recall once project has been closed
project_uuid = current_project.uuid()
# Check for existing metadata item on the project and create it if necessary
if not (
current_project.hasMetadata(self.archive_location_metaId) and
current_project.hasMetadata(self.archive_version_metaId)
):
# Display user prompt to choose where to archive files
res = mari.utils.messageResult(
text='This project does not have a location specified for saving '
'archives to, would you like to create a default one?',
title='No archive location found!',
buttons=QMessageBox.StandardButton.Ok|QMessageBox.StandardButton.Cancel,
icon=QMessageBox.Icon.Information,
details='The location that you choose will be saved as additional'
'metadata into the project, so that you only need to do this once.'
)
if res == QMessageBox.Ok:
mari_user_directory = os.path.abspath(
mari.resources.path(mari.resources.USER)
)
# Create a default location for saving the archives to
default_archive_location = os.path.join(
mari_user_directory,
'Archives',
current_project.name()
)
if not os.path.isdir(default_archive_location):
os.makedirs(default_archive_location)
self.logger.debug('Default archive location set at: {0}'
.format(default_archive_location))
activeWindow = QApplication.activeWindow()
# Show prompt for user to choose archive location from
archive_location = QFileDialog.getExistingDirectory(
parent=activeWindow,
caption='Select a location to save the archives',
dir=default_archive_location,
options=QFileDialog.ShowDirsOnly
)
if not archive_location:
return
# Set base version number
archive_version = 0
if os.path.isdir(archive_location):
# Set the metadata
current_project.setMetadata(
self.archive_location_metaId, archive_location)
current_project.setMetadata(
self.archive_version_metaId, archive_version)
else:
self.logger.error('### The chosen location: {0} is not a '
'valid directory!!!'
.format(archive_location))
mari.utils.message(
text='{0}\nis not a valid directory!'.format(archive_location),
title='Invalid directory!',
icon=QMessageBox.Icon.Critical
)
return IOError
else:
self.logger.debug('User cancelled operation, aborting!')
return
# Archive the project to the location defined
# in metadata and increment version number
archive_location = current_project.metadata(self.archive_location_metaId)
archive_version = str(
int(current_project.metadata(self.archive_version_metaId)) + 1
)
# Format location to save the archive to
archive_location_dir = os.path.join(
archive_location,
'v'+archive_version.zfill(self.version_padding)
)
archive_location_path = os.path.join(
archive_location_dir,
current_project.name()+'.mra'
)
# Create location to save to
if not os.path.isdir(archive_location_dir):
self.logger.debug('Directories being created for location '
'since they do not already exist...')
os.makedirs(archive_location_dir)
self.logger.debug('Attempting to archive project at: {0}'
.format(archive_location_path))
# Clear the project history and
try: mari.history.clear(False)
except RuntimeError:
self.logger.error('### Could not clear the history because no project was open,'
'or the current project requires saving!!!\n{0}'
.format(traceback.print_exc()))
mari.utils.message(
text='Cannot archive the project! Ran into an issue while trying to clear'
'the project history!',
title='Runtime error!',
icon=QMessageBox.Icon.Critical,
details=traceback.print_exc()
)
return
# perform memory garbage collection
mari.ddi.garbageCollect()
mari.ddi.clearMemoryCache()
self.logger.debug('Saving project...')
# Save the project first
current_project.save()
# Close current project before archiving for successful operation
# Note: explicitly using ``confirm_if_modified flag`` in API fails
current_project.close(False)
try:
mari.projects.archive(project_uuid, archive_location_path)
self.logger.info('Successfully archived project!')
except RuntimeError:
self.logger.error('### User cancelled the operation/Project was '
'open while attempting to archive!!!'
.format(traceback.print_exc()))
mari.utils.message(
text='Cannot archive the project! Is it still open?',
title='Runtime error!',
icon=QMessageBox.Icon.Critical
)
raise RuntimeError
except ValueError:
self.logger.error('### Invalid project specified for archiving!!!'
.format(traceback.print_exc()))
mari.utils.message(
text='Invalid project was specified for archiving!',
title='Invalid project!',
icon=QMessageBox.Icon.Critical
)
raise ValueError
except IOError:
self.logger.error('### Error occurred while writing archive to disk!!!'
.format(traceback.print_exc()))
mari.utils.message(
text='Error occurred while trying to write the archive to disk!',
title='IO Error!',
icon=QMessageBox.Icon.Critical,
details=traceback.print_exc()
)
raise IOError
# Re-open project after successful archive
mari.projects.open(project_uuid)
self.logger.info('Incrementing current version of project...')
# Update metadata for the version of the project
mari.projects.current().setMetadata(
self.archive_version_metaId,
archive_version
)
# Save project again to store metadata changes
mari.projects.save()
self.logger.info('Successfully performed incremental archive save operation!')