Skip to content

Instantly share code, notes, and snippets.

@alensiljak
Last active May 20, 2019 14:35
Show Gist options
  • Save alensiljak/260f45fbcd7cec00acaa5284811eb41b to your computer and use it in GitHub Desktop.
Save alensiljak/260f45fbcd7cec00acaa5284811eb41b to your computer and use it in GitHub Desktop.
GnuCash Book Backup
"""
This will backup the current gnucash book, which is expected to be in the same folder. The copy will be stored in the "backup" sub-directory. The copied file will be LZMA-compressed to reduce the storage size.
Only a pre-set number of backup files will be kept in the backup directory. The oldest ones will be deleted when a new one is created.
The script will also delete *.log files in the current working directory as these are redundant.
To adapt the script to your needs, modify the variables at the top of the file.
Future additions may include the functionality to keep files created in the last n days, instead of the
fixed number of files.
"""
import os
import logging
import shutil
from datetime import datetime
#################################################################
# Modify these variables to adapt the script to your environment.
BOOK = "alen.sql.gnucash"
FILES_TO_KEEP = 50
# DAYS_TO_KEEP = 30
################
# Logging
logging.basicConfig(level=logging.INFO)
def assemble_dest_path(book_name: str, dest_folder: str):
""" Create destination file path """
# Get current date and time.
today_iso = datetime.today().strftime("%Y%m%d-%H%M%S")
filename_parts = os.path.splitext(book_name)
file_name = filename_parts[0]
ext = filename_parts[1]
dest_filename = file_name + "." + today_iso + ext
dest_path = os.path.join(dest_folder, dest_filename)
return dest_path
def clean_up_log_files():
""" Clean up the .log files in the main directory. """
all_files = os.listdir(".")
for filename in all_files:
if filename.endswith(".log"):
to_delete = "./" + filename
logging.info("deleting %s", filename)
os.remove(to_delete)
def backup_book(current_filename: str, dest_path: str):
""" Backup the book """
src = os.path.join(".", current_filename)
logging.info(f"Copying {src} to {dest_path}")
# shutil.copyfile(src, dest)
# Compress on the fly.
compress_file(src, dest_path)
def compress_file(source_path: str, dest_path: str):
""" Compress the backup file """
import lzma
# read the db file
with open(source_path, "rb") as db_file:
content = db_file.read()
# save compressed file
destination = dest_path + ".xz"
with lzma.open(destination, "w") as compressed_file:
compressed_file.write(content)
def prune_backups(dest_folder: str):
""" Delete backups, leave only the latest n (FILES_TO_KEEP) """
all_backups = os.listdir("./backup")
# all_backups.sort(key=lambda x: x, reverse=True)
all_backups.sort()
# How many to delete?
count = len(all_backups) - FILES_TO_KEEP
to_delete = all_backups[:count]
for file_to_delete in to_delete:
dest_path = os.path.join(dest_folder, file_to_delete)
logging.info("deleting %s", dest_path)
os.remove(dest_path)
def main():
""" entry point """
clean_up_log_files()
dest_folder = os.path.relpath("./backup")
dest = assemble_dest_path(BOOK, dest_folder)
# copy book
backup_book(BOOK, dest)
# prune the destination. Leave only the latest 20 files.
prune_backups(dest_folder)
######################################
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment