Initial Commit
This commit is contained in:
commit
c57ccab039
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
*.pyc
|
||||
bin/*
|
||||
lib/*
|
||||
src/*
|
||||
man/*
|
||||
dist/*
|
||||
docs/build/*
|
||||
include/*
|
||||
.Python
|
||||
*egg*
|
||||
share/*
|
||||
.tox/*
|
||||
build/*
|
61
readme.rst
Normal file
61
readme.rst
Normal 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
25
setup.py
Normal 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'
|
||||
],
|
||||
},
|
||||
)
|
110
taskwarrior_daily_timetracking_hook/__init__.py
Normal file
110
taskwarrior_daily_timetracking_hook/__init__.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user