hysts HF staff commited on
Commit
6afd365
1 Parent(s): 1f64a84
Files changed (11) hide show
  1. .gitattributes +0 -1
  2. .gitignore +162 -0
  3. .pre-commit-config.yaml +60 -0
  4. .vscode/settings.json +30 -0
  5. LICENSE +21 -0
  6. README.md +6 -3
  7. app.py +296 -0
  8. constants.py +47 -0
  9. demo_list.py +188 -0
  10. requirements.txt +4 -0
  11. style.css +104 -0
.gitattributes CHANGED
@@ -25,7 +25,6 @@
25
  *.safetensors filter=lfs diff=lfs merge=lfs -text
26
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
  *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
  *.tflite filter=lfs diff=lfs merge=lfs -text
30
  *.tgz filter=lfs diff=lfs merge=lfs -text
31
  *.wasm filter=lfs diff=lfs merge=lfs -text
 
25
  *.safetensors filter=lfs diff=lfs merge=lfs -text
26
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
  *.tar.* filter=lfs diff=lfs merge=lfs -text
 
28
  *.tflite filter=lfs diff=lfs merge=lfs -text
29
  *.tgz filter=lfs diff=lfs merge=lfs -text
30
  *.wasm filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio_cached_examples/
2
+
3
+ # Byte-compiled / optimized / DLL files
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # Distribution / packaging
12
+ .Python
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ downloads/
17
+ eggs/
18
+ .eggs/
19
+ lib/
20
+ lib64/
21
+ parts/
22
+ sdist/
23
+ var/
24
+ wheels/
25
+ share/python-wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+ MANIFEST
30
+
31
+ # PyInstaller
32
+ # Usually these files are written by a python script from a template
33
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .nox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ nosetests.xml
49
+ coverage.xml
50
+ *.cover
51
+ *.py,cover
52
+ .hypothesis/
53
+ .pytest_cache/
54
+ cover/
55
+
56
+ # Translations
57
+ *.mo
58
+ *.pot
59
+
60
+ # Django stuff:
61
+ *.log
62
+ local_settings.py
63
+ db.sqlite3
64
+ db.sqlite3-journal
65
+
66
+ # Flask stuff:
67
+ instance/
68
+ .webassets-cache
69
+
70
+ # Scrapy stuff:
71
+ .scrapy
72
+
73
+ # Sphinx documentation
74
+ docs/_build/
75
+
76
+ # PyBuilder
77
+ .pybuilder/
78
+ target/
79
+
80
+ # Jupyter Notebook
81
+ .ipynb_checkpoints
82
+
83
+ # IPython
84
+ profile_default/
85
+ ipython_config.py
86
+
87
+ # pyenv
88
+ # For a library or package, you might want to ignore these files since the code is
89
+ # intended to run in multiple environments; otherwise, check them in:
90
+ # .python-version
91
+
92
+ # pipenv
93
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
94
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
95
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
96
+ # install all needed dependencies.
97
+ #Pipfile.lock
98
+
99
+ # poetry
100
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
101
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
102
+ # commonly ignored for libraries.
103
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
104
+ #poetry.lock
105
+
106
+ # pdm
107
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
108
+ #pdm.lock
109
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
110
+ # in version control.
111
+ # https://pdm.fming.dev/#use-with-ide
112
+ .pdm.toml
113
+
114
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115
+ __pypackages__/
116
+
117
+ # Celery stuff
118
+ celerybeat-schedule
119
+ celerybeat.pid
120
+
121
+ # SageMath parsed files
122
+ *.sage.py
123
+
124
+ # Environments
125
+ .env
126
+ .venv
127
+ env/
128
+ venv/
129
+ ENV/
130
+ env.bak/
131
+ venv.bak/
132
+
133
+ # Spyder project settings
134
+ .spyderproject
135
+ .spyproject
136
+
137
+ # Rope project settings
138
+ .ropeproject
139
+
140
+ # mkdocs documentation
141
+ /site
142
+
143
+ # mypy
144
+ .mypy_cache/
145
+ .dmypy.json
146
+ dmypy.json
147
+
148
+ # Pyre type checker
149
+ .pyre/
150
+
151
+ # pytype static type analyzer
152
+ .pytype/
153
+
154
+ # Cython debug symbols
155
+ cython_debug/
156
+
157
+ # PyCharm
158
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
161
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
+ #.idea/
.pre-commit-config.yaml ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.5.0
4
+ hooks:
5
+ - id: check-executables-have-shebangs
6
+ - id: check-json
7
+ - id: check-merge-conflict
8
+ - id: check-shebang-scripts-are-executable
9
+ - id: check-toml
10
+ - id: check-yaml
11
+ - id: end-of-file-fixer
12
+ - id: mixed-line-ending
13
+ args: ["--fix=lf"]
14
+ - id: requirements-txt-fixer
15
+ - id: trailing-whitespace
16
+ - repo: https://github.com/myint/docformatter
17
+ rev: v1.7.5
18
+ hooks:
19
+ - id: docformatter
20
+ args: ["--in-place"]
21
+ - repo: https://github.com/pycqa/isort
22
+ rev: 5.13.2
23
+ hooks:
24
+ - id: isort
25
+ args: ["--profile", "black"]
26
+ - repo: https://github.com/pre-commit/mirrors-mypy
27
+ rev: v1.8.0
28
+ hooks:
29
+ - id: mypy
30
+ args: ["--ignore-missing-imports"]
31
+ additional_dependencies:
32
+ [
33
+ "types-python-slugify",
34
+ "types-requests",
35
+ "types-PyYAML",
36
+ "types-pytz",
37
+ ]
38
+ - repo: https://github.com/psf/black
39
+ rev: 24.2.0
40
+ hooks:
41
+ - id: black
42
+ language_version: python3.10
43
+ args: ["--line-length", "119"]
44
+ - repo: https://github.com/kynan/nbstripout
45
+ rev: 0.7.1
46
+ hooks:
47
+ - id: nbstripout
48
+ args:
49
+ [
50
+ "--extra-keys",
51
+ "metadata.interpreter metadata.kernelspec cell.metadata.pycharm",
52
+ ]
53
+ - repo: https://github.com/nbQA-dev/nbQA
54
+ rev: 1.7.1
55
+ hooks:
56
+ - id: nbqa-black
57
+ - id: nbqa-pyupgrade
58
+ args: ["--py37-plus"]
59
+ - id: nbqa-isort
60
+ args: ["--float-to-top"]
.vscode/settings.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "editor.formatOnSave": true,
3
+ "files.insertFinalNewline": false,
4
+ "[python]": {
5
+ "editor.defaultFormatter": "ms-python.black-formatter",
6
+ "editor.formatOnType": true,
7
+ "editor.codeActionsOnSave": {
8
+ "source.organizeImports": "explicit"
9
+ }
10
+ },
11
+ "[jupyter]": {
12
+ "files.insertFinalNewline": false
13
+ },
14
+ "black-formatter.args": [
15
+ "--line-length=119"
16
+ ],
17
+ "isort.args": ["--profile", "black"],
18
+ "flake8.args": [
19
+ "--max-line-length=119"
20
+ ],
21
+ "ruff.lint.args": [
22
+ "--line-length=119"
23
+ ],
24
+ "notebook.output.scrolling": true,
25
+ "notebook.formatOnCellExecution": true,
26
+ "notebook.formatOnSave.enabled": true,
27
+ "notebook.codeActionsOnSave": {
28
+ "source.organizeImports": "explicit"
29
+ }
30
+ }
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2023 hysts
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,12 +1,15 @@
1
  ---
