From bd33eb84b89ccab1977cc0e3d88f9507bc8b4e6d Mon Sep 17 00:00:00 2001
From: Ruben Young On <r.d.youngon@student.tudelft.nl>
Date: Sat, 25 May 2019 18:33:20 +0200
Subject: [PATCH] Revert "Merge branch 'develop' of
 https://gitlab.kwant-project.org/works-on-my-machine/zesje into
 feature/pre-grading"

This reverts commit f3484bc973a0a779bef1a6a3e49d380398155106, reversing
changes made to 4ea46142f97f4ab774a6e385e266a45828a308ed.
---
 client/views/Exam.jsx                         |  12 +-
 client/views/ExamEditor.jsx                   |  85 +++--------
 tests/data/cornermarkers/a4-3-markers.png     | Bin 15274 -> 0 bytes
 .../cornermarkers/a4-rotated-3-markers.png    | Bin 15803 -> 0 bytes
 tests/data/cornermarkers/a4-rotated.png       | Bin 16448 -> 0 bytes
 tests/test_three_corners.py                   |  57 --------
 zesje/api/exams.py                            |   3 +-
 zesje/api/mult_choice.py                      |   4 +-
 zesje/images.py                               |  59 --------
 zesje/pdf_generation.py                       |  16 +--
 zesje/pregrader.py                            | 135 ------------------
 zesje/scans.py                                |  27 +---
 12 files changed, 36 insertions(+), 362 deletions(-)
 delete mode 100644 tests/data/cornermarkers/a4-3-markers.png
 delete mode 100644 tests/data/cornermarkers/a4-rotated-3-markers.png
 delete mode 100644 tests/data/cornermarkers/a4-rotated.png
 delete mode 100644 tests/test_three_corners.py
 delete mode 100644 zesje/pregrader.py

diff --git a/client/views/Exam.jsx b/client/views/Exam.jsx
index 3ccbc008..85737fb3 100644
--- a/client/views/Exam.jsx
+++ b/client/views/Exam.jsx
@@ -42,11 +42,7 @@ class Exams extends React.Component {
             page: problem.page,
             name: problem.name,
             graded: problem.graded,
-            mc_options: problem.mc_options.map((option) => {
-              option.widget.x -= 7
-              option.widget.y -= 21
-              return option
-            }),
+            mc_options: problem.mc_options,
             isMCQ: problem.mc_options && problem.mc_options.length !== 0 // is the problem a mc question - used to display PanelMCQ
           }
         }
@@ -365,8 +361,8 @@ class Exams extends React.Component {
       'feedback_id': null,
       'widget': {
         'name': 'mc_option_' + labels[index],
-        'x': xPos + 7,
-        'y': yPos + 21,
+        'x': xPos,
+        'y': yPos,
         'type': 'mcq_widget'
       }
     }
