Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r53dbc37b6825194501d9f178308323373eebc3fa -r3085af1c7e3e6c3496af23a748d998886d7168fd --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 53dbc37b6825194501d9f178308323373eebc3fa) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 3085af1c7e3e6c3496af23a748d998886d7168fd) @@ -1120,3 +1120,7 @@ label.qb.collection.remove.questions.tooltip = Remove all selected questions from the collection. If they are present only in this collection, they will be permanently removed. label.qb.collection.remove.questions.fail = Questions can only be removed/deleted from the collection if they have not been used in an assessment activity. If the question is already in used, it will not be deleted. label.authoring.short.answer.hint = In each option box type answers in separate lines. +label.vsa.allocate.button = Allocate VSAs +label.vsa.allocate.description = Allocate students' answers by dragging and dropping them to correct options +label.vsa.deallocate.button.tip = Click to move answer back to queue +label.vsa.deallocate.confirm = Are you sure you want to mark this answer as not correct? Students' scores will be recalculated. Index: lams_central/src/java/org/lamsfoundation/lams/web/qb/VsaController.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/web/qb/VsaController.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/web/qb/VsaController.java (revision 3085af1c7e3e6c3496af23a748d998886d7168fd) @@ -0,0 +1,81 @@ +package org.lamsfoundation.lams.web.qb; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang.StringUtils; +import org.lamsfoundation.lams.qb.service.IQbService; +import org.lamsfoundation.lams.tool.service.ILamsToolService; +import org.lamsfoundation.lams.web.util.AttributeNames; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + +@Controller +@RequestMapping("/qb/vsa") +public class VsaController { + + @Autowired + private IQbService qbService; + + @Autowired + private ILamsToolService toolService; + + @RequestMapping("/displayVsaAllocate") + public String displayVsaAllocate(@RequestParam(name = AttributeNames.PARAM_TOOL_CONTENT_ID) long toolContentId, + Model model) { + + model.addAttribute("questions", toolService.getUnallocatedVSAnswers(toolContentId)); + return "qb/vsa/vsaAllocate"; + } + + @RequestMapping(path = "/allocateUserAnswer", method = RequestMethod.POST) + @ResponseBody + public String allocateUserAnswer(HttpServletResponse response, @RequestParam Long toolQuestionUid, + @RequestParam Long targetOptionUid, @RequestParam Long previousOptionUid, @RequestParam String answer) { + + Long optionUid = null; + boolean answerFoundInResults = false; + + if (!targetOptionUid.equals(previousOptionUid) && StringUtils.isNotBlank(answer)) { + /* + * We need to synchronise this operation. + * When multiple requests are made to modify the same option, for example to add a VSA answer, + * we have a case of dirty reads. + * One answer gets added, but while DB is still flushing, + * another answer reads the option without the first answer, + * because it is not there yet. + * The second answer gets added, but the first one gets lost. + * + * We can not synchronise the method in service + * as the "dirty" transaction is already started before synchronisation kicks in. + * We do it here, before transaction starts. + * It will not work for distributed environment, though. + * If teachers allocate answers on different LAMS servers, + * we can still get the same problem. We will need a more sophisticated solution then. + */ + + synchronized (qbService) { + optionUid = qbService.allocateVSAnswerToOption(toolQuestionUid, targetOptionUid, previousOptionUid, + answer); + } + + // recalculate marks for all lessons in all cases except for reshuffling inside the same container + answerFoundInResults = toolService.recalculateMarksForVsaQuestion(toolQuestionUid, answer); + } + + ObjectNode responseJSON = JsonNodeFactory.instance.objectNode(); + responseJSON.put("isAnswerDuplicated", optionUid != null); + responseJSON.put("optionUid", optionUid == null ? -1 : optionUid); + responseJSON.put("answerFoundInResults", answerFoundInResults); + response.setContentType("application/json;charset=utf-8"); + return responseJSON.toString(); + } + +} Index: lams_central/web/includes/javascript/vsaAllocate.js =================================================================== diff -u -rd99e586940f6f2ca7a230ed6770dc51803d71e23 -r3085af1c7e3e6c3496af23a748d998886d7168fd --- lams_central/web/includes/javascript/vsaAllocate.js (.../vsaAllocate.js) (revision d99e586940f6f2ca7a230ed6770dc51803d71e23) +++ lams_central/web/includes/javascript/vsaAllocate.js (.../vsaAllocate.js) (revision 3085af1c7e3e6c3496af23a748d998886d7168fd) @@ -10,15 +10,15 @@ filter: '.filtered', // 'filtered' class is not draggable onEnd: function (evt) { let data = { - questionUid: questionUid, + toolQuestionUid: questionUid, targetOptionUid: $(evt.to).data("option-uid"), previousOptionUid: $(evt.from).data("option-uid"), answer: $('.answer-text', evt.item).text() }; data[csrfTokenName] = csrfTokenValue; $.ajax({ - url: WEB_APP_URL + 'monitoring/allocateUserAnswer.do', + url: LAMS_URL + 'qb/vsa/allocateUserAnswer.do', data: data, method: 'post', dataType: "json", @@ -44,7 +44,7 @@ optionUid = container.data('option-uid'), isCorrect = container.data('option-correct'), data = { - questionUid: questionUid, + toolQuestionUid: questionUid, targetOptionUid: -1, previousOptionUid: optionUid, answer: answer @@ -57,7 +57,7 @@ data[csrfTokenName] = csrfTokenValue; $.ajax({ - url: WEB_APP_URL + 'monitoring/allocateUserAnswer.do', + url: LAMS_URL + 'qb/vsa/allocateUserAnswer.do', data: data, method: 'post', dataType: "json", Index: lams_central/web/qb/vsa/vsaAllocate.jsp =================================================================== diff -u -rd99e586940f6f2ca7a230ed6770dc51803d71e23 -r3085af1c7e3e6c3496af23a748d998886d7168fd --- lams_central/web/qb/vsa/vsaAllocate.jsp (.../vsaAllocate.jsp) (revision d99e586940f6f2ca7a230ed6770dc51803d71e23) +++ lams_central/web/qb/vsa/vsaAllocate.jsp (.../vsaAllocate.jsp) (revision 3085af1c7e3e6c3496af23a748d998886d7168fd) @@ -1,15 +1,15 @@ <%@ include file="/common/taglibs.jsp"%> + - <%@ include file="/common/header.jsp"%> - <fmt:message key="label.vsa.allocate.button" /> - + + + + + + - + @@ -53,9 +57,10 @@

- - - <%@ include file="parts/vsaQuestionAllocate.jsp"%> + + + + <%@ include file="vsaQuestionAllocate.jsp"%>