Mark Febrizio commited on
Commit
7da65a3
1 Parent(s): 808d55e

Alt significance (#17)

Browse files

* Update app.py

* fix bugs in new summary layout

* Update plotting.py

account for multiple rule type inputs

Files changed (2) hide show
  1. app.py +11 -92
  2. modules/plotting.py +17 -16
app.py CHANGED
@@ -1,9 +1,6 @@
1
  # ----- IMPORTS ----- #
2
 
3
 
4
- # ----- IMPORTS ----- #
5
-
6
-
7
  import asyncio
8
  from datetime import datetime, date, time
9
  from pathlib import Path
@@ -40,14 +37,6 @@ ui.include_css( Path(__file__).parent.joinpath("www") / "style.css")
40
  # ----- CREATE OBJECTS ----- #
41
 
42
 
43
- # this text appears in the browser tab
44
- # load css styles from external file
45
- ui.include_css( Path(__file__).parent.joinpath("www") / "style.css")
46
-
47
-
48
- # ----- CREATE OBJECTS ----- #
49
-
50
-
51
  # this text appears in the browser tab
52
  TITLE = "CRA Window Tracker - GW Regulatory Studies Center"
53
 
@@ -61,15 +50,6 @@ page_header = ui.HTML(
61
  """
62
  )
63
 
64
- # logo at the top of the sidebar
65
- page_header = ui.HTML(
66
- f"""
67
- <div class="header">
68
- <span>{HEADER}</span>
69
- </div>
70
- """
71
- )
72
-
73
  # logo at the top of the sidebar
74
  sidebar_logo = ui.HTML(
75
  f"""
@@ -92,19 +72,6 @@ FOOTER = f"""
92
  # ----- APP LAYOUT ----- #
93
 
94
 
95
- ui.tags.title(TITLE)
96
-
97
- # footer at the bottom of the page
98
- FOOTER = f"""
99
- -----
100
-
101
- &copy; 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.
102
- """
103
-
104
-
105
- # ----- APP LAYOUT ----- #
106
-
107
-
108
  ui.tags.title(TITLE)
109
 
110
  page_header
@@ -113,7 +80,6 @@ page_header
113
  with ui.sidebar(open={"desktop": "open", "mobile": "closed"}):
114
  sidebar_logo
115
 
116
- #ui.markdown("Estimated CRA window open date: [May 23](https://www.huntonak.com/the-nickel-report/federal-agencies-face-looming-congressional-review-act-deadline)")
117
  with ui.tooltip(placement="right", id="window_tooltip"):
118
  ui.input_date("start_date", "Select start of window", value=WINDOW_OPEN_DATE, min=START_DATE, max=date.today())
119
  "The estimated CRA window open date is May 23. See the notes for more information."
@@ -124,8 +90,6 @@ with ui.sidebar(open={"desktop": "open", "mobile": "closed"}):
124
 
125
  ui.input_select("menu_significant", "Select rule significance", choices=["all", "3f1-significant", "other-significant"], selected="all", multiple=True)
126
 
127
- #ui.input_switch("switch", "Stack significant rules in plots", False)
128
-
129
  # value boxes with summary data
130
  with ui.layout_column_wrap():
131
  with ui.value_box():
@@ -133,8 +97,7 @@ with ui.layout_column_wrap():
133
  @render.text
134
  def count_rules():
135
  return f"{filtered_df()['document_number'].count()}"
136
- ui.input_action_button("filter_all", "Clear Filters", class_="btn-filter", icon=icon_svg("book"))
137
- ui.input_action_button("filter_all", "Clear Filters", class_="btn-filter", icon=icon_svg("book"))
138
 
139
  with ui.value_box():
140
  "Section 3(f)(1) Significant rules *"
@@ -144,8 +107,7 @@ with ui.layout_column_wrap():
144
  if GET_SIGNIFICANT:
145
  output = f"{filtered_df()['3f1_significant'].sum()}"
146
  return output
147
- ui.input_action_button("filter_3f1", "Filter", class_="btn-filter", icon=icon_svg("book"))
148
- ui.input_action_button("filter_3f1", "Filter", class_="btn-filter", icon=icon_svg("book"))
149
 
150
  with ui.value_box():
151
  "Other Significant rules *"
@@ -155,8 +117,7 @@ with ui.layout_column_wrap():
155
  if GET_SIGNIFICANT:
156
  output = f"{filtered_df()['other_significant'].sum()}"
157
  return output
158
- ui.input_action_button("filter_other", "Filter", class_="btn-filter", icon=icon_svg("book"))
159
- ui.input_action_button("filter_other", "Filter", class_="btn-filter", icon=icon_svg("book"))
160
 
161
  # documentation note on significance data
162
  ui.markdown(
@@ -171,7 +132,7 @@ with ui.navset_card_underline(title=""):
171
  with ui.nav_panel("Rules in detail"):
172
  @render.data_frame
173
  def table_rule_detail():
174
- df = filtered_sig().copy()
175
  df.loc[:, "date"] = df.loc[:, "publication_date"].apply(lambda x: f"{x.date()}")
176
  char, limit = " ", 10
177
  df.loc[:, "title"] = df["title"].apply(lambda x: x if len(x.split(char)) < (limit + 1) else f"{char.join(x.split(char)[:limit])}...")
@@ -203,7 +164,7 @@ with ui.navset_card_underline(title=""):
203
  return plot_tf(
204
  grouped,
205
  input.frequency(),
206
- rule_type=filter_value.get(),
207
  )
208
 
209
  with ui.card(full_screen=True):
@@ -236,16 +197,13 @@ with ui.navset_card_underline(title=""):
236
  with ui.card(full_screen=True):
237
  @render.plot
238
  def plot_by_agency():
239
- grouped = grouped_df_agency()
240
- #if input.switch():
241
- # pass
242
- # # placeholder for stacked bar chart
243
  if len(grouped) < 2:
244
  return plot_NA()
245
  else:
246
  plot = plot_agency(
247
  grouped.head(10),
248
- rule_type=filter_value.get(),
249
  )
250
  return plot
251
 
@@ -346,31 +304,16 @@ def filtered_df():
346
  return filt_df
347
 
348
 
349
- @reactive.calc
350
- def filtered_sig():
351
- filt_df = filtered_df()
352
-
353
- # filter significance
354
- if filter_value.get() == "all":
355
- pass
356
- elif filter_value.get() == "3f1":
357
- filt_df = filt_df.loc[filt_df["3f1_significant"] == 1]
358
- elif filter_value.get() == "other":
359
- filt_df = filt_df.loc[filt_df["other_significant"] == 1]
360
-
361
- return filt_df
362
-
363
-
364
  @reactive.calc
365
  def grouped_df_month():
366
- filt_df = filtered_sig()
367
  grouped = groupby_date(filt_df, significant=GET_SIGNIFICANT)
368
  return grouped
369
 
370
 
371
  @reactive.calc
372
  def grouped_df_day():
373
- filt_df = filtered_sig()
374
  date_col = "publication_date"
375
  grouped = groupby_date(filt_df, group_col=date_col, significant=GET_SIGNIFICANT)
376
  grouped = pad_missing_dates(
@@ -387,7 +330,7 @@ def grouped_df_day():
387
 
388
  @reactive.calc
389
  def grouped_df_week():
390
- filt_df = filtered_sig()
391
  filt_df = add_weeks_to_data(filt_df)
392
  try:
393
  grouped = groupby_date(filt_df, group_col=("week_number", "week_of"), significant=GET_SIGNIFICANT)
@@ -407,7 +350,7 @@ def grouped_df_week():
407
 
408
  @reactive.calc
409
  def grouped_df_agency():
410
- filt_df = filtered_sig()
411
  grouped = groupby_agency(filt_df, metadata=METADATA, significant=GET_SIGNIFICANT)
412
  return grouped
413
 
@@ -423,27 +366,3 @@ def get_grouped_data_over_time():
423
  else:
424
  raise ValueError("Only 'daily', 'monthly', or 'weekly' are valid inputs.")
425
  return grouped
426
-
427
-
428
- # ----- REACTIVE VALUES ----- #
429
-
430
-
431
- filter_value = reactive.value("all")
432
-
433
- @reactive.effect
434
- @reactive.event(input.filter_all)
435
- def _():
436
- filter_value.set("all")
437
- filtered_df()
438
-
439
- @reactive.effect
440
- @reactive.event(input.filter_3f1)
441
- def _():
442
- filter_value.set("3f1")
443
- filtered_df()
444
-
445
- @reactive.effect
446
- @reactive.event(input.filter_other)
447
- def _():
448
- filter_value.set("other")
449
- filtered_df()
 
1
  # ----- IMPORTS ----- #
2
 
3
 
 
 
 
4
  import asyncio
5
  from datetime import datetime, date, time
6
  from pathlib import Path
 
37
  # ----- CREATE OBJECTS ----- #
38
 
39
 
 
 
 
 
 
 
 
 
40
  # this text appears in the browser tab
41
  TITLE = "CRA Window Tracker - GW Regulatory Studies Center"
42
 
 
50
  """
51
  )
52
 
 
 
 
 
 
 
 
 
 
53
  # logo at the top of the sidebar
54
  sidebar_logo = ui.HTML(
55
  f"""
 
72
  # ----- APP LAYOUT ----- #
73
 
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  ui.tags.title(TITLE)
76
 
77
  page_header
 
80
  with ui.sidebar(open={"desktop": "open", "mobile": "closed"}):
81
  sidebar_logo
82
 
 
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."
 
90
 
91
  ui.input_select("menu_significant", "Select rule significance", choices=["all", "3f1-significant", "other-significant"], selected="all", multiple=True)
92
 
 
 
93
  # value boxes with summary data
94
  with ui.layout_column_wrap():
95
  with ui.value_box():
 
97
  @render.text
98
  def count_rules():
99
  return f"{filtered_df()['document_number'].count()}"
100
+ #ui.input_action_button("filter_all", "Clear Filters", class_="btn-filter", icon=icon_svg("book"))
 
101
 
102
  with ui.value_box():
103
  "Section 3(f)(1) Significant rules *"
 
107
  if GET_SIGNIFICANT:
108
  output = f"{filtered_df()['3f1_significant'].sum()}"
109
  return output
110
+ #ui.input_action_button("filter_3f1", "Filter", class_="btn-filter", icon=icon_svg("book"))
 
111
 
112
  with ui.value_box():
113
  "Other Significant rules *"
 
117
  if GET_SIGNIFICANT:
118
  output = f"{filtered_df()['other_significant'].sum()}"
119
  return output
120
+ #ui.input_action_button("filter_other", "Filter", class_="btn-filter", icon=icon_svg("book"))
 
121
 
122
  # documentation note on significance data
123
  ui.markdown(
 
132
  with ui.nav_panel("Rules in detail"):
133
  @render.data_frame
134
  def table_rule_detail():
135
+ df = filtered_df().copy()
136
  df.loc[:, "date"] = df.loc[:, "publication_date"].apply(lambda x: f"{x.date()}")
137
  char, limit = " ", 10
138
  df.loc[:, "title"] = df["title"].apply(lambda x: x if len(x.split(char)) < (limit + 1) else f"{char.join(x.split(char)[:limit])}...")
 
164
  return plot_tf(
165
  grouped,
166
  input.frequency(),
167
+ rule_types=input.menu_significant(),
168
  )
169
 
170
  with ui.card(full_screen=True):
 
197
  with ui.card(full_screen=True):
198
  @render.plot
199
  def plot_by_agency():
200
+ grouped = grouped_df_agency()
 
 
 
201
  if len(grouped) < 2:
202
  return plot_NA()
203
  else:
204
  plot = plot_agency(
205
  grouped.head(10),
206
+ rule_types=input.menu_significant(),
207
  )
208
  return plot
209
 
 
304
  return filt_df
305
 
306
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  @reactive.calc
308
  def grouped_df_month():
309
+ filt_df = filtered_df()
310
  grouped = groupby_date(filt_df, significant=GET_SIGNIFICANT)
311
  return grouped
312
 
313
 
314
  @reactive.calc
315
  def grouped_df_day():
316
+ filt_df = filtered_df()
317
  date_col = "publication_date"
318
  grouped = groupby_date(filt_df, group_col=date_col, significant=GET_SIGNIFICANT)
319
  grouped = pad_missing_dates(
 
330
 
331
  @reactive.calc
332
  def grouped_df_week():
333
+ filt_df = filtered_df()
334
  filt_df = add_weeks_to_data(filt_df)
335
  try:
336
  grouped = groupby_date(filt_df, group_col=("week_number", "week_of"), significant=GET_SIGNIFICANT)
 
350
 
351
  @reactive.calc
352
  def grouped_df_agency():
353
+ filt_df = filtered_df()
354
  grouped = groupby_agency(filt_df, metadata=METADATA, significant=GET_SIGNIFICANT)
355
  return grouped
356
 
 
366
  else:
367
  raise ValueError("Only 'daily', 'monthly', or 'weekly' are valid inputs.")
368
  return grouped
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/plotting.py CHANGED
@@ -31,20 +31,21 @@ def plot_NA(placeholder_text: str = "Not enough data available to visualize.", p
31
  )
32
 
33
 
34
- def generate_rule_axis_label(rule_type: str | None = None):
35
  """Generate axis label for rules, accounting for rule type ("all", "3f1", or "other")."""
36
- y_lab = ""
37
- if rule_type is not None:
38
- rule_type_options = {
39
- "all": "",
40
- "3f1": "3(f)(1) Significant",
41
- "other": "Other Significant",
42
- }
43
- y_lab = f"Number of {rule_type_options.get(rule_type)} rules".replace(" ", " ")
44
- return y_lab
45
-
46
-
47
- def plot_agency(df, group_col = "acronym", value_col = "rules", color="#033C5A", rule_type: str | None = None):
 
48
  """Plot rules by agency.
49
 
50
  Args:
@@ -57,7 +58,7 @@ def plot_agency(df, group_col = "acronym", value_col = "rules", color="#033C5A",
57
  """
58
  order_list = df.loc[:, group_col].to_list()[::-1]
59
 
60
- y_lab = generate_rule_axis_label(rule_type)
61
 
62
  plot = (
63
  ggplot(
@@ -212,7 +213,7 @@ def plot_week(
212
  return plot
213
 
214
 
215
- def plot_tf(df: DataFrame, frequency: str, rule_type: str | None = None, **kwargs) -> ggplot:
216
  """Plot rules over time by given frequency.
217
 
218
  Args:
@@ -234,6 +235,6 @@ def plot_tf(df: DataFrame, frequency: str, rule_type: str | None = None, **kwarg
234
  if plot_freq is None:
235
  raise ValueError(f"Frequency must be one of: {', '.join(freq_options.keys())}")
236
 
237
- y_lab = generate_rule_axis_label(rule_type)
238
 
239
  return plot_freq(df, y_lab=y_lab, **kwargs)
 
31
  )
32
 
33
 
34
+ def generate_rule_axis_label(rule_types: list | None = None):
35
  """Generate axis label for rules, accounting for rule type ("all", "3f1", or "other")."""
36
+ categories = ""
37
+ if (rule_types is None) or ("all" in rule_types):
38
+ pass
39
+ elif all(True if cat in rule_types else False for cat in ("3f1-significant", "other-significant")):
40
+ categories = "significant"
41
+ elif ("3f1-significant" in rule_types) and ("other-significant" not in rule_types):
42
+ categories = "Section 3(f)(1) Significant"
43
+ elif ("3f1-significant" not in rule_types) and ("other-significant" in rule_types):
44
+ categories = "Other Significant"
45
+ return f"Number of {categories} rules".replace(" ", " ")
46
+
47
+
48
+ def plot_agency(df, group_col = "acronym", value_col = "rules", color="#033C5A", rule_types: list | None = None):
49
  """Plot rules by agency.
50
 
51
  Args:
 
58
  """
59
  order_list = df.loc[:, group_col].to_list()[::-1]
60
 
61
+ y_lab = generate_rule_axis_label(rule_types)
62
 
63
  plot = (
64
  ggplot(
 
213
  return plot
214
 
215
 
216
+ def plot_tf(df: DataFrame, frequency: str, rule_types: str | None = None, **kwargs) -> ggplot:
217
  """Plot rules over time by given frequency.
218
 
219
  Args:
 
235
  if plot_freq is None:
236
  raise ValueError(f"Frequency must be one of: {', '.join(freq_options.keys())}")
237
 
238
+ y_lab = generate_rule_axis_label(rule_types)
239
 
240
  return plot_freq(df, y_lab=y_lab, **kwargs)