diff --git a/application/config/abgabe.php b/application/config/abgabe.php new file mode 100644 index 000000000..e2092a367 --- /dev/null +++ b/application/config/abgabe.php @@ -0,0 +1,9 @@ + self::PERM_LOGGED, 'getStudentProjektarbeiten' => self::PERM_LOGGED, // TODO: abgabetool berechtigung? 'getStudentProjektabgaben' => self::PERM_LOGGED, 'postStudentProjektarbeitZwischenabgabe' => self::PERM_LOGGED, @@ -70,6 +71,22 @@ class Abgabe extends FHCAPI_Controller //------------------------------------------------------------------------------------------------------------------ // Public methods + /** + * loads config related to abgabetool, found in application/config/abgabe + */ + public function getConfig() { + $this->load->config('abgabe'); + $old_abgabe_beurteilung_link =$this->config->item('old_abgabe_beurteilung_link'); + $turnitin_link =$this->config->item('turnitin_link'); + + $ret = array( + 'old_abgabe_beurteilung_link' => $old_abgabe_beurteilung_link, + 'turnitin_link' => $turnitin_link + ); + + $this->terminateWithSuccess($ret); + } + /** * fetches all projektabgabetermine for a given projektarbeit_id used in cis4 student abgabetool */ @@ -116,9 +133,19 @@ class Abgabe extends FHCAPI_Controller $isMitarbeiter = $this->MitarbeiterModel->isMitarbeiter(getAuthUID()); if ($isMitarbeiter && $isZugeteilterBetreuer){ - $projektarbeiten = $this->ProjektarbeitModel->getStudentProjektarbeitenWithBetreuer($uid); + $result = $this->ProjektarbeitModel->getStudentProjektarbeitenWithBetreuer($uid); } else { - $projektarbeiten = $this->ProjektarbeitModel->getStudentProjektarbeitenWithBetreuer(getAuthUID()); + $result = $this->ProjektarbeitModel->getStudentProjektarbeitenWithBetreuer(getAuthUID()); + } + + $projektarbeiten = getData($result); + + // TODO: save access to this, array could be empty + foreach($projektarbeiten as $pa) { + $result = $this->ProjektarbeitModel->getProjektbetreuerEmail($pa->projektarbeit_id); + + // TODO: save access + $pa->email = getData($result)[0]->private_email; } $this->terminateWithSuccess(array($projektarbeiten, DOMAIN, $uid)); @@ -327,12 +354,8 @@ class Abgabe extends FHCAPI_Controller $maildata['parbeituebersichtlink'] = "

Zur Projektarbeitsübersicht

"; $maildata['bewertunglink'] = $num_rows_sem >= 1 && $paabgabetyp_kurzbz == 'end' ? "

Zur Beurteilung der Arbeit

" : ""; $maildata['token'] = ""; - - // TODO: clarify if all betreuer are mitarbeiter and have benutzer entries - // or if uid = null has to be checked WITH - // 'CASE WHEN tbl_benutzer.uid IS NULL THEN kontakt ELSE tbl_benutzer.uid || '@".DOMAIN."' END AS email' - $email = $this->getBetreuerEmail($bperson_id); + $email = $this->getProjektbetreuerEmail($projektarbeit_id); if(!$email) $this->terminateWithError($this->p->t('abgabetool', 'fehlerMailBegutachter'), 'general'); @@ -401,12 +424,7 @@ class Abgabe extends FHCAPI_Controller $zweitbetmaildata['parbeituebersichtlink'] = $intern ? $maildata['parbeituebersichtlink'] : ""; $zweitbetmaildata['bewertunglink'] = $num_rows_sem >= 1 ? "

Zur Beurteilung der Arbeit

" : ""; $zweitbetmaildata['token'] = $num_rows_sem >= 1 && isset($begutachterMitToken->zugangstoken) && !$intern ? "

Zugangstoken: " . $begutachterMitToken->zugangstoken . "

