Skip to content

Commit

Permalink
feat(vocutil): implement fill-in-the blank items
Browse files Browse the repository at this point in the history
Implement fill-in-the-blank items with the `Item` interface.

Github-closes: #10
Signed-off-by: Jeremy A Gray <[email protected]>
  • Loading branch information
jeremyagray committed Jan 13, 2025
1 parent 60decd8 commit 6a66df1
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 161 deletions.
3 changes: 2 additions & 1 deletion vocutil/cc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# ******************************************************************************

"""vocutil common cartridge module interface."""
"""vocutil Common Cartridge module interface."""

from .assessment import Assessment
from .bank import Bank
Expand All @@ -23,3 +23,4 @@
from .resources import HTMLFileResource
from .resources import QuestionBankResource
from .resources import Resource
from .tf import TrueFalse
149 changes: 89 additions & 60 deletions vocutil/cc/fib.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# vocutil, educational vocabulary utilities.
#
# Copyright 2022-2024 Jeremy A Gray <[email protected]>.
# Copyright 2022-2025 Jeremy A Gray <[email protected]>.
#
# All rights reserved.
#
Expand All @@ -12,6 +12,7 @@

"""Common Cartridge fill-in-the-blank item."""

import json
from xml.etree.ElementTree import Element as ETElement # nosec B405
from xml.etree.ElementTree import SubElement as ETSubElement # nosec B405

Expand All @@ -23,12 +24,24 @@
class FillInTheBlank(Item):
"""A Common Cartridge fill in the blank item."""

def __init__(self, question, answers, case="No", **kwargs):
"""Initialize a fill in the item."""
def __init__(self, question, answers, default_case="No", **kwargs):
"""Initialize a ``FillInTheBlank`` item.
Initialize a ``FillInTheBlank`` item.
Parameters
----------
question : str
The HTML formatted question text.
answers : [obj]
The correct answer(s).
"""
# Call the super.
super().__init__(**kwargs)

self.question = question
self.answers = answers
self.case = case if case == "Yes" else "No"
self.default_case = default_case if default_case == "Yes" else "No"

self.item = ETElement("item", ident=str(self.uuid))
self.itemmetadata = ETSubElement(self.item, "itemmetadata")
Expand Down Expand Up @@ -75,69 +88,85 @@ def __init__(self, question, answers, case="No", **kwargs):
varequal = ETSubElement(
condvar,
"varequal",
case=self.case,
case=answer["case"] if "case" in answer else self.default_case,
respident=f"fib-resp-{str(self.uuid)}",
)
varequal.text = answer
varequal.text = answer["answer"]

setvar = ETSubElement(cond, "setvar", action="Set", varname="SCORE")
setvar.text = "100"

return

def to_xml(self):
"""Initialize a fill in the blank item."""
item = ETElement("item", ident=str(self.uuid))
itemmetadata = ETSubElement(item, "itemmetadata")
qtimetadata = ETSubElement(itemmetadata, "qtimetadata")
qtimetadatafield = ETSubElement(qtimetadata, "qtimetadatafield")
fieldlabel = ETSubElement(qtimetadatafield, "fieldlabel")
fieldlabel.text = "cc_profile"
fieldentry = ETSubElement(qtimetadatafield, "fieldentry")
fieldentry.text = "cc.fib.v0p1"
presentation = ETSubElement(item, "presentation")
material = ETSubElement(presentation, "material")
mattext = ETSubElement(material, "mattext", texttype="text/html")
mattext.text = self.question

