From decd514b22d189a9524341456654eeac2deb6820 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Mon, 9 Feb 2026 09:50:22 +0100 Subject: [PATCH] WIP improving notenimport with punktefeature --- .../controllers/api/frontend/v1/Noten.php | 99 +++++++++++++++---- .../Cis/Benotungstool/Benotungstool.js | 70 ++++++++----- .../DropdownModes/LehreinheitenModule.js | 3 +- public/js/helpers/StringHelpers.js | 19 +++- 4 files changed, 141 insertions(+), 50 deletions(-) diff --git a/application/controllers/api/frontend/v1/Noten.php b/application/controllers/api/frontend/v1/Noten.php index f4d9d3584..125b3e0bc 100644 --- a/application/controllers/api/frontend/v1/Noten.php +++ b/application/controllers/api/frontend/v1/Noten.php @@ -44,6 +44,20 @@ class Noten extends FHCAPI_Controller $this->load->library('AuthLib', null, 'AuthLib'); $this->load->library('PhrasesLib'); + + // Loads LogLib with different debug trace levels to get data of the job that extends this class + // It also specify parameters to set database fields + $this->load->library('LogLib', array( + 'classIndex' => 5, + 'functionIndex' => 5, + 'lineIndex' => 4, + 'dbLogType' => 'API', // required + 'dbExecuteUser' => 'RESTful API', + 'requestId' => 'API', + 'requestDataFormatter' => function ($data) { + return json_encode($data); + } + ), 'logLib'); // Loads phrases system $this->loadPhrases([ @@ -235,10 +249,12 @@ class Noten extends FHCAPI_Controller if (defined('CIS_GESAMTNOTE_GEWICHTUNG') && CIS_GESAMTNOTE_GEWICHTUNG) { // Lehreinheitsgewichtung $punkte_vorschlag = round($punktesumme_gewichtet / $gewichtsumme, 2); - $note_vorschlag = $this->NotenschluesselaufteilungModel->getNote($punkte_vorschlag, $lv_id, $sem_kurzbz); + $note_vorschlag_result = $this->NotenschluesselaufteilungModel->getNote($punkte_vorschlag, $lv_id, $sem_kurzbz); + $note_vorschlag = $this->getDataOrTerminateWithError($note_vorschlag_result); } else { $punkte_vorschlag = round($punktesumme / $anzahlnoten, 2); - $note_vorschlag = $this->NotenschluesselaufteilungModel->getNote($punkte_vorschlag, $lv_id, $sem_kurzbz); + $note_vorschlag_result = $this->NotenschluesselaufteilungModel->getNote($punkte_vorschlag, $lv_id, $sem_kurzbz); + $note_vorschlag = $this->getDataOrTerminateWithError($note_vorschlag_result); } } else { if (defined('CIS_GESAMTNOTE_GEWICHTUNG') && CIS_GESAMTNOTE_GEWICHTUNG) { @@ -403,6 +419,11 @@ class Noten extends FHCAPI_Controller } $studlist .= ""; + $this->logLib->logInfoDB(array('saveStudentenNoten', array( + 'updatevon' => getAuthUID(), + 'updateamum' => date('Y-m-d H:i:s') + ), getAuthUID(), getAuthPersonId(), array($result->noten, $lv_id, $sem_kurzbz))); + // always send the mail, config toggles data contents $this->sendFreigabeEmail($lektorFullName, $lvaFullName, count($result->noten), $emails, $studlist, $betreff); @@ -505,21 +526,12 @@ class Noten extends FHCAPI_Controller $typ = $result->typ; $jetzt = date("Y-m-d H:i:s"); - - // nachpruefungeintragen.php script calls query on campus.student_lehrveranstaltung to find a - // lehreinheit_id for lva_id -> lehreinheit should be determined prior to that in new benotungstool - // by retrieving it from students row in campus.vw_student_lehrveranstaltung earlier on - -// $lehreinheit_id = getLehreinheit($db, $lvid, $student_uid, $stsem); -// $lehreinheit_id = $result->lehreinheit_id; - -// $punkte = null; if(isset($punkte) && $punkte >= 0) { // Bei Punkteeingabe wird die Note nochmals geprueft und ggf korrigiert $result = $this->NotenschluesselaufteilungModel->getNote($punkte, $lva_id, $stsem); if(isError($result)) { - $this->terminateWithError('notenspiegel hats zrissen'); + $this->terminateWithError('Notenspiegel Error'); } else { $data = getData($result); if($data != $note) @@ -533,7 +545,6 @@ class Noten extends FHCAPI_Controller // TODO: more sophisticated empty check if($note=='') $note = 9; - $this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); @@ -549,7 +560,6 @@ class Noten extends FHCAPI_Controller $this->terminateWithError('Kein gültiger Studiengang gefunden für ID: '.$studiengang_kz); } - //Gesamtnote updaten $result = $this->LvgesamtnoteModel->getLvGesamtNoten($lva_id, $student_uid, $stsem); if(!isError($result) && !hasData($result)) { @@ -576,6 +586,12 @@ class Noten extends FHCAPI_Controller $res = $this->LvgesamtnoteModel->load($id->retval); if(hasData($res)) $lvgesamtnote = getData($res)[0]; } + + $this->logLib->logInfoDB(array('saveStudentPruefung insert lvnote', $res, array( + 'insertvon' => getAuthUID(), + 'insertamum' => date('Y-m-d H:i:s') + ), getAuthUID(), getAuthPersonId(), $student_uid, $lva_id, $stsem, $note,$punkte, $jetzt)); + } else if(!isError($result) && hasData($result)) { @@ -597,6 +613,10 @@ class Noten extends FHCAPI_Controller if(hasData($res)) $lvgesamtnote = getData($res)[0]; } + $this->logLib->logInfoDB(array('saveStudentPruefung update lvnote', $res, array( + 'updatevon' => getAuthUID(), + 'updateamum' => date('Y-m-d H:i:s') + ), getAuthUID(), getAuthPersonId(), $student_uid, $lva_id, $stsem, $note,$punkte, $jetzt)); } // save pruefung after updating lvnote, since pruefungspunkte get loaded by lv punkte @@ -705,8 +725,10 @@ class Noten extends FHCAPI_Controller if(hasData($res)) $pruefungenChanged['extraPruefung'] = getData($res); } } - - + + $this->logLib->logInfoDB(array('termin1 created',$res, getAuthUID(), getAuthPersonId())); + + // Die Pruefung wird als Termin2 eingetragen $result2 = $this->LePruefungModel->getPruefungenByUidTypLvStudiensemester($student_uid, "Termin2", $lva_id, $stsem); // if there is a termin 2 entry already update it @@ -729,6 +751,9 @@ class Noten extends FHCAPI_Controller if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res); } + $this->logLib->logInfoDB(array('termin2 updated',$res, getAuthUID(), getAuthPersonId())); + + } else if(!isError($result2) && !hasData($result2)) { // new entry termin 2 @@ -753,6 +778,9 @@ class Noten extends FHCAPI_Controller $res = $this->LePruefungModel->load($id->retval); if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res); } + + $this->logLib->logInfoDB(array('termin2 inserted',$res, getAuthUID(), getAuthPersonId())); + } } else if($typ == "Termin3" && defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN3') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN3) @@ -780,6 +808,8 @@ class Noten extends FHCAPI_Controller if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res); } + $this->logLib->logInfoDB(array('termin3 updated',$res, getAuthUID(), getAuthPersonId())); + } else if(!isError($result3) && !hasData($result3)) { // insert new termin3 @@ -805,6 +835,8 @@ class Noten extends FHCAPI_Controller if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res); } + $this->logLib->logInfoDB(array('termin3 inserted',$res, getAuthUID(), getAuthPersonId())); + } } else { $this->terminateWithError($this->p->t('benotungstool', 'wrongPruefungType', [$student_uid, $typ]), 'general'); @@ -855,6 +887,9 @@ class Noten extends FHCAPI_Controller $res = $this->LvgesamtnoteModel->load($id->retval); if(hasData($res)) $lvgesamtnote = getData($res)[0]; } + + $this->logLib->logInfoDB(array('saveNotenvorschlag update lv gesamtnote',$res, getAuthUID(), getAuthPersonId())); + } else if(!isError($result) && !hasData($result)) { $id = $this->LvgesamtnoteModel->insert( array( @@ -878,6 +913,8 @@ class Noten extends FHCAPI_Controller $res = $this->LvgesamtnoteModel->load($id->retval); if(hasData($res)) $lvgesamtnote = getData($res)[0]; } + + $this->logLib->logInfoDB(array('saveNotenvorschlag insert lv gesamtnote',$res, getAuthUID(), getAuthPersonId())); } $this->terminateWithSuccess(array($lvgesamtnote)); @@ -895,6 +932,7 @@ class Noten extends FHCAPI_Controller !property_exists($result, 'noten')) { $this->terminateWithError($this->p->t('global', 'missingParameters'), 'general'); } + $lv_id = $result->lv_id; $sem_kurzbz = $result->sem_kurzbz; @@ -907,6 +945,11 @@ class Noten extends FHCAPI_Controller $result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, $note->uid, $sem_kurzbz); + if(defined(CIS_GESAMTNOTE_PUNKTE) && CIS_GESAMTNOTE_PUNKTE) { + $note->note = $this->NotenschluesselaufteilungModel->getNote($note->punkte, $lv_id, $sem_kurzbz); + $this->addMeta($note); + } + if(!isError($result) && hasData($result)) { $lvgesamtnote = getData($result)[0]; @@ -914,7 +957,7 @@ class Noten extends FHCAPI_Controller [$lvgesamtnote->student_uid, $lvgesamtnote->studiensemester_kurzbz, $lvgesamtnote->lehrveranstaltung_id], array( 'note' => trim($note->note), - 'punkte' => null, + 'punkte' => $note->punkte, 'benotungsdatum' => date("Y-m-d H:i:s"), 'updateamum' => date("Y-m-d H:i:s"), 'updatevon' => getAuthUID() @@ -925,6 +968,9 @@ class Noten extends FHCAPI_Controller $res = $this->LvgesamtnoteModel->load($id->retval); if(hasData($res)) $lvgesamtnote = getData($res)[0]; } + + $this->logLib->logInfoDB(array('saveNotenvorschlagBulk update lv gesamtnote',$res, getAuthUID(), getAuthPersonId())); + } else if(!isError($result) && !hasData($result)) { $id = $this->LvgesamtnoteModel->insert( array( @@ -932,7 +978,7 @@ class Noten extends FHCAPI_Controller 'lehrveranstaltung_id' => $lv_id, 'studiensemester_kurzbz' => $sem_kurzbz, 'note' => trim($note->note), - 'punkte' => null, + 'punkte' => $note->punkte, 'mitarbeiter_uid' => getAuthUID(), 'benotungsdatum' => date("Y-m-d H:i:s"), 'freigabedatum' => null, @@ -948,6 +994,8 @@ class Noten extends FHCAPI_Controller $res = $this->LvgesamtnoteModel->load($id->retval); if(hasData($res)) $lvgesamtnote = getData($res)[0]; } + + $this->logLib->logInfoDB(array('saveNotenvorschlagBulk insert lv gesamtnote',$res, getAuthUID(), getAuthPersonId())); } $retLvNoten[] = $lvgesamtnote; @@ -987,6 +1035,8 @@ class Noten extends FHCAPI_Controller $ret[$student->uid] = $this->savePruefungstermin($typ, $student_uid, $lva_id, $stsem, $lehreinheit_id, $note, $punkte, $datum); } + $this->logLib->logInfoDB(array('createPruefungen',$ret, getAuthUID(), getAuthPersonId())); + $this->terminateWithSuccess($ret); } @@ -1010,15 +1060,23 @@ class Noten extends FHCAPI_Controller $ret = []; foreach ($pruefungen as $pruefung) { + + if(defined(CIS_GESAMTNOTE_PUNKTE) && CIS_GESAMTNOTE_PUNKTE) { + $result = $this->NotenschluesselaufteilungModel->getNote($pruefung->punkte, $lv_id, $sem_kurzbz); + $pruefung->note = $this->getDataOrTerminateWithError($result); + } + $student_uid = $pruefung->uid; $typ = $pruefung->typ; $note = $pruefung->note; // TODO: parameterize for import maybe $datum = $pruefung->datum; - $punkte = null; + $punkte = $pruefung->punkte; $lehreinheit_id = $pruefung->lehreinheit_id; $ret[$student_uid] = $this->savePruefungstermin($typ, $student_uid, $lv_id, $sem_kurzbz, $lehreinheit_id, $note, $punkte, $datum); } + + $this->logLib->logInfoDB(array('savePruefungenBulk',$ret, getAuthUID(), getAuthPersonId())); $this->terminateWithSuccess($ret); } @@ -1051,8 +1109,7 @@ class Noten extends FHCAPI_Controller public function getNoteByPunkte() { $result = $this->getPostJSON(); - - // TODO validate post properly + if(!property_exists($result, 'punkte') || !property_exists($result, 'lv_id') || !property_exists($result, 'sem_kurzbz')) { diff --git a/public/js/components/Cis/Benotungstool/Benotungstool.js b/public/js/components/Cis/Benotungstool/Benotungstool.js index 7f23ca8d6..ca79737e0 100644 --- a/public/js/components/Cis/Benotungstool/Benotungstool.js +++ b/public/js/components/Cis/Benotungstool/Benotungstool.js @@ -260,7 +260,6 @@ export const Benotungstool = { // in case we need to further validate noten, currently parser does all }, parseNote(rowParts, notenbulk, rowNum) { - debugger const id = this.identifyUid(rowParts[0]) const idTrimmed = rowParts[0].trim() let student = null @@ -271,36 +270,47 @@ export const Benotungstool = { student = this.studenten.find(s => s.uid?.trim() === idTrimmed) } if(!student) { + // TODO: phrase this.$fhcAlert.alertWarning('Kein Student gefunden für ID ' + rowParts[0] + ' in Zeile Nr. ' + rowNum + ' Die Zeile wurde übersprungen.') return } - const note = rowParts[1] + let punkte = null + let note = null + if(this.config?.CIS_GESAMTNOTE_PUNKTE) { + punkte = Number.parseFloat(rowParts[1]) + } else { + note = rowParts[1] - // find notenoption and check if its allowed to use in lehre - const notenOption = this.notenOptions.find(n => n.note == note) - if(!notenOption.lehre) { - this.$fhcAlert.alertWarning('Keine gültige Note gefunden für ID ' + rowParts[0] + ' in Zeile Nr. ' + rowNum + ' Die Zeile wurde übersprungen.') - return + // find notenoption and check if its allowed to use in lehre + const notenOption = this.notenOptions.find(n => n.note == note) + if(!notenOption.lehre) { + // TODO: phrasen + this.$fhcAlert.alertWarning('Keine gültige Note gefunden für ID ' + rowParts[0] + ' in Zeile Nr. ' + rowNum + ' Die Zeile wurde übersprungen.') + return + } } - - notenbulk.push({uid: student.uid, note}) + + notenbulk.push({uid: student.uid, note, punkte}) }, parsePruefung(rowParts, pruefungbulk, rowNum) { const id = this.identifyUid(rowParts[0]) + const idTrimmed = rowParts[0].trim() let student = null if(id === 'matrikelnr') { // find student by matrnr and use uid later on - student = this.studenten.find(s => s.matrikelnr === rowParts[0]) + student = this.studenten.find(s => s.matrikelnr?.trim() === idTrimmed) } else if(id === 'uid') { - student = this.studenten.find(s => s.uid === rowParts[0]) + student = this.studenten.find(s => s.uid?.trim() === idTrimmed) } if(!student) { + // TODO: phrase this.$fhcAlert.alertWarning('Kein Student gefunden für ID ' + rowParts[0] + ' in Zeile Nr. ' + rowNum + ' Die Zeile wurde übersprungen.') return } const datum = rowParts[1] // should be in 'dd.MM.yyyy' if(!this.isValidDate_ddmmyyyy(datum)) { + // TODO: phrase this.$fhcAlert.alertWarning('Ungültiges Datumformat für ID ' + rowParts[0] + ' in Zeile Nr. ' + rowNum + '. Bitte verwenden Sie das Format "DD.MM.YYYY". Die Zeile wurde übersprungen.') return } @@ -314,21 +324,27 @@ export const Benotungstool = { let monthInt = parseInt(month, 10) monthInt -= 1 const dateObj = new Date(year, monthInt, day) - - const note = rowParts[2] - // find notenoption and check if its allowed to use in lehre - const notenOption = this.notenOptions.find(n => n.note == note) - if(!notenOption.lehre) { - - - this.$fhcAlert.alertWarning('Keine gültige Note gefunden für ID ' + rowParts[0] + ' in Zeile Nr. ' + rowNum + ' Die Zeile wurde übersprungen.') - return - } + let punkte = null + let note = null + if(this.config?.CIS_GESAMTNOTE_PUNKTE) { + punkte = Number.parseFloat(rowParts[1]) + } else { + note = rowParts[1] + + // find notenoption and check if its allowed to use in lehre + const notenOption = this.notenOptions.find(n => n.note == note) + if(!notenOption.lehre) { + // TODO: phrasen + this.$fhcAlert.alertWarning('Keine gültige Note gefunden für ID ' + rowParts[0] + ' in Zeile Nr. ' + rowNum + ' Die Zeile wurde übersprungen.') + return + } + } + const typ = this.getPruefungstypForStudentByAntritt(student) - pruefungbulk.push({uid: student.uid, datum: dateStr, note, typ, lehreinheit_id: student.lehreinheit_id, dateObj}) + pruefungbulk.push({uid: student.uid, datum: dateStr, note, punkte, typ, lehreinheit_id: student.lehreinheit_id, dateObj}) }, saveNotenBulk(notenbulk) { this.loading = true @@ -477,8 +493,6 @@ export const Benotungstool = { } }) - // TODO: punkte parsen! - // parsers check for notenOption.lehre === true and if student uid/matrikelnr matches // pruefungen check for younger pruefungen, so there are no further antritte with @@ -514,6 +528,7 @@ export const Benotungstool = { return arrsREqual }, getNotenTableOptions() { + return { // debugEventsExternal:true, // debugEventsInternal:true, @@ -527,17 +542,18 @@ export const Benotungstool = { selectablePersistence: false, // reset selection on table reload selectableCheck: function(row, e){ const data = row.getData(); - + if(data['kommPruef']) return false else if(data.hoechsterAntritt >= 3) return false // 3 pruefungen counted - + return true; // student can be selected to add pruefung }, rowHeight: 40, rowFormatter: this.fixTabulatorSelectionFormatter, columns: this.getColumnsDefinition(), persistence: false, - } + } + }, getColumnsDefinition() { const columns = [] diff --git a/public/js/components/DropdownModes/LehreinheitenModule.js b/public/js/components/DropdownModes/LehreinheitenModule.js index 25686c81b..8bea0ac77 100644 --- a/public/js/components/DropdownModes/LehreinheitenModule.js +++ b/public/js/components/DropdownModes/LehreinheitenModule.js @@ -1,4 +1,5 @@ import ApiLehre from "../../api/factory/lehre.js"; +import {capitalize} from "../../helpers/StringHelpers.js"; const options = Vue.ref([]); const params = Vue.ref({}); @@ -63,7 +64,7 @@ async function fetchLehreinheiten(lv_id, sem_kurzbz) { const LehreinheitenModule = Vue.reactive({ options, optionLabel: 'infoString', - placeholder: Vue.computed(()=>appContext?.$p.t('lehre/lehreinheit')), + placeholder: capitalize(Vue.computed(()=>appContext?.$p.t('lehre/lehreinheit'))), setupContext, bindParams }); diff --git a/public/js/helpers/StringHelpers.js b/public/js/helpers/StringHelpers.js index a67d0138f..4b3686ca6 100644 --- a/public/js/helpers/StringHelpers.js +++ b/public/js/helpers/StringHelpers.js @@ -1,4 +1,21 @@ export function capitalize(string) { if (!string) return ''; - return string[0].toUpperCase() + string.slice(1); + + if (Vue.isRef(string)) { + console.log('Vue.isRef(string)', string) + + return Vue.computed(() => { + const val = Vue.unref(string); + return (val && typeof val === 'string') ? val.charAt(0).toUpperCase() + val.slice(1) : ''; + }); + } + + // just a plain string, return a plain string + if (typeof string === 'string') { + console.log('if (typeof string === \'string\') {', string) + return string.charAt(0).toUpperCase() + string.slice(1); + } + + return ''; + } \ No newline at end of file