diff --git a/evap/staff/templates/quick-review.js b/evap/staff/templates/quick-review.js index 285c4fc27f1a1f8bb6d139ed907ac1ce112a08b0..04a3ea6019435abeb7a790016bfcc9fa287846b0 100644 --- a/evap/staff/templates/quick-review.js +++ b/evap/staff/templates/quick-review.js @@ -9,9 +9,12 @@ $(document).ready(() => { .css({top: "", height: ""}); }); - slider.on("click", "[data-action]", event => { - reviewAction($(event.target).data("action")); - }); + for (const actionButton of slider[0].querySelectorAll("[data-action]")) { + actionButton.addEventListener("click", () => { + reviewAction(actionButton.dataset.action); + }); + } + slider.on("click", "[data-slide]", event => { let offset = $(event.target).data("slide") === "left" ? -1 : 1; slideTo(index + offset); @@ -29,6 +32,7 @@ $(document).ready(() => { "k": "[data-action=make_private]", "l": "[data-action=hide]", "backspace": "[data-action=unreview]", + "e": "[data-action=textanswer_edit]", "enter": `[data-url=next-evaluation][data-next-evaluation-index=${nextEvaluationIndex}]`, "m": "[data-startover=unreviewed]", "n": "[data-startover=all]", @@ -164,6 +168,7 @@ $(document).ready(() => { type: "POST", url: "{% url 'staff:evaluation_textanswers_update_publish' %}", data: parameters, + success: function(data) { if(action == "textanswer_edit" && data) window.location = data; }, error: function(){ window.alert("{% trans 'The server is not responding.' %}"); } }); @@ -175,7 +180,7 @@ $(document).ready(() => { updateButtonActive(); } - if(action !== "unreview") { + if(!["unreview", "textanswer_edit"].includes(action)) { slideTo(index + 1); slider.find("[data-action=" + action + "]").focus(); } diff --git a/evap/staff/templates/staff_evaluation_textanswers_quick.html b/evap/staff/templates/staff_evaluation_textanswers_quick.html index 48d1dacab3193b1d619caef5fd51ba2e5357f1af..e7d25566f7a7b950fd7d84a6aa4fb892775e0c89 100644 --- a/evap/staff/templates/staff_evaluation_textanswers_quick.html +++ b/evap/staff/templates/staff_evaluation_textanswers_quick.html @@ -16,6 +16,7 @@ <tr><td><kbd>K</kbd></td><td>{% trans 'Make answer private to the contributor' %}</td></tr> <tr><td><kbd>L</kbd></td><td>{% trans 'Remove answer' %}</td></tr> <tr><td><kbd>⌫</kbd></td><td>{% trans 'Unreview answer' %}</td></tr> + <tr><td><kbd>E</kbd></td><td>{% trans 'Edit answer' %}</td></tr> <tr><td><kbd>↲</kbd></td><td>{% trans 'Review next evaluation' %}</td></tr> <tr><td><kbd>S</kbd></td><td>{% trans 'Skip evaluation' %}</td></tr> <tr><td><kbd>N</kbd></td><td>{% trans 'Show all again' %}</td></tr> @@ -52,12 +53,11 @@ </div> {% for answer in result.answers %} <div class="slider-item card-body" data-layer="2" data-id="{{ answer.id }}"{% if contributor %} data-contribution="yes"{% endif %} - {% if answer.is_reviewed %}data-review="{% if answer.is_published %}publish{% elif answer.is_private %}make_private{% else %}hide{% endif %}"{% endif %} - > - <a class="textanswer-link" href="{% url 'staff:evaluation_textanswer_edit' semester.id evaluation.id answer.id %}">{{ answer.answer|linebreaksbr }}</a> - {% if answer.original_answer %} - <span class="textanswer-original">({{ answer.original_answer|linebreaksbr }})</span> - {% endif %} + {% if answer.is_reviewed %}data-review="{% if answer.is_published %}publish{% elif answer.is_private %}make_private{% else %}hide{% endif %}"{% endif %}> + {{ answer.answer|linebreaksbr }} + {% if answer.original_answer %} + <span class="textanswer-original">({{ answer.original_answer|linebreaksbr }})</span> + {% endif %} </div> {% endfor %} {% endfor %} @@ -89,7 +89,13 @@ </div> </div> <div class="card-footer d-flex"> - <div class="lcr-left"></div> + <div class="lcr-left"> + <div data-action-set="reviewing"> + <button data-action="unreview" class="btn btn-sm btn-outline-secondary"> + {% trans 'unreview' %} + </button> + </div> + </div> <div class="lcr-center" data-action-set="reviewing"> <button data-action="publish" class="btn btn-sm btn-outline-secondary"> {% trans 'yes' %} @@ -121,11 +127,13 @@ </button> </div> <div class="lcr-right"> + {% if user.is_manager %} <div data-action-set="reviewing"> - <button data-action="unreview" class="btn btn-sm btn-outline-secondary"> - {% trans 'unreview' %} + <button data-action="textanswer_edit" class="btn btn-sm btn-outline-secondary"> + <span class="fas fa-pencil-alt"> </button> </div> + {% endif %} </div> </div> </div> diff --git a/evap/staff/templates/staff_evaluation_textanswers_section.html b/evap/staff/templates/staff_evaluation_textanswers_section.html index 4e339348a17639f3a5ea79384cc691ff41778d8d..ce4222fab4de541524ddf5d497e64332ee4a0abb 100644 --- a/evap/staff/templates/staff_evaluation_textanswers_section.html +++ b/evap/staff/templates/staff_evaluation_textanswers_section.html @@ -11,7 +11,8 @@ <table class="table table-striped{% if not forloop.last %} mb-4{% endif %}"> <thead> <tr> - <th style="width: 85%">{% trans 'Text answer' %}</th> + <th style="width: 80%">{% trans 'Text answer' %}</th> + <th style="width: 5%"></th> <th style="width: 15%">{% trans 'Publish' %}</th> </tr> </thead> @@ -19,12 +20,17 @@ {% for answer in result.answers %} <tr id="{{ answer.id }}"> <td class="text-answer"> - <a class="textanswer-link" href="{% url 'staff:evaluation_textanswer_edit' semester.id evaluation.id answer.id %}">{{ answer.answer|linebreaksbr }}</a> + {{ answer.answer|linebreaksbr }} {% if answer.original_answer %} <br /> <span class="textanswer-original">({{ answer.original_answer|linebreaksbr }})</span> {% endif %} </td> + <td> + {% if user.is_manager %} + <a class="btn btn-sm btn-outline-secondary" href="{% url 'staff:evaluation_textanswer_edit' semester.id evaluation.id answer.id %}"><span class="fas fa-pencil-alt"></a> + {% endif %} + </td> <td> <div id="{{ answer.id }}-buttons" class="btn-group"> <button type="button" id="{{ answer.id }}-publish" onclick="press('{{ answer.id }}', 'publish');" class="btn btn-sm btn-light{% if answer.is_published %} active{% endif %}">{% trans 'yes' %}</button> diff --git a/evap/staff/tests/test_views.py b/evap/staff/tests/test_views.py index 6c3ef6eb6c6658f2025f9fb1eb602d19a4313e5d..ea404e47c6e7f84859dd0c745ca6d87761d35945 100644 --- a/evap/staff/tests/test_views.py +++ b/evap/staff/tests/test_views.py @@ -2748,7 +2748,17 @@ class TestEvaluationTextAnswersUpdatePublishView(WebTest): cls.text_question = baker.make(Question, questionnaire=top_general_questionnaire, type=Question.TEXT) cls.evaluation.general_contribution.questionnaires.set([top_general_questionnaire]) - def helper(self, old_state, expected_new_state, action, expect_errors=False): + def helper_check_follow(self, action): + with run_in_staff_mode(self): + textanswer = baker.make(TextAnswer) + response = self.app.post( + self.url, + params={"id": textanswer.id, "action": action, "evaluation_id": self.evaluation.pk}, + user=self.manager, + ) + self.assertEqual(response.status_code, 200) + + def helper_check_state(self, old_state, expected_new_state, action, expect_errors=False): with run_in_staff_mode(self): textanswer = baker.make(TextAnswer, state=old_state) response = self.app.post( @@ -2766,15 +2776,18 @@ class TestEvaluationTextAnswersUpdatePublishView(WebTest): def test_review_actions(self): # in an evaluation with only one voter reviewing should fail - self.helper(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish", expect_errors=True) + self.helper_check_state( + TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish", expect_errors=True + ) let_user_vote_for_evaluation(self.app, self.student2, self.evaluation) # now reviewing should work - self.helper(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish") - self.helper(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.HIDDEN, "hide") - self.helper(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PRIVATE, "make_private") - self.helper(TextAnswer.State.PUBLISHED, TextAnswer.State.NOT_REVIEWED, "unreview") + self.helper_check_state(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish") + self.helper_check_state(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.HIDDEN, "hide") + self.helper_check_state(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PRIVATE, "make_private") + self.helper_check_state(TextAnswer.State.PUBLISHED, TextAnswer.State.NOT_REVIEWED, "unreview") + self.helper_check_follow("textanswer_edit") def test_finishing_review_updates_results(self): let_user_vote_for_evaluation(self.app, self.student2, self.evaluation) @@ -2796,15 +2809,19 @@ class TestEvaluationTextAnswersUpdatePublishView(WebTest): def test_published(self): let_user_vote_for_evaluation(self.app, self.student2, self.evaluation) - self.helper(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish") + self.helper_check_state(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish") Evaluation.objects.filter(id=self.evaluation.id).update(state=Evaluation.State.PUBLISHED) - self.helper(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish", expect_errors=True) + self.helper_check_state( + TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish", expect_errors=True + ) def test_archived(self): let_user_vote_for_evaluation(self.app, self.student2, self.evaluation) - self.helper(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish") + self.helper_check_state(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish") Semester.objects.filter(id=self.evaluation.course.semester.id).update(results_are_archived=True) - self.helper(TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish", expect_errors=True) + self.helper_check_state( + TextAnswer.State.NOT_REVIEWED, TextAnswer.State.PUBLISHED, "publish", expect_errors=True + ) class TestEvaluationTextAnswersSkip(WebTestStaffMode): diff --git a/evap/staff/views.py b/evap/staff/views.py index e332af52df17ef301fdd4daf161c3df9edf8b1c9..c205af0edc5cf503830f632f2008b5f1d1f17231 100644 --- a/evap/staff/views.py +++ b/evap/staff/views.py @@ -1535,6 +1535,11 @@ def evaluation_textanswers_update_publish(request): answer.hide() elif action == "unreview": answer.unreview() + elif action == "textanswer_edit": + url = reverse( + "staff:evaluation_textanswer_edit", args=[evaluation.course.semester.id, evaluation_id, textanswer_id] + ) + return HttpResponse(url) else: return HttpResponse(status=400) # 400 Bad Request answer.save() @@ -1549,7 +1554,7 @@ def evaluation_textanswers_update_publish(request): return HttpResponse() # 200 OK -@reviewer_required +@manager_required def evaluation_textanswer_edit(request, semester_id, evaluation_id, textanswer_id): semester = get_object_or_404(Semester, id=semester_id) if semester.results_are_archived: