Skip to content

Commit 7e63a7e

Browse files
committed
contrib_graph: track Generated-by adoption trend
Add git log parsing for the Generated-by trailer and render a monthly adoption timeline in the dashboard. Rework the layout to fit the new subplot and show tag totals in the stats panel. Update the contribution docs to describe the seventh panel. Generated-by: ChatGPT Codex Signed-off-by: Luis Chamberlain <[email protected]>
1 parent c0fe526 commit 7e63a7e

File tree

2 files changed

+168
-14
lines changed

2 files changed

+168
-14
lines changed

docs/contrib/README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ make contrib-graph
2525

2626
## Generated Files
2727

28-
The visualization tool creates comprehensive 6-panel dashboards saved as:
28+
The visualization tool creates comprehensive 7-panel dashboards saved as:
2929

3030
### For Specific Year
3131
- `kdevops_contributions_YYYY.png` - High-resolution image (300 DPI)
@@ -37,7 +37,7 @@ The visualization tool creates comprehensive 6-panel dashboards saved as:
3737

3838
## Visualization Components
3939

40-
Each generated dashboard includes six analytical panels:
40+
Each generated dashboard includes seven analytical panels:
4141

4242
### 1. Total Commits Bar Chart
4343
Shows the overall contribution ranking by commit count. Names are automatically shortened for contributors with fewer commits to prevent overlapping labels.
@@ -61,6 +61,10 @@ Key metrics including:
6161
- Most active month
6262
- Top contributor
6363
- Analysis period and generation timestamp
64+
- Generated-by tag usage share
65+
66+
### 7. Generated-by Tag Adoption Trend
67+
Shows how often commits include the `Generated-by:` trailer each month. This timeline highlights adoption of automated contribution tags and makes it easy to spot growth in AI-assisted commit generation.
6468

6569
## Technical Details
6670

@@ -141,6 +145,7 @@ The contribution analysis is integrated into the kdevops build system:
141145
- **Growing Contributor Base**: More contributors over time
142146
- **Sustained Activity**: Regular commits throughout periods
143147
- **Balanced Contributions**: Activity from multiple contributors
148+
- **Generated-by Adoption**: Visibility into automated tag usage trends
144149

145150
## Files in This Directory
146151

scripts/contrib_graph.py

Lines changed: 161 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import calendar
1414
from collections import defaultdict
1515
import matplotlib.pyplot as plt
16+
import matplotlib.dates as mdates
1617
import numpy as np
1718
import pandas as pd
1819
import seaborn as sns
@@ -143,6 +144,24 @@ def get_contribution_data(year=None, month=None):
143144
except (ValueError, IndexError):
144145
continue
145146

147+
# Track Generated-by usage over time
148+
cmd = (
149+
f'git log {date_filter} --grep="Generated-by:" '
150+
'--pretty=format:"%cd" --date=format:"%Y-%m"'
151+
)
152+
output = run_git_command(cmd)
153+
154+
generated_by_monthly = defaultdict(int)
155+
156+
for line in output.split("\n"):
157+
entry = line.strip()
158+
if entry:
159+
try:
160+
month_date = datetime.strptime(f"{entry}-01", "%Y-%m-%d").date()
161+
generated_by_monthly[month_date] += 1
162+
except ValueError:
163+
continue
164+
146165
# Get total commits for period calculation
147166
cmd = f"git log {date_filter} --oneline | wc -l"
148167
total_commits = int(run_git_command(cmd))
@@ -170,6 +189,7 @@ def get_contribution_data(year=None, month=None):
170189
total_commits,
171190
period_desc,
172191
ai_boom_marker,
192+
dict(generated_by_monthly),
173193
)
174194

175195

@@ -179,11 +199,15 @@ def create_contribution_graphs(
179199
total_commits,
180200
period_desc,
181201
ai_boom_marker=None,
202+
generated_by_monthly=None,
182203
year=None,
183204
month=None,
184205
):
185206
"""Create comprehensive visualization graphs"""
186207

208+
if generated_by_monthly is None:
209+
generated_by_monthly = {}
210+
187211
# Determine output filename
188212
if year:
189213
if month:
@@ -222,12 +246,19 @@ def create_contribution_graphs(
222246
title_suffix = " (All Time)"
223247

224248
# Create figure with multiple subplots and better styling
225-
fig = plt.figure(figsize=(22, 14), facecolor='#f8f9fa')
249+
fig = plt.figure(figsize=(24, 16), facecolor='#f8f9fa')
250+
gs = fig.add_gridspec(
251+
3,
252+
3,
253+
height_ratios=[1, 1, 1.1],
254+
hspace=0.35,
255+
wspace=0.3,
256+
)
226257
fig.suptitle(
227258
f"kdevops Contribution Analysis{title_suffix}",
228259
fontsize=24,
229260
fontweight="bold",
230-
y=0.98,
261+
y=0.985,
231262
color='#2c3e50'
232263
)
233264

@@ -236,7 +267,7 @@ def create_contribution_graphs(
236267
return None
237268

238269
# 1. Total Contributions Bar Chart
239-
ax1 = plt.subplot(2, 3, 1)
270+
ax1 = fig.add_subplot(gs[0, 0])
240271
contributors = list(contributors_total.keys())
241272
commits = list(contributors_total.values())
242273

@@ -296,7 +327,7 @@ def create_contribution_graphs(
296327
)
297328

298329
# 2. Pie Chart of Contributions (top contributors only)
299-
ax2 = plt.subplot(2, 3, 2)
330+
ax2 = fig.add_subplot(gs[0, 1])
300331
# Group smaller contributors together
301332
main_contributors = dict(list(contributors_total.items())[:8]) # Top 8
302333
others_count = (
@@ -344,7 +375,7 @@ def create_contribution_graphs(
344375
)
345376

346377
# 3. Monthly Activity Heatmap
347-
ax3 = plt.subplot(2, 3, 3)
378+
ax3 = fig.add_subplot(gs[0, 2])
348379

349380
# Get months that have activity
350381
active_months = set()
@@ -427,7 +458,7 @@ def create_contribution_graphs(
427458
ax3.set_title("Monthly Activity Heatmap", fontweight="bold", fontsize=14)
428459

429460
# 4. Monthly Timeline
430-
ax4 = plt.subplot(2, 3, 4)
461+
ax4 = fig.add_subplot(gs[1, 0])
431462

432463
if active_months:
433464
# Calculate total commits per month
@@ -562,7 +593,7 @@ def create_contribution_graphs(
562593
ax4.set_title("Monthly Commit Activity", fontweight="bold", fontsize=14)
563594

564595
# 5. Top Contributors Timeline
565-
ax5 = plt.subplot(2, 3, 5)
596+
ax5 = fig.add_subplot(gs[1, 1])
566597

567598

568599
if active_months:
@@ -720,7 +751,7 @@ def create_contribution_graphs(
720751
)
721752

722753
# 6. Statistics Summary
723-
ax6 = plt.subplot(2, 3, 6)
754+
ax6 = fig.add_subplot(gs[1, 2])
724755
ax6.axis("off")
725756

726757
# Calculate stats
@@ -774,6 +805,11 @@ def create_contribution_graphs(
774805
else ("N/A", 0)
775806
)
776807

808+
generated_by_total = sum(generated_by_monthly.values())
809+
generated_by_percentage = (
810+
(generated_by_total / total_commits) * 100 if total_commits else 0
811+
)
812+
777813
# Add note about date anomaly if we detect future dates
778814
current_year = datetime.now().year
779815
date_note = ""
@@ -794,6 +830,8 @@ def create_contribution_graphs(
794830
795831
Active Months: {active_months_count}
796832
833+
Generated-by Tag Commits: {generated_by_total} ({generated_by_percentage:.1f}% of total)
834+
797835
Project: Linux Kernel DevOps Framework
798836
Analysis Period: {period_desc}
799837
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}{date_note}
@@ -816,7 +854,112 @@ def create_contribution_graphs(
816854
color='#2c3e50'
817855
)
818856

819-
plt.tight_layout(pad=2.0)
857+
# 7. Generated-by Tag Adoption Trend
858+
ax7 = fig.add_subplot(gs[2, :])
859+
if generated_by_monthly:
860+
timeline_map = {k: v for k, v in generated_by_monthly.items()}
861+
sorted_dates = sorted(timeline_map.keys())
862+
min_date = sorted_dates[0]
863+
max_date = sorted_dates[-1]
864+
date_range = pd.date_range(start=min_date, end=max_date, freq="MS")
865+
timeline_dates = [d.to_pydatetime() for d in date_range]
866+
timeline_counts = [timeline_map.get(d.date(), 0) for d in date_range]
867+
868+
ax7.plot(
869+
timeline_dates,
870+
timeline_counts,
871+
marker="o",
872+
linewidth=3,
873+
markersize=8,
874+
color="#8e44ad",
875+
markerfacecolor="#f39c12",
876+
markeredgecolor="#2c3e50",
877+
markeredgewidth=1.2,
878+
)
879+
if len(timeline_dates) > 1:
880+
ax7.fill_between(
881+
timeline_dates,
882+
timeline_counts,
883+
alpha=0.15,
884+
color="#8e44ad",
885+
)
886+
887+
locator = mdates.AutoDateLocator()
888+
formatter = mdates.ConciseDateFormatter(locator)
889+
ax7.xaxis.set_major_locator(locator)
890+
ax7.xaxis.set_major_formatter(formatter)
891+
892+
ax7.set_title(
893+
"Generated-by Tag Adoption Over Time",
894+
fontweight="bold",
895+
fontsize=16,
896+
color='#2c3e50',
897+
pad=20,
898+
)
899+
ax7.set_ylabel("Commits with Generated-by", fontsize=12, color='#34495e')
900+
ax7.set_xlabel("Month", fontsize=12, color='#34495e')
901+
ax7.spines['top'].set_visible(False)
902+
ax7.spines['right'].set_visible(False)
903+
ax7.grid(True, alpha=0.3)
904+
ax7.set_ylim(bottom=0)
905+
ax7.tick_params(axis="x", labelrotation=45)
906+
907+
ax7.text(
908+
0.01,
909+
0.95,
910+
f"Total commits tagged: {generated_by_total} ({generated_by_percentage:.1f}% of commits)",
911+
transform=ax7.transAxes,
912+
fontsize=10,
913+
fontweight="bold",
914+
color="#2c3e50",
915+
bbox=dict(
916+
boxstyle="round,pad=0.4",
917+
facecolor="#f4ecf7",
918+
edgecolor="#8e44ad",
919+
linewidth=1.2,
920+
alpha=0.6,
921+
),
922+
verticalalignment="top",
923+
)
924+
925+
if (year is None) or (int(year) == datetime.now().year):
926+
current_marker = datetime.now()
927+
if timeline_dates and timeline_dates[0] <= current_marker <= timeline_dates[-1]:
928+
ax7.axvline(
929+
current_marker,
930+
color="#7f8c8d",
931+
linestyle=":",
932+
linewidth=1.2,
933+
alpha=0.7,
934+
)
935+
else:
936+
ax7.set_title(
937+
"Generated-by Tag Adoption Over Time",
938+
fontweight="bold",
939+
fontsize=16,
940+
color="#2c3e50",
941+
pad=20,
942+
)
943+
ax7.spines['top'].set_visible(False)
944+
ax7.spines['right'].set_visible(False)
945+
ax7.set_ylabel("Commits with Generated-by", fontsize=12, color="#34495e")
946+
ax7.set_xlabel("Month", fontsize=12, color="#34495e")
947+
ax7.tick_params(axis="x", labelbottom=False)
948+
ax7.tick_params(axis="y", labelleft=False)
949+
ax7.set_ylim(0, 1)
950+
ax7.text(
951+
0.5,
952+
0.5,
953+
"No Generated-by tag usage detected",
954+
ha="center",
955+
va="center",
956+
transform=ax7.transAxes,
957+
fontsize=12,
958+
fontweight="bold",
959+
color="#7f8c8d",
960+
)
961+
962+
fig.tight_layout(rect=[0, 0, 1, 0.97], pad=2.5)
820963

821964
# Ensure output directory exists and save the plots
822965
os.makedirs("docs/contrib", exist_ok=True)
@@ -897,9 +1040,14 @@ def main():
8971040

8981041
# Get contribution data
8991042
month_int = int(args.month) if args.month else None
900-
contributors_total, monthly_data, total_commits, period_desc, ai_boom_marker = (
901-
get_contribution_data(args.year, month_int)
902-
)
1043+
(
1044+
contributors_total,
1045+
monthly_data,
1046+
total_commits,
1047+
period_desc,
1048+
ai_boom_marker,
1049+
generated_by_monthly,
1050+
) = get_contribution_data(args.year, month_int)
9031051

9041052
# Create visualization
9051053
fig = create_contribution_graphs(
@@ -908,6 +1056,7 @@ def main():
9081056
total_commits,
9091057
period_desc,
9101058
ai_boom_marker,
1059+
generated_by_monthly,
9111060
args.year,
9121061
month_int,
9131062
)

0 commit comments

Comments
 (0)