Added support for static doctest files generated by the iwmsite static site generator.
This commit is contained in:
parent
0cff31e65b
commit
13e0473328
4
.gitignore
vendored
4
.gitignore
vendored
@ -83,4 +83,6 @@ typings/
|
||||
# ignore generated contents-
|
||||
/doc/out/*
|
||||
**/thumbnails
|
||||
**/thumbnail.png
|
||||
**/thumbnail.png
|
||||
/site/dist
|
||||
/site/__pycache__
|
249
doc/ast.html
249
doc/ast.html
@ -1,30 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="utf-8" />
|
||||
<style>
|
||||
.node circle {
|
||||
fill: #999;
|
||||
}
|
||||
|
||||
.node circle {
|
||||
fill: #999;
|
||||
}
|
||||
.node text {
|
||||
font: 10px sans-serif;
|
||||
}
|
||||
|
||||
.node text {
|
||||
font: 10px sans-serif;
|
||||
}
|
||||
.node--internal circle {
|
||||
fill: #555;
|
||||
}
|
||||
|
||||
.node--internal circle {
|
||||
fill: #555;
|
||||
}
|
||||
|
||||
.node--internal text {
|
||||
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
|
||||
}
|
||||
|
||||
.link {
|
||||
fill: none;
|
||||
stroke: #555;
|
||||
stroke-opacity: 0.4;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
.node--internal text {
|
||||
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
|
||||
}
|
||||
|
||||
.link {
|
||||
fill: none;
|
||||
stroke: #555;
|
||||
stroke-opacity: 0.4;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
</style>
|
||||
<svg width="960" height="512"></svg>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||
@ -32,104 +30,106 @@
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.7.1/d3.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/4.0.11/acorn.min.js"></script>
|
||||
<script>
|
||||
fetch('../apps/loader/js/main.js')
|
||||
.then((response) => response.text())
|
||||
.then((text) => {
|
||||
const ast = acorn.parse(text, {
|
||||
sourceType: 'module',
|
||||
// collect ranges for each node
|
||||
ranges: true,
|
||||
// collect comments in Esprima's format
|
||||
onComment: null,
|
||||
// collect token ranges
|
||||
onToken: null
|
||||
})
|
||||
|
||||
fetch("../apps/loader/js/main.js")
|
||||
.then(response => response.text())
|
||||
.then(text => {
|
||||
const ast = acorn.parse(text, {
|
||||
sourceType: "module",
|
||||
// collect ranges for each node
|
||||
ranges: true,
|
||||
// collect comments in Esprima's format
|
||||
onComment: null,
|
||||
// collect token ranges
|
||||
onToken: null
|
||||
})
|
||||
|
||||
console.info(ast)
|
||||
console.info(ast)
|
||||
|
||||
drawAst(ast)
|
||||
})
|
||||
drawAst(ast)
|
||||
})
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
function drawAst(ast) {
|
||||
|
||||
// Create SVG element
|
||||
//---------------------------
|
||||
let svg = d3.select("svg")
|
||||
const width = svg.attr("width")
|
||||
const height = svg.attr("height")
|
||||
let g = svg.append("g").attr("transform", "translate(40,0)")
|
||||
|
||||
// Convert data
|
||||
//---------------------------
|
||||
const data = acornToHierarchy(ast)
|
||||
|
||||
// Create D3 Hierarchy
|
||||
//---------------------------
|
||||
let root = d3.hierarchy(data).sort((a, b) => {
|
||||
return (a.height - b.height) || a.data.name.localeCompare(b.data.name);
|
||||
})
|
||||
/*
|
||||
*
|
||||
*/
|
||||
function drawAst(ast) {
|
||||
// Create SVG element
|
||||
//---------------------------
|
||||
let svg = d3.select('svg')
|
||||
const width = svg.attr('width')
|
||||
const height = svg.attr('height')
|
||||
let g = svg.append('g').attr('transform', 'translate(40,0)')
|
||||
|
||||
// Create D3 Cluster
|
||||
//---------------------------
|
||||
let tree = d3.cluster().size([height, width - 200])
|
||||
tree(root)
|
||||
|
||||
// Create SVG elements
|
||||
//---------------------------
|
||||
let link = g.selectAll(".link")
|
||||
.data(root.descendants().slice(1))
|
||||
.enter().append("path")
|
||||
.attr("class", "link")
|
||||
.attr("d", d => {
|
||||
return `M${d.y},${d.x}C${d.parent.y + 100},${d.x} ${d.parent.y + 100},${d.parent.x} ${d.parent.y},${d.parent.x}`
|
||||
})
|
||||
|
||||
let node = g.selectAll(".node")
|
||||
.data(root.descendants())
|
||||
.enter().append("g")
|
||||
.attr("class", d => "node" + (d.children ? " node--internal" : " node--leaf"))
|
||||
.attr("transform", d => `translate(${d.y},${d.x})`)
|
||||
|
||||
node.append("circle")
|
||||
.attr("r", 5)
|
||||
|
||||
node.append("text")
|
||||
.attr("dy", 3)
|
||||
.attr("x", d => d.children ? -8 : 8)
|
||||
.style("text-anchor", d => d.children ? "end" : "start")
|
||||
.text(d => d.data.name)
|
||||
}
|
||||
// Convert data
|
||||
//---------------------------
|
||||
const data = acornToHierarchy(ast)
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
function acornToHierarchy(ast) {
|
||||
|
||||
console.info(JSON.stringify(ast))
|
||||
|
||||
let data = {}
|
||||
|
||||
for (const clazz of ast.body) {
|
||||
if (clazz.type === "ClassDeclaration") {
|
||||
data.name = clazz.id.name
|
||||
data.children = []
|
||||
|
||||
for (const method of clazz.body.body) {
|
||||
if (method.type === "MethodDefinition") {
|
||||
data.children.push({
|
||||
name: method.key.name,
|
||||
children: []
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Create D3 Hierarchy
|
||||
//---------------------------
|
||||
let root = d3.hierarchy(data).sort((a, b) => {
|
||||
return a.height - b.height || a.data.name.localeCompare(b.data.name)
|
||||
})
|
||||
|
||||
// Create D3 Cluster
|
||||
//---------------------------
|
||||
let tree = d3.cluster().size([height, width - 200])
|
||||
tree(root)
|
||||
|
||||
// Create SVG elements
|
||||
//---------------------------
|
||||
let link = g
|
||||
.selectAll('.link')
|
||||
.data(root.descendants().slice(1))
|
||||
.enter()
|
||||
.append('path')
|
||||
.attr('class', 'link')
|
||||
.attr('d', (d) => {
|
||||
return `M${d.y},${d.x}C${d.parent.y + 100},${d.x} ${d.parent.y + 100},${d.parent.x} ${d.parent.y},${
|
||||
d.parent.x
|
||||
}`
|
||||
})
|
||||
|
||||
let node = g
|
||||
.selectAll('.node')
|
||||
.data(root.descendants())
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', (d) => 'node' + (d.children ? ' node--internal' : ' node--leaf'))
|
||||
.attr('transform', (d) => `translate(${d.y},${d.x})`)
|
||||
|
||||
node.append('circle').attr('r', 5)
|
||||
|
||||
node.append('text')
|
||||
.attr('dy', 3)
|
||||
.attr('x', (d) => (d.children ? -8 : 8))
|
||||
.style('text-anchor', (d) => (d.children ? 'end' : 'start'))
|
||||
.text((d) => d.data.name)
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
function acornToHierarchy(ast) {
|
||||
console.info(JSON.stringify(ast))
|
||||
|
||||
let data = {}
|
||||
|
||||
for (const clazz of ast.body) {
|
||||
if (clazz.type === 'ClassDeclaration') {
|
||||
data.name = clazz.id.name
|
||||
data.children = []
|
||||
|
||||
for (const method of clazz.body.body) {
|
||||
if (method.type === 'MethodDefinition') {
|
||||
data.children.push({
|
||||
name: method.key.name,
|
||||
children: []
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
const data = {
|
||||
"name": "Eve",
|
||||
"children": [{
|
||||
@ -153,22 +153,7 @@ function acornToHierarchy(ast) {
|
||||
}]
|
||||
}
|
||||
*/
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
return data
|
||||
}
|
||||
</script>
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>App</title>
|
||||
<link rel="stylesheet" href="./3rdparty/highlight/styles/default.css">
|
||||
<link rel="stylesheet" href="../css/doctest.css">
|
||||
<script src="./3rdparty/highlight/highlight.pack.js"></script>
|
||||
|
1
site/.python-version
Normal file
1
site/.python-version
Normal file
@ -0,0 +1 @@
|
||||
3.10.1
|
45
site/main.py
Normal file
45
site/main.py
Normal file
@ -0,0 +1,45 @@
|
||||
import pathlib
|
||||
from ssg.tags import *
|
||||
from ssg.utils import *
|
||||
from markup import iwmlibdoc_document, sitemap_main
|
||||
|
||||
src = pathlib.Path('..')
|
||||
content = src / 'lib'
|
||||
|
||||
async def main(path:pathlib.Path):
|
||||
"""Typical main program, setting up content publishers, defining the site structure and
|
||||
typical order of generating initial pages and observing subsequent changes.
|
||||
"""
|
||||
|
||||
# 2. Build the site structure with nested context managers
|
||||
with site('iwmlibdoc', path=path, doc=iwmlibdoc_document) as docsite:
|
||||
# If we use symlinks the css and image files can be modified without
|
||||
# notifications.
|
||||
docsite.symlink(src, 'css')
|
||||
docsite.symlink(src, 'assets')
|
||||
docsite.symlink(src, 'dist')
|
||||
# structures can be created in a loop to ensure indentical hierarchies
|
||||
|
||||
sitemap = page('sitemap.html',
|
||||
title='Sitemap',
|
||||
main=sitemap_main)
|
||||
static_folder(content)
|
||||
|
||||
# 3. Start generation.
|
||||
docsite.generate()
|
||||
|
||||
#print(docsite)
|
||||
# 4. Let's check whether all links are working...
|
||||
# site.validate()
|
||||
|
||||
await docsite.observe_filesystem(timeout=120)
|
||||
|
||||
def run():
|
||||
default = pathlib.Path('.') / 'dist'
|
||||
if default.exists():
|
||||
shutil.rmtree(default)
|
||||
default.mkdir(parents=True)
|
||||
asyncio.run(main(default))
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
107
site/markup.py
Normal file
107
site/markup.py
Normal file
@ -0,0 +1,107 @@
|
||||
|
||||
from typing import Dict, List
|
||||
import ssg.document
|
||||
import ssg.utils
|
||||
from ssg.tags import *
|
||||
from dominate.tags import *
|
||||
|
||||
def iwmlibdoc_document(context):
|
||||
doc = ssg.document.document(title=context.title)
|
||||
with doc.head:
|
||||
meta(charset="utf-8")
|
||||
attr(lang=context.language())
|
||||
if isinstance(context, static_page):
|
||||
for tag in context.soup.find_all('link'):
|
||||
if href := tag.attrs.get('href'):
|
||||
link(rel="stylesheet", href=href)
|
||||
|
||||
for tag in context.soup.find_all('script'):
|
||||
if src := tag.attrs.get('src'):
|
||||
script(src=src)
|
||||
else:
|
||||
link(rel="stylesheet", href= "css/doctest.css")
|
||||
link(rel="stylesheet", href= "css/demo.css")
|
||||
|
||||
with doc.body:
|
||||
site_navigation(context)
|
||||
ssg.document.placeholder(key='main', alternatives=['body']) # here goes the special content part
|
||||
site_footer(context)
|
||||
return doc
|
||||
|
||||
def site_navigation(context):
|
||||
mainmenu(context)
|
||||
br()
|
||||
br()
|
||||
breadcrumb(context)
|
||||
hr()
|
||||
|
||||
|
||||
def level_menu(context, root):
|
||||
for name, child in sorted(context.named_children.items()):
|
||||
if child.folderish():
|
||||
if index := child.named_children.get('index.html'):
|
||||
url = '.' + index.url(relative=root)
|
||||
yield dict(url=url, title=index.title)
|
||||
|
||||
if child.title == name:
|
||||
continue
|
||||
if name.endswith('.html'):
|
||||
url = '.' + child.url(relative=root)
|
||||
yield dict(url=url, title=child.title)
|
||||
|
||||
|
||||
def main_menu(context):
|
||||
root = context.menuroot()
|
||||
if context == root:
|
||||
for info in level_menu(root, root):
|
||||
yield info
|
||||
else:
|
||||
yield dict(url= context.up() + 'sitemap.html', title="Home")
|
||||
for info in level_menu(context.parent, root):
|
||||
yield info
|
||||
|
||||
@nav(cls="breadcrumb")
|
||||
def breadcrumb(context):
|
||||
"""
|
||||
{% macro breadcrumb(context) -%}
|
||||
<nav class="breadcrumb">
|
||||
{% for info in context.breadcrumb() %}
|
||||
{% if info.url %}
|
||||
<a href="{{info.url}}">{{info.title}}</a>
|
||||
{% else %}
|
||||
<a href="#"><u>{{info.title}}</u></a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</nav>
|
||||
{% endmacro %}
|
||||
"""
|
||||
for info in context.breadcrumb():
|
||||
if url := info.get('url'):
|
||||
a(info['title'], href=url)
|
||||
else:
|
||||
a(info['title'], href='#')
|
||||
|
||||
@nav(cls="mainmenu")
|
||||
def mainmenu(context:ssg.generator):
|
||||
for info in main_menu(context):
|
||||
if url := info.get('url'):
|
||||
a(info['title'], href=url)
|
||||
else:
|
||||
a(info['title'], href='#')
|
||||
|
||||
@main
|
||||
def sitemap_main(context:ssg.generator):
|
||||
root = context.menuroot()
|
||||
h3("Sitemap")
|
||||
with ul(id="sitemap"):
|
||||
for info in getsite().page_infos(relative=root):
|
||||
url = info['url']
|
||||
if url.endswith('.html'):
|
||||
li(a(info['title'], href=url))
|
||||
|
||||
@footer
|
||||
def site_footer(context):
|
||||
hr()
|
||||
date = ssg.utils.now('en')
|
||||
p(f'Generated by IWMSite {date}')
|
||||
|
21
site/requirements.txt
Normal file
21
site/requirements.txt
Normal file
@ -0,0 +1,21 @@
|
||||
aiohttp
|
||||
dominate
|
||||
pytest
|
||||
Babel
|
||||
mypy
|
||||
beautifulsoup4
|
||||
python-socketio
|
||||
requests
|
||||
watchdog
|
||||
|
||||
# streamlit apps
|
||||
streamlit==1.4.0
|
||||
openpyxl
|
||||
plotly
|
||||
fpdf
|
||||
|
||||
# architecture overview
|
||||
diagrams
|
||||
|
||||
# ssg
|
||||
git+ssh://git@gitea.iwm-tuebingen.de/Medienentwicklung/iwmsite.git@master
|
Loading…
Reference in New Issue
Block a user