new: basic arbtt module
This commit is contained in:
parent
ad924ebca8
commit
86497f9b13
7 changed files with 107 additions and 3 deletions
93
my/arbtt.py
Normal file
93
my/arbtt.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
'''
|
||||||
|
[[https://github.com/nomeata/arbtt#arbtt-the-automatic-rule-based-time-tracker][Arbtt]] time tracking
|
||||||
|
'''
|
||||||
|
|
||||||
|
REQUIRES = ['ijson', 'cffi']
|
||||||
|
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Sequence, Iterable, List, Optional
|
||||||
|
|
||||||
|
|
||||||
|
def inputs() -> Sequence[Path]:
|
||||||
|
try:
|
||||||
|
from my.config import arbtt as user_config
|
||||||
|
except ImportError:
|
||||||
|
from .core.warnings import low
|
||||||
|
low("Couldn't find 'arbtt' config section, falling back to the default capture.log (usually in HOME dir). Add 'arbtt' section with logfiles = '' to suppress this warning.")
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
from .core import get_files
|
||||||
|
return get_files(user_config.logfiles)
|
||||||
|
|
||||||
|
|
||||||
|
from .core import dataclass, Json, Res, PathIsh, datetime_aware
|
||||||
|
from .core.common import isoparse
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Entry:
|
||||||
|
'''
|
||||||
|
For the format reference, see
|
||||||
|
https://github.com/nomeata/arbtt/blob/e120ad20b9b8e753fbeb02041720b7b5b271ab20/src/DumpFormat.hs#L39-L46
|
||||||
|
'''
|
||||||
|
|
||||||
|
json: Json
|
||||||
|
# inactive time -- in ms
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dt(self) -> datetime_aware:
|
||||||
|
# contains utc already
|
||||||
|
ds = self.json['date']
|
||||||
|
elen = 27
|
||||||
|
lds = len(ds)
|
||||||
|
if lds < elen:
|
||||||
|
# ugh. sometimes contains less that 6 decimal points
|
||||||
|
ds = ds[:-1] + '0' * (elen - lds) + 'Z'
|
||||||
|
elif lds > elen:
|
||||||
|
# ahd sometimes more...
|
||||||
|
ds = ds[:elen - 1] + 'Z'
|
||||||
|
|
||||||
|
return isoparse(ds)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active(self) -> Optional[str]:
|
||||||
|
# NOTE: WIP, might change this in the future...
|
||||||
|
ait = (w for w in self.json['windows'] if w['active'])
|
||||||
|
a = next(ait, None)
|
||||||
|
if a is None:
|
||||||
|
return None
|
||||||
|
a2 = next(ait, None)
|
||||||
|
assert a2 is None, a2 # hopefully only one can be active in a time?
|
||||||
|
|
||||||
|
p = a['program']
|
||||||
|
t = a['title']
|
||||||
|
# todo perhaps best to keep it structured, e.g. for influx
|
||||||
|
return f'{p}: {t}'
|
||||||
|
|
||||||
|
|
||||||
|
# todo multiple threads? not sure if would help much... (+ need to find offset somehow?)
|
||||||
|
def entries() -> Iterable[Res[Entry]]:
|
||||||
|
inps = list(inputs())
|
||||||
|
|
||||||
|
base: List[PathIsh] = ['arbtt-dump', '--format=json']
|
||||||
|
|
||||||
|
cmds: List[List[PathIsh]]
|
||||||
|
if len(inps) == 0:
|
||||||
|
cmds = [base] # rely on default
|
||||||
|
else:
|
||||||
|
# otherise, 'merge' them
|
||||||
|
cmds = [base + ['--logfile', f] for f in inps]
|
||||||
|
|
||||||
|
import ijson.backends.yajl2_cffi as ijson # type: ignore
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
for cmd in cmds:
|
||||||
|
with Popen(cmd, stdout=PIPE) as p:
|
||||||
|
out = p.stdout; assert out is not None
|
||||||
|
for json in ijson.items(out, 'item'):
|
||||||
|
yield Entry(json=json)
|
||||||
|
|
||||||
|
|
||||||
|
from .core import stat, Stats
|
||||||
|
def stats() -> Stats:
|
||||||
|
return stat(entries)
|
|
@ -69,3 +69,7 @@ class time:
|
||||||
|
|
||||||
class orgmode:
|
class orgmode:
|
||||||
paths: Paths
|
paths: Paths
|
||||||
|
|
||||||
|
|
||||||
|
class arbtt:
|
||||||
|
logfiles: Paths
|
||||||
|
|
|
@ -4,6 +4,7 @@ from .common import get_files
|
||||||
from .common import LazyLogger
|
from .common import LazyLogger
|
||||||
from .common import warn_if_empty
|
from .common import warn_if_empty
|
||||||
from .common import stat, Stats
|
from .common import stat, Stats
|
||||||
|
from .common import datetime_naive, datetime_aware
|
||||||
|
|
||||||
from .cfg import make_config
|
from .cfg import make_config
|
||||||
from .util import __NOT_HPI_MODULE__
|
from .util import __NOT_HPI_MODULE__
|
||||||
|
|
|
@ -552,3 +552,7 @@ def to_jsons(it) -> Iterable[Json]:
|
||||||
yield error_to_json(r)
|
yield error_to_json(r)
|
||||||
else:
|
else:
|
||||||
yield asdict(r)
|
yield asdict(r)
|
||||||
|
|
||||||
|
|
||||||
|
datetime_naive = datetime
|
||||||
|
datetime_aware = datetime
|
||||||
|
|
|
@ -67,9 +67,8 @@ def pages() -> List[Res[Page]]:
|
||||||
return sort_res_by(_dal().pages(), key=key)
|
return sort_res_by(_dal().pages(), key=key)
|
||||||
|
|
||||||
|
|
||||||
# todo not public api yet
|
from .core import stat, Stats
|
||||||
def stats():
|
def stats() -> Stats:
|
||||||
from .core import stat
|
|
||||||
return {
|
return {
|
||||||
**stat(highlights),
|
**stat(highlights),
|
||||||
**stat(pages),
|
**stat(pages),
|
||||||
|
|
|
@ -41,6 +41,7 @@ TsLatLon = Tuple[int, int, int]
|
||||||
|
|
||||||
def _iter_via_ijson(fo) -> Iterable[TsLatLon]:
|
def _iter_via_ijson(fo) -> Iterable[TsLatLon]:
|
||||||
# ijson version takes 25 seconds for 1M items (without processing)
|
# ijson version takes 25 seconds for 1M items (without processing)
|
||||||
|
# todo extract to common?
|
||||||
try:
|
try:
|
||||||
# pip3 install ijson cffi
|
# pip3 install ijson cffi
|
||||||
import ijson.backends.yajl2_cffi as ijson # type: ignore
|
import ijson.backends.yajl2_cffi as ijson # type: ignore
|
||||||
|
|
2
tox.ini
2
tox.ini
|
@ -76,6 +76,7 @@ commands =
|
||||||
hpi module install my.reddit
|
hpi module install my.reddit
|
||||||
hpi module install my.stackexchange.stexport
|
hpi module install my.stackexchange.stexport
|
||||||
hpi module install my.pinboard
|
hpi module install my.pinboard
|
||||||
|
hpi module install my.arbtt
|
||||||
|
|
||||||
# todo fuck. -p my.github isn't checking the subpackages?? wtf...
|
# todo fuck. -p my.github isn't checking the subpackages?? wtf...
|
||||||
# guess it wants .pyi file??
|
# guess it wants .pyi file??
|
||||||
|
@ -94,6 +95,7 @@ commands =
|
||||||
-p my.location.google \
|
-p my.location.google \
|
||||||
-p my.time.tz.via_location \
|
-p my.time.tz.via_location \
|
||||||
-p my.calendar.holidays \
|
-p my.calendar.holidays \
|
||||||
|
-p my.arbtt \
|
||||||
--txt-report .coverage.mypy-misc \
|
--txt-report .coverage.mypy-misc \
|
||||||
--html-report .coverage.mypy-misc \
|
--html-report .coverage.mypy-misc \
|
||||||
{posargs}
|
{posargs}
|
||||||
|
|
Loading…
Add table
Reference in a new issue