|
3 | 3 | import pandas as pd |
4 | 4 | from PIL import Image, ImageDraw, ImageFont |
5 | 5 | from github_client import calculate_community_health |
| 6 | +import plotly.express as px |
| 7 | +from datetime import datetime, timedelta |
6 | 8 |
|
7 | 9 | # --- Layout Configuration --- |
8 | 10 | st.set_page_config( |
|
140 | 142 | st.dataframe(df_pulls, use_container_width=True) |
141 | 143 |
|
142 | 144 | # --------------------------------------------------------- |
143 | | -# 📈 TAB 3: TRENDS |
| 145 | +# 📈 TAB 3: TREND ANALYSIS & OPERATIONAL BOTTLENECKS |
144 | 146 | # --------------------------------------------------------- |
145 | 147 | with tab_trend: |
146 | 148 | st.header("Issue Trend Analysis") |
| 149 | + st.subheader("Macroscopic view of project progression") |
| 150 | + |
| 151 | + if not df_issues.empty: |
| 152 | + # Clone raw data to avoid mutations |
| 153 | + df_trends = df_issues.copy() |
| 154 | + |
| 155 | + # Ensure datetime parsing is active |
| 156 | + df_trends["created_at"] = pd.to_datetime(df_trends["created_at"]) |
| 157 | + df_trends["closed_at"] = pd.to_datetime(df_trends["closed_at"]) |
| 158 | + |
| 159 | + # 🎛️ TIME-RANGE SIDEBAR CONTROL Extension |
| 160 | + st.sidebar.markdown("---") |
| 161 | + st.sidebar.header("📈 Historical Scope") |
| 162 | + |
| 163 | + time_window = st.sidebar.selectbox( |
| 164 | + "Select Time Frame:", |
| 165 | + options=[ |
| 166 | + "Last 30 Days", |
| 167 | + "Last 60 Days", |
| 168 | + "Last 90 Days", |
| 169 | + "All Retrieved (Recent 100)", |
| 170 | + ], |
| 171 | + index=3, |
| 172 | + ) |
| 173 | + |
| 174 | + # Apply Time Range Filters using explicit delta offsets |
| 175 | + now = datetime.now(df_trends["created_at"].dt.tz) |
| 176 | + if time_window == "Last 30 Days": |
| 177 | + cutoff = now - timedelta(days=30) |
| 178 | + df_trends = df_trends[df_trends["created_at"] >= cutoff] |
| 179 | + elif time_window == "Last 60 Days": |
| 180 | + cutoff = now - timedelta(days=60) |
| 181 | + df_trends = df_trends[df_trends["created_at"] >= cutoff] |
| 182 | + elif time_window == "Last 90 Days": |
| 183 | + cutoff = now - timedelta(days=90) |
| 184 | + df_trends = df_trends[df_trends["created_at"] >= cutoff] |
| 185 | + elif time_window == "All Retrieved (Recent 100)": |
| 186 | + pass # No filtering needed |
| 187 | + |
| 188 | + # --- SECTION 1: CUMULATIVE VOLUMES (TREND ANALYSIS) --- |
| 189 | + st.markdown("### 📊 Cumulative Issues Volumetrics") |
| 190 | + # Updated description helper note |
| 191 | + st.write( |
| 192 | + f"Historical projection matching data window: `{time_window}`" |
| 193 | + ) |
| 194 | + # Sort dates to build timeline |
| 195 | + df_trends = df_trends.sort_values("created_at") |
| 196 | + df_trends["date_only"] = df_trends["created_at"].dt.date |
| 197 | + |
| 198 | + # Compute arrival totals |
| 199 | + created_daily = ( |
| 200 | + df_trends.groupby("date_only").size().reset_index(name="Opened") |
| 201 | + ) |
| 202 | + created_daily["Cumulative Opened"] = created_daily["Opened"].cumsum() |
| 203 | + fig_cum = px.area( |
| 204 | + created_daily, |
| 205 | + x="date_only", |
| 206 | + y="Cumulative Opened", |
| 207 | + title=f"Ecosystem Growth Tracking ({time_window})", |
| 208 | + labels={ |
| 209 | + "date_only": "Timeline", |
| 210 | + "Cumulative Opened": "Total Issues Created", |
| 211 | + }, |
| 212 | + template="plotly_dark", |
| 213 | + ) |
| 214 | + st.plotly_chart(fig_cum, use_container_width=True) |
| 215 | + |
| 216 | + st.divider() |
| 217 | + |
| 218 | + # --- SECTION 2: BOTTLENECK IDENTIFICATION BY TAGS --- |
| 219 | + st.markdown("### 🏷️ Workload Bottleneck Analysis") |
| 220 | + st.write("Breakdown of task volume by community label types.") |
| 221 | + |
| 222 | + # Explode tags out to analyze categorical density |
| 223 | + df_exploded = df_trends.explode("labels") |
| 224 | + |
| 225 | + if not df_exploded.empty and df_exploded["labels"].notna().any(): |
| 226 | + tag_counts = ( |
| 227 | + df_exploded.groupby("labels") |
| 228 | + .size() |
| 229 | + .reset_index(name="Issue Count") |
| 230 | + .sort_values(by="Issue Count", ascending=False) |
| 231 | + ) |
| 232 | + |
| 233 | + # Generate horizontal bar plot to analyze tag backlogs |
| 234 | + fig_tags = px.bar( |
| 235 | + tag_counts, |
| 236 | + x="Issue Count", |
| 237 | + y="labels", |
| 238 | + orientation="h", |
| 239 | + title="Density Distribution of Active Labels", |
| 240 | + labels={ |
| 241 | + "labels": "Repository Tag", |
| 242 | + "Issue Count": "Volume", |
| 243 | + }, |
| 244 | + color="Issue Count", |
| 245 | + color_continuous_scale="Blues", |
| 246 | + template="plotly_dark", |
| 247 | + ) |
| 248 | + fig_tags.update_layout(yaxis={"categoryorder": "total ascending"}) |
| 249 | + st.plotly_chart(fig_tags, use_container_width=True) |
| 250 | + else: |
| 251 | + st.info("No categorical tags detected within this range.") |
| 252 | + |
| 253 | + else: |
| 254 | + st.info("No baseline issue data found to chart trend analysis logs.") |
147 | 255 |
|
148 | 256 | # --------------------------------------------------------- |
149 | 257 | # 📱 TAB 4: MARKETING & SOCIAL MEDIA ASSETS |
|
0 commit comments