Initial Commit

This commit is contained in:
Aleksander Cynarski 2018-03-26 19:41:04 +02:00
commit c57ccab039
4 changed files with 209 additions and 0 deletions

13
.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
*.pyc
bin/*
lib/*
src/*
man/*
dist/*
docs/build/*
include/*
.Python
*egg*
share/*
.tox/*
build/*

61
readme.rst Normal file
View File

@ -0,0 +1,61 @@
Taskwarrior Time Tracking Hook
==============================
Ensure you have taskwarrior `2.4.x` or higher.
Install
+++++++
Install using pip::
pip install taskwarrior-time-tracking-hook
And add it to your Taskwarrior hooks::
mkdir -p ~/.task/hooks
ln -s `which taskwarrior_time_tracking_hook` ~/.task/hooks/on-modify.timetracking
Add the ``totalactivetime`` user defined attribute configuration::
task config uda.totalactivetime.type duration
task config uda.totalactivetime.label Total active time
task config uda.totalactivetime.values ''
Add to reports (replace list with whichever report type you want to modify)::
task show report.list.labels
ID,Active,Age,...,Urg
task show report.list.columns
id,start.age,entry.age,...,urgency
task config report.list.labels 'ID,Active,Age,Time Spent,...,Urg'
task config report.list.labels 'id,start.age,entry.age,totalactivetime,...,urgency'
Usage
+++++
Use ``task <TASK ID> start`` and ``task <TASK ID> stop`` to record when you have
started and stopped working on tasks.
Tracked time is stored in a task duration attribute named ``totalactivetime``
holding the total number of seconds that the task was active.
By default, this plugin allows you to have one task active at a time. You can
change this by setting `max_active_tasks` in `taskrc` to a value greater than 1.
Un-install
++++++++++
Delete the hook::
rm ~/.task/hooks/on-modify.timetracking
Remove the User Defined Attribute (UDA) configuration::
task config uda.totalactivetime.values
task config uda.totalactivetime.label
task config uda.totalactivetime.type
Remove the Python program::
pip uninstall taskwarrior-time-tracking-hook

25
setup.py Normal file
View File

@ -0,0 +1,25 @@
from setuptools import setup, find_packages
setup(
name='taskwarrior-daily-timetracking-hook',
version='0.0.1',
url='https://git.cynarski.pl/paramah/taskwarrior-daily-timetracking-hook.git',
description=(
'Track your daily time in a UDA in taskwarrior'
),
author='Aleksander Cynarski',
author_email='Aleksander Cynarski',
classifiers=[
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
],
install_requires=[
"taskw"
],
packages=find_packages(),
entry_points={
'console_scripts': [
'taskwarrior_daily_timetracking_hook = taskwarrior_daily_timetracking_hook:cmdline'
],
},
)

View File

@ -0,0 +1,110 @@
#!/usr/bin/env python
import datetime
import json
import re
import sys
import subprocess
from taskw import TaskWarrior
TIME_FORMAT = '%Y%m%dT%H%M%SZ'
UDA_KEY = 'dailyactivetime'
w = TaskWarrior()
config = w.load_config()
if ('max_active_tasks' in config):
MAX_ACTIVE = int(config['max_active_tasks'])
else:
MAX_ACTIVE = 1
ISO8601DURATION = re.compile(
"P((\d*)Y)?((\d*)M)?((\d*)D)?T((\d*)H)?((\d*)M)?((\d*)S)?")
# Convert duration string into a timedelta object.
# Valid formats for duration_str include
# - int (in seconds)
# - string ending in seconds e.g "123seconds"
# - ISO-8601: e.g. "PT1H10M31S"
def duration_str_to_time_delta(duration_str):
if (duration_str.startswith("P")):
match = ISO8601DURATION.match(duration_str)
if (match):
year = match.group(2)
month = match.group(4)
day = match.group(6)
hour = match.group(8)
minute = match.group(10)
second = match.group(12)
value = 0
if (second):
value += int(second)
if (minute):
value += int(minute)*60
if (hour):
value += int(hour)*3600
if (day):
value += int(day)*3600*24
if (month):
# Assume a month is 30 days for now.
value += int(month)*3600*24*30
if (year):
# Assume a year is 365 days for now.
value += int(year)*3600*24*365
else:
value = int(duration_str)
elif (duration_str.endswith("seconds")):
value = int(duration_str.rstrip("seconds"))
else:
value = int(duration_str)
return datetime.timedelta(seconds=value)
def main():
original = json.loads(sys.stdin.readline())
modified = json.loads(sys.stdin.readline())
# An inactive task has just been started.
if 'start' in modified and 'start' not in original:
# Check if `task +ACTIVE count` is greater than MAX_ACTIVE. If so
# prevent this task from starting.
p = subprocess.Popen(
['task', '+ACTIVE', 'status:pending', 'count', 'rc.verbose:off'],
stdout=subprocess.PIPE)
out, err = p.communicate()
count = int(out.rstrip())
if count >= MAX_ACTIVE:
print("Only %d task(s) can be active at a time. "
"See 'max_active_tasks' in .taskrc." % (MAX_ACTIVE))
sys.exit(1)
# An active task has just been stopped.
if 'start' in original and 'start' not in modified:
# Let's see how much time has elapsed
start = datetime.datetime.strptime(original['start'], TIME_FORMAT)
end = datetime.datetime.utcnow()
if UDA_KEY not in modified:
modified[UDA_KEY] = 0
this_duration = (end - start)
total_duration = (
this_duration
+ duration_str_to_time_delta(str(modified[UDA_KEY]))
)
print(
"Daily Time Tracked: %s (%s in this instance)" % (
total_duration,
this_duration,
)
)
modified[UDA_KEY] = str(int(
total_duration.days * (60 * 60 * 24) + total_duration.seconds
)) + "seconds"
return json.dumps(modified, separators=(',',':'))
def cmdline():
sys.stdout.write(main())
if __name__ == '__main__':
cmdline()