feat(exam-correct): general improvement
This commit is contained in:
parent
014036e4e3
commit
23044b28db
@ -47,6 +47,7 @@ export class ExamCorrect {
|
|||||||
_userInputStatus;
|
_userInputStatus;
|
||||||
_userInputCandidates;
|
_userInputCandidates;
|
||||||
_partInputs;
|
_partInputs;
|
||||||
|
_partDeleteBoxes;
|
||||||
|
|
||||||
_dateFormat;
|
_dateFormat;
|
||||||
_cIndices;
|
_cIndices;
|
||||||
@ -75,6 +76,7 @@ export class ExamCorrect {
|
|||||||
this._userInputStatus = document.getElementById(EXAM_CORRECT_USER_INPUT_STATUS_ID);
|
this._userInputStatus = document.getElementById(EXAM_CORRECT_USER_INPUT_STATUS_ID);
|
||||||
this._userInputCandidates = document.getElementById(EXAM_CORRECT_USER_INPUT_CANDIDATES_ID);
|
this._userInputCandidates = document.getElementById(EXAM_CORRECT_USER_INPUT_CANDIDATES_ID);
|
||||||
this._partInputs = [...this._element.querySelectorAll(`input[${EXAM_CORRECT_PART_INPUT_ATTR}]`)];
|
this._partInputs = [...this._element.querySelectorAll(`input[${EXAM_CORRECT_PART_INPUT_ATTR}]`)];
|
||||||
|
this._partDeleteBoxes = [...this._element.querySelectorAll('input.uw-exam-correct--delete-exam-part')];
|
||||||
|
|
||||||
if (this._sendBtn)
|
if (this._sendBtn)
|
||||||
this._sendBtn.addEventListener('click', this._sendCorrectionHandler.bind(this));
|
this._sendBtn.addEventListener('click', this._sendCorrectionHandler.bind(this));
|
||||||
@ -84,6 +86,10 @@ export class ExamCorrect {
|
|||||||
this._userInput.addEventListener('focusout', this._validateUserInput.bind(this));
|
this._userInput.addEventListener('focusout', this._validateUserInput.bind(this));
|
||||||
else throw new Error('ExamCorrect utility could not detect user input!');
|
else throw new Error('ExamCorrect utility could not detect user input!');
|
||||||
|
|
||||||
|
for (let deleteBox of this._partDeleteBoxes) {
|
||||||
|
deleteBox.addEventListener('change', (() => { this._updatePartDeleteDisabled(deleteBox); }).bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._userInputStatus) {
|
if (!this._userInputStatus) {
|
||||||
throw new Error('ExamCorrect utility could not detect user input status element!');
|
throw new Error('ExamCorrect utility could not detect user input status element!');
|
||||||
}
|
}
|
||||||
@ -93,7 +99,7 @@ export class ExamCorrect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO get date format by post request
|
// TODO get date format by post request
|
||||||
this._dateFormat = 'YYYY-MM-DD HH:mm:ss';
|
this._dateFormat = 'DD.MM.YYYY HH:mm:ss';
|
||||||
|
|
||||||
this._cIndices = new Map(
|
this._cIndices = new Map(
|
||||||
[...this._element.querySelectorAll('[uw-exam-correct-header]')]
|
[...this._element.querySelectorAll('[uw-exam-correct-header]')]
|
||||||
@ -116,6 +122,14 @@ export class ExamCorrect {
|
|||||||
// TODO destroy handlers on user input candidate elements
|
// TODO destroy handlers on user input candidate elements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updatePartDeleteDisabled(deleteBox) {
|
||||||
|
const partInput = deleteBox.parentElement.querySelector(`input[${EXAM_CORRECT_PART_INPUT_ATTR}]`);
|
||||||
|
if (!partInput)
|
||||||
|
return;
|
||||||
|
|
||||||
|
partInput.disabled = deleteBox.checked;
|
||||||
|
}
|
||||||
|
|
||||||
_validateUserInput() {
|
_validateUserInput() {
|
||||||
(!this._userInput.value) ? this._userInput.classList.add('no-value') : this._userInput.classList.remove('no-value');
|
(!this._userInput.value) ? this._userInput.classList.add('no-value') : this._userInput.classList.remove('no-value');
|
||||||
|
|
||||||
@ -176,11 +190,17 @@ export class ExamCorrect {
|
|||||||
|
|
||||||
const results = {};
|
const results = {};
|
||||||
for (const input of this._partInputs) {
|
for (const input of this._partInputs) {
|
||||||
if (input.reportValidity && !input.reportValidity()) {
|
if (input.disabled) {
|
||||||
|
const partKey = input.getAttribute(EXAM_CORRECT_PART_INPUT_ATTR);
|
||||||
|
if (!partKey) {
|
||||||
|
console.error('Error while parsing results: Could not detect exam part key attribute');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
results[partKey] = null;
|
||||||
|
} else if (input.reportValidity && !input.reportValidity()) {
|
||||||
input.focus();
|
input.focus();
|
||||||
return;
|
return;
|
||||||
}
|
} else if (input.value) {
|
||||||
if (input.value) {
|
|
||||||
const partKey = input.getAttribute(EXAM_CORRECT_PART_INPUT_ATTR);
|
const partKey = input.getAttribute(EXAM_CORRECT_PART_INPUT_ATTR);
|
||||||
if (!partKey) {
|
if (!partKey) {
|
||||||
console.error('Error while parsing results: Could not detect exam part key attribute');
|
console.error('Error while parsing results: Could not detect exam part key attribute');
|
||||||
@ -189,24 +209,25 @@ export class ExamCorrect {
|
|||||||
results[partKey] = parseFloat(input.value);
|
results[partKey] = parseFloat(input.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log('results', results);
|
||||||
|
|
||||||
const result = this._resultSelect.value !== 'none' && this._resultSelect.value;
|
const result = this._resultSelect && this._resultSelect.value !== 'none' && this._resultSelect.value;
|
||||||
|
|
||||||
// abort send if there are no results (after validation)
|
// abort send if there are no results (after validation)
|
||||||
if (Object.keys(results).length <= 0) return;
|
if (Object.keys(results).length <= 0 && !result) return;
|
||||||
|
|
||||||
const rowInfo = {
|
const rowInfo = {
|
||||||
users: [user],
|
users: [user],
|
||||||
results: results,
|
results: results,
|
||||||
|
result: result === 'delete' ? null : result,
|
||||||
status: STATUS.LOADING,
|
status: STATUS.LOADING,
|
||||||
};
|
};
|
||||||
if (results) rowInfo.results = results;
|
|
||||||
if (result) rowInfo.result = result === 'delete' ? null : result;
|
|
||||||
this._addRow(rowInfo);
|
this._addRow(rowInfo);
|
||||||
|
|
||||||
// clear inputs on validation success
|
// clear inputs on validation success
|
||||||
this._clearUserInput();
|
this._clearUserInput();
|
||||||
this._partInputs.forEach(clearInput);
|
this._partInputs.forEach(clearInput);
|
||||||
|
this._partDeleteBoxes.forEach(box => { box.checked = false; this._updatePartDeleteDisabled(box); });
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
user: userId || user,
|
user: userId || user,
|
||||||
@ -305,39 +326,38 @@ export class ExamCorrect {
|
|||||||
case 'success':
|
case 'success':
|
||||||
status = STATUS.SUCCESS;
|
status = STATUS.SUCCESS;
|
||||||
if (response.user) {
|
if (response.user) {
|
||||||
userElem.setAttribute(EXAM_CORRECT_USER_ATTR, response.user.id);
|
|
||||||
userElem.innerHTML = userToHTML(response.user);
|
|
||||||
const timeElem = row.cells.item(0);
|
const timeElem = row.cells.item(0);
|
||||||
timeElem.innerHTML = moment(response.time).format(this._dateFormat);
|
timeElem.innerHTML = moment(response.time).format(this._dateFormat);
|
||||||
timeElem.classList.remove('exam-correct--local-time');
|
timeElem.classList.remove('exam-correct--local-time');
|
||||||
newEntry.users = [response.user];
|
newEntry.users = [response.user];
|
||||||
newEntry.results = response.results;
|
newEntry.results = response.results;
|
||||||
newEntry.result = response.grade;
|
newEntry.result = response.grade;
|
||||||
|
} else {
|
||||||
|
console.error('Invalid response');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// TODO set edit button visibility
|
// TODO set edit button visibility
|
||||||
break;
|
break;
|
||||||
case 'ambiguous':
|
case 'ambiguous':
|
||||||
// TODO set edit button visibility
|
// TODO set edit button visibility
|
||||||
status = STATUS.AMBIGUOUS;
|
status = STATUS.AMBIGUOUS;
|
||||||
if (response.users) {
|
newEntry.users = response.users;
|
||||||
userElem = this._showUserList(row, response.users, results);
|
newEntry.results = typeof results === 'undefined' ? {} : results;
|
||||||
newEntry.users = response.users;
|
newEntry.result = typeof result === 'undefined' ? undefined : result; // eslint-disable-line no-undef
|
||||||
newEntry.results = results.partResults;
|
|
||||||
newEntry.result = results.result;
|
|
||||||
}
|
|
||||||
newEntry.message = response.message || null;
|
newEntry.message = response.message || null;
|
||||||
break;
|
break;
|
||||||
case 'failure':
|
case 'failure':
|
||||||
status = STATUS.FAILURE;
|
status = STATUS.FAILURE;
|
||||||
newEntry.users = (response.user && [response.user]) || null;
|
newEntry.users = (response.user && [response.user]) || null;
|
||||||
newEntry.results = results;
|
newEntry.results = typeof results === 'undefined' ? {} : results;
|
||||||
newEntry.message = response.message || null;
|
newEntry.message = response.message || null;
|
||||||
newEntry.result = results.result;
|
newEntry.result = typeof result === 'undefined' ? undefined : result; // eslint-disable-line no-undef
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// TODO show tooltip with 'invalid response'
|
// TODO show tooltip with 'invalid response'
|
||||||
// TODO set edit button visibility
|
// TODO set edit button visibility
|
||||||
console.error('Invalid response');
|
console.error('Invalid response');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
row.querySelectorAll('.fa-spin').forEach((elem) => {
|
row.querySelectorAll('.fa-spin').forEach((elem) => {
|
||||||
setStatus(elem, status);
|
setStatus(elem, status);
|
||||||
@ -359,19 +379,23 @@ export class ExamCorrect {
|
|||||||
statusCell.appendChild(messageElem);
|
statusCell.appendChild(messageElem);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userCell = row.querySelector('.uw-exam-correct--user-cell');
|
if (userElem && newEntry.users && newEntry.users.length === 1) {
|
||||||
if (userCell && newEntry.users && newEntry.users.length === 1) {
|
|
||||||
const user = newEntry.users[0];
|
const user = newEntry.users[0];
|
||||||
userCell.innerHTML = userToHTML(user);
|
userElem.innerHTML = userToHTML(user);
|
||||||
userCell.setAttribute(EXAM_CORRECT_USER_ATTR, user);
|
userElem.setAttribute(EXAM_CORRECT_USER_ATTR, user.id || user);
|
||||||
} else if (userCell && newEntry.users) {
|
} else if (userElem && newEntry.users) {
|
||||||
row.replaceChild(userCell, this._showUserList(row, newEntry.users, request.results));
|
row.replaceChild(userElem, this._showUserList(row, newEntry.users, { partResults: request.results, result: request.grade } ));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let [k, v] of Object.entries(newEntry.results)) {
|
for (let [k, v] of Object.entries(newEntry.results)) {
|
||||||
const resultCell = row.cells.item(this._cIndices.get(k));
|
const resultCell = row.cells.item(this._cIndices.get(k));
|
||||||
if (v.result)
|
if (v === null) {
|
||||||
|
resultCell.innerHTML = '<i class="fas fa-fw fa-trash"></i>';
|
||||||
|
resultCell.classList.remove('exam-correct--result-unconfirmed');
|
||||||
|
} else if (v && v.result) {
|
||||||
resultCell.innerHTML = v.result;
|
resultCell.innerHTML = v.result;
|
||||||
|
resultCell.classList.remove('exam-correct--result-unconfirmed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
savedEntries.push(newEntry);
|
savedEntries.push(newEntry);
|
||||||
@ -429,9 +453,9 @@ export class ExamCorrect {
|
|||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
user: listItem.getAttribute(EXAM_CORRECT_USER_ATTR),
|
user: listItem.getAttribute(EXAM_CORRECT_USER_ATTR),
|
||||||
results: results.partResults,
|
|
||||||
grade: results.result,
|
|
||||||
};
|
};
|
||||||
|
if (results.partResults) body.results = results.partResults;
|
||||||
|
if (results.result || results.result === null) body.grade = results.result;
|
||||||
|
|
||||||
this._app.httpClient.post({
|
this._app.httpClient.post({
|
||||||
url: EXAM_CORRECT_URL_POST,
|
url: EXAM_CORRECT_URL_POST,
|
||||||
@ -478,8 +502,13 @@ export class ExamCorrect {
|
|||||||
console.error('Could not determine cell index from part key!');
|
console.error('Could not determine cell index from part key!');
|
||||||
} else {
|
} else {
|
||||||
const partCell = document.createElement('TD');
|
const partCell = document.createElement('TD');
|
||||||
partCell.innerHTML = partResult;
|
|
||||||
partCell.classList.add('uw-exam-correct--part-cell');
|
if (partResult === null) {
|
||||||
|
partCell.innerHTML = '<i class="fas fa-fw fa-trash"></i>';
|
||||||
|
} else {
|
||||||
|
partCell.innerHTML = partResult;
|
||||||
|
}
|
||||||
|
partCell.classList.add('uw-exam-correct--part-cell', 'exam-correct--result-unconfirmed');
|
||||||
cells.set(cellIndex, partCell);
|
cells.set(cellIndex, partCell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ table[uw-exam-correct]
|
|||||||
min-width: 200px
|
min-width: 200px
|
||||||
|
|
||||||
th.uw-exam-correct--part-cell, td.uw-exam-correct--part-cell
|
th.uw-exam-correct--part-cell, td.uw-exam-correct--part-cell
|
||||||
width: min-content
|
width: 115px
|
||||||
text-align: center
|
text-align: center
|
||||||
white-space: nowrap
|
white-space: nowrap
|
||||||
|
|
||||||
@ -24,12 +24,16 @@ table[uw-exam-correct]
|
|||||||
opacity: .5
|
opacity: .5
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
margin-left: 5px
|
margin-left: 5px
|
||||||
.uw-exam-correct--delete-exam-part ~ .fa-trash:hover
|
|
||||||
opacity: 1
|
&:hover
|
||||||
|
opacity: 1
|
||||||
.uw-exam-correct--delete-exam-part:checked ~ .fa-trash
|
.uw-exam-correct--delete-exam-part:checked ~ .fa-trash
|
||||||
opacity: 1
|
opacity: 1
|
||||||
color: var(--color-error)
|
color: var(--color-error)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color: var(--color-error-dark)
|
||||||
|
|
||||||
td#uw-exam-correct__result
|
td#uw-exam-correct__result
|
||||||
width: min-content
|
width: min-content
|
||||||
select
|
select
|
||||||
@ -63,7 +67,6 @@ table[uw-exam-correct]
|
|||||||
border: 2px solid var(--color-error)
|
border: 2px solid var(--color-error)
|
||||||
|
|
||||||
[uw-exam-correct] tbody ul
|
[uw-exam-correct] tbody ul
|
||||||
list-style: none
|
|
||||||
li
|
li
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
text-decoration: underline
|
text-decoration: underline
|
||||||
@ -73,9 +76,11 @@ table[uw-exam-correct]
|
|||||||
width: calc(100% - 18em/14 - #{$exam-correct--input-status-margin})
|
width: calc(100% - 18em/14 - #{$exam-correct--input-status-margin})
|
||||||
i
|
i
|
||||||
margin-left: $exam-correct--input-status-margin
|
margin-left: $exam-correct--input-status-margin
|
||||||
|
ul
|
||||||
|
margin-top: 7px
|
||||||
|
|
||||||
.exam-correct--local-time
|
.exam-correct--local-time, .exam-correct--result-unconfirmed
|
||||||
color: var(--color-fontsec)
|
opacity: .5
|
||||||
font-style: italic
|
font-style: italic
|
||||||
|
|
||||||
.exam-correct--success
|
.exam-correct--success
|
||||||
|
|||||||
@ -413,6 +413,7 @@ UnauthorizedAllocationLecturer: Sie sind nicht als Veranstalter für eine Verans
|
|||||||
UnauthorizedCorrector: Sie sind nicht als Korrektor für diese Veranstaltung eingetragen.
|
UnauthorizedCorrector: Sie sind nicht als Korrektor für diese Veranstaltung eingetragen.
|
||||||
UnauthorizedSheetCorrector: Sie sind nicht als Korrektor für dieses Übungsblatt eingetragen.
|
UnauthorizedSheetCorrector: Sie sind nicht als Korrektor für dieses Übungsblatt eingetragen.
|
||||||
UnauthorizedExamCorrector: Sie sind nicht als Korrektor für diese Prüfung eingetragen.
|
UnauthorizedExamCorrector: Sie sind nicht als Korrektor für diese Prüfung eingetragen.
|
||||||
|
UnauthorizedExamCorrectorGrade: Sie haben nicht die Berechtigung für diese Prüfung Gesamtergebnisse einzutragen.
|
||||||
UnauthorizedCorrectorAny: Sie sind nicht als Korrektor für eine Veranstaltung eingetragen.
|
UnauthorizedCorrectorAny: Sie sind nicht als Korrektor für eine Veranstaltung eingetragen.
|
||||||
UnauthorizedRegistered: Sie sind nicht als Teilnehmer für diese Veranstaltung registriert.
|
UnauthorizedRegistered: Sie sind nicht als Teilnehmer für diese Veranstaltung registriert.
|
||||||
UnauthorizedAllocationRegistered: Sie sind nicht als Teilnehmer für diese Zentralanmeldung registriert.
|
UnauthorizedAllocationRegistered: Sie sind nicht als Teilnehmer für diese Zentralanmeldung registriert.
|
||||||
|
|||||||
@ -411,6 +411,7 @@ UnauthorizedAllocationLecturer: You are no administrator for any of the courses
|
|||||||
UnauthorizedCorrector: You are no sheet corrector for this course.
|
UnauthorizedCorrector: You are no sheet corrector for this course.
|
||||||
UnauthorizedSheetCorrector: You are no corrector for this sheet.
|
UnauthorizedSheetCorrector: You are no corrector for this sheet.
|
||||||
UnauthorizedExamCorrector: You are no corrector for this exam.
|
UnauthorizedExamCorrector: You are no corrector for this exam.
|
||||||
|
UnauthorizedExamCorrectorGrade: You may not enter overall exam achievements for this exam.
|
||||||
UnauthorizedCorrectorAny: You are no corrector for any course.
|
UnauthorizedCorrectorAny: You are no corrector for any course.
|
||||||
UnauthorizedRegistered: You are no participant in this course.
|
UnauthorizedRegistered: You are no participant in this course.
|
||||||
UnauthorizedAllocationRegistered: You are no participant in this central allocation.
|
UnauthorizedAllocationRegistered: You are no participant in this central allocation.
|
||||||
|
|||||||
@ -10,10 +10,13 @@ import qualified Data.Aeson as JSON
|
|||||||
|
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
import qualified Database.Esqueleto.Utils as E
|
import qualified Database.Esqueleto.Utils as E
|
||||||
|
import Database.Persist.Sql (transactionUndo)
|
||||||
|
|
||||||
import Handler.Utils
|
import Handler.Utils
|
||||||
import Handler.Utils.Exam (fetchExam)
|
import Handler.Utils.Exam (fetchExam)
|
||||||
|
|
||||||
|
import qualified Data.HashMap.Strict as HashMap
|
||||||
|
|
||||||
|
|
||||||
data CorrectInterfaceUser
|
data CorrectInterfaceUser
|
||||||
= CorrectInterfaceUser
|
= CorrectInterfaceUser
|
||||||
@ -30,7 +33,7 @@ data CorrectInterfaceResponse
|
|||||||
= CorrectInterfaceResponseSuccess
|
= CorrectInterfaceResponseSuccess
|
||||||
{ cirsUser :: CorrectInterfaceUser
|
{ cirsUser :: CorrectInterfaceUser
|
||||||
, cirsResults :: Map ExamPartNumber (Maybe ExamResultPoints)
|
, cirsResults :: Map ExamPartNumber (Maybe ExamResultPoints)
|
||||||
, cirsGrade :: Maybe ExamResultPassedGrade
|
, cirsGrade :: Maybe (Maybe ExamResultPassedGrade)
|
||||||
, cirsTime :: UTCTime
|
, cirsTime :: UTCTime
|
||||||
}
|
}
|
||||||
| CorrectInterfaceResponseAmbiguous
|
| CorrectInterfaceResponseAmbiguous
|
||||||
@ -44,17 +47,18 @@ data CorrectInterfaceResponse
|
|||||||
| CorrectInterfaceResponseNoOp
|
| CorrectInterfaceResponseNoOp
|
||||||
{ cirnUsers :: Set CorrectInterfaceUser
|
{ cirnUsers :: Set CorrectInterfaceUser
|
||||||
}
|
}
|
||||||
deriveJSON defaultOptions
|
deriveToJSON defaultOptions
|
||||||
{ constructorTagModifier = camelToPathPiece' 3
|
{ constructorTagModifier = camelToPathPiece' 3
|
||||||
, fieldLabelModifier = camelToPathPiece' 1
|
, fieldLabelModifier = camelToPathPiece' 1
|
||||||
, sumEncoding = TaggedObject "status" "results"
|
, sumEncoding = TaggedObject "status" "results"
|
||||||
|
, omitNothingFields = True
|
||||||
} ''CorrectInterfaceResponse
|
} ''CorrectInterfaceResponse
|
||||||
|
|
||||||
data CorrectInterfaceRequest
|
data CorrectInterfaceRequest
|
||||||
= CorrectInterfaceRequest
|
= CorrectInterfaceRequest
|
||||||
{ ciqUser :: Either Text (CryptoID UUID (Key User))
|
{ ciqUser :: Either Text (CryptoID UUID (Key User))
|
||||||
, ciqResults :: Maybe (NonNull (Map ExamPartNumber (Maybe Points)))
|
, ciqResults :: Maybe (NonNull (Map ExamPartNumber (Maybe Points)))
|
||||||
, ciqGrade :: Maybe ExamResultPassedGrade
|
, ciqGrade :: Maybe (Maybe ExamResultPassedGrade)
|
||||||
}
|
}
|
||||||
|
|
||||||
instance FromJSON CorrectInterfaceRequest where
|
instance FromJSON CorrectInterfaceRequest where
|
||||||
@ -62,7 +66,11 @@ instance FromJSON CorrectInterfaceRequest where
|
|||||||
ciqUser <- Right <$> o JSON..: "user" <|> Left <$> o JSON..: "user"
|
ciqUser <- Right <$> o JSON..: "user" <|> Left <$> o JSON..: "user"
|
||||||
results <- o JSON..:? "results"
|
results <- o JSON..:? "results"
|
||||||
ciqResults <- for results $ maybe (fail "Results may not be nullable") return . fromNullable
|
ciqResults <- for results $ maybe (fail "Results may not be nullable") return . fromNullable
|
||||||
ciqGrade <- o JSON..:? "grade"
|
ciqGrade <- if
|
||||||
|
| "grade" `HashMap.member` o
|
||||||
|
-> Just <$> o JSON..: "grade"
|
||||||
|
| otherwise
|
||||||
|
-> pure Nothing
|
||||||
return CorrectInterfaceRequest{..}
|
return CorrectInterfaceRequest{..}
|
||||||
|
|
||||||
|
|
||||||
@ -104,7 +112,9 @@ postECorrectR tid ssh csh examn = do
|
|||||||
|
|
||||||
CorrectInterfaceRequest{..} <- requireCheckJsonBody
|
CorrectInterfaceRequest{..} <- requireCheckJsonBody
|
||||||
|
|
||||||
response <- exceptT return return . hoist runDB $ do
|
let mayEditResults = False
|
||||||
|
|
||||||
|
response <- runDB . exceptT (<$ transactionUndo) return $ do
|
||||||
Entity eId Exam{..} <- lift $ fetchExam tid ssh csh examn
|
Entity eId Exam{..} <- lift $ fetchExam tid ssh csh examn
|
||||||
euid <- traverse decrypt ciqUser
|
euid <- traverse decrypt ciqUser
|
||||||
|
|
||||||
@ -192,20 +202,22 @@ postECorrectR tid ssh csh examn = do
|
|||||||
| otherwise -> return Nothing
|
| otherwise -> return Nothing
|
||||||
| otherwise -> return mempty
|
| otherwise -> return mempty
|
||||||
|
|
||||||
newExamResult <- lift $ do
|
newExamResult <- for ciqGrade $ \ciqGrade' -> lift $ do
|
||||||
|
unless mayEditResults $
|
||||||
|
permissionDeniedI MsgUnauthorizedExamCorrectorGrade
|
||||||
mOldResult <- getBy $ UniqueExamResult eId uid
|
mOldResult <- getBy $ UniqueExamResult eId uid
|
||||||
if
|
if
|
||||||
| Just (Entity oldId _) <- mOldResult, is _Nothing ciqGrade -> do
|
| Just (Entity oldId _) <- mOldResult, is _Nothing ciqGrade' -> do
|
||||||
delete oldId
|
delete oldId
|
||||||
audit $ TransactionExamResultDeleted eId uid
|
audit $ TransactionExamResultDeleted eId uid
|
||||||
return Nothing
|
return Nothing
|
||||||
| Just result <- ciqGrade -> let
|
| Just result <- ciqGrade' -> let
|
||||||
mOld = view passedGrade . examResultResult . entityVal <$> mOldResult
|
mOld = view passedGrade . examResultResult . entityVal <$> mOldResult
|
||||||
resultGrade = review passedGrade result
|
resultGrade = review passedGrade result
|
||||||
passedGrade :: Iso' ExamResultGrade ExamResultPassedGrade
|
passedGrade :: Iso' ExamResultGrade ExamResultPassedGrade
|
||||||
passedGrade = iso (fmap $ bool (Left . view passingGrade) Right examShowGrades) (fmap $ either (review passingGrade) id)
|
passedGrade = iso (fmap $ bool (Left . view passingGrade) Right examShowGrades) (fmap $ either (review passingGrade) id)
|
||||||
in if
|
in if
|
||||||
| ciqGrade /= mOld -> do
|
| ciqGrade' /= mOld -> do
|
||||||
newResult <- upsert ExamResult
|
newResult <- upsert ExamResult
|
||||||
{ examResultExam = eId
|
{ examResultExam = eId
|
||||||
, examResultUser = uid
|
, examResultUser = uid
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user