File size: 4,702 Bytes
3e887f8
574b4e5
3e887f8
 
09ab9c0
362fadb
 
 
574b4e5
362fadb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9054fbb
 
 
362fadb
9054fbb
 
 
09ab9c0
362fadb
 
574b4e5
362fadb
574b4e5
3e887f8
362fadb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574b4e5
 
362fadb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574b4e5
 
 
09ab9c0
9054fbb
 
3e887f8
 
1465ed6
3e887f8
 
 
 
 
 
 
 
 
 
 
574b4e5
3e887f8
362fadb
 
 
3e887f8
 
 
 
 
 
 
362fadb
574b4e5
 
362fadb
 
574b4e5
362fadb
 
 
 
 
574b4e5
 
3e887f8
 
 
574b4e5
 
 
9054fbb
09ab9c0
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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;