From 9d8745d887b1f03594664fb9f6cc5634125fb505 Mon Sep 17 00:00:00 2001
From: Roosted7 <thomasroos@live.nl>
Date: Tue, 3 Apr 2018 23:19:32 +0200
Subject: [PATCH] Add fish virtualenv file to gitignore

Patrial solutions api and integration, will sqaush this commit
---
 .gitignore                           |  3 +
 client/views/grade/FeedbackBlock.jsx | 11 +++-
 client/views/grade/FeedbackPanel.jsx |  9 +++
 zesje/api.py                         |  4 +-
 zesje/resources/solutions.py         | 85 ++++++++++++++++++++++++++++
 5 files changed, 109 insertions(+), 3 deletions(-)
 create mode 100644 zesje/resources/solutions.py

diff --git a/.gitignore b/.gitignore
index 32c94bd9a..4e0553ea6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,5 +77,8 @@ __pycache__/
 # vscode folder
 .vscode
 
+# Fish Virtualenv
+.venv
+
 # development data
 data-dev
diff --git a/client/views/grade/FeedbackBlock.jsx b/client/views/grade/FeedbackBlock.jsx
index a18b3b266..4d831ac7e 100644
--- a/client/views/grade/FeedbackBlock.jsx
+++ b/client/views/grade/FeedbackBlock.jsx
@@ -16,6 +16,10 @@ class FeedbackBlock extends React.Component {
         })
     }
 
+    componentDidMount = () => {
+        console.log('mounting! ' + this.props.feedback.id)        
+    }
+
 
     render() {
         const score = this.props.feedback.score;
@@ -29,10 +33,13 @@ class FeedbackBlock extends React.Component {
                     {this.props.feedback.name}
                 </span>
                 <div className="field is-grouped">
-                    <div className="control" onMouseEnter={this.enter} onMouseLeave={this.leave} >
+                    <div className="control">
                         <div className="tags has-addons">
                             {score ? <span className="tag is-link">{this.props.feedback.score}</span> : null}
-                            <span className={"tag" + (this.state.hover ? " is-white" : "")} onClick={() => console.log('pencil click')}> <i className="fa fa-pencil"></i></span>
+                            <span className={"tag" + (this.state.hover ? " is-white" : "")} 
+                                onMouseEnter={this.enter} onMouseLeave={this.leave} onClick={() => console.log('pencil click')}> 
+                                <i className="fa fa-pencil"></i>
+                            </span>
                         </div>
                     </div>
                 </div>
diff --git a/client/views/grade/FeedbackPanel.jsx b/client/views/grade/FeedbackPanel.jsx
index d9cba5096..8351b4da5 100644
--- a/client/views/grade/FeedbackPanel.jsx
+++ b/client/views/grade/FeedbackPanel.jsx
@@ -32,6 +32,15 @@ class FeedbackPanel extends React.Component {
         }
     }
 
+    shouldComponentUpdate = (nextProps, nextState) => {
+        if (this.props.problem !== nextProps.problem || this.state.feedback != nextState.feedback) {
+            return true;            
+        } else {
+            console.log('halting re-render')
+            return false;
+        }
+    }
+
     render() {
 
         return (
diff --git a/zesje/api.py b/zesje/api.py
index b535d058a..8cda05bee 100644
--- a/zesje/api.py
+++ b/zesje/api.py
@@ -12,6 +12,7 @@ from .resources import summary_plot
 from .resources import export
 from .resources.problems import Problems
 from .resources.feedback import Feedback
+from .resources.solutions import Solutions
 
 api_bp = Blueprint(__name__, __name__)
 
@@ -34,6 +35,7 @@ api.add_resource(Submissions,
                  '/submissions/<int:exam_id>/<int:submission_id>')
 api.add_resource(Problems, '/problems/<int:exam_id>')
 api.add_resource(Feedback, '/feedback/<int:problem_id>')
+api.add_resource(Solutions, '/solution/<int:exam_id>/<int:submission_id>/<int:problem_id>')
 
 
 # Other resources that don't return JSON
@@ -48,7 +50,7 @@ api_bp.add_url_rule(
 )
 api_bp.add_url_rule(
     '/images/solutions/<int:exam_id>/<int:problem_id>/<int:submission_id>',
-    'solution',
+    'solution_image',
     images.get,
 )
 api_bp.add_url_rule(
diff --git a/zesje/resources/solutions.py b/zesje/resources/solutions.py
new file mode 100644
index 000000000..52a041f50
--- /dev/null
+++ b/zesje/resources/solutions.py
@@ -0,0 +1,85 @@
+""" REST api for solutions """
+
+from flask_restful import Resource, reqparse
+from datetime import datetime
+from pony import orm
+
+from ..models import Exam, Submission, Problem, Solution, FeedbackOption
+
+class Solutions(Resource):
+    """ Solution provided on a specific problem and exam """
+
+    @orm.db_session
+    def get(self, exam_id, submission_id, problem_id):
+        """get solution to problem
+
+        Returns
+        -------
+            feedback: list of int (IDs of FeedbackOption)
+            gradedBy: grader
+            gradedAt: datetime
+            imagePath: string (url)
+            remarks: string
+        """
+
+        problem = Problem[problem_id]
+        exam = Exam[exam_id]
+
+        sub = Submission.get(exam=exam, copy_number=submission_id)
+        if not sub:
+            raise orm.core.ObjectNotFound(Submission)
+
+        solution = Solution.get(submission=sub, problem=problem)
+        if not solution:
+            raise orm.core.ObjectNotFound(Solution)
+
+        return {
+                    'feedback': [fb.id for fb in solution.feedback],
+                    'gradedBy': solution.graded_by,
+                    'gradedAt': solution.graded_at.isoformat(),
+                    'imagePath': solution.image_path,
+                    'remarks': solution.remarks
+                }
+
+    put_parser = reqparse.RequestParser()
+    put_parser.add_argument('id', type=int, required=True)
+    @orm.db_session
+    def put(self, exam_id, submission_id, problem_id):
+        """Toggles an existing feedback option
+
+        Parameters
+        ----------
+            id: int
+
+        Returns
+        -------
+            state: boolean
+        """
+
+        problem = Problem[problem_id]
+        exam = Exam[exam_id]
+
+        sub = Submission.get(exam=exam, copy_number=submission_id)
+        if not sub:
+            raise orm.core.ObjectNotFound(Submission)
+
+        solution = Solution.get(submission=sub, problem=problem)
+        if not solution:
+            raise orm.core.ObjectNotFound(Solution)
+
+
+        args = self.put_parser.parse_args()
+
+        fb = FeedbackOption.get(id = args.id)
+        if not fb:
+            raise orm.core.ObjectNotFound(FeedbackOption)
+
+        solution.graded_at = datetime.now()
+
+        if fb in solution.feedback:
+            solution.feedback.remove(fb)
+            return { 'state': False }
+        else:
+            solution.feedback.add(fb)
+            return { 'state': True }
+
-- 
GitLab