From 75b0f5c668e46ed74faefdfaba99aab2a2351821 Mon Sep 17 00:00:00 2001 From: Emilia Allison Date: Sun, 27 Oct 2024 13:40:22 -0400 Subject: [PATCH] Archive creation, minimal management --- src/.gitignore | 1 + src/__init__.py | 0 src/main.py | 65 ++++++++++++++++++++++++++++++++++++++ src/pictures/__init__.py | 0 src/pictures/_util.py | 46 +++++++++++++++++++++++++++ src/pictures/add.py | 28 ++++++++++++++++ src/pictures/create.py | 5 +++ src/requirements.txt | 2 ++ src/static_gen/__init__.py | 0 9 files changed, 147 insertions(+) create mode 100644 src/.gitignore create mode 100644 src/__init__.py create mode 100755 src/main.py create mode 100644 src/pictures/__init__.py create mode 100644 src/pictures/_util.py create mode 100644 src/pictures/add.py create mode 100644 src/pictures/create.py create mode 100644 src/requirements.txt create mode 100644 src/static_gen/__init__.py diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/main.py b/src/main.py new file mode 100755 index 0000000..1f53ea7 --- /dev/null +++ b/src/main.py @@ -0,0 +1,65 @@ +#!/bin/python3 + +import argparse + + +def main(): + parser = argparse.ArgumentParser() + + # Top Level Subcommands + subparsers = parser.add_subparsers(dest='command', + required=True) + + # Archive : Picture Library Management + add_archive_parser(subparsers) + + # Run + args = parser.parse_args() + args.func(args) + + +def add_archive_parser(top_level_parsers: argparse._SubParsersAction): + parser: argparse.ArgumentParser = top_level_parsers.add_parser("archive") + subsub = parser.add_subparsers(dest='archive', + required=True) + + add = subsub.add_parser("add") + add.add_argument("files", + help="files to add to archive", + default=[], + nargs="+") + add.add_argument("-a", "--archive", + help="path to archive", + dest="archive_path", + nargs=1, + required=True) + + add.add_argument("-p", "--path", + help="path inside archive where files will be placed", + dest="basepath", + default="") + add.set_defaults(func=run_archive_add) + + create = subsub.add_parser("create") + create.add_argument("-a", "--archive", + help="path to archive", + dest="archive_path", + nargs=1, + required=True) + create.set_defaults(func=run_archive_create) + + +def run_archive_add(args): + from pictures.add import add + add(archive_path=args.archive_path[0], + files_to_add=args.files, + basepath=args.basepath) + + +def run_archive_create(args): + from pictures.create import create + create(args.archive_path[0]) + + +if __name__ == "__main__": + main() diff --git a/src/pictures/__init__.py b/src/pictures/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/pictures/_util.py b/src/pictures/_util.py new file mode 100644 index 0000000..d6da9fd --- /dev/null +++ b/src/pictures/_util.py @@ -0,0 +1,46 @@ +# Util functions for picture archive + +from zipfile import ZipFile, is_zipfile, ZIP_LZMA +from os.path import isfile, basename + + +ARCHIVE_MARKER_FILE = "PICARCHIVE" + + +def check_valid_archive(path: str) -> bool: + if not isfile(path): + return False + if not is_zipfile(path): + return False + try: + with ZipFile(path, 'r') as zip: + if ARCHIVE_MARKER_FILE not in zip.namelist(): + return False + except Exception: + return False + + return True + + +def create_new_archive(path: str): + if check_valid_archive(path): + raise FileExistsError("Archive already exists") + if isfile(path): + raise FileExistsError("A file with that name already exists") + + with ZipFile(path, 'x', + compression=ZIP_LZMA, + allowZip64=True) as zip: + with zip.open(ARCHIVE_MARKER_FILE, 'w') as f: + f.write("horses".encode()) + + +def get_already_existing_files(path: str, files: [str]) -> [str]: + if not check_valid_archive(path): + raise ValueError("Archive is not valid") + + with ZipFile(path, 'r') as zip: + existing_files_set = {basename(p) for p in zip.namelist()} + + arg_files_set = {basename(p) for p in files} + return list(existing_files_set & arg_files_set) diff --git a/src/pictures/add.py b/src/pictures/add.py new file mode 100644 index 0000000..f4f6972 --- /dev/null +++ b/src/pictures/add.py @@ -0,0 +1,28 @@ +# Add function for archive +from pictures._util import check_valid_archive, get_already_existing_files +from zipfile import ZipFile +from os.path import join + + +def add(archive_path: str, files_to_add: [str], + basepath: str = "", + error_on_duplicate=False, + overwrite_duplicate=False): + if not check_valid_archive(archive_path): + raise ValueError("Archive is not valid") + + if overwrite_duplicate: + raise NotImplementedError() + + if error_on_duplicate and len( + e := get_already_existing_files(archive_path, files_to_add)) > 0: + raise Exception(f"Erroring on duplicate files: {e}") + + with ZipFile(archive_path, 'a') as zip: + for file in files_to_add: + basepath_corrected_path = join(basepath, file) + with ( + zip.open(basepath_corrected_path, 'w') as zf, + open(file, 'rb') as of + ): + zf.write(of.read()) diff --git a/src/pictures/create.py b/src/pictures/create.py new file mode 100644 index 0000000..519cf1e --- /dev/null +++ b/src/pictures/create.py @@ -0,0 +1,5 @@ +# Create new archive +from pictures._util import create_new_archive + +def create(path: str): + create_new_archive(path) diff --git a/src/requirements.txt b/src/requirements.txt new file mode 100644 index 0000000..3ba63e1 --- /dev/null +++ b/src/requirements.txt @@ -0,0 +1,2 @@ +Jinja2==3.1.4 +MarkupSafe==3.0.2 diff --git a/src/static_gen/__init__.py b/src/static_gen/__init__.py new file mode 100644 index 0000000..e69de29