Skip to content

Commit

Permalink
Added code for rendering markdown files from tutorials to PlumedToHTML
Browse files Browse the repository at this point in the history
  • Loading branch information
Gareth Aneurin Tribello committed Oct 10, 2024
1 parent 7419546 commit 074cc82
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 2 deletions.
106 changes: 106 additions & 0 deletions PlumedToHTML/PlumedToHTML.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def test_plumed( executible, filename, header=[], printjson=False, jsondir="./"
with open(outfile,"w") as stdout:
with open(errtxtfile,"w") as stderr:
with cd(run_folder):
print( cmd )
plumed_out = subprocess.run(cmd, text=True, stdout=stdout, stderr=stderr )
# write header and preamble to errfile
with open(errfile,"w") as stderr:
Expand Down Expand Up @@ -432,12 +433,117 @@ def compare_to_reference( output, reference ) :
for i in range(len(soup_comments)) :
if soup_comments[i].get_text()!=reference["comments"][i] : return False

print("Comments fine")
# Check that everything that should be rendered as a tooltip has been rendered as a tooltip
# This is action names and keywords
if "tooltips" in reference.keys() :
soup_tooltips = soup.find_all(attrs={'class': 'tooltip'})
print("CHECK TOOLTIP", soup_tooltips )
print("TOOLTIP NUMBER CORRECT", len(soup_tooltips), len(reference["tooltips"]))
if len(soup_tooltips)!=len(reference["tooltips"]) : return False
for i in range(len(soup_tooltips)) :
print("COMPARISON", soup_tooltips[i].contents[0], reference["tooltips"][i] )
if soup_tooltips[i].contents[0]!=reference["tooltips"][i] : return False

return True

def processMarkdown( filename, plumed_exe, plumed_names, actions, jsondir="./" ) :
if not os.path.exists(filename) :
raise RuntimeError("Found no file called " + filename + " in lesson")

with open( filename, "r" ) as f:
inp = f.read()

ninputs = 0
nfail = len(plumed_exe)*[0]
inplumed = False
plumed_inp = ""
solutionfile = None
incomplete = False
usemermaid = ""
dirname = os.path.dirname(filename)
if dirname=="" : dirname = "."

with open( filename, "w+" ) as ofile:
for line in inp.splitlines() :
# Detect and copy plumed input files
if "```plumed" in line :
inplumed = True
plumed_inp = ""
solutionfile = None
incomplete = False
ninputs = ninputs + 1

# Test plumed input files that have been found in tutorial
elif inplumed and "```" in line :
inplumed = False
# Create mermaid graphs from PLUMED inputs if this has been requested
if usemermaid!="" :
mermaidinpt = ""
if usemermaid=="value" :
mermaidinpt = get_mermaid( plumed_exe[-1], plumed_inp, False )
elif usemermaid=="force" :
mermaidinpt = get_mermaid( plumed_exe[-1], plumed_inp, True )
else :
raise RuntimeError(usemermaid + "is invalid instruction for use mermaid")
ofile.write("```mermaid\n" + mermaidinpt + "\n```\n")
if incomplete :
if solutionfile:
# Read solution from solution file
try:
with open( dirname + "/" + solutionfile, "r" ) as sf:
solution = sf.read()
plumed_inp += "#SOLUTION \n" + solution
except:
raise RuntimeError(f"error in opening {solutionfile} as solution"
f" for an incomplete input from file {filename}")
else:
raise RuntimeError(f"an incomplete input from file {filename}"
" does not have its solution file")
# Create the full input for PlumedToHTML formatter
else :
solutionfile = filename + "_working_" + str(ninputs) + ".dat"
with open( dirname + "/" + solutionfile, "w+" ) as sf:
sf.write( plumed_inp )

# Test whether the input solution can be parsed
success = len(plumed_exe)*[False]
for i in range(len(plumed_exe)) :
if i==len(plumed_exe)-1 :
# Json files are put in directory one up from us to ensure that
# PlumedToHTML finds them when we do get_html (i.e. these will be in
# the data directory where the calculation is run)
if incomplete :
success[i]=test_plumed(plumed_exe[i], dirname + "/" + solutionfile )
else :
success[i]=test_plumed(plumed_exe[i], dirname + "/" + solutionfile,
printjson=True, jsondir=jsondir )
else :
success[i]=test_plumed( plumed_exe[i], dirname + "/" + solutionfile )
if(success[i]!=0 and success[i]!="custom") : nfail[i] = nfail[i] + 1
# Use PlumedToHTML to create the input with all the bells and whistles
html = get_html(plumed_inp,
solutionfile,
solutionfile,
plumed_names,
success,
plumed_exe,
usejson=(not success[-1]),
actions=actions )
# Print the html for the solution
ofile.write( "{% raw %}\n" + html + "\n {% endraw %} \n" )
# This finds us the solution file
elif inplumed and "#SOLUTIONFILE=" in line :
solutionfile=line.replace("#SOLUTIONFILE=","")
elif inplumed and "#MERMAID=" in line :
usemermaid = line.replace("#MERMAID=","").strip()
elif inplumed :
if "__FILL__" in line :
incomplete = True
plumed_inp += line + "\n"
# Just copy any line that isn't part of a plumed input
elif not inplumed :
ofile.write( line + "\n" )

return ninputs, nfail

2 changes: 1 addition & 1 deletion PlumedToHTML/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .PlumedToHTML import test_plumed, test_and_get_html, get_html, get_html_header, compare_to_reference, get_mermaid
from .PlumedToHTML import test_plumed, test_and_get_html, get_html, get_html_header, compare_to_reference, get_mermaid, processMarkdown
67 changes: 67 additions & 0 deletions PlumedToHTML/tests/test_markdown.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from unittest import TestCase

import os
import json
import PlumedToHTML
from bs4 import BeautifulSoup

class TestPlumedToHTML(TestCase):
def testBasicOutput(self) :
# Open the json file and read it in
f = open("tdata/tests.json")
tests = json.load(f)
f.close()

# Now run over all the inputs in the json
n=1
for item in tests["regtests"] :
with self.subTest(item=item):
print("INPUT", item["index"], item["input"] )
if "__FILL__" in item["input"] : continue
with open("testmarkdown" + str(n) +".md", "w") as of :
of.write("# TEST MARKDOWN \n\n")
of.write("Some text before \n")
of.write("```plumed`\n")
of.write( item["input"] + "\n")
of.write("```\n")
of.write("Some text after \n")
actions = set()
PlumedToHTML.processMarkdown( "testmarkdown" + str(n) +".md", ("plumed",), ("master",), actions )
self.assertTrue( actions==set(item["actions"]) )
with open("testmarkdown" + str(n) +".md", "r") as f : inp = f.read()
out, inhtml = "", False
for line in inp.splitlines() :
if inhtml and "{% endraw %}" in line : inhtml = False
elif inhtml : out += line + "\n"
elif "{% raw %}" in line : inhtml = True
self.assertTrue( PlumedToHTML.compare_to_reference( out, item ) )
soup = BeautifulSoup( out, "html.parser" )
# Check the badges
out_badges = soup.find_all("img")
self.assertTrue( len(out_badges)==len(item["badges"]) )
for i in range(len(out_badges)) :
if item["badges"][i]=="pass" : self.assertTrue( out_badges[i].attrs["src"].find("passing")>0 )
elif item["badges"][i]=="fail" : self.assertTrue( out_badges[i].attrs["src"].find("failed")>0 )
elif item["badges"][i]=="load" : self.assertTrue( out_badges[i].attrs["src"].find("with-LOAD")>0 )
elif item["badges"][i]=="incomplete" : self.assertTrue( out_badges[i].attrs["src"].find("incomplete")>0 )
n=n+1

with open("testmarkdown" + str(n) +".md", "w") as of :
of.write("# TEST MARKDOWN \n\n")
of.write("Some text before \n")
of.write("```plumed`\n")
of.write("#SOLUTIONFILE=tdata/solution.dat\n")
of.write("d: DISTANCE __FILL__=1,2\n")
of.write("```\n")
of.write("Some text after \n")
actions = set()
PlumedToHTML.processMarkdown( "testmarkdown" + str(n) +".md", ("plumed",), ("master",), actions )
self.assertTrue( actions==set(["DISTANCE"]) )


def testHeader(self) :
headerfilename = os.path.join(os.path.dirname(__file__),"../assets/header.html")
hfile = open( headerfilename )
codes = hfile.read()
hfile.close()
self.assertTrue( codes==PlumedToHTML.get_html_header() )
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

setuptools.setup(
name='PlumedToHTML',
version='0.75',
version='0.76',
author="Gareth Tribello",
author_email="[email protected]",
description="A package for creating pretified HTML for PLUMED files",
Expand Down
1 change: 1 addition & 0 deletions tdata/solution.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
d: DISTANCE ATOMS=1,2

0 comments on commit 074cc82

Please sign in to comment.