Skip to content

Commit d06e20e

Browse files
Merge pull request #69 from dvklopfenstein/dev
Creating Microsoft Word docx files is now optional
2 parents e971e08 + 46e947b commit d06e20e

File tree

11 files changed

+218
-176
lines changed

11 files changed

+218
-176
lines changed

.github/workflows/pylint.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token
33
name: Pylint
44

5-
on: [push]
5+
on: [push, pull_request]
66

77
jobs:
88
build:
@@ -24,20 +24,21 @@ jobs:
2424
# statuses: none
2525
strategy:
2626
matrix:
27-
python-version: ["3.9", "3.10", "3.11", "3.12"]
27+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
2828
steps:
2929
- uses: actions/checkout@v4
3030
- name: Set up Python ${{ matrix.python-version }}
3131
uses: actions/setup-python@v3
3232
with:
3333
python-version: ${{ matrix.python-version }}
3434
- name: Install dependencies
35+
# `pytest` is required for linting because it is imported by some tests
3536
run: |
3637
python -m pip install --upgrade pip
3738
pip install --upgrade build
3839
pip install --upgrade setuptools[core]
39-
pip install pylint
40-
pip install pytest
40+
pip install --upgrade pylint
41+
pip install --upgrade pytest
4142
pip install pytimeparse2
4243
pip install dateparser
4344
pip install python-docx

.timetracker/.gitignore

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

