@@ -95,19 +95,19 @@ def fetch_pr_metrics(merged_since: date, merged_until: date | None = None) -> pd
9595 else :
9696 default_since = date .fromisoformat ("2022-04-01" )
9797
98- # Get until date from query params. Defaults to today, which effectively
99- # means "no upper bound" so the range behaves like a plain "since" filter .
98+ # Get until date from query params. When omitted, the range has no upper
99+ # bound and the input is left empty (optional) .
100100 until_param = st .query_params .get ("until" , None )
101+ default_until : date | None = None
101102 if until_param :
102103 try :
103104 default_until = date .fromisoformat (until_param )
104105 except ValueError :
105- default_until = today
106- else :
107- default_until = today
106+ default_until = None
108107
109108 # Clamp the range so the "Until" date is never before the "Since" date.
110- default_until = min (max (default_until , default_since ), today )
109+ if default_until is not None :
110+ default_until = min (max (default_until , default_since ), today )
111111
112112 since_input = st .date_input (
113113 "Since" ,
@@ -120,7 +120,7 @@ def fetch_pr_metrics(merged_since: date, merged_until: date | None = None) -> pd
120120 value = default_until ,
121121 min_value = since_input ,
122122 max_value = today ,
123- help = "Include PRs and issues on or before this date. Defaults to today ." ,
123+ help = "Include PRs and issues on or before this date. Optional - leave empty for no upper bound ." ,
124124 )
125125
126126 # Allow configuring the bot PR toggle via the `exclude_bots` query param
@@ -133,25 +133,26 @@ def fetch_pr_metrics(merged_since: date, merged_until: date | None = None) -> pd
133133 )
134134 exclude_bot_prs = st .toggle ("Exclude Bot PRs" , value = default_exclude_bots )
135135
136- # Whether an explicit upper bound has been set (i.e. not the default "today").
137- has_until_bound = until_input < today
136+ # Effective upper bound used for filtering; today acts as a no-op bound when no
137+ # explicit "Until" date is selected.
138+ effective_until = until_input if until_input is not None else today
138139
139140# Human-readable description of the selected time range, used in captions.
140- if has_until_bound :
141+ if until_input is not None :
141142 period_label = f"between { since_input .strftime ('%Y/%m/%d' )} and { until_input .strftime ('%Y/%m/%d' )} "
142143else :
143144 period_label = f"since { since_input .strftime ('%Y/%m/%d' )} "
144145
145146# GitHub search query fragment for the selected merged-date range.
146147merged_query_suffix = f"merged%3A>={ since_input .strftime ('%Y-%m-%d' )} "
147- if has_until_bound :
148+ if until_input is not None :
148149 merged_query_suffix += f"+merged%3A<={ until_input .strftime ('%Y-%m-%d' )} "
149150
150151
151152try :
152153 merged_prs_df = fetch_pr_metrics (
153154 merged_since = since_input ,
154- merged_until = until_input if has_until_bound else None ,
155+ merged_until = until_input ,
155156 )
156157except Exception as ex :
157158 # The GitHub GraphQL API can occasionally fail transiently (e.g. non-JSON
@@ -476,7 +477,7 @@ def calculate_percentage(row: dict) -> float:
476477 # Closers who closed issues with the most reactions
477478 closers_df = all_issues_df .copy ()
478479 closers_df = closers_df [
479- (closers_df ["closed_at" ].dt .date >= since_input ) & (closers_df ["closed_at" ].dt .date <= until_input )
480+ (closers_df ["closed_at" ].dt .date >= since_input ) & (closers_df ["closed_at" ].dt .date <= effective_until )
480481 ]
481482
482483 closers_df ["closed_by_login" ] = closers_df ["closed_by" ].apply (
@@ -637,7 +638,7 @@ def calculate_percentage(row: dict) -> float:
637638 authors_df = authors_df [authors_df ["author" ] != "" ]
638639
639640 authors_df = authors_df [
640- (authors_df ["created_at" ].dt .date >= since_input ) & (authors_df ["created_at" ].dt .date <= until_input )
641+ (authors_df ["created_at" ].dt .date >= since_input ) & (authors_df ["created_at" ].dt .date <= effective_until )
641642 ]
642643
643644 st .caption (
@@ -1384,7 +1385,7 @@ def calculate_percentage(row: dict) -> float:
13841385 closed_reactions_df = reactions_issues_df [
13851386 (reactions_issues_df ["closed_at" ].notna ())
13861387 & (reactions_issues_df ["closed_at" ].dt .date >= since_input )
1387- & (reactions_issues_df ["closed_at" ].dt .date <= until_input )
1388+ & (reactions_issues_df ["closed_at" ].dt .date <= effective_until )
13881389 ].copy ()
13891390
13901391 if not closed_reactions_df .empty :
@@ -1523,14 +1524,15 @@ def calculate_percentage(row: dict) -> float:
15231524
15241525 # Filter by date for "Created" metrics
15251526 created_in_period = all_issues_df [
1526- (all_issues_df ["created_at" ].dt .date >= since_input ) & (all_issues_df ["created_at" ].dt .date <= until_input )
1527+ (all_issues_df ["created_at" ].dt .date >= since_input )
1528+ & (all_issues_df ["created_at" ].dt .date <= effective_until )
15271529 ]
15281530
15291531 # Filter by date for "Closed" metrics
15301532 closed_in_period = all_issues_df [
15311533 (all_issues_df ["closed_at" ].notna ())
15321534 & (all_issues_df ["closed_at" ].dt .date >= since_input )
1533- & (all_issues_df ["closed_at" ].dt .date <= until_input )
1535+ & (all_issues_df ["closed_at" ].dt .date <= effective_until )
15341536 ]
15351537
15361538 total_created = len (created_in_period )
0 commit comments