mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-07-20 00:12:15 +00:00
testing/implementing more config flags; dont select on certain cols; certain cols only available with certain flags;
This commit is contained in:
@@ -18,18 +18,9 @@
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
//require_once('../../../include/studiengang.class.php');
|
||||
//require_once('../../../include/student.class.php');
|
||||
//require_once('../../../include/datum.class.php');
|
||||
//require_once('../../../include/mail.class.php');
|
||||
//require_once('../../../include/benutzerberechtigung.class.php');
|
||||
//require_once('../../../include/phrasen.class.php');
|
||||
//require_once('../../../include/projektarbeit.class.php');
|
||||
//require_once('../../../include/projektbetreuer.class.php');
|
||||
|
||||
class Lehre extends FHCAPI_Controller
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Object initialization
|
||||
*/
|
||||
@@ -38,39 +29,57 @@ class Lehre extends FHCAPI_Controller
|
||||
parent::__construct([
|
||||
'lvStudentenMail' => self::PERM_LOGGED,
|
||||
'LV' => self::PERM_LOGGED,
|
||||
'Pruefungen' => self::PERM_LOGGED
|
||||
'Pruefungen' => self::PERM_LOGGED,
|
||||
'getZugewieseneLv' => self::PERM_LOGGED,
|
||||
'getLeForLv' => self::PERM_LOGGED
|
||||
]);
|
||||
|
||||
|
||||
$this->load->library('PhrasesLib');
|
||||
|
||||
$this->loadPhrases(
|
||||
array(
|
||||
'global',
|
||||
'ui',
|
||||
'abgabetool'
|
||||
)
|
||||
);
|
||||
|
||||
$this->load->helper('hlp_sancho_helper');
|
||||
|
||||
require_once(FHCPATH . 'include/studiengang.class.php');
|
||||
require_once(FHCPATH . 'include/student.class.php');
|
||||
require_once(FHCPATH . 'include/projektarbeit.class.php');
|
||||
require_once(FHCPATH . 'include/projektbetreuer.class.php');
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
/**
|
||||
* constructs the emails of the groups from a lehrveranstaltung
|
||||
*/
|
||||
public function lvStudentenMail()
|
||||
public function lvStudentenMail()
|
||||
{
|
||||
$lehreinheit_id = $this->input->get("lehreinheit_id",TRUE);
|
||||
|
||||
// return early if the required parameter is missing
|
||||
if(!isset($lehreinheit_id))
|
||||
{
|
||||
$this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL);
|
||||
}
|
||||
$lehreinheit_id = $this->input->get("lehreinheit_id",TRUE);
|
||||
|
||||
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
|
||||
|
||||
$studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id);
|
||||
// return early if the required parameter is missing
|
||||
if(!isset($lehreinheit_id))
|
||||
{
|
||||
$this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL);
|
||||
}
|
||||
|
||||
$studentenMails = $this->getDataOrTerminateWithError($studentenMails);
|
||||
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
|
||||
|
||||
$studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id);
|
||||
|
||||
$studentenMails = $this->getDataOrTerminateWithError($studentenMails);
|
||||
|
||||
//convert array of objects into array of strings
|
||||
$studentenMails = array_map(function($element){
|
||||
return $element->mail;
|
||||
}, $studentenMails);
|
||||
|
||||
$this->terminateWithSuccess($studentenMails);
|
||||
$this->terminateWithSuccess($studentenMails);
|
||||
}
|
||||
|
||||
public function LV($studiensemester_kurzbz, $lehrveranstaltung_id)
|
||||
@@ -80,13 +89,13 @@ class Lehre extends FHCAPI_Controller
|
||||
$result = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(getAuthUID(), $studiensemester_kurzbz, getUserLanguage(), $lehrveranstaltung_id);
|
||||
|
||||
$result = current($this->getDataOrTerminateWithError($result));
|
||||
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* fetches all Pruefungen of a student for a specific lehrveranstaltung
|
||||
* if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database
|
||||
* if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database
|
||||
* @param mixed $lehrveranstaltung_id
|
||||
* @return void
|
||||
*/
|
||||
@@ -100,5 +109,46 @@ class Lehre extends FHCAPI_Controller
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fetches all assigned lehrveranstaltungen of a mitarbeiter for a given semester
|
||||
* @param mixed $uid
|
||||
* @param mixed $sem_kurzbz
|
||||
* @return void
|
||||
*/
|
||||
public function getZugewieseneLv() {
|
||||
$uid = $this->input->get("uid",TRUE);
|
||||
$sem_kurzbz = $this->input->get("sem_kurzbz",TRUE);
|
||||
|
||||
// TODO: error messages
|
||||
|
||||
if(!isset($sem_kurzbz) || isEmptyString($sem_kurzbz))
|
||||
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
|
||||
|
||||
if (!isset($uid) || isEmptyString($uid))
|
||||
$uid = getAuthUID();
|
||||
|
||||
// querying other ma_uids data requires admin permission
|
||||
if($uid !== getAuthUID()) {
|
||||
$this->load->library('PermissionLib');
|
||||
$isAdmin = $this->permissionlib->isBerechtigt('admin');
|
||||
if(!$isAdmin) $this->terminateWithError($this->p->t('ui', 'keineBerechtigung'), 'general');
|
||||
}
|
||||
|
||||
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
|
||||
$result = $this->LehrveranstaltungModel->getLvForLektorInSemester($sem_kurzbz, $uid);
|
||||
$data = $this->getDataOrTerminateWithError($result);
|
||||
$this->terminateWithSuccess($data);
|
||||
}
|
||||
|
||||
public function getLeForLv() {
|
||||
$lv_id = $this->input->get("lv_id",TRUE);
|
||||
$sem_kurzbz = $this->input->get("sem_kurzbz",TRUE);
|
||||
|
||||
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
|
||||
|
||||
// $this->terminateWithSuccess($this->LehreinheitModel->getLesForLv($lv_id, $sem_kurzbz));
|
||||
$this->terminateWithSuccess($this->LehreinheitModel->getAllLehreinheitenForLvaAndMaUid($lv_id, getAuthUID(), $sem_kurzbz));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,6 @@ class Noten extends FHCAPI_Controller
|
||||
'lehre',
|
||||
'ui'
|
||||
]);
|
||||
require_once(FHCPATH . 'include/mobilitaet.class.php');
|
||||
|
||||
$this->load->model('education/LePruefung_model', 'LePruefungModel');
|
||||
$this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel');
|
||||
@@ -72,45 +71,38 @@ class Noten extends FHCAPI_Controller
|
||||
public function getCisConfig() {
|
||||
$this->terminateWithSuccess(
|
||||
array(
|
||||
// TODO
|
||||
// Punkte bei der Noteneingabe anzeigen
|
||||
'CIS_GESAMTNOTE_PUNKTE' => CIS_GESAMTNOTE_PUNKTE,
|
||||
|
||||
// TODO
|
||||
// basically on/of toggle for the points/grade col and the arrow button
|
||||
// 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,
|
||||
|
||||
// only relevant in punkte calculation in backend
|
||||
// // Gewichtung der Lehreinheiten bei Noteneintragung true|false
|
||||
// 'CIS_GESAMTNOTE_GEWICHTUNG' => CIS_GESAMTNOTE_GEWICHTUNG,
|
||||
|
||||
// this one should always be set true since fh prüfungsordnung requires at least 3 antritte (t1+t2+kP)
|
||||
// Bei Gesamtnote eine zusaetzliche Spalte fuer den 2. Termin anzeigen
|
||||
// 'CIS_GESAMTNOTE_PRUEFUNG_TERMIN2' => CIS_GESAMTNOTE_PRUEFUNG_TERMIN2,
|
||||
|
||||
|
||||
// TODO
|
||||
// should in 99% of cases be kept true to enable 4 antritte in total, but if a certain
|
||||
// fh still works with 3 antritte per note this can limit the max number of pruefungen accordingly
|
||||
// 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,
|
||||
|
||||
// used to toggle availability of kommPruef type pruefungen
|
||||
// Bei Gesamtnote eine zusaetzliche Spalte fuer die kommissionelle Pruefung anlegen
|
||||
'CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF' => CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF,
|
||||
|
||||
|
||||
//technically exists but is never used
|
||||
// // Bei Gesamtnote die Spalte fuer die Quelle der Noten anzeigen (Moodle oder LE)
|
||||
//technically exists but is never used, could be LE pendant to next flag
|
||||
// 'CIS_GESAMTNOTE_PRUEFUNG_MOODLE_NOTE' => CIS_GESAMTNOTE_PRUEFUNG_MOODLE_NOTE,
|
||||
|
||||
|
||||
// basically a toggle for "use teilnoten" and the source is always moodle
|
||||
// Bei Gesamtnote die Spalte fuer die Quelle der Noten anzeigen (Moodle oder LE)
|
||||
// setting this to false breaks legacy tool and if that was fixed it wouldnt render any table at all
|
||||
// anyway so not sure why this even is a config at all. placebo at best
|
||||
|
||||
// TODO: do we really need this?
|
||||
'CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE' => CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE,
|
||||
// Gibt an ob die Note im Notenfreigabemail enthalten ist oder nicht
|
||||
|
||||
// send a mail when approving grades
|
||||
'CIS_GESAMTNOTE_FREIGABEMAIL_NOTE' => CIS_GESAMTNOTE_FREIGABEMAIL_NOTE
|
||||
)
|
||||
);
|
||||
@@ -263,7 +255,7 @@ class Noten extends FHCAPI_Controller
|
||||
// get all prüfungen with noten held in that semester in that lva
|
||||
$pruefungen = $this->LePruefungModel->getPruefungenByLvStudiensemester($lv_id, $sem_kurzbz);
|
||||
$pruefungenData = getData($pruefungen);
|
||||
|
||||
|
||||
$this->terminateWithSuccess(array($studentenData, $pruefungenData, DOMAIN, $grades, $anwresult));
|
||||
}
|
||||
|
||||
@@ -348,6 +340,9 @@ class Noten extends FHCAPI_Controller
|
||||
<td><b>" . $this->p->t('lehre','studiengang') . "</b></td>\n
|
||||
<td><b>" . $this->p->t('benotungstool','c4nachname') . "</b></td>\n
|
||||
<td><b>" . $this->p->t('benotungstool','c4vorname') . "</b></td>\n";
|
||||
if(defined(CIS_GESAMTNOTE_PUNKTE) && CIS_GESAMTNOTE_PUNKTE) {
|
||||
$studlist .= "<td><b>" . $this->p->t('benotungstool','c4punkte') . "</b></td>\n";
|
||||
}
|
||||
$studlist .= "<td><b>" . $this->p->t('benotungstool','c4grade') . "</b></td>\n";
|
||||
$studlist .= "<td><b>" . $this->p->t('ui','bearbeitetVon') . "</b></td></tr>\n";
|
||||
} else {
|
||||
@@ -391,7 +386,9 @@ class Noten extends FHCAPI_Controller
|
||||
$studlist .= "<td>" . trim($note->nachname) . "</td>";
|
||||
$studlist .= "<td>" . trim($note->vorname) . "</td>";
|
||||
|
||||
// TODO: if defined(CIS_PUNKTE) ...
|
||||
if(defined(CIS_GESAMTNOTE_PUNKTE) && CIS_GESAMTNOTE_PUNKTE) {
|
||||
$studlist .= "<td>" . trim($lvgesamtnote->punkte) . "</td>";
|
||||
}
|
||||
$studlist .= "<td>" .$note->noteBezeichnung. "</td>";
|
||||
|
||||
$studlist .= "<td>" . $lvgesamtnote->mitarbeiter_uid;
|
||||
@@ -407,13 +404,13 @@ class Noten extends FHCAPI_Controller
|
||||
$studlist .= "</table>";
|
||||
|
||||
// always send the mail, config toggles data contents
|
||||
$this->sendEmail($lektorFullName, $lvaFullName, count($result->noten), $emails, $studlist, $betreff);
|
||||
$this->sendFreigabeEmail($lektorFullName, $lvaFullName, count($result->noten), $emails, $studlist, $betreff);
|
||||
|
||||
$this->terminateWithSuccess($ret);
|
||||
}
|
||||
|
||||
|
||||
private function sendEmail($lektorFullName, $lvaFullName, $notenCount, $emailAdressen, $studlist, $betreff)
|
||||
private function sendFreigabeEmail($lektorFullName, $lvaFullName, $notenCount, $emailAdressen, $studlist, $betreff)
|
||||
{
|
||||
$emailAdressen[] = getAuthUID() . "@" . DOMAIN; // also send mail to lektors own adress
|
||||
$adressen = implode(";", $emailAdressen);
|
||||
@@ -984,7 +981,7 @@ class Noten extends FHCAPI_Controller
|
||||
$student_uid = $student->uid;
|
||||
$typ = $student->typ;
|
||||
$note = 9; //$result->note; // TODO: parameterize for import maybe
|
||||
$punkte = ''; // new pruefungen never have punkte, TODO: check if null or '' prefered
|
||||
$punkte = null; // new pruefungen never have punkte,
|
||||
|
||||
$lehreinheit_id = $student->lehreinheit_id;
|
||||
$ret[$student->uid] = $this->savePruefungstermin($typ, $student_uid, $lva_id, $stsem, $lehreinheit_id, $note, $punkte, $datum);
|
||||
@@ -1017,7 +1014,7 @@ class Noten extends FHCAPI_Controller
|
||||
$typ = $pruefung->typ;
|
||||
$note = $pruefung->note; // TODO: parameterize for import maybe
|
||||
$datum = $pruefung->datum;
|
||||
$punkte = ''; // TODO: check punkte feature
|
||||
$punkte = null;
|
||||
|
||||
$lehreinheit_id = $pruefung->lehreinheit_id;
|
||||
$ret[$student_uid] = $this->savePruefungstermin($typ, $student_uid, $lv_id, $sem_kurzbz, $lehreinheit_id, $note, $punkte, $datum);
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
}
|
||||
|
||||
|
||||
/* styling for points input column for notenvorschläge in benotungstool*/
|
||||
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="punkte"] {
|
||||
position: relative;
|
||||
background-color: rgba(255, 255, 157, 0.73);
|
||||
}
|
||||
|
||||
/* styling for editable dropdown column of notenvorschläge in benotungstool*/
|
||||
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="note_vorschlag"] {
|
||||
position: relative;
|
||||
|
||||
@@ -119,18 +119,9 @@ export const Benotungstool = {
|
||||
|
||||
const row = cell.getRow()
|
||||
row.reformat() // trigger reformat of arrow
|
||||
} else if (field === 'punkte') {
|
||||
const newValue = cell.getValue();
|
||||
if(newValue == '' || newValue == null) return
|
||||
this.$api.call(ApiNoten.getNoteByPunkte(newValue, this.lv_id, this.sem_kurzbz)).then(res => {
|
||||
if(res?.meta?.status === 'success' && res.data >= 0) {
|
||||
const row = cell.getRow();
|
||||
row.update({note_vorschlag: res.data})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "cellClick",
|
||||
handler: async (e, cell) => {
|
||||
@@ -139,18 +130,68 @@ export const Benotungstool = {
|
||||
if(field == "mobility_zusatz") {
|
||||
this.$refs.drawer.show()
|
||||
e.stopPropagation()
|
||||
this.undoSelection(cell)
|
||||
} else if (field == "punkte" || field == "note_vorschlag" || field == "übernehmen") {
|
||||
this.undoSelection(cell)
|
||||
}
|
||||
}
|
||||
}
|
||||
]};
|
||||
},
|
||||
methods: {
|
||||
undoSelection(cell) {
|
||||
// checks if cells row is selected and unselects -> imitates columns which dont trigger row selection
|
||||
// but actually just revert it after the fact
|
||||
|
||||
const row = cell.getRow()
|
||||
if(row.isSelected()) {
|
||||
row.deselect();
|
||||
}
|
||||
},
|
||||
// using this to expose input event of editor element properly, tabulator makes it hard to access on default editor
|
||||
// implemented after tabulator/src/js/modules/edit/defaults/editors/number.js
|
||||
liveNumberEditor(cell, onRendered, success, cancel) {
|
||||
const editor = document.createElement("input");
|
||||
editor.setAttribute("type", "number");
|
||||
editor.value = cell.getValue();
|
||||
|
||||
const row = cell.getRow()
|
||||
const rowData = row.getData()
|
||||
|
||||
rowData._debouncedFetchNoteForPunkte = debounce(this.fetchNoteForPunkte, 500)
|
||||
editor.addEventListener("input", (e) => {
|
||||
rowData._debouncedFetchNoteForPunkte(e.target.value, row)
|
||||
});
|
||||
|
||||
onRendered(() => {
|
||||
editor.focus();
|
||||
editor.style.height = "100%";
|
||||
});
|
||||
|
||||
editor.addEventListener("change", () => success(editor.value));
|
||||
editor.addEventListener("blur", () => success(editor.value));
|
||||
editor.addEventListener("keydown", (e) => {
|
||||
if (e.keyCode === 13) success(editor.value);
|
||||
if (e.keyCode === 27) cancel();
|
||||
});
|
||||
|
||||
return editor;
|
||||
},
|
||||
fetchNoteForPunkte(valueParam, row) {
|
||||
const value = valueParam == '' ? null : valueParam
|
||||
this.$api.call(ApiNoten.getNoteByPunkte(value, this.lv_id, this.sem_kurzbz)).then(res => {
|
||||
if(res?.meta?.status === 'success' && res.data >= 0) {
|
||||
row.update({note_vorschlag: res.data})
|
||||
}
|
||||
})
|
||||
},
|
||||
fetchNoteForPunktePruefung(event) {
|
||||
this.$api.call(ApiNoten.getNoteByPunkte(event.value, this.lv_id, this.sem_kurzbz)).then(res => {
|
||||
const value = event.value == '' ? null : event.value
|
||||
this.$api.call(ApiNoten.getNoteByPunkte(value, this.lv_id, this.sem_kurzbz)).then(res => {
|
||||
if(res?.meta?.status === 'success' && res.data >= 0) {
|
||||
this.selectedPruefungNote = this.notenOptions.find(n => n.note == res.data)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
isValidDate_ddmmyyyy(str) {
|
||||
if (typeof str !== 'string') return false;
|
||||
@@ -219,12 +260,15 @@ 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
|
||||
|
||||
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 === uid)
|
||||
student = this.studenten.find(s => s.uid?.trim() === idTrimmed)
|
||||
}
|
||||
if(!student) {
|
||||
this.$fhcAlert.alertWarning('Kein Student gefunden für ID ' + rowParts[0] + ' in Zeile Nr. ' + rowNum + ' Die Zeile wurde übersprungen.')
|
||||
@@ -471,6 +515,8 @@ export const Benotungstool = {
|
||||
},
|
||||
getNotenTableOptions() {
|
||||
return {
|
||||
// debugEventsExternal:true,
|
||||
// debugEventsInternal:true,
|
||||
height: 700,
|
||||
virtualDom: false,
|
||||
index: 'uid',
|
||||
@@ -489,152 +535,157 @@ export const Benotungstool = {
|
||||
},
|
||||
rowHeight: 40,
|
||||
rowFormatter: this.fixTabulatorSelectionFormatter,
|
||||
columns: [
|
||||
{
|
||||
formatter: function (cell, formatterParams, onRendered) {
|
||||
// create the built-in checkbox
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
|
||||
// Handle select manually
|
||||
checkbox.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
// call our function
|
||||
if (formatterParams && formatterParams.handleClick) {
|
||||
formatterParams.handleClick(e, cell);
|
||||
}
|
||||
});
|
||||
|
||||
return checkbox;
|
||||
},
|
||||
titleFormatter: function (cell, formatterParams, onRendered) {
|
||||
// create the built-in checkbox
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
|
||||
// Handle "select all" manually
|
||||
checkbox.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
// call our function
|
||||
if (formatterParams && formatterParams.handleClick) {
|
||||
formatterParams.handleClick(e, cell);
|
||||
}
|
||||
});
|
||||
|
||||
return checkbox;
|
||||
},
|
||||
hozAlign: "center",
|
||||
headerSort: false,
|
||||
formatterParams: {
|
||||
handleClick: this.selectHandler
|
||||
},
|
||||
titleFormatterParams: {
|
||||
handleClick: this.selectAllHandler
|
||||
},
|
||||
width: 50,
|
||||
cssClass: 'sticky-col'
|
||||
},
|
||||
{title: 'UID', field: 'uid', tooltip: false, widthGrow: 1, topCalc: this.sumCalcFunc, cssClass: 'sticky-col'},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4mail'))), field: 'email', formatter: this.mailFormatter, tooltip: false, visible: false, widthGrow: 1, variableHeight: true},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4antrittCountv2'))), field: 'hoechsterAntritt', tooltip: false, widthGrow: 1},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4vorname'))), field: 'vorname', headerFilter: true, tooltip: false, widthGrow: 1},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4nachname'))), field: 'nachname', headerFilter: true, widthGrow: 1},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4anwesenheitsquote'))), field: 'anwquote', widthGrow: 1, formatter: this.percentFormatter},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4mobility'))), field: 'mobility_zusatz', headerFilter: true, widthGrow: 1, visible: false},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4teilnoten'))), field: 'teilnote', widthGrow: 1, formatter: this.teilnotenFormatter, variableHeight: true},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4punkte'))), field: 'punkte', widthGrow: 1,
|
||||
editor: 'number',
|
||||
editorParams: (cell) => {
|
||||
return {
|
||||
min: 0,
|
||||
max: 9999,
|
||||
step: 1,
|
||||
elementAttributes: {
|
||||
maxlength: "4"
|
||||
},
|
||||
selectContents: true,
|
||||
verticalNavigation: "table"
|
||||
}
|
||||
},
|
||||
variableHeight: true
|
||||
},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4notenvorschlag'))), field: 'note_vorschlag',
|
||||
editor: 'list',
|
||||
editorParams: (cell) => {
|
||||
// write original cell value into row to it can be retrieved if edit is cancelled without selection
|
||||
const rowData = cell.getRow().getData();
|
||||
rowData._originalNoteVorschlag = cell.getValue();
|
||||
|
||||
return {
|
||||
values: this.notenOptionsLehre.map(opt => ({
|
||||
label: opt.bezeichnung,
|
||||
value: opt.note
|
||||
}))
|
||||
};
|
||||
},
|
||||
editable: (cell) => {
|
||||
// TODO: css style this a bit
|
||||
// punkte features enables mapping but unable to set note directly
|
||||
if(this.config?.CIS_GESAMTNOTE_PUNKTE) return false
|
||||
const rowData = cell.getRow().getData();
|
||||
const noteOption = this.notenOptions.find(opt => opt.note == rowData.note)
|
||||
if(!noteOption) return true
|
||||
|
||||
// also if student has any pruefungsnote disable noten selection
|
||||
if(this.pruefungen?.find(p => p.student_uid == rowData.uid)) return false
|
||||
|
||||
return noteOption.lkt_ueberschreibbar
|
||||
},
|
||||
formatter: (cell) => {
|
||||
const rowData = cell.getRow().getData();
|
||||
const value = cell.getValue()
|
||||
const match = this.notenOptions?.find(opt => opt.note == value)
|
||||
const val = match ? match.bezeichnung : value
|
||||
const p = this.pruefungen?.find(p => p.student_uid == rowData.uid)
|
||||
let style = ''
|
||||
|
||||
if(val === undefined) return ''
|
||||
if(p || !match?.lkt_ueberschreibbar) style = 'color: gray;font-style: italic; background-color: #f0f0f0;pointer-events: none;opacity: 0.6;user-select: none;cursor: not-allowed;'
|
||||
return '<div style="'+style+'">' + val + '</div>'
|
||||
},
|
||||
widthGrow: 1
|
||||
},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4vorschlag_übernehmen'))), width: 50, hozAlign: 'center', formatter: this.arrowFormatter, cellClick: this.saveNote, variableHeight: true},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4lvnote'))), field: 'lv_note',
|
||||
formatter: this.notenFormatter,
|
||||
headerFilter: 'list',
|
||||
headerFilterParams: () => {
|
||||
return { values: ["\u00A0",this.$p.t('benotungstool/c4noteEmpty') ,this.$p.t('benotungstool/c4positiv'), this.$p.t('benotungstool/c4negativ') ,...this.notenOptions.map(opt => opt.bezeichnung)] }
|
||||
},
|
||||
headerFilterFunc: this.notenFilterFunc,
|
||||
widthGrow: 1
|
||||
},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4freigabe'))), field: 'freigegeben', widthGrow: 1, formatter: this.freigabeFormatter, variableHeight: true},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4zeugnisnote'))),
|
||||
field: 'note',
|
||||
formatter: this.notenFormatter,
|
||||
topCalc: this.negativeNotenCalc,
|
||||
topCalcFormatter: this.negativeNotenCalcFormatter,
|
||||
headerFilter: 'list',
|
||||
headerFilterParams: () => {
|
||||
return { values: ["\u00A0", this.$p.t('benotungstool/c4noteEmpty'),this.$p.t('benotungstool/c4positiv'), this.$p.t('benotungstool/c4negativ') ,...this.notenOptions.map(opt => opt.bezeichnung)] }
|
||||
},
|
||||
headerFilterFunc: this.notenFilterFunc,
|
||||
widthGrow: 1
|
||||
},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4kommPruef'))),
|
||||
field: 'kommPruef', widthGrow: 1,
|
||||
formatter: this.pruefungFormatter,
|
||||
topCalc: this.terminCalcFunc,
|
||||
topCalcFormatter: this.terminCalcFormatter,
|
||||
hozAlign:"center", minWidth: 150, visible: false
|
||||
}
|
||||
],
|
||||
columns: this.getColumnsDefinition(),
|
||||
persistence: false,
|
||||
}
|
||||
},
|
||||
getColumnsDefinition() {
|
||||
const columns = []
|
||||
|
||||
|
||||
columns.push({
|
||||
formatter: function (cell, formatterParams, onRendered) {
|
||||
// create the built-in checkbox
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
|
||||
// Handle select manually
|
||||
checkbox.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
// call our function
|
||||
if (formatterParams && formatterParams.handleClick) {
|
||||
formatterParams.handleClick(e, cell);
|
||||
}
|
||||
});
|
||||
|
||||
return checkbox;
|
||||
},
|
||||
titleFormatter: function (cell, formatterParams, onRendered) {
|
||||
// create the built-in checkbox
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
|
||||
// Handle "select all" manually
|
||||
checkbox.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
// call our function
|
||||
if (formatterParams && formatterParams.handleClick) {
|
||||
formatterParams.handleClick(e, cell);
|
||||
}
|
||||
});
|
||||
|
||||
return checkbox;
|
||||
},
|
||||
hozAlign: "center",
|
||||
headerSort: false,
|
||||
formatterParams: {
|
||||
handleClick: this.selectHandler
|
||||
},
|
||||
titleFormatterParams: {
|
||||
handleClick: this.selectAllHandler
|
||||
},
|
||||
width: 50,
|
||||
cssClass: 'sticky-col'
|
||||
})
|
||||
columns.push({title: 'UID', field: 'uid', tooltip: false, widthGrow: 1, topCalc: this.sumCalcFunc, cssClass: 'sticky-col'})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4mail'))), field: 'email', formatter: this.mailFormatter, tooltip: false, visible: false, widthGrow: 1, variableHeight: true})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4antrittCountv2'))), field: 'hoechsterAntritt', tooltip: false, widthGrow: 1})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4vorname'))), field: 'vorname', headerFilter: true, tooltip: false, widthGrow: 1})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4nachname'))), field: 'nachname', headerFilter: true, widthGrow: 1})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4anwesenheitsquote'))), field: 'anwquote', widthGrow: 1, formatter: this.percentFormatter})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4mobility'))), field: 'mobility_zusatz', headerFilter: true, widthGrow: 1, visible: false})
|
||||
if(this.config?.CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE) {
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4teilnoten'))), field: 'teilnote', widthGrow: 1, formatter: this.teilnotenFormatter, variableHeight: true})
|
||||
}
|
||||
if(this.config?.CIS_GESAMTNOTE_PUNKTE) {
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4punkte'))), field: 'punkte', widthGrow: 1,
|
||||
editor: this.liveNumberEditor,
|
||||
editable: (cell) => {
|
||||
const rowData = cell.getRow().getData();
|
||||
if(this.pruefungen?.find(p => p.student_uid == rowData.uid)) return false
|
||||
|
||||
return true
|
||||
},
|
||||
variableHeight: true
|
||||
})
|
||||
}
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4notenvorschlag'))), field: 'note_vorschlag',
|
||||
editor: 'list',
|
||||
editorParams: (cell) => {
|
||||
// write original cell value into row to it can be retrieved if edit is cancelled without selection
|
||||
const rowData = cell.getRow().getData();
|
||||
rowData._originalNoteVorschlag = cell.getValue();
|
||||
|
||||
return {
|
||||
values: this.notenOptionsLehre.map(opt => ({
|
||||
label: opt.bezeichnung,
|
||||
value: opt.note
|
||||
}))
|
||||
};
|
||||
},
|
||||
editable: (cell) => {
|
||||
// punkte features enables mapping but unable to set note directly
|
||||
if(this.config?.CIS_GESAMTNOTE_PUNKTE) return false
|
||||
const rowData = cell.getRow().getData();
|
||||
const noteOption = this.notenOptions.find(opt => opt.note == rowData.note)
|
||||
if(!noteOption) return true
|
||||
|
||||
// also if student has any pruefungsnote disable noten selection
|
||||
if(this.pruefungen?.find(p => p.student_uid == rowData.uid)) return false
|
||||
|
||||
return noteOption.lkt_ueberschreibbar
|
||||
},
|
||||
formatter: (cell) => {
|
||||
const rowData = cell.getRow().getData();
|
||||
const value = cell.getValue()
|
||||
const match = this.notenOptions?.find(opt => opt.note == value)
|
||||
const val = match ? match.bezeichnung : value
|
||||
const p = this.pruefungen?.find(p => p.student_uid == rowData.uid)
|
||||
let style = ''
|
||||
|
||||
if(val === undefined) return ''
|
||||
if(p || !match?.lkt_ueberschreibbar) style = 'color: gray;font-style: italic; background-color: #f0f0f0;pointer-events: none;opacity: 0.6;user-select: none;cursor: not-allowed;'
|
||||
return '<div style="'+style+'">' + val + '</div>'
|
||||
},
|
||||
widthGrow: 1
|
||||
})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4vorschlag_übernehmen'))), field: 'übernehmen', width: 150, hozAlign: 'center', formatter: this.arrowFormatter,
|
||||
// cellClick: this.saveNote,
|
||||
variableHeight: true})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4lvnote'))), field: 'lv_note',
|
||||
formatter: this.notenFormatter,
|
||||
headerFilter: 'list',
|
||||
headerFilterParams: () => {
|
||||
return { values: ["\u00A0",this.$p.t('benotungstool/c4noteEmpty') ,this.$p.t('benotungstool/c4positiv'), this.$p.t('benotungstool/c4negativ') ,...this.notenOptions.map(opt => opt.bezeichnung)] }
|
||||
},
|
||||
headerFilterFunc: this.notenFilterFunc,
|
||||
widthGrow: 1
|
||||
})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4freigabe'))), field: 'freigegeben', widthGrow: 1, formatter: this.freigabeFormatter, variableHeight: true})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4zeugnisnote'))),
|
||||
field: 'note',
|
||||
formatter: this.notenFormatter,
|
||||
topCalc: this.negativeNotenCalc,
|
||||
topCalcFormatter: this.negativeNotenCalcFormatter,
|
||||
headerFilter: 'list',
|
||||
headerFilterParams: () => {
|
||||
return { values: ["\u00A0", this.$p.t('benotungstool/c4noteEmpty'),this.$p.t('benotungstool/c4positiv'), this.$p.t('benotungstool/c4negativ') ,...this.notenOptions.map(opt => opt.bezeichnung)] }
|
||||
},
|
||||
headerFilterFunc: this.notenFilterFunc,
|
||||
widthGrow: 1
|
||||
})
|
||||
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4kommPruef'))),
|
||||
field: 'kommPruef', widthGrow: 1,
|
||||
formatter: this.pruefungFormatter,
|
||||
topCalc: this.terminCalcFunc,
|
||||
topCalcFormatter: this.terminCalcFormatter,
|
||||
hozAlign:"center", minWidth: 150, visible: false
|
||||
})
|
||||
|
||||
return columns
|
||||
},
|
||||
selectHandler(e, cell) {
|
||||
const row = cell.getRow();
|
||||
|
||||
@@ -789,6 +840,10 @@ export const Benotungstool = {
|
||||
return value
|
||||
},
|
||||
saveNote(e, cell) { // Notenvorschlag freigeben
|
||||
// TODO: rename & rework so it handles with a button
|
||||
|
||||
// TODO: save this method once we decide the arrow was actually very cool
|
||||
|
||||
const row = cell.getRow()
|
||||
const data = row.getData()
|
||||
|
||||
@@ -812,9 +867,6 @@ export const Benotungstool = {
|
||||
}).finally(()=>this.loading = false)
|
||||
|
||||
|
||||
},
|
||||
punkteFormatter(cell) {
|
||||
|
||||
},
|
||||
teilnotenFormatter(cell) {
|
||||
const val = cell.getValue()
|
||||
@@ -843,7 +895,6 @@ export const Benotungstool = {
|
||||
const canAdd = field !== 'kommPruef' && data.hoechsterAntritt < 4 && !colDef.originalNote
|
||||
|
||||
// TODO: check for some time limit maybe? old pruefungen can be changed/created
|
||||
// TODO: it also looks ugly and unprofessional, should at some peoplt disable/hide the change action
|
||||
|
||||
// Create root row div
|
||||
const rowDiv = document.createElement('div');
|
||||
@@ -892,6 +943,7 @@ export const Benotungstool = {
|
||||
}
|
||||
|
||||
if(data[field]) {
|
||||
|
||||
// showing date in
|
||||
|
||||
// const dateParts = data[field].datum.split('-')
|
||||
@@ -912,16 +964,18 @@ export const Benotungstool = {
|
||||
return rowDiv
|
||||
}
|
||||
|
||||
// Third column (button)
|
||||
const button = document.createElement('button');
|
||||
button.className = 'btn btn-outline-secondary';
|
||||
button.textContent = this.$capitalize(this.$p.t('benotungstool/changePruefungButtonText'));
|
||||
button.addEventListener('click', () => {
|
||||
this.openPruefungModal(data, data[field], field);
|
||||
});
|
||||
|
||||
rowDiv.appendChild(createCol(button, 'col-4 d-flex justify-content-center align-items-center'));
|
||||
if(data[field]?.pruefungstyp_kurzbz !== 'Termin1') {
|
||||
// Third column (button)
|
||||
const button = document.createElement('button');
|
||||
button.className = 'btn btn-outline-secondary';
|
||||
button.textContent = this.$capitalize(this.$p.t('benotungstool/changePruefungButtonText'));
|
||||
button.addEventListener('click', () => {
|
||||
this.openPruefungModal(data, data[field], field);
|
||||
});
|
||||
|
||||
rowDiv.appendChild(createCol(button, 'col-4 d-flex justify-content-center align-items-center'));
|
||||
}
|
||||
|
||||
return rowDiv;
|
||||
|
||||
} else if (canAdd) { // return new btn action
|
||||
@@ -984,14 +1038,27 @@ export const Benotungstool = {
|
||||
|
||||
let style = 'display: flex; justify-content: center; align-items: center; height: 100%;'
|
||||
|
||||
if(!data.note_vorschlag || (data.note_vorschlag == data.lv_note)) { // uncolored arrow
|
||||
return '<div style="'+style+'">' +
|
||||
'<i class="fa fa-arrow-right"></i></div>'
|
||||
if(!data.note_vorschlag || (data.note_vorschlag == data.lv_note)) {
|
||||
// arrow to ambiguous in meaning, use str8 forward worded button here instead
|
||||
// uncolored arrow
|
||||
// return '<div style="'+style+'">' +
|
||||
// '<i class="fa fa-arrow-right"></i></div>'
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
// can save a notenvorschlag -> colored
|
||||
return '<div style="'+style+'">' +
|
||||
'<i class="fa fa-arrow-right fa-2xl" style="color:#00649C"></i></div>'
|
||||
const button = document.createElement('button');
|
||||
button.className = 'btn btn-outline-secondary';
|
||||
button.textContent = this.$capitalize(this.$p.t('benotungstool/c4notenvorschlagUebernehmen'));
|
||||
button.addEventListener('click', () => {
|
||||
this.saveNote(data)
|
||||
console.log('button click')
|
||||
});
|
||||
return button;
|
||||
|
||||
// // can save a notenvorschlag -> colored
|
||||
// return '<div style="'+style+'">' +
|
||||
// '<i class="fa fa-arrow-right fa-2xl" style="color:#00649C"></i></div>'
|
||||
},
|
||||
mailFormatter(cell) {
|
||||
const val = cell.getValue()
|
||||
@@ -1676,10 +1743,10 @@ export const Benotungstool = {
|
||||
return cs
|
||||
},
|
||||
getNotenfreigabeHinweistext() {
|
||||
return this.$capitalize(this.$p.t('benotungstool/notenfreigabeHinweistextv3'))
|
||||
return this.$capitalize(this.$p.t('benotungstool/notenfreigabeHinweistextv4'))
|
||||
},
|
||||
getNotenimportHinweistext() {
|
||||
return this.$capitalize(this.$p.t('benotungstool/notenimportHinweistextv3'))
|
||||
return this.$capitalize(this.$p.t('benotungstool/notenimportHinweistextv5'))
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
+21
-33
@@ -54320,26 +54320,24 @@ and represent the current state of research on the topic. The prescribed citatio
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'benotungstool',
|
||||
'phrase' => 'notenfreigabeHinweistextv3',
|
||||
'phrase' => 'notenfreigabeHinweistextv4',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Notenfreigabe
|
||||
<br><br>
|
||||
'text' => '
|
||||
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>
|
||||
<br>
|
||||
Aus Gründen der erhöhten Sicherheit ist bei der Freigabe der Noten die Eingabe Ihres Passwortes erforderlich.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Grade Release
|
||||
<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)',
|
||||
'text' => '
|
||||
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 "Confirm Grades"
|
||||
<br>
|
||||
For increased security reasons, you will be required to enter your password when releasing grades.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
@@ -54348,38 +54346,28 @@ and represent the current state of research on the topic. The prescribed citatio
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'benotungstool',
|
||||
'phrase' => 'notenimportHinweistextv3',
|
||||
'phrase' => 'notenimportHinweistextv5',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Laden Sie sich die Notenliste im Excel-Format unter CIS → Lehrveranstaltungen → Anwesenheits- und Notenlisten → Notenliste herunter.
|
||||
<br><br>
|
||||
Tragen Sie die Noten in das Dokument und speichern Sie dieses.
|
||||
<br><br>
|
||||
Markieren Sie im Excel-Dokument die Inhalte der Spalten Personenkennzeichen und Note für jene Studierende, deren Noten Sie importieren möchten (ohne Überschrift !)
|
||||
<br><br>
|
||||
Kopieren Sie die markierten Inhalte mittels strg + c oder Bearbeiten → Kopieren in die Zwischenablage
|
||||
<br><br>
|
||||
Einfügen der Inhalte mittels strg + v oder Bearbeiten → Einfügen
|
||||
<br><br>
|
||||
Mit einem Klick auf Import werden die Noten übernommen.',
|
||||
'text' => '• Laden Sie sich die Notenliste im Excel-Format unter CIS → Lehrveranstaltungen → Anwesenheits- und Notenlisten → Notenliste herunter.<br>
|
||||
• Tragen Sie die Noten in das Dokument und speichern Sie dieses.<br>
|
||||
• Markieren Sie im Excel-Dokument die Inhalte der Spalten Personenkennzeichen und Note für jene Studierende, deren Noten Sie importieren möchten (ohne Überschrift !)<br>
|
||||
• Kopieren Sie die markierten Inhalte mittels strg + c oder Bearbeiten → Kopieren in die Zwischenablage<br>
|
||||
• Einfügen der Inhalte mittels strg + v oder Bearbeiten → Einfügen<br>
|
||||
• Mit einem Klick auf Import werden die Noten übernommen.<br>',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Download the grade list in Excel format from CIS → Courses → Attendance and Grade Lists → Grade List.
|
||||
<br><br>
|
||||
Enter the grades into the document and save it.
|
||||
<br><br>
|
||||
In the Excel document, select the contents of the Person ID and Grade columns for the students whose grades you want to import (without headings!).
|
||||
<br><br>
|
||||
Copy the selected content to the clipboard using Ctrl + c or Edit → Copy.
|
||||
<br><br>
|
||||
Paste the content using Ctrl + v or Edit → Paste.
|
||||
<br><br>
|
||||
Click Import to import the grades.',
|
||||
'text' => '• Download the grade list in Excel format from CIS → Courses → Attendance and Grade Lists → Grade List.<br>
|
||||
• Enter the grades into the document and save it.<br>
|
||||
• In the Excel document, select the contents of the Person ID and Grade columns for the students whose grades you want to import (without headings!).<br>
|
||||
• Copy the selected content to the clipboard using Ctrl + c or Edit → Copy.<br>
|
||||
• Paste the content using Ctrl + v or Edit → Paste.<br>
|
||||
• Click Import to import the grades.<br>',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
@@ -54468,7 +54456,7 @@ and represent the current state of research on the topic. The prescribed citatio
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'benotungstool',
|
||||
'phrase' => 'c4vorschlag_übernehmen',
|
||||
'phrase' => 'c4notenvorschlagUebernehmen',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
|
||||
Reference in New Issue
Block a user