diff --git a/README.md b/README.md index fe85df5..16c9ee6 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ Installer python, og lag og aktiver et venv. Installer så avhengighetene med `p Deretter kan du kjøre programmet med ```console -python -m dibbler -c example-config.ini create-db -python -m dibbler -c example-config.ini seed-data -python -m dibbler -c example-config.ini loop +python -m dibbler -c example-config.toml create-db +python -m dibbler -c example-config.toml seed-data +python -m dibbler -c example-config.toml loop ``` ## Nix @@ -46,7 +46,7 @@ Du kan også bygge pakken manuelt, eller kjøre den direkte: ```console nix build .#dibbler -nix run .# -- --config example-config.ini create-db -nix run .# -- --config example-config.ini seed-data -nix run .# -- --config example-config.ini loop +nix run .# -- --config example-config.toml create-db +nix run .# -- --config example-config.toml seed-data +nix run .# -- --config example-config.toml loop ``` diff --git a/dibbler/conf.py b/dibbler/conf.py index 8fef746..0a4e404 100644 --- a/dibbler/conf.py +++ b/dibbler/conf.py @@ -1,13 +1,10 @@ -# This module is supposed to act as a singleton and be filled -# with config variables by cli.py +from typing import Any from pathlib import Path +import tomllib import os +import sys -import configparser - -config = configparser.ConfigParser() - -DEFAULT_CONFIG_PATH = Path('/etc/dibbler/dibbler.conf') +DEFAULT_CONFIG_PATH = Path('/etc/dibbler/dibbler.toml') def default_config_path_submissive_and_readable() -> bool: return DEFAULT_CONFIG_PATH.is_file() and any( @@ -23,3 +20,17 @@ def default_config_path_submissive_and_readable() -> bool: (DEFAULT_CONFIG_PATH.stat().st_mode & 0o004), ] ) + +config: dict[str, dict[str, Any]] = dict() + +def load_config(config_path: Path | None = None): + global config + if config_path is not None: + with Path(config_path).open("rb") as file: + config = tomllib.load(file) + elif default_config_path_submissive_and_readable(): + with DEFAULT_CONFIG_PATH.open("rb") as file: + config = tomllib.load(file) + else: + print("Could not read config file, it was neither provided nor readable in default location", file=sys.stderr) + sys.exit(1) diff --git a/dibbler/db.py b/dibbler/db.py index 299a091..7f52b43 100644 --- a/dibbler/db.py +++ b/dibbler/db.py @@ -1,19 +1,5 @@ -from pathlib import Path +from sqlalchemy.engine.base import Engine +from sqlalchemy.orm import Session -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker - -from dibbler.conf import config - -database_url: str | None = None - -if (url := config.get("database", "url")) is not None: - database_url = url -elif (url_file := config.get("database", "url_file")) is not None: - with Path(url_file).open() as file: - database_url = file.read().strip() - -assert database_url is not None, "Database URL must be specified in config" - -engine = create_engine(database_url) -Session = sessionmaker(bind=engine) +engine: Engine = None +session: Session = None diff --git a/dibbler/lib/statistikkHelpers.py b/dibbler/lib/statistikkHelpers.py index 0441aec..f17da25 100644 --- a/dibbler/lib/statistikkHelpers.py +++ b/dibbler/lib/statistikkHelpers.py @@ -6,13 +6,13 @@ from collections import defaultdict from .helpers import * from ..models import Transaction -from ..db import Session +from ..db import session as create_session def getUser(): while 1: string = input("user? ") - session = Session() + session = create_session() user = search_user(string, session) session.close() if not isinstance(user, list): @@ -40,7 +40,7 @@ def getUser(): def getProduct(): while 1: string = input("product? ") - session = Session() + session = create_session() product = search_product(string, session) session.close() if not isinstance(product, list): @@ -242,7 +242,7 @@ def buildDatabaseFromDb(inputType, inputProduct, inputUser): sdate = input("enter start date (yyyy-mm-dd)? ") edate = input("enter end date (yyyy-mm-dd)? ") print("building database...") - session = Session() + session = create_session() transaction_list = session.query(Transaction).all() inputLine = InputLine(inputUser, inputProduct, inputType) startDate = getDateDb(transaction_list[0].time, sdate) diff --git a/dibbler/main.py b/dibbler/main.py index 89674a3..33f25b4 100644 --- a/dibbler/main.py +++ b/dibbler/main.py @@ -1,8 +1,11 @@ import argparse -import sys from pathlib import Path -from dibbler.conf import DEFAULT_CONFIG_PATH, config, default_config_path_submissive_and_readable +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +from dibbler.conf import load_config +from dibbler.db import engine, session parser = argparse.ArgumentParser() @@ -12,7 +15,7 @@ parser.add_argument( help="Path to the config file", type=Path, metavar="FILE", - default="config.ini", + required=False, ) subparsers = parser.add_subparsers( @@ -29,12 +32,13 @@ subparsers.add_parser("seed-data", help="Fill with mock data") def main(): args = parser.parse_args() - if args.config is not None: - config.read(args.config) - elif default_config_path_submissive_and_readable(): - config.read(DEFAULT_CONFIG_PATH) - else: - print("Could not read config file, it was neither provided nor readable in default location", file=sys.stderr) + load_config(args.config) + + from dibbler.conf import config + + global engine, session + engine = create_engine(config['database']['url']) + session = sessionmaker(bind=engine) if args.subcommand == "loop": import dibbler.subcommands.loop as loop diff --git a/dibbler/menus/buymenu.py b/dibbler/menus/buymenu.py index 05be7f9..66fa340 100644 --- a/dibbler/menus/buymenu.py +++ b/dibbler/menus/buymenu.py @@ -37,7 +37,7 @@ When finished, write an empty line to confirm the purchase.\n""" """ assert isinstance(user, User) - return user.credit > config.getint("limits", "low_credit_warning_limit") + return user.credit > config["limits"]["low_credit_warning_limit"] def low_credit_warning(self, user, timeout=False): assert isinstance(user, User) @@ -58,7 +58,7 @@ When finished, write an empty line to confirm the purchase.\n""" print("***********************************************************************") print("") print( - f"USER {user.name} HAS LOWER CREDIT THAN {config.getint('limits', 'low_credit_warning_limit'):d}." + f"USER {user.name} HAS LOWER CREDIT THAN {config['limits']['low_credit_warning_limit']:d}." ) print("THIS PURCHASE WILL CHARGE YOUR CREDIT TWICE AS MUCH.") print("CONSIDER PUTTING MONEY IN THE BOX TO AVOID THIS.") @@ -178,9 +178,9 @@ When finished, write an empty line to confirm the purchase.\n""" for t in self.purchase.transactions: if not t.user.is_anonymous(): print(f"User {t.user.name}'s credit is now {t.user.credit:d} kr") - if t.user.credit < config.getint("limits", "low_credit_warning_limit"): + if t.user.credit < config["limits"]["low_credit_warning_limit"]: print( - f"USER {t.user.name} HAS LOWER CREDIT THAN {config.getint('limits', 'low_credit_warning_limit'):d},", + f"USER {t.user.name} HAS LOWER CREDIT THAN {config['limits']['low_credit_warning_limit']:d},", "AND SHOULD CONSIDER PUTTING SOME MONEY IN THE BOX.", ) diff --git a/dibbler/menus/helpermenus.py b/dibbler/menus/helpermenus.py index 88a40ce..2270d8c 100644 --- a/dibbler/menus/helpermenus.py +++ b/dibbler/menus/helpermenus.py @@ -5,7 +5,7 @@ import re import sys from select import select -from dibbler.db import Session +from dibbler.db import session as create_session from dibbler.models import User from dibbler.lib.helpers import ( search_user, @@ -485,7 +485,7 @@ class Menu(object): self.set_context(None) try: if self.uses_db and not self.session: - self.session = Session() + self.session = create_session() return self._execute(**kwargs) except ExitMenu: self.at_exit() diff --git a/dibbler/menus/mainmenu.py b/dibbler/menus/mainmenu.py index a9c20ca..da1c4a6 100644 --- a/dibbler/menus/mainmenu.py +++ b/dibbler/menus/mainmenu.py @@ -3,7 +3,7 @@ import os import random import sys -from dibbler.db import Session +from dibbler.db import session as create_session from .buymenu import BuyMenu from .faq import FAQMenu @@ -28,7 +28,7 @@ class MainMenu(Menu): else: num = 1 item_name = in_str - buy_menu = BuyMenu(Session()) + buy_menu = BuyMenu(create_session()) thing = buy_menu.search_for_thing(item_name, find_hidden_products=False) if thing: buy_menu.execute(initial_contents=[(thing, num)]) diff --git a/dibbler/menus/miscmenus.py b/dibbler/menus/miscmenus.py index 7cf7519..218842b 100644 --- a/dibbler/menus/miscmenus.py +++ b/dibbler/menus/miscmenus.py @@ -56,7 +56,7 @@ class ShowUserMenu(Menu): ( "transactions", "Recent transactions (List of last " - + str(config.getint("limits", "user_recent_transaction_limit")) + + str(config["limits"]["user_recent_transaction_limit"]) + ")", ), ("products", f"Which products {user.name} has bought, and how many"), @@ -65,7 +65,7 @@ class ShowUserMenu(Menu): ) what = selector.execute() if what == "transactions": - self.print_transactions(user, config.getint("limits", "user_recent_transaction_limit")) + self.print_transactions(user, config["limits"]["user_recent_transaction_limit"]) elif what == "products": self.print_purchased_products(user) elif what == "transactions-all": diff --git a/dibbler/menus/printermenu.py b/dibbler/menus/printermenu.py index 8b6bfc8..fe521f6 100644 --- a/dibbler/menus/printermenu.py +++ b/dibbler/menus/printermenu.py @@ -32,14 +32,14 @@ Put it up somewhere in the vicinity. thing.bar_code, thing.name, barcode_type=bar_type, - rotate=config.getboolean("printer", "rotate"), + rotate=config["printer"]["rotate"], printer_type="QL-700", label_type=config.get("printer", "label_type"), ) elif isinstance(thing, User): print_name_label( text=thing.name, - label_type=config.get("printer", "label_type"), - rotate=config.getboolean("printer", "rotate"), + label_type=config["printer"]["label_type"], + rotate=config["printer"]["rotate"], printer_type="QL-700", ) diff --git a/dibbler/subcommands/loop.py b/dibbler/subcommands/loop.py index e2a2f3c..2faff90 100755 --- a/dibbler/subcommands/loop.py +++ b/dibbler/subcommands/loop.py @@ -13,10 +13,10 @@ random.seed() def main(): - if not config.getboolean("general", "stop_allowed"): + if not config["general"]["stop_allowed"]: signal.signal(signal.SIGQUIT, signal.SIG_IGN) - if not config.getboolean("general", "stop_allowed"): + if not config["general"]["stop_allowed"]: signal.signal(signal.SIGTSTP, signal.SIG_IGN) main = MainMenu( @@ -56,7 +56,7 @@ def main(): exit_msg="happy happy joy joy", exit_confirm_msg="Really quit Dibbler?", ) - if not config.getboolean("general", "quit_allowed"): + if not config["general"]["quit_allowed"]: main.exit_disallowed_msg = "You can check out any time you like, but you can never leave." while True: # noinspection PyBroadException @@ -68,7 +68,7 @@ def main(): except: print("Something went wrong.") print(f"{sys.exc_info()[0]}: {sys.exc_info()[1]}") - if config.getboolean("general", "show_tracebacks"): + if config["general"]["show_tracebacks"]: traceback.print_tb(sys.exc_info()[2]) else: break diff --git a/dibbler/subcommands/seed_test_data.py b/dibbler/subcommands/seed_test_data.py index 07454ea..9ffd02a 100644 --- a/dibbler/subcommands/seed_test_data.py +++ b/dibbler/subcommands/seed_test_data.py @@ -1,5 +1,5 @@ import json -from dibbler.db import Session +from dibbler.db import session as create_session from pathlib import Path @@ -17,7 +17,7 @@ def clear_db(session): def main(): - session = Session() + session = create_session() clear_db(session) product_items = [] user_items = [] diff --git a/dibbler/subcommands/slabbedasker.py b/dibbler/subcommands/slabbedasker.py index a1a9df1..c66df30 100644 --- a/dibbler/subcommands/slabbedasker.py +++ b/dibbler/subcommands/slabbedasker.py @@ -1,12 +1,12 @@ #!/usr/bin/python -from dibbler.db import Session +from dibbler.db import session as create_session from dibbler.models import User def main(): # Start an SQL session - session = Session() + session = create_session() # Let's find all users with a negative credit slabbedasker = session.query(User).filter(User.credit < 0).all() diff --git a/example-config.ini b/example-config.toml similarity index 100% rename from example-config.ini rename to example-config.toml diff --git a/nix/module.nix b/nix/module.nix index 4eb6726..904047b 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -1,7 +1,7 @@ { config, pkgs, lib, ... }: let cfg = config.services.dibbler; - format = pkgs.formats.ini { }; + format = pkgs.formats.toml { }; in { options.services.dibbler = { enable = lib.mkEnableOption "dibbler, the little kiosk computer"; @@ -58,7 +58,7 @@ in { config = lib.mkIf cfg.enable (lib.mkMerge [ { - services.dibbler.settings = lib.pipe ../example-config.ini [ + services.dibbler.settings = lib.pipe ../example-config.toml [ builtins.readFile builtins.fromTOML (lib.mapAttrsRecursive (_: lib.mkDefault)) @@ -67,7 +67,7 @@ in { { environment.systemPackages = [ cfg.package ]; - environment.etc."dibbler/dibbler.conf".source = format.generate "dibbler.conf" cfg.settings; + environment.etc."dibbler/dibbler.toml".source = format.generate "dibbler.toml" cfg.settings; users = { users.dibbler = { @@ -97,7 +97,7 @@ in { }; serviceConfig = { Type = "oneshot"; - ExecStart = "${lib.getExe cfg.package} --config /etc/dibbler/dibbler.conf create-db"; + ExecStart = "${lib.getExe cfg.package} --config /etc/dibbler/dibbler.toml create-db"; ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/dibbler/.db-setup-done"; StateDirectory = "dibbler"; @@ -163,7 +163,7 @@ in { ]; dibblerArgs = lib.cli.toCommandLineShellGNU { } { - config = "/etc/dibbler/dibbler.conf"; + config = "/etc/dibbler/dibbler.toml"; }; in "${lib.getExe cfg.screenPackage} ${screenArgs} ${lib.getExe cfg.package} ${dibblerArgs} loop";