Source code for questioned.questions.parsons_problem
"""
This module defines the manual entry question.
"""
import random
import html
from questioned.utils import select_questions
from .question import Question
[docs]class ParsonsProblem(Question):
"""
Defines a parsons problem that is input manually using the exam_spec file.
This question type is based on a paper by Paul Denny, Andrex Luxton-Reilly
and Beth Simon. The paper can be found at
https://cseweb.ucsd.edu/classes/fa08/cse599/denny.pdf
Though the basic structure of this question type is similar to the one
described in the paper. It does not support the more complex rubrics that
would require a human to correct the exam.
Instead, it is best to focus on shorter code snippets where ordering is
the main focus of the problem.
The parsons problem requires additional information through the ``exam_spec``.
For example:
::
parsons_problems:
- description: "Pyramid printing function."
code: |
#include<stdio.h>
int main() {
int i, j, rows;
printf("Enter number of rows: ");
scanf("%d", &rows);
for (i=1; i<=rows; ++i) {
for (j=1; j<=i; ++j)
{ printf("* "); }
printf("\\n");}
return 0;}
All parsons problems require at least a description and a code segment.
The code segment is automatically broken up into lines and jumbled for
the assignment. As such it must be entered in the correct form.
"""
# The dynamic use of attributes in this class causes pylint to falsely flag no-member
# pylint: disable=no-member
[docs] def render_blackboard(self):
"""
Render the parsons problem for blackboard.
Uses the JUMBLED_SENTENCE type.
"""
# Create choice-answer pairs
ca_tuples = []
answer_lines = self.answer.split('\n')
question_lines = self.question.split('\n')
for line_nr in range(len(answer_lines)-1):
ca_tuples.append([line_nr, answer_lines[line_nr], question_lines[line_nr]])
if self.image is not None:
image = self.image
else:
image = ""
# In this case, this length is acceptable
# pylint: disable=line-too-long
out = f"JUMBLED_SENTENCE\t{image}Please reassemble the following code snippets to form a {self.description}.<br/><br/>"
out += "<pre>"
for line in question_lines:
if line.strip() == '':
continue
escaped_line = html.escape(
line.replace('[', '[')\
.replace(']', ']')
)
out += ' - '
out += escaped_line.strip()
out += '<br />'
out += '</pre>'
# Place variable fields:
for tup in ca_tuples:
out += f"[{tup[0]}]<br/>"
out += '\t'
# Add answers
for tup in random.sample(ca_tuples, k=len(ca_tuples)):
out += f'{tup[1]}\t{tup[0]}\t\t'
out = out[:-2] # Remove extraneous tabs
out += '\n'
return out
[docs] def render_markdown(self):
"""
Render the parsons problem to markdown.
"""
out = f"Please reassemble the following code snippets to form a {self.description}.\n\n"
for line in self.question.split('\n'):
out += f"\t {line.strip()}\n"
return out
[docs] @classmethod
def generate(cls, exam_spec, count: int = 1, section_data=None):
"""
Generates an amount of manually input questions.
"""
# False positive
# pylint: disable=unsubscriptable-object
if section_data is None:
section_data = {}
out = []
selection = select_questions(cls, exam_spec, 'parsons_problems', count, section_data)
for selected_problem in selection:
lines = selected_problem['code'].split('\n')
jumbled_lines = random.sample(lines, k=len(lines))
jumbled_str = "\n".join(jumbled_lines)
problem = cls(exam_spec,
jumbled_str,
selected_problem['code'],
description=selected_problem['description'],
question_data=selected_problem
)
out.append(problem)
return out