Repository: OpenEnergyPlatform/oeplatform

Issue:
auto correct unclosed tags in markdown2html from raw html



Discussion:

maybe like this: 

```python
BeautifulSoup(html).prettify()
```

Resolving patch:
diff --git a/tutorials/views.py b/tutorials/views.py
--- a/tutorials/views.py
+++ b/tutorials/views.py
@@ -5,6 +5,7 @@
 
 import nbconvert
 import nbformat
+from bs4 import BeautifulSoup
 from django.conf import settings
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.shortcuts import Http404, redirect, render
@@ -17,7 +18,6 @@
 from .forms import TutorialForm
 from .models import CATEGORY_OPTIONS, LEVEL_OPTIONS, Tutorial
 
-
 youtubeUrlRegex = re.compile(r"^.*youtube\.com\/watch\?v=(?P<id>[A-z0-9]+)$")
 
 template_cache = {}
@@ -215,8 +215,9 @@ def _resolveDynamicTutorials(tutorials_qs):
     """
     Get all tutorials(created via oep website) form django db.
 
-    Evaluates a QuerySet and passes each evaluated object to the next function which returns a python
-    dictionary that contains all parameters from the object as dict. The dict is added to a list to
+    Evaluates a QuerySet and passes each evaluated object to the next function
+    which returns a python dictionary that contains all parameters from the
+    object as dict. The dict is added to a list to
     later merge the static and dynamic tutorials together.
 
     :param tutorials_qs:
@@ -272,35 +273,51 @@ def _processFormInput(form):
     return tutorial
 
 
+def cleanup_html(html):
+    """
+
+    Args:
+        html (str): html code parsed from markdown
+    Returns:
+        str: cleaned html
+    """
+    html = BeautifulSoup(html, "html.parser")
+    html = str(html)
+
+    # remove iframes and script
+    html = re.sub("<script[^>]*>.*</script>", "", html, re.IGNORECASE)
+    html = re.sub("<iframe[^>]*>.*</iframe>", "", html, re.IGNORECASE)
+
+    return html
+
+
 def formattedMarkdown(markdown):
     """Markdown style text to html and return.
     This functionality is implemented using Markdown2 package.
 
     Args:
         markdown(str): markdown formatted text
-    
+
     Returns:
         str: html
     """
-    # TODO: Add syntax highliting, 
+    # TODO: Add syntax highliting,
     # add css files -> https://github.com/trentm/python-markdown2/wiki/fenced-code-blocks
-    
+
     # list of extras: https://github.com/trentm/python-markdown2/wiki/Extras
     extras = {
-        "break-on-newline": {}, 
+        "break-on-newline": {},
         "fenced-code-blocks": {},
-        "header-ids": {}, # Adds "id" attributes to headers. value is slug of the header text.
-        "target-blank-links": {}, # Add target="_blank" to all <a> tags with an href. 
-        "task_list": {}, # Allows github-style task lists (i.e. check boxes),
+        "header-ids": {},  # Adds "id" attributes to headers. value is slug of the header text.
+        "target-blank-links": {},  # Add target="_blank" to all <a> tags with an href.
+        "task_list": {},  # Allows github-style task lists (i.e. check boxes),
         "html-classes": {
-            "img": "img-fluid" # add bootstrap class img-fluid to all images (so they dont overflow)
-        }
+            "img": "img-fluid"  # add bootstrap class img-fluid to all images (so they dont overflow)
+        },
     }
     markdowner = Markdown(extras=extras)
     html = markdowner.convert(markdown)
-
-    html = re.sub('<script[^>]*>.*</script>', '', html, re.IGNORECASE)
-    html = re.sub('<iframe[^>]*>.*</iframe>', '', html, re.IGNORECASE)    
+    html = cleanup_html(html)
 
     return html
 


Test patch:
diff --git a/tutorials/tests.py b/tutorials/tests.py
--- a/tutorials/tests.py
+++ b/tutorials/tests.py
@@ -1,35 +1,37 @@
 import re
+
 from django.test import TestCase
+
 from tutorials.views import formattedMarkdown
 
+
 def md_to_html(md):
     # if md is list: assume these are lines and join them
     if isinstance(md, list):
-        md = '\n'.join(md)
+        md = "\n".join(md)
     html = formattedMarkdown(md)
     # make lowercase for comparison
     html = html.lower()
     # remove tag attributes for testing
-    html = re.sub('<([^> ]+)(|[^>]*)>', r'<\1>', html)
+    html = re.sub("<([^> ]+)(|[^>]*)>", r"<\1>", html)
     # remove additional <p> and <br> tags for testing
-    html = re.sub('</?p>', '', html)
+    html = re.sub("</?p>", "", html)
     # <br/> to <br>
-    html = re.sub('<br/>', '<br>', html)
+    html = re.sub("<br/>", "<br>", html)
     # remove linebreaks spaces for test comparison
-    html = re.sub('\s+', '', html).strip()
+    html = re.sub("\s+", "", html).strip()
 
     return html
 
-class TestMarkdown(TestCase):
 
-    
+class TestMarkdown(TestCase):
     def test_formatted_markdown(self):
         """simple test that basic conversion works"""
         html = md_to_html("# header")
-        self.assertEqual(html, "<h1>header</h1>")    
+        self.assertEqual(html, "<h1>header</h1>")
 
     def test_raw_html(self):
-        # https://spec.commonmark.org/0.30/#raw-html        
+        # https://spec.commonmark.org/0.30/#raw-html
         # usually, the result is enclosed in a <p> tag
 
         # inline
@@ -39,16 +41,25 @@ def test_raw_html(self):
         # block
         html = md_to_html(["<table>", "    <tr></tr>", "</table>"])
         self.assertEqual("<table><tr></tr></table>", html)
-    
+
     def test_blockquote(self):
         # https://spec.commonmark.org/0.30/#raw-html
         html = md_to_html(["> block line 1", "> block line 2"])
         self.assertEqual("<blockquote>blockline1<br>blockline2</blockquote>", html)
-    
+
     def test_codeblock(self):
         html = md_to_html(["```python", "code", "```"])
         self.assertRegexpMatches(html, ".*<pre>.*<code>")
 
     def test_script(self):
-        html = md_to_html(["<script>alert('hi');</script>", "text", "<script>alert('hi');</script>"])
+        html = md_to_html(
+            ["<script>alert('hi');</script>", "text", "<script>alert('hi');</script>"]
+        )
         self.assertRegexpMatches(html, "text")
+
+    def test_autocorrect_html_964(self):
+        """automatically balance tags in raw html."""
+        html = md_to_html(["<div>test</div></div>"])
+        self.assertEquals(html, "<div>test</div>")
+        html = md_to_html(["<div><div>test</div>"])
+        self.assertEquals(html, "<div><div>test</div></div>")