From df1bde020bed2525ac63f09b964b1088093b4dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Thu, 19 Mar 2020 17:18:04 +0100 Subject: [PATCH] svupdater/lists: add support for labels Labels are new generic approach to existing official boolean option. This removes that option and instead introduces flags that can symbol anything. This implementation is just extension of existing one so everything is till returned all at once. Labels can be matched by name between various pkglists but that has to be done by user. Here info for labels is duplicated to every occurrence of that label. --- svupdater/const.py | 2 + svupdater/lists.py | 111 +++++++++++++++++++++++++++++---------------- 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/svupdater/const.py b/svupdater/const.py index d8f0e01..6a536e8 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 1a7054e..304dd3f 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: -- GitLab