diff --git a/application/controllers/api/frontend/v1/Noten.php b/application/controllers/api/frontend/v1/Noten.php
index f8bbc966b..261bf612b 100644
--- a/application/controllers/api/frontend/v1/Noten.php
+++ b/application/controllers/api/frontend/v1/Noten.php
@@ -242,7 +242,7 @@ class Noten extends FHCAPI_Controller
$res = $this->LehrveranstaltungModel->load($lv_id);
if(isError($res) || !hasData($res)) {
- $this->terminateWithError('Keine gültige Lehrveranstaltung gefunden für ID: '.$lv_id);
+ $this->terminateWithError($this->p->t('benotungstool', 'noValidLvFoundForId', [$lv_id]));
}
$lv = getData($res)[0];
@@ -250,7 +250,7 @@ class Noten extends FHCAPI_Controller
$studiengang_kz = $lv->studiengang_kz;
$res = $this->StudiengangModel->load($studiengang_kz);
if(isError($res) || !hasData($res)) {
- $this->terminateWithError('Kein gültiger Studiengang gefunden für ID: '.$studiengang_kz);
+ $this->terminateWithError($this->p->t('benotungstool', 'noValidStudiengangFoundForId', [$studiengang_kz]));
}
$sg = getData($res)[0];
$lvaFullName = $sg->kurzbzlang . ' ' . $lv->semester . '.Semester
@@ -261,7 +261,7 @@ class Noten extends FHCAPI_Controller
$res = $this->PersonModel->load(getAuthPersonId());
if(isError($res) || !hasData($res)) {
- $this->terminateWithError('Keine gültige Person gefunden für ID: '.getAuthPersonId());
+ $this->terminateWithError($this->p->t('benotungstool', 'noValidPersonFoundForId', [getAuthPersonId()]));
}
$pers = getData($res)[0];
$lektorFullName = $pers->anrede.' '.$pers->vorname.' '.$pers->nachname; //.' ('.$pers->kurzbz.')';
@@ -273,7 +273,7 @@ class Noten extends FHCAPI_Controller
foreach ($data as $row) {
$studienplan_bezeichnung .= $row->bezeichnung . ' ';
}
- $betreff = 'Notenfreigabe ' . $lv->bezeichnung . ' ' . $lv->orgform_kurzbz . ' - ' . $studienplan_bezeichnung;
+ $betreff = $this->p->t('benotungstool','notenfreigabe').' ' . $lv->bezeichnung . ' ' . $lv->orgform_kurzbz . ' - ' . $studienplan_bezeichnung;
$studlist = "
";
@@ -493,7 +493,7 @@ class Noten extends FHCAPI_Controller
$this->addMeta('$pruefungenChanged', $pruefungenChanged);
$this->addMeta('$lvgesamtnote', $lvgesamtnote);
$savedPruefung = $pruefungenChanged['savedPruefung'] ?? null;
- $extraPruefung = $pruefungenChanged['extraPruefung'] ?? null; // TODO: test
+ $extraPruefung = $pruefungenChanged['extraPruefung'] ?? null;
$savedPruefungData = count($savedPruefung) > 0 ? $savedPruefung[0] : null;
$extraPruefungData = count($extraPruefung) > 0 ? $extraPruefung[0] : null;
@@ -509,8 +509,8 @@ class Noten extends FHCAPI_Controller
$this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel');
-
- if($typ == "Termin2") {
+ if($typ == "Termin2")
+ {
// Wenn eine Nachprüfung angelegt wird, wird zuerst eine Pruefung mit 1. Termin angelegt welche für die ursprüngliche Note
// vor den Prüfungsantritten zählt
@@ -537,7 +537,7 @@ class Noten extends FHCAPI_Controller
}
else if(!hasData($resultLV))// set Termin1 note to "noch nicht eingetragen"
{
- $pr_note = 9;
+ $pr_note = 9;
$pr_punkte = null;
$benotungsdatum = $jetzt;
}
@@ -550,7 +550,7 @@ class Noten extends FHCAPI_Controller
'student_uid' => $student_uid,
'mitarbeiter_uid' => getAuthUID(),
'note' => $pr_note,
- 'punkte' => $pr_punkte,
+// 'punkte' => $pr_punkte,
'pruefungstyp_kurzbz' => "Termin1",
'datum' => $benotungsdatum,
'anmerkung' => "",
@@ -581,7 +581,7 @@ class Noten extends FHCAPI_Controller
'updateamum' => $jetzt,
'updatevon' => getAuthUID(),
'note' => $note,
- 'punkte' => $punkte,
+// 'punkte' => $punkte,
'datum' => $datum,
'anmerkung' => ""
)
@@ -600,7 +600,7 @@ class Noten extends FHCAPI_Controller
'student_uid' => $student_uid,
'mitarbeiter_uid' => getAuthUID(),
'note' => $note,
- 'punkte' => null,//$punkte,
+// 'punkte' => null,//$punkte,
'pruefungstyp_kurzbz' => $typ,
'datum' => $datum,
'anmerkung' => "",
@@ -617,7 +617,8 @@ class Noten extends FHCAPI_Controller
}
}
- } else if($typ == "Termin3") {
+ } else if($typ == "Termin3")
+ {
$result3 = $this->LePruefungModel->getPruefungenByUidTypLvStudiensemester($student_uid, "Termin3", $lva_id, $stsem);
@@ -631,7 +632,7 @@ class Noten extends FHCAPI_Controller
'updateamum' => $jetzt,
'updatevon' => getAuthUID(),
'note' => $note,
- 'punkte' => $punkte,
+// 'punkte' => $punkte,
'datum' => $datum,
'anmerkung' => ""
)
@@ -650,7 +651,7 @@ class Noten extends FHCAPI_Controller
'student_uid' => $student_uid,
'mitarbeiter_uid' => getAuthUID(),
'note' => $note,
- 'punkte' => null,//$punkte,
+// 'punkte' => null,//$punkte,
'pruefungstyp_kurzbz' => $typ,
'datum' => $datum,
'anmerkung' => "",
@@ -668,8 +669,7 @@ class Noten extends FHCAPI_Controller
}
} else {
- // TODO: proper error phrase that explains better why we terminated with error
- $this->terminateWithError("Typ is not termin2 or termin3.", 'general');
+ $this->terminateWithError($this->p->t('benotungstool', 'wrongPruefungType', [$student_uid, $typ]), 'general');
}
return $pruefungenChanged;
@@ -749,7 +749,6 @@ class Noten extends FHCAPI_Controller
$noten = $result->noten;
$retLvNoten = [];
- $responseMsgs = [];
foreach($noten as $note)
{
@@ -858,7 +857,6 @@ class Noten extends FHCAPI_Controller
$ret[$student_uid] = $this->savePruefungstermin($typ, $student_uid, $lv_id, $sem_kurzbz, $lehreinheit_id, $note, $punkte, $datum);
}
-
$this->terminateWithSuccess($ret);
}
diff --git a/application/models/organisation/Studienplan_model.php b/application/models/organisation/Studienplan_model.php
index 569aebd0b..5a3f3498d 100644
--- a/application/models/organisation/Studienplan_model.php
+++ b/application/models/organisation/Studienplan_model.php
@@ -59,6 +59,37 @@ class Studienplan_model extends DB_Model
'tbl_studienplan_lehrveranstaltung.semester' => $semester
));
}
+
+ public function getStudienplanByLvaSemKurzbz($lehrveranstaltung_id, $studiensemester_kurzbz) {
+ $qry= "
+ SELECT
+ DISTINCT tbl_studienplan.*
+ FROM
+ lehre.tbl_studienplan
+ JOIN lehre.tbl_studienplan_lehrveranstaltung
+ USING(studienplan_id)
+ WHERE
+ tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id IN (
+ SELECT
+ lv.lehrveranstaltung_id
+ FROM
+ lehre.tbl_lehrveranstaltung AS lv
+ LEFT JOIN lehre.tbl_lehrveranstaltung AS t ON t.lehrveranstaltung_id=lv.lehrveranstaltung_template_id
+ WHERE
+ lv.lehrtyp_kurzbz<>'tpl'
+ AND (lv.lehrveranstaltung_id= ? OR (lv.lehrveranstaltung_template_id= ? AND t.lehrtyp_kurzbz='tpl'))
+ )
+ AND EXISTS (
+ SELECT 1
+ FROM
+ lehre.tbl_studienplan_semester
+ WHERE studienplan_id=tbl_studienplan.studienplan_id
+ AND studiensemester_kurzbz= ?
+ AND semester = tbl_studienplan_lehrveranstaltung.semester)
+ ORDER BY bezeichnung";
+
+ return $this->execReadOnlyQuery($qry, array($lehrveranstaltung_id, $lehrveranstaltung_id, $studiensemester_kurzbz));
+ }
public function getStudienplanLehrveranstaltungForPrestudent($studienplan_id, $semester, $prestudent_id)
{
diff --git a/public/js/components/Cis/Benotungstool/Benotungstool.js b/public/js/components/Cis/Benotungstool/Benotungstool.js
index 2cdfad4a2..a873e2613 100644
--- a/public/js/components/Cis/Benotungstool/Benotungstool.js
+++ b/public/js/components/Cis/Benotungstool/Benotungstool.js
@@ -39,6 +39,9 @@ export const Benotungstool = {
},
data() {
return {
+ neuesPruefungsdatumModalVisible: false,
+ offsetLeft: 0,
+ headerEl: null,
loading: false,
selectedUids: [], // shared selection state
selectedLehreinheit: null,
@@ -76,7 +79,16 @@ export const Benotungstool = {
{
event: "rowSelectionChanged",
handler: async (data, rows) => {
- this.selectedUids = data.filter(d => d.selectable);
+ // avoid an expensive update loop if selection happens in modal
+ if(this.neuesPruefungsdatumModalVisible) return
+
+ if(data.length == 1 && this.selectedUids.length == 1 && data[0].uid === this.selectedUids[0].uid){
+ // special case to work around an internal tabulator selection quirk
+ this.selectedUids = []
+ } else {
+ this.selectedUids = data.filter(d => d.selectable);
+ }
+
}
},
{
@@ -101,7 +113,7 @@ export const Benotungstool = {
row.reformat() // trigger reformat of arrow
}
}
- },
+ }
]};
},
methods: {
@@ -243,7 +255,7 @@ export const Benotungstool = {
this.loading = true
this.$api.call(ApiNoten.saveNotenvorschlagBulk(this.lv_id, this.sem_kurzbz, notenbulk)).then(res => {
if(res.meta.status === 'success') {
- this.$fhcAlert.alertWarning('Noten erfolgreich importiert') // TODO: phrase
+ this.$fhcAlert.alertSuccess(this.$p.t('benotungstool/notenImportSuccessAlert'))
const lvNoten = res.data
@@ -274,7 +286,7 @@ export const Benotungstool = {
this.$api.call(ApiNoten.saveStudentPruefungBulk(this.lv_id, this.sem_kurzbz, pruefungenbulk))
.then((res)=> {
if(res.meta.status === 'success') {
- this.$fhcAlert.alertWarning('Prüfungen erfolgreich importiert und gespeichert') // TODO: phrase
+ this.$fhcAlert.alertSuccess(this.$p.t('benotungstool/pruefungImportSuccessAlert'))
this.handleAddNewPruefungenResponse(res, pruefungenbulk)
}
}).finally(()=>{this.loading = false})
@@ -282,8 +294,8 @@ export const Benotungstool = {
handleAddNewPruefungenResponse(res, uids) {
const pruefungen = res.data
uids.forEach(entry => {
- const saved = pruefungen[entry.uid].savedPruefung
- const extra = pruefungen[entry.uid].extraPruefung
+ const saved = pruefungen[entry.uid].savedPruefung?.[0]
+ const extra = pruefungen[entry.uid].extraPruefung?.[0]
const student = this.studenten.find(s => s.uid == entry.uid)
if(!student) return
@@ -332,9 +344,7 @@ export const Benotungstool = {
this.distinctPruefungsDates.forEach((date, index)=>{
const dateparts = date.split('-')
const titledate = `${dateparts[2]}.${dateparts[1]}.${dateparts[0]}`
-
- // TODO: should studenten without shadow pruefung Termin have their "ursprüngliche Zeugnisnote"
- // col filled for consistency reasons?
+
// TODO: test if this holds true
const originalNote = index === 0
@@ -346,7 +356,8 @@ export const Benotungstool = {
hozAlign:"center",
widthGrow: 1,
minWidth: 150,
- originalNote
+ originalNote,
+ visible: true
})
})
@@ -419,7 +430,7 @@ export const Benotungstool = {
selectable: true,
selectableRangeMode: "click", // shift+click
selectablePersistence: false, // reset selection on table reload
- selectableCheck: function(row){
+ selectableCheck: function(row, e){
const data = row.getData();
if(data['kommPruef']) return false
@@ -558,7 +569,7 @@ export const Benotungstool = {
row.select();
}
- // stop Tabulator’s built-in handler
+ // stop built-in handler
e.stopPropagation();
return false;
},
@@ -576,7 +587,7 @@ export const Benotungstool = {
allowed.forEach(r => r.select());
}
- // stop Tabulator’s built-in handler
+ // stop built-in handler
e.stopPropagation();
return false;
},
@@ -598,13 +609,11 @@ export const Benotungstool = {
},
terminCalcFormatter(cell) {
const cellval = cell.getValue()
- // TODO: phrase
- return 'Prüflinge: ' + cellval
+ return this.$p.t('benotungstool/prueflingSelection')+': ' + cellval
},
negativeNotenCalcFormatter(cell) {
const cellval = cell.getValue()
- // TODO: phrase
- return 'Negativ: ' + cellval
+ return this.$p.t('benotungstool/c4negativ')+': ' + cellval
},
negativeNotenCalc(entries) {
return entries.reduce((acc, cur) => {
@@ -717,8 +726,8 @@ export const Benotungstool = {
s.freigegeben = this.checkFreigabe(s.freigabedatum, s.benotungsdatum, s.uid);
- row.update({ lv_note: data.note_vorschlag })
- row.update({ freigegeben: 'changed' })
+ row.update({ lv_note: data.note_vorschlag, freigegeben: 'changed' })
+ // row.update({ freigegeben: 'changed' })
row.reformat() // trigger reformat of arrow
this.changedNotenCounter++;
}
@@ -822,7 +831,7 @@ export const Benotungstool = {
// Third column (button)
const button = document.createElement('button');
button.className = 'btn btn-outline-secondary';
- button.textContent = 'Change'; // TODO: phrase
+ button.textContent = this.$p.t('benotungstool/changePruefungButtonText');
button.addEventListener('click', () => {
this.openPruefungModal(data, data[field], field);
});
@@ -839,7 +848,7 @@ export const Benotungstool = {
const button = document.createElement('button');
button.className = 'btn btn-outline-secondary';
- button.textContent = 'Add'; // TODO: phrase
+ button.textContent = this.$p.t('benotungstool/addPruefungButtonText');
button.addEventListener('click', () => {
this.openPruefungModal(data, null, field)
});
@@ -998,6 +1007,9 @@ export const Benotungstool = {
const kP = s.pruefungen?.find(p => p.pruefungstyp_kurzbz == 'kommPruef')
return !(kP || s.hoechsterAntritt >= 3)
},
+ set() {
+ // empty setter so tabulator doesnt scream
+ },
enumerable: true,
configurable: true
})
@@ -1032,7 +1044,8 @@ export const Benotungstool = {
hozAlign:"center",
widthGrow: 1,
minWidth: 200,
- originalNote
+ originalNote,
+ visible: true
})
})
@@ -1099,8 +1112,30 @@ export const Benotungstool = {
async setupMounted() {
this.tableBuiltPromise = new Promise(this.tableResolve)
await this.tableBuiltPromise
+
+ this.headerEl = document.getElementById('nav-main')
+ if(this.headerEl) {
+ this.headerEl.addEventListener("shown.bs.collapse", this.handleSidebar);
+ this.headerEl.addEventListener("hidden.bs.collapse", this.handleSidebar);
+ this.handleSidebar()
+ }
+
+
+
+
this.loadNoten(this.lv_id, this.sem_kurzbz)
- this.calcMaxTableHeight()
+ // this.calcMaxTableHeight()
+
+ },
+ handleSidebar() {
+ // this.$refs.notenTable.tabulator.blockRedraw(); // stop automatic resizes
+
+ this.offsetLeft = this.headerEl.getBoundingClientRect().width;
+
+ // setTimeout(() => {
+ // this.$refs.notenTable.tabulator.restoreRedraw(); // re-enable
+ // this.$refs.notenTable.tabulator.redraw(true); // manual recalculation
+ // }, 400); // after animation finishes
},
lvChanged(e) {
@@ -1188,8 +1223,8 @@ export const Benotungstool = {
this.sem_kurzbz,
typ
)).then(res => {
- if(res.meta.status === 'success') {
- this.$fhcAlert.alertWarning('Prüfung für Student ' + this.pruefungStudent.uid + ' bearbeitet oder angelegt') // TODO: phrase
+ if(res.meta.status === 'success') { //'Prüfung für Student ' + this.pruefungStudent.uid + ' bearbeitet oder angelegt'
+ this.$fhcAlert.alertSuccess(this.$p.t('benotungstool/pruefungSaveForUid', [this.pruefungStudent.uid]))
const s = this.studenten.find(s => s.uid === res.data[1]?.student_uid)
@@ -1214,18 +1249,18 @@ export const Benotungstool = {
s.hoechsterAntritt = this.getAntrittCountStudent(s)
}
- // TODO: maybe fake rerun the selectable check by just assigning
- // css classes tabulator-selectable/tabulator-unselectable on row elements
-
- // const rows = this.$refs.notenTable.tabulator.getRows()
- // const row = rows.find(r => {
- // const data = r.getData()
- // return data.uid == this.pruefungStudent.uid
- // })
- // row.reformat()
+ const oldScrollLeft = this.$refs.notenTable.tabulator.rowManager.scrollLeft
+ const oldScrollTop = this.$refs.notenTable.tabulator.rowManager.scrollTop
this.$refs.notenTable.tabulator.redraw(true)
-
- this.$fhcAlert.alertWarning('Prüfung gespeichert') // TODO: phrase
+
+ Vue.nextTick(()=> {
+ const table = this.$refs.notenTable.tabulator.element.querySelector('.tabulator-tableholder')
+ if(table) {
+ table.scrollLeft = oldScrollLeft;
+ table.oldScrollTop = oldScrollTop;
+ }
+ })
+
}
}).finally(()=> {
this.pruefungStudent = null
@@ -1296,7 +1331,8 @@ export const Benotungstool = {
hozAlign:"center",
widthGrow: 1,
minWidth: 200,
- originalNote
+ originalNote,
+ visible: true
})
})
@@ -1372,7 +1408,7 @@ export const Benotungstool = {
this.sem_kurzbz,
)).then(res => {
if(res.meta.status === "success") {
- this.$fhcAlert.alertWarning('Prüfung an ' + dateStr + ' angelegt') // TODO: phrase
+ this.$fhcAlert.alertSuccess(this.$p.t('benotungstool/pruefungAngelegtAn', [dateStr]))
this.handleAddNewPruefungenResponse(res, uids)
@@ -1492,7 +1528,7 @@ export const Benotungstool = {
return acc
}, []) : []
return cs
- }
+ },
},
created() {
this.setupCreated()
@@ -1500,7 +1536,12 @@ export const Benotungstool = {
mounted() {
this.setupMounted()
},
+ unmounted() {
+ this.headerEl.removeEventlistener(this.handleSidebar)
+ this.headerEl.removeEventlistener(this.handleSidebar)
+ },
template: `
+
{{$p.t('benotungstool/c4notenImportieren')}}
@@ -1515,7 +1556,10 @@ export const Benotungstool = {
-
+
{{$p.t('benotungstool/c4addNewPruefung')}}
@@ -1606,7 +1650,7 @@ export const Benotungstool = {
-
+
{{$p.t('benotungstool/benotungstoolTitle')}}
{{ lv?.bezeichnung }}
@@ -1652,7 +1696,7 @@ export const Benotungstool = {
-