-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDISProcessingStatusReport.py
299 lines (239 loc) · 10.1 KB
/
DISProcessingStatusReport.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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
#!/usr/bin/env python
"""Create a new report based on the new ProcessingStatus block.
"""
from functools import cached_property
from cdrcgi import Controller
from cdrapi.docs import Doc
class Control(Controller):
SUBTITLE = "DIS Processing Status Report"
LOGNAME = "DISProcessingStatusReport"
STATUS_PATH = "/DrugInformationSummary/ProcessingStatus"
VALUE_PATH = f"{STATUS_PATH}/ProcessingStatusValue"
DATE_PATH = f"{STATUS_PATH}/StatusDate"
def populate_form(self, page):
"""Add the fields to the report form.
Pass:
page - HTMLPage object where the fields go
"""
fieldset = page.fieldset("Select Status Value(s)")
for value in self.statuses:
opts = dict(value=value, label=value)
fieldset.append(page.checkbox("status", **opts))
page.form.append(fieldset)
fieldset = page.fieldset("Date Range")
fieldset.append(page.date_field("start"))
fieldset.append(page.date_field("end"))
page.form.append(fieldset)
page.add_output_options("html")
def build_tables(self):
"""Assemble the report's table."""
opts = dict(columns=self.columns, fixed=True)
return [self.Reporter.Table(self.rows, **opts)]
def show_report(self):
"""Override so we can widen the report table."""
table = self.report.page.form.find("table")
report_footer = self.report.page.main.find("p")
report_footer.addprevious(table)
css = ".report .usa-table { width: 90%; margin: 3rem auto 1.25rem; }"
self.report.page.add_css(css)
self.report.send(self.format)
@cached_property
def columns(self):
"""Column headers for the report's table."""
return (
"CDR ID",
"DIS Title",
"Processing Status Value",
"Processing Status Date",
"Entered By",
self.Reporter.Column("Comments", width="20%"),
"Last Version Publishable?",
"Date First Published",
"Published Date",
)
@property
def drugs(self):
"""Documents selected for the report."""
if not hasattr(self, "_drugs"):
query = self.Query("query_term s", "s.doc_id").unique()
query.where(f"s.path = '{self.VALUE_PATH}'")
if self.status:
query.where(query.Condition("s.value", self.status, "IN"))
if self.start or self.end:
query.join("query_term d", "d.doc_id = s.doc_id",
"LEFT(d.node_loc, 4) = LEFT(s.node_loc, 4)")
if self.start:
start = str(self.start)
query.where(query.Condition("d.value", start, ">="))
if self.end:
end = f"{self.end} 23:59:59"
query.where(query.Condition("d.value", end, "<="))
rows = query.execute(self.cursor).fetchall()
self._drugs = [Drug(self, row.doc_id) for row in rows]
return self._drugs
@property
def end(self):
"""Ending date for the report's date range."""
if not hasattr(self, "_end"):
self._end = self.parse_date(self.fields.getvalue("end"))
return self._end
@property
def rows(self):
"""Rows collected from the drug documents for the report."""
if not hasattr(self, "_rows"):
self._rows = []
for drug in self.drugs:
self._rows += drug.rows
return self._rows
@property
def start(self):
"""Starting date for the report's date range."""
if not hasattr(self, "_start"):
self._start = self.parse_date(self.fields.getvalue("start"))
return self._start
@property
def status(self):
"""Processing status(es) selected for the report."""
if not hasattr(self, "_status"):
self._status = self.fields.getlist("status")
if set(self._status) - set(self.statuses):
self.bail()
return self._status
@property
def statuses(self):
"""Processing status values found in the data."""
if not hasattr(self, "_statuses"):
query = self.Query("query_term", "value").unique()
query.where(f"path = '{self.VALUE_PATH}'")
rows = query.execute(self.cursor).fetchall()
self._statuses = sorted([row.value for row in rows])
return self._statuses
class Drug:
"""Document with status value(s) selected for the report."""
def __init__(self, control, id):
"""Save the caller's values.
Pass:
control - access to the database and the current session
id - CDR document ID for the DrugInformationSummary document
"""
self.__control = control
self.__id = id
@property
def date_last_modified(self):
"""Date set by the users for the last significant change."""
if not hasattr(self, "_date_last_modified"):
query = self.__control.Query("query_term", "value")
query.where("path = '/DrugInformationSummary/DateLastModified'")
query.where(query.Condition("doc_id", self.doc.id))
rows = query.execute(self.__control.cursor).fetchall()
self._date_last_modified = rows[0].value if rows else None
return self._date_last_modified
@property
def doc(self):
"""`Doc` object for the DIS document."""
if not hasattr(self, "_doc"):
self._doc = Doc(self.__control.session, id=self.__id)
return self._doc
@property
def last_version_publishable(self):
"""True if the most recently created version is marked publishable."""
if not hasattr(self, "_last_version_publishable"):
last_ver = self.doc.last_version
pub_ver = self.doc.last_publishable_version
self._last_version_publishable = pub_ver and pub_ver == last_ver
return self._last_version_publishable
@property
def rows(self):
"""Table rows from this document for the report."""
if not hasattr(self, "_rows"):
self._rows = []
first_pub = str(self.doc.first_pub or "")[:10]
for status in sorted(self.statuses):
lvp = "Y" if self.last_version_publishable else "N"
dlm = self.date_last_modified
self._rows.append([
self.__control.Reporter.Cell(self.doc.cdr_id, center=True),
self.title,
status.value,
self.__control.Reporter.Cell(status.date, center=True),
status.entered_by,
status.comment,
self.__control.Reporter.Cell(lvp, center=True),
self.__control.Reporter.Cell(first_pub, center=True),
self.__control.Reporter.Cell(dlm, center=True),
])
return self._rows
@property
def statuses(self):
"""Sequence of statuses which are in scope for the report."""
if not hasattr(self, "_statuses"):
self._statuses = []
for node in self.doc.root.findall("ProcessingStatus"):
status = self.Status(self.__control, node)
if status.in_scope:
self._statuses.append(status)
return self._statuses
@property
def title(self):
"""Official name of the drug or drug combination."""
if not hasattr(self, "_title"):
self._title = Doc.get_text(self.doc.root.find("Title"))
if not self._title:
self._title = self.doc.title.split(";")[0]
return self._title
class Status:
"""A ProcessingStatus block from the DIS document."""
def __init__(self, control, node):
"""Remember the caller's values.
Pass:
control - access to the report options
node - the DOM node for the ProcessingStatus block
"""
self.__control = control
self.__node = node
def __lt__(self, other):
"""Support sorting of the statuses by date."""
return self.date < other.date
@property
def comment(self):
"""The (optional) comment entered for the status."""
if not hasattr(self, "_comment"):
self._comment = Doc.get_text(self.__node.find("Comment"))
return self._comment
@property
def date(self):
"""The date (ISO format) for the status."""
if not hasattr(self, "_date"):
self._date = Doc.get_text(self.__node.find("StatusDate"), "")
return self._date
@property
def entered_by(self):
"""User name for the account used to record the status."""
if not hasattr(self, "_entered_by"):
self._entered_by = Doc.get_text(self.__node.find("EnteredBy"))
return self._entered_by
@property
def in_scope(self):
"""True if this status block should be included on the report."""
if not hasattr(self, "_in_scope"):
self._in_scope = True
if self.__control.status:
if self.value not in self.__control.status:
self._in_scope = False
if self._in_scope and self.__control.start:
if not self.date or self.date < str(self.__control.start):
self._in_scope = False
if self._in_scope and self.__control.end:
if not self.date or self.date > str(self.__control.end):
self._in_scope = False
return self._in_scope
@property
def value(self):
"""The status value string selected for this status."""
if not hasattr(self, "_value"):
node = self.__node.find("ProcessingStatusValue")
self._value = Doc.get_text(node)
return self._value
if __name__ == "__main__":
"""Don't execute the script if loaded as a module."""
Control().run()