.timetracker/config

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

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ $ trk hours
9090
## Installation
9191
Install with [timetracker-csv](https://pypi.org/project/timetracker-csv/) pip:
9292
```
93-
$ pip install timetracker-csv
93+
$ pip install --upgrade timetracker-csv
9494
```
9595
Or install from source:
9696
```

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ keywords = [
3535
dependencies = [
3636
'tomlkit',
3737
'pytimeparse2',
38-
'dateparser',
39-
'python-docx',
38+
# 'python-docx', # OPTIONAL; lxml (does not install in some systems)
39+
# typing_extensions
40+
'dateparser', # python-dateutil, pytz, regex, tzlocal
4041
]
4142

4243
# https://pypi.org/classifiers/
@@ -46,6 +47,7 @@ classifiers=[
4647
'Intended Audience :: End Users/Desktop',
4748
'Topic :: Office/Business :: Financial :: Spreadsheet',
4849
'Topic :: Office/Business :: News/Diary',
50+
'Topic :: Office/Business :: Scheduling',
4951
'Topic :: Scientific/Engineering',
5052
'Programming Language :: Python',
5153
'Environment :: Console',

tests/test_docx.py

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,52 @@
11
#!/usr/bin/env python3
2-
"""Test Timetracker use cases"""
2+
"""Test Timetracker use cases.
3+
4+
NOTE: Package `lxml` did not load easily in cygwin, hanging in:
5+
`Building wheel for lxml (pyproject.toml) ...`
6+
7+
Therefore, `docx` (`pip install python-docx`), is optional.
8+
I successfully installed it in cygwin by:
9+
$ git clone git@github.com:lxml/lxml.git
10+
$ cd lxml
11+
$ python setup.py build
12+
$ python setup.py install
13+
14+
"""
315

4-
#from os import environ
5-
#from datetime import timedelta
6-
#from timeit import default_timer
7-
#from timetracker.consts import DIRTRK
8-
#from timetracker.cli import Cli
916
from datetime import datetime
1017
from datetime import timedelta
11-
from timetracker.docx import WordDoc
18+
19+
from timetracker.docx import write_doc
20+
from timetracker.docx import write_invoice
21+
from timetracker.cmd.invoice import _get_billable_timeslots
22+
1223
from timetracker.csvfile import CsvFile
1324
from timetracker.epoch.text import get_data_formatted
1425
from tests.pkgtttest.cmpstr import get_filename
1526

16-
# pylint: disable=fixme
1727

28+
def test_docx_report():
29+
"""Test Timetracker report writing"""
30+
print('\nTEST REPORT WRITING...')
31+
filename = get_filename('docxtest_report.docx')
32+
##doc = WordDoc(get_data_formatted(_get_datadt()))
33+
##doc.write_doc(filename)
34+
time_formatted = get_data_formatted(_get_datadt())
35+
exp = _get_exp_worddoc()
36+
assert (act := write_doc(filename, time_formatted)) == exp, f'WordDoc: EXP({exp}) != ACT({act})'
1837

19-
def test_docx():
38+
def test_docx_invoice():
2039
"""Test Timetracker use cases"""
21-
filename = get_filename('docxtest.docx')
22-
doc = WordDoc(get_data_formatted(_get_datadt()))
23-
doc.write_doc(filename)
24-
# pylint: disable=line-too-long
25-
# make clobber
26-
27-
# trk
28-
# Run `trk init` to initialize time-tracking for the project in
29-
30-
# trk
31-
# Run `trk init` to initialize time-tracking for the project in
40+
print('\nTEST INVOICE WRITING...')
41+
filename = get_filename('docxtest_invoice.docx')
42+
##doc = WordDoc(get_data_formatted(_get_datadt()))
43+
##doc.write_doc(filename)
44+
##time_formatted = get_data_formatted(_get_datadt())
45+
nttimeslots = _get_datadt()
46+
exp = _get_exp_worddoc()
47+
billable = _get_billable_timeslots(nttimeslots, hourly_rate=350)
48+
assert (act := write_invoice(filename, billable)) == exp, f'WordDoc: EXP({exp}) != ACT({act})'
3249

33-
# trk init
34-
# Initialized timetracker directory:
35-
36-
# trk init
37-
# Trk repository already initialized:
38-
39-
# trk start
40-
# Timetracker started Wed 03:21 PM: 2025-02-05 15:21:36.452917 for project 'timetracker' ID=username
41-
42-
# trk start
43-
# Do `trk stop -m "task description"` to stop tracking this time unit
44-
45-
# trk stop
46-
# usage: timetracker stop [-h] -m MESSAGE [--activity ACTIVITY] [-t [TAGS ...]]
47-
# timetracker stop: error: the following arguments are required: -m/--message
48-
49-
# trk stop -m 'test stopped'
50-
# Timer stopped; Elapsed H:M:S=0:00:19.531226 appended to timetracker_timetracker_username.csv
5150

5251
def _get_datadt():
5352
# pylint: disable=line-too-long
@@ -74,5 +73,16 @@ def _get_datadt():
7473
]
7574

7675

76+
def _get_exp_worddoc():
77+
try:
78+
# pylint: disable=import-outside-toplevel
79+
# pylint: disable=unused-import
80+
import docx
81+
return True
82+
except ModuleNotFoundError:
83+
return False
84+
85+
7786
if __name__ == '__main__':
78-
test_docx()
87+
test_docx_report()
88+
test_docx_invoice()

timetracker/cmd/invoice.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
from timetracker.cmd.common import str_uninitialized
1212
from timetracker.epoch.calc import str_td
1313
from timetracker.csvfile import CsvFile
14-
from timetracker.docx import WordDoc
14+
#from timetracker.docx import WordDoc
15+
from timetracker.docx import write_invoice
1516

1617
NtPaid = namedtuple('NtPaid', 'span cum_time due desc')
1718

@@ -64,12 +65,13 @@ def run_invoice(fcsv, fout_docx='invoice.docx', hourly_rate=100): #, report_all
6465
billable = _get_billable_timeslots(ntcsv.results, hourly_rate)
6566
num_billable = len(billable)
6667
if num_billable != 0:
67-
doc = WordDoc()
68-
##doc.write_doc(fout_docx)
69-
doc.write_invoice(fout_docx, billable)
68+
##doc = WordDoc()
69+
##doc.write_invoice(fout_docx, billable)
70+
write_invoice(fout_docx, billable)
7071
print(f'WROTE: {num_billable} billable rows of {num_all} total rows: {fout_docx}')
7172
else:
72-
print('Use `--bill-all` to get an invoice for all timeslots')
73+
print('INFO: NO TIMESLOTS MARKED BILLABLE; '
74+
'Use `--bill-all` to get an invoice for all timeslots')
7375
return ntcsv
7476

7577
def _get_billable_timeslots(nts, hourly_rate, currency_sym='$'):

timetracker/cmd/projects.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ def _show_projects(cfg_glb, show_exists=False):
3232
print(f'{len(proj_cfgs)} projects listed in global config: {cfg_glb.filename}')
3333
if not show_exists:
3434
for proj, pcfg in proj_cfgs:
35-
print(f' {proj:25} {dirname(pcfg)}')
35+
print(f' {proj:25} {dirname(dirname(pcfg))}')
3636
else:
3737
for proj, pcfg in proj_cfgs:
38-
print(f'exists({int(exists(pcfg))}) {proj:25} {dirname(pcfg)}')
38+
print(f'exists({int(exists(pcfg))}) {proj:25} {dirname(dirname(pcfg))}')
3939
else:
4040
print(f'There are no projects in {cfg_glb.filename}')
4141

timetracker/cmd/report.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
from timetracker.cmd.common import no_csv
99
from timetracker.cmd.common import str_uninitialized
1010
from timetracker.csvfile import CsvFile
11-
from timetracker.docx import WordDoc
1211
from timetracker.epoch.text import get_data_formatted
1312
from timetracker.csvrun import chk_n_convert
1413
from timetracker.report import prt_basic
14+
from timetracker.docx import write_doc
1515

1616

1717
def cli_run_report(fcfgproj, args):
@@ -55,8 +55,9 @@ def run_report(fcsv, fout_docx):
5555
timefmtd = get_data_formatted(ntcsv.results)
5656
prt_basic(timefmtd)
5757
if fout_docx:
58-
doc = WordDoc(timefmtd)
59-
doc.write_doc(fout_docx)
58+
##doc = get_worddoc(timefmtd)
59+
##doc.write_doc(fout_docx)
60+
write_doc(fout_docx, timefmtd)
6061
return ntcsv
6162

6263

timetracker/cmd/stop.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def run_stop(cfgproj, uname, csvfields, stop_at=None, **kwargs):
7070
def _msg_stop_complete(fcsv, delta, stoptime, quiet):
7171
"""Finish stopping"""
7272
if not quiet:
73-
print(f'Timetracker stopped at: {stoptime.strftime(consts.FMTDT_H)}: {stoptime}\n'
73+
print(f'Timetracker stopped at: {stoptime.strftime(consts.FMTDT_H)}\n'
7474
f'Elapsed H:M:S {delta} appended to {get_shortest_name(fcsv)}')
7575

7676

0 commit comments

Comments
 (0)