-
Notifications
You must be signed in to change notification settings - Fork 1
/
tt_audit_report.py
194 lines (162 loc) · 6.58 KB
/
tt_audit_report.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
"""TagTracker by Julias Hocking.
Audit report functions for the TagTracker suite.
Copyright (C) 2023-2024 Julias Hocking and Todd Glover
Notwithstanding the licensing information below, this code may not
be used in a commercial (for-profit, non-profit or government) setting
without the copyright-holder's written consent.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import common.tt_constants as k
from common.tt_time import VTime
from common.tt_tag import TagID
from common.tt_trackerday import TrackerDay
import common.tt_util as ut
import tt_printer as pr
import client_base_config as cfg
import tt_reports as rep
DEFAULT_RETIRED_TAG_STR = " ●"
# def notes_bit(day: OldTrackerDay) -> None:
# """Add a 'notes' section to a report."""
# pr.iprint()
# pr.iprint("Today's notes:", style=k.SUBTITLE_STYLE)
# if day.notes:
# for line in day.notes:
# pr.iprint(line, style=k.NORMAL_STYLE, num_indents=1)
# else:
# pr.iprint("There are no notes yet today.", num_indents=2)
def inout_summary(day: TrackerDay, as_of_when: VTime = VTime("")) -> None:
"""Print summary table of # of bikes in, out and still onsite."""
# # Count the totals
total_in, regular_in, oversize_in = day.num_bikes_parked(as_of_when)
total_out, regular_out, oversize_out = day.num_bikes_returned(as_of_when)
# Count bikes currently onsite
regular_onsite = 0
oversize_onsite = 0
for tagid in day.tags_in_use(as_of_when=as_of_when):
if day.biketags[tagid].bike_type == k.REGULAR:
regular_onsite += 1
else:
oversize_onsite += 1
total_onsite = regular_onsite + oversize_onsite
ut.squawk(f"{total_onsite=}, {day.num_tags_in_use(as_of_when)=}", cfg.DEBUG)
# Print summary of bikes in/out/here
pr.iprint()
pr.iprint("Summary Regular Oversize Total", style=k.SUBTITLE_STYLE)
pr.iprint(
f"Bikes checked in: {regular_in:4d} {oversize_in:4d}"
f" {total_in:4d}"
)
pr.iprint(
f"Bikes returned out: {regular_out:4d} {oversize_out:4d}"
f" {total_out:4d}"
)
pr.iprint(
f"Bikes onsite: {(regular_onsite):4d}"
f" {(oversize_onsite):4d} {total_onsite:4d}"
)
def audit_report(
day: TrackerDay,
args: list[str],
include_notes: bool = True,
include_returns: bool = False,
retired_tag_str: str = DEFAULT_RETIRED_TAG_STR,
) -> None:
"""Create & display audit report as at a particular time.
On entry: as_of_when_args is a list that can optionally
have a first element that's a time at which to make this for.
If include_notes is True, includes any notes from the day.
If include_returns is True, shows a matrix of bikes for which
tags were returned, else won't.
This is smart about any checkouts that are later than as_of_when.
If as_of_when is missing, then counts as of current time.
"""
# What time will this audit report reflect?
as_of_when = args[0] if args else "now"
as_of_when = VTime(as_of_when)
if not as_of_when:
pr.iprint("Unrecognized time", style=k.WARNING_STYLE)
return False
# Audit report header.
pr.iprint()
pr.iprint(
f"Audit report for {day.date} {rep.time_description(as_of_when,day=day)}",
style=k.TITLE_STYLE,
)
rep.later_events_warning(day, as_of_when)
# Summary of bikes in a& bikes out
inout_summary(day, as_of_when)
# Want list of biketags on hand and those returned but not reused
tags_in_use = day.tags_in_use(as_of_when=as_of_when)
tags_done = day.tags_done(as_of_when)
ut.squawk(f"{tags_done=}",cfg.DEBUG)
# Tags matrixes
# Tags broken down by prefix (for tags matrix)
prefixes_on_hand = ut.tagnums_by_prefix(tags_in_use)
prefixes_returned_out = ut.tagnums_by_prefix(tags_done)
returns_by_colour = {}
for prefix, numbers in prefixes_returned_out.items():
colour_code = prefix[:-1] # prefix without the tag_letter
if colour_code not in returns_by_colour:
returns_by_colour[colour_code] = len(numbers)
else:
returns_by_colour[colour_code] += len(numbers)
NO_ITEM_STR = " " # what to show when there's no tag
pr.iprint()
# Bikes still; onsite
pr.iprint(
f"Bikes still onsite at {as_of_when.short}"
f" ({retired_tag_str} --> retired tag)",
style=k.SUBTITLE_STYLE,
)
for prefix in sorted(prefixes_on_hand.keys()):
numbers = prefixes_on_hand[prefix]
line = f"{prefix:3>} "
greatest_num = ut.greatest_tagnum(prefix, day.regular_tagids, day.oversize_tagids)
if greatest_num is None:
continue
for i in range(
0, ut.greatest_tagnum(prefix, day.regular_tagids, day.oversize_tagids) + 1
):
if i in numbers:
s = f"{i:02d}"
elif TagID(f"{prefix}{i}") in day.retired_tagids:
s = retired_tag_str
else:
s = NO_ITEM_STR
line = f"{line} {s}"
pr.iprint(line)
if not prefixes_on_hand:
pr.iprint("-no bikes-")
# Bikes returned out -- tags matrix.
if include_returns:
pr.iprint()
pr.iprint("Tags available for re-use", style=k.SUBTITLE_STYLE)
for prefix in sorted(prefixes_returned_out.keys()):
numbers = prefixes_returned_out[prefix]
line = f"{prefix:3>} "
for i in range(
0,
ut.greatest_tagnum(prefix, day.regular_tagids, day.oversize_tagids) + 1,
):
if i in numbers:
s = f"{i:02d}"
elif TagID(f"{prefix}{i}") in day.retired_tagids:
s = retired_tag_str
else:
s = NO_ITEM_STR
line = f"{line} {s}"
pr.iprint(line)
if not prefixes_returned_out:
pr.iprint("-no bikes-")
# if include_notes:
# notes_bit(day)
return