Versioned Backup Files
Posted: Thu May 16, 2013 12:07 am
The code below should be saved to the versions.py file in the following Zeus zScript folder:
For details on how to use the script refer to the comments found in the macro itself.
The code represents the latest iteration of this versions.py file and it includes changes from the discussion thread found below.
Code: Select all
C:\Users\<user Id>\AppData\Local\Programs\Zeus\zScript\versions.py
The code represents the latest iteration of this versions.py file and it includes changes from the discussion thread found below.
Code: Select all
##############################################################################################################################################
#
# Name: Versioned Backup Files Macro
#
# Original Author: Jussi Jumppanen
#
# Current Author: WilsonDevo (Modified) (this version)
#
# Language: Python Macro (V3.x - Does not work with Python V2.x)
#
# Description: This macro creates a time stamped backup of the file every time the file is saved.
#
# To configure this macro, for all document types, use the Options, Default Document Type menu
# and attach the macro to the "File Save Prefix" trigger option. (e.g.)
#
# Options|Default Document Type...|Triggers|File Save Prefix = $zud\zScript\versions.py
#
# To configure this macro, for a particular document type, use the Options, Trigger Options menu
# and attach the macro to the "File Save Prefix" trigger option. (e.g.)
#
# Options|Trigger Options...|File Save Prefix = $zud\zScript\versions.py
#
# The time stamped backups are created using one of six possible backup modes and are controlled by
# the 'BACKUP_MODE' and 'BACKUP_DIR' strings. The different backups modes are listed below using examples.
#
# The most fine grained backup directory location includes the week number of the month (BACKUP_MODE=01 or 02)
# or the day of the month (BACKUP_MODE=03 or 04) in an attempt to make managing the backups easier.
#
# For example, old backups beyond a certain age can be quickly identified and deleted using their directory name
# as an indication of their age rather than sifting through the time stamped backup files using either their
# filename or their file date as a selector. (e.g. I can quickly delete all Python (.py) time stamped backup files
# created in April 2017, because they are now redundant, just by deleting the '...\.py\2017\Apr\...' directory.)
#
# Being able to locate a particular time stamped backup file easily, in times of high stress, is also much easier with
# a logical hierarchical backup directory structure.
#
# (The simplest backup mode is BACKUP_MODE=8 but this means that the time stamped backups are created in a
# a sub-directory of the current file's directory. If the current file's directory is accidentally deleted then
# all of the time stamped backups will also have been deleted. Each backup scheme will have its own pros and cons, however.)
#
# By default the backups are written to this folder:
#
# C:\Users\<User Id>\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\
#
##############################################################################################################################################
# (e.g.)
#
# Original file 1: C:\Users\frank\work\ProjectProposal
# Original file 2: C:\Users\frank\play\ShoppingList.txt
#
##############################################################################################################################################
# BACKUP_MODE = "MODE_01"
# BACKUP_DIR = "C:\\Users\\" + current_user + "\\EDITOR_BACKUPS\\ZEUS_BACKUPS\\TIME_STAMPED\\"
#
# Time-stamped backup 1: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\(no ext)\2017\May\01\ProjectProposal__03-May-2017__094512 (i.e. week 01 of month)
# Time-stamped backup 2: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\.txt\2017\Jun\03\ShoppingList__17-Jun-2017__081623.txt (i.e. week 03 of month)
##############################################################################################################################################
# BACKUP_MODE = "MODE_02"
# BACKUP_DIR = "C:\\Users\\" + current_user + "\\EDITOR_BACKUPS\\ZEUS_BACKUPS\\TIME_STAMPED\\"
#
# Time-stamped backup 1: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\(no ext)\2017\05\01\ProjectProposal__2017-05-03__094512 (i.e. week 01 of month)
# Time-stamped backup 2: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\.txt\2017\06\03\ShoppingList__2017-06-17__081623.txt (i.e. week 03 of month)
##############################################################################################################################################
# BACKUP_MODE = "MODE_03"
# BACKUP_DIR = "C:\\Users\\" + current_user + "\\EDITOR_BACKUPS\\ZEUS_BACKUPS\\TIME_STAMPED\\"
#
# Time-stamped backup 1: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\(no ext)\2017\May\03\ProjectProposal__03-May-2017__094512 (i.e. day 03 of month)
# Time-stamped backup 2: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\.txt\2017\Jun\17\ShoppingList__17-Jun-2017__081623.txt (i.e. day 17 of month)
##############################################################################################################################################
# BACKUP_MODE = "MODE_04"
# BACKUP_DIR = "C:\\Users\\" + current_user + "\\EDITOR_BACKUPS\\ZEUS_BACKUPS\\TIME_STAMPED\\"
#
# Time-stamped backup 1: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\(no ext)\2017\05\03\ProjectProposal__2017-05-03__094512 (i.e. day 03 of month)
# Time-stamped backup 2: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\.txt\2017\06\17\ShoppingList__2017-06-17__081623.txt (i.e. day 17 of month)
##############################################################################################################################################
# BACKUP_MODE = "MODE_05"
# BACKUP_DIR = "C:\\Users\\" + current_user + "\\EDITOR_BACKUPS\\ZEUS_BACKUPS\\TIME_STAMPED\\"
#
# Time-stamped backup 1: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\(no ext)\2017\May\ProjectProposal__03-May-2017__094512
# Time-stamped backup 2: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\.txt\2017\Jun\ShoppingList__17-Jun-2017__081623.txt
##############################################################################################################################################
# BACKUP_MODE = "MODE_06"
# BACKUP_DIR = "C:\\Users\\" + current_user + "\\EDITOR_BACKUPS\\ZEUS_BACKUPS\\TIME_STAMPED\\"
#
# Time-stamped backup 1: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\(no ext)\2017\05\ProjectProposal__2017-05-03__094512
# Time-stamped backup 2: C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\TIME_STAMPED\.txt\2017\06\ShoppingList__2017-06-17__081623.txt
##############################################################################################################################################
# BACKUP_MODE = "MODE_07"
# BACKUP_DIR = "!version\\"
#
# Time-stamped backup 1: C:\Users\frank\work\!version\(no ext)\ProjectProposal__2017-05-03__094512
# Time-stamped backup 2: C:\Users\frank\play\!version\.txt\ShoppingList__17-Jun-2017__081623.txt
##############################################################################################################################################
# BACKUP_MODE = "MODE_08"
# BACKUP_DIR = "!version\\"
#
# Time-stamped backup 1: C:\Users\frank\work\!version\ProjectProposal__2017-05-03__094512
# Time-stamped backup 2: C:\Users\frank\play\!version\ShoppingList__17-Jun-2017__081623.txt
##############################################################################################################################################
# Non time-stamped backup 1 : C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\ProjectProposal
# Non time-stamped backup 2 : C:\Users\frank\EDITOR_BACKUPS\ZEUS_BACKUPS\ShoppingList.txt
#
# (created by Zeus itself - if it has been enabled e.g.)
#
# Options|Editor Options|Backup||Backup Mode = Use existing file name
# Options|Editor Options|Backup||Backup Details||Alternative backup directory = C:\Users\$UserID\EDITOR_BACKUPS\ZEUS_BACKUPS
#
# The non time-stamped backup, if enabled, provides an extra file backup and represents
# the most 'current backup'.
#
# See the code below for more info on configuring this backup scheme
#
##############################################################################################################################################
#
# NOTE: The time stamped backup IS created when a 'FileSave' operation is performed.
#
# NOTE: The time stamped backup IS NOT created when a 'FileSaveAll' or 'FileSaveAllNamed' operation is performed.
#
##############################################################################################################################################
#
# NOTE: This script over time could generate hundreds of time stamped backup files but since Zeus is often used to
# edit program code that exists in text format then the total hard disk space consumed should be manageable
# since individual program code files are generally not large in size.
#
##############################################################################################################################################
import os
import re
import sys
import zeus
import shutil
import getpass
def save_trigger():
# The backup mode (or scheme) to use for creating the time stamped backup file
BACKUP_MODE = "MODE_01"
if (BACKUP_MODE == "MODE_01") or (BACKUP_MODE == "MODE_02") or (BACKUP_MODE == "MODE_03") or \
(BACKUP_MODE == "MODE_04") or (BACKUP_MODE == "MODE_05") or (BACKUP_MODE == "MODE_06"):
# backup directory will be locted in current users folder
BACKUP_DIR = os.path.expanduser("~\\EDITOR_BACKUPS\\ZEUS_BACKUPS\\TIME_STAMPED\\")
elif (BACKUP_MODE == "MODE_07") or (BACKUP_MODE == "MODE_08"):
# backup directory will be locted off the directory of file
BACKUP_DIR = "!version\\"
else:
zeus.message_box(1, "BACKUP_MODE=" + BACKUP_MODE + " is not supported.\n\nBackup not created.")
return
# this macro expects to be passed at least two arguments
if zeus.argc() < 2: return
# ignore any non-document files
if not zeus.is_document(): return
# ignore any un-named files
if not zeus.is_named(): return
# useful for debugging
# debug_info = ""
# debug_info += "zeus_argv_0 : " + str(zeus.argv(0), 'utf-8') + "\n"
# debug_info += "zeus_argv_1 : " + str(zeus.argv(1), 'utf-8') + "\n"
# debug_info += "zeus_argv_2 : " + str(zeus.argv(2), 'utf-8') + "\n"
# debug_info += "zeus_argv_3 : " + str(zeus.argv(3), 'utf-8') + "\n"
# debug_info += "zeus_argv_4 : " + str(zeus.argv(4), 'utf-8') + "\n"
# debug_info += "zeus_argv_5 : " + str(zeus.argv(5), 'utf-8') + "\n"
# zeus.message_box(1, debug_info, "Arguments")
# look for the modified flag is passed in as the fifth argument
result = re.match("(Modified=(.*))", str(zeus.argv(1), 'utf-8'))
# debugging
# zeus.message_box(1, "result := " + str(result))
# this would be an unexpected result
if not result or not result.group(1): return
# debugging
# modified_1 = result.group(1)
# zeus.message_box(1, "modified_1 := " + modified_1)
# zeus.message_box(1, "sys.version := " + sys.version)
# get the modified flag
modified_2 = result.group(2)
# debugging
# zeus.message_box(1, "modified_2 := " + modified_2)
# ignore files that have not been modified
if modified_2 != "true": return
# trigger will pass in the full file name as the first argument
# strip out string 'FileName='
fullfilename = str(zeus.argv(4), 'utf-8').replace('FileName=', '')
# Zeus adds quotes for files containing spaces so remove those quotes
fullfilename = fullfilename.replace('"', '')
# don't backup the backup files
if fullfilename.lower().find(BACKUP_DIR.lower()) != -1:
return;
# split the full file into it's parts
current_dir, current_file = os.path.split(fullfilename)
# if the file does not exist assume this is a ftp file
if not os.path.exists(fullfilename):
# get the local temp file name used for remote files
result = re.match("(LocalFileName=(.*))", str(zeus.argv(2), 'utf-8'))
# this would be an unexpected result
if not result or not result.group(1): return
# use the local file name as the source
fullfilename = result.group(2)
# if the file does not exist something must have gone wrong
if not os.path.exists(fullfilename):
return;
# update the file directory details
current_dir, file_base = os.path.split(fullfilename)
# debugging
# zeus.message_box(1, "fullfilename := " + fullfilename)
zeus.message("Writing backup: " + current_file);
# split the file name into it's parts
file_base, file_extension = os.path.splitext(current_file)
# Check for the case when a file has no file extension
if file_extension == "":
file_extension_subst = "(no ext)"
else:
file_extension_subst = file_extension
# debugging
#zeus.message_box(1, "current_file := " + current_file)
#zeus.message_box(1, "file_extension := " + file_extension)
#zeus.message_box(1, "file_extension_subst := " + file_extension_subst)
# Convert the day of the month to an integer
day_of_week = int(zeus.macro_tag("$DT<dd>").decode('utf8'))
# Determine which week of the month it is
if day_of_week < 8:
week_of_month = "01"
elif day_of_week < 15:
week_of_month = "02"
elif day_of_week < 22:
week_of_month = "03"
else:
week_of_month = "04"
# Generate the directory path where the time stamped backup file will be created based on the chosen backup mode
if BACKUP_MODE == "MODE_01":
version_dir = BACKUP_DIR + file_extension_subst + "\\" + zeus.macro_tag("$DT<yyyy>").decode('utf-8') + "\\" + zeus.macro_tag("$DT<MMM>").decode('utf-8') + "\\Week - " + week_of_month + "\\"
elif BACKUP_MODE == "MODE_02":
version_dir = BACKUP_DIR + file_extension_subst + "\\" + zeus.macro_tag("$DT<yyyy>").decode('utf-8') + "\\" + zeus.macro_tag("$DT<MM>").decode('utf-8') + "\\Week - " + week_of_month + "\\"
if BACKUP_MODE == "MODE_03":
version_dir = BACKUP_DIR + file_extension_subst + "\\" + zeus.macro_tag("$DT<yyyy>").decode('utf-8') + "\\" + zeus.macro_tag("$DT<MMM>").decode('utf-8') + "\\" + zeus.macro_tag("$DT<dd>").decode('utf-8') + "\\"
elif BACKUP_MODE == "MODE_04":
version_dir = BACKUP_DIR + file_extension_subst + "\\" + zeus.macro_tag("$DT<yyyy>").decode('utf-8') + "\\" + zeus.macro_tag("$DT<MM>").decode('utf-8') + "\\" + zeus.macro_tag("$DT<dd>").decode('utf-8') + "\\"
elif BACKUP_MODE == "MODE_05":
version_dir = BACKUP_DIR + file_extension_subst + "\\" + zeus.macro_tag("$DT<yyyy>").decode('utf-8') + "\\" + zeus.macro_tag("$DT<MMM>").decode('utf-8') + "\\"
elif BACKUP_MODE == "MODE_06":
version_dir = BACKUP_DIR + file_extension_subst + "\\" + zeus.macro_tag("$DT<yyyy>").decode('utf-8') + "\\" + zeus.macro_tag("$DT<MM>").decode('utf-8') + "\\"
elif BACKUP_MODE == "MODE_07":
version_dir = os.path.join(current_dir, BACKUP_DIR + file_extension_subst + "\\")
elif BACKUP_MODE == "MODE_08":
version_dir = os.path.join(current_dir, BACKUP_DIR)
# debugging
# zeus.message_box(1, "version_dir := " + version_dir)
# zeus.message_box(1, "current_dir := " + current_dir + "\ncurrent_file := " + current_file + "\nfile_base := " + file_base + "\nfile_extension_subst := " + file_extension_subst)
try:
# make sure it exists
if not os.path.exists(version_dir): os.makedirs(version_dir)
# Generate the time stamped backup file name
if (BACKUP_MODE == "MODE_01") or (BACKUP_MODE == "MODE_03") or (BACKUP_MODE == "MODE_05"):
backup_filename = file_base + zeus.macro_tag("__$DT<dd-MMM-yyyy>__$T<HHmmss>").decode('utf-8') + file_extension
else:
backup_filename = file_base + zeus.macro_tag("__$DT<yyyy-MM-dd>__$T<HHmmss>").decode('utf-8') + file_extension
# the full backup file path
backup_filepath = os.path.join(version_dir, backup_filename)
# debugging
# zeus.message_box(1, "fullfilename := " + fullfilename + "\nbackup_filepath := " + backup_filepath)
# copy the original file to the backup location (i.e. create the time stamped backup file)
shutil.copy2(fullfilename, backup_filepath)
zeus.message("Backup complete.");
except Exception as e:
zeus.message_box(0, 'Versions Error:\n\n{}'.format(str(e)), 'Zeus IDE')
save_trigger() # run the macro