Commit bd14cb89 authored by Anton Akhmerov's avatar Anton Akhmerov
Browse files

Merge branch '526-fix-email-unstructured' into 'master'

Fix email solution pdf in unstructured exams

Closes #526

See merge request !316
parents c69519f0 e8401c97
Pipeline #49092 passed with stages
in 4 minutes and 35 seconds
......@@ -4,18 +4,6 @@ import Notification from 'react-bulma-notification'
import SearchBox from '../../components/SearchBox.jsx'
import * as api from '../../api.jsx'
function withoutDuplicates (items, keyFn = (x => x)) {
let seenKeys = new Set()
let uniqueItems = []
for (const item of items) {
if (!seenKeys.has(keyFn(item))) {
uniqueItems.push(item)
seenKeys.add(keyFn(item))
}
}
return uniqueItems
}
class StudentControls extends React.Component {
state = {
students: []
......@@ -34,13 +22,15 @@ class StudentControls extends React.Component {
updateStudents = () => {
api.get('submissions/' + this.props.examID)
.then(submissions => {
// Need to de-duplicate, as some students
const students = withoutDuplicates(
submissions.map(s => s.student).filter(s => s !== null),
student => student.id
)
// filter list of students with validated submissions
const students = submissions.reduce((acc, sub) => {
return sub.validated && sub.student ? acc.concat(sub.student) : acc
}, [])
this.setState({ students })
this.props.setStudent(students[0] || null) // in case 'students' is empty
if (students.length !== submissions.length) {
Notification.warn('There are students with unvalidated submissions, go to the Students tab before sending emails to them.')
}
})
.catch(err => {
console.log(err)
......@@ -85,7 +75,7 @@ class StudentControls extends React.Component {
</div>
</div>
) : (
<p>No submissions found for this exam.</p>
<p className='has-text-danger'>No submissions found for this exam.</p>
)}
</div>
</div>
......
import pytest
import os
from pikepdf import Pdf
import numpy as np
from zesje.database import db, Exam, ExamLayout, ExamWidget, Submission, Copy, Page, Student
from zesje.emails import solution_pdf
from zesje.image_extraction import extract_image_pikepdf
from zesje.images import get_box
from zesje.scans import exam_student_id_widget
@pytest.mark.parametrize('layout, anonymous', [
(ExamLayout.templated, False),
(ExamLayout.templated, True),
(ExamLayout.unstructured, False),
(ExamLayout.unstructured, True)
], ids=['Templated', 'Templated & anonymous', 'Unstructured', 'Unstructured & anonymous'])
def test_solution_pdf(app, datadir, layout, anonymous):
exam = Exam(name='Email', layout=layout, finalized=True)
student = Student(id=1234323, first_name='Jamy', last_name='Macgiver', email='J.M@tudelft.nl')
db.session.add(exam)
db.session.add(student)
db.session.commit()
if layout == ExamLayout.templated:
db.session.add(ExamWidget(
name='student_id_widget',
x=50,
y=50,
exam=exam,
))
db.session.commit()
sub = Submission(exam=exam, student=student, validated=True)
db.session.add(sub)
copy = Copy(submission=sub, number=1)
db.session.add(copy)
for index, filepath in enumerate(['studentnumbers/1234323.jpg', 'studentnumbers/4300947.jpg']):
page = Page(number=index, path=os.path.join(datadir, filepath))
db.session.add(page)
copy.pages.append(page)
db.session.commit()
with Pdf.open(solution_pdf(exam.id, student.id, anonymous=anonymous)) as pdf:
pagecount = len(pdf.pages)
assert pagecount == 2
if anonymous and layout == ExamLayout.templated:
image = extract_image_pikepdf(pdf.pages[0])
_, coords = exam_student_id_widget(exam.id)
widget_area = get_box(np.array(image), np.array(coords, dtype=float) / 72, padding=-.3)
w, h, *_ = widget_area.shape
assert 145 < (np.mean(widget_area[:w//2]) + np.mean(widget_area[:, :h//2])) / 2 < 155
......@@ -213,7 +213,7 @@ class Email(Resource):
404,
message="Exam does not exist"
)
student_ids = [sub.student_id for sub in exam.submissions if sub.student_id]
student_ids = [sub.student_id for sub in exam.submissions if sub.student_id and sub.validated]
failed_to_build = list()
to_send = dict()
......
......@@ -15,6 +15,7 @@ def sub_to_data(sub):
'lastName': sub.student.last_name,
'email': sub.student.email
} if sub.student else None,
'validated': sub.validated,
'problems': [
{
'id': sol.problem.id,
......
......@@ -15,7 +15,7 @@ from reportlab.lib.utils import ImageReader
import cv2
from PIL import Image
from .database import Submission
from .database import Submission, ExamLayout
from . import statistics
from .images import guess_dpi
from .api.images import _grey_out_student_widget
......@@ -39,7 +39,10 @@ def solution_pdf(exam_id, student_id, anonymous=False):
"""
sub = Submission.query.filter(Submission.exam_id == exam_id,
Submission.student_id == student_id,
Submission.validated).one()
Submission.validated).one_or_none()
if sub is None:
raise RuntimeError('Student did not make a submission for this exam')
pages = sorted((page for copy in sub.copies for page in copy.pages), key=(lambda p: (p.copy.number, p.number)))
page_size = current_app.config['PAGE_FORMATS'][current_app.config['PAGE_FORMAT']]
......@@ -47,7 +50,7 @@ def solution_pdf(exam_id, student_id, anonymous=False):
result = BytesIO()
pdf = canvas.Canvas(result, pagesize=page_size)
for page in pages:
if anonymous and page.number == 0:
if anonymous and page.number == 0 and sub.exam.layout == ExamLayout.templated:
page_im = cv2.imread(page.abs_path)
dpi = guess_dpi(page_im)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment