Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion evap/staff/templates/staff_evaluation_textanswer_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h3>{% translate 'Text answer' %}</h3>
</div>
<div class="card card-submit-area text-center mb-3">
<div class="card-body">
<button type="submit" class="btn btn-primary">{% translate 'Save text answer' %}</button>
<button id="textanswer-edit-submit-button" type="submit" class="btn btn-primary">{% translate 'Save text answer' %}</button>
</div>
</div>
</form>
Expand Down
5 changes: 4 additions & 1 deletion evap/staff/templates/staff_evaluation_textanswers_quick.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ <h5 class="modal-title" id="hotkeys-modal-title">Hotkeys</h5>
<div
class="slider-item card-body"
data-layer="2"
id="textanswer-{{ answer.id }}"
data-id="{{ answer.id }}"
{% if contributor %} data-contribution="yes"{% endif %}
{% if answer.is_reviewed %}
Expand Down Expand Up @@ -200,6 +201,8 @@ <h5 class="modal-title" id="hotkeys-modal-title">Hotkeys</h5>
document.querySelector("#shared-full-textanswer-flag-form"),
"{% url 'staff:evaluation_textanswers_skip' %}",
);
slider.attach();
const hash = document.location.hash
const hashPrefix = "#textanswer-"
slider.attach(hash.startsWith(hashPrefix) ? hash.substring(hashPrefix.length) : null)
</script>
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<div>{% translate 'Flag' %}</div>
</div>
{% for answer in result.answers %}
<div class="grid-row" id="{{ answer.id }}">
<div class="grid-row" id="textanswer-{{ answer.id }}">
<div>
{{ answer.answer|linebreaksbr }}
{% if answer.original_answer %}
Expand All @@ -26,7 +26,7 @@
</div>
<div>
{% if user.is_manager %}
<a class="btn btn-sm btn-outline-secondary" href="{% url 'staff:evaluation_textanswer_edit' answer.id %}"><span class="fas fa-pencil"></a>
<a class="btn btn-sm btn-outline-secondary" href="{% url 'staff:evaluation_textanswer_edit' answer.id %}?next-view={{ view }}"><span class="fas fa-pencil"></a>
{% endif %}
</div>
<div>
Expand Down
63 changes: 63 additions & 0 deletions evap/staff/tests/test_live.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
Question,
Questionnaire,
Semester,
TextAnswer,
UserProfile,
)
from evap.evaluation.tests.tools import LiveServerTest, classes_of_element
Expand Down Expand Up @@ -174,3 +175,65 @@ def test_collapse_without_editor_approved(self) -> None:

counter = card_header.find_element(By.CSS_SELECTOR, ".rounded-pill")
self.assertEqual(counter.text, "0")


class TextAnswerEditLiveTest(LiveServerTest):
def test_edit_textanswer_redirect(self):
"""Regression test for #1696"""

responsible = baker.make(UserProfile)
evaluation = baker.make(
Evaluation,
course=baker.make(Course, programs=[baker.make(Program)], responsibles=[responsible]),
vote_start_datetime=datetime(2099, 1, 1, 0, 0),
vote_end_date=date(2099, 12, 31),
state=Evaluation.State.EVALUATED,
can_publish_text_results=True,
)

question1 = baker.make(
Question,
)

general_questionnaire = baker.make(Questionnaire, questions=[question1])
evaluation.general_contribution.questionnaires.set([general_questionnaire])

contribution1 = baker.make(
Contribution, evaluation=evaluation, contributor=None, questionnaires=[general_questionnaire]
)

textanswer1 = baker.make(
TextAnswer,
question=question1,
contribution=contribution1,
answer="this is answer will be edited",
original_answer=None,
review_decision=TextAnswer.ReviewDecision.UNDECIDED,
)

baker.make(
TextAnswer,
question=question1,
contribution=contribution1,
answer="this is a dummy answer",
original_answer=None,
review_decision=TextAnswer.ReviewDecision.UNDECIDED,
)

