From 9e79f387a5e0c0721e2f11e87ae65c17e99acb35 Mon Sep 17 00:00:00 2001 From: Stephen Arnold Date: Sat, 24 Aug 2024 19:17:32 -0700 Subject: [PATCH 1/2] new: usr: add menu option to install extensions, beef up tests * add some pytest fixtures and more tests, improve readme * refactor for correctness and testability, cleanup packaging Signed-off-by: Stephen Arnold --- README.rst | 74 ++++++++++++++++++++++++++++------ scripts/timew-status-indicator | 23 +++++++++-- setup.cfg | 6 +-- src/timew_status/__init__.py | 2 + src/timew_status/utils.py | 47 ++++++++++++++++----- tests/conftest.py | 15 +++++++ tests/test_utils.py | 67 +++++++++++++++++++++++++++++- tests/testdata/onelineday.py | 0 tests/testdata/totals.py | 0 9 files changed, 203 insertions(+), 31 deletions(-) create mode 100644 tests/conftest.py create mode 100644 tests/testdata/onelineday.py create mode 100644 tests/testdata/totals.py diff --git a/README.rst b/README.rst index 08c9e2b..6dbc091 100644 --- a/README.rst +++ b/README.rst @@ -161,7 +161,9 @@ something like:: Finally, copy the desired extension(s) into the extensions folder:: - $ cp path/to/onelineday.py ~/.timewarrior/extensions/ + $ cp /usr/lib/timew-addons/extensions/onelineday.py ~/.timewarrior/extensions/ + +When using OS packages, extensions should be installed to the above path. Run the extension by substituting the extension name for the usual "summary" command, eg, instead of ``timew summary june``, use something like:: @@ -175,6 +177,47 @@ using:: should also work. +Environment +----------- + +The report extensions used by the `Appindicator GUI`_ have 2 output formats: + +* the default verbose mode is "human" report output +* the optional terse mode is consumed and displayed by the GUI + +The output mode and job-tag separator are exported as shell environment +variables by the GUI script on startup, which affects *only the internal* +runtime environment of the GUI. However, this means the variables are set +in the shell environment of the terminal launched by the menu option, so +running ``timew`` commands from this terminal instance will use the "terse" +output mode unless the environment variable is unset, eg, after launching +a terminal from the GUI menu, run the following in that terminal window:: + + $ timew one yesterday + xyz-test;08:39:36 + vctlabs;00:36:20 + total;09:15:56 + $ unset INDICATOR_FMT + $ timew one yesterday + Duration has 1 days and 2 total job tags: + ['xyz-test', 'vctlabs'] + + -- xyz-test + 2024-08-23 3:58:47 xyz-test,continue test case document structure + 2024-08-23 2:38:37 xyz-test,test doc development + 2024-08-23 0:18:55 xyz-test,test doc development discussion + 2024-08-23 1:43:17 xyz-test,test status mtg + + Total for xyz-test: 08:39:36 hrs + + -- vctlabs + 2024-08-23 0:36:20 vctlabs,project status/planning mtg + + Total for vctlabs: 00:36:20 hrs + + Final total for all jobs in duration: 09:15:56 hrs + + Appindicator GUI ~~~~~~~~~~~~~~~~ @@ -191,20 +234,34 @@ update to show the current state of timew vs configurable limits. GUI usage --------- -Select Timew Status Tool from the Applications View or the Internet menu in +Select Timew Status Tool from the Applications View or the Utils menu in your desktop of choice, eg, Gnome, Unity, Xfce, etc. You can also add it to your session startup or run it from an X terminal to get some debug output:: $ timew-status-indicator +Indicator states +################ + +It would not be an Appindicator_ without icons, so we use icons as one way +to show current state. + + +PyGObject references +-------------------- + +* https://lazka.github.io/pgi-docs/ PyGObject API Reference +* https://pygobject-tutorial.readthedocs.io/en/latest/index.html Tutorial +* https://github.com/candidtim/vagrant-appindicator (old) + Operating System Support -######################## +~~~~~~~~~~~~~~~~~~~~~~~~ The extension scripts require a basic console environment with both timewarrior and the timew-report packages installed (usually via system package manager). Running the indicator GUI script requires both -Python_ and a modern Gtk+ windowing environment with Gtk3+_ and +Python_ and a modern Gtk+ windowing environment with Gtk3_ and PyGObject_. .. important:: The GUI script requires one of the following extensions to @@ -233,18 +290,11 @@ separator if needed: .. _Python: https://docs.python.org/3/contents.html +.. _Gtk3: https://pygobject.gnome.org/tutorials/gtk3.html .. _PyGObject: https://pygobject.gnome.org/index.html .. _on your platform: https://timewarrior.net/docs/install/ -PyGObject references -#################### - -* https://lazka.github.io/pgi-docs/ PyGObject API Reference -* https://pygobject-tutorial.readthedocs.io/en/latest/index.html Tutorial -* https://github.com/candidtim/vagrant-appindicator (old) - - .. |CI| image:: https://github.com/sarnold/timew-addons/actions/workflows/ci.yml/badge.svg :target: https://github.com/sarnold/timew-addons/actions/workflows/ci.yml :alt: CI workflow status diff --git a/scripts/timew-status-indicator b/scripts/timew-status-indicator index 846648f..84593ca 100755 --- a/scripts/timew-status-indicator +++ b/scripts/timew-status-indicator @@ -34,6 +34,7 @@ from timew_status import ( DEBUG, TAG, __version__, + do_install, get_config, get_state_icon, get_state_str, @@ -90,7 +91,7 @@ class Indicator: def __init__(self): self.app_id = APPINDICATOR_ID app_category = appindicator.IndicatorCategory.APPLICATION_STATUS - icon_name = get_state_icon('INACTIVE') + icon_name = get_state_icon('INACTIVE', CFG) self.indicator = appindicator.Indicator.new(self.app_id, icon_name, app_category) self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE) @@ -120,11 +121,11 @@ class Indicator: result = get_status() if result.returncode == 1: new_state = 'INACTIVE' - print(f'State {new_state} msg: {result.stdout.decode().strip()}') + print(f'{new_state} state msg: {result.stdout.decode().strip()}') else: proc, _ = run_cmd() - msg, new_state = get_state_str(proc, current_tick_count) - print(f'State {new_state} msg: {msg.strip()}') + msg, new_state = get_state_str(proc, current_tick_count, CFG) + print(f'{new_state} state msg: {msg.strip()}') # if there is a change in state, update the icon if old_state != new_state: self.indicator.set_icon_full(get_state_icon(new_state), new_state) @@ -168,6 +169,10 @@ class Indicator: item_counter.connect('activate', self.reset_counter) menu.append(item_counter) + item_install = Gtk.MenuItem(label='Install extensions') + item_install.connect('activate', self.install_extensions) + menu.append(item_install) + item_about = Gtk.MenuItem(label='About ...') item_about.connect('activate', self.about) menu.append(item_about) @@ -204,6 +209,16 @@ class Indicator: win = TextWindow() win.show_all() + def install_extensions(self, source): + """Install report extensions to timew extensions directory""" + ret = do_install(CFG) + if len(ret) == 2: + msg = f'INFO: extensions installed:\nPath: {ret[0]}\nPath:{ret[1]}' + Notify.Notification.new("Install status", msg, None).show() + else: + msg = 'ERROR: check extensions folder' + Notify.Notification.new("Install status", msg, None).show() + def open_terminal(self, source): """Open a new terminal window""" subprocess.Popen([CFG['terminal_emulator']]) diff --git a/setup.cfg b/setup.cfg index 6edd5e7..164676e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -48,10 +48,6 @@ scripts = [options.packages.find] where = src -[options.package_data] -timew_status.data = - *.yaml - [options.data_files] # this is no longer supported as a valid use case, but still works share/applications = @@ -65,7 +61,7 @@ share/icons/hicolor/scalable/status = icons/hicolor/status/timew_error.svg icons/hicolor/status/timew_warning.svg icons/hicolor/status/timew_inactive.svg -lib/timew-addons/extensions = +share/timew-addons/extensions = extensions/onelineday.py extensions/totals.py extensions/csv_rpt.py diff --git a/src/timew_status/__init__.py b/src/timew_status/__init__.py index a0db505..0cb3deb 100644 --- a/src/timew_status/__init__.py +++ b/src/timew_status/__init__.py @@ -7,6 +7,7 @@ from .utils import ( CFG, DEBUG, + do_install, get_config, get_delta_limits, get_state_icon, @@ -31,6 +32,7 @@ "CFG", "DEBUG", "TAG", + "do_install", "get_config", "get_delta_limits", "get_state_icon", diff --git a/src/timew_status/utils.py b/src/timew_status/utils.py index 99489d4..6c18fd7 100644 --- a/src/timew_status/utils.py +++ b/src/timew_status/utils.py @@ -23,9 +23,36 @@ "loop_idle_seconds": 20, "show_state_label": False, "terminal_emulator": "gnome-terminal", + "extensions_dir": ".timewarrior/extensions", + "install_dir": "share/timew-addons/extensions", + "install_prefix": "/usr", } +def do_install(cfg): + """ + Install report extensions to timew extensions directory. The default + paths are preconfigured and should probably not be changed unless you + know what you are doing, since *they are created during install or setup*. + """ + prefix = cfg["install_prefix"] + srcdir = Path(prefix) / cfg["install_dir"] + destdir = Path.home() / cfg["extensions_dir"] + extensions = ['totals.py', 'onelineday.py'] + files = [] + + for file in extensions: + dest = destdir / file + src = srcdir / file + if DEBUG: + print(f"do_install: src is {src}") + print(f"do_install: dest is {dest}") + dest.write_bytes(src.read_bytes()) + print(f"{str(file)} written successfully") + files.append(str(file)) + return files + + def get_config(file_encoding='utf-8'): """ Load configuration file and munchify the data. If local file is not @@ -45,24 +72,26 @@ def get_config(file_encoding='utf-8'): return cfgobj, cfgfile -def get_delta_limits(): +def get_delta_limits(ucfg): """ Return config max/snooze limits as timedeltas. Everything comes from static config values and gets padded with seconds. :return: tuple of 4 timedeltas """ - day_sum = [CFG["day_max"] + ':00', CFG["day_snooze"] + ':00'] - seat_sum = [CFG["seat_max"] + ':00', CFG["seat_snooze"] + ':00'] + cfg = Munch.fromDict(ucfg) + pad = ':00' + day_sum = [cfg.day_max + pad, cfg.day_snooze + pad] + seat_sum = [cfg.seat_max + pad, cfg.seat_snooze + pad] day_limit = sum(map(to_td, day_sum), timedelta()) # noqa: seat_limit = sum(map(to_td, seat_sum), timedelta()) # noqa: - day_max = to_td(CFG["day_max"] + ':00') - seat_max = to_td(CFG["seat_max"] + ':00') + day_max = to_td(cfg.day_max + pad) + seat_max = to_td(cfg.seat_max + pad) return day_max, day_limit, seat_max, seat_limit -def get_state_icon(state): +def get_state_icon(state, cfg): """ Look up the state msg and return the icon name. Use builtin symbolic icons as fallback. @@ -92,13 +121,13 @@ def get_state_icon(state): state_dict = timew_dict app_icon = Path(install_path).joinpath(icon_name) - if CFG["use_symbolic_icons"] or not app_icon.exists(): + if cfg["use_symbolic_icons"] or not app_icon.exists(): state_dict = fallback_dict return state_dict.get(state, state_dict['INACTIVE']) -def get_state_str(cmproc, count): +def get_state_str(cmproc, count, cfg): """ Return timew state message and tracking state, ie, the key for dict with icons. @@ -110,7 +139,7 @@ def get_state_str(cmproc, count): :return: tuple of state msg and state string """ - DAY_MAX, DAY_LIMIT, SEAT_MAX, SEAT_LIMIT = get_delta_limits() + DAY_MAX, DAY_LIMIT, SEAT_MAX, SEAT_LIMIT = get_delta_limits(cfg) state = 'INACTIVE' if cmproc.returncode == 1 else 'ACTIVE' msg = cmproc.stdout.decode('utf8') diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..fec5fe6 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,15 @@ +import pytest + + +@pytest.fixture(scope="module") +def script_loc(request): + """Return the directory of the currently running test script""" + + return request.path.parent + + +@pytest.fixture(scope='session') +def tmpdir_session(request, tmp_path_factory): + """A tmpdir fixture for the session scope. Persists throughout the pytest session.""" + + return tmp_path_factory.mktemp(tmp_path_factory.getbasetemp().name) diff --git a/tests/test_utils.py b/tests/test_utils.py index 038dc4c..6093eb9 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,26 @@ -from timew_status.utils import get_config, get_userdirs, parse_for_tag +from datetime import timedelta + +from munch import Munch + +from timew_status.utils import ( + do_install, + get_config, + get_delta_limits, + get_state_icon, + get_userdirs, + parse_for_tag, +) + +CFG = { + "day_max": "08:00", + "day_snooze": "01:00", + "seat_max": "01:30", + "seat_snooze": "00:40", + "use_symbolic_icons": False, + "extensions_dir": "~/.timewarrior/extensions", + "install_dir": "lib/timew-addons/extensions", + "install_prefix": "/usr", +} start_txt = """ Note: '"vct-sw,refactor timew indicator config to yaml"' is a new tag. @@ -16,6 +38,21 @@ """ +def test_do_install(script_loc, tmpdir_session): + cfg = Munch.fromDict(CFG) + # destination dir + tgt_dir = tmpdir_session / 'extensions' + tgt_dir.mkdir(exist_ok=True) + cfg.extensions_dir = str(tgt_dir) + # source dir + cfg.install_prefix = str(script_loc) + cfg.install_dir = 'testdata' + # print(cfg) + + ret = do_install(cfg.toDict()) + print(ret) + + def test_get_config(): cfgobj, cfgfile = get_config() assert repr(cfgobj).startswith("Munch({'") @@ -24,6 +61,34 @@ def test_get_config(): print(cfgobj) +def test_get_delta_limits(): + for td in get_delta_limits(CFG): + assert isinstance(td, timedelta) + print(td) + tds = get_delta_limits(CFG) + print(len(tds)) + assert isinstance(tds, tuple) + assert len(tds) == 4 + + +def test_get_state_icon(): + states = ['INACTIVE', 'ACTIVE', 'WARNING', 'ERROR', 'APP'] + for state in states: + icon = get_state_icon(state, CFG) + assert 'timew' in icon or 'dialog' in icon + print(icon) + + +def test_get_state_icon_fallback(): + states = ['INACTIVE', 'ACTIVE', 'WARNING', 'ERROR', 'APP'] + cfg = Munch.fromDict(CFG) + cfg.use_symbolic_icons = True + for state in states: + icon = get_state_icon(state, cfg.toDict()) + assert 'symbolic' in icon + print(icon) + + def test_get_userdirs(): udir = get_userdirs() print(f'\nuserdir: {udir}') diff --git a/tests/testdata/onelineday.py b/tests/testdata/onelineday.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/testdata/totals.py b/tests/testdata/totals.py new file mode 100644 index 0000000..e69de29 From dbe376eee8379a3dc2f7619f0882c86c9ec9d7e2 Mon Sep 17 00:00:00 2001 From: Stephen Arnold Date: Sun, 25 Aug 2024 10:20:30 -0700 Subject: [PATCH 2/2] chg: usr: update docs and packaging, post refactoring cleanup * add symlinks so icons are available for both sphinx and GH rendering * optionally disable seat-timer by setting params to 00:00 Signed-off-by: Stephen Arnold --- .github/workflows/ci.yml | 2 +- README.rst | 138 +++++++++++++++++++++++++- docs/source/images/timew.svg | 1 + docs/source/images/timew_error.svg | 1 + docs/source/images/timew_inactive.svg | 1 + docs/source/images/timew_info.svg | 1 + docs/source/images/timew_warning.svg | 1 + images | 1 + scripts/timew-status-indicator | 6 +- setup.cfg | 5 +- src/timew_status/utils.py | 13 +-- 11 files changed, 155 insertions(+), 15 deletions(-) create mode 120000 docs/source/images/timew.svg create mode 120000 docs/source/images/timew_error.svg create mode 120000 docs/source/images/timew_inactive.svg create mode 120000 docs/source/images/timew_info.svg create mode 120000 docs/source/images/timew_warning.svg create mode 120000 images diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd2537a..8c1c5e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: sudo apt-get install -yqq software-properties-common sudo add-apt-repository -y -s ppa:nerdboy/embedded sudo apt-get -qq update - sudo apt-get install -yqq timew-report + sudo apt-get install -yqq timew-addons - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 diff --git a/README.rst b/README.rst index 6dbc091..2917934 100644 --- a/README.rst +++ b/README.rst @@ -124,6 +124,94 @@ To run the tests:: .. _Tox: https://github.com/tox-dev/tox +Installed files +--------------- + +Whether installed via OS packages or ``pip``, the installed files are +essentially the same, other than packaging-specific requirements and +generated python byte-code. In the latter case, the list of installed +files can be obtained with the following command:: + + $ python -m pip show -f timew-addons + Name: timew-addons + Version: 0.1.2.dev3+gda11428.d20240825 + Summary: A collection of timewarrior extensions and experiments + Home-page: https://github.com/sarnold/timew-addons + Author: Stephen L Arnold + Author-email: + License: + Location: /home/user/src/timew-addons/.tox/check/lib/python3.11/site-packages + Requires: munch, pycairo, PyGObject, timew-report + Required-by: + Files: + ../../../bin/timew-status-indicator + ../../../share/applications/timew-status-indicator.desktop + ../../../share/icons/hicolor/48x48/apps/timew.png + ../../../share/icons/hicolor/scalable/apps/timew.svg + ../../../share/icons/hicolor/scalable/status/timew_error.svg + ../../../share/icons/hicolor/scalable/status/timew_inactive.svg + ../../../share/icons/hicolor/scalable/status/timew_info.svg + ../../../share/icons/hicolor/scalable/status/timew_warning.svg + ../../../share/timew-addons/extensions/csv_rpt.py + ../../../share/timew-addons/extensions/onelineday.py + ../../../share/timew-addons/extensions/totals.py + timew_addons-0.1.2.dev3+gda11428.d20240825.dist-info/INSTALLER + timew_addons-0.1.2.dev3+gda11428.d20240825.dist-info/METADATA + timew_addons-0.1.2.dev3+gda11428.d20240825.dist-info/RECORD + timew_addons-0.1.2.dev3+gda11428.d20240825.dist-info/REQUESTED + timew_addons-0.1.2.dev3+gda11428.d20240825.dist-info/WHEEL + timew_addons-0.1.2.dev3+gda11428.d20240825.dist-info/top_level.txt + timew_status/__init__.py + timew_status/utils.py + +Generated files +--------------- + +On first run, the ``timew-status-indicator`` script will create its YAML +configuration file in the standard XDG location:: + + $HOME/.config/timew_status_indicator/config.yaml + +with the following contents: + +.. code-block:: yaml + + day_max: 08:00 + day_snooze: 01:00 + seat_max: 01:30 + seat_snooze: 00:40 + seat_reset_on_stop: false + use_last_tag: false + use_symbolic_icons: false + extension_script: onelineday + default_jtag_str: vct-sw,implement skeleton timew indicator + jtag_separator: ',' + loop_idle_seconds: 20 + show_state_label: false + terminal_emulator: gnome-terminal + +Edit the above file to set your preferred values. Note the default value +of ``loop_idle_seconds`` seems to be a happy medium between update rate +and wasted CPU cycles. + +Uninstalling +------------ + +Depending on how it was installed, use on or more of the following: + +* delete the cloned directory, eg, ``rm -rf src/timew-addons`` +* delete the virtual environment, eg, ``rm -rf ``.venv`` +* remove the OS package, eg, on Ubuntu: + +:: + + $ sudo apt remove timew-addons + $ sudo apt autoremove + +Finally, delete the above configuration file:: + + $ rm ~/.config/timew_status_indicator/config.yaml + Reporting examples ~~~~~~~~~~~~~~~~~~ @@ -240,11 +328,55 @@ your session startup or run it from an X terminal to get some debug output:: $ timew-status-indicator -Indicator states -################ +What exactly are we tracking? +############################# + +Simply put, we want to track work hours and seat time in the context of +the daily hours tracked via the ``timew`` command. The configuration file +contains 2 parameters each for setting desired limits, the base max value, +and an optional "snooze" period: + +:day_max: target number of daily work hours +:day_snooze: additional snooze period appended to daily max +:seat_max: max number of minutes to stay seated +:seat_snooze: additional snooze period appended to seat max + +Values for the above are given in hours and minutes formatted +as "time" strings, eg, the following sets an 8-hour max: + +.. code-block:: yaml + + day_max: "08:00" + +The seat timer can be disabled by setting both *max* and *snooze* to +zeros, ie, set both values like so:: + +.. code-block:: yaml + + seat_max: "00:00" + seat_snooze: "00:00" + + +Status indicator GUI +#################### It would not be an Appindicator_ without icons, so we use icons as one way -to show current state. +to show current state. This has nothing to do with application state; in +this case we only care about the state of our *timew tracking interval*; +note this includes the seat timer warnings when there is an active timew +tracking interval. The states and corresponding icons are shown below: + +:INACTIVE: |inactive| The state when there is no active tracking interval. +:INFO: |info| The default active state when tracking interval is open. +:WARNING: |warn| The state when either timer has reached the snooze period. +:ERROR: |err| The state when either snooze period has expired. +:APP: |app| While not a state, we use this to retrieve the app icon. + +.. |app| image:: images/timew.svg +.. |inactive| image:: images/timew_inactive.svg +.. |info| image:: images/timew_info.svg +.. |warn| image:: images/timew_warning.svg +.. |err| image:: images/timew_error.svg PyGObject references diff --git a/docs/source/images/timew.svg b/docs/source/images/timew.svg new file mode 120000 index 0000000..10dfd81 --- /dev/null +++ b/docs/source/images/timew.svg @@ -0,0 +1 @@ +../../../icons/hicolor/scalable/timew.svg \ No newline at end of file diff --git a/docs/source/images/timew_error.svg b/docs/source/images/timew_error.svg new file mode 120000 index 0000000..d8e5d71 --- /dev/null +++ b/docs/source/images/timew_error.svg @@ -0,0 +1 @@ +../../../icons/hicolor/status/timew_error.svg \ No newline at end of file diff --git a/docs/source/images/timew_inactive.svg b/docs/source/images/timew_inactive.svg new file mode 120000 index 0000000..33ffe86 --- /dev/null +++ b/docs/source/images/timew_inactive.svg @@ -0,0 +1 @@ +../../../icons/hicolor/status/timew_inactive.svg \ No newline at end of file diff --git a/docs/source/images/timew_info.svg b/docs/source/images/timew_info.svg new file mode 120000 index 0000000..150d7c9 --- /dev/null +++ b/docs/source/images/timew_info.svg @@ -0,0 +1 @@ +../../../icons/hicolor/status/timew_info.svg \ No newline at end of file diff --git a/docs/source/images/timew_warning.svg b/docs/source/images/timew_warning.svg new file mode 120000 index 0000000..16e215b --- /dev/null +++ b/docs/source/images/timew_warning.svg @@ -0,0 +1 @@ +../../../icons/hicolor/status/timew_warning.svg \ No newline at end of file diff --git a/images b/images new file mode 120000 index 0000000..35878e8 --- /dev/null +++ b/images @@ -0,0 +1 @@ +docs/source/images \ No newline at end of file diff --git a/scripts/timew-status-indicator b/scripts/timew-status-indicator index 84593ca..787d4f2 100755 --- a/scripts/timew-status-indicator +++ b/scripts/timew-status-indicator @@ -128,7 +128,7 @@ class Indicator: print(f'{new_state} state msg: {msg.strip()}') # if there is a change in state, update the icon if old_state != new_state: - self.indicator.set_icon_full(get_state_icon(new_state), new_state) + self.indicator.set_icon_full(get_state_icon(new_state, CFG), new_state) if CFG['show_state_label']: # note the second label arg should be the longest possible label str self.indicator.set_label(new_state.format().center(8), guide) @@ -238,7 +238,7 @@ class Indicator: def startd(self, source): my_tag = TAG["text"] _, svc_msg = run_cmd(action='start', tag=my_tag) - self.indicator.set_icon_full(get_state_icon('ACTIVE'), 'ACTIVE') + self.indicator.set_icon_full(get_state_icon('ACTIVE', CFG), 'ACTIVE') Notify.Notification.new("Timew status", svc_msg, None).show() def statusd(self, source): @@ -253,7 +253,7 @@ class Indicator: self.last_tag = svc_msg if CFG['use_last_tag']: TAG["text"] = self.last_tag - self.indicator.set_icon_full(get_state_icon('INACTIVE'), 'INACTIVE') + self.indicator.set_icon_full(get_state_icon('INACTIVE', CFG), 'INACTIVE') if CFG['show_state_label']: self.indicator.set_label('INACTIVE'.format().center(9), '99999999') Notify.Notification.new("Timew status", svc_msg, None).show() diff --git a/setup.cfg b/setup.cfg index 164676e..df7d9ee 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [metadata] name = timew-addons version = attr: setuptools_scm.get_version -description = A collection of timewarrior exentions and experiments +description = A collection of timewarrior extensions and experiments url = https://github.com/sarnold/timew-addons author = Stephen L Arnold email = nerdboy@gentoo.org @@ -23,6 +23,7 @@ classifiers = Topic :: Utilities keywords = + appindicator timew [options] @@ -73,7 +74,6 @@ doc = sphinx sphinx_git sphinx_rtd_theme - sphinxcontrib-apidoc rst2pdf[svgsupport] test = pytest @@ -83,6 +83,7 @@ cov = coverage_python_version all = %(cov)s + %(doc)s %(test)s [check] diff --git a/src/timew_status/utils.py b/src/timew_status/utils.py index 6c18fd7..b3377d0 100644 --- a/src/timew_status/utils.py +++ b/src/timew_status/utils.py @@ -154,12 +154,13 @@ def get_state_str(cmproc, count, cfg): if to_td(day_total) > DAY_LIMIT: state = 'ERROR' msg = f'ERROR: day limit of {DAY_LIMIT} has been exceeded\n' + msg - if SEAT_MAX < count < SEAT_LIMIT: - state = 'WARNING' - msg = f'WARNING: seat max of {SEAT_MAX} has been exceeded\n' + msg - if count > SEAT_LIMIT: - state = 'ERROR' - msg = f'ERROR: seat limit of {SEAT_LIMIT} has been exceeded\n' + msg + if cfg["seat_max"] != "00:00" and cfg["seat_snooze"] != "00:00": + if SEAT_MAX < count < SEAT_LIMIT: + state = 'WARNING' + msg = f'WARNING: seat max of {SEAT_MAX} has been exceeded\n' + msg + if count > SEAT_LIMIT: + state = 'ERROR' + msg = f'ERROR: seat limit of {SEAT_LIMIT} has been exceeded\n' + msg return msg, state