2
- title: Historical Spaces Of The Week
3
- emoji: 💻
4
- colorFrom: green
5
  colorTo: purple
6
  sdk: gradio
7
  sdk_version: 4.19.2
 
8
  app_file: app.py
9
  pinned: false
 
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Historical Spaces of the Week
3
+ emoji:
4
+ colorFrom: red
5
  colorTo: purple
6
  sdk: gradio
7
  sdk_version: 4.19.2
8
+ python_version: 3.10.13
9
  app_file: app.py
10
  pinned: false
11
+ license: mit
12
+ header: mini
13
  ---
14
 
15
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+
3
+ from __future__ import annotations
4
+
5
+ import dataclasses
6
+ import datetime
7
+ import pathlib
8
+
9
+ import datasets
10
+ import gradio as gr
11
+ import pandas as pd
12
+ import tqdm.auto
13
+ from huggingface_hub import HfApi
14
+
15
+ from constants import HARDWARE_CHOICES, SDK_CHOICES, SLEEP_TIME_CHOICES, STATUS_CHOICES
16
+ from demo_list import DemoInfo, DemoList
17
+
18
+ TITLE = "# Historical Spaces of the Week"
19
+
20
+ repo_dir = pathlib.Path(__file__).parent.absolute()
21
+
22
+ api = HfApi()
23
+
24
+ df_pre = datasets.load_dataset("hysts-bot/spaces-of-the-week-pre-2024-01-08")["train"].to_pandas()
25
+ df_post = datasets.load_dataset("hysts-bot/spaces-of-the-week")["train"].to_pandas()
26
+
27
+ BEFORE_2024_01_08 = "before 2024-01-08"
28
+
29
+
30
+ def get_space_info(df: pd.DataFrame, sort_by: list[str], ascending: list[bool]) -> pd.DataFrame:
31
+ data = []
32
+ for _, row in tqdm.auto.tqdm(df.iterrows(), total=len(df)):
33
+ space_id = row["space_id"]
34
+ try:
35
+ info = DemoInfo.from_space_id(space_id)
36
+ data.append({"featured_week": row.get("date", BEFORE_2024_01_08)} | dataclasses.asdict(info))
37
+ except Exception as e:
38
+ print(f"Failed to load {space_id}: {e}")
39
+ data.append(
40
+ {
41
+ "featured_week": row.get("date", BEFORE_2024_01_08),
42
+ "space_id": space_id,
43
+ "url": f"https://huggingface.co/spaces/{space_id}",
44
+ "title": row["title"],
45
+ "owner": space_id.split("/")[0],
46
+ "sdk": "",
47
+ "sdk_version": "",
48
+ "likes": None,
49
+ "status": "",
50
+ "last_modified": "",
51
+ "sleep_time": 0,
52
+ "replicas": 0,
53
+ "private": True,
54
+ "hardware": "",
55
+ "suggested_hardware": "",
56
+ "created": datetime.datetime.fromisoformat(row["created_at"]).strftime("%Y/%m/%d %H:%M:%S"),
57
+ }
58
+ )
59
+ return pd.DataFrame(data).sort_values(sort_by, ascending=ascending).reset_index(drop=True)
60
+
61
+
62
+ df_merged = pd.concat(
63
+ [
64
+ get_space_info(df_post, sort_by=["featured_week", "space_id"], ascending=[False, True]),
65
+ get_space_info(df_pre, sort_by=["created", "space_id"], ascending=[False, True]),
66
+ ]
67
+ ).reset_index(drop=True)
68
+ demo_list = DemoList(df_merged)
69
+
70
+
71
+ def update_status_checkboxes(choices: list[str]) -> list[str]:
72
+ if "(ALL)" in choices:
73
+ return STATUS_CHOICES
74
+ elif "(NONE)" in choices:
75
+ return []
76
+ else:
77
+ return choices
78
+
79
+
80
+ def update_hardware_checkboxes(choices: list[str]) -> list[str]:
81
+ if "(ALL)" in choices:
82
+ return HARDWARE_CHOICES
83
+ elif "(NONE)" in choices:
84
+ return []
85
+ else:
86
+ return choices
87
+
88
+
89
+ def update_sdk_checkboxes(choices: list[str]) -> list[str]:
90
+ if "(ALL)" in choices:
91
+ return SDK_CHOICES
92
+ elif "(NONE)" in choices:
93
+ return []
94
+ else:
95
+ return choices
96
+
97
+
98
+ def update_sleep_time_checkboxes(choices: list[str]) -> list[str]:
99
+ if "(ALL)" in choices:
100
+ return SLEEP_TIME_CHOICES
101
+ elif "(NONE)" in choices:
102
+ return []
103
+ else:
104
+ return choices
105
+
106
+
107
+ DEFAULT_COLUMNS = [
108
+ "featured_week",
109
+ "status",
110
+ "hardware",
111
+ "title",
112
+ "owner",
113
+ "likes",
114
+ "created",
115
+ "sdk",
116
+ ]
117
+ FEATURED_WEEKS = demo_list.df_raw.featured_week.unique().tolist()
118
+ DEFAULT_FEATURED_WEEKS = FEATURED_WEEKS[:4]
119
+
120
+
121
+ def update_df(
122
+ status: list[str],
123
+ hardware: list[str],
124
+ sdk: list[str],
125
+ sleep_time: list[str],
126
+ multiple_replicas: bool,
127
+ owner: str,
128
+ featured_weeks: list[str],
129
+ column_names: list[str],
130
+ ) -> pd.DataFrame:
131
+ return gr.DataFrame(
132
+ value=demo_list.filter(
133
+ status,
134
+ hardware,
135
+ sdk,
136
+ sleep_time,
137
+ multiple_replicas,
138
+ owner,
139
+ featured_weeks,
140
+ column_names,
141
+ ),
142
+ datatype=demo_list.get_column_datatypes(column_names),
143
+ )
144
+
145
+
146
+ def update_num_spaces(df: pd.DataFrame) -> str:
147
+ return f"{len(df)} / {len(demo_list.df_raw)}"
148
+
149
+
150
+ with gr.Blocks(css="style.css") as demo:
151
+ gr.Markdown(TITLE)
152
+ with gr.Accordion(label="Filter", open=True):
153
+ with gr.Group():
154
+ featured_weeks = gr.Dropdown(
155
+ label="Featured weeks",
156
+ choices=FEATURED_WEEKS,
157
+ value=DEFAULT_FEATURED_WEEKS,
158
+ multiselect=True,
159
+ )
160
+ with gr.Row():
161
+ reset_featured_weeks_button = gr.Button("Reset")
162
+ select_all_featured_weeks_button = gr.Button("Select all")
163
+ clear_featured_weeks_button = gr.Button("Clear")
164
+ with gr.Accordion(label="Advanced", open=False):
165
+ status = gr.CheckboxGroup(
166
+ label="Status",
167
+ choices=["(ALL)", "(NONE)"] + STATUS_CHOICES,
168
+ value=STATUS_CHOICES,
169
+ type="value",
170
+ )
171
+ hardware = gr.CheckboxGroup(
172
+ label="Hardware",
173
+ choices=["(ALL)", "(NONE)"] + HARDWARE_CHOICES,
174
+ value=HARDWARE_CHOICES,
175
+ type="value",
176
+ )
177
+ sdk = gr.CheckboxGroup(
178
+ label="SDK",
179
+ choices=["(ALL)", "(NONE)"] + SDK_CHOICES,
180
+ value=SDK_CHOICES,
181
+ type="value",
182
+ )
183
+ sleep_time = gr.CheckboxGroup(
184
+ label="Sleep time",
185
+ choices=["(ALL)", "(NONE)"] + SLEEP_TIME_CHOICES,
186
+ value=SLEEP_TIME_CHOICES,
187
+ type="value",
188
+ )
189
+ multiple_replicas = gr.Checkbox(label="Multiple replicas", value=False)
190
+ owner = gr.Dropdown(
191
+ label="Owner",
192
+ choices=["(ALL)"] + sorted(demo_list.df_raw.owner.unique().tolist()),
193
+ value="(ALL)",
194
+ )
195
+ with gr.Group():
196
+ column_names = gr.CheckboxGroup(label="Columns", choices=demo_list.column_names, value=DEFAULT_COLUMNS)
197
+ apply_button = gr.Button("Apply")
198
+
199
+ num_spaces = gr.Textbox(label="Number of Spaces", interactive=False)
200
+ df = gr.Dataframe(
201
+ value=demo_list.df_prettified,
202
+ datatype=demo_list.get_column_datatypes(demo_list.column_names),
203
+ type="pandas",
204
+ row_count=(0, "dynamic"),
205
+ height=1000,
206
+ elem_id="table",
207
+ interactive=False,
208
+ )
209
+
210
+ status.input(
211
+ fn=update_status_checkboxes,
212
+ inputs=status,
213
+ outputs=status,
214
+ queue=False,
215
+ show_progress=False,
216
+ api_name=False,
217
+ )
218
+ hardware.input(
219
+ fn=update_hardware_checkboxes,
220
+ inputs=hardware,
221
+ outputs=hardware,
222
+ queue=False,
223
+ show_progress=False,
224
+ api_name=False,
225
+ )
226
+ sdk.input(
227
+ fn=update_sdk_checkboxes,
228
+ inputs=sdk,
229
+ outputs=sdk,
230
+ queue=False,
231
+ show_progress=False,
232
+ api_name=False,
233
+ )
234
+ sleep_time.input(
235
+ fn=update_sleep_time_checkboxes,
236
+ inputs=sleep_time,
237
+ outputs=sleep_time,
238
+ queue=False,
239
+ show_progress=False,
240
+ api_name=False,
241
+ )
242
+ reset_featured_weeks_button.click(
243
+ fn=lambda: gr.Checkbox(value=DEFAULT_FEATURED_WEEKS),
244
+ outputs=featured_weeks,
245
+ queue=False,
246
+ api_name=False,
247
+ )
248
+ select_all_featured_weeks_button.click(
249
+ fn=lambda: gr.Checkbox(value=FEATURED_WEEKS),
250
+ outputs=featured_weeks,
251
+ queue=False,
252
+ api_name=False,
253
+ )
254
+ clear_featured_weeks_button.click(
255
+ fn=lambda: gr.Checkbox(value=[]),
256
+ outputs=featured_weeks,
257
+ queue=False,
258
+ api_name=False,
259
+ )
260
+ inputs = [
261
+ status,
262
+ hardware,
263
+ sdk,
264
+ sleep_time,
265
+ multiple_replicas,
266
+ owner,
267
+ featured_weeks,
268
+ column_names,
269
+ ]
270
+ apply_button.click(
271
+ fn=update_df,
272
+ inputs=inputs,
273
+ outputs=df,
274
+ api_name=False,
275
+ ).then(
276
+ fn=update_num_spaces,
277
+ inputs=df,
278
+ outputs=num_spaces,
279
+ queue=False,
280
+ api_name=False,
281
+ )
282
+ demo.load(
283
+ fn=update_df,
284
+ inputs=inputs,
285
+ outputs=df,
286
+ api_name=False,
287
+ ).then(
288
+ fn=update_num_spaces,
289
+ inputs=df,
290
+ outputs=num_spaces,
291
+ queue=False,
292
+ api_name=False,
293
+ )
294
+
295
+ if __name__ == "__main__":
296
+ demo.queue(api_open=False).launch(show_api=False)
constants.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ STATUS_CHOICES = [
2
+ "RUNNING",
3
+ "SLEEPING",
4
+ "PAUSED",
5
+ "RUNTIME_ERROR",
6
+ "BUILD_ERROR",
7
+ "CONFIG_ERROR",
8
+ "BUILDING",
9
+ "RUNNING_BUILDING",
10
+ "NO_APP_FILE",
11
+ "",
12
+ ]
13
+ HARDWARE_CHOICES = [
14
+ "cpu-basic",
15
+ "cpu-upgrade",
16
+ "cpu-xl",
17
+ "t4-small",
18
+ "t4-medium",
19
+ "zero-a10g",
20
+ "a10g-small",
21
+ "a10g-large",
22
+ "a100-large",
23
+ "a10g-largex2",
24
+ "a10g-largex4",
25
+ "",
26
+ ]
27
+ SDK_CHOICES = [
28
+ "gradio",
29
+ "streamlit",
30
+ "docker",
31
+ "static",
32
+ "",
33
+ ]
34
+ SLEEP_TIME_INT_TO_STR = {
35
+ 0: "null",
36
+ 300: "5 minutes",
37
+ 900: "15 minutes",
38
+ 1800: "30 minutes",
39
+ 3600: "1 hour",
40
+ 36000: "10 hours",
41
+ 86400: "24 hours",
42
+ 172800: "48 hours",
43
+ 259200: "72 hours",
44
+ 604800: "1 week",
45
+ }
46
+ SLEEP_TIME_CHOICES = list(SLEEP_TIME_INT_TO_STR.values())
47
+ SLEEP_TIME_STR_TO_INT = {v: k for k, v in SLEEP_TIME_INT_TO_STR.items()}
demo_list.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dataclasses
2
+ import datetime
3
+ import operator
4
+ import pathlib
5
+
6
+ import numpy as np
7
+ import pandas as pd
8
+ import tqdm.auto
9
+ import yaml
10
+ from huggingface_hub import HfApi
11
+
12
+ from constants import SLEEP_TIME_INT_TO_STR, SLEEP_TIME_STR_TO_INT
13
+
14
+
15
+ @dataclasses.dataclass(frozen=True)
16
+ class DemoInfo:
17
+ space_id: str
18
+ url: str
19
+ title: str
20
+ owner: str
21
+ sdk: str
22
+ sdk_version: str
23
+ likes: int
24
+ status: str
25
+ last_modified: str
26
+ sleep_time: int
27
+ replicas: int
28
+ private: bool
29
+ hardware: str
30
+ suggested_hardware: str
31
+ created: str = ""
32
+
33
+ def __post_init__(self):
34
+ object.__setattr__(self, "last_modified", DemoInfo.convert_timestamp(self.last_modified))
35
+ object.__setattr__(self, "created", DemoInfo.convert_timestamp(self.created))
36
+
37
+ @staticmethod
38
+ def convert_timestamp(timestamp: str | datetime.datetime) -> str:
39
+ if isinstance(timestamp, datetime.datetime):
40
+ return timestamp.strftime("%Y/%m/%d %H:%M:%S")
41
+ try:
42
+ return datetime.datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").strftime("%Y/%m/%d %H:%M:%S")
43
+ except ValueError:
44
+ return timestamp
45
+
46
+ @classmethod
47
+ def from_space_id(cls, space_id: str) -> "DemoInfo":
48
+ api = HfApi()
49
+ space_info = api.space_info(repo_id=space_id)
50
+ card = space_info.cardData
51
+ runtime = space_info.runtime
52
+
53
+ return cls(
54
+ space_id=space_id,
55
+ url=f"https://huggingface.co/spaces/{space_id}",
56
+ title=card["title"] if "title" in card else "",
57
+ owner=space_id.split("/")[0],
58
+ sdk=card["sdk"],
59
+ sdk_version=card.get("sdk_version", ""),
60
+ likes=space_info.likes,
61
+ status=runtime.stage,
62
+ last_modified=space_info.lastModified,
63
+ sleep_time=runtime.sleep_time or 0,
64
+ replicas=runtime.raw["replicas"]["current"] or runtime.raw["replicas"]["requested"],
65
+ private=space_info.private,
66
+ hardware=runtime.hardware or runtime.requested_hardware or "",
67
+ suggested_hardware=card.get("suggested_hardware", ""),
68
+ created=space_info.created_at,
69
+ )
70
+
71
+
72
+ def get_df_from_yaml(path: pathlib.Path | str) -> pd.DataFrame:
73
+ with pathlib.Path(path).open() as f:
74
+ data = yaml.safe_load(f)
75
+ demo_info = []
76
+ for space_id in tqdm.auto.tqdm(list(data)):
77
+ base_info = DemoInfo.from_space_id(space_id)
78
+ info = DemoInfo(**(dataclasses.asdict(base_info) | data[space_id]))
79
+ demo_info.append(info)
80
+ return pd.DataFrame([dataclasses.asdict(info) for info in demo_info])
81
+
82
+
83
+ class Prettifier:
84
+ @staticmethod
85
+ def create_link(text: str, url: str) -> str:
86
+ return f'<a href={url} target="_blank">{text}</a>'
87
+
88
+ @staticmethod
89
+ def to_div(text: str | None, category_name: str) -> str:
90
+ if text is None:
91
+ text = ""
92
+ class_name = f"{category_name}-{text.lower()}"
93
+ return f'<div class="{class_name}">{text}</div>'
94
+
95
+ @staticmethod
96
+ def add_div_tag_to_replicas(replicas: int) -> str:
97
+ if replicas == 0:
98
+ return ""
99
+ if replicas == 1:
100
+ return "1"
101
+ return f'<div class="multiple-replicas">{replicas}</div>'
102
+
103
+ @staticmethod
104
+ def add_div_tag_to_sleep_time(sleep_time_s: str, hardware: str) -> str:
105
+ if hardware == "cpu-basic":
106
+ return f'<div class="sleep-time-cpu-basic">{sleep_time_s}</div>'
107
+ s = sleep_time_s.replace(" ", "-")
108
+ return f'<div class="sleep-time-{s}">{sleep_time_s}</div>'
109
+
110
+ def __call__(self, df: pd.DataFrame) -> pd.DataFrame:
111
+ new_rows = []
112
+ for _, row in df.iterrows():
113
+ new_row = dict(row) | {
114
+ "status": self.to_div(row.status, "status"),
115
+ "hardware": self.to_div(row.hardware, "hardware"),
116
+ "suggested_hardware": self.to_div(row.suggested_hardware, "hardware"),
117
+ "title": self.create_link(row.title, row.url),
118
+ "owner": self.create_link(row.owner, f"https://huggingface.co/{row.owner}"),
119
+ "sdk": self.to_div(row.sdk, "sdk"),
120
+ "sleep_time": (
121
+ self.add_div_tag_to_sleep_time(SLEEP_TIME_INT_TO_STR[row.sleep_time], row.hardware)
122
+ if ~np.isnan(row.sleep_time)
123
+ else ""
124
+ ),
125
+ "replicas": self.add_div_tag_to_replicas(row.replicas),
126
+ }
127
+ new_rows.append(new_row)
128
+ return pd.DataFrame(new_rows, columns=df.columns)
129
+
130
+
131
+ class DemoList:
132
+ COLUMN_INFO = [
133
+ ["featured_week", "str"],
134
+ ["status", "markdown"],
135
+ ["hardware", "markdown"],
136
+ ["title", "markdown"],
137
+ ["owner", "markdown"],
138
+ ["likes", "number"],
139
+ ["last_modified", "str"],
140
+ ["created", "str"],
141
+ ["sdk", "markdown"],
142
+ ["sdk_version", "str"],
143
+ ["suggested_hardware", "markdown"],
144
+ ["sleep_time", "markdown"],
145
+ ["replicas", "markdown"],
146
+ ]
147
+
148
+ def __init__(self, df: pd.DataFrame):
149
+ self.df_raw = df
150
+ self._prettifier = Prettifier()
151
+ self.df_prettified = self._prettifier(df).loc[:, self.column_names]
152
+
153
+ @property
154
+ def column_names(self):
155
+ return list(map(operator.itemgetter(0), self.COLUMN_INFO))
156
+
157
+ def get_column_datatypes(self, column_names: list[str]) -> list[str]:
158
+ mapping = dict(self.COLUMN_INFO)
159
+ return [mapping[name] for name in column_names]
160
+
161
+ def filter(
162
+ self,
163
+ status: list[str],
164
+ hardware: list[str],
165
+ sdk: list[str],
166
+ sleep_time: list[str],
167
+ multiple_replicas: bool,
168
+ owner: str,
169
+ featured_weeks: list[str],
170
+ column_names: list[str],
171
+ ) -> pd.DataFrame:
172
+ df = self.df_raw.copy()
173
+
174
+ if multiple_replicas:
175
+ df = df[self.df_raw.replicas > 1]
176
+ if owner != "(ALL)":
177
+ df = df[self.df_raw.owner == owner]
178
+
179
+ sleep_time_int = [SLEEP_TIME_STR_TO_INT[s] for s in sleep_time]
180
+ df = df[
181
+ (self.df_raw.status.isin(status))
182
+ & (self.df_raw.hardware.isin(hardware))
183
+ & (self.df_raw.sleep_time.isin(sleep_time_int))
184
+ & (self.df_raw.sdk.isin(sdk))
185
+ & (self.df_raw.featured_week.isin(featured_weeks))
186
+ ]
187
+
188
+ return self._prettifier(df).loc[:, column_names]
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio==4.19.2
2
+ huggingface-hub==0.20.3
3
+ pandas==2.2.0
4
+ tqdm==4.66.1
style.css ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ h1 {
2
+ text-align: center;
3
+ display: block;
4
+ }
5
+
6
+ body a,
7
+ #table a {
8
+ background-color: transparent;
9
+ color: #58a6ff;
10
+ text-decoration: none;
11
+ }
12
+
13
+ body a:active,
14
+ body a:hover {
15
+ outline-width: 0;
16
+ }
17
+
18
+ body a:hover {
19
+ text-decoration: underline;
20
+ }
21
+
22
+ /* set colors for statuses */
23
+ div.status-running {
24
+ color: green;
25
+ }
26
+
27
+ div.status-sleeping {
28
+ color: orange;
29
+ }
30
+
31
+ div.status-paused {
32
+ color: gray;
33
+ }
34
+
35
+ div.status-runtime_error,
36
+ div.status-build_error {
37
+ color: red;
38
+ }
39
+
40
+ /* set colors for sdks */
41
+ div.sdk-gradio {
42
+ color: orange;
43
+ }
44
+
45
+ div.sdk-docker {
46
+ color: deepskyblue;
47
+ }
48
+
49
+ /* set colors for hardware types */
50
+ div.hardware-a100-large,
51
+ div.hardware-zero-a10g,
52
+ div.hardware-cpu-xl,
53
+ div.hardware-a10g-largex4,
54
+ div.hardware-a10g-largex2,
55
+ div.hardware-a10g-large,
56
+ div.hardware-a10g-small {
57
+ color: red;
58
+ }
59
+
60
+ div.hardware-t4-medium,
61
+ div.hardware-t4-small {
62
+ color: orange;
63
+ }
64
+
65
+ div.hardware-cpu-upgrade {
66
+ color: green;
67
+ }
68
+
69
+ div.hardware-cpu-basic {
70
+ color: deepskyblue;
71
+ }
72
+
73
+ /* set colors for sleep time */
74
+ div.sleep-time-cpu-basic {
75
+ color: gray;
76
+ }
77
+
78
+ div.sleep-time-5-minutes,
79
+ div.sleep-time-10-minutes,
80
+ div.sleep-time-15-minutes,
81
+ div.sleep-time-30-minutes {
82
+ color: green;
83
+ }
84
+
85
+ div.sleep-time-1-hour,
86
+ div.sleep-time-10-hours {
87
+ color: orange;
88
+ }
89
+
90
+ div.sleep-time-24-hours,
91
+ div.sleep-time-48-hours,
92
+ div.sleep-time-72-hours,
93
+ div.sleep-time-1-week {
94
+ color: orangered;
95
+ }
96
+
97
+ div.sleep-time-null {
98
+ color: red;
99
+ }
100
+
101
+ /* set colors for replica */
102
+ div.multiple-replicas {
103
+ color: red;
104
+ }