Source code for wbuild.createIndex
import os
import sys
import pathlib
import shutil
import wbuild
import re
from string import Template
from os import listdir
from os.path import isfile, join
from wbuild.utils import parseWBInfosFromRFiles, parseMDFiles, \
getYamlParam, Config, removeFilePrefix, findFirstFile, pathsepsToUnderscore
sys.path.insert(0, os.getcwd() + "/.wBuild") # TODO - is this line required?
[docs]def writeSubMenu(top, wbData, level):
"""
Recursive call to construct the dropdown list and hover-over side-menus in it adhereing to a "top" toolbar category.
:param top: "top" toolbar directory to be appointed to
:param wbData: wb relevant data of all scanned files
:param level: deepness of the current submenu (first dropdown list, then hover-over side-menus in the html)
:return: deeply constructed dropdown list of the top toolbar category as an HTML string
"""
menuString = ''
temp = []
newWb = []
for r in wbData:
temptemp = pathlib.PurePath(r['file']).parts[level - 1]
if (pathlib.PurePath(r['file']).parts[level - 1] == top):
# Is it a file
if (len(pathlib.PurePath(r['file']).parts) == (level + 1)):
if getYamlParam(r, 'type') != 'script' and getYamlParam(r, 'type') != 'noindex':
menuString += ('<li><a href="javascript:navigate(\'' +
pathlib.PurePath(r['outputFile']).name + '\');">' +
pathlib.PurePath(r['file']).parts[level] + '</a></li>\n')
continue
temp.append(pathlib.PurePath(r['file']).parts[level])
newWb.append(r)
temp = sorted(set(temp))
for top in temp:
menuString += '<li class="dropdown-submenu">\n'
menuString += ' <a tabindex="-1" href="#">' + top + '</a>\n'
menuString += ' <ul class="dropdown-menu">\n'
menuString += ' ' + writeSubMenu(top, newWb, level + 1)
menuString += ' </ul>\n'
menuString += '</li>\n'
return menuString
[docs]def getRecentMenu():
"""
Support recently edited files list to the HTML web output.
:return: HTML string: "Recently viewed" menu contents
"""
conf = Config()
htmlOutputPath = conf.get("htmlOutputPath")
rFiles = sorted([join(htmlOutputPath, f) for f in listdir(htmlOutputPath)
if isfile(join(htmlOutputPath, f))], key=os.path.getmtime, reverse=True)
## delete all files containing the word "index from html menu "
rFiles = [f for f in rFiles if "index" not in f]
rFiles = rFiles[:10]
# open recent Files in new tab
menuString = ""
for f in rFiles:
fo = pathlib.PurePath(f).name
# Open in a new tab
# menuString += ('<p><a href='+ fo + ' target="_blank">' + fo.replace('_', ' ').replace('.html', '') +
# '</a></p>\n')
# Open in same tab
menuString += ('<p><a href="javascript:navigate(\'' +
fo + '\');" >' + fo.replace('_', ' ').replace('.html', '') +
'</a></p>\n')
return menuString
[docs]def writeReadme(readmePath):
"""
Create readme file output for html template
readmePath: html readme path
"""
readmeFilename = os.path.abspath(readmePath)
readmeString = '<li><a href="javascript:navigate(' + " '{}'".format(readmeFilename) + ');">Readme</a></li> '
readmeIframeString = '<iframe id="Iframe" src="' + readmeFilename + '" width=100% height=95% ></iframe> '
readmeFilename = '"{}" '.format(readmeFilename)
return readmeString, readmeIframeString, readmeFilename
[docs]def writeDepSVG(graphPath=None):
""" Search for rule graph. If path not specified in config, take default dep.svg in snakeroot path"""
# mumichae: Is this search really necessary?
conf = Config()
htmlOutputPath = conf.get("htmlOutputPath")
if graphPath is None:
graphPath = os.path.abspath(htmlOutputPath) + "/dep.svg"
snakeroot = conf.snakeroot
foldername = snakeroot.split("/")[-1]
try:
filename_SVG = conf.get("ruleGraphPath") #### should be .md file
except AttributeError as e:
### try with default name "dep.svg"
if os.path.isfile(os.path.join(htmlOutputPath, "dep.svg")):
filename_SVG = "dep.svg"
else:
### search for files containing "svg" and foldername
filename_SVG = ""
onlyfiles = [f for f in os.listdir(htmlOutputPath) if os.path.isfile(os.path.join(htmlOutputPath, f))]
for f in onlyfiles:
if (foldername in f) and f.endswith(".svg"):
filename_SVG = f
filename_SVG = os.path.abspath(graphPath)
svgString = '<li><a href="javascript:navigate(' + "'{}'".format(filename_SVG) + ');">Dependency</a></li>'
return svgString
def createIndexName(scriptsPath, default=None):
if default is not None:
return default
name = ""
conf = Config()
indexWithFolderName = conf.get("indexWithFolderName")
if indexWithFolderName:
abs_path = str(os.path.abspath(scriptsPath))
name = abs_path.split("/")[-2]
return name
[docs]def writeIndexHTMLMenu(scriptsPath=None, index_name=None):
"""
Scan for files involved in the current HTML rendering and fill the HTML quick access toolbar correspondingly
"""
conf = Config()
if scriptsPath is None:
scriptsPath = conf.get("scriptsPath")
htmlOutputPath = conf.get("htmlOutputPath")
pageTitle = conf.get("projectTitle")
snakeroot = conf.snakeroot
wbData = parseWBInfosFromRFiles(script_dir=scriptsPath, htmlPath=htmlOutputPath)
mdData = parseMDFiles(script_dir=scriptsPath, htmlPath=htmlOutputPath)
wbData += mdData
temp = []
# for all of the scanned files, collect their paths
for r in wbData:
# this is needed so the relative path to "../wbuild/Snakefile" is not
# part of the html sub menu
r['file'] = removeFilePrefix(r['file'], snakeroot)
temp.append(pathlib.PurePath(r['file']).parts[1])
menuString = ""
for top in sorted(set(temp)):
menuString += (
'<li class="dropdown">\n' +
# write the current directory's name to the main ("top") toolbar tab
' <a href="#" class="dropdown-toggle" data-toggle="dropdown" ' +
'role="button" aria-haspopup="true" aria-expanded="false">' +
top + '<span class="caret"></span></a>\n'
' <ul class="dropdown-menu multi-level" role="menu">\n' +
# write sub-directories to the dropdown list of the "top" tabs
writeSubMenu(top, wbData, 2) +
' </ul>\n' +
'</li>\n')
_, output, graphPath, readmePath = createIndexRule(scriptsPath, index_name)
readmeString, readmeIframeString, readmeFilename = writeReadme(readmePath)
depSVGString = writeDepSVG(graphPath)
# fill the HTML template with the constructed tag structure
wbuildPath = pathlib.Path(wbuild.__file__).parent
template = open(str(wbuildPath / "html/template.html")).read()
template = Template(template).substitute(menu=menuString, title=pageTitle, rf=getRecentMenu(),
readme=readmeString, readmeIframe=readmeIframeString,
readmeFilename=readmeFilename
, depSVG=depSVGString)
f = open(output, 'w')
f.write(template)
f.close()
[docs]def createIndexRule(scriptsPath=None, index_name=None, wbData=None, mdData=None):
"""
Create the input and output files necessary for an HTML index rule.
:param scriptsPath: relative scripts path. If not specified, use the default scripts path from the config.
:param index_name: prefix of the index file name `<index_name>_index.html`
:param wbData: wBuild rule data from parsed R scripts
:param mdData: wBuild rule data from parsed markdown files
:return:
+ **inputFiles** - list of output HTML files from the `scriptsPath`, comprising the input of the index rule
+ **indexPath** - index html file name, equates to the output
+ **graphPath** - dependency graph image file name for HTML template (graph is needs to be written to this file)
+ **readmePath** - readme HTML name for HTML template (takes readme from the `scriptsPath`)
"""
conf = Config()
if scriptsPath is None or scriptsPath == conf.get("scriptsPath"):
readmePath = conf.get("readmePath")
scriptsPath = conf.get("scriptsPath")
else: # find readme file
readmePath = findFirstFile(scriptsPath, ".*readme.*", "md", re.I)
if readmePath is None:
readmePath = scriptsPath + "/readme.md"
open(readmePath, "a").close()
htmlOutputPath = conf.get("htmlOutputPath")
htmlIndex = conf.get("htmlIndex")
index_name = createIndexName(scriptsPath, index_name)
# gather index input files
inputFiles = []
if wbData is None:
wbData = parseWBInfosFromRFiles(script_dir=scriptsPath, htmlPath=htmlOutputPath)
if mdData is None:
mdData = parseMDFiles(script_dir=scriptsPath, htmlPath=htmlOutputPath, readmePath=readmePath)
for r in wbData:
# ignore if the file is script or noindex
if getYamlParam(r, 'type') == 'script' or getYamlParam(r, 'type') == 'noindex':
continue
inputFiles.append(r['outputFile'])
for r in mdData:
inputFiles.append(r['outputFile'])
if len(index_name) > 0:
index_name = index_name + "_" # separator for distinct index_name
indexPath = "/".join([htmlOutputPath, index_name + htmlIndex])
graphPath = "/".join([htmlOutputPath, index_name + "dep.svg"])
# readme html
readmePath = htmlOutputPath + "/" + pathsepsToUnderscore(os.path.splitext(readmePath)[0]) + ".html"
return inputFiles, indexPath, graphPath, readmePath
[docs]def ci(scriptsPath=None, index_name=None):
"""
Write HTML index file
:param scriptsPath: relative scripts path. If not specified, use the default scripts path from the config.
:param index_name: prefix of the index file name `<index_name>_index.html`
"""
conf = Config()
htmlOutputPath = conf.get("htmlOutputPath")
writeIndexHTMLMenu(scriptsPath, index_name)
libDir = htmlOutputPath + "/lib"
# if os.path.exists(libDir):
# shutil.rmtree(libDir)
if not os.path.exists(libDir):
wbuildPath = pathlib.Path(wbuild.__file__).parent
shutil.copytree(str(wbuildPath) + "/html/lib", libDir)