Added support for static doctest files generated by the iwmsite static site generator.

This commit is contained in:
Uwe Oestermeier 2023-05-09 09:52:39 +02:00
parent 0cff31e65b
commit 13e0473328
7 changed files with 295 additions and 133 deletions

2
.gitignore vendored
View File

@ -84,3 +84,5 @@ typings/
/doc/out/* /doc/out/*
**/thumbnails **/thumbnails
**/thumbnail.png **/thumbnail.png
/site/dist
/site/__pycache__

View File

@ -1,30 +1,28 @@
<!DOCTYPE html> <!DOCTYPE html>
<meta charset="utf-8"> <meta charset="utf-8" />
<style> <style>
.node circle {
.node circle {
fill: #999; fill: #999;
} }
.node text { .node text {
font: 10px sans-serif; font: 10px sans-serif;
} }
.node--internal circle { .node--internal circle {
fill: #555; fill: #555;
} }
.node--internal text { .node--internal text {
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff; text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
} }
.link { .link {
fill: none; fill: none;
stroke: #555; stroke: #555;
stroke-opacity: 0.4; stroke-opacity: 0.4;
stroke-width: 1.5px; stroke-width: 1.5px;
} }
</style> </style>
<svg width="960" height="512"></svg> <svg width="960" height="512"></svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
@ -32,12 +30,11 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.7.1/d3.min.js"></script> <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 src="https://cdnjs.cloudflare.com/ajax/libs/acorn/4.0.11/acorn.min.js"></script>
<script> <script>
fetch('../apps/loader/js/main.js')
fetch("../apps/loader/js/main.js") .then((response) => response.text())
.then(response => response.text()) .then((text) => {
.then(text => {
const ast = acorn.parse(text, { const ast = acorn.parse(text, {
sourceType: "module", sourceType: 'module',
// collect ranges for each node // collect ranges for each node
ranges: true, ranges: true,
// collect comments in Esprima's format // collect comments in Esprima's format
@ -51,17 +48,16 @@ fetch("../apps/loader/js/main.js")
drawAst(ast) drawAst(ast)
}) })
/* /*
* *
*/ */
function drawAst(ast) { function drawAst(ast) {
// Create SVG element // Create SVG element
//--------------------------- //---------------------------
let svg = d3.select("svg") let svg = d3.select('svg')
const width = svg.attr("width") const width = svg.attr('width')
const height = svg.attr("height") const height = svg.attr('height')
let g = svg.append("g").attr("transform", "translate(40,0)") let g = svg.append('g').attr('transform', 'translate(40,0)')
// Convert data // Convert data
//--------------------------- //---------------------------
@ -70,7 +66,7 @@ function drawAst(ast) {
// Create D3 Hierarchy // Create D3 Hierarchy
//--------------------------- //---------------------------
let root = d3.hierarchy(data).sort((a, b) => { let root = d3.hierarchy(data).sort((a, b) => {
return (a.height - b.height) || a.data.name.localeCompare(b.data.name); return a.height - b.height || a.data.name.localeCompare(b.data.name)
}) })
// Create D3 Cluster // Create D3 Cluster
@ -80,46 +76,50 @@ function drawAst(ast) {
// Create SVG elements // Create SVG elements
//--------------------------- //---------------------------
let link = g.selectAll(".link") let link = g
.selectAll('.link')
.data(root.descendants().slice(1)) .data(root.descendants().slice(1))
.enter().append("path") .enter()
.attr("class", "link") .append('path')
.attr("d", d => { .attr('class', 'link')
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}` .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") let node = g
.selectAll('.node')
.data(root.descendants()) .data(root.descendants())
.enter().append("g") .enter()
.attr("class", d => "node" + (d.children ? " node--internal" : " node--leaf")) .append('g')
.attr("transform", d => `translate(${d.y},${d.x})`) .attr('class', (d) => 'node' + (d.children ? ' node--internal' : ' node--leaf'))
.attr('transform', (d) => `translate(${d.y},${d.x})`)
node.append("circle") node.append('circle').attr('r', 5)
.attr("r", 5)
node.append("text") node.append('text')
.attr("dy", 3) .attr('dy', 3)
.attr("x", d => d.children ? -8 : 8) .attr('x', (d) => (d.children ? -8 : 8))
.style("text-anchor", d => d.children ? "end" : "start") .style('text-anchor', (d) => (d.children ? 'end' : 'start'))
.text(d => d.data.name) .text((d) => d.data.name)
} }
/* /*
* *
*/ */
function acornToHierarchy(ast) { function acornToHierarchy(ast) {
console.info(JSON.stringify(ast)) console.info(JSON.stringify(ast))
let data = {} let data = {}
for (const clazz of ast.body) { for (const clazz of ast.body) {
if (clazz.type === "ClassDeclaration") { if (clazz.type === 'ClassDeclaration') {
data.name = clazz.id.name data.name = clazz.id.name
data.children = [] data.children = []
for (const method of clazz.body.body) { for (const method of clazz.body.body) {
if (method.type === "MethodDefinition") { if (method.type === 'MethodDefinition') {
data.children.push({ data.children.push({
name: method.key.name, name: method.key.name,
children: [] children: []
@ -155,20 +155,5 @@ function acornToHierarchy(ast) {
*/ */
return data return data
} }
</script> </script>

View File

@ -3,6 +3,7 @@
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <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="./3rdparty/highlight/styles/default.css">
<link rel="stylesheet" href="../css/doctest.css"> <link rel="stylesheet" href="../css/doctest.css">
<script src="./3rdparty/highlight/highlight.pack.js"></script> <script src="./3rdparty/highlight/highlight.pack.js"></script>

1
site/.python-version Normal file
View File

@ -0,0 +1 @@
3.10.1

45
site/main.py Normal file
View 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
View 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
View 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