From 2ffaf3627013703b08c6ccc955ab206b0d45c0ef Mon Sep 17 00:00:00 2001 From: ma0048 Date: Tue, 15 Sep 2026 15:46:48 +0200 Subject: [PATCH 1/4] suche fuer lehreinheiten und lehrveranstaltungen in der lvverwaltung --- application/config/routes.php | 3 + .../api/frontend/v1/Lehrveranstaltung.php | 43 ++++ application/libraries/SearchBarLib.php | 58 +++++- .../models/education/Lehreinheit_model.php | 185 ++++++++++++++---- public/js/apps/LVVerwaltung.js | 2 + .../components/LVVerwaltung/LVVerwaltung.js | 68 ++++++- .../js/components/LVVerwaltung/Setup/Table.js | 62 +++++- .../searchbar/result/teachingunit.js | 45 +++++ public/js/components/searchbar/searchbar.js | 2 + 9 files changed, 412 insertions(+), 56 deletions(-) create mode 100644 public/js/components/searchbar/result/teachingunit.js diff --git a/application/config/routes.php b/application/config/routes.php index bb0b0f65e..733dcf8b8 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -136,6 +136,9 @@ $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)'] = ' $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)/stg/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByEmp/$1/$2/$3'; $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)/stg/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByEmp/$1/$2/$3'; +$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/le/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByLe/$1/$2'; +$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/lv/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByLv/$1/$2'; + // load routes from extensions, also look for environment-specific configs $subdirs = ['application/config/extensions', 'application/config/' . ENVIRONMENT . '/extensions']; diff --git a/application/controllers/api/frontend/v1/Lehrveranstaltung.php b/application/controllers/api/frontend/v1/Lehrveranstaltung.php index 78e0acce3..80e299f3c 100644 --- a/application/controllers/api/frontend/v1/Lehrveranstaltung.php +++ b/application/controllers/api/frontend/v1/Lehrveranstaltung.php @@ -28,6 +28,8 @@ class Lehrveranstaltung extends FHCAPI_Controller { parent::__construct([ 'getByEmp' => ['admin:r', 'assistenz:r'], + 'getByLe' => ['admin:r', 'assistenz:r'], + 'getByLv' => ['admin:r', 'assistenz:r'], 'getByStg' => ['admin:r', 'assistenz:r'], 'loadByLV' => ['admin:r', 'assistenz:r'], ]); @@ -77,6 +79,47 @@ class Lehrveranstaltung extends FHCAPI_Controller $this->terminateWithSuccess($tree); } + + public function getByLe($studiensemester_kurzbz = null, $lehreinheit_id = null) + { + if (is_null($lehreinheit_id)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); + + + $lehreinheiten = $this->_ci->LehreinheitModel->getByLeStudiensemester($lehreinheit_id, $studiensemester_kurzbz); + $lehreinheiten_data = $this->getDataOrTerminateWithError($lehreinheiten); + + $this->terminateWithSuccess($lehreinheiten_data); + } + + public function getByLv($studiensemester_kurzbz = null, $lehrveranstaltung_id = null) + { + if (is_null($lehrveranstaltung_id)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); + + $tree = []; + + $lehrveranstaltungen = $this->_ci->LehreinheitModel->getLvsById($lehrveranstaltung_id, $studiensemester_kurzbz); + $lehrveranstaltungen_data = $this->getDataOrTerminateWithError($lehrveranstaltungen); + + foreach ($lehrveranstaltungen_data as $lehrveranstaltung) + { + $lehreinheiten = $this->_ci->LehreinheitModel->getByLvidStudiensemester($lehrveranstaltung->lehrveranstaltung_id, $studiensemester_kurzbz); + $lehreinheiten_data = $this->getDataOrTerminateWithError($lehreinheiten); + + if (!isset($lehrveranstaltung->_children)) + { + $lehrveranstaltung->_children = $lehreinheiten_data; + } + $tree[] = $lehrveranstaltung; + } + + $this->terminateWithSuccess($tree); + } public function getByStg($studiensemester_kurzbz = null, $stg_kz = null, $semester = null) { $studiengang_kz = $this->getStudiengangKz($stg_kz); diff --git a/application/libraries/SearchBarLib.php b/application/libraries/SearchBarLib.php index f19365c02..12718b9d6 100644 --- a/application/libraries/SearchBarLib.php +++ b/application/libraries/SearchBarLib.php @@ -33,7 +33,7 @@ class SearchBarLib const ERROR_NOT_AUTH = 'ERR005'; // List of allowed types of search - const ALLOWED_TYPES = ['mitarbeiter', 'mitarbeiter_ohne_zuordnung', 'organisationunit', 'raum', 'person', 'student','studentStv', 'prestudent', 'document', 'cms']; + const ALLOWED_TYPES = ['mitarbeiter', 'mitarbeiter_ohne_zuordnung', 'organisationunit', 'raum', 'person', 'student','studentStv', 'prestudent', 'document', 'cms', 'lehreinheit', 'lehrveranstaltung']; const PHOTO_IMG_URL = '/cis/public/bild.php?src=person&person_id='; @@ -596,5 +596,61 @@ EOSC; // Otherwise return an empty array return array(); } + + + private function _lehreinheit($searchstr, $type) + { + $dbModel = new DB_Model(); + + $lehreinheit = $dbModel->execReadOnlyQuery(' + SELECT + \'teachingunit\' AS renderer, + \''.$type.'\' AS type, + lehreinheit_id as id, + studiensemester_kurzbz, + lv_bezeichnung as bezeichnung, + \'lv_table_icon icon-\' as foto, + UPPER(CONCAT(vw_lehreinheit.stg_typ, vw_lehreinheit.stg_kurzbz)) as studiengang + FROM campus.vw_lehreinheit + JOIN public.tbl_studiensemester USING(studiensemester_kurzbz) + WHERE cast(lehreinheit_id as text) ILIKE \'%'.$dbModel->escapeLIKE($searchstr).'%\' + OR lv_bezeichnung ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\' + ORDER BY start DESC, lv_bezeichnung + '); + + // If something has been found then return it + if (hasData($lehreinheit)) return getData($lehreinheit); + + // Otherwise return an empty array + return array(); + } + + private function _lehrveranstaltung($searchstr, $type) + { + $dbModel = new DB_Model(); + + $lehreinheit = $dbModel->execReadOnlyQuery(' + + SELECT + \'teachingunit\' AS renderer, + \''.$type.'\' AS type, + lehrveranstaltung_id as id, + tbl_lehrveranstaltung.bezeichnung, + \'lv_table_icon icon-lv\' as foto, + UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as studiengang + FROM lehre.tbl_lehrveranstaltung + JOIN public.tbl_studiengang USING(studiengang_kz) + WHERE cast(lehrveranstaltung_id as text) ILIKE \'%'.$dbModel->escapeLIKE($searchstr).'%\' + OR tbl_lehrveranstaltung.bezeichnung ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\' + ORDER BY lehrveranstaltung_id DESC + '); + + // If something has been found then return it + if (hasData($lehreinheit)) return getData($lehreinheit); + + // Otherwise return an empty array + return array(); + } + } diff --git a/application/models/education/Lehreinheit_model.php b/application/models/education/Lehreinheit_model.php index 808c4e443..8cc701da2 100644 --- a/application/models/education/Lehreinheit_model.php +++ b/application/models/education/Lehreinheit_model.php @@ -340,6 +340,20 @@ EOSQL; } + public function getByLeStudiensemester($lehreinheit_id, $studiensemester_kurzbz) + { + $qry = "WITH lehreinheiten AS ( + SELECT * + FROM lehre.tbl_lehreinheit + WHERE lehreinheit_id = ? + AND studiensemester_kurzbz = ? + )," . $this->_getLeCTE(); + + $params = array($lehreinheit_id, $studiensemester_kurzbz); + + return $this->execReadOnlyQuery($qry, $params); + + } public function getByLvidStudiensemester($lv_id, $studiensemester_kurzbz, $mitarbeiter_uid = null, $fachbereich_kurzbz = null) { $qry = "WITH lehreinheiten AS ( @@ -347,35 +361,7 @@ EOSQL; FROM lehre.tbl_lehreinheit WHERE lehrveranstaltung_id = ? AND studiensemester_kurzbz = ? - ), - ". $this->_getGruppenCTE() . ", - ". $this->_getLektorenCTE() . ", - ". $this->_getFachbereichCTE() . ", - ". $this->_getTagsCTE() . " - - SELECT lehreinheiten.*, - lehreinheiten.lehrform_kurzbz as lv_lehrform_kurzbz, - tbl_lehrveranstaltung.kurzbz as lv_kurzbz, - tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung, - COALESCE(tag_data_agg.tags, '[]'::json) AS tags, - gruppen.gruppen, - mitarbeiter.lektoren, - mitarbeiter.le_planstunden, - mitarbeiter.vorname, - mitarbeiter.nachname, - mitarbeiter.semesterstunden, - fachbereich.bezeichnung as fachbereich, - UPPER(CONCAT(tbl_studiengang.typ,tbl_studiengang.kurzbz)) as studiengang, - semester - FROM lehreinheiten - LEFT JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = lehreinheiten.lehrfach_id - LEFT JOIN public.tbl_studiengang USING(studiengang_kz) - LEFT JOIN tag_data_agg ON tag_data_agg.lehreinheit_id = lehreinheiten.lehreinheit_id - LEFT JOIN mitarbeiter ON lehreinheiten.lehreinheit_id = mitarbeiter.lehreinheit_id - LEFT JOIN fachbereich ON lehreinheiten.lehreinheit_id = fachbereich.lehreinheit_id - LEFT JOIN gruppen ON lehreinheiten.lehreinheit_id = gruppen.lehreinheit_id - WHERE true - "; + )," . $this->_getLeCTE(); $params = array($lv_id, $studiensemester_kurzbz); @@ -395,9 +381,83 @@ EOSQL; return $this->execReadOnlyQuery($qry, $params); } - private function getLVTmp($stg_kz = null) + public function getOffeneStunden($lehreinheit_ids) { - $qry = "SELECT DISTINCT ON(lehrveranstaltung_id) *, + if (!is_array($lehreinheit_ids)) + $lehreinheit_ids = [$lehreinheit_ids]; + + if (empty($lehreinheit_ids)) + return success([]); + + $db = new DB_Model(); + $placeholders = implode(',', array_fill(0, count($lehreinheit_ids), '?')); + + $qry = "WITH eventdauer AS ( + SELECT + tbl_kalender_lehreinheit.lehreinheit_id, + tbl_kalender.von, + tbl_kalender.bis, + tbl_kalender.kalender_id + FROM lehre.tbl_lehreinheit + JOIN lehre.tbl_kalender_lehreinheit USING(lehreinheit_id) + JOIN lehre.tbl_kalender USING(kalender_id) + WHERE tbl_lehreinheit.lehreinheit_id IN ($placeholders) + AND NOT EXISTS (SELECT 1 FROM lehre.tbl_kalender nachfolger + WHERE nachfolger.vorgaenger_kalender_id = tbl_kalender.kalender_id) + AND tbl_kalender.status_kurzbz NOT IN ('deleted', 'todelete', 'archived') + GROUP BY tbl_kalender_lehreinheit.lehreinheit_id, tbl_kalender.von, tbl_kalender.bis, tbl_kalender.kalender_id + ), + raster AS ( + SELECT + eventdauer.lehreinheit_id, + eventdauer.kalender_id, + eventdauer.von, + eventdauer.bis, + vonstunde.stunde AS vonstunde_nr, + bisstunde.stunde AS bisstunde_nr + FROM eventdauer + LEFT JOIN lehre.tbl_stunde vonstunde ON eventdauer.von::time = vonstunde.beginn + LEFT JOIN lehre.tbl_stunde bisstunde ON eventdauer.bis::time = bisstunde.ende + ), + unterrichtsdauer AS ( + SELECT + raster.lehreinheit_id, + raster.kalender_id, + sum(EXTRACT(EPOCH FROM (LEAST(stunde.ende, raster.bis::time) - GREATEST(stunde.beginn, raster.von::time))) / 60) AS minuten + FROM raster JOIN lehre.tbl_stunde stunde ON stunde.beginn < raster.bis::time AND stunde.ende > raster.von::time + WHERE raster.vonstunde_nr IS NULL OR raster.bisstunde_nr IS NULL + GROUP BY raster.lehreinheit_id, raster.kalender_id + ), + verplantestunden AS ( + SELECT raster.lehreinheit_id, raster.kalender_id, + CASE + WHEN raster.vonstunde_nr IS NOT NULL AND raster.bisstunde_nr IS NOT NULL + THEN (raster.bisstunde_nr - raster.vonstunde_nr + 1) + ELSE coalesce(unterrichtsdauer.minuten, 0) / 45 + END AS verplantestunde + FROM raster LEFT JOIN unterrichtsdauer ON unterrichtsdauer.lehreinheit_id = raster.lehreinheit_id AND unterrichtsdauer.kalender_id = raster.kalender_id + ), + verplant_gesamt AS ( + SELECT lehreinheit_id, SUM(verplantestunde) AS verplant + FROM verplantestunden + GROUP BY lehreinheit_id + ) + SELECT + lema.lehreinheit_id, + lema.mitarbeiter_uid, + lema.planstunden, + ROUND(coalesce(verplant_gesamt.verplant, 0), 2) AS verplant, + ROUND((lema.planstunden - coalesce(verplant_gesamt.verplant, 0)), 2) AS offenestunden + FROM lehre.tbl_lehreinheitmitarbeiter lema + LEFT JOIN verplant_gesamt ON verplant_gesamt.lehreinheit_id = lema.lehreinheit_id + WHERE lema.lehreinheit_id IN ($placeholders)"; + + return $db->execReadOnlyQuery($qry, array_merge($lehreinheit_ids, $lehreinheit_ids)); + } + + private function getLVTmp() + { + return "SELECT DISTINCT ON(lehrveranstaltung_id) *, '' as stundenblockung, '' as lehreinheit_id, '' as wochenrythmus, @@ -408,24 +468,33 @@ EOSQL; '' as studienplan_beeichnung, UPPER(CONCAT(vw_lehreinheit.stg_typ, vw_lehreinheit.stg_kurzbz)) as studiengang FROM campus.vw_lehreinheit - WHERE mitarbeiter_uid = ? - AND studiensemester_kurzbz = ?"; - - if (!is_null($stg_kz)) { - $qry .= " AND lv_studiengang_kz = ?"; - } - - return $qry; + WHERE studiensemester_kurzbz = ? "; } - public function getLvsByEmployee($mitarbeiter_uid, $studiensemester_kurzbz, $stg_kz = null) + public function getLvsById($lehrveranstaltung_id, $studiensemester_kurzbz) { - $qry = "WITH lvs AS (" . $this->getLVTmp($stg_kz) . ") + $where = ' AND lehrveranstaltung_id = ?'; + $qry = "WITH lvs AS (" . $this->getLVTmp() . $where .") SELECT lvs.* FROM lvs - "; + "; + + $params = array($studiensemester_kurzbz, $lehrveranstaltung_id); + return $this->execReadOnlyQuery($qry, $params); + } + public function getLvsByEmployee($mitarbeiter_uid, $studiensemester_kurzbz, $stg_kz = null) + { + $where = ' AND mitarbeiter_uid = ?'; + + if ($stg_kz != null) + $where .= " AND lv_studiengang_kz = ?"; + + $qry = "WITH lvs AS (" . $this->getLVTmp() . $where .") + SELECT lvs.* + FROM lvs"; + + $params = array($studiensemester_kurzbz, $mitarbeiter_uid); - $params = array($mitarbeiter_uid, $studiensemester_kurzbz); if (!is_null($stg_kz)) { $params[] = $stg_kz; @@ -632,6 +701,36 @@ EOSQL; return success('Contract successfully updated.'); } + private function _getLeCTE() + { + return $this->_getGruppenCTE() . ", + ". $this->_getLektorenCTE() . ", + ". $this->_getFachbereichCTE() . ", + ". $this->_getTagsCTE() . " + + SELECT lehreinheiten.*, + lehreinheiten.lehrform_kurzbz as lv_lehrform_kurzbz, + tbl_lehrveranstaltung.kurzbz as lv_kurzbz, + tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung, + COALESCE(tag_data_agg.tags, '[]'::json) AS tags, + gruppen.gruppen, + mitarbeiter.lektoren, + mitarbeiter.le_planstunden, + mitarbeiter.vorname, + mitarbeiter.nachname, + mitarbeiter.semesterstunden, + fachbereich.bezeichnung as fachbereich, + UPPER(CONCAT(tbl_studiengang.typ,tbl_studiengang.kurzbz)) as studiengang, + semester + FROM lehreinheiten + LEFT JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = lehreinheiten.lehrfach_id + LEFT JOIN public.tbl_studiengang USING(studiengang_kz) + LEFT JOIN tag_data_agg ON tag_data_agg.lehreinheit_id = lehreinheiten.lehreinheit_id + LEFT JOIN mitarbeiter ON lehreinheiten.lehreinheit_id = mitarbeiter.lehreinheit_id + LEFT JOIN fachbereich ON lehreinheiten.lehreinheit_id = fachbereich.lehreinheit_id + LEFT JOIN gruppen ON lehreinheiten.lehreinheit_id = gruppen.lehreinheit_id + WHERE true"; + } private function _getGruppenCTE() { return "gruppen AS ( diff --git a/public/js/apps/LVVerwaltung.js b/public/js/apps/LVVerwaltung.js index b9ab52ef6..39c9a94ad 100644 --- a/public/js/apps/LVVerwaltung.js +++ b/public/js/apps/LVVerwaltung.js @@ -17,6 +17,8 @@ const router = VueRouter.createRouter({ component: LVVerwaltung, children: [ { name: 'emp', path: 'emp/:emp/:treemenu(.*)*', component: LVVerwaltung }, + { name: 'le', path: 'le/:le', component: LVVerwaltung }, + { name: 'lv', path: 'lv/:lv', component: LVVerwaltung }, { name: 'treemenu', path: ':treemenu(.*)*', component: LVVerwaltung } ] }, diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index 6d9331d38..0034ac452 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -61,6 +61,7 @@ export default { selected: [], studiengang_kz: null, selectedStudiensemester: this.defaultSemester, + preselected: null, dropdowns: { studiensemester_array: [], sprachen_array: [], @@ -73,7 +74,9 @@ export default { calcheightonly: true, types: [ "mitarbeiter", - "mitarbeiter_ohne_zuordnung" + "mitarbeiter_ohne_zuordnung", + "lehreinheit", + "lehrveranstaltung", ], actions: { employee: { @@ -86,6 +89,26 @@ export default { childactions: [ ] }, + lehreinheit: { + defaultaction: { + type: "function", + action: (data) => { + this.onSelectLehreinheit(data); + } + }, + childactions: [ + ] + }, + lehrveranstaltung: { + defaultaction: { + type: "function", + action: (data) => { + this.onSelectLehrveranstaltung(data); + } + }, + childactions: [ + ] + } } }, } @@ -94,6 +117,8 @@ export default { filter() { let filter = { emp: this.$route.params.emp, + le: this.$route.params.le, + lv: this.$route.params.lv, studiensemester_kurzbz: this.selectedStudiensemester }; let index; @@ -105,7 +130,7 @@ export default { if (index > -1) filter.semester = this.$route.params.treemenu[index+1]; } - filter.activeFilter = filter.emp ? 'employee' : filter.stg ? 'verband' : null; + filter.activeFilter = filter.emp ? 'employee' : filter.stg ? 'verband' : filter.le ? 'lehreinheit' : filter.lv ? 'lehrveranstaltung' : null; return filter; }, emp() { @@ -203,20 +228,50 @@ export default { }, onSelectEmployee(emp) { + let params = { ...this.$route.params } + delete params.le; + delete params.lv; + this.$router.push({ name: 'emp', params: { - ...this.$route.params, + ...params, emp } }); }, + onSelectLehreinheit(lehreinheit) + { + this.$router.push({ + name: 'le', + params: { + le: lehreinheit.id, + stdsem: lehreinheit.studiensemester_kurzbz.toLowerCase() + } + }); + }, + + onSelectLehrveranstaltung(lehrveranstaltung) + { + this.$router.push({ + name: 'lv', + params: { + lv: lehrveranstaltung.id, + stdsem: lehrveranstaltung?.studiensemester_kurzbz?.toLowerCase() ?? this.selectedStudiensemester.toLowerCase() + } + }); + }, onSelectVerband({ path: link, stg_kz }) { + + let params = { ...this.$route.params } + delete params.le; + delete params.lv; + this.$router.push({ name: this.$route.name == 'emp' ? 'emp' : 'treemenu', params: { - ...this.$route.params, + ...params, treemenu: link.split('/') } }); @@ -295,7 +350,7 @@ export default { } }, created() { - if (!this.$route.params.stdsem) { + if (this.$route.name !== 'le' && this.$route.name !== 'lv' && !this.$route.params.stdsem) { this.$router.replace({ name: 'stdsem', params: { @@ -477,7 +532,8 @@ export default {