Caleb Fahlgren
commited on
Commit
·
6aaff3e
1
Parent(s):
08cd799
create custom pie chart and add other queries
Browse files- app/page.tsx +106 -43
- components/area-chart.tsx +31 -27
- components/pie-chart.tsx +105 -0
- config/site.ts +1 -2
- lib/queries.ts +43 -0
- styles/globals.css +51 -49
app/page.tsx
CHANGED
@@ -1,12 +1,30 @@
|
|
1 |
"use client"
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
import { AreaChartStacked, ChartDataPoint } from "@/components/area-chart"
|
4 |
-
import {
|
5 |
-
import * as duckdb from '@duckdb/duckdb-wasm'
|
6 |
|
7 |
export default function IndexPage() {
|
8 |
-
const [
|
9 |
const [chartData, setChartData] = useState<ChartDataPoint[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
useEffect(() => {
|
12 |
const initDB = async () => {
|
@@ -15,17 +33,19 @@ export default function IndexPage() {
|
|
15 |
const JSDELIVR_BUNDLES = {
|
16 |
mvp: {
|
17 |
mainModule: `${CDN_BASE}/dist/duckdb-mvp.wasm`,
|
18 |
-
mainWorker: `${CDN_BASE}/dist/duckdb-browser-mvp.worker.js
|
19 |
},
|
20 |
eh: {
|
21 |
mainModule: `${CDN_BASE}/dist/duckdb-eh.wasm`,
|
22 |
-
mainWorker: `${CDN_BASE}/dist/duckdb-browser-eh.worker.js
|
23 |
-
}
|
24 |
}
|
25 |
|
26 |
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES)
|
27 |
const worker_url = URL.createObjectURL(
|
28 |
-
new Blob([`importScripts("${bundle.mainWorker}");`], {
|
|
|
|
|
29 |
)
|
30 |
|
31 |
const worker = new Worker(worker_url)
|
@@ -33,55 +53,67 @@ export default function IndexPage() {
|
|
33 |
const db = new duckdb.AsyncDuckDB(logger, worker)
|
34 |
await db.instantiate(bundle.mainModule)
|
35 |
|
36 |
-
const
|
37 |
|
38 |
-
|
39 |
-
await conn.query(`
|
40 |
-
CREATE VIEW models AS SELECT * FROM read_parquet('https://huggingface.co/datasets/cfahlgren1/hub-stats/resolve/refs%2Fconvert%2Fparquet/models/train/0000.parquet?download=true');
|
41 |
-
CREATE VIEW datasets AS SELECT * FROM read_parquet('https://huggingface.co/datasets/cfahlgren1/hub-stats/resolve/refs%2Fconvert%2Fparquet/datasets/train/0000.parquet?download=true');
|
42 |
-
CREATE VIEW spaces AS SELECT * FROM read_parquet('https://huggingface.co/datasets/cfahlgren1/hub-stats/resolve/refs%2Fconvert%2Fparquet/spaces/train/0000.parquet?download=true');
|
43 |
-
`)
|
44 |
|
45 |
-
|
46 |
-
await fetchChartData(
|
47 |
}
|
48 |
|
49 |
initDB()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
}, [])
|
51 |
|
52 |
-
const fetchChartData = async (
|
53 |
-
|
|
|
54 |
|
55 |
-
const
|
56 |
-
WITH all_data AS (
|
57 |
-
SELECT DATE_TRUNC('month', CAST(createdAt AS DATE)) AS month, 'model' AS type FROM models
|
58 |
-
UNION ALL
|
59 |
-
SELECT DATE_TRUNC('month', CAST(createdAt AS DATE)) AS month, 'dataset' AS type FROM datasets
|
60 |
-
UNION ALL
|
61 |
-
SELECT DATE_TRUNC('month', CAST(createdAt AS DATE)) AS month, 'space' AS type FROM spaces
|
62 |
-
)
|
63 |
-
SELECT
|
64 |
-
month,
|
65 |
-
COUNT(*) FILTER (WHERE type = 'model') AS models,
|
66 |
-
COUNT(*) FILTER (WHERE type = 'dataset') AS datasets,
|
67 |
-
COUNT(*) FILTER (WHERE type = 'space') AS spaces
|
68 |
-
FROM all_data
|
69 |
-
GROUP BY month
|
70 |
-
ORDER BY month
|
71 |
-
`)
|
72 |
-
|
73 |
-
const data: ChartDataPoint[] = result.toArray().map(row => ({
|
74 |
month: new Date(row.month),
|
75 |
models: Number(row.models),
|
76 |
datasets: Number(row.datasets),
|
77 |
-
spaces: Number(row.spaces)
|
78 |
}))
|
79 |
|
80 |
-
console.log(data)
|
81 |
-
|
82 |
-
await conn.close()
|
83 |
-
|
84 |
setChartData(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
}
|
86 |
|
87 |
return (
|
@@ -92,8 +124,39 @@ export default function IndexPage() {
|
|
92 |
</h1>
|
93 |
</div>
|
94 |
<div className="flex flex-col gap-4 max-w-6xl mt-10 w-full mx-auto">
|
95 |
-
{chartData.length > 0 ?
|
|
|
|
|
|
|
|
|
96 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
</section>
|
98 |
)
|
99 |
}
|
|
|
1 |
"use client"
|
2 |
|
3 |
+
import { useEffect, useState } from "react"
|
4 |
+
import * as duckdb from "@duckdb/duckdb-wasm"
|
5 |
+
|
6 |
+
import {
|
7 |
+
CREATE_VIEWS_QUERY,
|
8 |
+
FETCH_CHART_DATA_QUERY,
|
9 |
+
FETCH_DATASET_LICENSE_DATA_QUERY,
|
10 |
+
FETCH_MODEL_LICENSE_DATA_QUERY,
|
11 |
+
FETCH_SPACE_SDK_DATA_QUERY,
|
12 |
+
} from "@/lib/queries"
|
13 |
import { AreaChartStacked, ChartDataPoint } from "@/components/area-chart"
|
14 |
+
import { CustomPieChart } from "@/components/pie-chart"
|
|
|
15 |
|
16 |
export default function IndexPage() {
|
17 |
+
const [conn, setConn] = useState<duckdb.AsyncDuckDBConnection | null>(null)
|
18 |
const [chartData, setChartData] = useState<ChartDataPoint[]>([])
|
19 |
+
const [modelLicenseData, setModelLicenseData] = useState<
|
20 |
+
Array<{ name: string; value: number; fill: string }>
|
21 |
+
>([])
|
22 |
+
const [datasetLicenseData, setDatasetLicenseData] = useState<
|
23 |
+
Array<{ name: string; value: number; fill: string }>
|
24 |
+
>([])
|
25 |
+
const [spaceSdkData, setSpaceSdkData] = useState<
|
26 |
+
Array<{ name: string; value: number; fill: string }>
|
27 |
+
>([])
|
28 |
|
29 |
useEffect(() => {
|
30 |
const initDB = async () => {
|
|
|
33 |
const JSDELIVR_BUNDLES = {
|
34 |
mvp: {
|
35 |
mainModule: `${CDN_BASE}/dist/duckdb-mvp.wasm`,
|
36 |
+
mainWorker: `${CDN_BASE}/dist/duckdb-browser-mvp.worker.js`,
|
37 |
},
|
38 |
eh: {
|
39 |
mainModule: `${CDN_BASE}/dist/duckdb-eh.wasm`,
|
40 |
+
mainWorker: `${CDN_BASE}/dist/duckdb-browser-eh.worker.js`,
|
41 |
+
},
|
42 |
}
|
43 |
|
44 |
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES)
|
45 |
const worker_url = URL.createObjectURL(
|
46 |
+
new Blob([`importScripts("${bundle.mainWorker}");`], {
|
47 |
+
type: "text/javascript",
|
48 |
+
})
|
49 |
)
|
50 |
|
51 |
const worker = new Worker(worker_url)
|
|
|
53 |
const db = new duckdb.AsyncDuckDB(logger, worker)
|
54 |
await db.instantiate(bundle.mainModule)
|
55 |
|
56 |
+
const connection = await db.connect()
|
57 |
|
58 |
+
await connection.query(CREATE_VIEWS_QUERY)
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
+
setConn(connection)
|
61 |
+
await fetchChartData(connection)
|
62 |
}
|
63 |
|
64 |
initDB()
|
65 |
+
|
66 |
+
// Clean up the connection when the component unmounts
|
67 |
+
return () => {
|
68 |
+
if (conn) {
|
69 |
+
conn.close()
|
70 |
+
}
|
71 |
+
}
|
72 |
}, [])
|
73 |
|
74 |
+
const fetchChartData = async (connection: duckdb.AsyncDuckDBConnection) => {
|
75 |
+
// Use the imported query
|
76 |
+
const result = await connection.query(FETCH_CHART_DATA_QUERY)
|
77 |
|
78 |
+
const data: ChartDataPoint[] = result.toArray().map((row) => ({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
month: new Date(row.month),
|
80 |
models: Number(row.models),
|
81 |
datasets: Number(row.datasets),
|
82 |
+
spaces: Number(row.spaces),
|
83 |
}))
|
84 |
|
|
|
|
|
|
|
|
|
85 |
setChartData(data)
|
86 |
+
|
87 |
+
const [modelLicenseResult, datasetLicenseResult, spaceSdkResult] =
|
88 |
+
await Promise.all([
|
89 |
+
connection.query(FETCH_MODEL_LICENSE_DATA_QUERY),
|
90 |
+
connection.query(FETCH_DATASET_LICENSE_DATA_QUERY),
|
91 |
+
connection.query(FETCH_SPACE_SDK_DATA_QUERY),
|
92 |
+
])
|
93 |
+
|
94 |
+
setModelLicenseData(
|
95 |
+
modelLicenseResult.toArray().map((row, index) => ({
|
96 |
+
name: row.tag.replace("license:", ""),
|
97 |
+
value: Number(row.count),
|
98 |
+
fill: `hsl(${index * 30}, 70%, 50%)`,
|
99 |
+
}))
|
100 |
+
)
|
101 |
+
|
102 |
+
setDatasetLicenseData(
|
103 |
+
datasetLicenseResult.toArray().map((row, index) => ({
|
104 |
+
name: row.tag.replace("license:", ""),
|
105 |
+
value: Number(row.count),
|
106 |
+
fill: `hsl(${index * 30}, 70%, 50%)`,
|
107 |
+
}))
|
108 |
+
)
|
109 |
+
|
110 |
+
setSpaceSdkData(
|
111 |
+
spaceSdkResult.toArray().map((row, index) => ({
|
112 |
+
name: row.sdk,
|
113 |
+
value: Number(row.count),
|
114 |
+
fill: `hsl(${index * 30}, 70%, 50%)`,
|
115 |
+
}))
|
116 |
+
)
|
117 |
}
|
118 |
|
119 |
return (
|
|
|
124 |
</h1>
|
125 |
</div>
|
126 |
<div className="flex flex-col gap-4 max-w-6xl mt-10 w-full mx-auto">
|
127 |
+
{chartData.length > 0 ? (
|
128 |
+
<AreaChartStacked data={chartData} />
|
129 |
+
) : (
|
130 |
+
<p>Loading...</p>
|
131 |
+
)}
|
132 |
</div>
|
133 |
+
{modelLicenseData.length > 0 &&
|
134 |
+
datasetLicenseData.length > 0 &&
|
135 |
+
spaceSdkData.length > 0 && (
|
136 |
+
<div className="flex flex-wrap gap-8 max-w-6xl mt-10 w-full mx-auto">
|
137 |
+
<div className="flex-1 min-w-[300px]">
|
138 |
+
<CustomPieChart
|
139 |
+
title="Model Licenses"
|
140 |
+
data={modelLicenseData}
|
141 |
+
dataKey="value"
|
142 |
+
/>
|
143 |
+
</div>
|
144 |
+
<div className="flex-1 min-w-[300px]">
|
145 |
+
<CustomPieChart
|
146 |
+
title="Dataset Licenses"
|
147 |
+
data={datasetLicenseData}
|
148 |
+
dataKey="value"
|
149 |
+
/>
|
150 |
+
</div>
|
151 |
+
<div className="flex-1 min-w-[300px]">
|
152 |
+
<CustomPieChart
|
153 |
+
title="Space SDKs"
|
154 |
+
data={spaceSdkData}
|
155 |
+
dataKey="value"
|
156 |
+
/>
|
157 |
+
</div>
|
158 |
+
</div>
|
159 |
+
)}
|
160 |
</section>
|
161 |
)
|
162 |
}
|
components/area-chart.tsx
CHANGED
@@ -19,39 +19,43 @@ import {
|
|
19 |
} from "@/components/ui/chart"
|
20 |
|
21 |
export interface ChartDataPoint {
|
22 |
-
month: Date
|
23 |
-
models: number
|
24 |
-
datasets: number
|
25 |
-
spaces: number
|
26 |
}
|
27 |
|
28 |
interface AreaChartStackedProps {
|
29 |
-
data: ChartDataPoint[]
|
30 |
}
|
31 |
|
32 |
const chartConfig = {
|
33 |
models: {
|
34 |
label: "Models",
|
35 |
-
color: "hsl(
|
36 |
},
|
37 |
datasets: {
|
38 |
label: "Datasets",
|
39 |
-
color: "hsl(
|
40 |
},
|
41 |
spaces: {
|
42 |
label: "Spaces",
|
43 |
-
color: "hsl(
|
44 |
},
|
45 |
} satisfies ChartConfig
|
46 |
|
47 |
export function AreaChartStacked({ data }: AreaChartStackedProps) {
|
48 |
-
const sortedData = [...data].sort(
|
|
|
|
|
49 |
|
50 |
return (
|
51 |
-
<Card>
|
52 |
<CardHeader>
|
53 |
-
<CardTitle
|
54 |
-
|
|
|
|
|
55 |
Monthly creation trends for models, datasets, and spaces
|
56 |
</CardDescription>
|
57 |
</CardHeader>
|
@@ -72,44 +76,44 @@ export function AreaChartStacked({ data }: AreaChartStackedProps) {
|
|
72 |
axisLine={false}
|
73 |
tickMargin={8}
|
74 |
tickFormatter={(value) => {
|
75 |
-
const date = new Date(value)
|
76 |
-
return date.toLocaleString(
|
|
|
|
|
|
|
77 |
}}
|
78 |
/>
|
79 |
<ChartTooltip
|
80 |
cursor={true}
|
81 |
-
content={
|
82 |
-
<ChartTooltipContent
|
83 |
-
indicator="line"
|
84 |
-
hideLabel
|
85 |
-
/>
|
86 |
-
}
|
87 |
/>
|
88 |
<Area
|
89 |
dataKey="spaces"
|
90 |
type="natural"
|
91 |
-
fill="hsl(
|
92 |
fillOpacity={0.4}
|
93 |
-
stroke="hsl(
|
94 |
stackId="a"
|
95 |
/>
|
96 |
<Area
|
97 |
dataKey="datasets"
|
98 |
type="natural"
|
99 |
-
fill="hsl(
|
100 |
fillOpacity={0.2}
|
101 |
-
stroke="hsl(
|
102 |
stackId="a"
|
103 |
/>
|
104 |
<Area
|
105 |
dataKey="models"
|
106 |
type="natural"
|
107 |
-
fill="hsl(
|
108 |
fillOpacity={0.4}
|
109 |
-
stroke="hsl(
|
110 |
stackId="a"
|
111 |
/>
|
112 |
-
<ChartLegend
|
|
|
|
|
113 |
</AreaChart>
|
114 |
</ChartContainer>
|
115 |
</CardContent>
|
|
|
19 |
} from "@/components/ui/chart"
|
20 |
|
21 |
export interface ChartDataPoint {
|
22 |
+
month: Date
|
23 |
+
models: number
|
24 |
+
datasets: number
|
25 |
+
spaces: number
|
26 |
}
|
27 |
|
28 |
interface AreaChartStackedProps {
|
29 |
+
data: ChartDataPoint[]
|
30 |
}
|
31 |
|
32 |
const chartConfig = {
|
33 |
models: {
|
34 |
label: "Models",
|
35 |
+
color: "hsl(var(--chart-1))",
|
36 |
},
|
37 |
datasets: {
|
38 |
label: "Datasets",
|
39 |
+
color: "hsl(var(--chart-2))",
|
40 |
},
|
41 |
spaces: {
|
42 |
label: "Spaces",
|
43 |
+
color: "hsl(var(--chart-3))",
|
44 |
},
|
45 |
} satisfies ChartConfig
|
46 |
|
47 |
export function AreaChartStacked({ data }: AreaChartStackedProps) {
|
48 |
+
const sortedData = [...data].sort(
|
49 |
+
(a, b) => a.month.getTime() - b.month.getTime()
|
50 |
+
)
|
51 |
|
52 |
return (
|
53 |
+
<Card className="bg-[var(--card-background)]">
|
54 |
<CardHeader>
|
55 |
+
<CardTitle className="text-[var(--card-text)]">
|
56 |
+
Hugging Face Hub Growth Each Month
|
57 |
+
</CardTitle>
|
58 |
+
<CardDescription className="text-[var(--card-text)]">
|
59 |
Monthly creation trends for models, datasets, and spaces
|
60 |
</CardDescription>
|
61 |
</CardHeader>
|
|
|
76 |
axisLine={false}
|
77 |
tickMargin={8}
|
78 |
tickFormatter={(value) => {
|
79 |
+
const date = new Date(value)
|
80 |
+
return date.toLocaleString("default", {
|
81 |
+
month: "short",
|
82 |
+
year: "numeric",
|
83 |
+
})
|
84 |
}}
|
85 |
/>
|
86 |
<ChartTooltip
|
87 |
cursor={true}
|
88 |
+
content={<ChartTooltipContent indicator="line" hideLabel />}
|
|
|
|
|
|
|
|
|
|
|
89 |
/>
|
90 |
<Area
|
91 |
dataKey="spaces"
|
92 |
type="natural"
|
93 |
+
fill="hsl(var(--chart-3))"
|
94 |
fillOpacity={0.4}
|
95 |
+
stroke="hsl(var(--chart-3))"
|
96 |
stackId="a"
|
97 |
/>
|
98 |
<Area
|
99 |
dataKey="datasets"
|
100 |
type="natural"
|
101 |
+
fill="hsl(var(--chart-2))"
|
102 |
fillOpacity={0.2}
|
103 |
+
stroke="hsl(var(--chart-2))"
|
104 |
stackId="a"
|
105 |
/>
|
106 |
<Area
|
107 |
dataKey="models"
|
108 |
type="natural"
|
109 |
+
fill="hsl(var(--chart-1))"
|
110 |
fillOpacity={0.4}
|
111 |
+
stroke="hsl(var(--chart-1))"
|
112 |
stackId="a"
|
113 |
/>
|
114 |
+
<ChartLegend
|
115 |
+
content={<ChartLegendContent className="text-white" />}
|
116 |
+
/>
|
117 |
</AreaChart>
|
118 |
</ChartContainer>
|
119 |
</CardContent>
|
components/pie-chart.tsx
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use client"
|
2 |
+
|
3 |
+
import { Cell, LabelList, Pie, PieChart } from "recharts"
|
4 |
+
|
5 |
+
import {
|
6 |
+
Card,
|
7 |
+
CardContent,
|
8 |
+
CardDescription,
|
9 |
+
CardFooter,
|
10 |
+
CardHeader,
|
11 |
+
CardTitle,
|
12 |
+
} from "@/components/ui/card"
|
13 |
+
import {
|
14 |
+
ChartConfig,
|
15 |
+
ChartContainer,
|
16 |
+
ChartTooltip,
|
17 |
+
ChartTooltipContent,
|
18 |
+
} from "@/components/ui/chart"
|
19 |
+
|
20 |
+
interface PieChartProps {
|
21 |
+
title: string
|
22 |
+
description?: string
|
23 |
+
data: Array<{ name: string; value: number; fill: string }>
|
24 |
+
dataKey: string
|
25 |
+
}
|
26 |
+
|
27 |
+
const chartConfig: ChartConfig = {
|
28 |
+
value: {
|
29 |
+
label: "Value",
|
30 |
+
},
|
31 |
+
}
|
32 |
+
|
33 |
+
export function CustomPieChart({
|
34 |
+
title,
|
35 |
+
description,
|
36 |
+
data,
|
37 |
+
dataKey,
|
38 |
+
}: PieChartProps) {
|
39 |
+
const chartColors = [
|
40 |
+
"hsl(var(--chart-1))",
|
41 |
+
"hsl(var(--chart-2))",
|
42 |
+
"hsl(var(--chart-3))",
|
43 |
+
"hsl(var(--chart-4))",
|
44 |
+
"hsl(var(--chart-5))",
|
45 |
+
]
|
46 |
+
|
47 |
+
// Sort data by value in descending order
|
48 |
+
const sortedData = [...data].sort((a, b) => b.value - a.value)
|
49 |
+
|
50 |
+
// Take top 4 items
|
51 |
+
const topItems = sortedData.slice(0, 4)
|
52 |
+
|
53 |
+
// Sum the rest under "Other"
|
54 |
+
const otherValue = sortedData
|
55 |
+
.slice(4)
|
56 |
+
.reduce((sum, item) => sum + item.value, 0)
|
57 |
+
|
58 |
+
const chartData =
|
59 |
+
otherValue > 0
|
60 |
+
? [
|
61 |
+
...topItems,
|
62 |
+
{ name: "Other", value: otherValue, fill: chartColors[4] },
|
63 |
+
]
|
64 |
+
: topItems
|
65 |
+
|
66 |
+
return (
|
67 |
+
<Card className="bg-[var(--card-background)]">
|
68 |
+
<CardHeader className="items-center pb-0">
|
69 |
+
<CardTitle className="text-[var(--card-text)]">{title}</CardTitle>
|
70 |
+
{description && ( // Conditionally render CardDescription
|
71 |
+
<CardDescription className="text-[var(--card-text)]">
|
72 |
+
{description}
|
73 |
+
</CardDescription>
|
74 |
+
)}
|
75 |
+
</CardHeader>
|
76 |
+
<CardContent className="flex-1 pb-0">
|
77 |
+
<ChartContainer
|
78 |
+
config={chartConfig}
|
79 |
+
className="mx-auto aspect-square max-h-[500px]"
|
80 |
+
>
|
81 |
+
<PieChart>
|
82 |
+
<ChartTooltip
|
83 |
+
cursor={false}
|
84 |
+
content={<ChartTooltipContent hideIndicator hideLabel />}
|
85 |
+
/>
|
86 |
+
<Pie data={chartData} dataKey={dataKey} nameKey="name">
|
87 |
+
{chartData.map((entry, index) => (
|
88 |
+
<Cell
|
89 |
+
key={`cell-${index}`}
|
90 |
+
fill={chartColors[index % chartColors.length]}
|
91 |
+
/>
|
92 |
+
))}
|
93 |
+
<LabelList
|
94 |
+
dataKey="name"
|
95 |
+
className="fill-background"
|
96 |
+
stroke="none"
|
97 |
+
fontSize={12}
|
98 |
+
/>
|
99 |
+
</Pie>
|
100 |
+
</PieChart>
|
101 |
+
</ChartContainer>
|
102 |
+
</CardContent>
|
103 |
+
</Card>
|
104 |
+
)
|
105 |
+
}
|
config/site.ts
CHANGED
@@ -2,8 +2,7 @@ export type SiteConfig = typeof siteConfig
|
|
2 |
|
3 |
export const siteConfig = {
|
4 |
name: "Hugging Face Hub Stats",
|
5 |
-
description:
|
6 |
-
"Tracking Hugging Face Hub usage",
|
7 |
mainNav: [
|
8 |
{
|
9 |
title: "Home",
|
|
|
2 |
|
3 |
export const siteConfig = {
|
4 |
name: "Hugging Face Hub Stats",
|
5 |
+
description: "Tracking Hugging Face Hub usage",
|
|
|
6 |
mainNav: [
|
7 |
{
|
8 |
title: "Home",
|
lib/queries.ts
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const CREATE_VIEWS_QUERY = `
|
2 |
+
CREATE VIEW models AS SELECT * FROM read_parquet('https://huggingface.co/datasets/cfahlgren1/hub-stats/resolve/refs%2Fconvert%2Fparquet/models/train/0000.parquet?download=true');
|
3 |
+
CREATE VIEW datasets AS SELECT * FROM read_parquet('https://huggingface.co/datasets/cfahlgren1/hub-stats/resolve/refs%2Fconvert%2Fparquet/datasets/train/0000.parquet?download=true');
|
4 |
+
CREATE VIEW spaces AS SELECT * FROM read_parquet('https://huggingface.co/datasets/cfahlgren1/hub-stats/resolve/refs%2Fconvert%2Fparquet/spaces/train/0000.parquet?download=true');
|
5 |
+
`
|
6 |
+
|
7 |
+
export const FETCH_CHART_DATA_QUERY = `
|
8 |
+
WITH all_data AS (
|
9 |
+
SELECT DATE_TRUNC('month', CAST(createdAt AS DATE)) AS month, 'model' AS type FROM models
|
10 |
+
UNION ALL
|
11 |
+
SELECT DATE_TRUNC('month', CAST(createdAt AS DATE)) AS month, 'dataset' AS type FROM datasets
|
12 |
+
UNION ALL
|
13 |
+
SELECT DATE_TRUNC('month', CAST(createdAt AS DATE)) AS month, 'space' AS type FROM spaces
|
14 |
+
)
|
15 |
+
SELECT
|
16 |
+
month,
|
17 |
+
COUNT(*) FILTER (WHERE type = 'model') AS models,
|
18 |
+
COUNT(*) FILTER (WHERE type = 'dataset') AS datasets,
|
19 |
+
COUNT(*) FILTER (WHERE type = 'space') AS spaces
|
20 |
+
FROM all_data
|
21 |
+
GROUP BY month
|
22 |
+
ORDER BY month
|
23 |
+
`
|
24 |
+
|
25 |
+
export const FETCH_MODEL_LICENSE_DATA_QUERY = `
|
26 |
+
SELECT tag, COUNT(*) as count
|
27 |
+
FROM models, UNNEST(tags) AS t(tag)
|
28 |
+
WHERE tag LIKE 'license:%'
|
29 |
+
GROUP BY tag;
|
30 |
+
`
|
31 |
+
|
32 |
+
export const FETCH_DATASET_LICENSE_DATA_QUERY = `
|
33 |
+
SELECT tag, COUNT(*) as count
|
34 |
+
FROM datasets, UNNEST(tags) AS t(tag)
|
35 |
+
WHERE tag LIKE 'license:%'
|
36 |
+
GROUP BY tag;
|
37 |
+
`
|
38 |
+
|
39 |
+
export const FETCH_SPACE_SDK_DATA_QUERY = `
|
40 |
+
SELECT sdk, COUNT(*) as count
|
41 |
+
FROM spaces
|
42 |
+
GROUP BY sdk;
|
43 |
+
`
|
styles/globals.css
CHANGED
@@ -4,62 +4,64 @@
|
|
4 |
|
5 |
@layer base {
|
6 |
:root {
|
7 |
-
--background:
|
8 |
-
--foreground:
|
9 |
-
--
|
10 |
-
--
|
11 |
-
--
|
12 |
-
--
|
13 |
-
--
|
14 |
-
--
|
15 |
-
--
|
16 |
-
--secondary-foreground: 240 5.9% 10%;
|
17 |
-
--muted: 240 4.8% 95.9%;
|
18 |
-
--muted-foreground: 240 3.8% 46.1%;
|
19 |
-
--accent: 240 4.8% 95.9%;
|
20 |
-
--accent-foreground: 240 5.9% 10%;
|
21 |
-
--destructive: 0 84.2% 60.2%;
|
22 |
--destructive-foreground: 0 0% 98%;
|
23 |
-
--
|
24 |
-
--
|
25 |
-
--
|
26 |
-
--
|
27 |
-
--
|
28 |
-
--
|
29 |
-
--
|
30 |
-
--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
}
|
32 |
|
33 |
.dark {
|
34 |
-
--background: 240
|
35 |
-
--foreground:
|
36 |
-
--
|
37 |
-
--
|
38 |
-
--
|
39 |
-
--
|
40 |
-
--
|
41 |
-
--
|
42 |
-
--
|
43 |
-
--secondary-foreground: 0 0% 98%;
|
44 |
-
--muted: 240 3.7% 15.9%;
|
45 |
-
--muted-foreground: 240 5% 64.9%;
|
46 |
-
--accent: 240 3.7% 15.9%;
|
47 |
-
--accent-foreground: 0 0% 98%;
|
48 |
-
--destructive: 0 62.8% 30.6%;
|
49 |
--destructive-foreground: 0 0% 98%;
|
50 |
-
--
|
51 |
-
--
|
52 |
-
--
|
53 |
-
--
|
54 |
-
--
|
55 |
-
--
|
56 |
-
--
|
57 |
-
--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
}
|
59 |
}
|
60 |
|
61 |
-
|
62 |
-
|
63 |
@layer base {
|
64 |
:root {
|
65 |
--background: 0 0% 100%;
|
@@ -137,4 +139,4 @@
|
|
137 |
@apply bg-background text-foreground;
|
138 |
font-feature-settings: "rlig" 1, "calt" 1;
|
139 |
}
|
140 |
-
}
|
|
|
4 |
|
5 |
@layer base {
|
6 |
:root {
|
7 |
+
--background: 240 5% 6%;
|
8 |
+
--foreground: 60 5% 90%;
|
9 |
+
--primary: 240 0% 90%;
|
10 |
+
--primary-foreground: 60 0% 0%;
|
11 |
+
--secondary: 240 4% 15%;
|
12 |
+
--secondary-foreground: 60 5% 85%;
|
13 |
+
--accent: 240 0% 13%;
|
14 |
+
--accent-foreground: 60 0% 100%;
|
15 |
+
--destructive: 0 60% 50%;
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
--destructive-foreground: 0 0% 98%;
|
17 |
+
--muted: 240 5% 25%;
|
18 |
+
--muted-foreground: 60 5% 85%;
|
19 |
+
--card: 240 4% 10%;
|
20 |
+
--card-foreground: 60 5% 90%;
|
21 |
+
--card-background: #1e1e1e;
|
22 |
+
--card-text: #fff;
|
23 |
+
--popover: 240 5% 15%;
|
24 |
+
--popover-foreground: 60 5% 85%;
|
25 |
+
--border: 240 6% 20%;
|
26 |
+
--input: 240 6% 20%;
|
27 |
+
--ring: 240 5% 90%;
|
28 |
+
--chart-1: 359 2% 90%;
|
29 |
+
--chart-2: 240 1% 74%;
|
30 |
+
--chart-3: 240 1% 58%;
|
31 |
+
--chart-4: 240 1% 42%;
|
32 |
+
--chart-5: 240 2% 26%;
|
33 |
}
|
34 |
|
35 |
.dark {
|
36 |
+
--background: 240 5% 6%;
|
37 |
+
--foreground: 60 5% 90%;
|
38 |
+
--primary: 240 0% 90%;
|
39 |
+
--primary-foreground: 60 0% 0%;
|
40 |
+
--secondary: 240 4% 15%;
|
41 |
+
--secondary-foreground: 60 5% 85%;
|
42 |
+
--accent: 240 0% 13%;
|
43 |
+
--accent-foreground: 60 0% 100%;
|
44 |
+
--destructive: 0 60% 50%;
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
--destructive-foreground: 0 0% 98%;
|
46 |
+
--muted: 240 5% 25%;
|
47 |
+
--muted-foreground: 60 5% 85%;
|
48 |
+
--card-text: #fff;
|
49 |
+
--card: 240 4% 10%;
|
50 |
+
--card-foreground: 60 5% 90%;
|
51 |
+
--card-background: #1e1e1e;
|
52 |
+
--popover: 240 5% 15%;
|
53 |
+
--popover-foreground: 60 5% 85%;
|
54 |
+
--border: 240 6% 20%;
|
55 |
+
--input: 240 6% 20%;
|
56 |
+
--ring: 240 5% 90%;
|
57 |
+
--chart-1: 359 2% 90%;
|
58 |
+
--chart-2: 240 1% 74%;
|
59 |
+
--chart-3: 240 1% 58%;
|
60 |
+
--chart-4: 240 1% 42%;
|
61 |
+
--chart-5: 240 2% 26%;
|
62 |
}
|
63 |
}
|
64 |
|
|
|
|
|
65 |
@layer base {
|
66 |
:root {
|
67 |
--background: 0 0% 100%;
|
|
|
139 |
@apply bg-background text-foreground;
|
140 |
font-feature-settings: "rlig" 1, "calt" 1;
|
141 |
}
|
142 |
+
}
|