docassemble.InterviewStats 0.2__tar.gz → 0.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docassemble_interviewstats-0.5.3/MANIFEST.in +18 -0
- {docassemble.InterviewStats-0.2/docassemble.InterviewStats.egg-info → docassemble_interviewstats-0.5.3}/PKG-INFO +15 -10
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/README.md +2 -0
- docassemble_interviewstats-0.5.3/docassemble/InterviewStats/__init__.py +1 -0
- docassemble_interviewstats-0.5.3/docassemble/InterviewStats/data/questions/data_report.yml +405 -0
- docassemble_interviewstats-0.5.3/docassemble/InterviewStats/data/questions/stats.yml +450 -0
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble/InterviewStats/data/questions/subscribe.yml +2 -2
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble/InterviewStats/data/questions/test_create_snapshot_data.yml +2 -1
- docassemble_interviewstats-0.5.3/docassemble/InterviewStats/data/questions/test_survey_data_generator.yml +110 -0
- docassemble_interviewstats-0.5.3/docassemble/InterviewStats/data/static/sortable.js +86 -0
- docassemble_interviewstats-0.5.3/docassemble/InterviewStats/data/static/styles.css +18 -0
- docassemble_interviewstats-0.5.3/docassemble/InterviewStats/data/static/summary.css +8 -0
- docassemble_interviewstats-0.5.3/docassemble/InterviewStats/data_report.py +149 -0
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble/InterviewStats/generate_test_data.py +9 -1
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble/InterviewStats/snapshot_geography.py +5 -1
- docassemble_interviewstats-0.5.3/docassemble/InterviewStats/snapshot_statistics.py +282 -0
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3/docassemble.InterviewStats.egg-info}/PKG-INFO +15 -10
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble.InterviewStats.egg-info/SOURCES.txt +7 -5
- docassemble_interviewstats-0.5.3/docassemble.InterviewStats.egg-info/requires.txt +6 -0
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble.InterviewStats.egg-info/top_level.txt +2 -0
- docassemble_interviewstats-0.5.3/pyproject.toml +58 -0
- docassemble_interviewstats-0.5.3/setup.cfg +4 -0
- docassemble.InterviewStats-0.2/MANIFEST.in +0 -1
- docassemble.InterviewStats-0.2/docassemble/InterviewStats/__init__.py +0 -1
- docassemble.InterviewStats-0.2/docassemble/InterviewStats/data/questions/stats.yml +0 -283
- docassemble.InterviewStats-0.2/docassemble/InterviewStats/snapshot_statistics.py +0 -59
- docassemble.InterviewStats-0.2/docassemble/__init__.py +0 -5
- docassemble.InterviewStats-0.2/docassemble.InterviewStats.egg-info/namespace_packages.txt +0 -1
- docassemble.InterviewStats-0.2/docassemble.InterviewStats.egg-info/not-zip-safe +0 -1
- docassemble.InterviewStats-0.2/docassemble.InterviewStats.egg-info/requires.txt +0 -6
- docassemble.InterviewStats-0.2/setup.cfg +0 -7
- docassemble.InterviewStats-0.2/setup.py +0 -60
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/LICENSE +0 -0
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble/InterviewStats/cache_geography.py +0 -0
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble/InterviewStats/data/sources/README.md +0 -0
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble/InterviewStats/data/static/README.md +0 -0
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble/InterviewStats/data/templates/README.md +0 -0
- {docassemble.InterviewStats-0.2 → docassemble_interviewstats-0.5.3}/docassemble.InterviewStats.egg-info/dependency_links.txt +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
graft docassemble/InterviewStats/data
|
|
3
|
+
recursive-exclude * *.egg-info
|
|
4
|
+
recursive-exclude .git *
|
|
5
|
+
recursive-exclude venv *
|
|
6
|
+
recursive-exclude .github *
|
|
7
|
+
recursive-exclude .pytest_cache *
|
|
8
|
+
recursive-exclude .vscode *
|
|
9
|
+
recursive-exclude build *
|
|
10
|
+
recursive-exclude dist *
|
|
11
|
+
recursive-exclude * __pycache__
|
|
12
|
+
recursive-exclude * *.pyc
|
|
13
|
+
recursive-exclude * *.pyo
|
|
14
|
+
recursive-exclude * *.orig
|
|
15
|
+
recursive-exclude * *~
|
|
16
|
+
recursive-exclude * *.bak
|
|
17
|
+
recursive-exclude * *.swp
|
|
18
|
+
|
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: docassemble.InterviewStats
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: A docassemble
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
License: The MIT License (MIT)
|
|
9
|
-
Platform: UNKNOWN
|
|
3
|
+
Version: 0.5.3
|
|
4
|
+
Summary: A docassemble interview to view statistics from other saved interview responses.
|
|
5
|
+
Author-email: Suffolk Legal Innovation and Technology Lab <litlab@suffolk.edu>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: GitHub, https://github.com/SuffolkLITLab/docassemble-InterviewStats
|
|
10
8
|
Description-Content-Type: text/markdown
|
|
11
9
|
License-File: LICENSE
|
|
10
|
+
Requires-Dist: bokeh>=2.4.2
|
|
11
|
+
Requires-Dist: cenpy>=1.0.0.post4
|
|
12
|
+
Requires-Dist: geopandas>=0.9.0
|
|
13
|
+
Requires-Dist: numpy>=1.0.4
|
|
14
|
+
Requires-Dist: pandas>=1.4.2
|
|
15
|
+
Requires-Dist: requests>=2.27.1
|
|
16
|
+
Dynamic: license-file
|
|
12
17
|
|
|
13
18
|
# docassemble.InterviewStats
|
|
14
19
|
|
|
20
|
+
[](https://badge.fury.io/py/docassemble.InterviewStats)
|
|
21
|
+
|
|
15
22
|
A docassemble interview that lets you view statistics from other saved interview responses.
|
|
16
23
|
|
|
17
24
|
## Getting Started
|
|
@@ -71,5 +78,3 @@ If you have specific feature requests, feel free to open an issue or make a PR!
|
|
|
71
78
|
* Quinten Steenhuis
|
|
72
79
|
* Bryce Willey
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# docassemble.InterviewStats
|
|
2
2
|
|
|
3
|
+
[](https://badge.fury.io/py/docassemble.InterviewStats)
|
|
4
|
+
|
|
3
5
|
A docassemble interview that lets you view statistics from other saved interview responses.
|
|
4
6
|
|
|
5
7
|
## Getting Started
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.5.3'
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
---
|
|
2
|
+
metadata:
|
|
3
|
+
title: Interview data reporting
|
|
4
|
+
short title: Data reporting
|
|
5
|
+
temporary session: True
|
|
6
|
+
required privileges:
|
|
7
|
+
- admin
|
|
8
|
+
---
|
|
9
|
+
features:
|
|
10
|
+
question help button: True
|
|
11
|
+
#question back button: True
|
|
12
|
+
css:
|
|
13
|
+
- styles.css
|
|
14
|
+
---
|
|
15
|
+
default screen parts:
|
|
16
|
+
footer: |
|
|
17
|
+
[:sync-alt: Restart](${ url_of('restart') } )
|
|
18
|
+
---
|
|
19
|
+
modules:
|
|
20
|
+
- .snapshot_statistics
|
|
21
|
+
- .data_report
|
|
22
|
+
---
|
|
23
|
+
objects:
|
|
24
|
+
- xlsx_output: DAFile.using(filename="stats.xlsx")
|
|
25
|
+
#--------------------------------------------------------------------------
|
|
26
|
+
# Main logic
|
|
27
|
+
#--------------------------------------------------------------------------
|
|
28
|
+
---
|
|
29
|
+
id: interview order
|
|
30
|
+
mandatory: True
|
|
31
|
+
code: |
|
|
32
|
+
filename # Choose interview for data report
|
|
33
|
+
simple_summary # Allow date filtering
|
|
34
|
+
excel_formatted_data # Prepare data for graph and excel
|
|
35
|
+
final # Display download and filter options
|
|
36
|
+
#--------------------------------------------------------------------------
|
|
37
|
+
# Question screens
|
|
38
|
+
#--------------------------------------------------------------------------
|
|
39
|
+
---
|
|
40
|
+
question: |
|
|
41
|
+
Data Reporting
|
|
42
|
+
subquestion: |
|
|
43
|
+
**Prerequisites**:
|
|
44
|
+
|
|
45
|
+
1. You have a survey-type interview (i.e. interviews that only collect answers but don't have a download screen);
|
|
46
|
+
1. you are using the `save_input_data()` function in ALToolbox to capture the answers for data reporting purposes. ([See a demo](https://apps-dev.suffolklitlab.org/interview?i=docassemble.ALToolbox%3Adata%2Fquestions%2Fsave_input_data_demo.yml#page1) if you haven't used it before);
|
|
47
|
+
1. your interview has been run successfully at least once.
|
|
48
|
+
|
|
49
|
+
You can then use this `data reporting` feature to pull your data records and make some quick on-screen data reports. In addition, you can download a complete data dump of the saved data records into an Excel file, and use it to create charts, review free typed answers etc..
|
|
50
|
+
|
|
51
|
+
Select an interview to see data reports:
|
|
52
|
+
fields:
|
|
53
|
+
- Filename: filename
|
|
54
|
+
datatype: combobox
|
|
55
|
+
code: |
|
|
56
|
+
[{interview.get('filename'): interview.get('title')} for interview in interview_menu()] or get_filenames()
|
|
57
|
+
---
|
|
58
|
+
question: |
|
|
59
|
+
Quick Summary for ${ interview_title }
|
|
60
|
+
subquestion: |
|
|
61
|
+
* Total sessions: ${ summary_stats[0] }
|
|
62
|
+
* First session date: ${ format_date(summary_stats[1], format='MM/dd/yyyy') }
|
|
63
|
+
* Latest session date: ${ format_date(summary_stats[2], format='MM/dd/yyyy') }
|
|
64
|
+
|
|
65
|
+
% if summary_stats[0] > 10000:
|
|
66
|
+
You have a lot of sessions; some operations might take a long time.
|
|
67
|
+
% endif
|
|
68
|
+
---
|
|
69
|
+
Do you want to filter your data records by date?
|
|
70
|
+
fields:
|
|
71
|
+
- no label: filter_date
|
|
72
|
+
datatype: yesnoradio
|
|
73
|
+
default: False
|
|
74
|
+
required: False
|
|
75
|
+
- Find submissions that are: date_op
|
|
76
|
+
datatype: dropdown
|
|
77
|
+
show if: filter_date
|
|
78
|
+
code: |
|
|
79
|
+
[{'gt': 'after'},
|
|
80
|
+
{'lt': 'before'},
|
|
81
|
+
{'eq': 'on'},
|
|
82
|
+
{'between': 'between'}]
|
|
83
|
+
- this date: begin_date
|
|
84
|
+
datatype: date
|
|
85
|
+
show if: filter_date
|
|
86
|
+
- and this date: end_date
|
|
87
|
+
datatype: date
|
|
88
|
+
js show if: |
|
|
89
|
+
val('date_op') == 'between'
|
|
90
|
+
continue button field: simple_summary
|
|
91
|
+
---
|
|
92
|
+
id: final screen
|
|
93
|
+
event: final
|
|
94
|
+
question: |
|
|
95
|
+
${interview_title} - Data Reports
|
|
96
|
+
subquestion: |
|
|
97
|
+
Total submissions: ${ len(stats) }
|
|
98
|
+
|
|
99
|
+
% if filter_date:
|
|
100
|
+
Date filter: Records dated ${phrase(date_op)} ${begin_date}
|
|
101
|
+
% if phrase(date_op) == 'between':
|
|
102
|
+
and ${end_date}
|
|
103
|
+
% endif
|
|
104
|
+
<br>Filtered submissions: ${ record_count }
|
|
105
|
+
% endif
|
|
106
|
+
|
|
107
|
+
% if have_multi_vars:
|
|
108
|
+
#### Multi-valued questions
|
|
109
|
+
The following fields are from multi-valued questions. Select one to see its table:<br>
|
|
110
|
+
% for col in col_headings_multi:
|
|
111
|
+
[${col}](${ url_action('show_filtered_data_multi', filter_by = col) })
|
|
112
|
+
% endfor
|
|
113
|
+
% endif
|
|
114
|
+
|
|
115
|
+
% if have_single_vars:
|
|
116
|
+
#### Single-valued questions (excluding text/area/numberic fields)
|
|
117
|
+
The following fields are from single-valued questions, except those whose answer lack statistic value, such as user-typed answer or numeric input.
|
|
118
|
+
|
|
119
|
+
Select a field to see its table:<br>
|
|
120
|
+
% for col in col_headings_single:
|
|
121
|
+
[${col}](${ url_action('show_filtered_data_single', filter_by = col) })
|
|
122
|
+
% endfor
|
|
123
|
+
% endif
|
|
124
|
+
|
|
125
|
+
#### All questions (excluding text/area/number fields)
|
|
126
|
+
[Click here](${ url_action('display_filtered_data_all') }) to see all the question tables on one screen.
|
|
127
|
+
|
|
128
|
+
#### Download an excel file:
|
|
129
|
+
To extract complete data including text/area/numeric fields, make an excel file:<br>
|
|
130
|
+
[:file-excel: Make Excel Download](${ url_ask('generate_excel') })
|
|
131
|
+
---
|
|
132
|
+
id: single-valued filtered screen
|
|
133
|
+
question: |
|
|
134
|
+
Data Report - Single value questions
|
|
135
|
+
subquestion: |
|
|
136
|
+
**Number of submissions**: ${ record_count }<br>
|
|
137
|
+
**Variable Name**: ${ my_col_single }<br>
|
|
138
|
+
**Table Content**: Answer count / % of total submission<br>
|
|
139
|
+
:bell: Total percentage should be about 100% (±0.5% due to rounding).
|
|
140
|
+
|
|
141
|
+
${ df_single[my_col_single].rename_axis("Answer").to_frame().query('index=='+my_index_single).to_html(classes=('table','table-striped')) }
|
|
142
|
+
continue button field: display_filtered_data_single
|
|
143
|
+
continue button label: Back
|
|
144
|
+
---
|
|
145
|
+
id: multi-valued filtered screen
|
|
146
|
+
question: |
|
|
147
|
+
Data Report - Multi-choices questions
|
|
148
|
+
subquestion: |
|
|
149
|
+
**Number of submissions**: ${ record_count }<br>
|
|
150
|
+
**Variable Name**: ${ my_col_multi }<br>
|
|
151
|
+
**Table Content**: Answer count / % of total submission<br>
|
|
152
|
+
:bell: Total percentage can be over or under 100%.
|
|
153
|
+
|
|
154
|
+
${ df_multi[my_col_multi].rename_axis("Answer").to_frame().query('index=='+my_index_multi).to_html(classes=('table','table-striped')) }
|
|
155
|
+
continue button field: display_filtered_data_multi
|
|
156
|
+
continue button label: Back
|
|
157
|
+
---
|
|
158
|
+
id: all_variables screen
|
|
159
|
+
question: |
|
|
160
|
+
Data Report - All questions
|
|
161
|
+
subquestion: |
|
|
162
|
+
Number of submissions: ${ record_count }<br>
|
|
163
|
+
**Table Content**: Answer count / % of total submission<br>
|
|
164
|
+
:bell: For multi-valued questions, the total percentage can be over or under 100%; for single-valued questions, the total percentage should be about 100% (±0.5% due to rounding).
|
|
165
|
+
|
|
166
|
+
#### Multi-value questions
|
|
167
|
+
% for fld in df_multi:
|
|
168
|
+
${ df_multi[fld].rename_axis("Answer").to_frame().query('index=='+str(answer_list_multi[col_headings_multi.index(fld)])).to_html(classes=('table','table-striped')) }
|
|
169
|
+
% endfor
|
|
170
|
+
|
|
171
|
+
#### Single-value questions
|
|
172
|
+
% for fld in df_single:
|
|
173
|
+
${ df_single[fld].rename_axis("Answer").to_frame().query('index=='+str((answer_list_single[col_headings_single.index(fld)]))).to_html(classes=('table','table-striped')) }
|
|
174
|
+
% endfor
|
|
175
|
+
continue button field: display_filtered_data_all
|
|
176
|
+
continue button label: Back
|
|
177
|
+
#--------------------------------------------------------------------------
|
|
178
|
+
# Data extract code blocks and screens
|
|
179
|
+
#--------------------------------------------------------------------------
|
|
180
|
+
---
|
|
181
|
+
code: |
|
|
182
|
+
summary_stats = get_summary_stats(filename)
|
|
183
|
+
---
|
|
184
|
+
# Extract full data from database into stats
|
|
185
|
+
code: |
|
|
186
|
+
# Get stats using filename
|
|
187
|
+
if filename is not None:
|
|
188
|
+
if 'data/questions' in filename or 'playground' in filename:
|
|
189
|
+
stats = get_stats(filename)
|
|
190
|
+
else:
|
|
191
|
+
stats = get_stats(filename.replace(':',':data/questions/'))
|
|
192
|
+
else:
|
|
193
|
+
no_interview_stats
|
|
194
|
+
|
|
195
|
+
# Check if stats has data
|
|
196
|
+
if not stats:
|
|
197
|
+
no_interview_stats
|
|
198
|
+
else:
|
|
199
|
+
# Check if stats is data-reporting ready - it must have a "title" column
|
|
200
|
+
if 'title' not in stats[0].keys():
|
|
201
|
+
no_reportable_data
|
|
202
|
+
---
|
|
203
|
+
event: no_interview_stats
|
|
204
|
+
question: Interview has no data!
|
|
205
|
+
subquestion: |
|
|
206
|
+
The interview you selected is <br>
|
|
207
|
+
${interview_title}.
|
|
208
|
+
|
|
209
|
+
This interview doesn't have any data. Try another one.
|
|
210
|
+
buttons:
|
|
211
|
+
- Restart: restart
|
|
212
|
+
---
|
|
213
|
+
event: no_reportable_data
|
|
214
|
+
question: Interview has no reportable data, you should use Stats
|
|
215
|
+
subquestion: |
|
|
216
|
+
The interview you selected is <br>
|
|
217
|
+
${interview_title}.
|
|
218
|
+
|
|
219
|
+
This interview contains data not suitable for Data Reporting. You should use Stats.
|
|
220
|
+
buttons:
|
|
221
|
+
- Restart: restart
|
|
222
|
+
---
|
|
223
|
+
code: |
|
|
224
|
+
interview_title = next(iter([interview.get('title') for interview in interview_menu() if interview.get('filename') == filename]), '(interview)')
|
|
225
|
+
if stats and 'title' in stats[0].keys():
|
|
226
|
+
interview_title = stats[0]['title']
|
|
227
|
+
---
|
|
228
|
+
# Filter records in stats according to the filter
|
|
229
|
+
code: |
|
|
230
|
+
filtered_stats = list()
|
|
231
|
+
|
|
232
|
+
# Build filtered_stats if filter_date is selected
|
|
233
|
+
if filter_date:
|
|
234
|
+
filter_begin = format_date(begin_date, format='MM/dd/yyyy')
|
|
235
|
+
if date_op == 'between':
|
|
236
|
+
filter_end = format_date(end_date, format='MM/dd/yyyy')
|
|
237
|
+
for record in stats:
|
|
238
|
+
record_date = format_date(record['modtime'], format='MM/dd/yyyy')
|
|
239
|
+
if (date_op == 'eq' and record_date == filter_begin) or (date_op == 'gt' and record_date > filter_begin) or (date_op == 'lt' and record_date < filter_begin) or (date_op == 'between' and record_date >= filter_begin and record_date < filter_end):
|
|
240
|
+
filtered_stats.append(record)
|
|
241
|
+
|
|
242
|
+
# In case a wrong date/date_op pair is selected
|
|
243
|
+
if filter_date and not filtered_stats:
|
|
244
|
+
no_filtered_stats
|
|
245
|
+
---
|
|
246
|
+
event: no_filtered_stats
|
|
247
|
+
question: No records in this period
|
|
248
|
+
subquestion: |
|
|
249
|
+
% if date_op == 'between':
|
|
250
|
+
You selected records "${phrase(date_op)} ${begin_date} and ${end_date}".
|
|
251
|
+
% else:
|
|
252
|
+
You selected records "${phrase(date_op)} ${begin_date}".
|
|
253
|
+
% endif
|
|
254
|
+
There aren't any records fit your selection.
|
|
255
|
+
|
|
256
|
+
Try another date.
|
|
257
|
+
back button: simple_summary
|
|
258
|
+
---
|
|
259
|
+
need: filtered_stats
|
|
260
|
+
code: |
|
|
261
|
+
# Process the filtered data records
|
|
262
|
+
columns = get_columns(stats)
|
|
263
|
+
if filtered_stats:
|
|
264
|
+
table_data = processing_data(raw_data = filtered_stats, headings = columns)
|
|
265
|
+
record_count = len(filtered_stats)
|
|
266
|
+
else:
|
|
267
|
+
record_count = len(stats)
|
|
268
|
+
table_data = processing_data(raw_data = stats, headings = columns)
|
|
269
|
+
|
|
270
|
+
# Extract data for multi-valued questions
|
|
271
|
+
have_multi_vars = False
|
|
272
|
+
if table_data[0]:
|
|
273
|
+
have_multi_vars = True
|
|
274
|
+
table_data_multi = table_data[0]
|
|
275
|
+
col_headings_multi = list(table_data[0].keys())
|
|
276
|
+
answer_list_multi = table_data[1]
|
|
277
|
+
|
|
278
|
+
# Extract data for single-valued questions
|
|
279
|
+
have_single_vars = False
|
|
280
|
+
if table_data[2]:
|
|
281
|
+
have_single_vars = True
|
|
282
|
+
table_data_single = table_data[2]
|
|
283
|
+
col_headings_single = list(table_data[2].keys())
|
|
284
|
+
# Extract possible answers for single-valued questions
|
|
285
|
+
answer_list_single = table_data[3]
|
|
286
|
+
#--------------------------------------------------------------------------
|
|
287
|
+
# Pandas related code blocks
|
|
288
|
+
#--------------------------------------------------------------------------
|
|
289
|
+
---
|
|
290
|
+
code: |
|
|
291
|
+
import pandas as pd
|
|
292
|
+
---
|
|
293
|
+
code: |
|
|
294
|
+
excel_formatted_data = pd.DataFrame()
|
|
295
|
+
df_single = pd.DataFrame()
|
|
296
|
+
df_multi = pd.DataFrame()
|
|
297
|
+
stats_size = 0
|
|
298
|
+
if stats is not None:
|
|
299
|
+
excel_formatted_data = pd.DataFrame(stats)
|
|
300
|
+
stats_size = excel_formatted_data.shape[0]
|
|
301
|
+
if have_single_vars:
|
|
302
|
+
df_single = pd.DataFrame(table_data_single)
|
|
303
|
+
if have_multi_vars:
|
|
304
|
+
df_multi = pd.DataFrame(table_data_multi)
|
|
305
|
+
---
|
|
306
|
+
event: show_filtered_data_single
|
|
307
|
+
code: |
|
|
308
|
+
# Receive user chosen field
|
|
309
|
+
my_col_single = action_argument('filter_by')
|
|
310
|
+
|
|
311
|
+
# Find the index of my_col_single, and use it to set my_index_single
|
|
312
|
+
my_row_single = col_headings_single.index(my_col_single)
|
|
313
|
+
my_index_single = str(answer_list_single[my_row_single])
|
|
314
|
+
|
|
315
|
+
# Display single-valued fields table screen
|
|
316
|
+
force_ask('display_filtered_data_single')
|
|
317
|
+
---
|
|
318
|
+
event: show_filtered_data_multi
|
|
319
|
+
code: |
|
|
320
|
+
# Receive user chosen field
|
|
321
|
+
my_col_multi = action_argument('filter_by')
|
|
322
|
+
|
|
323
|
+
# Find the index of my_col_multi, and use it to set my_index_multi
|
|
324
|
+
my_row_multi = col_headings_multi.index(my_col_multi)
|
|
325
|
+
my_index_multi = str(answer_list_multi[my_row_multi])
|
|
326
|
+
|
|
327
|
+
# Display multi-valued fields table screen
|
|
328
|
+
force_ask('display_filtered_data_multi')
|
|
329
|
+
---
|
|
330
|
+
code: |
|
|
331
|
+
row_single = col_headings_single.index(fld)
|
|
332
|
+
index_single = str(answer_list_single[row_single])
|
|
333
|
+
|
|
334
|
+
row_multi = col_headings_multi.index(fld)
|
|
335
|
+
index_multi = str(answer_list_multi[row_multi])
|
|
336
|
+
#--------------------------------------------------------------------------
|
|
337
|
+
# Excel related code blocks and screens
|
|
338
|
+
#--------------------------------------------------------------------------
|
|
339
|
+
---
|
|
340
|
+
event: generate_excel
|
|
341
|
+
code: |
|
|
342
|
+
if stats_size > 10000:
|
|
343
|
+
if make_excel_task.ready():
|
|
344
|
+
if make_excel_task.failed():
|
|
345
|
+
show_failed_screen
|
|
346
|
+
show_excel_download
|
|
347
|
+
else:
|
|
348
|
+
waiting_screen
|
|
349
|
+
else:
|
|
350
|
+
proper_name = space_to_underscore(filename) + "_stats"
|
|
351
|
+
xlsx_output.initialize(filename=proper_name + ".xlsx")
|
|
352
|
+
writer = pd.ExcelWriter(xlsx_output.path(),
|
|
353
|
+
engine='xlsxwriter',
|
|
354
|
+
engine_kwargs={'options': {'remove_timezone': True}})
|
|
355
|
+
excel_formatted_data.to_excel(writer, index=False)
|
|
356
|
+
writer.close()
|
|
357
|
+
xlsx_output.commit()
|
|
358
|
+
del writer
|
|
359
|
+
show_excel_download
|
|
360
|
+
---
|
|
361
|
+
event: show_excel_download
|
|
362
|
+
question: |
|
|
363
|
+
Excel Download
|
|
364
|
+
subquestion: |
|
|
365
|
+
[:file-excel: Download](${ xlsx_output.url_for() })
|
|
366
|
+
---
|
|
367
|
+
code: |
|
|
368
|
+
make_excel_task = background_action('excel_task', data=excel_formatted_data)
|
|
369
|
+
---
|
|
370
|
+
event: excel_task
|
|
371
|
+
code: |
|
|
372
|
+
bg_data = action_argument('data')
|
|
373
|
+
proper_name = space_to_underscore(filename) + "_stats"
|
|
374
|
+
xlsx_output.initialize(filename=proper_name + ".xlsx")
|
|
375
|
+
writer = pd.ExcelWriter(xlsx_output.path(),
|
|
376
|
+
engine='xlsxwriter',
|
|
377
|
+
engine_kwargs={'options': {'remove_timezone': True}})
|
|
378
|
+
bg_data.to_excel(writer, index=False)
|
|
379
|
+
writer.close()
|
|
380
|
+
xlsx_output.commit()
|
|
381
|
+
del writer
|
|
382
|
+
background_response_action('excel_resp', the_excel=xlsx_output)
|
|
383
|
+
---
|
|
384
|
+
event: waiting_screen
|
|
385
|
+
id: waiting screen
|
|
386
|
+
question: |
|
|
387
|
+
We're generating your excel sheet.
|
|
388
|
+
subquestion: |
|
|
389
|
+
This may take a few minutes, so leave this tab open. Thanks for waiting.
|
|
390
|
+
reload: True
|
|
391
|
+
---
|
|
392
|
+
event: show_failed_screen
|
|
393
|
+
question: |
|
|
394
|
+
Sorry, something went wrong
|
|
395
|
+
subquestion: |
|
|
396
|
+
We had some trouble trying to assemble your excel sheet.
|
|
397
|
+
|
|
398
|
+
${ make_excel_task.result() }
|
|
399
|
+
buttons:
|
|
400
|
+
- Restart: restart
|
|
401
|
+
---
|
|
402
|
+
event: excel_resp
|
|
403
|
+
code: |
|
|
404
|
+
xlsx_output = action_argument('the_excel')
|
|
405
|
+
background_response()
|