diff --git a/svupdater/const.py b/svupdater/const.py index d8f0e0163a7f421475972f85907ed38b91e6bf90..6a536e80577c58b0bc23d5c78c20ccdab12370c5 100644 --- a/svupdater/const.py +++ b/svupdater/const.py @@ -46,6 +46,8 @@ NOTIFY_MESSAGE_EN = "Your approval is required to apply pending updates." + \ L10N_FILE = "/usr/share/updater/l10n_supported" # File containing list of known pkglists in json PKGLISTS_FILE = "/usr/share/updater/pkglists.json" +# File containing list of known pkglists's labels in json +PKGLISTS_LABELS_FILE = "/usr/share/updater/pkglists-labels.json" # Hooks file containing commands to be run after updater execution finished. POSTRUN_HOOK_FILE = "/var/run/updater-postrun-hook" diff --git a/svupdater/lists.py b/svupdater/lists.py index 1a7054ecd22cae157087391ac35d37e149c05347..304dd3f43c90a6d61ad6bd8f794a10ad3a2efa82 100644 --- a/svupdater/lists.py +++ b/svupdater/lists.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, CZ.NIC, z.s.p.o. (http://www.nic.cz/) +# Copyright (c) 2018-2020, CZ.NIC, z.s.p.o. (http://www.nic.cz/) # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -26,68 +26,103 @@ import os import json import gettext import typing -from euci import EUci, UciExceptionNotFound -from .const import PKGLISTS_FILE +from euci import EUci +from .const import PKGLISTS_FILE, PKGLISTS_LABELS_FILE from .exceptions import UpdaterNoSuchListError, UpdaterNoSuchListOptionError +__PKGLIST_ENTRIES_LABELS = typing.Dict[str, str] +__PKGLIST_ENTRIES_OPTIONS = typing.Dict[str, typing.Union[str, bool, __PKGLIST_ENTRIES_LABELS]] __PKGLIST_ENTRIES = typing.Dict[ str, typing.Union[ - str, bool, typing.Dict[str, typing.Union[str, bool]] + str, bool, __PKGLIST_ENTRIES_OPTIONS, __PKGLIST_ENTRIES_LABELS ] ] -def _load_lists(): - if os.path.isfile(PKGLISTS_FILE): # Just to be sure - with open(PKGLISTS_FILE, 'r') as file: - return json.load(file) - else: +def _load_json_dict(file_path): + if not os.path.isfile(file_path): return {} + with open(file_path, 'r') as file: + return json.load(file) + + +def __labels(known_labels, trans, labels): + """Convert list of label names to label dictionaries with all info. + """ + return { + lbl: { + "title": trans.gettext(known_labels[lbl]['title']), + "description": trans.gettext(known_labels[lbl]['description']), + "severity": known_labels[lbl].get('severity', "primary"), + } for lbl in labels if lbl in known_labels.keys() + } + + +def __options(pkglist_name, trans, uci, known_labels, options): + """Convert list of options to option dictionaries with all info. + """ + return { + name: { + "enabled": uci.get('pkglists', pkglist_name, name, dtype=bool, default=option.get('default', False)), + "title": trans.gettext(option['title']), + "description": trans.gettext(option['description']), + "url": option.get('url'), + "labels": __labels(known_labels, trans, option.get('labels', {})), + } for name, option in options.items() + } def pkglists(lang=None) -> typing.Dict[str, __PKGLIST_ENTRIES]: """Returns dict of pkglists. - Argument lang is expected to be a string containing language code. This - code is then used for gettext translations of titles and descriptions of - messages. + Argument lang is expected to be a string containing language code. This code is then used for gettext translations + of titles and descriptions of messages. - Return pkglists are in dictionary where key is name of pkglist and value is - another dictionary with following content: - "enabled": This is boolean value containing info if pkglist is enabled. - "hidden": This is boolean value specifying if pkglist is visible. - "official": This is boolean value specifying if pkglist is supported. - "title": This is title text describing pkglist (human readable name). - "description": This is human readable description of given pkglist. - "url": Optional URL to documentation. This can be None if not provided. - "options": Additional package options + Return pkglists are in dictionary where key is name of pkglist and value is another dictionary with following + content: + "enabled": This is boolean value containing info if pkglist is enabled. + "title": This is title text describing pkglist (human readable name). + "description": This is human readable description of given pkglist. + "url": Optional URL to documentation. This can be None if not provided. + "options": Additional package options stored in dictionary where keys are option names and value another + dictionary with content: + "enabled": Boolean value if option is enabled or not. + "title": Human readable name of option. + "description": Human readable description of given pkglist option. + "url": Optional URL to documentation. This can be None if not provided. + "labels": Labels assigned to option. Value is dictionary same as for pkglists labels. + "labels": Labels assigned to pkglist. Value is dictionary with keys being name of labels and values dictionaries + with following content: + "title": Human readable name of label. + "description": Human readable text describing label's meaning. + "severity": String that is one of following values: + * "danger" + * "warning" + * "info" + * "success" + * "primary" + * "secondary" + * "light" + * "dark" """ trans = gettext.translation( 'pkglists', languages=[lang] if lang is not None else None, fallback=True) - known_lists = _load_lists() + known_lists = _load_json_dict(PKGLISTS_FILE) + known_labels = _load_json_dict(PKGLISTS_LABELS_FILE) - result = {} with EUci() as uci: enabled_lists = uci.get('pkglists', 'pkglists', 'pkglist', list=True, default=[]) - for name, lst in known_lists.items(): - result[name] = { + return { + name: { "enabled": name in enabled_lists, "title": trans.gettext(lst['title']), "description": trans.gettext(lst['description']), - "official": lst.get('official', False), "url": lst.get('url'), - "hidden": False, # Obsolete option for backward compatibility - "options": {}, - } - for opt_name, option in lst.get('options', {}).items(): - result[name]['options'][opt_name] = { - "enabled": uci.get('pkglists', name, opt_name, - dtype=bool, default=option.get('default', False)), - "title": trans.gettext(option['title']), - "description": trans.gettext(option['description']), - } - return result + "options": __options(name, trans, uci, known_labels, lst.get('options', {})), + "labels": __labels(known_labels, trans, lst.get('labels', {})), + } for name, lst in known_lists.items() + } def update_pkglists(lists: typing.Dict[str, typing.Dict[str, bool]]): @@ -96,7 +131,7 @@ def update_pkglists(lists: typing.Dict[str, typing.Dict[str, bool]]): and sub-dictionary with their options. Anything omitted will be disabled. """ - known_lists = _load_lists() + known_lists = _load_json_dict(PKGLISTS_FILE) for name, options in lists.items(): if name not in known_lists: