Skip to content

Commit

Permalink
add mathml support to qti
Browse files Browse the repository at this point in the history
closes QUIZ-3355

test plan:
- cr
- import a qti package with mathml block
- imported question should have rendered latex

Change-Id: I1c8f49197ad1294db32eb226b2951b37050d86d7
Reviewed-on: https://gerrit.instructure.com/144873
Tested-by: Jenkins
Reviewed-by: Steve Kacsmark <[email protected]>
QA-Review: Steve Kacsmark <[email protected]>
Product-Review: Han Yan <[email protected]>
  • Loading branch information
han-yan committed Mar 27, 2018
1 parent 0e4b762 commit 401e23e
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 6 deletions.
16 changes: 16 additions & 0 deletions lib/qti/models/base.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'nokogiri'
require 'sanitize'
require 'pathname'
require 'mathml2latex'

module Qti
class ParseError < StandardError; end
Expand Down Expand Up @@ -62,6 +63,21 @@ def initialize(path:, package_root: nil, html: false)
self.package_root = package_root || File.dirname(path)
@doc = html ? parse_html(File.read(path)) : parse_xml(File.read(path))
raise ArgumentError unless @doc
preprocess_xml_doc(@doc) unless html
end

def preprocess_xml_doc(xml_doc)
converter = Mathml2latex::Converter.new
converter.replace_with_latex(xml_doc)
nodes = xml_doc.xpath('//mm:latex', 'mm' => Mathml2latex::INSTUCTURE_LATEX_NS)

nodes.each do |node|
# convert all #160 space to regular #32 whitespace
# latex parser won't work for #160 space
text = node.text.gsub(/\u00a0/, ' ')
latex_string = "&#160;\\(#{text}\\)&#160;"
node.replace(latex_string)
end
end

def xpath_with_single_check(xpath)
Expand Down
2 changes: 1 addition & 1 deletion lib/qti/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Qti
VERSION = '0.9.11'.freeze
VERSION = '0.9.12'.freeze
end
1 change: 1 addition & 0 deletions qti.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Gem::Specification.new do |s|
s.add_dependency 'nokogiri', '>= 1.6.8', '< 1.9'
s.add_dependency 'sanitize', '>= 4.2.0', '< 5.0'
s.add_dependency 'actionview', '>= 4.2.0'
s.add_dependency 'mathml2latex', '>= 0.1.0'

s.add_development_dependency 'bundler', '~> 1.15'
s.add_development_dependency 'byebug', '~> 9.0'
Expand Down
93 changes: 93 additions & 0 deletions spec/fixtures/mathml/numeric_1.2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<questestinterop xmlns="http://www.imsglobal.org/xsd/ims_qtiasiv1p2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/ims_qtiasiv1p2 http://www.imsglobal.org/xsd/ims_qtiasiv1p2p1.xsd"
xmlns:m="http://www.w3.org/1998/Math/MathML">
<assessment ident="i98661073a66c6b2567c8f9b1a22c2988" title="Quiz1.1">
<section ident="root_section">
<item ident="ia63b6a115fa68a555398a58626a62f81" title="Question1">
<presentation>
<material>
<mattext texttype="text/html">&lt;div&gt;&lt;p&gt;question 1&lt;/p&gt;&lt;/div&gt;</mattext>
</material>
<response_str ident="response1" rcardinality="Single">
<render_fib fibtype="Decimal">
<response_label ident="answer1"/>
</render_fib>
</response_str>
</presentation>
<resprocessing>
<outcomes>
<decvar maxvalue="100" minvalue="0" varname="SCORE" vartype="Decimal"/>
</outcomes>
<respcondition continue="No">
<conditionvar>
<or>
<varequal respident="response1">1234.0</varequal>
<and>
<vargte respident="response1">1234.0</vargte>
<varlte respident="response1">1234.0</varlte>
</and>
</or>
</conditionvar>
<setvar action="Set" varname="SCORE">100</setvar>
</respcondition>
</resprocessing>
</item>
<item ident="i62e978ad510f5b51f61e1073df6357df" title="Question2">
<presentation>
<material>
<mattext texttype="text/html">&lt;div&gt;&lt;p&gt;question 2: &lt;/p&gt;&lt;/div&gt;</mattext>
<m:math>
<m:mfrac><m:mn>1</m:mn><m:mn>2</m:mn></m:mfrac><m:mo>&#xD7;</m:mo><m:mroot><m:mn>379</m:mn><m:mn>10</m:mn></m:mroot>
</m:math>
</material>
<response_str ident="response1" rcardinality="Single">
<render_fib fibtype="Decimal">
<response_label ident="answer1"/>
</render_fib>
</response_str>
</presentation>
<resprocessing>
<outcomes>
<decvar maxvalue="100" minvalue="0" varname="SCORE" vartype="Decimal"/>
</outcomes>
<respcondition continue="No">
<conditionvar>
<or>
<varequal respident="response1">4321.0</varequal>
<and>
<vargte respident="response1">4321.0</vargte>
<varlte respident="response1">4321.0</varlte>
</and>
</or>
</conditionvar>
<setvar action="Set" varname="SCORE">100</setvar>
</respcondition>
<respcondition continue="No">
<conditionvar>
<or>
<varequal respident="response1">1234.0</varequal>
<and>
<vargte respident="response1">1234.0</vargte>
<varlte respident="response1">1234.0</varlte>
</and>
</or>
</conditionvar>
<setvar action="Set" varname="SCORE">100</setvar>
</respcondition>
<respcondition continue="No">
<conditionvar>
<or>
<varequal respident="response1">1111.0</varequal>
<and>
<vargte respident="response1">1111.0</vargte>
<varlte respident="response1">1111.0</varlte>
</and>
</or>
</conditionvar>
<setvar action="Set" varname="SCORE">100</setvar>
</respcondition>
</resprocessing>
</item>
</section>
</assessment>
</questestinterop>
23 changes: 18 additions & 5 deletions spec/lib/qti/models/base_spec.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
require 'spec_helper'

describe Qti::Models::Base do
let(:loaded_class) do
path = File.join('spec', 'fixtures', 'test_qti_2.2', 'assessment.xml')
described_class.from_path!(path)
end

context 'specified as single content node matching helpers' do
let(:loaded_class) do
path = File.join('spec', 'fixtures', 'test_qti_2.2', 'assessment.xml')
described_class.from_path!(path)
end

let(:content_stub) { Struct.new(:content, :thing) }

# "assessmentItemRef" should match 4 nodes, and therefore raise for these helpers
Expand Down Expand Up @@ -89,4 +89,17 @@
end
end
end

context 'preprocessing' do
let(:loaded_class) do
path = File.join('spec', 'fixtures', 'mathml', 'numeric_1.2.xml')
described_class.from_path!(path)
end
let(:expected_latex) { '\\(\\frac{1}{2}\\times \\sqrt[10]{379}\\)' }

it 'replaces any mathml block' do
obj = loaded_class
expect(obj.instance_variable_get('@doc').to_s).to include(expected_latex)
end
end
end

0 comments on commit 401e23e

Please sign in to comment.