Skip to content

Commit 20e9a85

Browse files
Allow specifying input / output directories when building website (#667)
### Summary Beforehand, these were hard-coded. Now you can install the `ecosystem` package from this repo, then run: ```sh ecosystem build --resources ecosystem/resources --output website ``` --------- Co-authored-by: Eric Arellano <14852634+Eric-Arellano@users.noreply.github.com>
1 parent 310a08f commit 20e9a85

File tree

7 files changed

+138
-111
lines changed

7 files changed

+138
-111
lines changed

ecosystem/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,15 @@
11
"""Ecosystem main module."""
2+
import fire
3+
4+
from ecosystem.cli import CliMembers, CliCI, build_website
5+
6+
7+
def main():
8+
# pylint: disable=missing-function-docstring
9+
fire.Fire(
10+
{
11+
"members": CliMembers,
12+
"build": build_website,
13+
"ci": CliCI,
14+
}
15+
)

ecosystem/cli/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""CLI."""
22
from .members import CliMembers
3-
from .website import CliWebsite
3+
from .website import build_website
44
from .ci import CliCI

ecosystem/cli/website.py

Lines changed: 115 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,132 @@
11
"""CliWebsite class for controlling all CLI functions."""
2+
from __future__ import annotations
3+
4+
25
from pathlib import Path
3-
from typing import Optional
6+
from typing import Any
47
import json
5-
import os
68
import toml
79

8-
from jinja2 import Environment, FileSystemLoader
10+
from jinja2 import Environment, PackageLoader, Template
911

1012
from ecosystem.daos import DAO
13+
from ecosystem.models.repository import Repository
1114

1215

13-
class CliWebsite:
14-
"""CliMembers class.
15-
Entrypoint for all CLI website commands.
16+
def build_website(resources: str, output: str) -> None:
17+
"""
18+
Generates the ecosystem web page from data in `resources` dir, writing to `output` dir.
19+
"""
20+
resources_dir = Path(resources)
21+
html = _build_html(*_load_from_file(resources_dir))
22+
Path(output, "index.html").write_text(html)
1623

17-
Each public method of this class is CLI command
18-
and arguments for method are options/flags for this command.
1924

20-
Ex: `python manager.py website build_website`
25+
def _load_from_file(
26+
resources_dir: Path,
27+
) -> tuple[list[Repository], dict[str, Any], dict[str, str], dict[str, Template]]:
2128
"""
29+
Loads website data from file.
30+
Returns:
31+
* Projects: List of Repository objects from `members` folder.
32+
* Web data: Strings (title / descriptions etc.) from `website.toml`.
33+
* Label descriptions: from `labels.json`.
34+
* Jinja templates: from `html_templates` folder.
35+
"""
36+
# Projects list
37+
dao = DAO(path=resources_dir)
38+
projects = sorted(
39+
dao.get_all(),
40+
key=lambda item: (
41+
-(item.stars or 0),
42+
item.name,
43+
),
44+
)
2245

23-
def __init__(self, root_path: Optional[str] = None):
24-
"""CliWebsite class."""
25-
self.current_dir = root_path or os.path.abspath(os.getcwd())
26-
resources_dir = Path(self.current_dir, "ecosystem/resources")
27-
self.dao = DAO(path=resources_dir)
28-
self.label_descriptions = {
29-
item["name"]: item["description"]
30-
for item in json.loads(Path(resources_dir, "labels.json").read_text())
31-
}
32-
self.web_data = toml.loads((resources_dir / "website.toml").read_text())
33-
34-
def build_website(self):
35-
"""Generates the ecosystem web page reading the TOML files."""
36-
# pylint: disable=too-many-locals
37-
environment = Environment(loader=FileSystemLoader("ecosystem/html_templates/"))
38-
projects = self.dao.storage.read()
39-
projects_sorted = sorted(
40-
projects.items(),
41-
key=lambda item: (
42-
-item[1].stars if item[1].stars is not None else 0,
43-
item[1].name,
44-
),
45-
)
46-
templates = {
47-
"website": environment.get_template("webpage.html.jinja"),
48-
"card": environment.get_template("card.html.jinja"),
49-
"tag": environment.get_template("tag.html.jinja"),
50-
"link": environment.get_template("link.html.jinja"),
51-
}
52-
sections = {group["id"]: group for group in self.web_data["groups"]}
53-
for section in sections.values():
54-
section.setdefault("html", "")
55-
56-
max_chars_description_visible = 400
57-
min_chars_description_hidden = 100
58-
count_read_more = 1
59-
for _, repo in projects_sorted:
60-
# Card tags
61-
tags = ""
62-
for index, label in enumerate(repo.labels):
63-
tags += templates["tag"].render(
64-
color="purple",
65-
text=label,
66-
tooltip=self.label_descriptions[label],
67-
# Sometimes tooltips are clipped by the browser window.
68-
# While not perfect, the following line solves 95% of cases
69-
alignment="bottom" if (index % 3) == 2 else "bottom-left",
70-
)
71-
72-
# Card links
73-
links = ""
74-
for url, link_text in (
75-
(repo.url, "repository"),
76-
(repo.website, "website"),
77-
(repo.reference_paper, "paper"),
78-
(repo.documentation, "documentation"),
79-
):
80-
if url:
81-
links += templates["link"].render(url=url, place=link_text)
82-
83-
# Card description
84-
if (
85-
len(repo.description) - max_chars_description_visible
86-
>= min_chars_description_hidden
87-
):
88-
description = [
89-
repo.description[:max_chars_description_visible],
90-
repo.description[max_chars_description_visible:],
91-
]
92-
id_read_more = str(count_read_more)
93-
count_read_more += 1
94-
else:
95-
description = [repo.description, ""]
96-
id_read_more = "None"
97-
98-
# Create the card
99-
card = templates["card"].render(
100-
title=repo.name,
101-
tags=tags,
102-
description_visible=description[0],
103-
description_hidden=description[1],
104-
id_read_more=id_read_more,
105-
links=links,
46+
# Label descriptions
47+
label_descriptions = {
48+
item["name"]: item["description"]
49+
for item in json.loads(Path(resources_dir, "labels.json").read_text())
50+
}
51+
52+
# Website strings
53+
web_data = toml.loads((resources_dir / "website.toml").read_text())
54+
55+
# Jinja templates
56+
environment = Environment(loader=PackageLoader("ecosystem", "html_templates/"))
57+
templates = {
58+
"website": environment.get_template("webpage.html.jinja"),
59+
"card": environment.get_template("card.html.jinja"),
60+
"tag": environment.get_template("tag.html.jinja"),
61+
"link": environment.get_template("link.html.jinja"),
62+
}
63+
return projects, web_data, label_descriptions, templates
64+
65+
66+
def _build_html(projects, web_data, label_descriptions, templates) -> str:
67+
"""
68+
Take all data needed to build the website and produce a HTML string.
69+
"""
70+
sections = {group["id"]: group for group in web_data["groups"]}
71+
for section in sections.values():
72+
section.setdefault("html", "")
73+
74+
max_chars_description_visible = 400
75+
min_chars_description_hidden = 100
76+
count_read_more = 1
77+
for repo in projects:
78+
# Card tags
79+
tags = ""
80+
for index, label in enumerate(repo.labels):
81+
tags += templates["tag"].render(
82+
color="purple",
83+
text=label,
84+
tooltip=label_descriptions[label],
85+
# Sometimes tooltips are clipped by the browser window.
86+
# While not perfect, the following line solves 95% of cases
87+
alignment="bottom" if (index % 3) == 2 else "bottom-left",
10688
)
10789

108-
# Adding the card to a section
109-
sections[repo.group]["html"] += card
90+
# Card links
91+
links = ""
92+
for url, link_text in (
93+
(repo.url, "repository"),
94+
(repo.website, "website"),
95+
(repo.reference_paper, "paper"),
96+
(repo.documentation, "documentation"),
97+
):
98+
if url:
99+
links += templates["link"].render(url=url, place=link_text)
100+
101+
# Card description
102+
if (
103+
len(repo.description) - max_chars_description_visible
104+
>= min_chars_description_hidden
105+
):
106+
description = [
107+
repo.description[:max_chars_description_visible],
108+
repo.description[max_chars_description_visible:],
109+
]
110+
id_read_more = str(count_read_more)
111+
count_read_more += 1
112+
else:
113+
description = [repo.description, ""]
114+
id_read_more = "None"
110115

111-
return templates["website"].render(
112-
header=self.web_data["header"],
113-
sections=sections.values(),
116+
# Create the card
117+
card = templates["card"].render(
118+
title=repo.name,
119+
tags=tags,
120+
description_visible=description[0],
121+
description_hidden=description[1],
122+
id_read_more=id_read_more,
123+
links=links,
114124
)
125+
126+
# Adding the card to a section
127+
sections[repo.group]["html"] += card
128+
129+
return templates["website"].render(
130+
header=web_data["header"],
131+
sections=sections.values(),
132+
)

ecosystem/daos/dao.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def read(self) -> dict:
4343
{ url (str): repo (Repository) }
4444
"""
4545
data = {}
46-
for path in self.toml_dir.glob("*"):
46+
for path in self.toml_dir.glob("*.toml"):
4747
repo = Repository.from_dict(toml.load(path))
4848
data[repo.url] = repo
4949
return data

manager.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,7 @@
1818
python manager.py website build_website"
1919
```
2020
"""
21-
import fire
22-
23-
from ecosystem.cli import CliMembers, CliWebsite, CliCI
24-
21+
from ecosystem import main
2522

2623
if __name__ == "__main__":
27-
fire.Fire(
28-
{
29-
"members": CliMembers,
30-
"website": CliWebsite,
31-
"ci": CliCI,
32-
}
33-
)
24+
main()

setup.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
setuptools.setup(
1212
name="ecosystem",
1313
description="Ecosystem",
14+
entry_points = {
15+
'console_scripts': ['ecosystem=ecosystem:main'],
16+
},
1417
long_description=long_description,
1518
packages=setuptools.find_packages(),
19+
package_data={"ecosystem": ["html_templates/*.jinja"]},
1620
install_requires=install_requires,
1721
python_requires='>=3.6'
1822
)

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ commands = black {posargs} ecosystem tests --check
3737
allowlist_externals = bash
3838
basepython = python3
3939
commands =
40-
bash -ec "python manager.py website build_website > website/index.html"
40+
bash -ec "python manager.py build --resources ecosystem/resources --output website"

0 commit comments

Comments
 (0)