" : ""; - - - $email = $this->getBetreuerEmail($bperson_id); - - if(!$email) $this->terminateWithError($this->p->t('abgabetool', 'fehlerMailBegutachter'), 'general'); - + $mailres = sendSanchoMail( 'ParbeitsbeurteilungEndupload', @@ -464,12 +482,13 @@ class Abgabe extends FHCAPI_Controller $fixtermin = $_POST['fixtermin']; $kurzbz = $_POST['kurzbz']; $note = $_POST['note']; - $upload_required = $_POST['upload_required']; + $upload_allowed = $_POST['upload_allowed']; + $betreuer_person_id = $_POST['betreuer_person_id']; if (!isset($projektarbeit_id) || isEmptyString($projektarbeit_id) || !isset($paabgabe_id) || isEmptyString($paabgabe_id) || !isset($datum) || isEmptyString($datum) - || !isset($datum) || isEmptyString($datum) + || !isset($kurzbz) || !isset($paabgabetyp_kurzbz) || isEmptyString($paabgabetyp_kurzbz)) $this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general'); @@ -484,7 +503,7 @@ class Abgabe extends FHCAPI_Controller 'datum' => $datum, 'kurzbz' => $kurzbz, 'note' => $note, - 'upload_required' => $upload_required, + 'upload_allowed' => $upload_allowed, 'insertvon' => getAuthUID(), 'insertamum' => date('Y-m-d H:i:s') ) @@ -499,24 +518,37 @@ class Abgabe extends FHCAPI_Controller 'datum' => $datum, 'kurzbz' => $kurzbz, 'note' => $note, - 'upload_required' => $upload_required, + 'upload_allowed' => $upload_allowed, 'updatevon' => getAuthUID(), 'updateamum' => date('Y-m-d H:i:s') ) ); } - - $paabgabe_id = $this->getDataOrTerminateWithError($result); - - $paabgabe = $this->PaabgabeModel->load($paabgabe_id); // check if $paaabgabe is a qual gate and its note is deemed negative // -> send email to student with that info + $paabgabe_id = $this->getDataOrTerminateWithError($result); - // TODO: DEFAULT NOTE 9 OR DEFAULT NOTE NULL? 9 COUNTS AS NEGATIV :/ + $result = $this->PaabgabeModel->load($paabgabe_id); + $paabgabeArr = $this->getDataOrTerminateWithError($result); + $paabgabe = $paabgabeArr[0]; + $this->addMeta('paabgabe', $paabgabeArr); + + // check if abgabe even has note + if($paabgabe->note) { + $this->load->model('education/Note_model', 'NoteModel'); + $result = $this->NoteModel->load($paabgabe->note); + $noteArr = $this->getDataOrTerminateWithError($result); + $note = $noteArr[0]; + if($note->positiv === false) { + $this->addMeta('noteNegativ', true); + + $this->sendQualGateNegativEmail($projektarbeit_id, $betreuer_person_id, $paabgabe); + } + } - $this->terminateWithSuccess($result); + $this->terminateWithSuccess($paabgabe); } public function deleteProjektarbeitAbgabe() { @@ -556,7 +588,7 @@ class Abgabe extends FHCAPI_Controller if (!isset($projektarbeit_ids) || !is_array($projektarbeit_ids) || empty($projektarbeit_ids) || !isset($datum) || isEmptyString($datum) - || !isset($kurzbz) || isEmptyString($kurzbz) + || !isset($kurzbz) || !isset($bezeichnung) || isEmptyString($bezeichnung) || !isset($paabgabetyp_kurzbz) || isEmptyString($paabgabetyp_kurzbz)) $this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general'); @@ -664,11 +696,11 @@ class Abgabe extends FHCAPI_Controller $this->terminateWithSuccess($paabgabetypen); } - private function getBetreuerEmail($person_id) { - $this->load->model('education/Projektbetreuer_model', 'ProjektbetreuerModel'); - $result = $this->ProjektbetreuerModel->getBetreuerEmail($person_id); + private function getProjektbetreuerEmail($projektarbeit_id) { + $this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel'); + $result = $this->ProjektarbeitModel->getProjektbetreuerEmail($projektarbeit_id); $email = $this->getDataOrTerminateWithError($result); - return $email[0]->email; + return $email[0]->private_email; } //TODO: SWITCH TO NOTEN API ONCE NOTENTOOL IS IN MASTER TO AVOID DUPLICATE API @@ -685,7 +717,24 @@ class Abgabe extends FHCAPI_Controller $this->terminateWithSuccess($noten); } - private function sendQualGateNegativEmail($student_uid) { + private function sendQualGateNegativEmail($projektarbeit_id, $betreuer_person_id, $paabgabe) { + $this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel'); + + $result = $this->ProjektarbeitModel->load($projektarbeit_id); + $projektarbeitArr = $this->getDataOrTerminateWithError($result); + $projektarbeit = $projektarbeitArr[0]; + + $result = $this->ProjektarbeitModel->getProjektbetreuerAnrede($betreuer_person_id); + $anredeArr = $this->getDataOrTerminateWithError($result); + $anrede = $anredeArr[0]; + + $student_uid = $projektarbeit->student_uid; + + $this->load->model('education/Paabgabetyp_model', 'PaabgabetypModel'); + $result = $this->PaabgabetypModel->load($paabgabe->paabgabetyp_kurzbz); + $paabgabetyp_kurzbzArr = $this->getDataOrTerminateWithError($result); + $paabgabetyp_kurzbz = $paabgabetyp_kurzbzArr[0]; + // Mail an Student wenn Qualgate negativ beurteilt wurde $student = new student(); if(!$student->load($student_uid)) @@ -696,21 +745,21 @@ class Abgabe extends FHCAPI_Controller $tomail = $student_uid.'@'.DOMAIN; - // TODO: emaildata + // TODO: datum format $data = array( - 'betreuerfullname' => $student->vorname, - 'qualgatebezeichnung' => $student->nachname, - 'datum' => $datum, - 'projektarbeitname' => $name + 'betreuerfullname' => $anrede->first, + 'qualgatebezeichnung' => $paabgabetyp_kurzbz->bezeichnung, + 'datum' => $paabgabe->datum, + 'projektarbeitname' => $projektarbeit->titel ); + $this->addMeta('$emaildata', $data); + $mailres = sendSanchoMail( 'QualGateNegativ', $data, $tomail, $subject ); - - return $mailres; } } \ No newline at end of file diff --git a/application/models/education/Projektarbeit_model.php b/application/models/education/Projektarbeit_model.php index b1971cea7..c87e9c187 100644 --- a/application/models/education/Projektarbeit_model.php +++ b/application/models/education/Projektarbeit_model.php @@ -168,7 +168,7 @@ class Projektarbeit_model extends DB_Model campus.tbl_paabgabe.kurzbz, campus.tbl_paabgabe.datum, campus.tbl_paabgabe.note, - campus.tbl_paabgabe.upload_required, + campus.tbl_paabgabe.upload_allowed, campus.tbl_paabgabetyp.paabgabetyp_kurzbz, campus.tbl_paabgabetyp.bezeichnung, campus.tbl_paabgabe.abgabedatum, @@ -189,7 +189,28 @@ class Projektarbeit_model extends DB_Model WHERE public.tbl_person.person_id= ?"; return $this->execReadOnlyQuery($qry_betr, [$bperson_id]); + } + + public function getProjektbetreuerEmail($projektarbeit_id) { + $qry = "SELECT ( + SELECT kontakt + FROM public.tbl_kontakt + WHERE kontakttyp = 'email' + AND person_id = pers.person_id + ORDER BY + CASE WHEN zustellung THEN 0 ELSE 1 END, + insertamum DESC NULLS LAST + LIMIT 1 + ) AS private_email + FROM lehre.tbl_projektarbeit pa + JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id) + JOIN public.tbl_person pers USING (person_id) + LEFT JOIN public.tbl_benutzer ben USING (person_id) + LEFT JOIN public.tbl_mitarbeiter ma ON ben.uid = ma.mitarbeiter_uid + WHERE (ben.aktiv OR ben.aktiv IS NULL) + AND projektarbeit_id = ?"; + return $this->execReadOnlyQuery($qry, [$projektarbeit_id]); } public function getProjektarbeitBenutzer($uid) { @@ -225,7 +246,7 @@ class Projektarbeit_model extends DB_Model FROM (SELECT tbl_person.vorname, tbl_person.nachname, tbl_studiengang.typ, tbl_studiengang.kurzbz, tbl_projektarbeit.projekttyp_kurzbz, tbl_projekttyp.bezeichnung, tbl_projektarbeit.titel, tbl_projektarbeit.projektarbeit_id, - tbl_projektbetreuer.betreuerart_kurzbz, tbl_betreuerart.beschreibung AS betreuerart_beschreibung, + tbl_projektbetreuer.person_id as betreuer_person_id, tbl_projektbetreuer.betreuerart_kurzbz, tbl_betreuerart.beschreibung AS betreuerart_beschreibung, tbl_benutzer.uid, tbl_student.matrikelnr, tbl_lehreinheit.studiensemester_kurzbz FROM lehre.tbl_projektarbeit LEFT JOIN lehre.tbl_projektbetreuer using(projektarbeit_id) diff --git a/application/models/education/Projektbetreuer_model.php b/application/models/education/Projektbetreuer_model.php index ae78b96d9..95950bf95 100644 --- a/application/models/education/Projektbetreuer_model.php +++ b/application/models/education/Projektbetreuer_model.php @@ -231,40 +231,4 @@ class Projektbetreuer_model extends DB_Model return $this->execQuery($qry, array($projektarbeit_id, $betreuer_person_id)); } - - /** - * Gets email of a Betreuer for a Projektarbeit. - * Projektarbeitbetreuer can be external employees, which should be contacted on their private - * email contact, if they have one. Internal Betreuer should be contacted on their "uid@DOMAIN" adress. - * @param int betreuer_person_id - * @return object success or error - */ - public function getBetreuerEmail($betreuer_person_id) { - // TODO: check if benutzer uid exists, not fixangestellt - - $qry =" - SELECT uid, - CASE - WHEN public.tbl_mitarbeiter.fixangestellt = false - THEN ( - SELECT kontakt AS email - FROM public.tbl_kontakt - WHERE - public.tbl_kontakt.person_id = ? AND - kontakttyp = 'email' - ) - - ELSE ( - SELECT public.tbl_benutzer.uid || '@' || '".DOMAIN."' AS email - FROM public.tbl_benutzer - WHERE person_id = ? - ) - END AS email - FROM public.tbl_benutzer - JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid) - WHERE person_id = ? - "; - - return $this->execReadOnlyQuery($qry, array($betreuer_person_id, $betreuer_person_id, $betreuer_person_id)); - } } diff --git a/public/js/api/factory/abgabe.js b/public/js/api/factory/abgabe.js index 0602a218c..67e67f385 100644 --- a/public/js/api/factory/abgabe.js +++ b/public/js/api/factory/abgabe.js @@ -1,4 +1,10 @@ export default { + getConfig() { + return { + method: 'get', + url: '/api/frontend/v1/Abgabe/getConfig' + }; + }, getStudentProjektarbeiten(uid) { return { method: 'get', @@ -51,12 +57,13 @@ export default { paabgabe_id: termin.paabgabe_id, paabgabetyp_kurzbz: termin.bezeichnung.paabgabetyp_kurzbz, datum: termin.datum, - note: termin.note, - upload_required: termin.upload_required, + note: termin.note_pk, + upload_allowed: termin.upload_allowed, fixtermin: termin.fixtermin, insertvon: termin.insertvon, kurzbz: termin.kurzbz, - projektarbeit_id: termin.projektarbeit_id + projektarbeit_id: termin.projektarbeit_id, + betreuer_person_id: termin.betreuer_person_id } }; }, diff --git a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js index c70c40ba7..ca8d92cd8 100644 --- a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js +++ b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js @@ -13,7 +13,7 @@ export const AbgabeMitarbeiterDetail = { Textarea: primevue.textarea, VueDatePicker }, - inject: ['abgabeTypeOptions', 'allowedNotenOptions'], + inject: ['abgabeTypeOptions', 'allowedNotenOptions', 'turnitin_link', 'old_abgabe_beurteilung_link'], props: { projektarbeit: { type: Object, @@ -22,7 +22,6 @@ export const AbgabeMitarbeiterDetail = { }, data() { return { - oldPaBeurteilungLink: 'https://moodle.technikum-wien.at/mod/page/view.php?id=1005052', // TODO: inject from app & app provide link from config eidAkzeptiert: false, enduploadTermin: null, allActiveLanguages: FHC_JS_DATA_STORAGE_OBJECT.server_languages @@ -34,18 +33,15 @@ export const AbgabeMitarbeiterDetail = { }, saveTermin(termin) { const paabgabe_id = termin.paabgabe_id - // 9 => magic number for "noch nicht eingetragen" - termin.note = termin.note?.note ?? 9 + termin.note_pk = termin.note?.note ?? null + termin.betreuer_person_id = this.projektarbeit.betreuer_person_id this.$api.call(ApiAbgabe.postProjektarbeitAbgabe(termin)).then( (res) => { if(res?.meta?.status == 'success') { this.$fhcAlert.alertSuccess(this.$p.t('ui/gespeichert')) - if(paabgabe_id === -1) { // new abgabe has been inserted - termin.paabgabe_id = res?.data?.retval - + termin.paabgabe_id = res?.data?.paabgabe_id this.projektarbeit.abgabetermine.push({ // new abgatermin row - 'paabgabe_id': -1, 'projektarbeit_id': this.projektarbeit.projektarbeit_id, 'fixtermin': false, @@ -53,7 +49,7 @@ export const AbgabeMitarbeiterDetail = { 'datum': new Date().toISOString().split('T')[0], 'paabgabetyp_kurzbz': termin.paabgabetyp_kurzbz, 'note': this.allowedNotenOptions.find(opt => opt.note == termin.note?.note), - 'upload_required': termin.upload_required, + 'upload_allowed': termin.upload_allowed, 'bezeichnung': this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz), 'abgabedatum': null, 'insertvon': this.viewData?.uid ?? '', @@ -61,12 +57,9 @@ export const AbgabeMitarbeiterDetail = { 'allowedToDelete': true }) } - - } else if(res?.meta?.status == 'error'){ this.$fhcAlert.alertError() } - }) }, deleteTermin(termin) { @@ -76,8 +69,6 @@ export const AbgabeMitarbeiterDetail = { // this.$p.t('global/tooltipLektorDeleteKontrolle', [this.$entryParams.permissions.kontrolleDeleteMaxReach ]) const deletedTerminIndex = this.projektarbeit.abgabetermine.findIndex(t => t.paabgabe_id === termin.paabgabe_id) this.projektarbeit.abgabetermine.splice(deletedTerminIndex, 1) - - } else if(res?.meta?.status == 'error'){ this.$fhcAlert.alertError() } @@ -92,15 +83,13 @@ export const AbgabeMitarbeiterDetail = { return true; }, downloadAbgabe(termin) { - // TODO: test this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid)) - // this.$fhcApi.factory.lehre.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid) }, dateDiffInDays(datum, today){ const oneDayMs = 1000 * 60 * 60 * 24 return Math.round((new Date(datum) - new Date(today)) / oneDayMs) }, - getDateStyle(termin) { + getDateStyle(termin, mode) { const datum = new Date(termin.datum) const abgabedatum = new Date(termin.abgabedatum) @@ -108,21 +97,31 @@ export const AbgabeMitarbeiterDetail = { // https://wiki.fhcomplete.info/doku.php?id=cis:abgabetool_fuer_studierende let color = 'white' let fontColor = 'black' + let icon = ''; if (termin.abgabedatum === null) { if(datum < today) { color = 'red' fontColor = 'white' + icon = 'fa-triangle-exclamation' } else if (datum > today && this.dateDiffInDays(datum, today) <= 12) { color = 'yellow' + icon = 'fa-circle-exclamation' } } else if(abgabedatum > datum) { color = 'pink' // aka "hellrot" fontColor = 'white' + icon = 'fa-circle-question' } else { color = 'green' + icon = 'fa-square-check' } - return `font-color: ${fontColor} ; background-color: ${color}; border-radius: 50%;` + //return `font-color: ${fontColor} ; background-color: ${color}; border-radius: 50%;` + if( typeof mode !== 'undefined' || mode === 'icon') { + return icon; + } else { + return 'abgabe-zieldatum-border-' + color; + } }, openBeurteilungLink(link) { window.open(link, '_blank') @@ -153,8 +152,7 @@ export const AbgabeMitarbeiterDetail = { window.open(link, '_blank') }, openPlagiatcheck() { - // todo: hardcoded turnitin link? - const link = "https://technikum-wien.turnitin.com/sso/sp/redwood/saml/5IyfmBr2OcSIaWQTKlFCGj/start" + const link = this.turnitin_link window.open(link, '_blank') }, openBenotung() { @@ -175,8 +173,17 @@ export const AbgabeMitarbeiterDetail = { }, endUploadVorhanden(){ return this.projektarbeit?.abgabetermine.find(abgabe => abgabe.paabgabetyp_kurzbz === 'end' && abgabe.abgabedatum !== null) + }, + qualityGateTerminAvailable() { + let qgatefound = false + this.projektarbeit?.abgabetermine.forEach(abgabe => { + if(abgabe.bezeichnung?.paabgabetyp_kurzbz == 'qualgate1' + || abgabe?.bezeichnung?.paabgabetyp_kurzbz == 'qualgate2') { + qgatefound = true + } + }) + return qgatefound } - }, created() { @@ -185,7 +192,7 @@ export const AbgabeMitarbeiterDetail = { }, template: ` -
+
{{$p.t('abgabetool/c4abgabeMitarbeiterbereich')}}
@@ -199,20 +206,20 @@ export const AbgabeMitarbeiterDetail = {
- Kein Endupload vorhanden! + {{ $p.t('abgabetool/c4noEnduploadFound') }}
@@ -220,8 +227,8 @@ export const AbgabeMitarbeiterDetail = {
@@ -230,86 +237,87 @@ export const AbgabeMitarbeiterDetail = {
-
{{$p.t('abgabetool/c4fixtermin')}}
-
{{$p.t('abgabetool/c4zieldatum')}}
-
{{$p.t('abgabetool/c4abgabetyp')}}
-
{{$p.t('abgabetool/c4note')}}
-
{{$p.t('abgabetool/c4upload_required')}}
-
{{$p.t('abgabetool/c4abgabekurzbz')}}
-
{{$p.t('abgabetool/c4abgabedatum')}}
-
- +
{{$p.t('abgabetool/c4fixtermin')}}
+
+
{{$p.t('abgabetool/c4zieldatum')}}
+
{{$p.t('abgabetool/c4abgabetyp')}}
+
{{$p.t('abgabetool/c4note')}}
+
{{$p.t('abgabetool/c4upload_allowed')}}
+
{{$p.t('abgabetool/c4abgabekurzbz')}}
+
{{$p.t('abgabetool/c4abgabedatum')}}
+
+ +
keine Termine gefunden!
-

+ + +
-
-
- +
+
+ + + +
+
+
+ - -
-
-
- - -
-
- - -
-
- - -
-
- -
-
- {{ termin.abgabedatum?.split("-").reverse().join(".") }} - - - -
-
-
-
- -
-
- -
+ v-model="termin.bezeichnung" + :options="abgabeTypeOptions" + :optionLabel="getOptionLabelAbgabetyp"> + +
+
+ + +
+
+ + +
+
+ +
+
+ {{ termin.abgabedatum?.split("-").reverse().join(".") }} + + + +
+
+ +
diff --git a/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js b/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js index 3271ed97f..be44b0441 100644 --- a/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js +++ b/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js @@ -15,6 +15,7 @@ export const AbgabeStudentDetail = { Textarea: primevue.textarea, VueDatePicker }, + inject: ['notenOptions'], props: { projektarbeit: { type: Object, @@ -83,9 +84,7 @@ export const AbgabeStudentDetail = { this.$refs.modalContainerEnduploadZusatzdaten.hide() }, downloadAbgabe(termin) { - // TODO: test this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid)) - // this.$fhcApi.factory.lehre.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid) }, formatDate(dateParam) { const date = new Date(dateParam) @@ -181,6 +180,10 @@ export const AbgabeStudentDetail = { }, getOptionLabel(option) { return option.sprache + }, + getTerminNoteBezeichnung(termin) { + const noteOpt = this.notenOptions.find(opt => opt.note == termin.note) + return noteOpt ? noteOpt.bezeichnung : '' } }, watch: { @@ -201,6 +204,16 @@ export const AbgabeStudentDetail = { }, getAllowedToSendEndupload() { return !this.eidAkzeptiert + }, + qualityGateTerminAvailable() { + let qgatefound = false + this.projektarbeit?.abgabetermine.forEach(abgabe => { + if(abgabe.paabgabetyp_kurzbz == 'qualgate1' + || abgabe.paabgabetyp_kurzbz == 'qualgate2') { + qgatefound = true + } + }) + return qgatefound } }, created() { @@ -221,8 +234,10 @@ export const AbgabeStudentDetail = {
{{$p.t('abgabetool/c4fixtermin')}}
{{$p.t('abgabetool/c4zieldatum')}}
-
{{$p.t('abgabetool/c4abgabetyp')}}
-
{{$p.t('abgabetool/c4abgabekurzbz')}}
+
{{$p.t('abgabetool/c4abgabetyp')}}
+
{{$p.t('abgabetool/c4note')}}
+
{{$p.t('abgabetool/c4upload_allowed')}}
+
{{$p.t('abgabetool/c4abgabekurzbz')}}
{{$p.t('abgabetool/c4abgabedatum')}}
{{$p.t('abgabetool/c4fileupload')}} @@ -250,14 +265,27 @@ export const AbgabeStudentDetail = {
-
{{ termin.bezeichnung }}
-
- +
{{ termin.bezeichnung }}
+
+ {{ getTerminNoteBezeichnung(termin) }}
-
+
+ + +
+
+ +
+
{{ termin.abgabedatum?.split("-").reverse().join(".") }} - +
@@ -267,8 +295,8 @@ export const AbgabeStudentDetail = {
@@ -289,7 +317,7 @@ export const AbgabeStudentDetail = {
-

Titel: {{ projektarbeit?.titel }}

+

{{$p.t('abgabetool/c4titel')}}: {{ projektarbeit?.titel }}

diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js index c16abc8a3..1e9d61056 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js @@ -19,7 +19,9 @@ export const AbgabetoolMitarbeiter = { provide() { return { abgabeTypeOptions: Vue.computed(() => this.abgabeTypeOptions), - allowedNotenOptions: Vue.computed(() => this.allowedNotenOptions) + allowedNotenOptions: Vue.computed(() => this.allowedNotenOptions), + turnitin_link: Vue.computed(() => this.turnitin_link), + old_abgabe_beurteilung_link: Vue.computed(() => this.old_abgabe_beurteilung_link) } }, props: { @@ -34,6 +36,8 @@ export const AbgabetoolMitarbeiter = { }, data() { return { + turnitin_link: null, + old_abgabe_beurteilung_link: null, saving: false, loading: false, abgabeTypeOptions: null, @@ -59,9 +63,9 @@ export const AbgabetoolMitarbeiter = { tableBuiltResolve: null, tableBuiltPromise: null, abgabeTableOptions: { - height: 700, + minHeight: 250, index: 'projektarbeit_id', - layout: 'fitDataStretch', + layout: 'fitColumns', placeholder: this.$p.t('global/noDataAvailable'), selectable: true, selectableCheck: this.selectionCheck, @@ -80,13 +84,13 @@ export const AbgabetoolMitarbeiter = { {title: Vue.computed(() => this.$p.t('abgabetool/c4details')), field: 'details', formatter: this.detailFormatter, widthGrow: 1, tooltip: false}, {title: Vue.computed(() => this.$p.t('abgabetool/c4personenkennzeichen')), field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false}, {title: Vue.computed(() => this.$p.t('abgabetool/c4kontakt')), field: 'mail', formatter: this.mailFormatter, widthGrow: 1, tooltip: false}, - {title: Vue.computed(() => this.$p.t('abgabetool/c4vorname')), field: 'vorname', formatter: this.centeredTextFormatter, widthGrow: 1}, + {title: Vue.computed(() => this.$p.t('abgabetool/c4vorname')), field: 'vorname', formatter: this.centeredTextFormatter,widthGrow: 1}, {title: Vue.computed(() => this.$p.t('abgabetool/c4nachname')), field: 'nachname', formatter: this.centeredTextFormatter, widthGrow: 1}, {title: Vue.computed(() => this.$p.t('abgabetool/c4projekttyp')), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, widthGrow: 1}, - {title: Vue.computed(() => this.$p.t('abgabetool/c4stg')), field: 'stg', formatter: this.centeredTextFormatter, widthGrow: 2}, + {title: Vue.computed(() => this.$p.t('abgabetool/c4stg')), field: 'stg', formatter: this.centeredTextFormatter, widthGrow: 1}, {title: Vue.computed(() => this.$p.t('abgabetool/c4sem')), field: 'studiensemester_kurzbz', formatter: this.centeredTextFormatter, widthGrow: 1}, {title: Vue.computed(() => this.$p.t('abgabetool/c4titel')), field: 'titel', formatter: this.centeredTextFormatter, maxWidth: 500, widthGrow: 8}, - {title: Vue.computed(() => this.$p.t('abgabetool/c4betreuerart')), field: 'betreuerart_beschreibung',formatter: this.centeredTextFormatter, widthGrow: 8} + {title: Vue.computed(() => this.$p.t('abgabetool/c4betreuerart')), field: 'betreuerart_beschreibung',formatter: this.centeredTextFormatter, widthGrow: 1} ], persistence: false, }, @@ -209,7 +213,7 @@ export const AbgabetoolMitarbeiter = { 'kurzbz': '', 'datum': new Date().toISOString().split('T')[0], 'note': this.allowedNotenOptions.find(opt => opt.note == 9), - 'upload_required': false, + 'upload_allowed': false, 'paabgabetyp_kurzbz': '', 'bezeichnung': '', 'abgabedatum': null, @@ -227,7 +231,6 @@ export const AbgabetoolMitarbeiter = { paabgabetyp_kurzbz: termin.paabgabetyp_kurzbz } }) - pa.betreuer = this.buildBetreuer(pa) pa.student_uid = details.student_uid pa.student = `${pa.vorname} ${pa.nachname}` @@ -280,10 +283,6 @@ export const AbgabetoolMitarbeiter = { buildStg(projekt) { return (projekt.typ + projekt.kurzbz)?.toUpperCase() }, - buildBetreuer(abgabe) { - // TODO: preload and insert own titled name of betreuer somehow - return abgabe.betreuerart_beschreibung + ': ' + (abgabe.btitelpre ? abgabe.btitelpre + ' ' : '') + abgabe.bvorname + ' ' + abgabe.bnachname + (abgabe.btitelpost ? ' ' + abgabe.btitelpost : '') - }, setupData(data){ this.projektarbeiten = data[0] this.domain = data[1] @@ -337,7 +336,7 @@ export const AbgabetoolMitarbeiter = { if(!tableDataSet) return const rect = tableDataSet.getBoundingClientRect(); - this.abgabeTableOptions.height = window.visualViewport.height - rect.top + this.abgabeTableOptions.height = window.visualViewport.height - rect.top - 80 this.$refs.abgabeTable.tabulator.setHeight(this.abgabeTableOptions.height) }, async setupMounted() { @@ -346,7 +345,6 @@ export const AbgabetoolMitarbeiter = { this.loadProjektarbeiten() - this.$refs.verticalsplit.collapseBottom() this.calcMaxTableHeight() @@ -359,6 +357,15 @@ export const AbgabetoolMitarbeiter = { }, created() { + // fetch config to avoid hard coded links + this.$api.call(ApiAbgabe.getConfig()).then(res => { + this.turnitin_link = res.data?.turnitin_link + this.old_abgabe_beurteilung_link = res.data?.old_abgabe_beurteilung_link + }).catch(e => { + console.log(e) + this.loading = false + }) + // fetch noten options //TODO: SWITCH TO NOTEN API ONCE NOTENTOOL IS IN MASTER TO AVOID DUPLICATE API this.$api.call(ApiAbgabe.getNoten()).then(res => { @@ -367,7 +374,6 @@ export const AbgabetoolMitarbeiter = { this.allowedNotenOptions = this.notenOptions.filter( opt => opt.bezeichnung === 'Bestanden' || opt.bezeichnung === 'Nicht bestanden' - || opt.bezeichnung === 'Noch nicht eingetragen' ) }).catch(e => { this.loading = false @@ -385,7 +391,7 @@ export const AbgabetoolMitarbeiter = { }, template: ` + dialogClass="modal-lg"> + +
- - - - - - - + + + + + + +
`, }; diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolStudent.js b/public/js/components/Cis/Abgabetool/AbgabetoolStudent.js index bfd5da01b..2a1827b4e 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolStudent.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolStudent.js @@ -10,6 +10,11 @@ export const AbgabetoolStudent = { AbgabeDetail, VerticalSplit }, + provide() { + return { + notenOptions: Vue.computed(() => this.notenOptions) + } + }, props: { student_uid_prop: { default: null @@ -25,6 +30,7 @@ export const AbgabetoolStudent = { }, data() { return { + notenOptions: null, tabulatorUuid: Vue.ref(0), domain: '', student_uid: null, @@ -83,27 +89,42 @@ export const AbgabetoolStudent = { ]}; }, methods: { - checkQualityGates(termine, enduploadtermin) { - return true + checkQualityGates(termine) { + let qgate1Passed = false + let qgate2Passed = false + + termine.forEach(t => { + const noteOption = this.notenOptions.find(opt => opt.note == t.note) + if(noteOption && noteOption.positiv) { + if(t.paabgabetyp_kurzbz == 'qualgate1') { + qgate1Passed = true + } else if(t.paabgabetyp_kurzbz == 'qualgate2') { + qgate2Passed = true + } + } + }) + + return qgate1Passed && qgate2Passed }, isPastDate(date) { return new Date(date) < new Date(Date.now()) }, setDetailComponent(details){ this.loadAbgaben(details).then((res)=> { - const pa = this.projektarbeiten?.retval?.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id) + const pa = this.projektarbeiten?.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id) pa.abgabetermine = res.data[0].retval pa.abgabetermine.forEach(termin => { termin.file = [] - // termin.allowedToUpload = true - + termin.allowedToUpload = false + // termin.datum = '2025-10-16' // TODO: fixtermin logic? - if(termin.bezeichnung == 'Endupload' && - (this.isPastDate(termin.datum) || this.checkQualityGates(pa.abgabetermine, termin))) { + if(termin.paabgabetyp_kurzbz == 'enda') { - // termin.allowedToUpload = false + termin.allowedToUpload = !this.isPastDate(termin.datum) && this.checkQualityGates(pa.abgabetermine) + } else if(termin.paabgabetyp_kurzbz == 'qualgate1' || termin.paabgabetyp_kurzbz == 'qualgate2') { + termin.allowedToUpload = termin.upload_allowed } else { - // termin.allowedToUpload = true + termin.allowedToUpload = true } }) @@ -111,7 +132,6 @@ export const AbgabetoolStudent = { pa.student_uid = this.student_uid this.selectedProjektarbeit = pa - this.$refs.verticalsplit.showBoth() @@ -150,8 +170,12 @@ export const AbgabetoolStudent = { tableResolve(resolve) { this.tableBuiltResolve = resolve }, - buildMailToLink(abgabe) { - return 'mailto:' + abgabe.mitarbeiter_uid +'@'+ this.domain + buildMailToLink(projekt) { + if(projekt.mitarbeiter_uid) { // standard + return 'mailto:' + projekt.mitarbeiter_uid +'@'+ this.domain + } else { // private + return 'mailto:' + projekt.email + } }, buildBetreuer(abgabe) { return abgabe.betreuerart_beschreibung + ': ' + (abgabe.btitelpre ? abgabe.btitelpre + ' ' : '') + abgabe.bvorname + ' ' + abgabe.bnachname + (abgabe.btitelpost ? ' ' + abgabe.btitelpost : '') @@ -160,7 +184,7 @@ export const AbgabetoolStudent = { this.projektarbeiten = data[0] this.domain = data[1] this.student_uid = data[2] - const d = data[0]?.retval?.map(projekt => { + const d = data[0]?.map(projekt => { let mode = 'detailTermine' if (projekt.babgeschickt || projekt.zweitbetreuer_abgeschickt) { @@ -213,7 +237,7 @@ export const AbgabetoolStudent = { if(!tableDataSet) return const rect = tableDataSet.getBoundingClientRect(); - this.abgabeTableOptions.height = window.visualViewport.height - rect.top + this.abgabeTableOptions.height = window.visualViewport.height - rect.top - 100 this.$refs.abgabeTable.tabulator.setHeight(this.abgabeTableOptions.height) }, async setupMounted() { @@ -223,7 +247,7 @@ export const AbgabetoolStudent = { this.loadProjektarbeiten() this.$refs.verticalsplit.collapseBottom() - //this.calcMaxTableHeight() + this.calcMaxTableHeight() } }, watch: { @@ -235,34 +259,43 @@ export const AbgabetoolStudent = { } }, created() { + //TODO: SWITCH TO NOTEN API ONCE NOTENTOOL IS IN MASTER TO AVOID DUPLICATE API + this.$api.call(ApiAbgabe.getNoten()).then(res => { + this.notenOptions = res.data + }).catch(e => { + this.loading = false + }) }, mounted() { this.setupMounted() }, template: ` - - - - + +
+ + + + +
`, }; diff --git a/public/js/components/Cis/Abgabetool/DeadlineOverview.js b/public/js/components/Cis/Abgabetool/DeadlineOverview.js index 6f8347169..cc202b4b4 100644 --- a/public/js/components/Cis/Abgabetool/DeadlineOverview.js +++ b/public/js/components/Cis/Abgabetool/DeadlineOverview.js @@ -110,7 +110,7 @@ export const DeadlineOverview = { if(!tableDataSet) return const rect = tableDataSet.getBoundingClientRect(); - this.deadlineTableOptions.height = window.visualViewport.height - rect.top + this.deadlineTableOptions.height = window.visualViewport.height - rect.top - 30 this.$refs.deadlineTable.tabulator.setHeight(this.deadlineTableOptions.height) }, async setupMounted() { diff --git a/public/js/plugins/Api.js b/public/js/plugins/Api.js index e3e604d6c..43c2b3064 100644 --- a/public/js/plugins/Api.js +++ b/public/js/plugins/Api.js @@ -164,7 +164,6 @@ export default { return fhcApiAxios.post(uri, data, config); }, call(factory, configoverwrite, form) { - debugger if (Array.isArray(factory)) { const $fhcAlert = app.config.globalProperties.$fhcAlert; const $api = app.config.globalProperties.$api; diff --git a/system/dbupdate_3.4/61164_abgabetool_quality_gates.php b/system/dbupdate_3.4/61164_abgabetool_quality_gates.php index b4f6fd7c9..1ec257bb1 100644 --- a/system/dbupdate_3.4/61164_abgabetool_quality_gates.php +++ b/system/dbupdate_3.4/61164_abgabetool_quality_gates.php @@ -34,7 +34,7 @@ if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table { $qry = "ALTER TABLE campus.tbl_paabgabe - ADD COLUMN note SMALLINT NOT NULL DEFAULT 9, + ADD COLUMN note SMALLINT DEFAULT NULL, ADD CONSTRAINT tbl_paabgabe_note_fkey FOREIGN KEY (note) REFERENCES lehre.tbl_note(note) @@ -47,16 +47,16 @@ if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table } } -if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table_schema = 'campus' AND table_name = 'tbl_paabgabe' AND column_name = 'upload_required'")) +if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table_schema = 'campus' AND table_name = 'tbl_paabgabe' AND column_name = 'upload_allowed'")) { if($db->db_num_rows($result) === 0) { $qry = "ALTER TABLE campus.tbl_paabgabe - ADD COLUMN IF NOT EXISTS upload_required boolean DEFAULT false;"; + ADD COLUMN IF NOT EXISTS upload_allowed boolean DEFAULT false;"; if(!$db->db_query($qry)) echo 'campus.tbl_paabgabe: '.$db->db_last_error().'
'; else - echo '
paabgabe column upload_required default false hinzugefuegt'; + echo '
paabgabe column upload_allowed default false hinzugefuegt'; } } \ No newline at end of file diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index dd23a1c4a..19cf6469f 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -41490,7 +41490,7 @@ array( ), array( 'sprache' => 'English', - 'text' => "details", + 'text' => "Details", 'description' => '', 'insertvon' => 'system' ) @@ -41510,7 +41510,7 @@ array( ), array( 'sprache' => 'English', - 'text' => "semester", + 'text' => "Semester", 'description' => '', 'insertvon' => 'system' ) @@ -41530,7 +41530,7 @@ array( ), array( 'sprache' => 'English', - 'text' => "degree program", + 'text' => "Degree program", 'description' => '', 'insertvon' => 'system' ) @@ -41550,7 +41550,7 @@ array( ), array( 'sprache' => 'English', - 'text' => "contact", + 'text' => "Contact", 'description' => '', 'insertvon' => 'system' ) @@ -41590,7 +41590,7 @@ array( ), array( 'sprache' => 'English', - 'text' => "project type", + 'text' => "Project type", 'description' => '', 'insertvon' => 'system' ) @@ -41610,7 +41610,7 @@ array( ), array( 'sprache' => 'English', - 'text' => "title", + 'text' => "Title", 'description' => '', 'insertvon' => 'system' ) @@ -41670,7 +41670,7 @@ array( ), array( 'sprache' => 'English', - 'text' => "target date", + 'text' => "Target date", 'description' => '', 'insertvon' => 'system' ) @@ -41730,7 +41730,7 @@ array( ), array( 'sprache' => 'English', - 'text' => "submission date", + 'text' => "Submission date", 'description' => '', 'insertvon' => 'system' ) @@ -41776,6 +41776,26 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4benoten', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Benoten", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Grade", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'abgabetool', @@ -41796,6 +41816,46 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4save', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Speichern", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Save", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4delete', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Löschen", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Delete", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'abgabetool', @@ -42176,6 +42236,146 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4note', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Note", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Grade', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4upload_allowed', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Upload erlaubt", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Upload allowed', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4student_perspective', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Studentenansicht", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Student perspective', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4upload', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Hochladen", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Upload', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4plagiatcheck_link', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "zur Plagiatsprüfung", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Plagiarism check', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4plagiatcheck_link', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "zur Plagiatsprüfung", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Plagiarism check', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4noEnduploadFound', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Kein Endupload vorhanden!", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No final submission found!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'abgabetool',