From 50807745e720a3e94492d72ad41977a1ddccf76c Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 07:43:46 +0100 Subject: [PATCH 01/13] Stubs for controllers added --- CHANGELOG.md | 12 ++++++++++++ MarkdownMark/controller.py | 31 +++++++++++++++++++++++++++++++ test/test_controller.py | 11 +++++++++++ 3 files changed, 54 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 MarkdownMark/controller.py create mode 100644 test/test_controller.py 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..642dd97 --- /dev/null +++ b/MarkdownMark/controller.py @@ -0,0 +1,31 @@ +""" +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] +``` + +Things to think about: + + 1. Batch conversion, point him at a directory + 1. PDF conversion, use something (QT?) to render the HTML to PDF. +""" + +def convertFile(inputFile, templateFile = None, outputFile = None): + """ + Given a set of options convert a file from markdown + to a nicely rendered output + """ + + pass diff --git a/test/test_controller.py b/test/test_controller.py new file mode 100644 index 0000000..d4c1763 --- /dev/null +++ b/test/test_controller.py @@ -0,0 +1,11 @@ +""" +Testing for the Main Methods and Script Functions +""" + +import unittest + +import MarkdownMark.controller as controller + +class renderTest(unittest.TestCase): + + From 2450075cc23f58d2c658f323f953f7103f6dc6d5 Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 08:17:50 +0100 Subject: [PATCH 02/13] Test an Exception is thrown when we cannot find the template --- test/test_render.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/test_render.py b/test/test_render.py index b7520cd..c9c7076 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. @@ -335,3 +336,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") + + + + + From d15ea664f220396fb53ebe444c302f9e8f6554e5 Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 09:02:24 +0100 Subject: [PATCH 03/13] Test cases for controller updated --- test/inputs/inputController.md | 3 ++ test/templates/controller.jinja2 | 4 ++ test/test_controller.py | 90 +++++++++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 test/inputs/inputController.md create mode 100644 test/templates/controller.jinja2 diff --git a/test/inputs/inputController.md b/test/inputs/inputController.md new file mode 100644 index 0000000..0e24dfd --- /dev/null +++ b/test/inputs/inputController.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..835bd89 --- /dev/null +++ b/test/templates/controller.jinja2 @@ -0,0 +1,4 @@ +

Template Output

+

Markdown Input

