Instantly share code, notes, and snippets.
Last active
May 3, 2019 19:35
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save ryan-blunden/0830f71eeccf59bff0e72cac4ae9bcba to your computer and use it in GitHub Desktop.
WIP Python script for generating the nav for docs.sourcegraph.com. Once fully baked, it will be ported to Go and greatly improved.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from __future__ import annotations | |
from dataclasses import dataclass, field | |
import inspect | |
import os | |
import re | |
import sys | |
from typing import List | |
from frontmatter import Frontmatter | |
def log(message: str): | |
open('log.txt', 'a').write('{}: {}\n'.format(inspect.currentframe().f_back.f_lineno, message)) | |
def log_flush(): | |
open('log.txt', 'w').write('') | |
# TODO: Use regex, something like : list(filter(re.compile(FILE_EXCLUDE).match, ['phabricator.schema.json'])) | |
FILE_EXCLUDE = ['.DS_Store', '.mypy_cache', 'index.md', 'img', 'aws_codecommit.schema.json', | |
'other_external_service.schema.json', 'bitbucket_server.schema.json', 'phabricator.schema.json', | |
'gitolite.schema.json', 'github.schema.json', 'gitlab.schema.json', 'critical.schema.json', | |
'site.schema.json'] | |
DIR_EXCLUDE = ['.idea', '_resources'] | |
SECTION_TEMPLATE = '''<ul class="content-nav-section" data-nav-section="{section}"> | |
<li> | |
<a class="content-nav-section-header" href="/{section}" title="{section_title}">{section_nav_text}</a> | |
</li> | |
</ul> | |
''' | |
f = ''''<ul class="content-nav-section" data-nav-section="{section}"> | |
<li class="content-nav-no-hover"> | |
<a class="content-nav-section-header" href="/{section}" title="{section_title}">{section_nav_text}</a> | |
<ul class="content-nav-section-group"> | |
{section_item} | |
</ul> | |
</li> | |
</ul>''' | |
EXPANDABLE_SECTION_TEMPLATE = '''<ul class="content-nav-section expandable" data-nav-section="{section}"> | |
<button class="content-nav-button"></button> | |
<div class="close--icon"> | |
<svg class="mdi-icon material-icons" width="24" height="24" fill="#000" viewBox="0 0 24 24"> | |
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"></path> | |
</svg> | |
</div> | |
<li class="content-nav-no-hover"> | |
<a class="content-nav-section-header" href="/{section}" title="{section_title}">{section_nav_text}</a> | |
<ul class="content-nav-section-group"> | |
{section_group_items} | |
</ul> | |
</li> | |
</ul>''' | |
SECTION_GROUP_ITEM_TEMPLATE = '''<li><a href="{section_group_item_url}">{section_group_item_title}</a></li> | |
<li class="content-nav-no-hover"> | |
<ul class="content-nav-section-subsection"> | |
{section_group_items} | |
</ul> | |
</li>''' | |
SUB_SECTION_TEMPLATE = '''<li><a href="{section_group_subsection_item_url}">{section_group_subsection_item_title}</a></li> | |
<li class="content-nav-no-hover" data-sub-section-item="{section_group_subsection_item_title}")> | |
<ul> | |
{section_group_subsection_items} | |
</ul> | |
</li>''' | |
class Page: | |
path: str | |
file: str | |
title: str | |
url: str | |
nav_text: str | |
nav_show: bool | |
nav_weight: int | |
def __init__(self, path: str): | |
self.path = path | |
with open(self.path, 'r') as f: | |
self.file = f.read() | |
self.title = self._get_title() | |
self.url = self._get_url() | |
meta = self._get_meta() | |
self.nav_text = meta.get('navText', self.title) | |
self.nav_show = meta.get('navShow', True) | |
self.nav_weight = meta.get('navWeight', 0) | |
def _get_title(self) -> str: | |
for line in self.file.split('\n'): | |
try: | |
return re.search('^# (.*)$', line).groups()[0] | |
except Exception: | |
continue | |
# Sentence case last path segment as a last resort | |
return ' '.join(self.path.split('/')[-1].replace('-', '_').split('_')).capitalize() | |
def _get_url(self): | |
return '/{}'.format(self.path.replace('.md', '')) | |
def _get_meta(self): | |
try: | |
return Frontmatter.read(self.file)['attributes'] | |
except Exception: | |
return {} | |
@dataclass | |
class MenuItem: | |
title: str | |
nav_text: str | |
nav_weight: int | |
content: str | |
@dataclass | |
class MenuItems: | |
items: List[MenuItem] = field(default_factory=list) | |
def add(self, item: MenuItem): | |
if type(item) == str: | |
raise Exception | |
self.items.append(item) | |
def sorted(self) -> List[MenuItem]: | |
return sorted(self.items, key=lambda menu_item: (-menu_item.nav_weight, menu_item.nav_text)) | |
@property | |
def count(self) -> int: | |
return len(self.items) | |
@property | |
def contents(self) -> List[str]: | |
return [item.content for item in self.sorted()] | |
def get_sections(): | |
sections = MenuItems() | |
# Seed with home | |
home_page = Page(path='./index.md') | |
sections.add( | |
MenuItem( | |
title=home_page.title, | |
nav_text=home_page.nav_text, | |
nav_weight=home_page.nav_weight, | |
content=SECTION_TEMPLATE.format( | |
section='', | |
section_title=home_page.title, | |
section_nav_text=home_page.nav_text | |
) | |
) | |
) | |
for section in os.listdir('./'): | |
if not os.path.isdir(section) or section in DIR_EXCLUDE: | |
continue | |
page = Page(path='{}/index.md'.format(section)) | |
if page.nav_show is False: | |
continue | |
sections.add( | |
MenuItem( | |
title=page.title, | |
nav_text=page.nav_text, | |
nav_weight=page.nav_weight, | |
content=EXPANDABLE_SECTION_TEMPLATE.format( | |
section=section, | |
section_title=page.title, | |
section_nav_text=page.nav_text, | |
section_group_items='' | |
# section_group_items=get_section_items(section).content | |
) | |
) | |
) | |
return '\n'.join(sections.contents) | |
def get_title(f): | |
pass | |
def file_path_to_url(f): | |
pass | |
def make_menu_item(url, text): | |
pass | |
def get_section_items(section): | |
section_items = sections = MenuItems() | |
section_title = get_title('{}/index.md'.format(section)) | |
for section_item in os.listdir(section): | |
if section_item in FILE_EXCLUDE or section_item in DIR_EXCLUDE: | |
continue | |
section_item_file_path = '{}/{}'.format(section, section_item) | |
if os.path.isdir(section_item_file_path): | |
section_items.add( | |
get_section_group_item(section, section_item, section_item_file_path) | |
) | |
continue | |
title = get_title(section_item_file_path) | |
section_items.add( | |
MenuItem( | |
title=title, | |
content=make_menu_item(file_path_to_url(section_item_file_path), title) | |
) | |
) | |
return MenuItem( | |
title=section_title, | |
content='\n'.join(section_items.contents) | |
) | |
def get_section_group_item(section, section_item, url) -> str: | |
section_group_items = MenuItems() | |
section_group_item_file_path = '{}/{}'.format(section, section_item, section_item) | |
section_group_item_title = get_title('{}/index.md'.format(section_group_item_file_path)) | |
section_group_item_url = file_path_to_url(section_group_item_file_path) | |
for section_group_item_child in os.listdir('{}/{}'.format(section, section_item)): | |
if section_group_item_child in FILE_EXCLUDE: | |
continue | |
section_group_item_child_path = '{}/{}/{}'.format(section, section_item, section_group_item_child) | |
section_group_item_child_url = file_path_to_url(section_group_item_child_path) | |
if os.path.isdir(section_group_item_child_path): | |
section_group_items.add( | |
get_section_group_subsection_item(section, section_item, section_group_item_child) | |
) | |
continue | |
else: | |
section_group_item_child_title = get_title(section_group_item_child_path) | |
section_group_items.add( | |
MenuItem( | |
title=section_group_item_child_title, | |
content=make_menu_item(section_group_item_child_url, section_group_item_child_title) | |
) | |
) | |
if section_group_items.count <= 1: | |
return MenuItem( | |
title=section_group_item_title, | |
content=make_menu_item(section_group_item_url, section_group_item_title) | |
) | |
return MenuItem( | |
title=section_group_item_title, | |
content=SECTION_GROUP_ITEM_TEMPLATE.format( | |
section_group_item_title=section_group_item_title, | |
section_group_item_url=section_group_item_url, | |
section_group_items='\n'.join(section_group_items.contents) | |
) | |
) | |
def get_section_group_subsection_item(section, section_item, section_group_item_child) -> str: | |
section_group_subsection_items = MenuItems() | |
section_group_subsection_item_file_path = '{}/{}/{}'.format(section, section_item, section_group_item_child) | |
section_group_subsection_item_title = get_title('{}/index.md'.format(section_group_subsection_item_file_path)) | |
section_group_subsection_item_url = file_path_to_url(section_group_subsection_item_file_path) | |
for section_group_subsection_item_child in os.listdir( | |
'{}/{}/{}'.format(section, section_item, section_group_item_child)): | |
section_group_subsection_item_child_path = '{}/{}/{}/{}'.format(section, section_item, section_group_item_child, | |
section_group_subsection_item_child) | |
section_group_subsection_item_child_url = file_path_to_url(section_group_subsection_item_child_path) | |
if section_group_subsection_item_child in FILE_EXCLUDE or os.path.isdir( | |
section_group_subsection_item_child_path): | |
continue | |
section_group_subsection_item_child_title = get_title(section_group_subsection_item_child_path) | |
section_group_subsection_items.add( | |
MenuItem( | |
title=section_group_subsection_item_child_title, | |
content=make_menu_item(section_group_subsection_item_child_url, | |
get_title(section_group_subsection_item_child_path)) | |
) | |
) | |
return MenuItem( | |
title=section_group_subsection_item_title, | |
content=SUB_SECTION_TEMPLATE.format( | |
section_group_subsection_item_url=section_group_subsection_item_url, | |
section_group_subsection_item_title=section_group_subsection_item_title, | |
section_group_subsection_items='\n'.join(section_group_subsection_items.contents) | |
)) | |
if __name__ == '__main__': | |
log_flush() | |
sys.stdout.write(get_sections()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment