From 4e69f7d72e27e254c96e6f04f1918d078cfeab6e Mon Sep 17 00:00:00 2001
From: Roosted7 <thomasroos@live.nl>
Date: Sat, 14 Apr 2018 02:56:05 +0200
Subject: [PATCH] Move exam state up from pages to central app level

---
 client/components/NavBar.jsx         |  29 ++++-
 client/index.jsx                     |  76 ++++++-----
 client/views/AddExam.jsx             |   3 +-
 client/views/Exam.jsx                |  38 +++---
 client/views/Grade.jsx               |   2 +-
 client/views/Students.jsx            | 182 +++++++--------------------
 client/views/grade/FeedbackPanel.jsx |  30 ++++-
 zesje/resources/exams.py             |  42 ++++++-
 zesje/resources/submissions.py       |  96 +++++++++-----
 9 files changed, 261 insertions(+), 237 deletions(-)

diff --git a/client/components/NavBar.jsx b/client/components/NavBar.jsx
index 2ac80ca03..6b074ecd5 100644
--- a/client/components/NavBar.jsx
+++ b/client/components/NavBar.jsx
@@ -1,6 +1,8 @@
 import React from 'react';
 import { Link } from 'react-router-dom';
 
+import * as api from '../api.jsx'
+
 const BurgerButton = (props) => (
     <button className={"button navbar-burger" + (props.foldOut ? " is-active" : "")}
         onClick={props.burgerClick}>
@@ -12,8 +14,8 @@ const BurgerButton = (props) => (
 
 const ExamDropdown = (props) => (
     <div className="navbar-item has-dropdown is-hoverable">
-        <Link className="navbar-link" to={'/exams/' + (props.exam ? props.exam.id : "")}>
-            {props.exam ? <i>{props.exam.name}</i> : "Add exam"}
+        <Link className="navbar-link" to={'/exams/' + (props.exam.id ? props.exam.id : "")}>
+            {props.exam.id ? <i>{props.exam.name}</i> : "Add exam"}
         </Link>
         <div className="navbar-dropdown">
             {props.list.map((exam) => (
@@ -33,7 +35,22 @@ const ExamDropdown = (props) => (
 class NavBar extends React.Component {
 
     state = {
-        foldOut: false
+        foldOut: false,
+        examList: []
+    }
+
+    componentDidMount = () => {
+        this.updateExamList();
+    }
+
+    updateExamList = () => {
+        api.get('exams')
+            .then(exams => {
+                this.setState({
+                    examList: exams
+                })
+                if (this.props.exam.id == null && exams.length) this.props.updateExam(exams[exams.length - 1].id)
+            })
     }
 
     burgerClick = () => {
@@ -44,7 +61,7 @@ class NavBar extends React.Component {
 
     render() {
 
-        const examStyle = this.props.exam && this.props.exam.submissions ? {} : { pointerEvents: 'none', opacity: .65 }
+        const examStyle = this.props.exam.submissions.length ? {} : { pointerEvents: 'none', opacity: .65 }
 
         return (
             <nav className="navbar" role="navigation" aria-label="dropdown navigation">
@@ -65,8 +82,8 @@ class NavBar extends React.Component {
                 <div className={"navbar-menu" + (this.state.foldOut ? " is-active" : "")} onClick={this.burgerClick}>
                     <div className="navbar-start">
 
-                        {this.props.exam ?
-                            <ExamDropdown exam={this.props.exam} list={this.props.list} />
+                        {this.state.examList.length ?
+                            <ExamDropdown exam={this.props.exam} list={this.state.examList} />
                             :
                             <Link className="navbar-item" to='/exams'>Add exam</Link>
                         }
diff --git a/client/index.jsx b/client/index.jsx
index 3fd4a711d..cb43785d4 100644
--- a/client/index.jsx
+++ b/client/index.jsx
@@ -51,65 +51,63 @@ const Fail = Loadable({
 
 class App extends React.Component {
 
+    menu = React.createRef();
+
     state = {
-        examIndex: null,
-        examList: []
+        exam: {
+            id: null,
+            name: "",
+            submissions: [],
+            problems: [],
+            yaml: ""
+        }
     }
 
-    componentDidMount() {
-        this.updateExamList();
+    updateExam = (examID) => {
+        api.get('exams/' + examID)
+            .then(ex => this.setState({
+                    exam: ex
+                }))
     }
-
-    updateExamList = (callback, onlyList) => {
-        api.get('exams')
-            .then(exams => {
-                if (exams.length) {
-                    if (onlyList) {
-                        this.setState({
-                            examList: exams
-                        })
-                    } else {
-                        this.setState({
-                            examIndex: exams.length - 1,
-                            examList: exams
-                        }, callback)
+    updateSubmission = (index, sub) => {
+        if (index != undefined) {
+            if (sub) {
+                let newList = this.state.exam.submissions;
+                newList[index] = sub;
+                this.setState({
+                    exam: {
+                        ...this.state.exam,
+                        submissions: newList
                     }
-                }
-            })
-            .catch(resp => {
-                alert('failed to get exams (see javascript console for details)')
-                console.error('failed to get exams:', resp)
-            })
-    }
-
-    changeExam = (examID) => {
-        const index = this.state.examList.findIndex(exam => exam.id === examID)
-        if (index === -1) {
-            alert('Wrong exam url entered');
-            return;
+                })
+            }
         } else {
-            this.setState({
-                examIndex: index
-            })
+            api.get('submissions/' + this.state.exam.id)
+            .then(subs => this.setState({
+                exam: {
+                    ...this.state.exam,
+                    submissions: subs
+                }
+            }))
         }
     }
 
     render() {
 
-        const exam = this.state.examIndex === null ? null : this.state.examList[this.state.examIndex];
+        const exam = this.state.exam
 
         return (
             <Router>
                 <div>
-                    <NavBar exam={exam} list={this.state.examList} changeExam={this.changeExam} />
+                    <NavBar exam={exam} updateExam={this.updateExam} ref={this.menu} />
                     <Switch>
                         <Route exact path="/" component={Home} />
                         <Route path="/exams/:examID" render={({match}) => 
-                            <Exam exam={exam} urlID={match.params.examID} changeExam={this.changeExam} updateList={this.updateExamList}/> }/>
+                            <Exam exam={exam} urlID={match.params.examID} changeExam={this.changeExam} updateSubmission={this.updateSubmission}/> }/>
                         <Route path="/exams" render={({history}) => 
-                            <AddExam updateExamList={this.updateExamList} changeURL={history.push} /> }/>
+                            <AddExam updateExamList={() => this.menu.current.updateExamList()} changeURL={history.push} /> }/>
                         <Route path="/students" render={() => 
-                            <Students exam={exam} /> }/>
+                            <Students exam={exam} updateSubmission={this.updateSubmission}/> }/>
                         <Route path="/grade" render={() => (
                             exam && exam.submissions ? <Grade exam={exam}/> : <Fail message="No exams uploaded. Please do not bookmark URLs" />
                         )} />
diff --git a/client/views/AddExam.jsx b/client/views/AddExam.jsx
index 29cad59b1..0fc819fa7 100644
--- a/client/views/AddExam.jsx
+++ b/client/views/AddExam.jsx
@@ -17,7 +17,8 @@ class Exams extends React.Component {
         data.append('yaml', accepted[0])
         api.post('exams', data)
             .then(exam => {
-                this.props.updateExamList(() => this.props.changeURL('/exams/' + exam.id));
+                this.props.updateExamList()
+                this.props.changeURL('/exams/' + exam.id);
             })
             .catch(resp => {
                 alert('failed to upload yaml (see javascript console for details)')
diff --git a/client/views/Exam.jsx b/client/views/Exam.jsx
index a51c0a146..86d6e4f56 100644
--- a/client/views/Exam.jsx
+++ b/client/views/Exam.jsx
@@ -34,22 +34,8 @@ class Exams extends React.Component {
         pdfs: []
     };
 
-    loadExam = (id) => {
-        if (this.props.exam.id !== parseInt(id)) {
-            console.log('Changing exam id to ' + id)
-            this.props.changeExam(parseInt(id));
-        }
-
-        api.get('exams/' + id)
-            .then(exam => {
-                this.setState({
-                    yaml: exam.yaml
-                })
-            })
-    }
-
     putYaml = () => {
-        api.patch('exams/' + this.props.urlID, { yaml: this.state.yaml })
+        api.patch('exams/' + this.props.exam.id, { yaml: this.state.yaml })
             .then(() => alert('thank you for the update; it was delicious'))
             .catch(resp => {
                 alert('failed to update the YAML (see javascript console)')
@@ -64,13 +50,13 @@ class Exams extends React.Component {
     }
 
     updatePDFs = () => {
-        api.get('pdfs/' + this.props.urlID)
+        api.get('pdfs/' + this.props.exam.id)
             .then(pdfs => {
                 if (JSON.stringify(pdfs) != JSON.stringify(this.state.pdfs)) {
-                    this.props.updateList(null, true)
                     this.setState({
                         pdfs: pdfs
                     })
+                    this.props.updateSubmission()
                 }
             })
     }
@@ -83,7 +69,7 @@ class Exams extends React.Component {
         accepted.map(file => {
             const data = new FormData()
             data.append('pdf', file)
-            api.post('pdfs/' + this.props.urlID, data)
+            api.post('pdfs/' + this.props.exam.id, data)
                 .then(() => {
                     this.updatePDFs();
                 })
@@ -95,14 +81,22 @@ class Exams extends React.Component {
     }
 
     componentDidMount = () => {
-        this.loadExam(this.props.urlID);
         this.pdfUpdater = setInterval(this.updatePDFs, 1000)
+        if (this.props.exam.id) this.updatePDFs()
     }
 
-    componentWillReceiveProps = (newProps) => {
-        if (newProps.urlID !== this.props.urlID) {
-            this.loadExam(newProps.urlID)
+    static getDerivedStateFromProps = (newProps, prevState) => {
+        if (newProps.exam.id != prevState.examID) {
+            return {
+                yaml: newProps.exam.yaml,
+                pdfs: [],
+                examID: newProps.exam.id
+            }
         }
+        return null
+    }
+    componentDidUpdate = (prevProps) => {
+        if (prevProps.exam.id != this.props.exam.id) this.updatePDFs()
     }
 
     componentWillUnmount = () => {
diff --git a/client/views/Grade.jsx b/client/views/Grade.jsx
index dbf8b8e15..7ae618f1f 100644
--- a/client/views/Grade.jsx
+++ b/client/views/Grade.jsx
@@ -156,7 +156,7 @@ class Grade extends React.Component {
                                     <EditPanel problem={this.state.problem} feedbackID={this.state.editFeedback} toggleEdit={this.toggleEdit}/>
                                     :
                                     <FeedbackPanel problem={this.state.problem} toggleEdit={this.toggleEdit} 
-                                        editFeedback={this.editFeedback}/>
+                                        editFeedback={this.editFeedback} />
                                 }
                             </div>
 
diff --git a/client/views/Students.jsx b/client/views/Students.jsx
index 1c1d39870..fd243dce8 100644
--- a/client/views/Students.jsx
+++ b/client/views/Students.jsx
@@ -15,15 +15,9 @@ class CheckStudents extends React.Component {
     state = {
         editActive: false,
         editStud: null,
-        submission: {
-            id: 0,
-            input: 0,
-            index: 0,
-            student: null,
-            validated: false,
-            imagePath: null,
-            list: []
-        }
+        input: "",
+        index: 0,
+        examID: null
     };
 
     componentWillUnmount = () => {
@@ -45,88 +39,73 @@ class CheckStudents extends React.Component {
             event.preventDefault();
             this.prevUnchecked();
         });
+    }
 
-        if (this.props.exam) this.loadSubmissions();
-
+    static getDerivedStateFromProps = (newProps, prevState) => {
+        if (newProps.exam.id != prevState.examID && newProps.exam.submissions.length) {
+            return {
+                input: newProps.exam.submissions[0].id,
+                index: 0,
+                examID: newProps.examID
+            }
+        }
+        return null
     }
 
 
     prev = () => {
-        const newIndex = this.state.submission.index - 1;
+        const newIndex = this.state.index - 1;
 
-        if (newIndex >= 0 && newIndex < this.state.submission.list.length) {
+        if (newIndex >= 0 && newIndex < this.props.exam.submissions.length) {
             this.setState({
-                submission: {
-                    ...this.state.submission,
-                    input: this.state.submission.list[newIndex].id
-                }
+                input: this.props.exam.submissions[newIndex].id
             }, this.setSubmission)
         }
     }
     next = () => {
-        const newIndex = this.state.submission.index + 1;
+        const newIndex = this.state.index + 1;
 
-        if (newIndex >= 0 && newIndex < this.state.submission.list.length) {
+        if (newIndex >= 0 && newIndex < this.props.exam.submissions.length) {
             this.setState({
-                submission: {
-                    ...this.state.submission,
-                    input: this.state.submission.list[newIndex].id
-                }
+                input: this.props.exam.submissions[newIndex].id
             }, this.setSubmission)
         }
 
     }
 
     prevUnchecked = () => {
-        const unchecked = this.state.submission.list.filter(sub => sub.validated === false).map(sub => sub.id);
-        const newInput = getClosest.lowerNumber(this.state.submission.id - 1, unchecked);
+        const unchecked = this.props.exam.submissions.filter(sub => sub.validated === false).map(sub => sub.id);
+        const newInput = getClosest.lowerNumber(this.props.exam.submissions[this.state.index].id - 1, unchecked);
 
         if (typeof newInput !== 'undefined') {
             this.setState({
-                submission: {
-                    ...this.state.submission,
-                    input: unchecked[newInput]
-                }
+                input: unchecked[newInput]
             }, this.setSubmission)
         }
     }
     nextUnchecked = () => {
-        const unchecked = this.state.submission.list.filter(sub => sub.validated === false).map(sub => sub.id);
-        const newInput = getClosest.greaterNumber(this.state.submission.id + 1, unchecked);
+        const unchecked = this.props.exam.submissions.filter(sub => sub.validated === false).map(sub => sub.id);
+        const newInput = getClosest.greaterNumber(this.props.exam.submissions[this.state.index].id + 1, unchecked);
 
         if (typeof newInput !== 'undefined') {
             this.setState({
-                submission: {
-                    ...this.state.submission,
-                    input: unchecked[newInput]
-                }
+                input: unchecked[newInput]
             }, this.setSubmission)
         }
     }
 
     setSubmission = () => {
 
-        const input = parseInt(this.state.submission.input);
-        const i = this.state.submission.list.findIndex(sub => sub.id === input);
-        const sub = this.state.submission.list[i];
+        const input = parseInt(this.state.input);
+        const i = this.props.exam.submissions.findIndex(sub => sub.id === input);
 
         if (i >= 0) {
             this.setState({
-                submission: {
-                    ...this.state.submission,
-                    id: input,
-                    student: sub.student,
-                    validated: sub.validated,
-                    index: i,
-                    imagePath: 'api/images/signature/' + this.props.exam.id + '/' + input
-                }
-            }, this.getSubmission)
+                index: i,
+            }, /* UPDATE SUBMISSION IN TOP COMPONENT */)
         } else {
             this.setState({
-                submission: {
-                    ...this.state.submission,
-                    input: this.state.submission.id
-                }
+                input: this.props.submissions[this.state.index].id
             })
             alert('Could not find that submission number :(\nSorry!');
         }
@@ -136,91 +115,24 @@ class CheckStudents extends React.Component {
         const patt = new RegExp(/^([1-9]\d*|0)?$/);
 
         if (patt.test(event.target.value)) {
-            this.setState({
-                submission: {
-                    ...this.state.submission,
-                    input: event.target.value
-                }
-            })
+            this.setState({ input: event.target.value })
         }
     }
 
-    getSubmission = () => {
-        api.get('submissions/' + this.props.exam.id + '/' + this.state.submission.id)
-            .then(sub => {
-                let newList = this.state.submission.list;
-                const index = newList.findIndex(localSub => localSub.id === sub.id)
-                newList[index] = sub;
-                this.setState({
-                    submission: {
-                        ...this.state.submission,
-                        student: sub.student,
-                        validated: sub.validated,
-                        list: newList
-                    }
-                })
-            })
-            .catch(err => {
-                alert('failed to get submission (see javascript console for details)')
-                console.error('failed to get submission:', err)
-                throw err
-            })
-    }
-
-    loadSubmissions = () => {
-        api.get('submissions/' + this.props.exam.id)
-            .then(subs => {
-                if (subs.length) {
-                    this.setState({
-                        submission: {
-                            ...this.state.submission,
-                            id: subs[0].id,
-                            input: subs[0].id,
-                            student: subs[0].student,
-                            validated: subs[0].validated,
-                            imagePath: 'api/images/signature/' + this.props.exam.id + '/' + subs[0].id,
-                            list: subs
-                        }
-                    })
-                }
-            })
-            .catch(err => {
-                alert('failed to get submissions (see javascript console for details)')
-                console.error('failed to get submissions:', err)
-                throw err
-            })
-    }
-
     matchStudent = (stud) => {
 
-        if(!this.state.submission.list.length) return;
-
-        let newList = this.state.submission.list;
-        const index = this.state.submission.index;
-
-        this.setState({
-            submission: {
-                ...this.state.submission,
-                student: stud,
-                validated: true
-            }
-        }, this.nextUnchecked)
+        if (!this.props.exam.submissions.length) return;
 
-        api.put('submissions/' + this.props.exam.id + '/' + this.state.submission.id, { studentID: stud.id })
+        api.put('submissions/' + this.props.exam.id + '/' + this.props.exam.submissions[this.state.index].id, { studentID: stud.id })
             .then(sub => {
-                newList[index] = sub;
-                this.setState({
-                    submission: {
-                        ...this.state.submission,
-                        list: newList
-                    }
-                })
+                this.props.updateSubmission(this.state.index, sub)
+                this.nextUnchecked()                
             })
             .catch(err => {
                 alert('failed to put submission (see javascript console for details)')
                 console.error('failed to put submission:', err)
                 throw err
-            })
+            })        
     }
 
     toggleEdit = (student) => {
@@ -242,7 +154,9 @@ class CheckStudents extends React.Component {
             width: '5em'
         };
 
-        const maxSubmission = Math.max(...this.state.submission.list.map(o => o.id));
+        const maxSubmission = Math.max(...this.props.exam.submissions.map(o => o.id));
+
+        const subm = this.props.exam.submissions[this.state.index];
 
         return (
             <div>
@@ -259,11 +173,11 @@ class CheckStudents extends React.Component {
                                     <EditPanel toggleEdit={this.toggleEdit} editStud={this.state.editStud} />
                                     :
                                     <SearchPanel matchStudent={this.matchStudent} toggleEdit={this.toggleEdit}
-                                        student={this.state.submission.student} validated={this.state.submission.validated} />
+                                        student={subm && subm.student} validated={subm && subm.validated} />
                                 }
                             </div>
 
-                            {this.state.submission.list.length ?  
+                            {this.props.exam.submissions.length ?
                                 <div className="column">
                                     <div className="level">
                                         <div className="level-item">
@@ -271,17 +185,17 @@ class CheckStudents extends React.Component {
                                                 <div className="control">
                                                     <button type="submit" className="button is-info is-rounded is-hidden-mobile"
                                                         onClick={this.prevUnchecked}>unchecked</button>
-                                                    <button type="submit" className={"button" + (this.state.submission.validated ? " is-success" : " is-link")}
+                                                    <button type="submit" className={"button" + (subm.validated ? " is-success" : " is-link")}
                                                         onClick={this.prev}>Previous</button>
                                                 </div>
                                                 <div className="control">
-                                                    <input className={"input is-rounded has-text-centered" + (this.state.submission.validated ? " is-success" : " is-link")}
-                                                        value={this.state.submission.input} type="text"
+                                                    <input className={"input is-rounded has-text-centered" + (subm.validated ? " is-success" : " is-link")}
+                                                        value={this.state.input} type="text"
                                                         onChange={this.setSubInput} onSubmit={this.setSubmission}
                                                         onBlur={this.setSubmission} maxLength="4" size="6" style={inputStyle} />
                                                 </div>
                                                 <div className="control">
-                                                    <button type="submit" className={"button" + (this.state.submission.validated ? " is-success" : " is-link")}
+                                                    <button type="submit" className={"button" + (subm.validated ? " is-success" : " is-link")}
                                                         onClick={this.next}>Next</button>
                                                     <button type="submit" className="button is-info is-rounded is-hidden-mobile"
                                                         onClick={this.nextUnchecked}>unchecked</button>
@@ -290,14 +204,14 @@ class CheckStudents extends React.Component {
                                         </div>
                                     </div>
 
-                                    <ProgressBar submissions={this.state.submission.list}/>
+                                    <ProgressBar submissions={this.props.exam.submissions} />
 
                                     <p className="box">
-                                        <img src={this.state.submission.imagePath} alt="" />
+                                        <img src={'api/images/signature/' + this.props.exam.id + '/' + subm.id} alt="" />
                                     </p>
 
                                 </div>
-                            : null }
+                                : null}
                         </div>
                     </div>
                 </section>
diff --git a/client/views/grade/FeedbackPanel.jsx b/client/views/grade/FeedbackPanel.jsx
index e42123dd7..0346fed5a 100644
--- a/client/views/grade/FeedbackPanel.jsx
+++ b/client/views/grade/FeedbackPanel.jsx
@@ -7,7 +7,15 @@ import FeedbackBlock from './FeedbackBlock.jsx';
 class FeedbackPanel extends React.Component {
 
     state = {
-        feedback: []
+        feedback: [],
+        remark: null,
+        remarkActive: false
+    }
+
+    addRemark = () => {
+        this.setState({
+            remarkActive: true
+        })
     }
 
     componentDidMount = () => {
@@ -33,11 +41,11 @@ class FeedbackPanel extends React.Component {
     }
 
     shouldComponentUpdate = (nextProps, nextState) => {
-        if (this.props.problem !== nextProps.problem || this.state.feedback != nextState.feedback) {
+        if (this.props.problem !== nextProps.problem || this.state.feedback != nextState.feedback || this.state.rem) {
             return true;            
         } else {
             console.log('halting re-render')
-            return false;
+            return true;
         }
     }
 
@@ -51,12 +59,24 @@ class FeedbackPanel extends React.Component {
                 {this.state.feedback.map((feedback, i) =>
                     <FeedbackBlock key={i} index={i} feedback={feedback} checked={false} onClick={this.props.editFeedback} />
                 )}
-                <div className="panel-block is-hidden-mobile">
+                {this.state.remarkActive ?
+                    <div className="panel-block">
+                        <textarea className="textarea" rows="2" placeholder="remark" />
+                    </div>
+                    : null
+                }
+                <div className="panel-block">
                     <button className="button is-link is-outlined is-fullwidth" onClick={this.props.toggleEdit}>
                         <span className="icon is-small">
                             <i className="fa fa-plus"></i>
                         </span>
-                        <span>add option</span>
+                        <span>option</span>
+                    </button>
+                    <button className="button is-link is-outlined is-fullwidth" onClick={this.addRemark}>
+                        <span className="icon is-small">
+                            <i className="fa fa-plus"></i>
+                        </span>
+                        <span>remark</span>
                     </button>
                 </div>
             </nav>
diff --git a/zesje/resources/exams.py b/zesje/resources/exams.py
index ae1f6c0b3..080c5bbb6 100644
--- a/zesje/resources/exams.py
+++ b/zesje/resources/exams.py
@@ -42,7 +42,47 @@ class ExamConfig(Resource):
         return {
             'id': exam_id,
             'name': exam.name,
-            'submissions': exam.submissions.count(),
+            '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,
+                    'solutions':
+                    [
+                        {
+                            'problem': sol.problem.id,
+                            'graded_by': sol.graded_by,
+                            'graded_at': sol.graded_at.isoformat() if sol.graded_at else None,
+                            'feedback': [
+                                fb.id for fb in sol.feedback
+                            ],
+                            'remarks': sol.remarks
+                        } for sol in sub.solutions.order_by(lambda s: s.problem.id)
+                    ]
+                } for sub in exam.submissions.order_by(lambda s: s.copy_number)
+            ],
+            'problems': [
+                {
+                    'id': prob.id,
+                    'name': prob.name,
+                    'feedback': [
+                        {
+                            'id': fb.id,
+                            'name': fb.text,
+                            'description': fb.description,
+                            'score': fb.score,
+                            'used': fb.solutions.count()
+                        } for fb in prob.feedback_options.order_by(lambda f: f.id)
+                    ]
+                } for prob in exam.problems.order_by(lambda p: p.id)
+            ],
             'yaml': yml
         }
 
diff --git a/zesje/resources/submissions.py b/zesje/resources/submissions.py
index af9082877..9b44ab137 100644
--- a/zesje/resources/submissions.py
+++ b/zesje/resources/submissions.py
@@ -31,35 +31,57 @@ class Submissions(Resource):
         exam = Exam[exam_id]
 
         if submission_id is not None:
-            s = Submission.get(exam=exam, copy_number=submission_id)
-            if not s:
+            sub = Submission.get(exam=exam, copy_number=submission_id)
+            if not sub:
                 raise orm.core.ObjectNotFound(Submission)
             return {
-                'id': s.copy_number,
+                'id': sub.copy_number,
                 'student':
                     {
-                        'id': s.student.id,
-                        'firstName': s.student.first_name,
-                        'lastName': s.student.last_name,
-                        'email': s.student.email
-                    } if s.student else None,
-                'validated': s.signature_validated,
+                        '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,
+                'solutions':
+                [
+                    {
+                        'problem': sol.problem.id,
+                        'graded_by': sol.graded_by,
+                        'graded_at': sol.graded_at.isoformat() if sol.graded_at else None,
+                        'feedback': [
+                            fb.id for fb in sol.feedback
+                        ],
+                        'remarks': sol.remarks
+                    } for sol in sub.solutions.order_by(lambda s: s.problem.id)
+                ]
             }
 
         return [
             {
-                'id': s.copy_number,
+                'id': sub.copy_number,
                 'student':
                     {
-                        'id': s.student.id,
-                        'firstName': s.student.first_name,
-                        'lastName': s.student.last_name,
-                        'email': s.student.email
-                    } if s.student else None,
-                'validated': s.signature_validated,
-            }
-            for s in Submission.select(lambda s: s.exam == exam)
-                               .order_by(Submission.copy_number)
+                        '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,
+                'solutions':
+                [
+                    {
+                        'problem': sol.problem.id,
+                        'graded_by': sol.graded_by,
+                        'graded_at': sol.graded_at.isoformat() if sol.graded_at else None,
+                        'feedback': [
+                            fb.id for fb in sol.feedback
+                        ],
+                        'remarks': sol.remarks
+                    } for sol in sub.solutions.order_by(lambda s: s.problem.id)
+                ]
+            } for sub in Submission.select(lambda s: s.exam == exam).order_by(lambda s: s.copy_number)
         ]
 
     put_parser = reqparse.RequestParser()
@@ -91,8 +113,8 @@ class Submissions(Resource):
         args = self.put_parser.parse_args()
 
         exam = Exam[exam_id]
-        submission = Submission.get(exam=exam, copy_number=submission_id)
-        if not submission:
+        sub = Submission.get(exam=exam, copy_number=submission_id)
+        if not sub:
             raise orm.core.ObjectNotFound(Submission)
 
         student = Student.get(id=args.studentID)
@@ -100,10 +122,28 @@ class Submissions(Resource):
             msg = f'Student {args.studentID} does not exist'
             return dict(status=404, message=msg), 404
 
-        submission.student = student
-        submission.signature_validated = True
-        return  {
-                'id': submission.copy_number,
-                'studentID': submission.student.id,
-                'validated': submission.signature_validated,
-            }
+        sub.student = student
+        sub.signature_validated = True
+        return {
+            '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,
+            'solutions':
+            [
+                {
+                    'problem': sol.problem.id,
+                    'graded_by': sol.graded_by,
+                    'graded_at': sol.graded_at.isoformat() if sol.graded_at else None,
+                    'feedback': [
+                        fb.id for fb in sol.feedback
+                    ],
+                    'remarks': sol.remarks
+                } for sol in sub.solutions.order_by(lambda s: s.problem.id)
+            ]
+        }
-- 
GitLab