Skip to content

Instantly share code, notes, and snippets.

@ryan-blunden
Last active May 3, 2019 19:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryan-blunden/0830f71eeccf59bff0e72cac4ae9bcba to your computer and use it in GitHub Desktop.
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.
#!/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