"""Grotti starten. Kern und Seiten liegen daneben.""" import atexit import signal import threading import time from waitress import serve from grotti import db, jobs, library, logutil, store from grotti.config import load from web.app import create_app log = logutil.get("grotti.server") def background(cfg): def work(): try: with db.connect(cfg["database_url"]) as conn: library.sync_watch(conn, cfg, force=False) jobs.sync_history(conn, cfg) deleted = db.prune_run_samples(conn, keep_days=90) if deleted: log.info("Proben gelöscht: %s", deleted) conn.commit() except Exception as exc: log.warning("Abgleich: %s", exc) threading.Thread(target=work, name="grotti-abgleich", daemon=True).start() def _retention_loop(cfg, stop_event): """Nächtlich alte run_samples entfernen.""" while not stop_event.wait(3600): hour = time.localtime().tm_hour if hour != 3: continue try: with db.connect(cfg["database_url"]) as conn: n = db.prune_run_samples(conn, keep_days=90) conn.commit() if n: log.info("Retention: %s Proben gelöscht.", n) except Exception as exc: log.warning("Retention: %s", exc) stop_event.wait(3600) def main(): cfg = load() logutil.setup(cfg.get("log_level") or "INFO") cfg["data_dir"].mkdir(parents=True, exist_ok=True) (cfg["data_dir"] / "eingang").mkdir(exist_ok=True) db.prepare(cfg["database_url"]) db.init_pool(cfg["database_url"], min_size=2, max_size=16) with db.connect(cfg["database_url"]) as conn: store.seed_printers(conn, cfg["printers_file"]) store.seed_materials(conn) conn.commit() app = create_app(cfg) watcher = app.extensions["watcher"] dispatcher = app.extensions["print_queue"] send_queue = app.extensions["send_queue"] watcher.start() dispatcher.start() if hasattr(send_queue, "start"): send_queue.start() from grotti.daily_report import DailyReportThread daily = DailyReportThread(cfg["database_url"], cfg, watcher=watcher) daily.start() app.extensions["daily_report"] = daily stop_event = threading.Event() retention = threading.Thread( target=_retention_loop, args=(cfg, stop_event), name="grotti-retention", daemon=True, ) retention.start() background(cfg) def shutdown(*_args): stop_event.set() try: watcher.stop() except Exception: pass try: dispatcher.stop() except Exception: pass try: if hasattr(send_queue, "stop"): send_queue.stop() except Exception: pass try: daily.stop() except Exception: pass db.close_pool() atexit.register(shutdown) for sig in (signal.SIGINT, signal.SIGTERM): try: signal.signal(sig, lambda *_: shutdown()) except (ValueError, OSError): pass log.info("Grotti: http://%s:%s/", cfg["host"], cfg["port"]) serve( app, host=cfg["host"], port=cfg["port"], threads=int(cfg.get("http_threads") or 16), channel_timeout=120, ) if __name__ == "__main__": main()