Source code for main_program

"""DifferentialLab — application entry point."""

from __future__ import annotations

import sys
import tkinter as tk
from pathlib import Path
from tkinter import messagebox

# Ensure src/ is on the import path when run directly
_src_dir = Path(__file__).resolve().parent
if str(_src_dir) not in sys.path:
    sys.path.insert(0, str(_src_dir))


def _check_for_updates() -> None:
    """Check for updates once a week.

    If a newer version is available and CHECK_UPDATES is enabled, show a
    dialog asking if the user wants to update. If yes, perform git pull
    (preserves input/, output/, .env).
    """
    from config import APP_VERSION
    from utils import (
        get_logger,
        is_update_available,
        perform_git_pull,
        record_check_done,
        should_run_check,
    )

    logger = get_logger(__name__)

    if not should_run_check():
        logger.debug("Update check skipped (CHECK_UPDATES disabled or checked recently)")
        return

    latest = is_update_available(APP_VERSION)
    # Only record when no update found — if there is one, keep reminding every startup
    if not latest:
        record_check_done()

    if latest:
        logger.info("Update available: %s (current: %s)", latest, APP_VERSION)
    else:
        logger.debug("Update check done: no newer version (current: %s)", APP_VERSION)

    if not latest:
        return

    wants_update = messagebox.askyesno(
        "Update available",
        f"A new version ({latest}) is available. You have {APP_VERSION}.\n\n"
        "Do you want to update now? (input/, output/ and .env will be preserved)",
        default=messagebox.YES,
    )
    if not wants_update:
        return

    success, msg = perform_git_pull()
    if success:
        messagebox.showinfo("Update", msg)
    else:
        messagebox.showerror("Update failed", msg)


[docs] def main() -> None: """Initialize configuration, logging, and launch the main menu. Sets up the application environment, checks for updates, and starts the Tkinter main loop with the main menu window. """ from config import APP_VERSION, get_output_dir, initialize_and_validate_config from utils import get_logger initialize_and_validate_config() logger = get_logger(__name__) logger.info("DifferentialLab starting (v%s)", APP_VERSION) _check_for_updates() get_output_dir() try: root = tk.Tk() from frontend import MainMenu _app = MainMenu(root) root.mainloop() except Exception: logger.critical("Unhandled exception", exc_info=True) raise finally: import matplotlib.pyplot as _plt _plt.close("all") logger.info("DifferentialLab shutting down") sys.exit(0)
if __name__ == "__main__": main()