Skip to content

Commit

Permalink
v0.5
Browse files Browse the repository at this point in the history
  • Loading branch information
28left committed Apr 11, 2023
1 parent adb634d commit 03573a1
Show file tree
Hide file tree
Showing 19 changed files with 235 additions and 9 deletions.
Binary file modified dist/cyllene-0.4.5-py3-none-any.whl
Binary file not shown.
Binary file modified dist/cyllene-0.4.5.tar.gz
Binary file not shown.
Binary file added dist/cyllene-0.5-py3-none-any.whl
Binary file not shown.
Binary file added dist/cyllene-0.5.tar.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion pyproject.toml
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "cyllene"
version = "0.4.5"
version = "0.5"
description = "IPython package for math problem authoring"
readme = "README.md"
authors = [{ name = "Jan Reimann", email = "jan.reimann@psu.edu" }]
Expand Down
2 changes: 1 addition & 1 deletion src/cyllene.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: cyllene
Version: 0.4.5
Version: 0.5
Summary: IPython package for math problem authoring
Author-email: Jan Reimann <jan.reimann@psu.edu>
License: MIT License
Expand Down
2 changes: 2 additions & 0 deletions src/cyllene.egg-info/SOURCES.txt
Expand Up @@ -19,6 +19,7 @@ src/cyllene/MathFunctions/math_string.py
src/cyllene/MathFunctions/math_table.py
src/cyllene/MathProblems/problem_aux.py
src/cyllene/MathProblems/problem_basic.py
src/cyllene/MathProblems/problem_handler.py
src/cyllene/MathProblems/problem_instantiator.py
src/cyllene/MathProblems/problem_parameter.py
src/cyllene/MathProblems/problem_parametermultchoice.py
Expand All @@ -36,6 +37,7 @@ src/cyllene/user/problem_stack.py
src/cyllene/widgets/ipyvuetify_multiplechoice.py
src/cyllene/widgets/vue_problem_basic.py
src/cyllene/widgets/vue_problem_parameter.py
src/cyllene/widgets/widget_viewer.py
src/cyllene/widgets/widgets_aux.py
src/cyllene/widgets/widgets_problem_basic.py
src/cyllene/widgets/widgets_problem_param.py
Binary file modified src/cyllene/MathProblems/__pycache__/problem_basic.cpython-310.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
18 changes: 15 additions & 3 deletions src/cyllene/MathProblems/problem_basic.py
Expand Up @@ -57,6 +57,10 @@ def check_answer(self, answer_string):

return self.answer == answer_string

@property
def has_solution(self):
return bool(self.solution)


class MultipleChoice(Problem):

Expand All @@ -82,6 +86,9 @@ def __init__(self, my_dict={}):
if not isinstance(self.choices, list):
self.choices = [self.choices]

# shuffle answers and store them in a separate list
self.shuffle_answers()

@property
def num_choices(self):
return len(self.choices)
Expand All @@ -97,10 +104,15 @@ def load_dict(self, my_dict):
if len(self.choices) > 0:
self.answer = str(self.choices[0])

def shuffle_choices(self):
# def shuffle_choices(self):

# shuffled_choices = self.choices
# return random.shuffle(shuffled_choices)

shuffled_choices = self.choices
return random.shuffle(shuffled_choices)
def shuffle_answers(self):
self.indices = [i for i in range(self.num_choices)]
random.shuffle(self.indices)
self.correct = self.indices.index(0)


class ExpressionProblem(Problem):
Expand Down
43 changes: 43 additions & 0 deletions src/cyllene/MathProblems/problem_handler.py
@@ -0,0 +1,43 @@
import random
# from ..MathProblems.problem_basic import Problem, MultipleChoice
from ..MathProblems.problem_parameter import ParameterProblem
# from ..MathProblems.problem_parametermultchoice import MultipleChoiceParameterProblem
# from .widgets_aux import update_output_widget
"""
Defines a class for basic problem handling: statement, type, answer
and checking
"""


class ProblemHandler:
"""
Pass a problem instance (or a list thereof) and display it using corresponding widgets
"""

def __init__(self, problems: list):

self.problems = problems
self.status = 'undecided'
self.check = []
self.regenerates = False
self.current_problem = None

# determine whether several problems are present and whether they regenerate
if self.has_problem:
if len(problems) == 1:
if isinstance(problems[0], ParameterProblem):
self.regenerates = True
else:
self.regenerates = True

@property
def has_problem(self):
return bool(self.problems)

def select_current_problem(self):
if not self.has_problem:
return

self.current_problem = random.choice(self.problems)
if isinstance(self.current_problem, ParameterProblem):
self.current_problem = self.current_problem.get_problem()
2 changes: 1 addition & 1 deletion src/cyllene/__init__.py
@@ -1,4 +1,4 @@
__version__ = "0.4.5"
__version__ = "0.5"

from .user.problem_stack import ProbStack
from .magics import problem_magics
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
168 changes: 168 additions & 0 deletions src/cyllene/widgets/widget_viewer.py
@@ -0,0 +1,168 @@
from IPython.display import display, Markdown, clear_output
import ipywidgets as widgets
import markdown
from ..MathProblems.problem_handler import ProblemHandler
from ..MathProblems.problem_basic import MultipleChoice
from ..MathProblems.problem_parameter import ParameterProblem
from ..MathProblems.problem_parametermultchoice import MultipleChoiceParameterProblem
from .widgets_aux import update_output_widget
"""
Defines a class for basic problem handling: statement, type, answer
and checking
"""

