#!/usr/bin/python3 # SPDX-License-Identifier: WTFPL """ YMS native GUI. Runs as user SYSTEM with id=0 Configuration file located at ~/.config/ymsgui.conf: [DB] host = localhost db = yms user = ymsgui pass = topsecret [Documents] basepath = /var/local/lib/yms [Data] shapedir = /usr/local/share/yms/shapes """ import os import sys import locale import gettext import configparser os.environ["NO_AT_BRIDGE"] = "1" # fix dbus warnings import gi gi.require_version("Gtk", "3.0") gi.require_version('Rsvg', '2.0') from gi.repository import GLib, Gtk, Gdk, Rsvg from yms import database from yms import gtkutils, combobox, treeview from yms.model import user from yms.model import vessel from yms.view.base import BaseView from yms.view.storage import StorageView from yms.view.equipment import EquipmentView from yms.view.inventory import InventoryView from yms.view.provisions import ProvisionsView from yms.view.maintenance import MaintenanceView from yms.view.projects import ProjectsView from yms.view.cables import CablesView from yms.view.fuses import FusesView from yms.view.documents import DocumentsView from yms.view.status import StatusView class AppContext: cfg = { 'cfgfile': '~/.config/ymsgui.conf', 'host': 'localhost', 'db': 'yms', 'user': 'yms', 'docpath': '/var/local/yms', 'shapedir': '/usr/local/share/yms/shapes' } # mapping between PHP and Python datefmt_map = { 'Y-m-d': '%Y.%m.%d', 'Y/m/d': '%Y/%m/%d', 'd.m.Y': '%d.%m.%Y', 'd/m/Y': '%d/%m/%Y', 'm-d-Y': '%m.%d.%Y' } def __init__(self): self.db = None self.user = None self.vessel = None self.ui_path = os.path.join(self.get_sharepath(), "yms", "ui") self.textdomain = "ymsgui" self.datefmt = '%d.%m.%Y' def set_db(self, db): self.db = db def load(self): self.user = user.User(db, 0) self.vessel = vessel.Vessel(db) # load date format from db settings sql = "SELECT valstr FROM settings WHERE userid=0 AND sno=2" self.db.cur.execute(sql) row = self.db.cur.fetchone() if row is not None: try: self.datefmt = self.datefmt_map[row[0]] except KeyError: # almost impossible case where db value is not in mapping print("internal error: format mapping missinf") else: # perhaps create missing record here? pass def get_sharepath(self): path = os.path.realpath(__file__) if path.startswith("/usr/local/"): return "/usr/local/share" if path.startswith("/usr/"): return "/usr/share" return "" class Frontend: def __init__(self, context): self.db = context.db self.modules = { "base": BaseView(context), "storage": StorageView(context), "equipment": EquipmentView(context), "inventory": InventoryView(context), "provisions": ProvisionsView(context), "cables": CablesView(context), "fuses": FusesView(context), "maintenance": MaintenanceView(context), "projects": ProjectsView(context), "documents": DocumentsView(context), "status": StatusView(context) } builder = Gtk.Builder() builder.set_translation_domain(context.textdomain) ui_path = os.path.join(context.ui_path) builder.add_from_file(os.path.join(ui_path, "main.ui")) builder.connect_signals(self) self.window = builder.get_object("wnd_main") self.notebook = builder.get_object("ntb_modules") for module in self.modules.values(): self.notebook.append_page(module.topwidget, Gtk.Label(label=module.title)) self.db_poll_ts = context.db.get_timestamp() GLib.timeout_add_seconds(30, self.db_change_poller) print("Frontend initialized.") def run(self): #self.label['vessel'].set_text(self._vessel.vesselname) self.window.show_all() Gtk.main() def db_change_poller(self): # get all list ids which changes since last query print("db list change poller") #sql = "SELECT revid FROM revision WHERE changed>%s" self.db_poll_ts = self.db.get_timestamp() return True def on_ntb_modules_switch_page(self, notebook, page, page_num): print("Neue Seite:", page_num) # TODO here re-load lists def on_destroy(self, widget, data=None): Gtk.main_quit() def create_config(cfgfile): # create inital config file with open(cfgfile, 'w') as fh: fh.write('[DB]\n') fh.write('host = {}\n'.format(cfg['host'])) fh.write('db = {}\n'.format(cfg['db'])) fh.write('user = {}\n'.format(cfg['user'])) fh.write('pass = changeme!\n\n') fh.write('[Documents]\n') fh.write('basepath = {}\n\n'.format(cfg['docpath'])) fh.write('[Data]\n') fh.write('shapedir = {}\n'.format(cfg['shapedir'])) # secure configuration os.chmod(cfgfile, 0o600) if __name__ == "__main__": context = AppContext() locale.setlocale(locale.LC_ALL, '') gettext.bindtextdomain(context.textdomain, 'locale') gettext.textdomain(context.textdomain) gettext.install(context.textdomain) config = configparser.ConfigParser() cfgfile = os.path.expanduser(context.cfg['cfgfile']) if not config.read(cfgfile): create_config(cfgfile) gtkutils.messagebox( _("Configuration file '{}' not found.\n" "Default configuration has been created.\n" "Please edit that file according to your needs.\n\n" "Then restart this program." ).format(cfgfile), title="YMS") else: context.cfg['host'] = config.get('DB', 'host') context.cfg['db'] = config.get('DB', 'db') db = database.Database(context.cfg['host'], context.cfg['db']) connected = False if context.cfg['host'] == 'socket': connected = db.connect_local() else: context.cfg['user'] = config.get('DB', 'user') context.cfg['pass'] = config.get('DB', 'pass') connected = db.connect(context.cfg['user'], context.cfg['pass']) if connected: context.set_db(db) context.load() # check if shapedir exists context.cfg['shapedir'] = config.get('Data', 'shapedir') # everything seems to be fine: continue to start frontend app = Frontend(context) app.run() context.db.disconnect() else: errno, errtxt = context.db.last_error() gtkutils.messagebox( _("Database connection cannot be established.\n\n" "Server: {}\nDatabase: {}\n" "Error: {} ({})").format(context.cfg['host'], context.cfg['db'], errtxt, errno), title="YMS") # Another fine product of the sirius cybernetics corporation