diff --git a/application/config/navigation.php b/application/config/navigation.php index 3680930d0..d57cd697a 100644 --- a/application/config/navigation.php +++ b/application/config/navigation.php @@ -55,6 +55,12 @@ $config['navigation_header'] = array( 'description' => 'CIS', 'sort' => 10 ), + 'lehrveranstaltungen' => array( + 'link' => site_url('lehre/lvplanung/LvTemplateUebersicht'), + 'icon' => '', + 'description' => 'Lehrveranstaltungen', + 'sort' => 15 + ), 'reihungstest' => array( 'link' => site_url('organisation/Reihungstest'), 'description' => 'Reihungstests', @@ -287,6 +293,15 @@ $config['navigation_menu']['lehre/lehrauftrag/LehrauftragErteilen/*'] = array( ) ); +$config['navigation_menu']['lehre/lvplanung/LvTemplateUebersicht/index'] = array( + 'lvTemplateUebersicht' => array( + 'link' => site_url('lehre/lvplanung/LvTemplateUebersicht'), + 'description' => 'LV Template Übersicht', + 'icon' => '', + 'sort' => 1 + ) +); + $config['navigation_menu']['system/issues/Issues/*'] = array( 'fehlerzustaendigkeiten' => array( 'link' => site_url('system/issues/IssuesZustaendigkeiten'), diff --git a/application/controllers/api/frontend/v1/education/Lehrveranstaltung.php b/application/controllers/api/frontend/v1/education/Lehrveranstaltung.php new file mode 100644 index 000000000..636c8e3f3 --- /dev/null +++ b/application/controllers/api/frontend/v1/education/Lehrveranstaltung.php @@ -0,0 +1,65 @@ + array( + 'lehre/lehrveranstaltung:rw' + ) + )); + + // Load model LehrveranstaltungModel + $this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + } + + /** + * Get all Templates and union with all Lehrveranstaltungen of given Studiensemester and Oes of given Berechtigung, + * that are assigned to a template. This data structure can be used for nested tabulators' data tree. + * + * @param null|string $studiensemester_kurzbz + * @param null|string $berechtigung + * @return array|stdClass|null + */ + public function getTemplateLvTree() + { + $studiensemester_kurzbz = $this->input->get('studiensemester_kurzbz'); + $berechtigung = $this->input->get('berechtigung'); + + if ($berechtigung) + { + $oe_permissions = $this->permissionlib->getOE_isEntitledFor($berechtigung); + if(!$oe_permissions) $oe_permissions = []; + + $result = $this->LehrveranstaltungModel->getTemplateLvTree($studiensemester_kurzbz, $oe_permissions); + } + else + { + $result = $this->LehrveranstaltungModel->getTemplateLvTree($studiensemester_kurzbz); + } + + if (isError($result)) { + $this->terminateWithError(getError($result), self::ERROR_TYPE_DB); + } + + $this->terminateWithSuccess((getData($result) ?: [])); + } +} diff --git a/application/controllers/api/frontend/v1/organisation/Studiensemester.php b/application/controllers/api/frontend/v1/organisation/Studiensemester.php new file mode 100644 index 000000000..72a449aaa --- /dev/null +++ b/application/controllers/api/frontend/v1/organisation/Studiensemester.php @@ -0,0 +1,118 @@ + self::PERM_LOGGED, + 'getAktNext' => self::PERM_LOGGED + ) + ); + // Load model StudiensemesterModel + $this->load->model('organisation/studiensemester_model', 'StudiensemesterModel'); + } + + /** + * Get all Studiensemester. + * + * @param null|string $order Sorting order for the Studiensemester, 'asc' or 'desc'. Defaults to 'asc'. + * @param null|string $start Start date of the displayed Studiensemester in the format 'YYYY-MM-DD'. + * If provided, only Studiensemester starting from this date onwards will be returned. + * eg. '2020-09-01' will start with WS2020. + */ + public function getAll() + { + $order = $this->input->get('order'); + $start = $this->input->get('start'); + + if (strcasecmp($order, 'DESC') == 0) + { + $this->StudiensemesterModel->addOrder('ende', 'DESC'); + } + else + { + $this->StudiensemesterModel->addOrder('ende', 'ASC'); + } + + if ($start) + { + $result = $this->StudiensemesterModel->loadWhere([ + 'start >= ' => $start + ]); + } + else + { + $result = $this->StudiensemesterModel->load(); + } + + if (isError($result)) { + $this->terminateWithError(getError($result), self::ERROR_TYPE_DB); + } + + $this->terminateWithSuccess((getData($result) ?: [])); + } + + /** + * @return void + */ + public function getAktNext() + { + $semester = $this->input->get('semester'); + + $result = null; + + if (!is_numeric($semester)) + { + $result = $this->StudiensemesterModel->loadWhere(array('start <=' => 'NOW()', 'ende >=' => 'NOW()')); + } + + if (!hasData($result)) + { + $this->StudiensemesterModel->addOrder('ende'); + $this->StudiensemesterModel->addLimit(1); + + $whereArray = array('ende >=' => 'NOW()'); + + if (is_numeric($semester)) + { + if ($semester % 2 == 0) + { + $ss = 'SS'; + } + else + { + $ss = 'WS'; + } + + $whereArray['SUBSTRING(studiensemester_kurzbz FROM 1 FOR 2) ='] = $ss; + } + + $result = $this->StudiensemesterModel->loadWhere($whereArray); + } + + if (isError($result)) { + $this->terminateWithError(getError($result), self::ERROR_TYPE_DB); + } + + $this->terminateWithSuccess((getData($result) ?: '')); + } +} diff --git a/application/controllers/lehre/lvplanung/LvTemplateUebersicht.php b/application/controllers/lehre/lvplanung/LvTemplateUebersicht.php new file mode 100644 index 000000000..b1ab2fe26 --- /dev/null +++ b/application/controllers/lehre/lvplanung/LvTemplateUebersicht.php @@ -0,0 +1,45 @@ + 'lehre/lehrveranstaltung:rw', + ) + ); + + // Load libraries + $this->load->library('AuthLib'); + + + // Load language phrases + $this->loadPhrases( + array( + 'global', + 'lehre' + ) + ); + + $this->_setAuthUID(); + } + + public function index() + { + $this->load->view('lehre/lvplanung/lvTemplateUebersicht.php'); + } + + + /** + * Retrieve the UID of the logged user and checks if it is valid + */ + private function _setAuthUID() + { + $this->_uid = getAuthUID(); + + if (!$this->_uid) show_error('User authentification failed'); + } +} \ No newline at end of file diff --git a/application/models/education/Lehrveranstaltung_model.php b/application/models/education/Lehrveranstaltung_model.php index cefd147b9..de8ebc5c9 100644 --- a/application/models/education/Lehrveranstaltung_model.php +++ b/application/models/education/Lehrveranstaltung_model.php @@ -15,6 +15,297 @@ class Lehrveranstaltung_model extends DB_Model $this->load->model('organisation/studiensemester_model', 'StudiensemesterModel'); } + /** + * Get Lehrveranstaltungen by eventQuery string. Use with autocomplete event queries. + * @param $eventQuery String + * @param string $studiensemester_kurzbz Filter by Studiensemester + * @param array $oes Filter by Organisationseinheiten + * @return array + */ + public function getAutocompleteSuggestions($eventQuery, $studiensemester_kurzbz = null, $oes = null) + { + $subQry = $this->_getQryLvsByStudienplan($studiensemester_kurzbz, $oes); + $params = []; + + /* filter by input string */ + if (is_string($eventQuery)) { + $subQry.= ' AND lv.bezeichnung ILIKE ?'; + $params[] = '%' . $eventQuery . '%'; + } + + $qry = 'SELECT DISTINCT ON (lehrveranstaltung_id) * FROM ('. $subQry. ') AS tmp'; + + return $this->execQuery($qry, $params); + } + + /** + * Get Lehrveranstaltungen with its Stg, OE and OE-type. + * Filter by Studiensemester and Organisationseinheiten if necessary. + * @param $eventQuery String + * @param string $studiensemester_kurzbz Filter by Studiensemester + * @param array $oes Filter by Organisationseinheiten + * @param array $lv_ids Filter by Lehrveranstaltung-Ids + * @return array + */ + public function getLvsByStudienplan($studiensemester_kurzbz = null, $oes = null, $lv_ids = null) + { + $subQry = $this->_getQryLvsByStudienplan($studiensemester_kurzbz, $oes); + $qry = 'SELECT * FROM ('. $subQry. ') AS tmp'; + + if (isset($lv_ids) && is_array($lv_ids)) + { + /* filter by lv_ids */ + $implodedLvIds = "'". implode("', '", $lv_ids). "'"; + $qry.= ' WHERE lehrveranstaltung_id IN ('. $implodedLvIds. ')'; + } + + $qry.= ' ORDER BY stg_typ_kurzbz, orgform_kurzbz DESC'; + + return $this->execQuery($qry); + } + + /** + * Get basic query to retrieve Lehrveranstaltungen according to the Orgforms and Ausbildungssemesters actual Studienplan. + * + * @return string + */ + private function _getQryLvsByStudienplan($studiensemester_kurzbz = null, $oes = null, $lehrtyp_kurzbz = 'lv') + { + $qry = ' + SELECT + lv.oe_kurzbz AS lv_oe_kurzbz, + CASE + WHEN oe.organisationseinheittyp_kurzbz = \'Kompetenzfeld\' THEN (\'KF \' || oe.bezeichnung) + WHEN oe.organisationseinheittyp_kurzbz = \'Department\' THEN (\'DEP \' || oe.bezeichnung) + ELSE (oe.organisationseinheittyp_kurzbz || \' \' || oe.bezeichnung) + END AS lv_oe_bezeichnung, + stplsem.studiensemester_kurzbz, + studienordnung_id, + sto.studiengang_kz, + stpl.studienplan_id, + stplsem.semester, + stpl.orgform_kurzbz, + upper(stg.typ || stg.kurzbz) AS stg_typ_kurzbz, + stg.bezeichnung AS stg_bezeichnung, + stgtyp.bezeichnung AS stg_typ_bezeichnung, + lv.lehrveranstaltung_id, + lv.semester, + lv.bezeichnung AS lv_bezeichnung, + ( + -- comma seperated string of all lehreinheitgruppen + SELECT string_agg(bezeichnung, \', \') AS lehreinheitgruppe_bezeichnung + FROM( + -- distinct bezeichnung, as may come multiple times from different lehreinheiten + SELECT DISTINCT ON (studiengang_kz, bezeichnung) studiengang_kz, bezeichnung FROM + ( + -- distinct lehreinheitgruppe, as may come multiple times from different lehrform + SELECT DISTINCT ON (legr.lehreinheitgruppe_id) legr.studiengang_kz, + -- get Spezialgruppe or Lehrverbandgruppe + COALESCE( + legr.gruppe_kurzbz, + CONCAT( UPPER(stg1.typ), UPPER(stg1.kurzbz), \'-\', legr.semester, legr.verband, legr.gruppe ) + ) as bezeichnung + FROM lehre.tbl_lehreinheitgruppe legr + JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id) + JOIN lehre.tbl_lehrveranstaltung lv1 USING (lehrveranstaltung_id) + JOIN public.tbl_studiengang stg1 ON stg1.studiengang_kz = legr.studiengang_kz + WHERE lv1.lehrveranstaltung_id = lv.lehrveranstaltung_id + AND le.studiensemester_kurzbz = stplsem.studiensemester_kurzbz + ) AS lehreinheitgruppen + GROUP BY studiengang_kz, bezeichnung + ORDER BY studiengang_kz DESC + ) AS uniqueLehreinheitgruppen_bezeichnung + ) AS lehreinheitgruppen_bezeichnung + FROM + lehre.tbl_studienplan stpl + JOIN lehre.tbl_studienordnung sto USING (studienordnung_id) + JOIN lehre.tbl_studienplan_semester stplsem USING (studienplan_id) + JOIN lehre.tbl_studienplan_lehrveranstaltung stpllv ON (stpllv.studienplan_id = stpl.studienplan_id AND stpllv.semester = stplsem.semester) + JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id) + JOIN public.tbl_organisationseinheit oe USING (oe_kurzbz) + JOIN public.tbl_studiengang stg ON stg.studiengang_kz = sto.studiengang_kz + JOIN public.tbl_studiengangstyp stgtyp ON stgtyp.typ = stg.typ + /* filter by lehrtyp_kurzbz, default is lvs only */ + WHERE + lehrtyp_kurzbz = '. $this->db->escape($lehrtyp_kurzbz); + + if (isset($studiensemester_kurzbz) && is_string($studiensemester_kurzbz)) + { + /* filter by studiensemester */ + $qry.= ' AND stplsem.studiensemester_kurzbz = '. $this->db->escape($studiensemester_kurzbz); + + } + + if (isset($oes) && is_array($oes)) + { + /* filter by organisationseinheit */ + $implodedOes = "'". implode("', '", $oes). "'"; + $qry.= ' AND lv.oe_kurzbz IN ('. $implodedOes. ')'; + } + + return $qry; + } + + /** + * Get all Templates and union with all Lehrveranstaltungen of given Studiensemester and Oes, that are assigned to + * a template. This data structure can be used for nested tabulator data tree. + * + * @param null|string $studiensemester_kurzbz + * @param null|array $oes + * @return array|stdClass|null + */ + public function getTemplateLvTree($studiensemester_kurzbz = null, $oes = null){ + $params = []; + $qry = ' + WITH + -- All Lvs that are assigned to a template in given Studiensemester for given Oes + -- joining via actual Studienplan + standardisierteLvs AS ( + SELECT + lv.*, + stpl.studienplan_id::text as studienplan_id, + stpl.bezeichnung AS studienplan_bezeichnung, + stplsem.studiensemester_kurzbz + FROM + lehre.tbl_studienplan stpl + JOIN lehre.tbl_studienordnung sto USING (studienordnung_id) + JOIN lehre.tbl_studienplan_semester stplsem USING (studienplan_id) + JOIN lehre.tbl_studienplan_lehrveranstaltung stpllv ON (stpllv.studienplan_id = stpl.studienplan_id AND stpllv.semester = stplsem.semester) + JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id) + JOIN public.tbl_organisationseinheit oe USING (oe_kurzbz) + JOIN public.tbl_studiengang stg ON stg.studiengang_kz = sto.studiengang_kz + JOIN public.tbl_studiengangstyp stgtyp ON stgtyp.typ = stg.typ + WHERE + -- filter type lv + lehrtyp_kurzbz = \'lv\' + -- filter lvs assigned to template (= standardisierte lv) + AND lehrveranstaltung_template_id IS NOT NULL'; + + if (is_string($studiensemester_kurzbz)) + { + /* filter by studiensemester */ + $params[]= $studiensemester_kurzbz; + $qry.= ' AND stplsem.studiensemester_kurzbz = ? '; + + } + + if (is_array($oes)) + { + /* filter by organisationseinheit */ + $params[]= $oes; + $qry.= ' AND lv.oe_kurzbz IN ? '; + } + $qry.= ' + ), + -- All templates + templateLvs AS ( + SELECT + lv.*, + NULL AS studienplan_id, + ( + SELECT string_agg(stpl_bezeichnung, \', \') + FROM + ( + SELECT stlv.studienplan_bezeichnung AS stpl_bezeichnung + FROM standardisierteLvs stlv + WHERE stlv.lehrveranstaltung_template_id = lv.lehrveranstaltung_id + ) AS studienplaene + ) AS studienplan_bezeichnung, + NULL AS studiensemester_kurzbz + FROM + lehre.tbl_lehrveranstaltung lv + WHERE + -- filter type template + lehrtyp_kurzbz = \'tpl\' + -- filter semester that were retrieved by standardisierte lvs semester for selected studiensemester + AND EXISTS ( + SELECT 1 + FROM standardisierteLvs std + WHERE std.lehrveranstaltung_template_id = lv.lehrveranstaltung_id + )'; + + if (is_array($oes)) + { + /* filter by organisationseinheit */ + $params[]= $oes; + $qry.= ' AND lv.oe_kurzbz IN ? '; + } + $qry.= ' + ) + '; + + $qry.= ' + SELECT + lv.lehrveranstaltung_id, + lv.kurzbz, + lv.lehrtyp_kurzbz, + lv.bezeichnung AS lv_bezeichnung, + lv.bezeichnung_english, + lv.studiengang_kz, + lv.semester, + lv.oe_kurzbz, + lv.ects, + lv.lehrform_kurzbz, + lv.orgform_kurzbz, + lv.sprache, + lv.aktiv, + lv.lehrveranstaltung_template_id, + lv.studienplan_id, + lv.studienplan_bezeichnung, + lv.studiensemester_kurzbz, + upper(stg.typ || stg.kurzbz) AS "stg_typ_kurzbz", + stg.bezeichnung AS "stg_bezeichnung", + stgtyp.bezeichnung AS "stg_typ_bezeichnung", + CASE + WHEN oe.organisationseinheittyp_kurzbz = \'Kompetenzfeld\' THEN (\'KF \' || oe.bezeichnung) + WHEN oe.organisationseinheittyp_kurzbz = \'Department\' THEN (\'DEP \' || oe.bezeichnung) + ELSE (oe.organisationseinheittyp_kurzbz || \' \' || oe.bezeichnung) + END AS "lv_oe_bezeichnung", + ( + -- comma seperated string of all lehreinheitgruppen + SELECT string_agg(bezeichnung, \', \') AS lehreinheitgruppe_bezeichnung + FROM( + -- distinct bezeichnung, as may come multiple times from different lehreinheiten + SELECT DISTINCT ON (studiengang_kz, bezeichnung) studiengang_kz, bezeichnung FROM + ( + -- distinct lehreinheitgruppe, as may come multiple times from different lehrform + SELECT DISTINCT ON (legr.lehreinheitgruppe_id) legr.studiengang_kz, + -- get Spezialgruppe or Lehrverbandgruppe + COALESCE( + legr.gruppe_kurzbz, + CONCAT( UPPER(stg1.typ), UPPER(stg1.kurzbz), \'-\', legr.semester, legr.verband, legr.gruppe ) + ) as bezeichnung + FROM lehre.tbl_lehreinheitgruppe legr + JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id) + JOIN lehre.tbl_lehrveranstaltung lv1 USING (lehrveranstaltung_id) + JOIN public.tbl_studiengang stg1 ON stg1.studiengang_kz = legr.studiengang_kz + WHERE lv1.lehrveranstaltung_id = lv.lehrveranstaltung_id + AND le.studiensemester_kurzbz = lv.studiensemester_kurzbz + ) AS lehreinheitgruppen + GROUP BY studiengang_kz, bezeichnung + ORDER BY studiengang_kz DESC + ) AS uniqueLehreinheitgruppen_bezeichnung + ) AS lehreinheitgruppen_bezeichnung + FROM ( + SELECT + * + FROM + standardisierteLvs + UNION + SELECT + * + FROM templateLvs + ) AS lv + JOIN public.tbl_studiengang stg ON stg.studiengang_kz = lv.studiengang_kz + JOIN public.tbl_studiengangstyp stgtyp ON stgtyp.typ = stg.typ + JOIN public.tbl_organisationseinheit oe ON oe.oe_kurzbz = lv.oe_kurzbz + ORDER BY + oe.bezeichnung, lv.semester, lv.bezeichnung + '; + + return $this->execQuery($qry, $params); + } + /** * Gets unique Groupstrings for Lehrveranstaltungen, e.g. WS2018_BIF_1_PRJM_VZ_LV12345 * @param string $studiensemester_kurzbz diff --git a/application/models/organisation/Studienjahr_model.php b/application/models/organisation/Studienjahr_model.php index 3bbe3a07f..a6e1bc575 100644 --- a/application/models/organisation/Studienjahr_model.php +++ b/application/models/organisation/Studienjahr_model.php @@ -29,4 +29,54 @@ class Studienjahr_model extends DB_Model return $this->execQuery($query); } + + /** + * Get the current Studienjahr. During the summer term, continue using the previous Studienjahr. + * + * @param int $days + * @return array|stdClass|null + */ + public function getLastOrAktStudienjahr($days = 60) + { + if (!is_numeric($days)) + { + $days = 60; + } + + $query = ' + SELECT * + FROM public.tbl_studienjahr + JOIN public.tbl_studiensemester USING (studienjahr_kurzbz) + WHERE start < NOW() - \'' . $days . ' DAYS\'::INTERVAL + ORDER by start DESC + LIMIT 1 + '; + + return $this->execQuery($query); + } + + /** + * Get the current Studienjahr. During the summer term, get the upcoming next Studienjahr. + * + * @param int $days + * @return array|stdClass|null + */ + public function getAktOrNextStudienjahr($days = 62) + { + if (!is_numeric($days)) + { + $days = 62; + } + + $query = ' + SELECT * + FROM public.tbl_studienjahr + JOIN public.tbl_studiensemester using(studienjahr_kurzbz) + WHERE start < NOW() + \'' . $days . ' DAYS\'::INTERVAL + ORDER by start DESC + LIMIT 1 + '; + + return $this->execQuery($query); + } } diff --git a/application/views/lehre/lvplanung/lvTemplateUebersicht.php b/application/views/lehre/lvplanung/lvTemplateUebersicht.php new file mode 100644 index 000000000..5c873486c --- /dev/null +++ b/application/views/lehre/lvplanung/lvTemplateUebersicht.php @@ -0,0 +1,29 @@ + 'LV Template Übersicht', + 'vue3' => true, + 'axios027' => true, + 'bootstrap5' => true, + 'tabulator5' => true, + 'fontawesome6' => true, + 'primevue3' => true, + 'navigationcomponent' => true, + 'filtercomponent' => true, + 'customJSModules' => array('public/js/apps/lehre/lvplanung/LvTemplates.js'), + 'customCSSs' => array( + 'public/css/Fhc.css', + 'public/css/lvTemplateUebersicht.css' + ) +); + +$this->load->view('templates/FHC-Header', $includesArray); +?> + +
+ + + + +
+ +load->view('templates/FHC-Footer', $includesArray); ?> diff --git a/include/tw/vilesci_menu_main.inc.php b/include/tw/vilesci_menu_main.inc.php index 2aa2e67d3..2a1ca74ae 100644 --- a/include/tw/vilesci_menu_main.inc.php +++ b/include/tw/vilesci_menu_main.inc.php @@ -70,6 +70,7 @@ $menu=array 'link'=>'left.php?categorie=Lehre', 'target'=>'nav', 'Gruppenverwaltung'=>array('name'=>'Gruppen', 'permissions'=>array('admin','lv-plan','support','lehre/gruppe'), 'link'=>'stammdaten/lvbgruppenverwaltung.php', 'target'=>'main'), 'Lehrveranstaltung'=>array('name'=>'Lehrveranstaltung', 'link'=>'lehre/lehrveranstaltung_frameset.html', 'target'=>'main'), + 'lvTemplateUebersicht'=>array('name'=>'LV-Template Übersicht', 'link'=>'../index.ci.php/lehre/lvplanung/LvTemplateUebersicht', 'target'=>'_blank'), 'Studienordnung'=>array('name'=>'Studienordnung', 'link'=>'lehre/studienordnung.php', 'target'=>'_blank','permissions'=>array('lehre/studienordnung')), 'StudienplanGueltigkeit'=>array('name'=>'Studienplan Gültigkeit', 'link'=>'lehre/studienplan_gueltigkeit.php', 'target'=>'main','permissions'=>array('lehre/studienordnung')), 'StudienplanVorruecken'=>array('name'=>'Studienplan vorrücken', 'link'=>'lehre/studienplan_vorrueckung.php', 'target'=>'main','permissions'=>array('lehre/studienplan')), diff --git a/public/css/lvTemplateUebersicht.css b/public/css/lvTemplateUebersicht.css new file mode 100644 index 000000000..af279b8cb --- /dev/null +++ b/public/css/lvTemplateUebersicht.css @@ -0,0 +1,3 @@ +html { + font-size: .75em; +} \ No newline at end of file diff --git a/public/js/apps/lehre/lvplanung/LvTemplates.js b/public/js/apps/lehre/lvplanung/LvTemplates.js new file mode 100644 index 000000000..b6ad3ae81 --- /dev/null +++ b/public/js/apps/lehre/lvplanung/LvTemplates.js @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2023 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import LvTemplateUebersicht from '../../../lehre/lvplanung/LvTemplateUebersicht.js'; +import {CoreNavigationCmpt} from '../../../components/navigation/Navigation.js'; +import FhcAlert from '../../../plugin/FhcAlert.js'; +import FhcApi from "../../../plugin/FhcApi.js"; +import Phrasen from "../../../plugin/Phrasen.js"; + + +const lvTemplatesApp = Vue.createApp({ + components: { + CoreNavigationCmpt, + LvTemplateUebersicht + } +}); + +lvTemplatesApp + .use(primevue.config.default,{zIndex: {overlay: 9999}}) + .use(FhcAlert) + .use(FhcApi) + .use(Phrasen) + .mount('#main') \ No newline at end of file diff --git a/public/js/lehre/lvplanung/LvTemplateUebersicht.js b/public/js/lehre/lvplanung/LvTemplateUebersicht.js new file mode 100644 index 000000000..be0da75b8 --- /dev/null +++ b/public/js/lehre/lvplanung/LvTemplateUebersicht.js @@ -0,0 +1,289 @@ +import {CoreFilterCmpt} from '../../components/filter/Filter.js'; +import CoreFormInput from "../../components/Form/Input.js"; + +// Fields used to restructure table data for dataTree +const idField = 'lehrveranstaltung_id'; +const parentIdField = 'lehrveranstaltung_template_id'; +const STUDIENSEMESTER_DROPDOWN_STARTDATE = '2011-01-01'; + +export default { + components: { + CoreFilterCmpt, + CoreFormInput + }, + data: function() { + return { + table: null, + studiensemester: [], + selectedStudiensemester: '', + cbDataTreeStartExpanded: false // checkbox expand dataTree or not + } + }, + computed: { + tabulatorOptions() { + const fhcValuesLookup = function(cell) { + var values = {}; + const field = cell.getField(); + const data = cell.getTable().getData(); + const collectvalues = function(rows, field) { + var values = {}; + var childvalues = {}; + for(const row of rows) { + const rowvalue = (row[field] !== null) ? row[field] : ''; + values[rowvalue] = rowvalue; + if(row['_children'] && row['_children'].length > 0) { + childvalues = collectvalues(row['_children'], field); + values = {...values, ...childvalues} + } + } + return values; + } + values = collectvalues(data, field); + const vals = Object.keys(values).sort(); + if(vals.indexOf('') === -1) { + vals.unshift(''); + } + return vals; + }; + const fhctreefilter = function(headerValue, rowValue, rowData, filterParams){ + if (rowData['_children'] && rowData['_children'].length > 0) { + for (var i in rowData['_children']) { + return rowValue == headerValue || + fhctreefilter( + headerValue, + rowData['_children'][i][filterParams.field], + rowData['_children'][i], + filterParams + ); + } + } + + return rowValue == headerValue; + }; + const self = this; + return { + // NOTE: data is set on table built to await preselected actual Studiensemester + ajaxResponse(url, params, response) { + return self.prepDataTreeData(response.data) // Prepare data for dataTree view + }, + layout: 'fitColumns', + autoResize: false, // prevent auto resizing of table + resizableColumnFit: true, //maintain the fit of columns when resizing + index: 'lehrveranstaltung_id', + selectable: true, + selectableRangeMode: 'click', + dataTree: true, + dataTreeStartExpanded: self.cbDataTreeStartExpanded, + dataTreeChildIndent: 15, //indent child rows by 15 px + persistence:{ + filter: false, //persist filter sorting + }, + columns: [ + {title: 'LV-ID', field: 'lehrveranstaltung_id', headerFilter: true, visible: false}, + {title: 'LV Kurzbz', field: 'kurzbz', headerFilter: true, visible:false, width: 70}, + {title: 'STG Kurzbz', field: 'stg_typ_kurzbz', headerFilter: "list", headerFilterParams: {valuesLookup: fhcValuesLookup}, headerFilterFunc: fhctreefilter, headerFilterFuncParams: {field: 'stg_typ_kurzbz'}, visible:true, width: 80}, + {title: 'OrgEinheit', field: 'lv_oe_bezeichnung', headerFilter: true, visible: false, width: 250}, + {title: 'Lehrtyp Kurzbz', field: 'lehrtyp_kurzbz', headerFilter: true, visible:false, width: 70}, + {title: 'Studiengangtyp', field: 'stg_typ_bezeichnung', headerFilter: "list", headerFilterParams: {valuesLookup: fhcValuesLookup}, headerFilterFunc: fhctreefilter, headerFilterFuncParams: {field: 'stg_typ_bezeichnung'}, width: 150}, + {title: 'OrgForm', field: 'orgform_kurzbz', headerFilter: "list", headerFilterParams: {valuesLookup: fhcValuesLookup}, headerFilterFunc: fhctreefilter, headerFilterFuncParams: {field: 'orgform_kurzbz'}, width: 70}, + {title: 'Semester', field: 'semester', headerFilter: true, width: 50}, + {title: 'Lehrveranstaltung', field: 'lv_bezeichnung', headerFilter: true, minWidth: 250}, + {title: 'Lehrveranstaltung ENG', field: 'bezeichnung_english', headerFilter: true, minWidth: 250}, + {title: 'ECTS', field: 'ects', headerFilter: true, width: 50, hozAlign: 'right'}, + {title: 'Lehrform', field: 'lehrform_kurzbz', headerFilter: true, width: 50}, + {title: 'Sprache', field: 'sprache', headerFilter: true, width: 100}, + {title: 'Aktiv', field: 'aktiv', width: 50, + formatter:"tickCross", + headerFilter:"tickCross", + headerFilterParams:{"tristate": true}, + hozAlign:"center", + formatterParams: { + tickElement: '', + crossElement: '' + } + }, + {title: 'Studienplan', field: 'studienplan_bezeichnung', headerFilter: true, visible:true, width: 220}, + {title: 'OE Kurzbz', field: 'lv_oe_kurzbz', headerFilter: true, visible:false, minWidth: 80}, + { + title: this.$p.t('global/aktionen'), + field: 'actions', + width: 140, + formatter: (cell, formatterParams, onRendered) => { + let container = document.createElement('div'); + container.className = "d-flex gap-2"; + + let button = document.createElement('button'); + button.className = 'btn btn-outline-secondary'; + button.innerHTML = ' ' + this.$p.t('global/verwalten'); + button.addEventListener('click', (event) => this.openAdminLvTemplate(event, cell.getRow())); + container.append(button); + + return container; + }, + frozen: true + } + ] + } + }, + urlToAdminAllTemplates() { + return FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'vilesci/lehre/lehrveranstaltung.php?stg_kz=99999&semester=-1&orgform=-1'; + } + }, + methods: { + async loadAndSetStudiensemester(){ + const result = await this.$fhcApi + .get('api/frontend/v1/organisation/Studiensemester/getAll', {start: STUDIENSEMESTER_DROPDOWN_STARTDATE}) + .then(result => this.studiensemester = result.data ) + .then(() => this.$fhcApi.get('api/frontend/v1/organisation/Studiensemester/getAktNext') ) // Get actual Studiensemester + .then(result => this.selectedStudiensemester = result.data[0].studiensemester_kurzbz ) // Preselect Studiensemester + .catch(error => this.$fhcAlert.handleSystemError(error) ); + }, + async onTableBuilt(){ + + this.table = this.$refs.lvTemplateUebersichtTable.tabulator; + + // Await Studiensemester + await this.loadAndSetStudiensemester(); + + // Set table data + this.table.setData( + this.$fhcApi.getUri() + + '/api/frontend/v1/education/Lehrveranstaltung/getTemplateLvTree' + + '?studiensemester_kurzbz=' + this.selectedStudiensemester + ); + + // Await phrases categories + await this.$p.loadCategory(['lehre']); + + // Replace column titles with phrasen + this.table.updateColumnDefinition('lv_bezeichnung', {title: this.$p.t('lehre', 'lehrveranstaltung')}); + + }, + onChangeStudiensemester(){ + // Reset table data + this.table.setData( + this.$fhcApi.getUri() + + '/api/frontend/v1/education/Lehrveranstaltung/getTemplateLvTree' + + '?studiensemester_kurzbz=' + this.selectedStudiensemester + ); + }, + openAdminLvTemplate(event, row){ + const url = FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'vilesci/lehre/lehrveranstaltung.php?stg_kz=&semester=-1&orgform=-1&lehrveranstaltung_id=' + + row.getData().lehrveranstaltung_id; + + window.open(url, '_blank').focus(); + }, + prepDataTreeData(data){ + let toDelete = []; + + // loop through all data + for (let childIdx = 0; childIdx < data.length; childIdx++) + { + let child = data[childIdx]; + + // if it has parent id, it is a child + if (child[parentIdField]) + { + // append the child on the right place. If parent found, mark original sw child on 0 level for deleting + if (this._appendChild(data, child)) toDelete.push(childIdx); + } + } + + // delete the marked children from 0 level + for (let counter = 0; counter < toDelete.length; counter++) + { + // decrease index by counter as index of data array changes after every deletion + data.splice(toDelete[counter] - counter, 1); + } + + return data; + }, + _appendChild(data, child) { + // get parent id + let parentId = child[parentIdField]; + + // loop thorugh all data + for (let parentIdx = 0; parentIdx < data.length; parentIdx++) + { + let parent = data[parentIdx]; + + // if it's the parent + if (parent[idField] == parentId) + { + // create children array if not done yet + if (!parent._children) parent._children = []; + + // if child is not included in children array, append the child + if (!parent._children.includes(child)) parent._children.push(child); + + // parent found + return true; + } + // search children for parents + else if (parent._children) this._appendChild(parent._children, child); + } + + // parent not found + return false; + }, + reloadTabulator() { + if (this.table !== null && this.table !== undefined) + { + for (let option in this.tabulatorOptions) + { + if (this.table.options.hasOwnProperty(option)) + this.table.options[option] = this.tabulatorOptions[option]; + } + this.$refs.lvTemplateUebersichtTable.reloadTable(); + } + }, + }, + template: ` +
+
+
{{ $p.t('lehre/lvTemplatesUebersicht') }}
+
+ + + +
+
+
+
+ + + +
+
+ +
+` +}; diff --git a/system/filtersupdate.php b/system/filtersupdate.php index e885b4e14..51dd86314 100644 --- a/system/filtersupdate.php +++ b/system/filtersupdate.php @@ -1298,6 +1298,7 @@ $filters = array( {"name": "os"}, {"name": "lizenzserver_kurzbz"}, {"name": "lizenzserver_port"}, + {"name": "anzahl_lizenzen"}, {"name": "softwarestatus_kurzbz"} ], "filters": [] diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 82e90d1da..16631efa8 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -29198,6 +29198,446 @@ array( ) ), // Betriebsmittel end + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'softwareanforderung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Softwareanforderung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Software Request', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'softwareanforderungSubtitle', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Softwareanforderung und Lizenzmanagement für die Lehre', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Software Request and License management for Education', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'swAnforderungenUndLizenen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Softwareanforderungen & Lizenzen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Software Requirements & Licenses', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'anforderungNachSw', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anforderung nach Software', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Request by Software', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'anforderungNachLv', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anforderung nach LV', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Request by Course', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'swNichtGefundenHierBestellen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Software nicht gefunden?
Hier bei IT-Services bestellen", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Software not found?
Order here from IT Services", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'bereitsAngefordert', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Bereits angefordert", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Requested already", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'mindEineZuorndungExistiertBereits', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Mindestens eine Zuordnung existiert bereits.", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "At least one assignment already exists.", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'swFuerLvAnfordern', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Software für LV anfordern", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Request software for courses", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'swWurdeBereitsAngefordert', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Software wurde bereits angefordert", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Software has already been requested", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'swAnforderungUeberAuswahlVonSw', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Softwareanforderung über die Auswahl von Software", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Software Requirements based on the Selection of Software", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'swAnforderungUeberAuswahlVonLvs', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Softwareanforderung über die Auswahl von Lehrveranstaltungen", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Software Requirements based on the Selection of Courses", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'lizenzAnzahlNeu', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Lizenz-Anzahl NEU", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "License Number NEW", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'lizenzanzahlAendern', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Lizenzanzahl ändern", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Change Number of Licenses", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'global', + 'phrase' => 'eingabeFehlt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Eingabe fehlt", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Input missing", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'global', + 'phrase' => 'unveraendert', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Unverändert", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Unchanged", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'anforderungenVorruecken', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Anforderungen vorrücken", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Take over Requirements", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'vorrueckenInStudiensemester', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Vorrücken in Studiensemester", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Take over to semester", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'softwarebereitstellung', + 'category' => 'global', + 'phrase' => 'anteiligInProzent', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Anteilig in %", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Percentage share", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'global', + 'phrase' => 'verwalten', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Verwalten", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Administrate", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'lvTemplatesVerwalten', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "LV Templates verwalten", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Administrate Course Templates", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'lvTemplatesUebersicht', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "LV Templates Übersicht", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Course Templates Overview", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), //**************************** CORE/konto array( 'app' => 'core',