Skip to content

Commit 5bc643e

Browse files
authored
Merge pull request #32 from VectorInstitute/add_bootcamp_packages
Add packages for bootcamps too
2 parents d10fdeb + c13df32 commit 5bc643e

File tree

5 files changed

+305
-61
lines changed

5 files changed

+305
-61
lines changed

catalog/app/analytics/page.tsx

Lines changed: 94 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ interface PyPISnapshot {
7373
package_name: string;
7474
name: string;
7575
repo_id: string;
76+
type: string;
7677
timestamp: string;
7778
downloads_last_day: number | null;
7879
downloads_last_week: number | null;
@@ -85,6 +86,7 @@ interface PyPISnapshot {
8586
interface PyPIPackageHistory {
8687
name: string;
8788
repo_id: string;
89+
type: string;
8890
snapshots: PyPISnapshot[];
8991
}
9092

@@ -97,6 +99,7 @@ interface PyPIMetrics {
9799
package_name: string;
98100
name: string;
99101
repo_id: string;
102+
type: string;
100103
downloads_last_day: number;
101104
downloads_last_week: number;
102105
downloads_last_month: number;
@@ -108,6 +111,7 @@ type SortColumn = "name" | "language" | "stars" | "forks" | "unique_visitors" |
108111
type PyPISortColumn = "name" | "downloads_last_day" | "downloads_last_week" | "downloads_last_month" | "version";
109112
type SortDirection = "asc" | "desc";
110113
type ActiveTab = "github" | "pypi";
114+
type PyPIFilter = "all" | "tool" | "bootcamp";
111115

112116
export default function AnalyticsPage() {
113117
// Load data dynamically to ensure fresh data during development
@@ -120,6 +124,7 @@ export default function AnalyticsPage() {
120124
const [sortDirection, setSortDirection] = useState<SortDirection>("desc");
121125
const [pypiSortDirection, setPypiSortDirection] = useState<SortDirection>("desc");
122126
const [activeTab, setActiveTab] = useState<ActiveTab>("github");
127+
const [pypiFilter, setPypiFilter] = useState<PyPIFilter>("all");
123128

124129
useEffect(() => {
125130
const loadData = async () => {
@@ -296,6 +301,7 @@ export default function AnalyticsPage() {
296301
package_name,
297302
name: pkg.name,
298303
repo_id: pkg.repo_id,
304+
type: pkg.type || "tool", // Default to "tool" for backward compatibility
299305
downloads_last_day: latest.downloads_last_day || 0,
300306
downloads_last_week: latest.downloads_last_week || 0,
301307
downloads_last_month: latest.downloads_last_month || 0,
@@ -306,17 +312,23 @@ export default function AnalyticsPage() {
306312
.filter((p): p is PyPIMetrics => p !== null);
307313
}, [pypiData, repoDescriptions]);
308314

309-
// Calculate aggregate PyPI metrics
315+
// Filter PyPI metrics based on selected filter
316+
const filteredPypiMetrics = useMemo(() => {
317+
if (pypiFilter === "all") return allPypiMetrics;
318+
return allPypiMetrics.filter((pkg) => pkg.type === pypiFilter);
319+
}, [allPypiMetrics, pypiFilter]);
320+
321+
// Calculate aggregate PyPI metrics (using filtered data)
310322
const aggregatePypiMetrics = useMemo(() => {
311-
const totalDownloadsDay = allPypiMetrics.reduce(
323+
const totalDownloadsDay = filteredPypiMetrics.reduce(
312324
(sum, p) => sum + p.downloads_last_day,
313325
0
314326
);
315-
const totalDownloadsWeek = allPypiMetrics.reduce(
327+
const totalDownloadsWeek = filteredPypiMetrics.reduce(
316328
(sum, p) => sum + p.downloads_last_week,
317329
0
318330
);
319-
const totalDownloadsMonth = allPypiMetrics.reduce(
331+
const totalDownloadsMonth = filteredPypiMetrics.reduce(
320332
(sum, p) => sum + p.downloads_last_month,
321333
0
322334
);
@@ -325,32 +337,32 @@ export default function AnalyticsPage() {
325337
totalDownloadsDay,
326338
totalDownloadsWeek,
327339
totalDownloadsMonth,
328-
totalPackages: allPypiMetrics.length,
340+
totalPackages: filteredPypiMetrics.length,
329341
avgDownloadsPerPackage:
330-
allPypiMetrics.length > 0
331-
? Math.round(totalDownloadsMonth / allPypiMetrics.length)
342+
filteredPypiMetrics.length > 0
343+
? Math.round(totalDownloadsMonth / filteredPypiMetrics.length)
332344
: 0,
333345
};
334-
}, [allPypiMetrics]);
346+
}, [filteredPypiMetrics]);
335347

336-
// Get top PyPI performers
348+
// Get top PyPI performers (using filtered data)
337349
const topPypiPerformers = useMemo(() => {
338350
return {
339-
byDay: [...allPypiMetrics]
351+
byDay: [...filteredPypiMetrics]
340352
.sort((a, b) => b.downloads_last_day - a.downloads_last_day)
341353
.slice(0, 5),
342-
byWeek: [...allPypiMetrics]
354+
byWeek: [...filteredPypiMetrics]
343355
.sort((a, b) => b.downloads_last_week - a.downloads_last_week)
344356
.slice(0, 5),
345-
byMonth: [...allPypiMetrics]
357+
byMonth: [...filteredPypiMetrics]
346358
.sort((a, b) => b.downloads_last_month - a.downloads_last_month)
347359
.slice(0, 5),
348360
};
349-
}, [allPypiMetrics]);
361+
}, [filteredPypiMetrics]);
350362

351-
// Sort PyPI metrics
363+
// Sort PyPI metrics (using filtered data)
352364
const sortedPypiMetrics = useMemo(() => {
353-
const sorted = [...allPypiMetrics].sort((a, b) => {
365+
const sorted = [...filteredPypiMetrics].sort((a, b) => {
354366
let aValue: string | number | null = a[pypiSortColumn];
355367
let bValue: string | number | null = b[pypiSortColumn];
356368

@@ -372,7 +384,7 @@ export default function AnalyticsPage() {
372384
});
373385

374386
return sorted;
375-
}, [allPypiMetrics, pypiSortColumn, pypiSortDirection]);
387+
}, [filteredPypiMetrics, pypiSortColumn, pypiSortDirection]);
376388

377389
// Handle PyPI column header click
378390
const handlePypiSort = (column: PyPISortColumn) => {
@@ -771,10 +783,62 @@ export default function AnalyticsPage() {
771783
Downloads include CI/CD pipelines, automated builds, dependency installations, and development environment setups.
772784
A single user or organization typically generates many downloads through automation and tooling.
773785
</p>
786+
<p className="text-blue-800 dark:text-blue-200 mt-2">
787+
<span className="font-medium">Tool packages</span> are production-ready libraries for use in projects.
788+
<span className="font-medium ml-2">Bootcamp packages</span> are educational utilities for tutorials and demos.
789+
</p>
774790
</div>
775791
</div>
776792
</div>
777793

794+
{/* Filter Buttons */}
795+
<div className="mb-8 flex items-center gap-3">
796+
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
797+
Filter by type:
798+
</span>
799+
<div className="flex gap-2">
800+
<button
801+
onClick={() => setPypiFilter("all")}
802+
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
803+
pypiFilter === "all"
804+
? "bg-vector-magenta text-white shadow-md"
805+
: "bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700"
806+
}`}
807+
>
808+
All Packages
809+
<span className="ml-2 text-xs opacity-75">
810+
({allPypiMetrics.length})
811+
</span>
812+
</button>
813+
<button
814+
onClick={() => setPypiFilter("tool")}
815+
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
816+
pypiFilter === "tool"
817+
? "bg-vector-magenta text-white shadow-md"
818+
: "bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700"
819+
}`}
820+
>
821+
Tool
822+
<span className="ml-2 text-xs opacity-75">
823+
({allPypiMetrics.filter((p) => p.type === "tool").length})
824+
</span>
825+
</button>
826+
<button
827+
onClick={() => setPypiFilter("bootcamp")}
828+
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
829+
pypiFilter === "bootcamp"
830+
? "bg-vector-magenta text-white shadow-md"
831+
: "bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700"
832+
}`}
833+
>
834+
Bootcamp
835+
<span className="ml-2 text-xs opacity-75">
836+
({allPypiMetrics.filter((p) => p.type === "bootcamp").length})
837+
</span>
838+
</button>
839+
</div>
840+
</div>
841+
778842
{/* Key Metrics */}
779843
<section className="mb-12">
780844
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-2">
@@ -909,15 +973,20 @@ export default function AnalyticsPage() {
909973
className="px-6 py-4 whitespace-nowrap relative"
910974
title={pkg.description}
911975
>
912-
<a
913-
href={`https://pypi.org/project/${pkg.package_name}/`}
914-
target="_blank"
915-
rel="noopener noreferrer"
916-
className="flex items-center gap-2 text-sm font-medium text-vector-magenta hover:text-vector-cobalt dark:text-vector-magenta dark:hover:text-vector-cobalt"
917-
>
918-
{pkg.package_name}
919-
<ExternalLink className="w-3 h-3" />
920-
</a>
976+
<div>
977+
<a
978+
href={`https://pypi.org/project/${pkg.package_name}/`}
979+
target="_blank"
980+
rel="noopener noreferrer"
981+
className="flex items-center gap-2 text-sm font-medium text-vector-magenta hover:text-vector-cobalt dark:text-vector-magenta dark:hover:text-vector-cobalt"
982+
>
983+
{pkg.package_name}
984+
<ExternalLink className="w-3 h-3" />
985+
</a>
986+
<div className="mt-1 text-xs text-gray-500 dark:text-gray-400">
987+
{pkg.type === "tool" ? "Tool" : "Bootcamp"}
988+
</div>
989+
</div>
921990
{pkg.description && (
922991
<div className="hidden group-hover:block absolute left-0 top-full mt-2 z-50 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-sm rounded-lg py-3 px-4 w-96 max-w-[calc(100vw-2rem)] shadow-xl border-2 border-gray-200 dark:border-gray-600 leading-relaxed whitespace-normal break-words">
923992
{pkg.description}

catalog/public/data/pypi_metrics.json

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,62 +4,80 @@
44
"repo_id": "VectorInstitute/cyclops",
55
"name": "cyclops",
66
"package_name": "pycyclops",
7-
"timestamp": "2025-11-26T20:48:59.853569+00:00",
8-
"downloads_last_day": 3,
9-
"downloads_last_week": 505,
10-
"downloads_last_month": 1739,
11-
"total_downloads": 5077,
7+
"type": "tool",
8+
"timestamp": "2025-11-27T02:18:58.593894+00:00",
9+
"downloads_last_day": 111,
10+
"downloads_last_week": 613,
11+
"downloads_last_month": 1847,
12+
"total_downloads": 5179,
1213
"version": "0.2.12",
1314
"release_date": "2025-01-22T16:00:42"
1415
},
1516
"fed-rag": {
1617
"repo_id": "VectorInstitute/fed-rag",
1718
"name": "fed-rag",
1819
"package_name": "fed-rag",
19-
"timestamp": "2025-11-26T20:49:01.143760+00:00",
20-
"downloads_last_day": 6,
21-
"downloads_last_week": 8,
22-
"downloads_last_month": 36,
23-
"total_downloads": 1949,
20+
"type": "tool",
21+
"timestamp": "2025-11-27T02:18:59.987748+00:00",
22+
"downloads_last_day": 108,
23+
"downloads_last_week": 110,
24+
"downloads_last_month": 138,
25+
"total_downloads": 2034,
2426
"version": "0.0.27",
2527
"release_date": "2025-06-18T04:08:33"
2628
},
2729
"vec-inf": {
2830
"repo_id": "VectorInstitute/vector-inference",
2931
"name": "vector-inference",
3032
"package_name": "vec-inf",
31-
"timestamp": "2025-11-26T20:49:02.332426+00:00",
32-
"downloads_last_day": 5,
33-
"downloads_last_week": 12,
34-
"downloads_last_month": 184,
35-
"total_downloads": 890,
33+
"type": "tool",
34+
"timestamp": "2025-11-27T02:19:01.146546+00:00",
35+
"downloads_last_day": 6,
36+
"downloads_last_week": 13,
37+
"downloads_last_month": 185,
38+
"total_downloads": 889,
3639
"version": "0.7.2",
3740
"release_date": "2025-11-04T23:24:41"
3841
},
42+
"aieng-rag-utils": {
43+
"repo_id": "vectorinstitute/retrieval-augmented-generation",
44+
"name": "retrieval-augmented-generation",
45+
"package_name": "aieng-rag-utils",
46+
"type": "bootcamp",
47+
"timestamp": "2025-11-27T02:19:02.293328+00:00",
48+
"downloads_last_day": 1,
49+
"downloads_last_week": 3,
50+
"downloads_last_month": 29,
51+
"total_downloads": 419,
52+
"version": "1.0.0",
53+
"release_date": "2025-07-29T20:43:26"
54+
},
3955
"fair-sense-ai": {
4056
"repo_id": "VectorInstitute/fair-sense-ai",
4157
"name": "fair-sense-ai",
4258
"package_name": "fair-sense-ai",
43-
"timestamp": "2025-11-26T20:49:03.496054+00:00",
44-
"downloads_last_day": 1,
45-
"downloads_last_week": 10,
46-
"downloads_last_month": 57,
47-
"total_downloads": 801,
59+
"type": "tool",
60+
"timestamp": "2025-11-27T02:19:03.490408+00:00",
61+
"downloads_last_day": 3,
62+
"downloads_last_week": 12,
63+
"downloads_last_month": 59,
64+
"total_downloads": 802,
4865
"version": "1.0.11",
4966
"release_date": "2025-06-21T18:46:37"
5067
},
5168
"fl4health": {
5269
"repo_id": "VectorInstitute/FL4Health",
5370
"name": "fl4health",
5471
"package_name": "fl4health",
55-
"timestamp": "2025-11-26T20:49:04.672537+00:00",
56-
"downloads_last_day": 1,
57-
"downloads_last_week": 62,
58-
"downloads_last_month": 147,
59-
"total_downloads": 1220,
72+
"type": "tool",
73+
"timestamp": "2025-11-27T02:19:04.646673+00:00",
74+
"downloads_last_day": 3,
75+
"downloads_last_week": 64,
76+
"downloads_last_month": 149,
77+
"total_downloads": 1218,
6078
"version": "0.4.0",
6179
"release_date": "2025-07-15T20:39:30"
6280
}
6381
},
64-
"last_updated": "2025-11-26T20:49:05.866774+00:00"
82+
"last_updated": "2025-11-27T02:19:05.814963+00:00"
6583
}

0 commit comments

Comments
 (0)