@@ -379,8 +375,6 @@ class Exams extends React.Component {
     formData.append('label', data.label)
     api.put('mult-choice/', formData).then(result => {
       data.id = result.mult_choice_id
-      data.widget.x -= 7
-      data.widget.y -= 21
       this.createNewMCOWidget(problemWidget, data)
       this.generateAnswerBoxes(problemWidget, labels, index + 1, xPos + 24, yPos)
     }).catch(err => {
diff --git a/client/views/ExamEditor.jsx b/client/views/ExamEditor.jsx
index 311cadaf..15781e4c 100644
--- a/client/views/ExamEditor.jsx
+++ b/client/views/ExamEditor.jsx
@@ -187,27 +187,6 @@ class ExamEditor extends React.Component {
       this.props.updateExam()
     })
   }
-
-  updateState = (widget, data) => {
-    this.props.updateMCWidgetPosition(widget, {
-      x: Math.round(data.x),
-      y: Math.round(data.y)
-    })
-  }
-
-  updateMCOPosition = (widget, data) => {
-    this.updateState(widget, data)
-
-    widget.problem.mc_options.forEach(
-      (option, i) => {
-        let newData = {
-          x: Math.round(data.x) + i * 24 + 7,
-          y: Math.round(data.y) + 21
-        }
-        this.updateWidgetPositionDB(option, newData)
-      })
-  }
-
   /**
    * This function renders a group of options into one draggable widget
    * @returns {*}
@@ -217,9 +196,6 @@ class ExamEditor extends React.Component {
     let height = 38
     let enableResizing = false
     const isSelected = widget.id === this.props.selectedWidgetId
-    let xPos = widget.problem.mc_options[0].widget.x
-    let yPos = widget.problem.mc_options[0].widget.y
-
     return (
       <ResizeAndDrag
         key={'widget_mc_' + widget.id}
@@ -237,8 +213,8 @@ class ExamEditor extends React.Component {
           topRight: enableResizing
         }}
         position={{
-          x: xPos,
-          y: yPos
+          x: widget.problem.mc_options[0].widget.x,
+          y: widget.problem.mc_options[0].widget.y
         }}
         size={{
           width: width,
@@ -248,7 +224,19 @@ class ExamEditor extends React.Component {
           this.props.selectWidget(widget.id)
         }}
         onDragStop={(e, data) => {
-          this.updateMCOPosition(widget, data)
+          this.props.updateMCWidgetPosition(widget, {
+            x: Math.round(data.x),
+            y: Math.round(data.y)
+          })
+
+          widget.problem.mc_options.forEach(
+            (option, i) => {
+              let newData = {
+                x: Math.round(data.x),
+                y: Math.round(data.y) + i * 24
+              }
+              this.updateWidgetPositionDB(option, newData)
+            })
         }}
       >
         <div className={isSelected ? 'mcq-widget widget selected' : 'mcq-widget widget '}>
@@ -330,28 +318,6 @@ class ExamEditor extends React.Component {
         onDragStart={() => {
           this.props.selectWidget(widget.id)
         }}
-        onDrag={(e, data) => {
-          if (widget.problem.mc_options.length > 0) {
-            let xPos = widget.problem.mc_options[0].widget.x
-            let yPos = widget.problem.mc_options[0].widget.y
-            let width = 24 * widget.problem.mc_options.length
-            let height = 38
-
-            if (xPos < data.x) {
-              xPos = data.x
-            } else if (xPos + width > data.x + widget.width) {
-              xPos = data.x + widget.width - width
-            }
-
-            if (yPos < data.y) {
-              yPos = data.y
-            } else if (yPos + height > data.y + widget.height) {
-              yPos = data.y + widget.height - height
-            }
-
-            this.updateState(widget, { x: xPos, y: yPos })
-          }
-        }}
         onDragStop={(e, data) => {
           this.props.updateWidget(widget.id, {
             x: { $set: Math.round(data.x) },
@@ -361,26 +327,7 @@ class ExamEditor extends React.Component {
             x: Math.round(data.x),
             y: Math.round(data.y)
           }).then(() => {
-            if (widget.problem.mc_options.length > 0) {
-              let xPos = widget.problem.mc_options[0].widget.x
-              let yPos = widget.problem.mc_options[0].widget.y
-              let width = 24 * widget.problem.mc_options.length
-              let height = 38
-
-              if (xPos < data.x) {
-                xPos = data.x
-              } else if (xPos + width > data.x + widget.width) {
-                xPos = data.x + widget.width - width
-              }
-
-              if (yPos < data.y) {
-                yPos = data.y
-              } else if (yPos + height > data.y + widget.height) {
-                yPos = data.y + widget.height - height
-              }
-
-              this.updateMCOPosition(widget, { x: xPos, y: yPos })
-            }
+            // ok
           }).catch(err => {
             console.log(err)
             // update to try and get a consistent state
diff --git a/tests/data/cornermarkers/a4-3-markers.png b/tests/data/cornermarkers/a4-3-markers.png
deleted file mode 100644
index 32bdd11a2fbf87fed48625c3a12f0e609e2088a6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 15274
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

diff --git a/tests/data/cornermarkers/a4-rotated-3-markers.png b/tests/data/cornermarkers/a4-rotated-3-markers.png
deleted file mode 100644
index d32cbacac4d4a2d8a327736ce8967623f8aca22d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 15803
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

diff --git a/tests/data/cornermarkers/a4-rotated.png b/tests/data/cornermarkers/a4-rotated.png
deleted file mode 100644
index 8dbf8630a73b83a71dcc36eb687933632657f5aa..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 16448
zcmeAS@N?(olHy`uVBq!ia0y~yV7bA-z;=s+je&td{NbrM1_nmuOlRi+PiJR^fFOT2
zPajtX295J`C!F;<>>$#*|62T#by`Y`CbZselHOhPn4e9&b(Y(PU0&+CVv{8%O?f2!
z#rI3%E&-PK1Lgq*Z)d(Z<SS-XU@1Sn`ut4E71k3wKOe2_n5<O1ck#c_|F<G5lRI^#
z-dy7GnO9boZa4M$=c%)<PM`MmQGU9m%riHua8<RlTq|U|ZeLc~x+dxIx$k~Qj5i7&
z^VFyldf!@@ar0eOp^2gWyl-Z`iA6Uz*Iwn*dtUP5kwL<t868d2Oah-zF=G#!-qXNd
z)MJvg;$Da`>-I%4etO51;+NbGd6GN5GIlA?6T@lKeRrr&TYmQNzxUs79e3Rsd3(-s
zN3khuSwm}0n!dY7ym$UAc`Z+CQ&|1}wMolj&;5Db`+@aCT0kbpul&yp3=EtF9+AZi
z3_>L!%y=(NzJh^)`CU~=L`hI$xk5ovep+TuszOO+L8?M(MP?cUL&dGP;o+NP6nXwU
z7qMgNI#AdVy05YSeOj8C-R-AeelD1{AXRE18|R@v-%4BcJ*KF#Z=JGthr5by)Q&|H
zAJ05DYmwa3iEegPyZ<ZkFFwYsV&u{kb<VzKo@wZbw;vrZdEbkBs`z#D!JkaK{xN)*
zm~wv4?Z-i%n1t+}mV9;IX}99vmN~DF|83fP;QsGf2|bAig!yb*Rpvdlob14@85Ah(
zCH7P|a0~0EF0<M#@i+FCt^A&Mp!)UMx{u3d+ME&eI~~XT=d9oKId6RRzGwb$RetpQ
z%%3%a3^sPl)qk%w{S@^%CN`AcV0wJ_F5RaYi3!b?Pk)@5C|uW8W>w^PZsRe&Yi!eQ
zIpw?%NxD%r@zMgX?q`AJuL1%qi=58hiR6o%cBFOp91(@fk3<ffpTLu6HAA@L=Q?iL
z&(mz>)lSc-d|+T8e1Ic<&yx>&KW_hF+H&pQGZ(}8Wrwyk#O>LxbLGaFw?c~@IM$wC
zR(50hp~$1(w{(gcWVFvT7K=!>R-AP)IsfFr%`V&$tG0ihbBvWiI5Dr;=*Cg6i=I7m
zl9K#fHYBszi_UtVd;b7Kq=DRpNufs^{hryH@AE9_Nqrp5^PNe_Cq`(+K}M#?Rm=K5
zPutj<WngG(yglV+SEzWndAa|-zahSpOd>x`3G!17{b6u+n(gvA#V5BG2<|ZCKI*Nt
zb4rl5+S;hbP{Vyr7j}4FPCFZ&wR38i^)~<Od3UQ{?VKLR&M#TOnE1m(x!>s3lFqdz
zrMJG!Oq-W_t#$9Vl$V>HYOmdMYE}8Q7gpQ!*X%X^o_*~_QZN6aZ#%vu@IIQ7C$Qyi
zXYRQJ655R)w@4mndv-SR%#u^Hcb<*Z(Q!N?5p_!~{X!ZK&nuN}uI2CK&itLSo#FLA
z&HO*g&kiknS8pJ0R_cGI_Q_lJHN90kqAfXYEf+9rZ5NTT=UG+xc!Je+)mK}&#TcK*
zY|s33;a1gEo0TThj#_i+e!ihCdbBdaac1}Pt_@;=e`;m>+!)tfNm^5|RQa4mLd=eB
zc8mI%)IAr6SL|39T)U&kG_mN&PRFch%f$uRf3j<tlU#V#gg#_n@%*X6yYpe<|C%f9
z8X>SQJz-_BnbX6j2zGb<N!gqJw%xeLd;Rh1kL$lObG+AGw5+uz<M{0r%6$c0o19r5
z@ifW43wW~H=j2>*rMQWviyjtjd3ebGStsXvqnKj#fa5aKUR9Ca8NL^{Uhh6&a%&c6
zMMQqYT|qgcrXstx;*yqIvPvA+GmqSzSK+&;$=WIR;@g@I1)j31vNsPGr*bMv99+k(
z^u+1PH@V3SD@s_-{LRY{{<z8J_55pGiyt_Cl2xlty1%jK4JQKw15=W>y9>kr_Wm>b
z85kJYOFVsD*&nk?@W}BlTHM*iz+kn))5S5QBJRyy+wM6}<qkZwUZ<_}vPDG7(XnQF
z!?J*Y{moqw8cmB>TtysRM2;N0bc#PYJwH)dscCBK)U^k5%98ue8x)`0l`ijp=KS2r
zlIyth=56P&_WS<0-oE(ND|t{s&QK7W7bVBYz`)S(T44i-b{M574T6GKujHpamnnRD
zYU;<M;_*Fm=guwseAfK`%OCr%`I_JT692rt-~D|5?ti;qUs)MkzY7!*51w%581wP*
z9eX~%-p=mNhr>_jo-KNIX6DCp*6(}z?f+F&vV$UNvciV5H*VjS-hQv@wMF%}Hx)mh
zPCx$l`+fVm|9`(9o%L_JZ27<PYbLVgUuvw@U3*o<RlB<S^3PXQ_4W0IcCz~M`}Ra!
zzF+h5($bHw*YEGUd-v|gXS4JB`t5#Y{On|4U|_J|WV+pRzwUSLowD1xkN^Dq{P?oJ
zy{y08&nHUDrkc8Le*8{8uK5467k~Hfns;uw?xOE^iqG%({ciX4`5Z9k?Wp+p$fEYw
zmx^aI(~qUkuQi)j^=jo)VUX!R`123VH}q}vUAA`G<$jQd7!DM&Y?EAl!0d0<iUXi<
zI3UxQd+Z_SwwJYOARPrVPczy$%|Og`tq07KMQ@aqm9c>NX`DI6KHwx<pv$sNvR(bg
zo@QQ<BP>r{<cW3xrR5!BOt+Dl!Z{#=Vz?V~k7;f2eIGA;1H_-9yy2|DL9pdpElR)g
zpDyP6`SG~?@!Icq&F|Iyep~qPQ0vDN%KbX?L8|Xm=*8|b+4t|)>yKB%<8|+8*58@M
z*14?o$DbXhZ(mqf)!q5{;%DLY>zkH6T=sJR?YnnlWAddMbC2=c|1prO`Ec;#yWQ{m
z%<oks+x+=(__R96Nb`B!;`)91`~MW}`EZE)wDs(#H`C|KzTf}<U&W(Nbu+z9oH@q(
z{{Q>!8dCj!@AqS`*YCHJt9r2z9HtG@=l}it_v7XA`Nz!f*BCFmW4!m@s~2D=6n|eI
zv-8vP*N4CNS<A(;v$OxVTX*e?xqo(7^X2=0A9d^NDc%10`T6wuS;ibW#`}K1+x_vy
z;(on-SFXoZ=Ng^`1xUf-SwiPU)_(i;P3*b)&Sf2Ccm7_rdiOK_?(e75<Bt`ew>6Ke
zdb#xIhRcup?f3l&neCV``{Uc~_xp;UpX=qf`;oBc*Q?c!8`<R|>_E}+VE&iS=k5D9
zr=RZ&kE=}mxNF^Ux$2ysDaJoUFG^lM@hIi%B3n>+_#HDjX*RR&MS`$(&jGXK%ljih
z(dw(XA$R3f@$4p0BtJ;u%!xiF76_8~8c{k!bAzvL+GUO$V?O^4xsSxT85kHgXqwfk
zFDx=S%p%Rez;K}IhSIL3kQ9<&>$4$O6OtHSCpP7JbAr>w>y0mE-4bRec58su&q%l(
zqy|nP*A83X*fs^6JU7U-Tc6ot)_cIrIf0phfgyrlI49Z*!t4)i|H!vZR~eip*R*qP
z)7{FIb9uTJm_KKZuY8HnjkFmMwMg1POqfpuZfv`9@ZiBm8BkBI-PV!!YQf|KW{06(
zH8{9!X_#=%>?l7_fSR=)*tQgs2iCM7*ar6=%QoFqs4qBjqEA5sU=7plWqekgImYMa
zSQ_ia>@aw@>-D;gyLa#20}8GK_GejcPpkU(^Z9Z0`87u8=31NQ?R+}zX)Gv4{$<YI
zH#s0-cH!^0+xxBG?MR+uS6lV*DH8(&LxE9_AS);u4uGv;V7Uj*N(=|Hn$Ch9&9H-S
zTL(y`Lju@W3`~%a7k~ssLjtI5GJ$Bio+x?);-3ShkSxj22+92%5MhUcpP!x{Efjuy
z%LN>e5B_K6=jRu~v_E`xb@k(O*6(Gkt*x0r(a!KE*5Cf`mx|YGw;$X4{od}5oY~K=
zT-6i=xrgEUQK*w4N?As!(I8+L4T8}iU>FU8(I8+L4T8}i05yI_YlP9_aI`EKEel4=
z0&tINv^X3s4o8c_(c*BlI2<hwM@P&?Ys1mnaI`iYtqn(O!_nGsv^E^A4M%Ik(b{md
zHXN-DM{C2;+HkZs9IXvUYs1mnaI`iYtqti|8~$0ncX#E*Rb9*$|1Qroy#LZwJaEAe
z_KqFhVq1->`J=WL1Zrq`T~TUjacXWcP-$X3d_gQw#MLAB)=ti-Z5~l9O$#)-qB39h
z7+>9KQ@-Q#nd14@Wm3;~{`z^~;hgGsA-6Ug?3?>L2DIdp2jZ&~0q`<eg=X*)Sq4eS
z`s@?XjRb6spar`OMjYTpwi6t|>oyw(l~%Dof7hSeWqRz`u}2RMHgC+ixvApgQSrwQ
z+vVTg0j;xbxV?I+`VBQTwWN{~lRkNSx#j-z-@O5?AwBT+`l$nEmX?y~=jL?IHqRH!
z24(gc`fGV}PM$d7p%bxTK?G=>@PV3)+JxvG7pkSe^7d=_at<Fp><3;NY_J|0U>9B;
zFf%bxfeOxn2Gm;ngU?jKYvs?Vt3w0&JyZ@g{GC@bM^_ze1joXGnykN^X8GWyxe0b#
zH~IPc_=s$F2QQ1CkuL`pUvTZmkBT|A)n<AzI|P!Gljp{+iu&IHS%+^>t{1;g=J)sa
z{fpiE#TNF?gg9qGnAO3uA0Hn6`1ySP<4a4uL0bOG=78OC;LazNMB{$&THgTg_RMFH
z)wNqs@l9rm&N4i6<=6X!&j&%Fc1BkH#;mP&8-6B$Libr8-{mvPi$DQ$rKoA`Ovv)!
zD?&GBS%OyuJLt+Tn+f)mgRZ}j+2;<>`fP@+HcGe7K-LClh4Nc1J?H~=1jE*n8EH^f
zO=!3=IPo<^rM7>R&G`xm=Pb*E*VbHZ)U91%2VPjd^YMaUV@SxZc*+tD(_-a=U~LX#
z+Xjl=;$H!^8$b>%c2CHL`|o{1@r9N1pw2G3urhKr^K5=G1_p+NJkFf6HcFs9lweOl
zf`5hT2Gi`+1ZjDB{WN(U&|335TFkSX{k}qzA!yIRjs5lYhW5W+EZ$i1^3uv1VX~X=
zuI^j*`~PtXNy$#vZZT0%I%{w&dvjwWXbZ^W#r<|&;&By<Ve8}W?sQ%`wK4boy}h8V
z2FG@lzJ7F&UB2gNx48Jltcr??EFNB7-?Mj>UAOGoX|?NnkihD=>u<x?pDrzp+g+Bs
zCVs!&G@ZyxKV?Bt!?Ud4{-4FRyt`8FeKMV(ttnw^qppH2(6v?Cm41GnMaheR^yRT-
z@9)X}{`R)}_xt_(H-i$q!Hi2Mx4yl-U3{S~>o#_|iUq~(LE6mGMrLMVd)_Gg{iLq0
zo}alSz5VsN)1~WoR^QED5+7W8H{0gR1?P{Ky!DTEiE3Nz2UQyl2TiQ3q;{9R?ON>K
z5886@{6GNzT3&Era%L}RS4CXy*Q-CCO!j|tGkt#V?{~Y`gRHs_4)p_XC(b+Oed+tD
z<?!Tj>Ok1OJriewVuks|_xttzlhyr?^-7yB+uONI=I-^<>c8K1AJ3`J+ot?-YrOfX
z`Roh{8)PgB6z<i0KKtX#W&g)B)8}<g)ebLv7a(+lA%WrVkH`HV|NVY{eEt4^Qr%*@
zTE|YD@ZjO+_do0G>}>q(mB+y~yY$vquYK*t75DdB)!KAp^S57jy(`Z*`<?k^ce%BD
z&~|<XgN%EX&*y?xsXy9$-mZK3{JLA-($xZB^FJKs-<W@Y-;aO4-&<Rkx2K+-c69Ie
zd(z(1^?HwXiyycBe#cl;{Pe`6r2QKrWHncmo`3pc-#gQwFR!+TulxOW>CWqM>#Og+
z{$CU;`}bYw@tpd1VQ!bQ_vbM%G;1H-Dst2A|DVrF8+7CM?I~`bbbyV4Iisk^NKH+x
zYiIQm`(4#<r!T%9e|>Lw-1k|#ua_#<io1W0>HGNc<I0^@$BrHAnkZMhx9)G%pRd>B
zKR%yde@wpqkD$1ITu<@ybEW@y@5~Kb9~b+F{q-bO?`4mYObw<LZqzhbmT+yaU$Nz`
z^{?Hy;!CgZy|#Jg>~FiSZ{C@A%YX6xz5kP!Xw0!wX4oOT^tkio<VEo{A6;#}UI{+>
zUnJG4k(WV2BGu-NxZv-*-V4`f?vE*dANsvIcDmxm-@B})2X5S@cR0y%`HmTGUEP^6
zd#g<Ae!tyrQTi(6<N4juOE*N=?u|Hh)FjAbjrHD$-X6_GrR!H0f8Uk4q#&$3cHQZp
z6W!%HO|!3w)YmiYxV-1%G3mk&4-WqLbXvdrz_fpR(m>WHI4n7EO<!SIV7a~PzGZ*o
zUcb7t_nqmf1rh5`zs?HLdH!m}v7;qbhP~O>*ZIzkv#I}A^ZdS(wXA)e&Af_7ok|=`
z2j)K8dOfb2-|mOPzF)6aSA06D-hKI-kNO5i27~vf3Vy^hC3Dr>t={|X^y|9MId0`4
zK94^hm+$YDHWyQW#qhvybN2Oh60)+r>-YcL#gX%Ji{)j=_LVEQw&h04SQe@5`}u74
zjXjmcA7eNAY~NrE%8dtjUapp#^yTj2OoPdgl60-nhNa@aFU*D%NzZOf)ee8Oe14tQ
zzTKN^giF7!y|Q_4-mUe0H~*Z;IW|o<`qA$9`?~Y@|1~qqy%n<O?(CP~qO`%X?9q|V
zioaj4KYnv_^TwK=pB}j~zTAE0lJttp9v8Lt>MWnr-qyD4*()p8%|+|0=01D(b#0Hx
zrB~P9@BJP($EI>qM~>!(%9ZCKMfU+2@5bEYeX<`<>+dfqyY+NxcwFaP>vFODvwjbI
zp%&gwDJm+;TA~uXci&Y*L&L!8*ma-?yzkM!DeU=ixoRD=oErgiGLqH6<vxRv#bt2K
oIrN$&;P7P_{B5WQ_T4|&XXmaE+wIc(7F08Oy85}Sb4q9e0N(M2TmS$7

diff --git a/tests/test_three_corners.py b/tests/test_three_corners.py
deleted file mode 100644
index 1169be12..00000000
--- a/tests/test_three_corners.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import cv2
-import os
-import numpy as np
-
-from zesje.images import fix_corner_markers
-from zesje.scans import find_corner_marker_keypoints
-
-
-def test_three_straight_corners_1():
-    shape = (240, 200, 3)
-    corner_markers = [(50, 50), (120, 50), (50, 200)]
-
-    top_left, corner_markers = fix_corner_markers(corner_markers, shape)
-
-    assert (120, 200) in corner_markers
-    assert top_left == (50, 50)
-
-
-def test_three_straight_corners_2():
-    shape = (240, 200, 3)
-    corner_markers = [(120, 50), (50, 200), (120, 200)]
-
-    top_left, corner_markers = fix_corner_markers(corner_markers, shape)
-
-    assert (50, 50) in corner_markers
-    assert top_left == (50, 50)
-
-
-def test_pdf(datadir):
-    # Max deviation of inferred corner marker and actual location
-    epsilon = 2
-
-    # Scan rotated image with 4 corner markers
-    image_filename1 = 'a4-rotated.png'
-    image_path = os.path.join(datadir, 'cornermarkers', image_filename1)
-    page_img = cv2.imread(image_path)
-
-    corners1 = find_corner_marker_keypoints(page_img)
-
-    # Scan the same image with 3 corner markers
-    image_filename2 = 'a4-rotated-3-markers.png'
-    image_path = os.path.join(datadir, 'cornermarkers', image_filename2)
-    page_img = cv2.imread(image_path)
-
-    corners2 = find_corner_marker_keypoints(page_img)
-
-    # Get marker that was removed
-    diff = [corner for corner in corners1 if corner not in corners2]
-    diff_marker = min(diff)
-
-    _, fixed_corners2 = fix_corner_markers(corners2, page_img.shape)
-    added_marker = [corner for corner in fixed_corners2 if corner not in corners2][0]
-
-    # Check if 'inferred' corner marker is not too far away
-    dist = np.linalg.norm(np.subtract(added_marker, diff_marker))
-
-    assert dist < epsilon
diff --git a/zesje/api/exams.py b/zesje/api/exams.py
index a1c449a0..c88db564 100644
--- a/zesje/api/exams.py
+++ b/zesje/api/exams.py
@@ -47,7 +47,8 @@ def get_cb_data_for_exam(exam):
     cb_data = []
     for problem in exam.problems:
         page = problem.widget.page
-        cb_data += [(cb.x, cb.y, page, cb.label) for cb in problem.mc_options]
+        if page:
+            cb_data += [(cb.x, cb.y, page, cb.label) for cb in problem.mc_options]
 
     return cb_data
 
diff --git a/zesje/api/mult_choice.py b/zesje/api/mult_choice.py
index 29eaa61d..8a1b2c9f 100644
--- a/zesje/api/mult_choice.py
+++ b/zesje/api/mult_choice.py
@@ -64,8 +64,8 @@ class MultipleChoice(Resource):
         mc_type = 'mcq_widget'
 
         if not id:
-            # Insert new empty feedback option that links to the same problem, with the label as name
-            new_feedback_option = FeedbackOption(problem_id=problem_id, text=label)
+            # Insert new empty feedback option that links to the same problem
+            new_feedback_option = FeedbackOption(problem_id=problem_id, text='')
             db.session.add(new_feedback_option)
             db.session.commit()
 
diff --git a/zesje/images.py b/zesje/images.py
index 331a7a08..0e2167f2 100644
--- a/zesje/images.py
+++ b/zesje/images.py
@@ -2,8 +2,6 @@
 
 import numpy as np
 
-from operator import sub, add
-
 
 def guess_dpi(image_array):
     h, *_ = image_array.shape
@@ -38,63 +36,6 @@ def get_box(image_array, box, padding=0.3):
     return image_array[top:bottom, left:right]
 
 
-def fix_corner_markers(corner_keypoints, shape):
-    """
-    Corrects the list of corner markers if only three corner markers are found.
-    This function raises if less than three corner markers are detected.
-
-    Parameters
-    ----------
-    corner_keypoints :
-        List of corner marker locations as tuples
-    shape :
-        Shape of the image in (x, y, dim)
-
-    Returns
-    -------
-    corner_keypoints :
-        A list of four corner markers.
-    top_left : tuple
-        Coordinates of the top left corner marker
-    """
-
-    if len(corner_keypoints) == 4 or len(corner_keypoints) < 3:
-        raise RuntimeError("Fewer then 3 corner markers found")
-
-    x_sep = shape[1] / 2
-    y_sep = shape[0] / 2
-
-    top_left = [(x, y) for x, y in corner_keypoints if x < x_sep and y < y_sep]
-    bottom_left = [(x, y) for x, y in corner_keypoints if x < x_sep and y > y_sep]
-    top_right = [(x, y) for x, y in corner_keypoints if x > x_sep and y < y_sep]
-    bottom_right = [(x, y) for x, y in corner_keypoints if x > x_sep and y > y_sep]
-
-    missing_point = ()
-
-    if not top_left:
-        # Top left point is missing
-        (dx, dy) = tuple(map(sub, top_right[0], bottom_right[0]))
-        missing_point = tuple(map(add, bottom_left[0], (dx, dy)))
-        top_left = [missing_point]
-
-    elif not bottom_left:
-        # Bottom left point is missing
-        (dx, dy) = tuple(map(sub, top_right[0], bottom_right[0]))
-        missing_point = tuple(map(sub, top_left[0], (dx, dy)))
-
-    elif not top_right:
-        # Top right point is missing
-        (dx, dy) = tuple(map(sub, top_left[0], bottom_left[0]))
-        missing_point = tuple(map(add, bottom_right[0], (dx, dy)))
-
-    elif not bottom_right:
-        # bottom right
-        (dx, dy) = tuple(map(sub, top_left[0], bottom_left[0]))
-        missing_point = tuple(map(sub, top_right[0], (dx, dy)))
-
-    return top_left[0], corner_keypoints + [missing_point]
-
-
 def box_is_filled(image_array, box_coords, padding=0.3, threshold=150, pixels=False):
     """
     Determines if a box is filled
diff --git a/zesje/pdf_generation.py b/zesje/pdf_generation.py
index 09bf9641..0c0b8c5a 100644
--- a/zesje/pdf_generation.py
+++ b/zesje/pdf_generation.py
@@ -162,27 +162,25 @@ def generate_checkbox(canvas, x, y, label):
     canvas : reportlab canvas object
 
     x : int
-        the x coordinate of the top left corner of the box in points (pt)
+        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 points (pt)
+        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 checkboxes boxes
-    x_label = x + 1  # location of the label
-    y_label = y + margin  # remove fontsize from the y label since we draw from the bottom left up
-    box_y = y - markboxsize  # remove the markboxsize because the y is the coord of the top
-    # and reportlab prints from the bottom
+    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, box_y, markboxsize, markboxsize)
+    canvas.rect(x, y, markboxsize, markboxsize)
 
 
 def generate_datamatrix(exam_id, page_num, copy_num):
@@ -275,8 +273,6 @@ def _generate_overlay(canv, pagesize, exam_id, copy_num, num_pages, id_grid_x,
         index = 0
         max_index = len(cb_data)
         cb_data = sorted(cb_data, key=lambda tup: tup[2])
-        # invert the y axis
-        cb_data = [(cb[0], pagesize[1] - cb[1], cb[2], cb[3]) for cb in cb_data]
     else:
         index = 0
         max_index = 0
diff --git a/zesje/pregrader.py b/zesje/pregrader.py
deleted file mode 100644
index 4954bc8a..00000000
--- a/zesje/pregrader.py
+++ /dev/null
@@ -1,135 +0,0 @@
-import cv2
-import numpy as np
-
-from zesje.database import db, Solution
-from zesje.images import guess_dpi, get_box, fix_corner_markers
-
-
-def add_feedback_to_solution(sub, exam, page, page_img, corner_keypoints):
-    """
-    Adds the multiple choice options that are identified as marked as a feedback option to a solution
-
-    Parameters
-    ------
-    sub : Submission
-        the current submission
-    exam : Exam
-        the current exam
-    page_img : Image
-        image of the page
-    corner_keypoints : array
-        locations of the corner keypoints as (x, y) tuples
-    """
-    problems_on_page = [problem for problem in exam.problems if problem.widget.page == page]
-
-    top_left_point, fixed_corner_keypoints = fix_corner_markers(corner_keypoints, page_img.shape)
-
-    for problem in problems_on_page:
-        sol = Solution.query.filter(Solution.problem_id == problem.id, Solution.submission_id == sub.id).one_or_none()
-
-        for mc_option in problem.mc_options:
-            box = (mc_option.x, mc_option.y)
-
-            if box_is_filled(box, page_img, top_left_point):
-                feedback = mc_option.feedback
-                sol.feedback.append(feedback)
-                db.session.commit()
-
-
-def box_is_filled(box, page_img, corner_keypoints, marker_margin=72/2.54, threshold=225, cut_padding=0.1, box_size=11):
-    """
-    A function that finds the checkbox in a general area and then checks if it is filled in.
-
-    Params
-    ------
-    box: (int, int)
-        The coordinates of the top left (x,y) of the checkbox in points.
-    page_img: np.array
-        A numpy array of the image scan
-    corner_keypoints: (float,float)
-        The x coordinate of the left markers and the y coordinate of the top markers,
-        used as point of reference since scans can deviate from the original.
-        (x,y) are both in pixels.
-    marker_margin: float
-        The margin between the corner markers and the edge of a page when generated.
-    threshold: int
-        the threshold needed for a checkbox to be considered marked range is between 0 (fully black)
-        and 255 (absolutely white).
-    cut_padding: float
-        The extra padding when retrieving an area where the checkbox is in inches.
-    box_size: int
-        the size of the checkbox in points.
-
-    Output
-    ------
-    True if the box is marked, else False.
-    """
-
-    # shouldn't be needed, but some images are drawn a bit weirdly
-    y_shift = 5
-    # create an array with y top, y bottom, x left and x right. use the marker margin to allign to the page.
-    coords = np.asarray([box[1] - marker_margin + y_shift, box[1] + box_size - marker_margin + y_shift,
-                        box[0] - marker_margin, box[0] + box_size - marker_margin])/72
-
-    # add the actually margin from the scan to corner markers to the coords in inches
-    dpi = guess_dpi(page_img)
-    coords[0] = coords[0] + corner_keypoints[1]/dpi
-    coords[1] = coords[1] + corner_keypoints[1]/dpi
-    coords[2] = coords[2] + corner_keypoints[0]/dpi
-    coords[3] = coords[3] + corner_keypoints[0]/dpi
-
-    # get the box where we think the box is
-    cut_im = get_box(page_img, coords, padding=cut_padding)
-
-    # convert to grayscale
-    gray_im = cv2.cvtColor(cut_im, cv2.COLOR_BGR2GRAY)
-    # apply threshold to only have black or white
-    _, bin_im = cv2.threshold(gray_im, 150, 255, cv2.THRESH_BINARY)
-
-    h_bin, w_bin, *_ = bin_im.shape
-    # create a mask that gets applied when floodfill the white
-    mask = np.zeros((h_bin+2, w_bin+2), np.uint8)
-    flood_im = bin_im.copy()
-    # fill the image from the top left
-    cv2.floodFill(flood_im, mask, (0, 0),  0)
-    # fill it from the bottom right just in case the top left doesn't cover all the white
-    cv2.floodFill(flood_im, mask, (h_bin-2, w_bin-2), 0)
-
-    # find white parts
-    coords = cv2.findNonZero(flood_im)
-    # Find a bounding box of the white parts
-    x, y, w, h = cv2.boundingRect(coords)
-    # cut the image to this box
-    res_rect = bin_im[y:y+h, x:x+w]
-
-    # the size in pixels we expect the drawn box to
-    box_size_px = box_size*dpi / 72
-
-    # if the rectangle is bigger (higher) than expected, cut the image up a bit
-    if h > 1.5 * box_size_px:
-        print("in h resize")
-        y_partition = 0.333
-        # try getting another bounding box on bottom 2/3 of the screen
-        coords2 = cv2.findNonZero(flood_im[y + int(y_partition * h): y + h, x: x+w])
-        x2, y2, w2, h2 = cv2.boundingRect(coords2)
-        # add these coords to create a new bounding box we are looking at
-        new_y = y+y2 + int(y_partition * h)
-        new_x = x + x2
-        res_rect = bin_im[new_y:new_y + h2, new_x:new_x + w2]
-
-    else:
-        new_x, new_y, w2, h2 = x, y, w, h
-
-    # do the same for width
-    if w2 > 1.5 * box_size_px:
-        # usually the checkbox is somewhere in the bottom left of the bounding box
-        coords3 = cv2.findNonZero(flood_im[new_y: new_y + h2, new_x: new_x + int(0.66 * w2)])
-        x3, y3, w3, h3 = cv2.boundingRect(coords3)
-        res_rect = bin_im[new_y + y3: new_y + y3 + h3, new_x + x3: new_x + x3 + w3]
-
-    # if the found box is smaller than a certain threshold
-    # it means that we only found a little bit of white and the box is filled
-    res_x, res_y, *_ = res_rect.shape
-    if res_x < 0.333 * box_size_px or res_y < 0.333 * box_size_px:
-        return True
-    return np.average(res_rect) < threshold
diff --git a/zesje/scans.py b/zesje/scans.py
index 4d3e1c5b..bcbde9f6 100644
--- a/zesje/scans.py
+++ b/zesje/scans.py
@@ -5,7 +5,6 @@ import os
 from collections import namedtuple, Counter
 from io import BytesIO
 import signal
-import traceback
 
 import cv2
 import numpy as np
@@ -18,7 +17,7 @@ from .database import db, Scan, Exam, Page, Student, Submission, Solution, ExamW
 from .datamatrix import decode_raw_datamatrix
 from .images import guess_dpi, get_box
 from .factory import make_celery
-from .pregrader import add_feedback_to_solution
+
 
 ExtractedBarcode = namedtuple('ExtractedBarcode', ['token', 'copy', 'page'])
 
@@ -55,7 +54,7 @@ def process_pdf(scan_id):
         # TODO: When #182 is implemented, properly separate user-facing
         #       messages (written to DB) from developer-facing messages,
         #       which should be written into the log.
-        write_pdf_status(scan_id, 'error', f"Unexpected error: {error}\n Traceback:\n" + traceback.format_exc())
+        write_pdf_status(scan_id, 'error', "Unexpected error: " + str(error))
 
 
 def _process_pdf(scan_id, app_config):
@@ -92,8 +91,8 @@ def _process_pdf(scan_id, app_config):
                     print(description)
                     failures.append(page)
             except Exception as e:
-                report_error(f'Error processing page {e}.\nTraceback:\n{traceback.format_exc()}')
-                raise
+                report_error(f'Error processing page {page}: {e}')
+                return
     except Exception as e:
         report_error(f"Failed to read pdf: {e}")
         raise
@@ -338,13 +337,7 @@ def process_page(image_data, exam_config, output_dir=None, strict=False):
     else:
         return True, "Testing, image not saved and database not updated."
 
-    sub, exam = update_database(image_path, barcode)
-
-    try:
-        add_feedback_to_solution(sub, exam, barcode.page, image_array, corner_keypoints)
-    except RuntimeError as e:
-        if strict:
-            return False, str(e)
+    update_database(image_path, barcode)
 
     if barcode.page == 0:
         description = guess_student(
@@ -392,12 +385,8 @@ def update_database(image_path, barcode):
 
     Returns
     -------
-    sub, exam where
-
-    sub : Submission
-        the current submission
-    exam : Exam
-        the current exam
+    signature_validated : bool
+        If the corresponding submission has a validated signature.
     """
     exam = Exam.query.filter(Exam.token == barcode.token).first()
     if exam is None:
@@ -417,8 +406,6 @@ def update_database(image_path, barcode):
 
     db.session.commit()
 
-    return sub, exam
-
 
 def decode_barcode(image, exam_config):
     """Extract a barcode from a PIL Image."""
-- 
GitLab