+ +{{ TheInput.asStr() }} \ No newline at end of file diff --git a/test/test_controller.py b/test/test_controller.py index d4c1763..2d4b7aa 100644 --- a/test/test_controller.py +++ b/test/test_controller.py @@ -3,9 +3,97 @@ """ import unittest +import os +import jinja2 import MarkdownMark.controller as controller -class renderTest(unittest.TestCase): +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 setUp(self): + """ + Called before all tests, make sure the tmp directory is clean + """ + if os.path.exists("/tmp/render.html"): + os.remove("/tmp/render.html") + + def testConvertAll(self): + """ Convert with all parameters supplied """ + + controller.convertFile("input/inputController.md", + template, outputFile = "/tmp/render.html") + self.checkOutput() + pass + + 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 + """ + pass + + def testNoOutput(self): + """ + If no output file is specified, we render as .html + """ + + pass + + def testInputOnly(self): + """ + Run the command with only the input file supplied + """ + + 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(inputFile) + + #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(inputFile, 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) From 44aa18ec13336ac1d9761caf02ae8a8e94019899 Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 09:10:35 +0100 Subject: [PATCH 04/13] Unit testing for Contrller complete --- test/inputs/inputController.md | 5 +++++ test/test_controller.py | 40 ++++++++++++++++++++++++++++------ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/test/inputs/inputController.md b/test/inputs/inputController.md index 0e24dfd..8c09158 100644 --- a/test/inputs/inputController.md +++ b/test/inputs/inputController.md @@ -1,3 +1,8 @@ +--- +template: controller.jinja2 +templatedir: /test/templates +--- + # TheInput Some Input from a markdown file diff --git a/test/test_controller.py b/test/test_controller.py index 2d4b7aa..c2835f6 100644 --- a/test/test_controller.py +++ b/test/test_controller.py @@ -26,14 +26,20 @@ def setUp(self): """ 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.path.remove("inputController.html") + def testConvertAll(self): """ Convert with all parameters supplied """ controller.convertFile("input/inputController.md", - template, outputFile = "/tmp/render.html") - self.checkOutput() - pass + "controller.jinja2", + "test/templates", + outputFile = "/tmp/render.html") + self._checkOutput() + def testConvertHeaderTemplate(self): """ @@ -42,20 +48,40 @@ def testConvertHeaderTemplate(self): If we don't specify a template when calling the function, we look in the file header for a ```template:``` and use that """ - pass + + controller.convertFile("input/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 """ - pass + controller.convertFile("input/inputController.md", + "controller.jinja2", + "test/templates", + ) + + 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 inpue """ - + + + controller.convertFile("input/inputController.md", + ) + + self._checkOutput("inputController.html") + def testHeaderException(self): """ Throw an Exception if there is no template supplied From 0b7e1517f2656d7e975c468b30cef13ef7cd4443 Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 12:36:43 +0100 Subject: [PATCH 05/13] Fix unit test so the bloody paths are correct for template files --- test/test_controller.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/test_controller.py b/test/test_controller.py index c2835f6..4cca393 100644 --- a/test/test_controller.py +++ b/test/test_controller.py @@ -8,8 +8,6 @@ import MarkdownMark.controller as controller - - class controllerTest(unittest.TestCase): """ Tests for the controller. @@ -34,7 +32,7 @@ def setUp(self): def testConvertAll(self): """ Convert with all parameters supplied """ - controller.convertFile("input/inputController.md", + controller.convertFile("test/inputs/inputController.md", "controller.jinja2", "test/templates", outputFile = "/tmp/render.html") @@ -49,7 +47,7 @@ def testConvertHeaderTemplate(self): we look in the file header for a ```template:``` and use that """ - controller.convertFile("input/inputController.md", + controller.convertFile("test/inputs/inputController.md", templateFile = None, templateDir = "test/templates", outputFile = "/tmp/render.html") @@ -60,7 +58,7 @@ def testNoOutput(self): If no output file is specified, we render as .html """ - controller.convertFile("input/inputController.md", + controller.convertFile("test/inputs/inputController.md", "controller.jinja2", "test/templates", ) @@ -77,7 +75,7 @@ def testInputOnly(self): """ - controller.convertFile("input/inputController.md", + controller.convertFile("test/inputs/inputController.md", ) self._checkOutput("inputController.html") From 012ff827c45cc86543a2d125b704b29a2c979eb1 Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 12:43:00 +0100 Subject: [PATCH 06/13] Update controller test template to use markdown formatting. Controller render all functionality added --- MarkdownMark/controller.py | 49 ++++++++++++++++++++++++++++++-- test/templates/controller.jinja2 | 2 +- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/MarkdownMark/controller.py b/MarkdownMark/controller.py index 642dd97..86a0525 100644 --- a/MarkdownMark/controller.py +++ b/MarkdownMark/controller.py @@ -16,16 +16,59 @@ 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. """ -def convertFile(inputFile, templateFile = None, outputFile = None): +import logging + + + +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 + to a nicely rendered output. + + The template file can be specified as part of the function, or in the header file + + @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 """ - pass + 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() + + #Feed this to the Renderer + theRenderer = renderer.HTMLRenderer(templateFile, templateDir) + + theRenderer.toFile(theParser, outputFile) diff --git a/test/templates/controller.jinja2 b/test/templates/controller.jinja2 index 835bd89..fff7a48 100644 --- a/test/templates/controller.jinja2 +++ b/test/templates/controller.jinja2 @@ -1,4 +1,4 @@

Template Output

Markdown Input

