diff --git a/dbsync_daemon.py b/dbsync_daemon.py index 001f10b..8dd0b24 100644 --- a/dbsync_daemon.py +++ b/dbsync_daemon.py @@ -8,14 +8,70 @@ import sys import time import argparse +import platform +import logging +import os +import pathlib +import typing import dbsync from version import __version__ from config import config, validate_config, ConfigError, update_config_path +def is_pyinstaller() -> bool: + if getattr(sys, 'frozen', False) and platform.system() == "Windows": + return True + return False + + +def pyinstaller_update_path() -> None: + path = pathlib.Path(__file__).parent / "lib" + os.environ["PATH"] += os.pathsep + path.as_posix() + + +def pyinstaller_path_fix() -> None: + if is_pyinstaller(): + pyinstaller_update_path() + + +LOGGER: logging.Logger = None + + +def setup_logger(log_path, log_verbosity: str, with_time=True, with_level=True) -> logging.Logger: + global LOGGER + LOGGER = logging.getLogger(f"{log_path}") + if log_verbosity == "messages": + LOGGER.setLevel(logging.DEBUG) + elif log_verbosity == "errors": + LOGGER.setLevel(logging.WARNING) + else: + LOGGER.setLevel(logging.WARNING) + if not LOGGER.handlers: + log_handler = logging.FileHandler(log_path, mode="a") + format = "%(asctime)s -" if with_time else "" + format += "%(levelname)s - %(message)s" if with_level else "%(message)s" + log_handler.setFormatter(logging.Formatter(format)) + LOGGER.addHandler(log_handler) + + +def handle_error(error: typing.Union[Exception, str]): + if LOGGER: + LOGGER.error(str(error)) + print("Error: " + str(error), file=sys.stderr) + sys.exit(1) + + +def handle_message(msg: str): + if LOGGER: + LOGGER.debug(msg) + print(msg) + + def main(): + pyinstaller_path_fix() + parser = argparse.ArgumentParser(prog='dbsync_deamon.py', description='Synchronization tool between Mergin Maps project and database.', epilog='www.merginmaps.com') @@ -24,29 +80,33 @@ def main(): parser.add_argument("--skip-init", action="store_true", help="Skip DB sync init step to make the tool start faster. It is not recommend to use it unless you are really sure you can skip the initial sanity checks.") parser.add_argument("--single-run", action="store_true", help="Run just once performing single pull and push operation, instead of running in infinite loop.") parser.add_argument("--force-init", action="store_true", help="Force removing working directory and schemas from DB to initialize from scratch.") + parser.add_argument("--log-file", default="", action="store", help="Store logging to file.") + parser.add_argument("--log-verbosity", choices=["errors", "messages"], default="errors", help="Log messages, not only errors.") args = parser.parse_args() - print(f"== starting mergin-db-sync daemon == version {__version__} ==") + if args.log_file: + log_file = pathlib.Path(args.log_file) + setup_logger(log_file.as_posix(), args.log_verbosity) + + handle_message(f"== starting mergin-db-sync daemon == version {__version__} ==") try: update_config_path(args.config_file) except IOError as e: - print("Error: " + str(e), file=sys.stderr) - sys.exit(1) + handle_error(e) sleep_time = config.as_int("daemon.sleep_time") try: validate_config(config) except ConfigError as e: - print("Error: " + str(e), file=sys.stderr) - sys.exit(1) + handle_error(e) if args.force_init and args.skip_init: - print("Cannot use `--force-init` with `--skip-init` Initialization is required. ", file=sys.stderr) - sys.exit(1) + handle_error("Cannot use `--force-init` with `--skip-init` Initialization is required. ") + + handle_message("Logging in to Mergin...") - print("Logging in to Mergin...") mc = dbsync.create_mergin_client() if args.force_init: @@ -58,18 +118,17 @@ def main(): try: dbsync.dbsync_init(mc) except dbsync.DbSyncError as e: - print("Error: " + str(e), file=sys.stderr) - sys.exit(1) + handle_error(e) try: - print("Trying to pull") + handle_message("Trying to pull") dbsync.dbsync_pull(mc) - print("Trying to push") + handle_message("Trying to push") dbsync.dbsync_push(mc) + except dbsync.DbSyncError as e: - print("Error: " + str(e), file=sys.stderr) - sys.exit(1) + handle_error(e) else: @@ -77,18 +136,17 @@ def main(): try: dbsync.dbsync_init(mc) except dbsync.DbSyncError as e: - print("Error: " + str(e), file=sys.stderr) - sys.exit(1) + handle_error(e) while True: print(datetime.datetime.now()) try: - print("Trying to pull") + handle_message("Trying to pull") dbsync.dbsync_pull(mc) - print("Trying to push") + handle_message("Trying to push") dbsync.dbsync_push(mc) # check mergin client token expiration @@ -97,9 +155,9 @@ def main(): mc = dbsync.create_mergin_client() except dbsync.DbSyncError as e: - print("Error: " + str(e), file=sys.stderr) + handle_error(e) - print("Going to sleep") + handle_message("Going to sleep") time.sleep(sleep_time) diff --git a/scripts/build_exe.bat b/scripts/build_exe.bat new file mode 100755 index 0000000..4c0082a --- /dev/null +++ b/scripts/build_exe.bat @@ -0,0 +1,13 @@ +pyinstaller ../dbsync_daemon.py ^ + -c ^ + --noconfirm ^ + --add-binary="./windows_binaries/geodiff.exe;lib" ^ + --add-binary="./windows_binaries/geodiff.dll;lib" ^ + --add-binary="./windows_binaries/libcrypto-3-x64.dll;lib" ^ + --add-binary="./windows_binaries/LIBPQ.dll;lib" ^ + --add-binary="./windows_binaries/libssl-3-x64.dll;lib" ^ + --add-binary="./windows_binaries/sqlite3.dll;lib" ^ + --hidden-import dynaconf ^ + --collect-all mergin ^ + --clean ^ + -F \ No newline at end of file