Skip to content

Commit

Permalink
Merge pull request #5 from sarnold/docs_and_tests
Browse files Browse the repository at this point in the history
More tests, improved docs
  • Loading branch information
sarnold authored Aug 25, 2024
2 parents 5309db7 + dbe376e commit d90cdd1
Show file tree
Hide file tree
Showing 16 changed files with 355 additions and 43 deletions.
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

0 comments on commit d90cdd1

Please sign in to comment.