with self.enter_staff_mode():
self.selenium.get(self.live_server_url + reverse("staff:evaluation_textanswer_edit", args=[textanswer1.pk]))

textanswer_field = self.selenium.find_element(By.XPATH, "//textarea[@name='answer']")
submit_btn = self.selenium.find_element(By.ID, "textanswer-edit-submit-button")

textanswer_field.clear()
textanswer_field.send_keys("edited answer")

with self.enter_staff_mode():
submit_btn.click()

answer = self.selenium.find_elements(
By.XPATH, "//div[@class='slider-item card-body active' and @data-layer='2']"
)

self.assertEqual(str(answer[0].get_attribute("id")).split("-", 1)[1], str(textanswer1.pk))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the [0] also seems like it could break arbitrarily depending on the order in which the answers were returned from the database; I think we can just check that the edited text is visible and the one from the other questions is invisible, take a look at existing tests that contain self.wait.until(visibility_of_element ...)

10 changes: 7 additions & 3 deletions evap/staff/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,6 @@ def evaluation_textanswers(request: HttpRequest, evaluation_id: int) -> HttpResp
)

template_data = {"semester": semester, "evaluation": evaluation, "view": view}

if view == "quick":
visited = request.session.get("review-visited", set())
skipped = request.session.get("review-skipped", set())
Expand Down Expand Up @@ -1770,11 +1769,16 @@ def evaluation_textanswer_edit(request, textanswer_id):
assert_textanswer_review_permissions(evaluation)

form = TextAnswerForm(request.POST or None, instance=textanswer)

view = request.GET.get("next-view")
if form.is_valid():
form.save()
# jump to edited answer
url = reverse("staff:evaluation_textanswers", args=[evaluation.pk], fragment=str(textanswer.id))
url = reverse(
"staff:evaluation_textanswers",
args=[evaluation.pk],
query={"view": view} if view else None,
fragment="textanswer-" + str(textanswer_id),
)
return HttpResponseRedirect(url)

template_data = {
Expand Down
16 changes: 11 additions & 5 deletions evap/static/ts/src/quick-review-slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export class QuickReviewSlider {
//
// DOM
//
public attach = () => {
public attach = (initalTextAnswerId?: string) => {
this.reviewDecisionForm.addEventListener("submit", this.reviewDecisionFormSubmitHandler);
this.flagForm.addEventListener("submit", this.flagFormSubmitHandler);
this.slider.querySelectorAll<HTMLInputElement>("input[name=is_flagged]").forEach(isFlaggedInput => {
Expand All @@ -137,7 +137,7 @@ export class QuickReviewSlider {
trigger.addEventListener("click", this.startOverHandler(trigger)),
);

this.startOver(StartOverWhere.Undecided);
this.startOver(initalTextAnswerId ?? StartOverWhere.Undecided);
this.updateNextEvaluation();
};

Expand Down Expand Up @@ -377,15 +377,21 @@ export class QuickReviewSlider {
//
// Sliding
//
public startOver = (where: StartOverWhere) => {
public startOver = (where: StartOverWhere | string) => {
const decided = this.slider.querySelectorAll<HTMLElement>(`[data-layer="${Layer.Answer}"][data-review]`);
const undecided = this.slider.querySelectorAll<HTMLElement>(
`[data-layer="${Layer.Answer}"]:not([data-review])`,
);
this.answerSlides = Array.from(decided).concat(Array.from(undecided));

const startOverOnUndecided = where === StartOverWhere.Undecided && undecided.length > 0;
const startIndex = startOverOnUndecided ? decided.length : 0;
let startIndex;
if (where === StartOverWhere.All || where === StartOverWhere.Undecided) {
const startOverOnUndecided = where === StartOverWhere.Undecided && undecided.length > 0;
startIndex = startOverOnUndecided ? decided.length : 0;
} else {
startIndex = this.answerSlides.findIndex(element => element.id === `textanswer-${where}`);
assert(startIndex != -1);
}
this.slideTo(startIndex);
this.updateButtons();
};
Expand Down