Skip to content
Snippets Groups Projects

Toggling pregrading and Identifying blank solutions

Open Ghost User requested to merge feature/toggle-pregrading into develop
Compare and Show latest version
1 file
+ 1
9
Compare changes
  • Side-by-side
  • Inline
+ 131
9
import cv2
import numpy as np
from datetime import datetime
from .database import db
from .blanks import get_blank
from .database import db, Grader, FeedbackOption, GradingPolicy
from .images import guess_dpi, get_box
from .pdf_generation import CHECKBOX_FORMAT
def grade_mcq(sub, page, page_img):
def grade_problem(sub, page, page_img):
"""
Adds the multiple choice options that are identified as marked as a feedback option to a solution
Automatically checks if a problem is blank, and adds a feedback option
'blank' if so.
For multiple choice problems, a feedback option is added for each checkbox
that is identified as filled in is created.
Parameters
------
@@ -21,20 +26,137 @@ def grade_mcq(sub, page, page_img):
"""
solutions_to_grade = [
sol for sol in sub.solutions
if not sol.graded_at and sol.problem.widget.page == page
if (not sol.graded_by or sol.graded_by.name == 'Zesje') and sol.problem.widget.page == page
]
for sol in solutions_to_grade:
for mc_option in sol.problem.mc_options:
box = (mc_option.x, mc_option.y)
sol.feedback = []
problem = sol.problem
if box_is_filled(box, page_img, box_size=CHECKBOX_FORMAT["box_size"]):
feedback = mc_option.feedback
sol.feedback.append(feedback)
if problem.mc_options:
grade_mcq(sol, page_img)
elif is_blank(problem, page_img, sub):
grade_as_blank(sol)
db.session.commit()
def grade_mcq(sol, page_img):
"""
Pre-grades a multiple choice problem.
This function does either of two things:
- Adds a feedback option 'blank' if no option has been detected as filled
- Adds a feedback option for every option that has been detected as filled
In both cases, a grader named 'Zesje' is set for this problem.
Parameters
----------
sol : Solution
The solution to the multiple choice question
page_img: np.array
A numpy array of the image scan
"""
box_size = CHECKBOX_FORMAT["box_size"]
problem = sol.problem
mc_filled_counter = 0
for mc_option in problem.mc_options:
box = (mc_option.x, mc_option.y)
if box_is_filled(box, page_img, box_size=box_size):
feedback = mc_option.feedback
sol.feedback.append(feedback)
mc_filled_counter += 1
if mc_filled_counter == 0:
grade_as_blank(sol)
if mc_filled_counter == 1 and problem.grading_policy == GradingPolicy.set_blank_single:
set_auto_grader(sol)
db.session.commit()
def grade_as_blank(sol):
"""
Pre-grades a solution as identified as blank.
Parameters
----------
sol : Solution
The solution to pre-grade
"""
if sol.problem.grading_policy != GradingPolicy.set_nothing:
set_auto_grader(sol)
feedback = FeedbackOption.query.filter(FeedbackOption.problem_id == sol.problem.id,
FeedbackOption.text == 'blank').one_or_none()
if not feedback:
feedback = FeedbackOption(problem_id=sol.problem.id, text='blank', score=0)
db.session.add(feedback)
sol.feedback.append(feedback)
db.session.commit()
def set_auto_grader(solution):
"""
Sets the grader to 'Zesje', meaning that a question is
considered automatically graded.
To ensure a solution is graded manually, the grader of a solution
is set to a grader named Zesje. That way, the detected option is
not set as 'ungraded'.
Parameters
----------
solution : Solution
The solution
"""
zesje_grader = Grader.query.filter(Grader.name == 'Zesje').one_or_none() or Grader(name='Zesje')
solution.graded_by = zesje_grader
solution.graded_at = datetime.now()
db.session.commit()
def is_blank(problem, page_img, sub):
dpi = guess_dpi(page_img)
# get the box where we think the box is
widget_area = np.asarray([
problem.widget.y, # top
problem.widget.y + problem.widget.height, # bottom
problem.widget.x, # left
problem.widget.x + problem.widget.width, # right
])
widget_area_in = widget_area / 72
cut_im = get_box(page_img, widget_area_in, padding=0)
reference = get_blank(problem, dpi, widget_area_in, sub)
blank_image = np.array(reference)
blank_image = cv2.cvtColor(blank_image, cv2.COLOR_BGR2GRAY)
blank_image = np.array(blank_image)
input_image = np.array(cut_im)
input_image = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
input_image = np.array(input_image)
n = 0
max = input_image.shape[0]
while n + 50 < max:
m = n + 50
if np.average(~input_image[n: m]) > (1.03 * np.average(~blank_image[n: m])):
return False
n = m
return not(np.average(~input_image[n: max-1]) > (1.03 * np.average(~blank_image[n: max-1])))
def box_is_filled(box, page_img, threshold=225, cut_padding=0.05, box_size=9):
"""
A function that finds the checkbox in a general area and then checks if it is filled in.
Loading