Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More tests, improved docs #5

Merged
merged 2 commits into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
206 changes: 194 additions & 12 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -161,7 +249,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::
Expand All @@ -175,6 +265,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
~~~~~~~~~~~~~~~~

Expand All @@ -191,20 +322,78 @@ 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

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. 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
--------------------

* 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
Expand Down Expand Up @@ -233,18 +422,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
Expand Down
1 change: 1 addition & 0 deletions docs/source/images/timew.svg
1 change: 1 addition & 0 deletions docs/source/images/timew_error.svg
1 change: 1 addition & 0 deletions docs/source/images/timew_inactive.svg
1 change: 1 addition & 0 deletions docs/source/images/timew_info.svg
1 change: 1 addition & 0 deletions docs/source/images/timew_warning.svg
1 change: 1 addition & 0 deletions images
29 changes: 22 additions & 7 deletions scripts/timew-status-indicator
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ from timew_status import (
DEBUG,
TAG,
__version__,
do_install,
get_config,
get_state_icon,
get_state_str,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -120,14 +121,14 @@ 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)
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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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']])
Expand All @@ -223,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):
Expand All @@ -238,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()
Expand Down
Loading