-{{ TheInput.asStr() }} \ No newline at end of file +{{ TheInput.markdown() }} \ No newline at end of file From 61fe9ec0093f79856389f1e066ee48d0cfcd0cc7 Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 13:28:49 +0100 Subject: [PATCH 07/13] Leading slash in Temlate tests --- test/inputs/inputController.md | 2 +- test/inputs/inputControllerNoHead.md | 3 +++ test/test_controller.py | 13 +++++++------ 3 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 test/inputs/inputControllerNoHead.md diff --git a/test/inputs/inputController.md b/test/inputs/inputController.md index 8c09158..56cc3ed 100644 --- a/test/inputs/inputController.md +++ b/test/inputs/inputController.md @@ -1,6 +1,6 @@ --- template: controller.jinja2 -templatedir: /test/templates +templatedir: test/templates --- # TheInput 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/test_controller.py b/test/test_controller.py index 4cca393..4c07f2b 100644 --- a/test/test_controller.py +++ b/test/test_controller.py @@ -59,8 +59,6 @@ def testNoOutput(self): """ controller.convertFile("test/inputs/inputController.md", - "controller.jinja2", - "test/templates", ) self._checkOutput("inputController.html") @@ -71,12 +69,15 @@ 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 inpue + Filemane defaults to the file inputs """ controller.convertFile("test/inputs/inputController.md", - ) + templateFile = None, + templateDir = None, + outputFile = "/tmp/render.html" + ) self._checkOutput("inputController.html") @@ -88,7 +89,7 @@ def testHeaderException(self): """ with self.assertRaises(controller.RenderError): - out = controller.convertFile(inputFile) + out = controller.convertFile("test/inputs/inputControllerNoHead.md") #Can I unit test the exception message? @@ -101,7 +102,7 @@ def testMissingTemplate(self): """ with self.assertRaises(jinja2.exceptions.TemplateNotFound): - out = controller.convertFile(inputFile, templateFile = "missing.jinja") + out = controller.convertFile("test/inputs/inputController.md", templateFile = "missing.jinja") def _checkOutput(self, outFile = "/tmp/render.html"): From e9736045f44a27f1820d765009bc649292327268 Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 13:44:38 +0100 Subject: [PATCH 08/13] Rmove debugging code from a test case --- test/test_controller.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/test_controller.py b/test/test_controller.py index 4c07f2b..dc5e876 100644 --- a/test/test_controller.py +++ b/test/test_controller.py @@ -73,10 +73,7 @@ def testInputOnly(self): """ - controller.convertFile("test/inputs/inputController.md", - templateFile = None, - templateDir = None, - outputFile = "/tmp/render.html" + controller.convertFile("test/inputs/inputController.md" ) self._checkOutput("inputController.html") From beff537520b20f35caa282e7ad1e35d488157292 Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 13:52:59 +0100 Subject: [PATCH 09/13] Move test cleanup code to teardown --- test/test_controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_controller.py b/test/test_controller.py index dc5e876..8c04a64 100644 --- a/test/test_controller.py +++ b/test/test_controller.py @@ -18,7 +18,7 @@ class controllerTest(unittest.TestCase): ```convertFile(inputFile, templateFile = None, templateDir=None, outputFile = None):``` """ - def setUp(self): + def tearDown(self): """ Called before all tests, make sure the tmp directory is clean """ @@ -27,7 +27,7 @@ def setUp(self): #And if we dont sepcify a file if os.path.exists("inputController.html"): - os.path.remove("inputController.html") + os.remove("inputController.html") def testConvertAll(self): """ Convert with all parameters supplied """ From ed7d67413b64adc24d94624ac8e7d33013475932 Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 13:55:12 +0100 Subject: [PATCH 10/13] Intial cut of controller code added --- MarkdownMark/controller.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/MarkdownMark/controller.py b/MarkdownMark/controller.py index 86a0525..1246fe9 100644 --- a/MarkdownMark/controller.py +++ b/MarkdownMark/controller.py @@ -30,7 +30,8 @@ import logging - +import os +import pathlib import MarkdownMark.markdownParser as parser import MarkdownMark.renderer as renderer @@ -53,7 +54,8 @@ def convertFile(inputFile, templateFile = None, templateDir=None, outputFile = N 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 + 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. @@ -67,8 +69,25 @@ def convertFile(inputFile, templateFile = None, templateDir=None, outputFile = N #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) From 0c9a30a60b51f4c650f8a123287dee0584adb1da Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 13:55:29 +0100 Subject: [PATCH 11/13] Cleanup Print messages in test_render --- test/test_render.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_render.py b/test/test_render.py index c9c7076..5d94fa9 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -273,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

@@ -309,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

From 075bfa163090ef13ff9c53de8c652caef1012b71 Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 23:00:39 +0100 Subject: [PATCH 12/13] Runtime scripts added --- MarkdownMark/scripts/__init__.py | 0 MarkdownMark/scripts/runDMC.py | 41 ++++++++++++++++++++++++++++++++ README.md | 4 ++++ 3 files changed, 45 insertions(+) create mode 100644 MarkdownMark/scripts/__init__.py create mode 100644 MarkdownMark/scripts/runDMC.py 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 From be9b7c43b458414a3055f0115a37086c0a9bac5a Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Fri, 3 Jul 2020 23:01:03 +0100 Subject: [PATCH 13/13] Setup bumped to version 0.2 --- setup.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) 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" + ] + } )