Xianbao QIAN
commited on
Commit
·
e2e991a
1
Parent(s):
1f1c117
fix a few minor issues
Browse files- bun.lockb +0 -0
- package.json +1 -0
- src/pages/heatmap/index.tsx +22 -20
- src/utils/modelData.ts +16 -26
bun.lockb
CHANGED
Binary files a/bun.lockb and b/bun.lockb differ
|
|
package.json
CHANGED
@@ -13,6 +13,7 @@
|
|
13 |
"@emotion/styled": "^11.13.0",
|
14 |
"@mui/material": "^5.16.6",
|
15 |
"next": "14.2.5",
|
|
|
16 |
"react": "^18",
|
17 |
"react-activity-calendar": "^2.2.11",
|
18 |
"react-dom": "^18",
|
|
|
13 |
"@emotion/styled": "^11.13.0",
|
14 |
"@mui/material": "^5.16.6",
|
15 |
"next": "14.2.5",
|
16 |
+
"next-themes": "^0.4.3",
|
17 |
"react": "^18",
|
18 |
"react-activity-calendar": "^2.2.11",
|
19 |
"react-dom": "^18",
|
src/pages/heatmap/index.tsx
CHANGED
@@ -1,35 +1,35 @@
|
|
1 |
-
import
|
2 |
import ActivityCalendar from 'react-activity-calendar';
|
3 |
import { Tooltip } from '@mui/material';
|
4 |
-
import { PROVIDERS_MAP } from '../../utils/providers';
|
5 |
-
import { CalendarData, fetchAllModelData, generateCalendarData, aggregateCalendarData } from '../../utils/modelData';
|
6 |
import Link from 'next/link';
|
|
|
|
|
7 |
|
8 |
interface OpenSourceHeatmapProps {
|
9 |
-
calendarData: CalendarData;
|
10 |
}
|
11 |
|
12 |
export const getStaticProps = async () => {
|
13 |
try {
|
14 |
const modelData = await fetchAllModelData();
|
15 |
const calendarData = generateCalendarData(modelData);
|
16 |
-
|
17 |
-
|
18 |
return {
|
19 |
props: {
|
20 |
-
calendarData:
|
|
|
|
|
|
|
21 |
},
|
22 |
-
|
23 |
-
revalidate: 43200,
|
24 |
};
|
25 |
} catch (error) {
|
26 |
-
console.error('Error
|
27 |
return {
|
28 |
props: {
|
29 |
-
calendarData: {}
|
30 |
},
|
31 |
-
|
32 |
-
revalidate: 3600,
|
33 |
};
|
34 |
}
|
35 |
};
|
@@ -38,7 +38,7 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({ calendarData }) =
|
|
38 |
const [isLoading, setIsLoading] = useState(true);
|
39 |
|
40 |
useEffect(() => {
|
41 |
-
if (calendarData &&
|
42 |
setIsLoading(false);
|
43 |
}
|
44 |
}, [calendarData]);
|
@@ -69,11 +69,12 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({ calendarData }) =
|
|
69 |
<div className="w-full overflow-x-auto">
|
70 |
<div className="inline-block mx-auto">
|
71 |
<ActivityCalendar
|
72 |
-
data={calendarData}
|
73 |
theme={{
|
74 |
dark: ['#161b22', '#4CAF50'],
|
75 |
-
light: ['#
|
76 |
}}
|
|
|
77 |
hideTotalCount
|
78 |
renderBlock={(block, activity) => (
|
79 |
<Tooltip
|
@@ -93,8 +94,8 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({ calendarData }) =
|
|
93 |
{/* Individual Provider Heatmaps */}
|
94 |
{Object.entries(PROVIDERS_MAP)
|
95 |
.sort(([keyA], [keyB]) =>
|
96 |
-
calendarData[keyB].reduce((sum, day) => sum + day.count, 0) -
|
97 |
-
calendarData[keyA].reduce((sum, day) => sum + day.count, 0)
|
98 |
)
|
99 |
.map(([providerName, { color }]) => (
|
100 |
<div key={providerName} className="flex flex-col">
|
@@ -102,11 +103,12 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({ calendarData }) =
|
|
102 |
<div className="w-full overflow-x-auto">
|
103 |
<div className="inline-block mx-auto">
|
104 |
<ActivityCalendar
|
105 |
-
data={calendarData[providerName]}
|
106 |
theme={{
|
107 |
dark: ['#161b22', color],
|
108 |
-
light: ['#
|
109 |
}}
|
|
|
110 |
hideTotalCount
|
111 |
renderBlock={(block, activity) => (
|
112 |
<Tooltip
|
|
|
1 |
+
import { useState, useEffect } from 'react';
|
2 |
import ActivityCalendar from 'react-activity-calendar';
|
3 |
import { Tooltip } from '@mui/material';
|
|
|
|
|
4 |
import Link from 'next/link';
|
5 |
+
import { fetchAllModelData, generateCalendarData, aggregateCalendarData, CalendarData, Activity } from '@/utils/modelData';
|
6 |
+
import { PROVIDERS_MAP } from '@/utils/providers';
|
7 |
|
8 |
interface OpenSourceHeatmapProps {
|
9 |
+
calendarData: CalendarData & { total: Activity[] };
|
10 |
}
|
11 |
|
12 |
export const getStaticProps = async () => {
|
13 |
try {
|
14 |
const modelData = await fetchAllModelData();
|
15 |
const calendarData = generateCalendarData(modelData);
|
16 |
+
|
|
|
17 |
return {
|
18 |
props: {
|
19 |
+
calendarData: {
|
20 |
+
total: aggregateCalendarData(calendarData),
|
21 |
+
...calendarData
|
22 |
+
}
|
23 |
},
|
24 |
+
revalidate: process.env.VERCEL_ENV === 'production' ? 43200 : 3600, // 12 hours in production, 1 hour in development
|
|
|
25 |
};
|
26 |
} catch (error) {
|
27 |
+
console.error('Error fetching model data:', error);
|
28 |
return {
|
29 |
props: {
|
30 |
+
calendarData: { total: [] }
|
31 |
},
|
32 |
+
revalidate: 3600, // 1 hour on error
|
|
|
33 |
};
|
34 |
}
|
35 |
};
|
|
|
38 |
const [isLoading, setIsLoading] = useState(true);
|
39 |
|
40 |
useEffect(() => {
|
41 |
+
if (calendarData && calendarData.total && calendarData.total.length > 0) {
|
42 |
setIsLoading(false);
|
43 |
}
|
44 |
}, [calendarData]);
|
|
|
69 |
<div className="w-full overflow-x-auto">
|
70 |
<div className="inline-block mx-auto">
|
71 |
<ActivityCalendar
|
72 |
+
data={calendarData.total}
|
73 |
theme={{
|
74 |
dark: ['#161b22', '#4CAF50'],
|
75 |
+
light: ['#161b22', '#4CAF50'],
|
76 |
}}
|
77 |
+
colorScheme="dark"
|
78 |
hideTotalCount
|
79 |
renderBlock={(block, activity) => (
|
80 |
<Tooltip
|
|
|
94 |
{/* Individual Provider Heatmaps */}
|
95 |
{Object.entries(PROVIDERS_MAP)
|
96 |
.sort(([keyA], [keyB]) =>
|
97 |
+
(calendarData[keyB] || []).reduce((sum, day) => sum + day.count, 0) -
|
98 |
+
(calendarData[keyA] || []).reduce((sum, day) => sum + day.count, 0)
|
99 |
)
|
100 |
.map(([providerName, { color }]) => (
|
101 |
<div key={providerName} className="flex flex-col">
|
|
|
103 |
<div className="w-full overflow-x-auto">
|
104 |
<div className="inline-block mx-auto">
|
105 |
<ActivityCalendar
|
106 |
+
data={calendarData[providerName] || []}
|
107 |
theme={{
|
108 |
dark: ['#161b22', color],
|
109 |
+
light: ['#161b22', color],
|
110 |
}}
|
111 |
+
colorScheme="dark"
|
112 |
hideTotalCount
|
113 |
renderBlock={(block, activity) => (
|
114 |
<Tooltip
|
src/utils/modelData.ts
CHANGED
@@ -1,10 +1,13 @@
|
|
1 |
import { PROVIDERS_MAP } from './providers';
|
2 |
|
3 |
export interface ModelData {
|
4 |
-
createdAt: string;
|
5 |
id: string;
|
6 |
-
|
|
|
|
|
|
|
7 |
isDataset?: boolean;
|
|
|
8 |
}
|
9 |
|
10 |
export interface Activity {
|
@@ -25,8 +28,10 @@ export interface MonthlyActivity {
|
|
25 |
name?: string;
|
26 |
}
|
27 |
|
28 |
-
export interface DetailedModelData
|
|
|
29 |
name: string;
|
|
|
30 |
likes: number;
|
31 |
downloads: number;
|
32 |
monthKey: string; // YYYY-MM
|
@@ -136,7 +141,7 @@ export const generateMonthlyData = (modelData: ModelData[]): MonthlyActivity[] =
|
|
136 |
date: monthKey,
|
137 |
count: 0,
|
138 |
provider,
|
139 |
-
isDataset: model.isDataset,
|
140 |
name: model.name
|
141 |
};
|
142 |
}
|
@@ -243,38 +248,23 @@ export const fetchAllModelData = async (): Promise<ModelData[]> => {
|
|
243 |
};
|
244 |
|
245 |
export function processDetailedModelData(models: ModelData[]): DetailedModelData[] {
|
246 |
-
if (!models || models.length === 0) {
|
247 |
-
return [];
|
248 |
-
}
|
249 |
-
|
250 |
return models.map(model => {
|
251 |
const date = new Date(model.createdAt);
|
252 |
-
const monthKey = date.
|
253 |
-
|
254 |
-
|
255 |
-
});
|
256 |
-
const sortKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
|
257 |
-
const [org] = model.id.split('/');
|
258 |
-
const provider = Object.entries(PROVIDERS_MAP).find(([_, info]) =>
|
259 |
-
info.authors.includes(org)
|
260 |
-
)?.[0] || 'unknown';
|
261 |
|
262 |
return {
|
263 |
-
|
264 |
name: model.name || model.id,
|
|
|
265 |
likes: model.likes || 0,
|
266 |
-
downloads: model.downloads || 0,
|
267 |
monthKey,
|
268 |
provider,
|
269 |
sortKey,
|
270 |
-
isDataset: model.isDataset
|
271 |
};
|
272 |
-
}).sort((a, b) => {
|
273 |
-
// First sort by sortKey (year-month) in descending order
|
274 |
-
const dateCompare = b.sortKey.localeCompare(a.sortKey);
|
275 |
-
if (dateCompare !== 0) return dateCompare;
|
276 |
-
// Then by likes for items in the same month
|
277 |
-
return (b.likes || 0) - (a.likes || 0);
|
278 |
});
|
279 |
}
|
280 |
|
|
|
1 |
import { PROVIDERS_MAP } from './providers';
|
2 |
|
3 |
export interface ModelData {
|
|
|
4 |
id: string;
|
5 |
+
name: string;
|
6 |
+
createdAt: string;
|
7 |
+
likes: number;
|
8 |
+
downloads?: number;
|
9 |
isDataset?: boolean;
|
10 |
+
provider: string;
|
11 |
}
|
12 |
|
13 |
export interface Activity {
|
|
|
28 |
name?: string;
|
29 |
}
|
30 |
|
31 |
+
export interface DetailedModelData {
|
32 |
+
id: string;
|
33 |
name: string;
|
34 |
+
createdAt: string;
|
35 |
likes: number;
|
36 |
downloads: number;
|
37 |
monthKey: string; // YYYY-MM
|
|
|
141 |
date: monthKey,
|
142 |
count: 0,
|
143 |
provider,
|
144 |
+
isDataset: model.isDataset ?? false, // Use nullish coalescing to provide a default
|
145 |
name: model.name
|
146 |
};
|
147 |
}
|
|
|
248 |
};
|
249 |
|
250 |
export function processDetailedModelData(models: ModelData[]): DetailedModelData[] {
|
|
|
|
|
|
|
|
|
251 |
return models.map(model => {
|
252 |
const date = new Date(model.createdAt);
|
253 |
+
const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
|
254 |
+
const provider = model.provider || 'unknown';
|
255 |
+
const sortKey = `${monthKey}-${model.name}`;
|
|
|
|
|
|
|
|
|
|
|
|
|
256 |
|
257 |
return {
|
258 |
+
id: model.id,
|
259 |
name: model.name || model.id,
|
260 |
+
createdAt: model.createdAt,
|
261 |
likes: model.likes || 0,
|
262 |
+
downloads: model.downloads || 0, // Set downloads to 0 if it's undefined
|
263 |
monthKey,
|
264 |
provider,
|
265 |
sortKey,
|
266 |
+
isDataset: model.isDataset ?? false
|
267 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
});
|
269 |
}
|
270 |
|