diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..23942cf --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +# 0.2 + + - Command line scripts added + +# 0.1.1 + + - Allow both stock and user supplied templates + - Template inheritance + +# 0.1 + + - Rendering from templates via python functions diff --git a/MarkdownMark/controller.py b/MarkdownMark/controller.py new file mode 100644 index 0000000..1246fe9 --- /dev/null +++ b/MarkdownMark/controller.py @@ -0,0 +1,93 @@ +""" +Use Markdown to generate various uni documents + +Controller code to get the workflows working. + +Two use cases at the moment: + + 1. Called with all parts specifid + - Template + - Input File + 1. Called with just an input file. Which contains the template. + +I would then plumb this to a command line script. + +``` + markdownmark -t [template] [output] +``` + + +Exceptions to check for + + - jinja2.exceptions.TemplateNotFound + + +Things to think about: + + 1. Batch conversion, point him at a directory + 1. PDF conversion, use something (QT?) to render the HTML to PDF. +""" + +import logging + +import os +import pathlib + +import MarkdownMark.markdownParser as parser +import MarkdownMark.renderer as renderer + + +class RenderError(Exception): + """Exception raised for errors in the input. + + + @message: explanation of the error + """ + + def __init__(self, message): + self.message = message + + + +def convertFile(inputFile, templateFile = None, templateDir=None, outputFile = None): + """ + Given a set of options convert a file from markdown + to a nicely rendered output. + + The template file can be specified as part of the function, or in the header file. + NOTE: options specified to the function will overwrite anything in the template. + + @param inputFile: The Markdown File we want to render + @param templateFile: Template file to use to render. + @param templateDir: Optional Local Template directory + @param outputFile: Output file, defaults to .hmtl + """ + + log = logging.getLogger("[CONTROLER]") + log.debug(" Dealing with Input file {0}".format(inputFile)) + + #Create a parser for this input file + theParser = parser.markdownParser(inputFile) + theParser.parseFile() + #Check for Header + if theParser.header: + #Check for either template dir or template file in the header + if templateFile is None: + templateFile = theParser.header.get("template") + + if templateDir is None: + templateDir = theParser.header.get("templatedir") + + if templateFile is None: + raise RenderError("No Template has been specified") + #Feed this to the Renderer + theRenderer = renderer.HTMLRenderer(templateFile, templateDir) + + #We also need to know where to output the thing. + if outputFile is None: + log.debug("No Output file specified") + + outputFile = "{0}.html".format(pathlib.Path(inputFile).stem) + log.debug("Output File is now {0}".format(outputFile)) + + theRenderer.toFile(theParser, outputFile) diff --git a/MarkdownMark/scripts/__init__.py b/MarkdownMark/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/MarkdownMark/scripts/runDMC.py b/MarkdownMark/scripts/runDMC.py new file mode 100644 index 0000000..8409337 --- /dev/null +++ b/MarkdownMark/scripts/runDMC.py @@ -0,0 +1,41 @@ +import optparse + + +import MarkdownMark.controller as controller + + +def main(): + usage = "%prog inputfile.md [options]" + description = "TODO: Longer Desc here" + parser = optparse.OptionParser(usage=usage, + description = description, + version="2.0") + parser.add_option("-t","--template", + dest="template", + help="Template file to use for rendering" + ) + parser.add_option("-d","--templatedir", + dest="templatedir", + help="Local template directory", + ), + + parser.add_option("-o","--output", + dest="output", + help="Name of output file" + ) + + + (options, args) = parser.parse_args() + if len(args) < 1: + parser.print_usage() + return -1 + + + #Otherwise + controller.convertFile(args[0], + templateFile = options.template, + templateDir = options.templatedir, + outputFile = options.output) + +if __name__ == "__main__": + main() diff --git a/README.md b/README.md index 9f2907d..b400ac8 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,7 @@ Or to stop and capture outputs ``` pytest -x -s ``` + +Or Nicer HTML output + +pytest --html=report.html diff --git a/setup.py b/setup.py index de0870a..1c09c20 100644 --- a/setup.py +++ b/setup.py @@ -17,14 +17,20 @@ name="MarkdownMark", author="Dan Goldsmith", author_email="djgoldsmith@googlemail.com", - version="0.1.1", + version="0.2.0", #Dependencies install_requires=requires, tests_require=testRequires, #Load the Library packages=find_packages(), - package_data = {"MarkdownMark": ["templates/"]} - #package_data = {"" : ["templates/"]} + package_data = {"MarkdownMark": ["templates/"]}, + + #And scripts + entry_points = { + "console_scripts":[ + "runDMC = MarkdownMark.scripts.runDMC:main" + ] + } ) diff --git a/test/inputs/inputController.md b/test/inputs/inputController.md new file mode 100644 index 0000000..56cc3ed --- /dev/null +++ b/test/inputs/inputController.md @@ -0,0 +1,8 @@ +--- +template: controller.jinja2 +templatedir: test/templates +--- + +# TheInput + +Some Input from a markdown file diff --git a/test/inputs/inputControllerNoHead.md b/test/inputs/inputControllerNoHead.md new file mode 100644 index 0000000..0e24dfd --- /dev/null +++ b/test/inputs/inputControllerNoHead.md @@ -0,0 +1,3 @@ +# TheInput + +Some Input from a markdown file diff --git a/test/templates/controller.jinja2 b/test/templates/controller.jinja2 new file mode 100644 index 0000000..fff7a48 --- /dev/null +++ b/test/templates/controller.jinja2 @@ -0,0 +1,4 @@ +

