Skip to content

Commit

Permalink
Website/generator (#618)
Browse files Browse the repository at this point in the history
* Crude start substituting header and footer

* Start making generator for framework gallery

* Generate framework gallery

* Generate desktop header without icons

* Add icons

* Add mobile navigation bar

* Add source

* Add parsing of paper information

* Towards generating the frameworks page

* Switch to using string Templates

* Add data for first few frameworks

* Add GAMA, H2O, FLAML

* Fix typos

* Improve typing, efficiency

* Read template from main instead of inside function

* Separate file loading from parsing

* Fix bug that would invert mobile and desktop navigation

* Add summaries and documentation links

* Sort frameworks alphabetically

* Add TPOT paper

* Change expected location of stylesheet

* Do not rebuild mkdocs on website change

* Add generate static pages step
  • Loading branch information
PGijsbers authored Oct 6, 2024
1 parent 8fd39e1 commit 16091f9
Show file tree
Hide file tree
Showing 18 changed files with 899 additions and 2 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/build_deploy_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ concurrency:
cancel-in-progress: false

jobs:
# Single deploy job since we're just deploying
# TODO: Break up into separate jobs: build website, build docs, deploy
deploy:
environment:
name: github-pages
Expand All @@ -49,13 +49,19 @@ jobs:
sed -i "s@WEBSITE@${WEBSITE_URL}@g" mkdocs.yml
- uses: actions/setup-python@v4
with:
python-version: '3.11'
python-version: '3.12'
- name: Install MkDocs
run: |
python -m pip install mkdocs-material
- name: Build MkDocs Pages
run: |
mkdocs build --site-dir site/docs
- name: Build Website Pages
run: |
cd ./site/scripts
python generate_index.py
cp index.html ../
cp frameworks.html ../
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
Expand Down
4 changes: 4 additions & 0 deletions docs/website/img/icons/book-open.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/website/img/icons/file-chart-bar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/website/img/icons/file-lines.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/website/img/icons/github.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/website/img/icons/heart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/website/img/icons/home.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions docs/website/img/icons/messages.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/website/img/icons/swatchbook.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
138 changes: 138 additions & 0 deletions docs/website/scripts/generate_index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
from pathlib import Path
from string import Template

import tomllib
from typing import NamedTuple, Sequence, Iterable

from generate_navigation import generate_navigation


class Paper(NamedTuple):
title: str
abstract: str
pdf: str
venue: str
year: int
authors: str
arxiv: str | None = None


class Framework(NamedTuple):
name: str
repository: str
icon: str
summary: str
papers: Sequence[Paper]
documentation: str = None


def load_framework_definitions(definition_file: Path = Path("official_frameworks.toml")) -> list[Framework]:
with definition_file.open("rb") as fh:
frameworks = tomllib.load(fh)["frameworks"]
frameworks = parse_frameworks(frameworks)
return sorted(frameworks, key=lambda fw: fw.name.lower())

def parse_frameworks(framework_descriptions):
return [
Framework(
**{attr: val for attr, val in fw.items() if attr != "papers"},
papers=tuple(Paper(**paper) for paper in fw.get("papers", [])),
)
for fw in framework_descriptions
]


def load_navigation() -> str:
return generate_navigation()


def load_footer() -> str:
with open("templates/footer.html", "r") as f:
return f.read()


def generate_framework_gallery(frameworks: Iterable[Framework]) -> str:
template = Template("""
<a href=\"${repository}\" target="_blank" class="framework-logo">
<img src=\"${icon}\" title=\"${name}\"/>
</a>
""")
framework_icon_html = [
template.substitute(fw._asdict())
for fw in frameworks
]
return "\n".join(framework_icon_html)


def generate_main_page(frameworks: Iterable[Framework], template: Template) -> str:
header = load_navigation()
footer = load_footer()

framework_gallery = generate_framework_gallery(frameworks)
return template.substitute(
**dict(
navigation=header,
footer=footer,
framework_gallery=framework_gallery,
)
)


def generate_framework_list(
frameworks: Sequence[Framework],
framework_card_template: Template,
framework_paper_template: Template,
) -> str:
framework_cards = []
for framework in frameworks:
paper_list = "\n".join(
framework_paper_template.substitute(paper._asdict())
for paper in framework.papers
)
framework_card = framework_card_template.substitute(
framework._asdict() | {"paper_list": paper_list},
)
framework_cards.append(framework_card)

return "\n".join(framework_cards)


def generate_framework_page(frameworks: Sequence[Framework]) -> str:
navigation = load_navigation()
footer = load_footer()

with open("templates/frameworks_template.html", "r") as f:
main_content = Template(f.read())

with open("templates/framework_card_template.html", "r") as f:
framework_card_template = Template(f.read())

with open("templates/framework_card_paper_template.html") as f:
framework_paper_template = Template(f.read())

framework_cards = generate_framework_list(
frameworks,
framework_card_template,
framework_paper_template,

)

main_content = main_content.substitute(**dict(
navigation=navigation,
footer=footer,
framework_cards=framework_cards,
)
)

return main_content


if __name__ == "__main__":
main_page_template = Template(Path("templates/index_template.html").read_text())
frameworks = load_framework_definitions()

main_html = generate_main_page(frameworks, main_page_template)
Path("index.html").write_text(main_html)

framework_html = generate_framework_page(frameworks)
Path("frameworks.html").write_text(framework_html)
55 changes: 55 additions & 0 deletions docs/website/scripts/generate_navigation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from string import Template

import tomllib
from pathlib import Path
from typing import NamedTuple, Iterable


class NavigationItem(NamedTuple):
name: str
url: str
icon: str
icon_only: bool = False


def generate_navigation_for(items: Iterable[NavigationItem], mobile: bool = False) -> str:
# TODO: Add outlink icon
item_template = Template("<a href=\"${url}\" class=\"nav-link nav-icon\">${name_or_icon}</a>")
html_items = []
for item in items:
nav_html = generate_nav_item_html(item, item_template, mobile)
html_items.append(nav_html)

items_html = "\n".join(html_items)
classes = "mobile nav-mobile" if mobile else "desktop"
return f"""
<nav class="navigation-bar {classes}">
{items_html}
</nav>
"""


def generate_nav_item_html(item, item_template, mobile: bool):
requires_icon = mobile or item.icon_only
if not requires_icon:
return item_template.substitute(url=item.url, name_or_icon=item.name)

with open(item.icon, "r") as fh:
icon = fh.read()
return item_template.substitute(url=item.url, name_or_icon=icon)


def load_navigation_definitions(configuration_file: Path = Path("navigation.toml")) -> list[NavigationItem]:
with configuration_file.open("rb") as fh:
items = tomllib.load(fh)["pages"]
return [NavigationItem(**item) for item in items]


def generate_navigation() -> str:
items = load_navigation_definitions()
desktop_navigation = generate_navigation_for(items)
mobile_navigation = generate_navigation_for(items, mobile=True)
return desktop_navigation + mobile_navigation



41 changes: 41 additions & 0 deletions docs/website/scripts/navigation.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[[pages]]
name = "home" # Text displayed in desktop version of navbar header
icon = "../img/icons/home.svg" # relative path to an (.svg) icon that's displayed for the mobile version
url = "index.html" # (relative) link to the page it should link to. External links will have a little icon showing it's an out-link.
icon_only = true # If set, never show the text of `name` but show the icon even in desktop mode (default: false).

[[pages]]
name = "results"
icon = "../img/icons/file-chart-bar.svg"
url = "results.html"

[[pages]]
name = "frameworks"
icon = "../img/icons/swatchbook.svg"
url = "frameworks.html"

[[pages]]
name = "papers"
icon = "../img/icons/file-lines.svg"
url = "papers.html"

[[pages]]
name = "user guide"
icon = "../img/icons/book-open.svg"
url = "user_guide.html"

[[pages]]
name = "contributing"
icon = "../img/icons/heart.svg"
url = "contributing.html"

[[pages]]
name = "discussion"
icon = "../img/icons/messages.svg"
url = "https://github.com/openml/automlbenchmark/discussions/216"

[[pages]]
name = "Github"
icon = "../img/icons/github.svg"
url = "https://github.com/openml/automlbenchmark/"
icon_only = true
Loading

0 comments on commit 16091f9

Please sign in to comment.