response = ETSubElement(
presentation,
"response_str",
rcardinality="Single",
ident=f"fib-resp-{str(self.uuid)}",
@classmethod
def from_json(cls, item, **kwargs):
"""Create a ``FillInTheBlank`` item from JSON data.
Create a ``FillInTheBlank`` item from JSON fill-in-the-blank
item data.
Parameters
----------
cls
The ``FillInTheBlank`` class.
item : str
A string containing JSON fill-in-the-blank data.
"""
data = json.loads(item)

return cls(data["question"], data["answers"], **kwargs)

@classmethod
def from_xml(cls, item, **kwargs):
"""Create a ``FillInTheBlank`` item XML data.
Create a ``FillInTheBlank`` item from IMSCC fill-in-the-blank
XML data.
Parameters
----------
cls
The ``FillInTheBlank`` class.
item : str
A string containing IMSCC fill-in-the-blank XML data.
"""
tree = ET.fromstring(item)

# Question text.
q = tree.find("presentation").find("material").find("mattext").text

# Correct answers.
a = [
{
"answer": ele.text,
"case": ele.get("case"),
}
for ele in tree.find("resprocessing")
.find("respcondition")
.find("conditionvar")
.findall("varequal")
]

return cls(q, a, **kwargs)

def to_json(self):
"""Create a fill-in-the-blank JSON string from item data."""
return json.dumps(
{
"question": str(self.mattext.text),
"answers": [
{
"answer": ele.text,
"case": ele.get("case"),
}
for ele in self.item.find("resprocessing")
.find("respcondition")
.find("conditionvar")
.findall("varequal")
],
}
)
ETSubElement(response, "render_fib", prompt="Dashline")

grade = ETSubElement(item, "resprocessing")
outcomes = ETSubElement(grade, "outcomes")
ETSubElement(
outcomes,
"decvar",
maxvalue="100",
minvalue="0",
varname="SCORE",
vartype="Decimal",
)
cond = ETSubElement(
grade,
"respcondition",
attrib={
"continue": "No",
},
)
condvar = ETSubElement(cond, "conditionvar")

# Set possible correct answers.
for answer in self.answers:
varequal = ETSubElement(
condvar,
"varequal",
case=self.case,
respident=f"fib-resp-{str(self.uuid)}",
)
varequal.text = answer

setvar = ETSubElement(cond, "setvar", action="Set", varname="SCORE")
setvar.text = "100"

return ET.tostring(item, encoding="unicode")
def to_xml(self):
"""Create a fill-in-the-blank XML string from item data."""
return ET.tostring(self.item, encoding="unicode")
83 changes: 50 additions & 33 deletions vocutil/cc/mc.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,16 @@ class MultipleChoice(Item):
"""A Common Cartridge multiple choice item."""

def __init__(self, question, answers, **kwargs):
"""Initialize a multiple choice item.
"""Initialize a ``MultipleChoice`` item.
Initialize a multiple choice item by storing the question and
answer data as plain Python objects for later output and as
IMSCC XML elements.
Initialize a ``MultipleChoice`` item.
Parameters
----------
question : str
The HTML formatted question text.
answers : [obj]
The possible answers.
"""
# Call the super.
super().__init__(**kwargs)
Expand Down Expand Up @@ -93,36 +98,34 @@ def __init__(self, question, answers, **kwargs):
return

@classmethod
def from_json(cls, data, **kwargs):
"""Instantiate from JSON data.
def from_json(cls, item, **kwargs):
"""Create a ``MultipleChoice`` item from JSON data.
Instantiate from JSON data.
Create a ``MultipleChoice`` item from JSON data.
Parameters
----------
cls
The ``MultipleChoice`` class.
data : str
A string containing JSON data with which to generate the
PDF.
item : str
A string containing JSON multiple choice data.
"""
d = json.loads(data)
data = json.loads(item)

return cls(d["question"], d["answers"], **kwargs)
return cls(data["question"], data["answers"], **kwargs)

@classmethod
def from_xml(cls, item, **kwargs):
"""Instantiate from IMSCC multiple choice item XML data.
"""Create a ``MultipleChoice`` item from XML data.
Instantiate from IMSCC multiple choice item XML data.
Create a ``MultipleChoice`` item from XML data.
Parameters
----------
cls
The ``MultipleChoice`` class.
item : str
A string containing IMSCC multiple choice XML data with
which to generate the item.
A string containing IMSCC multiple choice XML data.
"""
tree = ET.fromstring(item)

Expand All @@ -139,31 +142,45 @@ def from_xml(cls, item, **kwargs):
)

# Answer choices.
a = []
for ele in (
tree.find("presentation")
.find("response_lid")
.find("render_choice")
.findall("response_label")
):
a.append(
{
"answer": ele.find("material").find("mattext").text,
"correct": True if correct == ele.get("ident") else False,
}
a = [
{
"answer": ele.find("material").find("mattext").text,
"correct": True if correct == ele.get("ident") else False,
}
for ele in (
tree.find("presentation")
.find("response_lid")
.find("render_choice")
.findall("response_label")
)
]

return cls(q, a, **kwargs)

def to_json(self):
"""Create JSON string from item data."""
"""Create a multiple choice JSON string from item data."""
# Correct answer.
correct = (
self.item.find("resprocessing")
.find("respcondition")
.find("conditionvar")
.find("varequal")
.text
)

return json.dumps(
{
"question": str(self.question),
"answers": self.answers,
"question": self.mattext.text,
"answers": [
{
"answer": ele.find("material").find("mattext").text,
"correct": True if correct == ele.get("ident") else False,
}
for ele in self.choices.findall("response_label")
],
}
)

def to_xml(self):
"""Create XML string from item data."""
return ET.tostring(self.item, encoding="unicode", xml_declaration=True)
"""Create a multiple choice XML string from item data."""
return ET.tostring(self.item, encoding="unicode")
Loading

0 comments on commit 6a66df1

Please sign in to comment.