Skip to content

Commit 19610d7

Browse files
author
Mark Coatsworth
committed
First pass at implementing Fork Analysis, still needs more testing
1 parent 9cfe3be commit 19610d7

File tree

10 files changed

+1229
-23
lines changed

10 files changed

+1229
-23
lines changed

.github/workflows/collect-metrics.yml

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ jobs:
4949
git config user.name "github-actions[bot]"
5050
git config user.email "github-actions[bot]@users.noreply.github.com"
5151
52-
- name: Ensure data directory exists
53-
run: mkdir -p catalog/public/data
52+
- name: Ensure data directories exist
53+
run: |
54+
mkdir -p catalog/public/data
55+
mkdir -p catalog-analytics/public/data
5456
5557
- name: Collect GitHub metrics
5658
id: collect
@@ -64,18 +66,44 @@ jobs:
6466
env:
6567
GH_TOKEN: ${{ secrets.METRICS_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
6668

69+
- name: Collect fork metrics
70+
id: collect_forks
71+
run: |
72+
python scripts/collect_fork_metrics.py
73+
if [ $? -ne 0 ]; then
74+
echo "error=true" >> $GITHUB_OUTPUT
75+
echo "⚠️ Fork metrics collection failed, continuing anyway"
76+
else
77+
echo "error=false" >> $GITHUB_OUTPUT
78+
fi
79+
env:
80+
GH_TOKEN: ${{ secrets.METRICS_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
81+
6782
- name: Validate JSON files
6883
run: |
6984
echo "Validating JSON files..."
7085
python -c "import json; json.load(open('catalog/public/data/github_metrics.json'))"
7186
python -c "import json; json.load(open('catalog/public/data/github_metrics_history.json'))"
87+
88+
# Validate fork metrics if it exists
89+
if [ -f catalog-analytics/public/data/fork_metrics.json ]; then
90+
python -c "import json; json.load(open('catalog-analytics/public/data/fork_metrics.json'))"
91+
echo "✓ Fork metrics JSON is valid"
92+
fi
93+
7294
echo "✓ JSON files are valid"
7395
7496
- name: Check for changes
7597
id: check_changes
7698
run: |
7799
git add catalog/public/data/github_metrics.json
78100
git add catalog/public/data/github_metrics_history.json
101+
102+
# Add fork metrics if it exists
103+
if [ -f catalog-analytics/public/data/fork_metrics.json ]; then
104+
git add catalog-analytics/public/data/fork_metrics.json
105+
fi
106+
79107
if git diff --cached --quiet; then
80108
echo "changed=false" >> $GITHUB_OUTPUT
81109
echo "No changes to commit"
@@ -93,6 +121,7 @@ jobs:
93121
94122
- Updated current metrics snapshot
95123
- Added to historical metrics database
124+
- Updated fork analysis and geographic distribution
96125
97126
Generated by: ${{ github.workflow }}
98127
Run ID: ${{ github.run_id }}"
@@ -137,10 +166,24 @@ jobs:
137166
echo "**Last Updated:** \`$LAST_UPDATED\`" >> $GITHUB_STEP_SUMMARY
138167
fi
139168
169+
# Add fork metrics summary
170+
if [ -f catalog-analytics/public/data/fork_metrics.json ]; then
171+
echo "" >> $GITHUB_STEP_SUMMARY
172+
echo "### Fork Analysis" >> $GITHUB_STEP_SUMMARY
173+
ACTIVE_FORKS=$(python -c "import json; data=json.load(open('catalog-analytics/public/data/fork_metrics.json')); print(data['summary']['active_forks'])")
174+
MEANINGFUL_FORKS=$(python -c "import json; data=json.load(open('catalog-analytics/public/data/fork_metrics.json')); print(data['summary']['meaningful_forks'])")
175+
COUNTRIES=$(python -c "import json; data=json.load(open('catalog-analytics/public/data/fork_metrics.json')); print(len(data['geographic_distribution']))")
176+
echo "**Active Forks:** $ACTIVE_FORKS" >> $GITHUB_STEP_SUMMARY
177+
echo "**Meaningful Forks:** $MEANINGFUL_FORKS" >> $GITHUB_STEP_SUMMARY
178+
echo "**Countries Represented:** $COUNTRIES" >> $GITHUB_STEP_SUMMARY
179+
fi
180+
140181
if [ "${{ steps.check_changes.outputs.changed }}" == "true" ]; then
182+
echo "" >> $GITHUB_STEP_SUMMARY
141183
echo "**Status:** ✅ Metrics updated and committed" >> $GITHUB_STEP_SUMMARY
142184
echo "" >> $GITHUB_STEP_SUMMARY
143185
echo "The deployment workflow will automatically trigger to publish the updated analytics." >> $GITHUB_STEP_SUMMARY
144186
else
187+
echo "" >> $GITHUB_STEP_SUMMARY
145188
echo "**Status:** ℹ️ No changes detected" >> $GITHUB_STEP_SUMMARY
146189
fi

catalog-analytics/app/analytics-content.tsx

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import {
2222
import Image from "next/image";
2323
import { getAssetPath } from "@/lib/utils";
2424
import type { User } from '@vector-institute/aieng-auth-core';
25+
import MeaningfulnessChart from '@/components/MeaningfulnessChart';
26+
import CodeConfigChart from '@/components/CodeConfigChart';
27+
import GeographicChart from '@/components/GeographicChart';
2528

2629
// Types
2730
interface RepoSnapshot {
@@ -108,6 +111,28 @@ interface PyPIMetrics {
108111
description?: string;
109112
}
110113

114+
interface GeographicData {
115+
country: string;
116+
count: number;
117+
}
118+
119+
interface ForkSummary {
120+
total_forks: number;
121+
active_forks: number;
122+
meaningful_forks: number;
123+
not_meaningful_forks: number;
124+
meaningful_rate: number;
125+
total_files_changed: number;
126+
code_files: number;
127+
config_files: number;
128+
}
129+
130+
interface ForkAnalysis {
131+
summary: ForkSummary;
132+
geographic_distribution: GeographicData[];
133+
last_updated: string;
134+
}
135+
111136
type SortColumn = "name" | "language" | "stars" | "forks" | "unique_visitors" | "unique_cloners";
112137
type PyPISortColumn = "name" | "downloads_last_day" | "downloads_last_week" | "downloads_last_month" | "version";
113138
type SortDirection = "asc" | "desc";
@@ -122,6 +147,7 @@ export default function AnalyticsPage({ user }: AnalyticsPageProps) {
122147
// Load data dynamically to ensure fresh data during development
123148
const [historicalData, setHistoricalData] = useState<HistoricalData | null>(null);
124149
const [pypiData, setPypiData] = useState<PyPIHistoricalData | null>(null);
150+
const [forkData, setForkData] = useState<ForkAnalysis | null>(null);
125151
const [isLoading, setIsLoading] = useState(true);
126152
const [repoDescriptions, setRepoDescriptions] = useState<Record<string, string>>({});
127153
const [sortColumn, setSortColumn] = useState<SortColumn>("unique_cloners");
@@ -182,6 +208,17 @@ export default function AnalyticsPage({ user }: AnalyticsPageProps) {
182208
} catch (error) {
183209
console.warn("No repository descriptions found:", error);
184210
}
211+
212+
// Load fork analysis data
213+
try {
214+
const forkResponse = await fetch(`${basePath}/data/fork_metrics.json`);
215+
if (forkResponse.ok) {
216+
const forkMetricsData = await forkResponse.json();
217+
setForkData(forkMetricsData);
218+
}
219+
} catch (error) {
220+
console.warn("No fork metrics data found:", error);
221+
}
185222
} catch (error) {
186223
console.warn("No historical metrics data found:", error);
187224
setHistoricalData(null);
@@ -648,6 +685,195 @@ export default function AnalyticsPage({ user }: AnalyticsPageProps) {
648685
</div>
649686
</section>
650687

688+
{/* Active Fork Analysis */}
689+
<section className="mb-12">
690+
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-2">
691+
<GitFork className="w-6 h-6 text-vector-magenta" />
692+
Active Fork Analysis
693+
</h2>
694+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
695+
{/* Column 1 - Meaningfulness Distribution */}
696+
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 shadow-sm">
697+
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
698+
Meaningfulness Distribution
699+
</h3>
700+
<div className="mt-4">
701+
<MeaningfulnessChart
702+
meaningful={forkData?.summary.meaningful_forks || 16}
703+
notMeaningful={forkData?.summary.not_meaningful_forks || 22}
704+
/>
705+
</div>
706+
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
707+
<div className="grid grid-cols-2 gap-4 text-center">
708+
<div>
709+
<div className="text-2xl font-bold text-green-600 dark:text-green-400">
710+
{forkData?.summary.meaningful_forks || 16}
711+
</div>
712+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1">Meaningful</div>
713+
<div className="text-xs text-gray-500 dark:text-gray-500">
714+
({forkData?.summary.meaningful_rate || 42.1}%)
715+
</div>
716+
</div>
717+
<div>
718+
<div className="text-2xl font-bold text-red-600 dark:text-red-400">
719+
{forkData?.summary.not_meaningful_forks || 22}
720+
</div>
721+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1">Not Meaningful</div>
722+
<div className="text-xs text-gray-500 dark:text-gray-500">
723+
({forkData ? (100 - forkData.summary.meaningful_rate).toFixed(1) : 57.9}%)
724+
</div>
725+
</div>
726+
</div>
727+
<div className="mt-4 text-center">
728+
<div className="text-sm font-medium text-gray-700 dark:text-gray-300">
729+
Active Forks Analyzed: <span className="font-bold text-gray-900 dark:text-white">
730+
{forkData?.summary.active_forks || 38}
731+
</span>
732+
</div>
733+
</div>
734+
</div>
735+
</div>
736+
737+
{/* Column 2 - Code vs Configuration & Geographic Distribution */}
738+
<div className="space-y-6">
739+
{/* Code vs Configuration */}
740+
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 shadow-sm">
741+
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
742+
Code vs Configuration
743+
</h3>
744+
<div className="mt-4">
745+
<CodeConfigChart
746+
codeFiles={forkData?.summary.code_files || 182}
747+
configFiles={forkData?.summary.config_files || 70}
748+
/>
749+
</div>
750+
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
751+
<div className="grid grid-cols-2 gap-4 text-center">
752+
<div>
753+
<div className="text-2xl font-bold text-indigo-600 dark:text-indigo-400">
754+
{forkData?.summary.code_files || 182}
755+
</div>
756+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1">Code Files</div>
757+
<div className="text-xs text-gray-500 dark:text-gray-500">
758+
({forkData ? ((forkData.summary.code_files / (forkData.summary.code_files + forkData.summary.config_files)) * 100).toFixed(1) : 72.2}%)
759+
</div>
760+
</div>
761+
<div>
762+
<div className="text-2xl font-bold text-amber-600 dark:text-amber-400">
763+
{forkData?.summary.config_files || 70}
764+
</div>
765+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1">Config Files</div>
766+
<div className="text-xs text-gray-500 dark:text-gray-500">
767+
({forkData ? ((forkData.summary.config_files / (forkData.summary.code_files + forkData.summary.config_files)) * 100).toFixed(1) : 27.8}%)
768+
</div>
769+
</div>
770+
</div>
771+
<div className="mt-4 text-center">
772+
<div className="text-sm font-medium text-gray-700 dark:text-gray-300">
773+
Total Files Changed: <span className="font-bold text-gray-900 dark:text-white">
774+
{forkData ? (forkData.summary.code_files + forkData.summary.config_files) : 252}
775+
</span>
776+
</div>
777+
</div>
778+
</div>
779+
</div>
780+
781+
{/* Geographic Distribution */}
782+
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 shadow-sm">
783+
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
784+
Geographic Distribution
785+
</h3>
786+
<div className="mt-4">
787+
{forkData?.geographic_distribution && forkData.geographic_distribution.length > 0 ? (
788+
<GeographicChart data={forkData.geographic_distribution} />
789+
) : (
790+
<div className="text-center py-8 text-gray-500 dark:text-gray-400">
791+
No geographic data available
792+
</div>
793+
)}
794+
</div>
795+
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
796+
<div className="text-center">
797+
<div className="text-sm font-medium text-gray-700 dark:text-gray-300">
798+
Countries Represented: <span className="font-bold text-gray-900 dark:text-white">
799+
{forkData?.geographic_distribution.length || 7}
800+
</span>
801+
</div>
802+
</div>
803+
</div>
804+
</div>
805+
</div>
806+
807+
{/* Column 3 - Key Statistics */}
808+
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 shadow-sm">
809+
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
810+
Key Statistics
811+
</h3>
812+
<div className="grid grid-cols-2 gap-4">
813+
{/* Active Forks */}
814+
<div className="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-lg p-4 text-center">
815+
<div className="text-2xl font-bold text-indigo-600 dark:text-indigo-400">
816+
{forkData?.summary.active_forks || 38}
817+
</div>
818+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1 uppercase tracking-wide">Active Forks</div>
819+
</div>
820+
821+
{/* Meaningful */}
822+
<div className="bg-gradient-to-br from-green-50 to-green-100 dark:from-green-900/20 dark:to-green-800/20 rounded-lg p-4 text-center">
823+
<div className="text-2xl font-bold text-green-600 dark:text-green-400">
824+
{forkData?.summary.meaningful_forks || 16}
825+
</div>
826+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1 uppercase tracking-wide">Meaningful</div>
827+
</div>
828+
829+
{/* Not Meaningful */}
830+
<div className="bg-gradient-to-br from-red-50 to-red-100 dark:from-red-900/20 dark:to-red-800/20 rounded-lg p-4 text-center">
831+
<div className="text-2xl font-bold text-red-600 dark:text-red-400">
832+
{forkData?.summary.not_meaningful_forks || 22}
833+
</div>
834+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1 uppercase tracking-wide">Not Meaningful</div>
835+
</div>
836+
837+
{/* Meaningful Rate */}
838+
<div className="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-lg p-4 text-center">
839+
<div className="text-2xl font-bold text-indigo-600 dark:text-indigo-400">
840+
{forkData?.summary.meaningful_rate || 42.1}%
841+
</div>
842+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1 uppercase tracking-wide">Meaningful Rate</div>
843+
</div>
844+
845+
{/* New Functions */}
846+
<div className="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-lg p-4 text-center">
847+
<div className="text-2xl font-bold text-indigo-600 dark:text-indigo-400">0</div>
848+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1 uppercase tracking-wide">New Functions</div>
849+
</div>
850+
851+
{/* New Classes */}
852+
<div className="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-lg p-4 text-center">
853+
<div className="text-2xl font-bold text-indigo-600 dark:text-indigo-400">0</div>
854+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1 uppercase tracking-wide">New Classes</div>
855+
</div>
856+
857+
{/* Files Changed */}
858+
<div className="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-lg p-4 text-center">
859+
<div className="text-2xl font-bold text-indigo-600 dark:text-indigo-400">
860+
{forkData?.summary.total_files_changed || 1267}
861+
</div>
862+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1 uppercase tracking-wide">Files Changed</div>
863+
</div>
864+
865+
{/* Code Files */}
866+
<div className="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-lg p-4 text-center">
867+
<div className="text-2xl font-bold text-indigo-600 dark:text-indigo-400">
868+
{forkData?.summary.code_files || 182}
869+
</div>
870+
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1 uppercase tracking-wide">Code Files</div>
871+
</div>
872+
</div>
873+
</div>
874+
</div>
875+
</section>
876+
651877
{/* All Repositories Table */}
652878
<section>
653879
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6">

0 commit comments

Comments
 (0)