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