analyze-recent-weeks.sh 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #!/bin/bash
  2. # GitHub Issues Analyzer for Recent Weeks
  3. # Analyzes Dec 15-21 (Week 50) and Dec 22-26 (Week 51)
  4. # Usage: ./scripts/analyze-recent-weeks.sh
  5. set -euo pipefail
  6. REPO="sst/opencode"
  7. GITHUB_API="https://api.github.com/repos"
  8. # Start from Dec 15
  9. START_DATE="2025-12-15T00:00:00Z"
  10. echo "Analyzing GitHub issues from Dec 15 onwards..."
  11. echo "Start date: $START_DATE"
  12. echo ""
  13. # Create temp file
  14. TEMP_FILE=$(mktemp)
  15. trap "rm -f $TEMP_FILE" EXIT
  16. echo "[]" > "$TEMP_FILE"
  17. # Fetch all issues from Dec 15 onwards (paginate through results)
  18. for page in {1..5}; do
  19. echo " Fetching page $page..."
  20. PAGE_DATA=$(curl -s "${GITHUB_API}/${REPO}/issues?state=all&sort=created&direction=desc&per_page=100&page=${page}")
  21. # Check if we got any results
  22. COUNT=$(echo "$PAGE_DATA" | jq 'length')
  23. if [ "$COUNT" -eq 0 ]; then
  24. echo " No more results on page $page"
  25. break
  26. fi
  27. # Filter issues from Dec 15 onwards
  28. FILTERED=$(echo "$PAGE_DATA" | jq "[.[] | select(.created_at >= \"${START_DATE}\")]")
  29. FILTERED_COUNT=$(echo "$FILTERED" | jq 'length')
  30. echo " Found $FILTERED_COUNT issues from Dec 15 onwards on page $page"
  31. # Append to temp file
  32. CURRENT=$(cat "$TEMP_FILE")
  33. MERGED=$(echo "$CURRENT" "$FILTERED" | jq -s '.[0] + .[1]')
  34. echo "$MERGED" > "$TEMP_FILE"
  35. # If we've started getting old data, we can stop
  36. OLDEST=$(echo "$PAGE_DATA" | jq -r '.[-1].created_at')
  37. if [[ "$OLDEST" < "$START_DATE" ]]; then
  38. echo " Reached data older than Dec 15, stopping"
  39. break
  40. fi
  41. done
  42. echo ""
  43. # Create Python analysis script
  44. PYTHON_SCRIPT=$(mktemp)
  45. trap "rm -f $PYTHON_SCRIPT $TEMP_FILE" EXIT
  46. cat > "$PYTHON_SCRIPT" << 'EOF'
  47. import json
  48. import sys
  49. from datetime import datetime
  50. from collections import defaultdict
  51. # Read the issues data from file
  52. with open(sys.argv[1], 'r') as f:
  53. data = json.load(f)
  54. if not data:
  55. print("No issues found from Dec 15 onwards")
  56. sys.exit(0)
  57. print(f"Analyzing {len(data)} issues...\n")
  58. # Categorize and group by week
  59. issues_by_week = defaultdict(lambda: defaultdict(int))
  60. week_totals = defaultdict(int)
  61. week_order = []
  62. # Response tracking
  63. response_by_week = defaultdict(lambda: {
  64. 'total': 0,
  65. 'with_response': 0,
  66. 'no_response': 0
  67. })
  68. def get_week_label(date_str):
  69. """Convert date to week label"""
  70. date = datetime.fromisoformat(date_str.replace('Z', '+00:00')).replace(tzinfo=None)
  71. # Manual week grouping for clarity
  72. if date >= datetime(2025, 12, 22):
  73. return "Week 51: Dec 22-26"
  74. elif date >= datetime(2025, 12, 15):
  75. return "Week 50: Dec 15-21"
  76. else:
  77. return "Earlier"
  78. def categorize_issue(item):
  79. """Categorize an issue"""
  80. if item.get('pull_request'):
  81. return "PR"
  82. labels = [label['name'] for label in item.get('labels', [])]
  83. title = item['title'].lower()
  84. if 'discussion' in labels:
  85. return "Feature Request"
  86. elif 'help-wanted' in labels:
  87. return "Help Question"
  88. elif 'bug' in labels:
  89. return "Bug Report"
  90. elif any(x in title for x in ['[feature]', 'feature request', '[feat]']):
  91. return "Feature Request"
  92. elif title.endswith('?') and 'bug' not in title:
  93. return "Help Question"
  94. else:
  95. return "Other"
  96. # Process each issue
  97. for item in data:
  98. week_label = get_week_label(item['created_at'])
  99. if week_label not in week_order:
  100. week_order.append(week_label)
  101. category = categorize_issue(item)
  102. # Check if it's an actual issue (not PR)
  103. if not item.get('pull_request'):
  104. response_by_week[week_label]['total'] += 1
  105. if item['comments'] > 0:
  106. response_by_week[week_label]['with_response'] += 1
  107. else:
  108. response_by_week[week_label]['no_response'] += 1
  109. issues_by_week[week_label][category] += 1
  110. week_totals[week_label] += 1
  111. # Sort weeks (most recent first)
  112. week_order = sorted([w for w in week_order if w != "Earlier"], reverse=True)
  113. # Print results
  114. print("="*80)
  115. print("GITHUB ISSUES BREAKDOWN - RECENT WEEKS")
  116. print("="*80 + "\n")
  117. for week in week_order:
  118. print(f"{week}: {week_totals[week]} total")
  119. for category in sorted(issues_by_week[week].keys()):
  120. count = issues_by_week[week][category]
  121. print(f" • {category}: {count}")
  122. print()
  123. print("---")
  124. total = sum(week_totals[w] for w in week_order)
  125. print(f"TOTAL: {total} issues/PRs\n")
  126. print("OVERALL SUMMARY:")
  127. all_counts = defaultdict(int)
  128. for week in week_order:
  129. for category, count in issues_by_week[week].items():
  130. all_counts[category] += count
  131. for category in sorted(all_counts.keys(), key=lambda x: -all_counts[x]):
  132. count = all_counts[category]
  133. pct = (count / total) * 100
  134. print(f" • {category}: {count} ({pct:.1f}%)")
  135. # Response rates
  136. print("\n" + "="*80)
  137. print("ISSUE RESPONSE RATES")
  138. print("="*80 + "\n")
  139. for week in week_order:
  140. data = response_by_week[week]
  141. if data['total'] > 0:
  142. rate = (data['with_response'] / data['total'] * 100)
  143. print(f"{week}:")
  144. print(f" Total issues: {data['total']}")
  145. print(f" With response: {data['with_response']} ({rate:.1f}%)")
  146. print(f" No response: {data['no_response']}")
  147. print()
  148. # Week over week comparison
  149. print("="*80)
  150. print("WEEK-OVER-WEEK COMPARISON")
  151. print("="*80 + "\n")
  152. if len(week_order) >= 2:
  153. w1 = week_order[0] # Most recent
  154. w2 = week_order[1] # Previous
  155. vol_change = week_totals[w1] - week_totals[w2]
  156. vol_pct = (vol_change / week_totals[w2] * 100) if week_totals[w2] > 0 else 0
  157. print(f"Volume Change: {week_totals[w2]} → {week_totals[w1]} ({vol_pct:+.1f}%)")
  158. print()
  159. print("Category Changes:")
  160. for category in sorted(all_counts.keys()):
  161. old_val = issues_by_week[w2].get(category, 0)
  162. new_val = issues_by_week[w1].get(category, 0)
  163. change = new_val - old_val
  164. direction = "↑" if change > 0 else "↓" if change < 0 else "→"
  165. print(f" {category:18s}: {old_val:3d} → {new_val:3d} {direction} {abs(change)}")
  166. print()
  167. if response_by_week[w1]['total'] > 0 and response_by_week[w2]['total'] > 0:
  168. r1 = (response_by_week[w1]['with_response'] / response_by_week[w1]['total'] * 100)
  169. r2 = (response_by_week[w2]['with_response'] / response_by_week[w2]['total'] * 100)
  170. print(f"Response Rate: {r2:.1f}% → {r1:.1f}% ({r1-r2:+.1f}pp)")
  171. print("\n" + "="*80 + "\n")
  172. EOF
  173. # Run the analysis
  174. python3 "$PYTHON_SCRIPT" "$TEMP_FILE"