|
1 | 1 | """CliWebsite class for controlling all CLI functions."""
|
| 2 | +from __future__ import annotations |
| 3 | + |
| 4 | + |
2 | 5 | from pathlib import Path
|
3 |
| -from typing import Optional |
| 6 | +from typing import Any |
4 | 7 | import json
|
5 |
| -import os |
6 | 8 | import toml
|
7 | 9 |
|
8 |
| -from jinja2 import Environment, FileSystemLoader |
| 10 | +from jinja2 import Environment, PackageLoader, Template |
9 | 11 |
|
10 | 12 | from ecosystem.daos import DAO
|
| 13 | +from ecosystem.models.repository import Repository |
11 | 14 |
|
12 | 15 |
|
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) |
16 | 23 |
|
17 |
| - Each public method of this class is CLI command |
18 |
| - and arguments for method are options/flags for this command. |
19 | 24 |
|
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]]: |
21 | 28 | """
|
| 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 | + ) |
22 | 45 |
|
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", |
106 | 88 | )
|
107 | 89 |
|
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" |
110 | 115 |
|
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, |
114 | 124 | )
|
| 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 | + ) |
0 commit comments