Skip to content

Commit 62c2b34

Browse files
Merge pull request #59 from dvklopfenstein/dev
Modified import to improve runtime speed
2 parents 9d3b6c8 + bf19a12 commit 62c2b34

File tree

26 files changed

+372
-270
lines changed

26 files changed

+372
-270
lines changed

.timetracker/.gitignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

.timetracker/config

Lines changed: 0 additions & 6 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# Summary
44
* [**Unreleased**](#unreleased)
5+
* [**Release 2025-06-21 v0.7a0**](#release-2025-06-21-v07a0) Much added functionality for invoicing
56
* [**Release 2025-05-27 v0.6a0**](#release-2025-05-27-v06a0) Added functions to collect various groups of csv files
67
* [**Release 2025-05-18 v0.5a7**](#release-2025-05-18-v05a7) Add starttime to `report`; Bug fix in `cancel`
78
* [**Release 2025-05-16 v0.5a6**](#release-2025-05-16-v05a6) Add options to `projects` command; Fix `report` command
@@ -30,6 +31,8 @@
3031
# Details
3132

3233
## Unreleased
34+
35+
## Release 2025-06-21 v0.7a0
3336
* ADDED command, `trk invoice`
3437
* ADDED command option, `-b` or `--billable` to `trk stop`
3538
to add a `Billable` tag when running `tag stop`
@@ -39,6 +42,7 @@
3942
* ADDED `--no-git-add` and `-A` options to the `trk init` command
4043
* CHANGED "Run `trk init`" message so it is clearer that no trk command
4144
will be run until the project is initialized
45+
* CHANGED imports to improve speed
4246

4347
## Release 2025-05-27 v0.6a0
4448
* ADDED `trk hours` options `--global` to show hours for all projects for a single username

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
77
[project]
88
name = "timetracker-csv"
99
description = "Pandas-friendly time tracking from the CLI"
10-
version = "0.6a0"
10+
version = "0.7a0"
1111
license = "AGPL-3.0-or-later"
1212
authors = [
1313
{name = 'DV Klopfenstein, PhD', email = 'dvklopfenstein@protonmail.com'},

tests/pkgtttest/fncs.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""Command line interface (CLI) for timetracking"""
2+
# https://stackoverflow.com/questions/42703908/how-do-i-use-importlib-lazyloader
3+
# https://python.plainenglish.io/lazy-imports-the-secret-to-faster-python-code-c33ae9eb1b13
4+
5+
__copyright__ = 'Copyright (C) 2025-present, DV Klopfenstein, PhD. All rights reserved.'
6+
__author__ = "DV Klopfenstein, PhD"
7+
8+
9+
from timetracker.cmd.init import cli_run_init
10+
from timetracker.cmd.start import cli_run_start
11+
from timetracker.cmd.stop import cli_run_stop
12+
from timetracker.cmd.projects import cli_run_projects
13+
from timetracker.cmd.cancel import cli_run_cancel
14+
from timetracker.cmd.hours import cli_run_hours
15+
from timetracker.cmd.csv import cli_run_csv
16+
from timetracker.cmd.report import cli_run_report
17+
from timetracker.cmd.invoice import cli_run_invoice
18+
from timetracker.cmd.paid import cli_run_paid
19+
#from timetracker.cmd.tag import cli_run_tag
20+
from timetracker.cmd.activity import cli_run_activity
21+
#from timetracker.cmd.csvloc import cli_run_csvloc
22+
23+
24+
FNCS = {
25+
'init' : cli_run_init,
26+
'start' : cli_run_start,
27+
'stop' : cli_run_stop,
28+
'cancel' : cli_run_cancel,
29+
'hours' : cli_run_hours,
30+
'csv' : cli_run_csv,
31+
'report' : cli_run_report,
32+
'invoice' : cli_run_invoice,
33+
'paid' : cli_run_paid,
34+
#'tag' : cli_run_tag,
35+
'activity' : cli_run_activity,
36+
'projects' : cli_run_projects,
37+
#'csvloc' : cli_run_csvloc,
38+
}
39+
40+
41+
# Copyright (C) 2025-present, DV Klopfenstein, PhD. All rights reserved.

tests/test_tt_fncs.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env python
2+
"""Test speed savings from lazy import"""
3+
4+
from timeit import default_timer
5+
from datetime import timedelta
6+
7+
8+
def test_tt_fncs(num_p_batch=1):
9+
"""Test speed savings from lazy import"""
10+
# pylint: disable=import-outside-toplevel
11+
mintime_slow = timedelta(seconds=1000)
12+
for _ in range(num_p_batch):
13+
tic = default_timer()
14+
import tests.pkgtttest.fncs
15+
del tests
16+
mintime_slow = min(mintime_slow, timedelta(seconds=default_timer()-tic))
17+
print(mintime_slow)
18+
19+
mintime_fast = timedelta(seconds=1000)
20+
for _ in range(num_p_batch):
21+
tic = default_timer()
22+
import timetracker.cmd.fncs
23+
del timetracker
24+
mintime_fast = min(mintime_fast, timedelta(seconds=default_timer()-tic))
25+
print(mintime_fast)
26+
27+
assert mintime_fast < mintime_slow
28+
29+
faster = mintime_slow.total_seconds()/mintime_fast.total_seconds()
30+
print(f'{faster:10.1f} times faster is import fncs compared to regular import')
31+
32+
33+
def test_tt_ospath(num_p_batch=1):
34+
"""Test speed savings from lazy import"""
35+
# pylint: disable=import-outside-toplevel,too-many-locals
36+
mintime_slow = timedelta(seconds=1000)
37+
for _ in range(num_p_batch):
38+
tic = default_timer()
39+
from os.path import exists
40+
from os.path import relpath
41+
from os.path import abspath
42+
from os.path import dirname
43+
from os.path import join
44+
from os.path import ismount
45+
from os.path import basename
46+
from os.path import normpath
47+
from os.path import realpath
48+
from logging import debug
49+
from timetracker.consts import DIRTRK
50+
del exists
51+
del relpath
52+
del abspath
53+
del dirname
54+
del join
55+
del ismount
56+
del basename
57+
del normpath
58+
del realpath
59+
del debug
60+
del DIRTRK
61+
mintime_slow = min(mintime_slow, timedelta(seconds=default_timer()-tic))
62+
print(mintime_slow)
63+
64+
mintime_fast = timedelta(seconds=1000)
65+
for _ in range(num_p_batch):
66+
tic = default_timer()
67+
import os.path as os_path
68+
del os_path
69+
mintime_fast = min(mintime_fast, timedelta(seconds=default_timer()-tic))
70+
print(mintime_fast)
71+
72+
assert mintime_fast < mintime_slow
73+
74+
faster = mintime_slow.total_seconds()/mintime_fast.total_seconds()
75+
print(f'{faster:10.1f} times faster is import fncs compared to regular import')
76+
77+
78+
if __name__ == '__main__':
79+
test_tt_fncs()
80+
test_tt_ospath()

tests/test_tt_getdt.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,18 @@ def test_tt_getdt(fcsv='timetrials_datatime.csv'):
2323

2424

2525
def _run(nto):
26+
# pylint: disable=too-many-locals
2627
timedata = []
2728
#cmp_time = re_compile(r'((\d{1,2}):){0,2}(\d{1,2})\s*(?P<AM_PM>[aApP][mM])')
2829
# pylint: disable=line-too-long
29-
cmp_time = re_compile(r'((?P<hour>\d{1,2})[^-/_](:(?P<minute>\d{1,2}))?[^-/_](:(?P<second>\d{1,2}))?\s*(?P<AM_PM>[aApP][mM])?)')
30+
cmp_time = re_compile(r'((?P<hour>\d{1,2})(:(?P<minute>\d{1,2}))?(:(?P<second>\d{1,2}))?\s*(?P<AM_PM>[aApP][mM])?)')
3031
cmp_date = re_compile(r'((?P<year>\d{4})[-/_]?)?(?P<month>\d{1,2})[-/_](?P<day>\d{1,2})')
3132
print(f'NOW: {NOW}')
3233
for timestr, expdct in TIMESTRS.items():
34+
# pylint: disable=too-many-locals
3335
print(f'\nTIMESTR({timestr})')
3436
tic = default_timer()
35-
print("SEARCH FOR TIME:", cmp_time.search(timestr))
37+
print("SEARCH FOR TIME:", (m := cmp_time.search(timestr)), m.groupdict() if m else '')
3638
print("SEARCH FOR DATE:", cmp_date.search(timestr))
3739
tt0 = timedelta(seconds=default_timer()-tic)
3840
print(f'{tt0} re ({timestr})') # {dta}')
@@ -46,7 +48,7 @@ def _run(nto):
4648
tic = default_timer()
4749
dtb = _conv_datetime(timestr, NOW)
4850
ttb = timedelta(seconds=default_timer()-tic)
49-
#print(f'{ttb} _conv_datetime({timestr}) {dtb}')
51+
print(f'{ttb} _conv_datetime({timestr}) {dtb}')
5052

5153
tic = default_timer()
5254
dtc = _conv_timedelta(timestr)

timetracker/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
__copyright__ = 'Copyright (C) 2025-present, DV Klopfenstein, PhD. All rights reserved'
44
__author__ = 'DV Klopfenstein, PhD'
5-
__version__ = '0.6a0'
5+
__version__ = '0.7a0'
66

77
# Copyright (C) 2025-present, DV Klopfenstein, PhD. All rights reserved

timetracker/cfg/cfg_global.py

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,12 @@
33
__copyright__ = 'Copyright (C) 2025-present, DV Klopfenstein, PhD. All rights reserved.'
44
__author__ = "DV Klopfenstein, PhD"
55

6-
from os.path import isabs
7-
from os.path import isdir
6+
import os.path as op
87
from os.path import exists
9-
from os.path import dirname
10-
from os.path import join
11-
from os.path import abspath
12-
from os.path import relpath
138
from logging import debug
149
from collections import namedtuple
1510

16-
from tomlkit import comment
17-
from tomlkit import document
18-
from tomlkit import nl
19-
from tomlkit import array
11+
import tomlkit
2012
from tomlkit.toml_file import TOMLFile
2113

2214
from timetracker.cfg.tomutils import read_config
@@ -101,8 +93,8 @@ def _err_reinit(self, ntcfg):
10193

10294
# -------------------------------------------------------------
10395
def _chk_global_dir(self):
104-
dir_global = dirname(self.filename)
105-
if exists(dir_global) and isdir(dir_global) or dir_global == '':
96+
dir_global = op.dirname(self.filename)
97+
if exists(dir_global) and op.isdir(dir_global) or dir_global == '':
10698
return
10799
raise NotADirectoryError(f'{dir_global}\n'
108100
f'Directory for global config does not exist({dir_global})\n'
@@ -112,7 +104,7 @@ def _chk_global_dir(self):
112104
def _add_project(self, doc, project, fcfgproj):
113105
"""Add a project to the global config file, if it is not already present"""
114106
debug('CfgGlobal _add_project(%s, %s)', project, fcfgproj)
115-
assert isabs(fcfgproj), f'CfgGlobal._add_project(...) cfg NOT abspath: {fcfgproj}'
107+
assert op.isabs(fcfgproj), f'CfgGlobal._add_project(...) cfg NOT abs path: {fcfgproj}'
116108
debug('CfgGlobal %s', doc)
117109
# If project is not already in global config
118110
if self._noproj(doc, project, fcfgproj):
@@ -150,23 +142,23 @@ def _wr_project_init(self, project, fcfgproj):
150142
def _get_docprt(self, doc):
151143
doc_cur = doc.copy()
152144
##truehome = expanduser('~')
153-
dirhome = dirname(self.filename)
145+
dirhome = op.dirname(self.filename)
154146
for idx, (projname, projdir) in enumerate(doc['projects'].unwrap()):
155-
##pdir = relpath(abspath(projdir), truehome)
156-
##pdir = relpath(abspath(projdir), dirhome)
147+
##pdir = op.relpath(op.abspath(projdir), truehome)
148+
##pdir = op.relpath(op.abspath(projdir), dirhome)
157149
##if pdir[:2] != '..':
158-
if has_homedir(dirhome, abspath(projdir)):
159-
##pdir = join('~', pdir)
160-
pdir = join('~', relpath(abspath(projdir), dirhome))
150+
if has_homedir(dirhome, op.abspath(projdir)):
151+
##pdir = op.join('~', pdir)
152+
pdir = op.join('~', op.relpath(op.abspath(projdir), dirhome))
161153
doc_cur['projects'][idx] = [projname, pdir]
162154
debug('CFGGLOBAL XXXXXXXXXXX %20s %s', projname, pdir)
163155
return doc_cur
164156

165157
def _get_new_doc(self):
166-
doc = document()
167-
doc.add(comment("TimeTracker global configuration file"))
168-
doc.add(nl())
169-
arr = array()
158+
doc = tomlkit.document()
159+
doc.add(tomlkit.comment("TimeTracker global configuration file"))
160+
doc.add(tomlkit.nl())
161+
arr = tomlkit.array()
170162
arr.multiline(True)
171163
doc["projects"] = arr
172164
return doc

0 commit comments

Comments
 (0)