COLORS = ['primary', 'success', 'info', 'warning', 'danger']


class WidgetViewer:
"""
Pass a ProblemHandler instance and create a viewer using corresponding widgets
"""

def __init__(self, problem: ProblemHandler):

self.problem = problem
self.problem.select_current_problem()

# initialize output widgets for various areas
self.title = widgets.Output()
self.statement = widgets.Output()
self.user_answer_area = widgets.Output()
self.button_area = widgets.Output()
self.feedback = widgets.Output(layout=widgets.Layout(height='50px'))
self.solution_text = widgets.Output()
self.solution_area = widgets.Accordion(children=[self.solution_text])

self.new_button = widgets.Button(description='New Version',
disabled=False)
self.new_button.on_click(self.new_button_clicked)

def new_button_clicked(self, bt):

self.problem.select_current_problem()

# update display
self.build_problem_widget()

def build_title(self):
update_output_widget(self.title, '### ' +
self.problem.current_problem.title)

def build_statement(self):
update_output_widget(
self.statement, self.problem.current_problem.statement)

def build_solution(self):
with self.solution_text:
clear_output()
display(widgets.HTMLMath(
markdown.markdown(self.problem.current_problem.solution)))

self.solution_area.selected_index = None
self.solution_area.set_title(0, 'Show Solution')

def build_user_answer(self):

if isinstance(self.problem.current_problem, MultipleChoice):
self.problem.current_problem.shuffle_answers()
with self.user_answer_area:
clear_output()

for i in range(self.problem.current_problem.num_choices):
display(widgets.HTMLMath(
value='<b>( ' + str(i+1) + ' )</b> &nbsp;&nbsp; ' + self.problem.current_problem.choices[self.problem.current_problem.indices[i]] + "&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;"))

def build_choice_buttons(self):

# create buttons
self.choice_buttons = [
widgets.Button(
description='( '+str(i+1)+' )',
disabled=False,
button_style=COLORS[i],
tooltip='Answer choice '+str(i+1),
layout=widgets.Layout(flex='1 1 auto', width='auto'))
for i in range(self.problem.current_problem.num_choices)]

# Activate handler for every button
for button in self.choice_buttons:
# link to a click event function
button.on_click(self.on_choice_button_clicked)

# display buttons in button area widget
with self.button_area:
clear_output()
display(widgets.HBox(self.choice_buttons))

def on_choice_button_clicked(self, bt):

self.check_answer(bt.description[2:-2])

def check_answer(self, answer):

# reset current answer check
self.check = []

# Pre-process answer string to remove parentheses (if present)
answer = answer[0]
if len(answer) > 0 and answer[0] == '(':
answer = answer[1:]
if len(answer) > 0 and answer[-1] == ')':
answer = answer[:-1]

try:
if self.problem.current_problem.correct == int(answer)-1:
self.check.append(True)
else:
self.check.append(False)
except ValueError:
self.check.append('Error')

if self.check[0] == True:
result_string = 'You selected: ('+answer+') -- ' + \
'&#9989; &nbsp; **Correct!**'
self.status = 'correct'
elif self.check[0] == False:
result_string = 'You selected: ('+answer+') -- ' + \
'&#10060; &nbsp; **Incorrect**'
self.status = 'incorrect'
else:
result_string = 'Please enter an integer value.'
self.status = 'undecided'

# show feedback
with self.feedback:
clear_output()
display(Markdown(result_string))

def build_problem_widget(self):

# build widgets and add to widget list depending on current problem type
self.build_title()
self.widget_list = [self.title]

if self.problem.regenerates:
self.widget_list.append(self.new_button)

self.build_statement()
self.widget_list.append(self.statement)

self.build_user_answer()
self.widget_list.append(self.user_answer_area)

if isinstance(self.problem.current_problem, MultipleChoice):
self.build_choice_buttons()
self.widget_list.append(self.button_area)

with self.feedback:
clear_output()
display(Markdown('Please select your answer.'))
self.widget_list.append(self.feedback)

if self.problem.current_problem.has_solution:
self.build_solution()
self.widget_list.append(self.solution_area)

def show(self):
self.build_problem_widget()
display(widgets.VBox(self.widget_list))
5 changes: 3 additions & 2 deletions src/cyllene/widgets/widgets_problem_basic.py
Expand Up @@ -14,7 +14,7 @@

class ProblemWidget:
"""
Show a basic widget displaying title and statement
Show a basic widget displaying title, statement, and solution
"""

def __init__(self, problem: Problem):
Expand Down Expand Up @@ -43,7 +43,8 @@ def show(self):
self.statement, self.problem.statement)

display(widgets.VBox([self.title, self.statement]))
display(self.solution)
if self.has_solution:
display(self.solution)


class MultipleChoiceWidget(ProblemWidget):
Expand Down
2 changes: 1 addition & 1 deletion src/cyllene/widgets/widgets_problem_param.py
Expand Up @@ -72,7 +72,7 @@ def update_problem_area(self):

for i in range(self.problem.num_choices):
display(widgets.HTMLMath(
value='<b>( ' + str(i+1) + ')</b> &nbsp;&nbsp; ' + self.problem.choices[self.indices[i]] + "&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;"))
value='<b>( ' + str(i+1) + ' )</b> &nbsp;&nbsp; ' + self.problem.choices[self.indices[i]] + "&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;"))

def new_button_clicked(self, bt):

Expand Down

0 comments on commit 03573a1

Please sign in to comment.