From 0840fe8e5cb65a57ae80ba2d33655bff6afa8caf Mon Sep 17 00:00:00 2001 From: Robert Raposa Date: Tue, 17 Jul 2018 05:38:11 -0400 Subject: [PATCH 1/2] Escape chemical equation in advanced problems. EDUCATOR-3206 --- common/lib/chem/chem/chemcalc.py | 33 ++++++++++++++++++++------------ common/lib/chem/chem/tests.py | 28 +++++++++++++++++++++++++-- common/lib/chem/setup.py | 3 ++- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/common/lib/chem/chem/chemcalc.py b/common/lib/chem/chem/chemcalc.py index dd4abefed2c0..3164a568daff 100644 --- a/common/lib/chem/chem/chemcalc.py +++ b/common/lib/chem/chem/chemcalc.py @@ -2,10 +2,12 @@ from fractions import Fraction +import markupsafe import nltk from nltk.tree import Tree from pyparsing import Literal, OneOrMore, ParseException, StringEnd + ARROWS = ('<->', '->') # Defines a simple pyparsing tokenizer for chemical equations @@ -26,6 +28,13 @@ tokens = reduce(lambda a, b: a ^ b, map(Literal, elements + digits + symbols + phases)) tokenizer = OneOrMore(tokens) + StringEnd() +# HTML, Text are temporarily copied from openedx.core.djangolib.markup +# These libraries need to be moved out of edx-platform to be used by +# other applications. +# See LEARNER-5853 for more details. +Text = markupsafe.escape # pylint: disable=invalid-name +def HTML(html): # pylint: disable=invalid-name + return markupsafe.Markup(html) def _orjoin(l): return "'" + "' | '".join(l) + "'" @@ -166,20 +175,20 @@ def molecule_count(tree, children): return tree[0][0] # If a fraction, return the fraction if len(tree) == 3: - return " {num}{den} ".format(num=tree[0][0], den=tree[2][0]) + return HTML(" {num}{den} ").format(num=tree[0][0], den=tree[2][0]) return "Error" def subscript(tree, children): - return "{sub}".format(sub=children) + return HTML("{sub}").format(sub=children) def superscript(tree, children): - return "{sup}".format(sup=children) + return HTML("{sup}").format(sup=children) def round_brackets(tree, children): - return "({insider})".format(insider=children) + return HTML("({insider})").format(insider=children) def square_brackets(tree, children): - return "[{insider}]".format(insider=children) + return HTML("[{insider}]").format(insider=children) dispatch = {'count': molecule_count, 'number_suffix': subscript, @@ -190,7 +199,7 @@ def square_brackets(tree, children): if isinstance(tree, str): return tree else: - children = "".join(map(_render_to_html, tree)) + children = HTML("").join(map(_render_to_html, tree)) if tree.label() in dispatch: return dispatch[tree.label()](tree, children) else: @@ -207,20 +216,20 @@ def err(s): """ Render as an error span """ - return '{0}'.format(s) + return HTML('{0}').format(s) def render_arrow(arrow): """ Turn text arrows into pretty ones """ if arrow == '->': - return u'\u2192' + return HTML(u'\u2192') if arrow == '<->': - return u'\u2194' + return HTML(u'\u2194') # this won't be reached unless we add more arrow types, but keep it to avoid explosions when - # that happens. - return arrow + # that happens. HTML-escape this unknown arrow just in case. + return Text(arrow) def render_expression(ex): """ @@ -232,7 +241,7 @@ def render_expression(ex): return err(ex) def spanify(s): - return u'{0}'.format(s) + return HTML(u'{0}').format(s) left, arrow, right = split_on_arrow(eq) if arrow == '': diff --git a/common/lib/chem/chem/tests.py b/common/lib/chem/chem/tests.py index 93f6132798af..7625095aa996 100644 --- a/common/lib/chem/chem/tests.py +++ b/common/lib/chem/chem/tests.py @@ -296,13 +296,20 @@ def test_render_error(self): log(out + ' ------- ' + correct, 'html') self.assertEqual(out, correct) - def test_render_simple_brackets(self): + def test_render_simple_round_brackets(self): test_string = "(Ar)" out = render_to_html(test_string) correct = u'(Ar)' log(out + ' ------- ' + correct, 'html') self.assertEqual(out, correct) + def test_render_simple_square_brackets(self): + test_string = "[Ar]" + out = render_to_html(test_string) + correct = u'[Ar]' + log(out + ' ------- ' + correct, 'html') + self.assertEqual(out, correct) + def test_render_eq1(self): test_string = "H^+ + OH^- -> H2O" out = render_to_html(test_string) @@ -320,7 +327,24 @@ def test_render_eq2(self): def test_render_eq3(self): test_string = "H^+ + OH^- <= H2O" # unsupported arrow out = render_to_html(test_string) - correct = u'H^+ + OH^- <= H2O' + correct = u'H^+ + OH^- <= H2O' + log(out + ' ------- ' + correct, 'html') + self.assertEqual(out, correct) + + def test_render_eq4(self): + test_string = "[H^+] + OH^- <-> (H2O)" # with brackets + out = render_to_html(test_string) + correct = u'[H+]+OH-\u2194(H2O)' + log(out + ' ------- ' + correct, 'html') + self.assertEqual(out, correct) + + def test_escaping(self): + """ + Tests that invalid input is escaped. + """ + test_string = "" + out = render_to_html(test_string) + correct = u'<script>f()</script>' log(out + ' ------- ' + correct, 'html') self.assertEqual(out, correct) diff --git a/common/lib/chem/setup.py b/common/lib/chem/setup.py index 005836c1f099..12f8bddefd7a 100644 --- a/common/lib/chem/setup.py +++ b/common/lib/chem/setup.py @@ -2,12 +2,13 @@ setup( name="chem", - version="0.1.2", + version="0.2.0", packages=["chem"], install_requires=[ "pyparsing==2.2.0", "numpy==1.6.2", "scipy==0.14.0", "nltk", + "markupsafe", # Should be replaced by other utilities. See LEARNER-5853 for more details. ], ) From edf1c8922e3408e96ff0cd39f602c4b63b76c91c Mon Sep 17 00:00:00 2001 From: Robert Raposa Date: Wed, 25 Jul 2018 11:34:10 -0400 Subject: [PATCH 2/2] Fix quality errors. --- common/lib/chem/chem/chemcalc.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/lib/chem/chem/chemcalc.py b/common/lib/chem/chem/chemcalc.py index 3164a568daff..196c99ae5ce0 100644 --- a/common/lib/chem/chem/chemcalc.py +++ b/common/lib/chem/chem/chemcalc.py @@ -33,9 +33,12 @@ # other applications. # See LEARNER-5853 for more details. Text = markupsafe.escape # pylint: disable=invalid-name + + def HTML(html): # pylint: disable=invalid-name return markupsafe.Markup(html) + def _orjoin(l): return "'" + "' | '".join(l) + "'"