Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
zesje
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Works on my machine
zesje
Commits
01f479f5
Commit
01f479f5
authored
5 years ago
by
Ruben Young On
Browse files
Options
Downloads
Plain Diff
Merge branch 'draw-pdf-box' into 'develop'
Draw pdf box See merge request
!6
parents
ab3c2799
d638fefd
Branches
Branches containing commit
No related tags found
1 merge request
!6
Draw pdf box
Pipeline
#17710
passed
5 years ago
Stage: build
Stage: test
Changes
4
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
tests/test_pdf_generation.py
+13
-0
13 additions, 0 deletions
tests/test_pdf_generation.py
zesje/api/__init__.py
+1
-0
1 addition, 0 deletions
zesje/api/__init__.py
zesje/api/exams.py
+59
-26
59 additions, 26 deletions
zesje/api/exams.py
zesje/pdf_generation.py
+55
-4
55 additions, 4 deletions
zesje/pdf_generation.py
with
128 additions
and
30 deletions
tests/test_pdf_generation.py
+
13
−
0
View file @
01f479f5
...
...
@@ -106,6 +106,19 @@ def test_generate_pdfs_num_files(datadir, tmpdir):
assert
len
(
tmpdir
.
listdir
())
==
num_copies
@pytest.mark.parametrize
(
'
checkboxes
'
,
[[(
300
,
100
,
1
,
'
c
'
),
(
500
,
50
,
0
,
'
d
'
),
(
500
,
500
,
0
,
'
a
'
),
(
250
,
200
,
1
,
'
b
'
)],
[],
[(
250
,
100
,
0
,
None
)]])
def
test_generate_checkboxes
(
datadir
,
tmpdir
,
checkboxes
):
blank_pdf
=
os
.
path
.
join
(
datadir
,
'
blank-a4-2pages.pdf
'
)
num_copies
=
1
copy_nums
=
range
(
num_copies
)
paths
=
map
(
lambda
copy_num
:
os
.
path
.
join
(
tmpdir
,
f
'
{
copy_num
}
.pdf
'
),
copy_nums
)
pdf_generation
.
generate_pdfs
(
blank_pdf
,
'
ABCDEFGHIJKL
'
,
copy_nums
,
paths
,
25
,
270
,
150
,
270
,
checkboxes
)
assert
len
(
tmpdir
.
listdir
())
==
num_copies
@pytest.mark.parametrize
(
'
name
'
,
[
'
a4
'
,
'
square
'
],
ids
=
[
'
a4
'
,
'
square
'
])
def
test_join_pdfs
(
mock_generate_datamatrix
,
mock_generate_id_grid
,
datadir
,
tmpdir
,
name
):
...
...
This diff is collapsed.
Click to expand it.
zesje/api/__init__.py
+
1
−
0
View file @
01f479f5
...
...
@@ -12,6 +12,7 @@ from .solutions import Solutions
from
.widgets
import
Widgets
from
.emails
import
EmailTemplate
,
RenderedEmailTemplate
,
Email
from
.mult_choice
import
MultipleChoice
from
.
import
signature
from
.
import
images
from
.
import
summary_plot
...
...
This diff is collapsed.
Click to expand it.
zesje/api/exams.py
+
59
−
26
View file @
01f479f5
...
...
@@ -25,6 +25,34 @@ def _get_exam_dir(exam_id):
)
def
get_cb_data_for_exam
(
exam
):
"""
Returns all multiple choice question check boxes for one specific exam
Parameters
----------
exam: the exam
Returns
-------
A list of tuples with checkbox data.
Each tuple is represented as (x, y, page, label)
Where
x: x position
y: y position
page: page number
label: checkbox label
"""
cb_data
=
[]
for
problem
in
exam
.
problems
:
page
=
problem
.
widget
.
page
if
page
:
cb_data
+=
[(
cb
.
x
,
cb
.
y
,
page
,
cb
.
label
)
for
cb
in
problem
.
mc_options
]
return
cb_data
class
Exams
(
Resource
):
def
get
(
self
,
exam_id
=
None
):
...
...
@@ -93,30 +121,30 @@ class Exams(Resource):
return
dict
(
status
=
404
,
message
=
'
Exam does not exist.
'
),
404
submissions
=
[
{
'
id
'
:
sub
.
copy_number
,
'
student
'
:
{
'
id
'
:
sub
.
student
.
id
,
'
firstName
'
:
sub
.
student
.
first_name
,
'
lastName
'
:
sub
.
student
.
last_name
,
'
email
'
:
sub
.
student
.
email
}
if
sub
.
student
else
None
,
'
validated
'
:
sub
.
signature_validated
,
'
problems
'
:
[
{
'
id
'
:
sol
.
problem
.
id
,
'
graded_by
'
:
{
'
id
'
:
sol
.
graded_by
.
id
,
'
name
'
:
sol
.
graded_by
.
name
}
if
sol
.
graded_by
else
None
,
'
graded_at
'
:
sol
.
graded_at
.
isoformat
()
if
sol
.
graded_at
else
None
,
'
feedback
'
:
[
fb
.
id
for
fb
in
sol
.
feedback
],
'
remark
'
:
sol
.
remarks
if
sol
.
remarks
else
""
}
for
sol
in
sub
.
solutions
# Sorted by sol.problem_id
],
}
for
sub
in
exam
.
submissions
{
'
id
'
:
sub
.
copy_number
,
'
student
'
:
{
'
id
'
:
sub
.
student
.
id
,
'
firstName
'
:
sub
.
student
.
first_name
,
'
lastName
'
:
sub
.
student
.
last_name
,
'
email
'
:
sub
.
student
.
email
}
if
sub
.
student
else
None
,
'
validated
'
:
sub
.
signature_validated
,
'
problems
'
:
[
{
'
id
'
:
sol
.
problem
.
id
,
'
graded_by
'
:
{
'
id
'
:
sol
.
graded_by
.
id
,
'
name
'
:
sol
.
graded_by
.
name
}
if
sol
.
graded_by
else
None
,
'
graded_at
'
:
sol
.
graded_at
.
isoformat
()
if
sol
.
graded_at
else
None
,
'
feedback
'
:
[
fb
.
id
for
fb
in
sol
.
feedback
],
'
remark
'
:
sol
.
remarks
if
sol
.
remarks
else
""
}
for
sol
in
sub
.
solutions
# Sorted by sol.problem_id
],
}
for
sub
in
exam
.
submissions
]
# Sort submissions by selecting those with students assigned, then by
# student number, then by copy number.
...
...
@@ -326,13 +354,16 @@ class ExamGeneratedPdfs(Resource):
generated_pdfs_dir
=
self
.
_get_generated_exam_dir
(
exam_dir
)
os
.
makedirs
(
generated_pdfs_dir
,
exist_ok
=
True
)
cb_data
=
get_cb_data_for_exam
(
exam
)
generate_pdfs
(
exam_path
,
exam
.
token
,
copy_nums
,
pdf_paths
,
student_id_widget
.
x
,
student_id_widget
.
y
,
barcode_widget
.
x
,
barcode_widget
.
y
barcode_widget
.
x
,
barcode_widget
.
y
,
cb_data
)
post_parser
=
reqparse
.
RequestParser
()
...
...
@@ -482,13 +513,15 @@ class ExamPreview(Resource):
exam_path
=
os
.
path
.
join
(
exam_dir
,
'
exam.pdf
'
)
cb_data
=
get_cb_data_for_exam
(
exam
)
generate_pdfs
(
exam_path
,
exam
.
token
[:
5
]
+
'
PREVIEW
'
,
[
1519
],
[
output_file
],
student_id_widget
.
x
,
student_id_widget
.
y
,
barcode_widget
.
x
,
barcode_widget
.
y
barcode_widget
.
x
,
barcode_widget
.
y
,
cb_data
)
output_file
.
seek
(
0
)
...
...
This diff is collapsed.
Click to expand it.
zesje/pdf_generation.py
+
55
−
4
View file @
01f479f5
...
...
@@ -12,7 +12,7 @@ output_pdf_filename_format = '{0:05d}.pdf'
def
generate_pdfs
(
exam_pdf_file
,
exam_id
,
copy_nums
,
output_paths
,
id_grid_x
,
id_grid_y
,
datamatrix_x
,
datamatrix_y
):
id_grid_y
,
datamatrix_x
,
datamatrix_y
,
cb_data
=
None
):
"""
Generate the final PDFs from the original exam PDF.
...
...
@@ -24,7 +24,6 @@ def generate_pdfs(exam_pdf_file, exam_id, copy_nums, output_paths, id_grid_x,
If maximum interchangeability with version 1 QR codes is desired (error
correction level M), use exam IDs composed of only uppercase letters, and
composed of at most 12 letters.
Parameters
----------
exam_pdf_file : file object or str
...
...
@@ -43,6 +42,9 @@ def generate_pdfs(exam_pdf_file, exam_id, copy_nums, output_paths, id_grid_x,
The x coordinate where the DataMatrix code should be placed
datamatrix_y : int
The y coordinate where the DataMatrix code should be placed
cb_data : list[ (int, int, int, str)]
The data needed for drawing a checkbox, namely: the x coordinate; y coordinate; page number and label
"""
exam_pdf
=
PdfReader
(
exam_pdf_file
)
mediabox
=
exam_pdf
.
pages
[
0
].
MediaBox
...
...
@@ -56,7 +58,7 @@ def generate_pdfs(exam_pdf_file, exam_id, copy_nums, output_paths, id_grid_x,
overlay_canv
=
canvas
.
Canvas
(
overlay_file
.
name
,
pagesize
=
pagesize
)
_generate_overlay
(
overlay_canv
,
pagesize
,
exam_id
,
copy_num
,
len
(
exam_pdf
.
pages
),
id_grid_x
,
id_grid_y
,
datamatrix_x
,
datamatrix_y
)
datamatrix_x
,
datamatrix_y
,
cb_data
)
overlay_canv
.
save
()
# Merge overlay and exam
...
...
@@ -151,6 +153,36 @@ def generate_id_grid(canv, x, y):
textboxwidth
,
textboxheight
)
def
generate_checkbox
(
canvas
,
x
,
y
,
label
):
"""
draw a checkbox and draw a singel character label ontop of the checkbox
Parameters
----------
canvas : reportlab canvas object
x : int
the x coordinate of the top left corner of the box in pixels
y : int
the y coordinate of the top left corner of the box in pixels
label: str
A string representing the label that is drawn on top of the box, will only take the first character
"""
fontsize
=
11
# Size of font
margin
=
5
# Margin between elements and sides
markboxsize
=
fontsize
-
2
# Size of student number boxes
x_label
=
x
+
1
y_label
=
y
+
margin
+
fontsize
# check that there is a label to print
if
(
label
and
not
(
len
(
label
)
==
0
)):
canvas
.
setFont
(
'
Helvetica
'
,
fontsize
)
canvas
.
drawString
(
x_label
,
y_label
,
label
[
0
])
canvas
.
rect
(
x
,
y
,
markboxsize
,
markboxsize
)
def
generate_datamatrix
(
exam_id
,
page_num
,
copy_num
):
"""
Generates a DataMatrix code to be used on a page.
...
...
@@ -187,7 +219,7 @@ def generate_datamatrix(exam_id, page_num, copy_num):
def
_generate_overlay
(
canv
,
pagesize
,
exam_id
,
copy_num
,
num_pages
,
id_grid_x
,
id_grid_y
,
datamatrix_x
,
datamatrix_y
):
id_grid_y
,
datamatrix_x
,
datamatrix_y
,
cb_data
=
None
):
"""
Generates an overlay (
'
watermark
'
) PDF, which can then be overlaid onto
the exam PDF.
...
...
@@ -221,6 +253,9 @@ def _generate_overlay(canv, pagesize, exam_id, copy_num, num_pages, id_grid_x,
The x coordinate where the DataMatrix codes should be placed
datamatrix_y : int
The y coordinate where the DataMatrix codes should be placed
cb_data : list[ (int, int, int, str)]
The data needed for drawing a checkbox, namely: the x coordinate; y coordinate; page number and label
"""
# Font settings for the copy number (printed under the datamatrix)
...
...
@@ -233,6 +268,15 @@ def _generate_overlay(canv, pagesize, exam_id, copy_num, num_pages, id_grid_x,
# ID grid on first page only
generate_id_grid
(
canv
,
id_grid_x
,
id_grid_y
)
# create index for list of checkbox data and sort the data on page
if
cb_data
:
index
=
0
max_index
=
len
(
cb_data
)
cb_data
=
sorted
(
cb_data
,
key
=
lambda
tup
:
tup
[
2
])
else
:
index
=
0
max_index
=
0
for
page_num
in
range
(
num_pages
):
_add_corner_markers_and_bottom_bar
(
canv
,
pagesize
)
...
...
@@ -246,6 +290,13 @@ def _generate_overlay(canv, pagesize, exam_id, copy_num, num_pages, id_grid_x,
datamatrix_x
,
datamatrix_y_adjusted
-
fontsize
,
f
"
#
{
copy_num
}
"
)
# call generate for all checkboxes that belong to the current page
while
index
<
max_index
and
cb_data
[
index
][
2
]
<=
page_num
:
x
,
y
,
_
,
label
=
cb_data
[
index
]
generate_checkbox
(
canv
,
x
,
y
,
label
)
index
+=
1
canv
.
showPage
()
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment