@@ -465,6 +465,34 @@ def _extract_team(df: pd.DataFrame) -> List[Dict[str, Any]]:
465465 "series" : series_30 ,
466466 })
467467
468+ # 19-T: Avg merge cycle time (by week) for this team
469+ if "created_at" in tdf .columns and "merged_at" in tdf .columns :
470+ ctdf = tdf .dropna (subset = ["created_at" , "merged_at" ]).copy ()
471+ ctdf ["cycle_hours" ] = (
472+ pd .to_datetime (ctdf ["merged_at" ], format = "mixed" , utc = False , errors = "coerce" )
473+ - pd .to_datetime (ctdf ["created_at" ], format = "mixed" , utc = False , errors = "coerce" )
474+ ).dt .total_seconds () / 3600
475+ ctdf = ctdf [ctdf ["cycle_hours" ] >= 0 ]
476+ if not ctdf .empty :
477+ merged = pd .to_datetime (ctdf ["merged_at" ], format = "mixed" , utc = False , errors = "coerce" )
478+ if merged .dt .tz is not None :
479+ merged = merged .dt .tz_localize (None , ambiguous = "infer" )
480+ ctdf ["week" ] = merged .dt .to_period ("W" ).dt .start_time
481+ weekly_cycle = ctdf .groupby ("week" )["cycle_hours" ].mean ()
482+ if not weekly_cycle .empty :
483+ labels = [d .strftime ("%Y-%m-%d" ) for d in weekly_cycle .index ]
484+ overall_avg = round (float (weekly_cycle .mean ()), 1 )
485+ charts .append ({
486+ "id" : f"19-{ team } " ,
487+ "type" : "line" ,
488+ "title" : f"Average Merge Cycle Time — { team } " ,
489+ "subtitle" : "created_at → merged_at in hours (by week)" ,
490+ "_subtab" : team ,
491+ "overall_avg" : overall_avg ,
492+ "x" : labels ,
493+ "y" : [round (float (v ), 1 ) for v in weekly_cycle .tolist ()],
494+ })
495+
468496 # 06: Scatter
469497 agg = tdf .groupby ("developer" ).agg (pr_count = ("pr_url" , "count" ), total_complexity = ("complexity" , "sum" ))
470498 if len (agg ) >= 2 :
0 commit comments