like-history / src /App.js
timqian's picture
functional
362fadb
raw
history blame
4.7 kB
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;