shorter passwort freigabe text; loadCisConfig for Benotungstool via api; anw% in notentable via event; WIP incorporating CIS config into actual noten logic;

This commit is contained in:
Johann Hoffmann
2025-12-18 15:24:08 +01:00
parent 2cee36d7b5
commit 957da460a6
6 changed files with 222 additions and 76 deletions
@@ -27,22 +27,8 @@ class Benotungstool extends Auth_Controller
*/
public function index()
{
// TODO: check if related CIS config is also loaded when being routed in Cis4 by vuerouter
// TODO: check if new benotungstool should be configurable the exact same way?
// TODO: move this to some config api?
$viewData = array(
'uid'=>getAuthUID(),
'CIS_GESAMTNOTE_UEBERSCHREIBEN' => CIS_GESAMTNOTE_UEBERSCHREIBEN,
'CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF' => CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF,
'CIS_GESAMTNOTE_PRUEFUNG_TERMIN3' => CIS_GESAMTNOTE_PRUEFUNG_TERMIN3,
'CIS_GESAMTNOTE_PRUEFUNG_TERMIN2' => CIS_GESAMTNOTE_PRUEFUNG_TERMIN2,
'CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE' => CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE,
'CIS_GESAMTNOTE_PUNKTE' => CIS_GESAMTNOTE_PUNKTE,
'CIS_GESAMTNOTE_GEWICHTUNG' => CIS_GESAMTNOTE_GEWICHTUNG,
'CIS_ANWESENHEITSLISTE_NOTENLISTE_ANZEIGEN' => CIS_ANWESENHEITSLISTE_NOTENLISTE_ANZEIGEN
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Benotungstool']);
@@ -37,7 +37,8 @@ class Noten extends FHCAPI_Controller
'saveStudentPruefung' => array('lehre/benotungstool:rw'),
'createPruefungen' => array('lehre/benotungstool:rw'),
'saveNotenvorschlagBulk' => array('lehre/benotungstool:rw'),
'savePruefungenBulk' => array('lehre/benotungstool:rw')
'savePruefungenBulk' => array('lehre/benotungstool:rw'),
'getCisConfig' => array('lehre/benotungstool:rw')
]);
$this->load->library('AuthLib', null, 'AuthLib');
@@ -66,6 +67,31 @@ class Noten extends FHCAPI_Controller
}
public function getCisConfig() {
$this->terminateWithSuccess(
array(
// Punkte bei der Noteneingabe anzeigen
'CIS_GESAMTNOTE_PUNKTE' => CIS_GESAMTNOTE_PUNKTE,
// Gibt an ob der Lektor erneut eine LVNote eintragen kann wenn bereits eine Zeugnisnote vorhanden ist (true | false) DEFAULT true
'CIS_GESAMTNOTE_UEBERSCHREIBEN' => CIS_GESAMTNOTE_UEBERSCHREIBEN,
// Gewichtung der Lehreinheiten bei Noteneintragung true|false
'CIS_GESAMTNOTE_GEWICHTUNG' => CIS_GESAMTNOTE_GEWICHTUNG,
// Bei Gesamtnote eine zusaetzliche Spalte fuer den 2. Termin anzeigen
'CIS_GESAMTNOTE_PRUEFUNG_TERMIN2' => CIS_GESAMTNOTE_PRUEFUNG_TERMIN2,
// Bei Gesamtnote eine zusaetzliche Spalte fuer den 3. Termin anzeigen
// Erfordert den Eintrag "Termin3" in der Tabelle lehre.tbl_pruefungstyp
'CIS_GESAMTNOTE_PRUEFUNG_TERMIN3' => CIS_GESAMTNOTE_PRUEFUNG_TERMIN3,
// Bei Gesamtnote eine zusaetzliche Spalte fuer die kommissionelle Pruefung anlegen
'CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF' => CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF,
// Bei Gesamtnote eine zusaetzliche Spalte fuer die kommissionelle Pruefung anlegen
'CIS_GESAMTNOTE_PRUEFUNG_MOODLE_NOTE' => CIS_GESAMTNOTE_PRUEFUNG_MOODLE_NOTE,
// Bei Gesamtnote die Spalte fuer die Quelle der Noten anzeigen (Moodle oder LE)
'CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE' => CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE,
// Gibt an ob die Note im Notenfreigabemail enthalten ist oder nicht
'CIS_GESAMTNOTE_FREIGABEMAIL_NOTE' => CIS_GESAMTNOTE_FREIGABEMAIL_NOTE
)
);
}
/**
* GET METHOD
@@ -93,6 +119,12 @@ class Noten extends FHCAPI_Controller
$grades = array();
$student_uids = array_map($func, $studentenData);
$funcpre = function ($value) {
return $value->prestudent_id;
};
$prestudent_ids = array_map($funcpre, $studentenData);
if(count($student_uids) > 0) {
$mobres = $this->MobilitaetModel->getMobilityZusatzForUids($student_uids);
@@ -146,8 +178,14 @@ class Noten extends FHCAPI_Controller
$this->addMeta('getExternalGradesError', $t->getMessage());
}
// assign the anw% to the students in the studentData loop
$anwresult = $this->getAnwesenheiten($prestudent_ids, $lv_id, $sem_kurzbz);
// calculate notenvorschläge from teilnoten
foreach($studentenData as $student) {
$student->anwquote = $anwresult[$student->prestudent_id];
$g = $grades[$student->uid]['grades'];
$note_lv = $grades[$student->uid]['note_lv'];
@@ -214,7 +252,7 @@ class Noten extends FHCAPI_Controller
$pruefungen = $this->LePruefungModel->getPruefungenByLvStudiensemester($lv_id, $sem_kurzbz);
$pruefungenData = getData($pruefungen);
$this->terminateWithSuccess(array($studentenData, $pruefungenData, DOMAIN, $grades));
$this->terminateWithSuccess(array($studentenData, $pruefungenData, DOMAIN, $grades, $anwresult));
}
/**
@@ -536,6 +574,15 @@ class Noten extends FHCAPI_Controller
private function savePruefungstermin($typ, $student_uid, $lva_id, $stsem, $lehreinheit_id, $note, $punkte, $datum)
{
// extra check if the student has lvnote and a zeugnisnote in the relevant lva
$resultLV = $this->LvgesamtnoteModel->getLvGesamtNoten($lva_id, $student_uid, $stsem);
$lvgesamtnoteData = getData($resultLV);
$this->addMeta('lvgesamtnoteData', $lvgesamtnoteData);
// allocating pruefungen before lv note is forbidden
if($lvgesamtnoteData == null) return $this->p->t('benotungstool', 'c4keineLvNoteEingetragen');
$status = [];
// send $grades reference to moodle addon
@@ -928,6 +975,32 @@ class Noten extends FHCAPI_Controller
$this->terminateWithSuccess($ret);
}
private function getAnwesenheiten($prestudent_ids, $lv_id, $sem_kurzbz) {
$anwesenheiten = [];
try {
$downloadFunc = function ($anwesenheitenResult) use (&$anwesenheiten) {
// map result rows by prestudent_uid to retrieve them by that key later on
foreach ($anwesenheitenResult as $anw) {
$anwesenheiten[$anw->prestudent_id] = $anw->sum;
}
};
Events::trigger(
'getAnwesenheitenForLvAndSemester',
$prestudent_ids,
$lv_id,
$sem_kurzbz,
$downloadFunc
);
} catch (Throwable $t) {
$this->addMeta('getAnwesenheitenForLvAndSemester', $t->getMessage());
}
return $anwesenheiten;
}
}
@@ -312,7 +312,7 @@ class Lehrveranstaltung_model extends DB_Model
{
$query = "SELECT
distinct on(nachname, vorname, person_id) vorname, nachname, matrikelnr,
tbl_studentlehrverband.semester, tbl_studentlehrverband.verband, tbl_studentlehrverband.gruppe,
tbl_studentlehrverband.semester, tbl_studentlehrverband.verband, tbl_studentlehrverband.gruppe, tbl_student.prestudent_id,
(SELECT status_kurzbz FROM public.tbl_prestudentstatus WHERE prestudent_id=tbl_student.prestudent_id ORDER BY datum DESC, insertamum DESC, ext_id DESC LIMIT 1) as status,
tbl_bisio.bisio_id, tbl_bisio.von, tbl_bisio.bis, tbl_student.studiengang_kz AS stg_kz_student,
tbl_zeugnisnote.note, tbl_mitarbeiter.mitarbeiter_uid, tbl_person.matr_nr, tbl_benutzer.uid,
+6
View File
@@ -16,6 +16,12 @@
*/
export default {
getCisConfig(){
return {
method: 'get',
url: '/api/frontend/v1/Noten/getCisConfig'
};
},
getStudentenNoten(lv_id, sem_kurzbz) {
return {
method: 'get',
@@ -44,6 +44,7 @@ export const Benotungstool = {
},
data() {
return {
config: null, // cis config
neuesPruefungsdatumModalVisible: false,
loading: false,
selectedUids: [], // shared selection state
@@ -362,7 +363,8 @@ export const Benotungstool = {
// add col to table
const cols = [...this.notenTableOptions.columns.slice(0, -1)];
const kommCol = this.notenTableOptions.columns[this.notenTableOptions.columns.length - 1];
let kommCol = null
if(this.config?.CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF) kommCol = this.notenTableOptions.columns[this.notenTableOptions.columns.length - 1];
// TODO: could reuse cols instead of recreating all from a variable maybe
this.distinctPruefungsDates.forEach((date, index)=>{
@@ -385,7 +387,7 @@ export const Benotungstool = {
})
})
cols.push(kommCol) // keep kommPruef Col as last
if(this.config?.CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF) cols.push(kommCol) // keep kommPruef Col as last
// redraw table
this.loading = false
@@ -516,7 +518,8 @@ export const Benotungstool = {
{title: Vue.computed(() => this.$p.t('benotungstool/c4mail')), field: 'email', formatter: this.mailFormatter, tooltip: false, visible: false, widthGrow: 1, variableHeight: true},
{title: Vue.computed(() => this.$p.t('benotungstool/c4antrittCountv2')), field: 'hoechsterAntritt', tooltip: false, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4vorname')), field: 'vorname', headerFilter: true, tooltip: false, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4nachname')), field: 'nachname', headerFilter: true, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4nachname')), field: 'nachname', headerFilter: true, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4anwesenheitsquote')), field: 'anwquote', widthGrow: 1, formatter: this.percentFormatter},
{title: Vue.computed(() => this.$p.t('benotungstool/c4mobility')), field: 'mobility_zusatz', headerFilter: true, widthGrow: 1, visible: false},
{title: Vue.computed(() => this.$p.t('benotungstool/c4teilnoten')), field: 'teilnote', widthGrow: 1, formatter: this.teilnotenFormatter, variableHeight: true},
{title: Vue.computed(() => this.$p.t('benotungstool/c4note')), field: 'note_vorschlag',
@@ -953,6 +956,11 @@ export const Benotungstool = {
return '<div style="'+style+'">' +
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
},
percentFormatter(cell) {
const data = cell.getData()
const val = data.anwquote ?? '-'
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">'+ val + ' %</div>'
},
buildMailToLink(student){
return 'mailto:' + student.uid +'@'+ this.domain
},
@@ -988,7 +996,8 @@ export const Benotungstool = {
// let pruefungenRegularColCount = 0;
this.distinctPruefungsDates = []
const cols = [...this.notenTableOptions.columns.slice(0, -1)];
const kommCol = this.notenTableOptions.columns[this.notenTableOptions.columns.length - 1];
let kommCol = null
if(this.config?.CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF) kommCol = this.notenTableOptions.columns[this.notenTableOptions.columns.length - 1];
this.pruefungen?.forEach(p => {
const dateParts = p.datum.split('-')
@@ -1091,7 +1100,7 @@ export const Benotungstool = {
})
})
cols.push(kommCol) // keep kommPruef Col as last
if(this.config?.CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF) cols.push(kommCol) // keep kommPruef Col as last
this.loading = false
@@ -1119,11 +1128,16 @@ export const Benotungstool = {
if(!tableDataSet) return
const rect = tableDataSet.getBoundingClientRect();
this.notenTableOptions.height = window.visualViewport.height - rect.top
this.notenTableOptions.height = window.visualViewport.height - rect.top - 50
this.$refs.notenTable.tabulator.setHeight(this.notenTableOptions.height)
},
setupCreated() {
this.loading = true
// fetch cis config regarding gesamtnoteneingabe
this.$api.call(ApiNoten.getCisConfig()).then(res => {
this.config = res.data
})
// fetch lva dropdown
this.$api.call(ApiLehre.getZugewieseneLv(this.viewData?.uid, this.sem_kurzbz)).then(res => {
this.lehrveranstaltungen = res.data
@@ -1341,7 +1355,8 @@ export const Benotungstool = {
// add col to table
const cols = [...this.notenTableOptions.columns.slice(0, -1)];
const kommCol = this.notenTableOptions.columns[this.notenTableOptions.columns.length - 1];
let kommCol = null
if(this.config?.CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF) kommCol = this.notenTableOptions.columns[this.notenTableOptions.columns.length - 1];
// TODO: could reuse cols instead of recreating all from a variable maybe
@@ -1367,7 +1382,7 @@ export const Benotungstool = {
})
})
cols.push(kommCol) // keep kommPruef Col as last
if(this.config?.CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF) cols.push(kommCol) // keep kommPruef Col as last
// set Cols
this.$refs.notenTable.tabulator.clearSort()
@@ -1443,17 +1458,46 @@ export const Benotungstool = {
this.sem_kurzbz,
)).then(res => {
if(res.meta.status === "success") {
this.$fhcAlert.alertDefault(
'success',
'Info',
this.$p.t('benotungstool/pruefungAngelegtAn', [dateStrFront]),
true
)
this.handleAddNewPruefungenResponse(res, uids)
// iterate over response data
// -> alert successful pruefungen
// -> alert denied pruefungen + reason
let uidListSuccess = ''
let uidListError = ''
const successData = []
Object.keys(res.data).forEach(student_uid => {
const student = res.data[student_uid]
// actual pruefung has been allocated
if(student.savedPruefung || student.extraPruefung) {
uidListSuccess += ' ' + student_uid
// keep res.data format intact for handleResponse method
successData[student_uid] = student
} else { // there should be an error message why no pruefungen where allocated for this person, many reasons possible
uidListError += student_uid + ' - ' + student +'\n'// student variable is the error message here
}
})
if(uidListError != '') {
this.$fhcAlert.alertError(
this.$p.t('benotungstool/c4pruefungAnlageError', [dateStrFront]) + ': ' + uidListError + ' '
)
}
if(uidListSuccess != '') {
this.$fhcAlert.alertDefault(
'success',
'Info',
this.$p.t('benotungstool/pruefungAngelegtAn', [dateStrFront]) + ': ' + uidListSuccess,
true
)
this.handleAddNewPruefungenResponse({data: successData}, uids)
}
}
})
}).finally(()=>this.loading = false)
},
getAntrittCountStudent(student) {
// checks for existence of a prüfung with a note that resolves to a
@@ -1516,6 +1560,7 @@ export const Benotungstool = {
if(newVal) this.$refs.notenTable.tabulator.setFilter("lehreinheit_id", "=", newVal.lehreinheit_id);
},
getKommPruefCount(newVal) {
if(!this.config.CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF) return 0
if(this.$refs.notenTable?.tabulator && newVal > 0) {
const kommPruefCol = this.$refs.notenTable?.tabulator.getColumn("kommPruef")
kommPruefCol.show()
@@ -1696,24 +1741,24 @@ export const Benotungstool = {
</template>
</bs-modal>
<BsOffcanvas
ref="drawer"
placement="end"
:backdrop="true"
:style="{ '--bs-offcanvas-width': '600px' }"
>
<template #title>
</template>
<BsOffcanvas
ref="drawer"
placement="end"
:backdrop="true"
:style="{ '--bs-offcanvas-width': '600px' }"
>
<template #title>
</template>
<MobilityLegende/>
<template #footer>
</template>
</BsOffcanvas>
<MobilityLegende/>
<template #footer>
</template>
</BsOffcanvas>
<FhcOverlay :active="loading || saving"></FhcOverlay>
<FhcOverlay :active="loading"></FhcOverlay>
<div class="row">
<div class="col-4">
+62 -26
View File
@@ -51647,19 +51647,7 @@ and represent the current state of research on the topic. The prescribed citatio
<br><br>
Wenn alle einzutragenden Noten vermerkt sind (Nachtragung jederzeit möglich) können diese per Klick auf Freigabe für die Studiengangsassistenz freigegeben werden.
<br><br>
Aus Gründen der erhöhten Sicherheit ist bei der Freigabe der Noten die Eingabe Ihres Passwortes erforderlich.
<br><br>
Zulässige Noten: 1-5, Nicht beurteilt (nb), Noch nicht eingetragen (), Bestanden (b), Mit Erfolg teilgenommen (met), entschuldigt (en), unentschuldigt (ue)
<br><br>
Bei der Freigabe wird ein Info-Email an Sie und die zuständige Studiengangsassistenz geschickt. Enthalten sind Mat.Nr., Vor- und Zuname sowie die Note der neuen oder geänderten Einträge.
<br><br>
Freigegebene Einträge sind mit einem grün hinterlegten Häkchen gekennzeichnet.
<br><br>
Wenn Sie bereits freigegebene Noten verändern, werden diese mit einem grau hinterlegten Häkchen markiert (als Hinweis für Sie, dass die Assistenz bislang darüber noch nicht per Mail informiert wurde. Sie sieht allerdings diese neue Note sofort in ihrer Oberfläche)
<br><br>
Freigegebene Noten kann die Assistenz nun als Zeugnisnote übernehmen (wird im nächsten Feld für Sie zur Kontrolle angezeigt)
<br><br>
Wenn sich die Zeugnisnote von der von Ihnen freigegebenen Note unterscheidet wird erstere rot umrandet markiert.',
Aus Gründen der erhöhten Sicherheit ist bei der Freigabe der Noten die Eingabe Ihres Passwortes erforderlich.',
'description' => '',
'insertvon' => 'system'
),
@@ -51669,19 +51657,7 @@ and represent the current state of research on the topic. The prescribed citatio
<br><br>
Once all grades to be entered have been recorded (additions are possible at any time), they can be released to the program assistant by clicking on "Release."
<br><br>
For increased security reasons, you will be required to enter your password when releasing grades.2)
<br><br>
Permissible grades: 1-5, Not Assessed (nb), Not Yet Entered (), Passed (b), Successfully Participated (met), Excused (en), Unexcused (ue)
<br><br>
When the grade is released, an information email will be sent to you and the responsible program assistant. This email contains the student ID number, first and last name, and the grade for the new or changed entries.
<br><br>
Released entries are marked with a green check mark.
<br><br>
If you change grades that have already been approved, they will be marked with a gray checkmark (indicating that the assistant has not yet been notified by email. However, they will immediately see this new grade in their interface).
<br><br>
The assistant can now adopt approved grades as the grade on their report card (this will be displayed in the next field for your review).
<br><br>
If the report card grade differs from the grade you approved, the former will be highlighted with a red border.',
For increased security reasons, you will be required to enter your password when releasing grades.2)',
'description' => '',
'insertvon' => 'system'
)
@@ -51727,6 +51703,66 @@ and represent the current state of research on the topic. The prescribed citatio
)
)
),
array(
'app' => 'core',
'category' => 'benotungstool',
'phrase' => 'c4anwesenheitsquote',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Anwesenheit',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Attendance',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'benotungstool',
'phrase' => 'c4keineLvNoteEingetragen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Keine LV Note eingetragen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No Subject Grade entered',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'benotungstool',
'phrase' => 'c4pruefungAnlageError',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Prüfungstermin anlegen für {0} fehlgeschlagen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Not possible to allocate exam on {0}',
'description' => '',
'insertvon' => 'system'
)
)
),
// CIS4 GESAMTNOTENEINGABE ENDE ------------------------------------------------------------------------------------