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