Mark Febrizio
commited on
Commit
·
22d0a3b
1
Parent(s):
4ea4f4c
Reapply "Update app.py"
Browse filesThis reverts commit 4ea4f4cd2d7ac81840f8f6a2cb3e043390e0c358.
app.py
CHANGED
@@ -1,10 +1,13 @@
|
|
|
|
|
|
|
|
1 |
import asyncio
|
2 |
from datetime import datetime, date, time
|
3 |
from pathlib import Path
|
4 |
|
5 |
from faicons import icon_svg
|
6 |
-
from pandas import DataFrame
|
7 |
-
from
|
8 |
|
9 |
from modules import (
|
10 |
DF,
|
@@ -20,65 +23,85 @@ from modules import (
|
|
20 |
pad_missing_dates,
|
21 |
plot_agency,
|
22 |
plot_tf,
|
|
|
23 |
)
|
24 |
|
25 |
from shiny import reactive
|
26 |
from shiny.express import input, render, ui
|
27 |
|
28 |
-
|
|
|
29 |
|
30 |
-
HEADER = "Rules in the Congressional Review Act (CRA) Window"
|
31 |
|
32 |
-
|
33 |
-
-----
|
34 |
-
|
35 |
-
© 2024 [GW Regulatory Studies Center](https://go.gwu.edu/regstudies). See our page on the [Congressional Review Act](https://regulatorystudies.columbian.gwu.edu/congressional-review-act) for more information.
|
36 |
-
"""
|
37 |
|
38 |
-
ui.include_css( Path(__file__).parent.joinpath("www") / "style.css")
|
39 |
|
40 |
-
|
|
|
41 |
|
42 |
-
|
|
|
|
|
43 |
f"""
|
44 |
<div class="header">
|
45 |
-
<
|
46 |
-
<img src="logo.png" alt="Regulatory Studies Center logo"/>
|
47 |
-
</a>
|
48 |
</div>
|
49 |
"""
|
50 |
)
|
51 |
|
52 |
-
|
|
|
53 |
f"""
|
54 |
<div class="header">
|
55 |
-
<
|
|
|
|
|
56 |
</div>
|
57 |
"""
|
58 |
)
|
59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
page_header
|
61 |
|
|
|
62 |
with ui.sidebar(open={"desktop": "open", "mobile": "closed"}):
|
63 |
sidebar_logo
|
64 |
|
65 |
-
ui.
|
66 |
-
|
|
|
|
|
|
|
67 |
ui.input_select("menu_agency", "Select agencies", choices=["all"] + AGENCIES, selected="all", multiple=True)
|
68 |
|
69 |
-
ui.input_select("frequency", "Select frequency", choices=["daily", "
|
|
|
|
|
70 |
|
71 |
#ui.input_switch("switch", "Stack significant rules in plots", False)
|
72 |
|
|
|
73 |
with ui.layout_column_wrap():
|
74 |
-
with ui.value_box(
|
75 |
"All final rules"
|
76 |
@render.text
|
77 |
def count_rules():
|
78 |
return f"{filtered_df()['document_number'].count()}"
|
79 |
-
ui.input_action_button("filter_all", "
|
80 |
|
81 |
-
with ui.value_box(
|
82 |
"Section 3(f)(1) Significant rules *"
|
83 |
@render.text
|
84 |
def count_3f1_significant():
|
@@ -86,9 +109,9 @@ with ui.layout_column_wrap():
|
|
86 |
if GET_SIGNIFICANT:
|
87 |
output = f"{filtered_df()['3f1_significant'].sum()}"
|
88 |
return output
|
89 |
-
ui.input_action_button("filter_3f1", "
|
90 |
|
91 |
-
with ui.value_box(
|
92 |
"Other Significant rules *"
|
93 |
@render.text
|
94 |
def count_other_significant():
|
@@ -96,14 +119,16 @@ with ui.layout_column_wrap():
|
|
96 |
if GET_SIGNIFICANT:
|
97 |
output = f"{filtered_df()['other_significant'].sum()}"
|
98 |
return output
|
99 |
-
ui.input_action_button("filter_other", "
|
100 |
|
|
|
101 |
ui.markdown(
|
102 |
f"""
|
103 |
\* *Executive Order 12866 significance data last updated **{LAST_UPDATED}***.
|
104 |
"""
|
105 |
)
|
106 |
|
|
|
107 |
with ui.navset_card_underline(title=""):
|
108 |
|
109 |
with ui.nav_panel("Rules in detail"):
|
@@ -136,14 +161,12 @@ with ui.navset_card_underline(title=""):
|
|
136 |
count_gte_zero = sum(1 if g > 0 else 0 for g in values)
|
137 |
max_val = max(values, default=0)
|
138 |
if (max_val < 2) or (count_gte_zero < 2):
|
139 |
-
return (
|
140 |
-
ggplot()
|
141 |
-
+ labs(title="Not enough data available to visualize.")
|
142 |
-
)
|
143 |
else:
|
144 |
return plot_tf(
|
145 |
grouped,
|
146 |
-
input.frequency()
|
|
|
147 |
)
|
148 |
|
149 |
with ui.card(full_screen=True):
|
@@ -180,10 +203,14 @@ with ui.navset_card_underline(title=""):
|
|
180 |
#if input.switch():
|
181 |
# pass
|
182 |
# # placeholder for stacked bar chart
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
|
|
|
|
|
|
|
|
187 |
|
188 |
with ui.card(full_screen=True):
|
189 |
@render.data_frame
|
@@ -198,6 +225,7 @@ with ui.navset_card_underline(title=""):
|
|
198 |
]
|
199 |
return render.DataTable(grouped.loc[:, [c for c in cols if c in grouped.columns]])
|
200 |
|
|
|
201 |
with ui.accordion(open=False):
|
202 |
|
203 |
with ui.accordion_panel("Download Data"):
|
@@ -231,6 +259,7 @@ with ui.accordion(open=False):
|
|
231 |
await asyncio.sleep(0.25)
|
232 |
yield filtered_df().loc[:, output_cols].to_csv(index=False)
|
233 |
|
|
|
234 |
with ui.accordion(open=False):
|
235 |
|
236 |
with ui.accordion_panel("Notes"):
|
@@ -243,6 +272,7 @@ with ui.accordion(open=False):
|
|
243 |
"""
|
244 |
)
|
245 |
|
|
|
246 |
ui.markdown(
|
247 |
FOOTER
|
248 |
)
|
@@ -266,6 +296,17 @@ def filtered_df():
|
|
266 |
bool_agency = [True if sum(selected in agency for selected in input.menu_agency()) > 0 else False for agency in filt_df["parent_slug"]]
|
267 |
filt_df = filt_df.loc[bool_agency]
|
268 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
269 |
return filt_df
|
270 |
|
271 |
|
|
|
1 |
+
# ----- IMPORTS ----- #
|
2 |
+
|
3 |
+
|
4 |
import asyncio
|
5 |
from datetime import datetime, date, time
|
6 |
from pathlib import Path
|
7 |
|
8 |
from faicons import icon_svg
|
9 |
+
from pandas import DataFrame
|
10 |
+
from numpy import array
|
11 |
|
12 |
from modules import (
|
13 |
DF,
|
|
|
23 |
pad_missing_dates,
|
24 |
plot_agency,
|
25 |
plot_tf,
|
26 |
+
plot_NA,
|
27 |
)
|
28 |
|
29 |
from shiny import reactive
|
30 |
from shiny.express import input, render, ui
|
31 |
|
32 |
+
# load css styles from external file
|
33 |
+
ui.include_css( Path(__file__).parent.joinpath("www") / "style.css")
|
34 |
|
|
|
35 |
|
36 |
+
# ----- CREATE OBJECTS ----- #
|
|
|
|
|
|
|
|
|
37 |
|
|
|
38 |
|
39 |
+
# this text appears in the browser tab
|
40 |
+
TITLE = "CRA Window Tracker - GW Regulatory Studies Center"
|
41 |
|
42 |
+
# page header above main content
|
43 |
+
HEADER = "Rules in the Congressional Review Act (CRA) Window"
|
44 |
+
page_header = ui.HTML(
|
45 |
f"""
|
46 |
<div class="header">
|
47 |
+
<span>{HEADER}</span>
|
|
|
|
|
48 |
</div>
|
49 |
"""
|
50 |
)
|
51 |
|
52 |
+
# logo at the top of the sidebar
|
53 |
+
sidebar_logo = ui.HTML(
|
54 |
f"""
|
55 |
<div class="header">
|
56 |
+
<a href="https://go.gwu.edu/regstudies" target="_blank">
|
57 |
+
<img src="logo.png" alt="Regulatory Studies Center logo"/>
|
58 |
+
</a>
|
59 |
</div>
|
60 |
"""
|
61 |
)
|
62 |
|
63 |
+
# footer at the bottom of the page
|
64 |
+
FOOTER = f"""
|
65 |
+
-----
|
66 |
+
|
67 |
+
© 2024 [GW Regulatory Studies Center](https://go.gwu.edu/regstudies). See our page on the [Congressional Review Act](https://regulatorystudies.columbian.gwu.edu/congressional-review-act) for more information.
|
68 |
+
"""
|
69 |
+
|
70 |
+
|
71 |
+
# ----- APP LAYOUT ----- #
|
72 |
+
|
73 |
+
|
74 |
+
ui.tags.title(TITLE)
|
75 |
+
|
76 |
page_header
|
77 |
|
78 |
+
# sidebar settings
|
79 |
with ui.sidebar(open={"desktop": "open", "mobile": "closed"}):
|
80 |
sidebar_logo
|
81 |
|
82 |
+
#ui.markdown("Estimated CRA window open date: [May 23](https://www.huntonak.com/the-nickel-report/federal-agencies-face-looming-congressional-review-act-deadline)")
|
83 |
+
with ui.tooltip(placement="right", id="window_tooltip"):
|
84 |
+
ui.input_date("start_date", "Select start of window", value=WINDOW_OPEN_DATE, min=START_DATE, max=date.today())
|
85 |
+
"The estimated CRA window open date is May 23. See the notes for more information."
|
86 |
+
|
87 |
ui.input_select("menu_agency", "Select agencies", choices=["all"] + AGENCIES, selected="all", multiple=True)
|
88 |
|
89 |
+
ui.input_select("frequency", "Select frequency", choices=["daily", "weekly", "monthly"], selected="weekly")
|
90 |
+
|
91 |
+
ui.input_select("menu_significant", "Select rule significance", choices=["all", "3f1-significant", "other-significant"], selected="all", multiple=True)
|
92 |
|
93 |
#ui.input_switch("switch", "Stack significant rules in plots", False)
|
94 |
|
95 |
+
# value boxes with summary data
|
96 |
with ui.layout_column_wrap():
|
97 |
+
with ui.value_box():
|
98 |
"All final rules"
|
99 |
@render.text
|
100 |
def count_rules():
|
101 |
return f"{filtered_df()['document_number'].count()}"
|
102 |
+
ui.input_action_button("filter_all", "Clear Filters", class_="btn-filter", icon=icon_svg("book"))
|
103 |
|
104 |
+
with ui.value_box():
|
105 |
"Section 3(f)(1) Significant rules *"
|
106 |
@render.text
|
107 |
def count_3f1_significant():
|
|
|
109 |
if GET_SIGNIFICANT:
|
110 |
output = f"{filtered_df()['3f1_significant'].sum()}"
|
111 |
return output
|
112 |
+
ui.input_action_button("filter_3f1", "Filter", class_="btn-filter", icon=icon_svg("book"))
|
113 |
|
114 |
+
with ui.value_box():
|
115 |
"Other Significant rules *"
|
116 |
@render.text
|
117 |
def count_other_significant():
|
|
|
119 |
if GET_SIGNIFICANT:
|
120 |
output = f"{filtered_df()['other_significant'].sum()}"
|
121 |
return output
|
122 |
+
ui.input_action_button("filter_other", "Filter", class_="btn-filter", icon=icon_svg("book"))
|
123 |
|
124 |
+
# documentation note on significance data
|
125 |
ui.markdown(
|
126 |
f"""
|
127 |
\* *Executive Order 12866 significance data last updated **{LAST_UPDATED}***.
|
128 |
"""
|
129 |
)
|
130 |
|
131 |
+
# main content
|
132 |
with ui.navset_card_underline(title=""):
|
133 |
|
134 |
with ui.nav_panel("Rules in detail"):
|
|
|
161 |
count_gte_zero = sum(1 if g > 0 else 0 for g in values)
|
162 |
max_val = max(values, default=0)
|
163 |
if (max_val < 2) or (count_gte_zero < 2):
|
164 |
+
return plot_NA()
|
|
|
|
|
|
|
165 |
else:
|
166 |
return plot_tf(
|
167 |
grouped,
|
168 |
+
input.frequency(),
|
169 |
+
rule_type=filter_value.get(),
|
170 |
)
|
171 |
|
172 |
with ui.card(full_screen=True):
|
|
|
203 |
#if input.switch():
|
204 |
# pass
|
205 |
# # placeholder for stacked bar chart
|
206 |
+
if len(grouped) < 2:
|
207 |
+
return plot_NA()
|
208 |
+
else:
|
209 |
+
plot = plot_agency(
|
210 |
+
grouped.head(10),
|
211 |
+
rule_type=filter_value.get(),
|
212 |
+
)
|
213 |
+
return plot
|
214 |
|
215 |
with ui.card(full_screen=True):
|
216 |
@render.data_frame
|
|
|
225 |
]
|
226 |
return render.DataTable(grouped.loc[:, [c for c in cols if c in grouped.columns]])
|
227 |
|
228 |
+
# download data
|
229 |
with ui.accordion(open=False):
|
230 |
|
231 |
with ui.accordion_panel("Download Data"):
|
|
|
259 |
await asyncio.sleep(0.25)
|
260 |
yield filtered_df().loc[:, output_cols].to_csv(index=False)
|
261 |
|
262 |
+
# notes
|
263 |
with ui.accordion(open=False):
|
264 |
|
265 |
with ui.accordion_panel("Notes"):
|
|
|
272 |
"""
|
273 |
)
|
274 |
|
275 |
+
# footer citation
|
276 |
ui.markdown(
|
277 |
FOOTER
|
278 |
)
|
|
|
296 |
bool_agency = [True if sum(selected in agency for selected in input.menu_agency()) > 0 else False for agency in filt_df["parent_slug"]]
|
297 |
filt_df = filt_df.loc[bool_agency]
|
298 |
|
299 |
+
print(input.menu_significant())
|
300 |
+
# filter significance
|
301 |
+
bool_ = []
|
302 |
+
if (input.menu_significant() is not None) and ("all" not in input.menu_significant()):
|
303 |
+
if "3f1-significant" in input.menu_significant():
|
304 |
+
bool_.append((filt_df["3f1_significant"] == 1).to_numpy())
|
305 |
+
if "other-significant" in input.menu_significant():
|
306 |
+
bool_.append((filt_df["other_significant"] == 1).to_numpy())
|
307 |
+
filt_df = filt_df.loc[array(bool_).any(axis=0)]
|
308 |
+
|
309 |
+
# return filtered dataframe
|
310 |
return filt_df
|
311 |
|
312 |
|