Spaces:
Running
Running
import { useState } from "react"; | |
import chartXkcd from "chart.xkcd"; | |
const projectTypes = ["model", "dataset", "space"]; | |
function transformLikesData(likesData) { | |
// Step 1 | |
likesData.sort((a, b) => new Date(a.likedAt) - new Date(b.likedAt)); | |
// Step 2 | |
const cumulativeLikes = {}; | |
let cumulativeCount = 0; | |
// Step 3 | |
likesData.forEach(like => { | |
const date = like.likedAt.split('T')[0]; | |
cumulativeCount++; | |
cumulativeLikes[date] = cumulativeCount; | |
}); | |
// Step 4 | |
const transformedData = Object.keys(cumulativeLikes).map(date => ({ | |
x: date, | |
y: cumulativeLikes[date].toString() | |
})); | |
return transformedData; | |
} | |
/** | |
* 1. User click enter key | |
* 2. Get model/dataset/space name | |
* 3. Get data from API(API format: https://huggingface.co/api/spaces/timqian/like-history/likers?expand[]=likeAt) | |
* 4. Format data(to 20 points at most) | |
* 5. Draw line chart | |
*/ | |
function App() { | |
const [projectType, setProjectType] = useState("models"); | |
const [projectName, setProjectName] = useState("openai/whisper-large-v3"); | |
const onSubmit = async () => { | |
const svg = document.querySelector('.line-chart') | |
const res = await fetch(`https://huggingface.co/api/${projectType}/${projectName}/likers?expand[]=likeAt`) | |
/** | |
* Format: | |
* [{"user": "timqian", "likedAt": "2021-07-01T00:00:00.000Z"}, {"user": "yy", "likedAt": "2021-07-02T00:00:00.000Z"}] | |
*/ | |
const likers = await res.json() | |
let likeHistory = transformLikesData(likers) | |
if (likeHistory.length > 40) { | |
// sample 20 points | |
const sampledLikeHistory = [] | |
const step = Math.floor(likeHistory.length / 40) | |
for (let i = 0; i < likeHistory.length; i += step) { | |
sampledLikeHistory.push(likeHistory[i]) | |
} | |
likeHistory = sampledLikeHistory | |
} | |
// if likeHistory is empty, show error message | |
if (likeHistory.length === 0) { | |
alert("No like history found") | |
return | |
} | |
new chartXkcd.XY(svg, { | |
title: 'HF like history', | |
xLabel: 'Time', | |
yLabel: 'Likes', | |
data: { | |
datasets: [{ | |
label: projectName, | |
data: likeHistory, | |
}], | |
}, | |
options: { | |
xTickCount: 3, | |
yTickCount: 4, | |
legendPosition: chartXkcd.config.positionType.upLeft, | |
showLine: true, | |
timeFormat: 'MM/DD/YYYY', | |
dotSize: 0.5, | |
dataColors: ["#dd4528", | |
"#28a3dd", | |
"#f3db52" | |
] | |
}, | |
}); | |
} | |
return ( | |
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-16"> | |
<div className="mx-auto max-w-3xl"> | |
<h1 className="text-sm font-light right-0 text-right text-gray-600"> | |
View the like history of a project on huggingface <span className="text-lg">🤗</span> | |
</h1> | |
<div> | |
<div className="relative mt-2 rounded-md shadow-sm"> | |
<div className="absolute inset-y-0 left-0 flex items-center"> | |
<label htmlFor="country" className="sr-only"> | |
Country | |
</label> | |
<select | |
id="country" | |
name="country" | |
autoComplete="country" | |
className="h-full rounded-md border-0 bg-transparent py-0 pl-3 pr-7 text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm" | |
onChange={(e) => setProjectType(e.target.value)} | |
> | |
<option value="models">Model</option> | |
<option value="datasets">Dataset</option> | |
<option value="spaces">Space</option> | |
</select> | |
</div> | |
<input | |
type="text" | |
name="phone-number" | |
id="phone-number" | |
className="block w-full rounded-md border-0 py-1.5 pl-24 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" | |
placeholder="openai/whisper-large-v3" | |
value={projectName} | |
onChange={(e) => setProjectName(e.target.value)} | |
onFocus={(e) => e.target.select()} | |
onKeyDown={async (e) => { | |
if (e.key === "Enter") { | |
try { | |
await onSubmit(); | |
} catch (err) { | |
alert(`No like history found for ${projectName}, please check the name and try again`); | |
} | |
} | |
}} | |
/> | |
</div> | |
</div> | |
<div className="mt-16"> | |
<svg className="line-chart"></svg> | |
</div> | |
</div> | |
</div> | |
); | |
} | |
export default App; | |