Template Output

+

Markdown Input

+ +{{ TheInput.markdown() }} \ No newline at end of file diff --git a/test/test_controller.py b/test/test_controller.py new file mode 100644 index 0000000..8c04a64 --- /dev/null +++ b/test/test_controller.py @@ -0,0 +1,121 @@ +""" +Testing for the Main Methods and Script Functions +""" + +import unittest +import os +import jinja2 + +import MarkdownMark.controller as controller + +class controllerTest(unittest.TestCase): + """ + Tests for the controller. + + The controller should allow us to bring all the parts togeter. + Meaning we can call controller.render(params) to get an sensible output. + + ```convertFile(inputFile, templateFile = None, templateDir=None, outputFile = None):``` + """ + + def tearDown(self): + """ + Called before all tests, make sure the tmp directory is clean + """ + if os.path.exists("/tmp/render.html"): + os.remove("/tmp/render.html") + + #And if we dont sepcify a file + if os.path.exists("inputController.html"): + os.remove("inputController.html") + + def testConvertAll(self): + """ Convert with all parameters supplied """ + + controller.convertFile("test/inputs/inputController.md", + "controller.jinja2", + "test/templates", + outputFile = "/tmp/render.html") + self._checkOutput() + + + def testConvertHeaderTemplate(self): + """ + Convert using template specified in the header. + + If we don't specify a template when calling the function, + we look in the file header for a ```template:``` and use that + """ + + controller.convertFile("test/inputs/inputController.md", + templateFile = None, + templateDir = "test/templates", + outputFile = "/tmp/render.html") + self._checkOutput() + + def testNoOutput(self): + """ + If no output file is specified, we render as .html + """ + + controller.convertFile("test/inputs/inputController.md", + ) + + self._checkOutput("inputController.html") + + + def testInputOnly(self): + """ + Run the command with only the input file supplied + + This should take the template and template dir from the header + Filemane defaults to the file inputs + """ + + + controller.convertFile("test/inputs/inputController.md" + ) + + self._checkOutput("inputController.html") + + def testHeaderException(self): + """ + Throw an Exception if there is no template supplied + + If no template is supplied we need to throw and exeption + """ + + with self.assertRaises(controller.RenderError): + out = controller.convertFile("test/inputs/inputControllerNoHead.md") + + #Can I unit test the exception message? + + + def testMissingTemplate(self): + """ + Throw an Exception if we cannot find the template + + If the template doesn't exist, we also throw an exception + """ + + with self.assertRaises(jinja2.exceptions.TemplateNotFound): + out = controller.convertFile("test/inputs/inputController.md", templateFile = "missing.jinja") + + + def _checkOutput(self, outFile = "/tmp/render.html"): + """ + Helper function to check the output + + We are going for very simple templating here + """ + + expected = """

Template Output

+

Markdown Input

+ +

Some Input from a markdown file

""" + + fd = open(outFile, "r") + out = fd.read() + fd.close() + + self.assertEqual(out, expected) diff --git a/test/test_render.py b/test/test_render.py index b7520cd..5d94fa9 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -2,6 +2,7 @@ import MarkdownMark.markdownParser as parser import MarkdownMark.renderer as renderer +import jinja2 """ God I hate testing web output. @@ -272,7 +273,6 @@ def testTemplateInheritance(self): """ theRenderer = renderer.HTMLRenderer("testInherit.jinja2", "test/templates") out = theRenderer.render(self.theParser) - print (out) expected = """

Inherit From Templates

Introduction

@@ -308,7 +308,6 @@ def testTemplateBaseInheritance(self): """ theRenderer = renderer.HTMLRenderer("testBaseInherit.jinja2", "test/templates") out = theRenderer.render(self.theParser) - print (out) expected = """

Inherit From Base

Introduction

@@ -335,3 +334,26 @@ def testTemplateBaseInheritance(self): """ self.assertEqual(out, expected) + + + def testRenderException(self): + """ + Throw an exception if we cannot find the template + """ + + with self.assertRaises(jinja2.exceptions.TemplateNotFound): + theRenderer = renderer.HTMLRenderer("missing.jinja2") + + + def testRenderExceptionLocal(self): + """ + Throw an exception if we cannot find the template locally + """ + + with self.assertRaises(jinja2.exceptions.TemplateNotFound): + theRenderer = renderer.HTMLRenderer("missing.jinja2", "test/templates") + + + + +