diff --git a/application/config/tempus.php b/application/config/tempus.php index 58dccebdd..e3e18b954 100644 --- a/application/config/tempus.php +++ b/application/config/tempus.php @@ -1,3 +1,5 @@ self::PERM_LOGGED, + 'getCalendarHours' => self::PERM_LOGGED, 'getPlan' => self::PERM_LOGGED, 'getPlanByOrt' => self::PERM_LOGGED, 'getRaumvorschlag' => self::PERM_LOGGED, @@ -38,9 +39,11 @@ class Kalender extends FHCAPI_Controller $this->_ci->load->library('LogLib'); $this->_ci->load->library('form_validation'); $this->_ci->load->library('KalenderLib'); + $this->_ci->load->library('RaumvorschlagLib'); $this->loadPhrases([ 'ui' ]); + $this->_ci->load->config('tempus'); $this->_ci->loglib->setConfigs(array( @@ -71,6 +74,17 @@ class Kalender extends FHCAPI_Controller $this->terminateWithSuccess($stunden); } + public function getCalendarHours() + { + $calender_start = $this->_ci->config->item('calendar_start') ?? 7; + $calender_end = $this->_ci->config->item('calendar_end') ?? 23; + + $this->terminateWithSuccess(array( + 'start' => $calender_start, + 'end' => $calender_end + )); + } + public function getPlan() { $this->_ci->form_validation->set_data($_GET); @@ -206,7 +220,7 @@ class Kalender extends FHCAPI_Controller $result = $this->_ci->kalenderlib->updateKalenderEvent($kalender_id, $updateFields->ort_kurzbz ?? null, $updateFields->start_time ?? null, $updateFields->end_time ?? null); if (isError($result)) - $this->terminateWithError(getError($result)); + $this->terminateWithError(getError($result), $result->code); $this->terminateWithSuccess(getData($result)); } @@ -214,39 +228,9 @@ class Kalender extends FHCAPI_Controller public function getRaumvorschlag() { $this->_ci->form_validation->set_data($_GET); - $this->_ci->form_validation->set_rules('start_date',"start_date","required"); - $this->_ci->form_validation->set_rules('end_date',"end_date","required"); - - if($this->_ci->form_validation->run() === FALSE) - $this->terminateWithValidationErrors($this->_ci->form_validation->error_array()); - - $start_date = $this->_ci->input->get('start_date', TRUE); - $end_date = $this->_ci->input->get('end_date', TRUE); $filter = $this->_checkFilter(self::ALLOWED_ROOM_FILTER); - - if (isset($filter->lehreinheit_id)) - { - $result = $this->_ci->kalenderlib->getRaumvorschlagByLehreinheitID( - $start_date, - $end_date, - $filter->lehreinheit_id - ); - } - - if (isset($filter->kalender_id)) - { - $result = $this->_ci->kalenderlib->getRaumvorschlagByKalenderID( - $start_date, - $end_date, - $filter->kalender_id - ); - } - - if (isError($result)) - $this->terminateWithError(getError($result)); - - $this->terminateWithSuccess(getData($result)); + $this->terminateWithSuccess($this->_ci->raumvorschlaglib->getVorschlaege($filter->kalender_id)); } public function getHistory() @@ -282,13 +266,13 @@ class Kalender extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result)); - $this->terminateWithSuccess($result); + $this->terminateWithSuccess(getData($result)); } public function sync() { $result = $this->_ci->kalenderlib->sync(); - $this->terminateWithSuccess($result); + $this->terminateWithSuccess(getData($result)); } public function syncToLecturer() { @@ -305,7 +289,7 @@ class Kalender extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result)); - $this->terminateWithSuccess($result); + $this->terminateWithSuccess(getData($result)); } public function syncToStudent() { @@ -322,7 +306,7 @@ class Kalender extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result)); - $this->terminateWithSuccess($result); + $this->terminateWithSuccess(getData($result)); } public function addKalenderEvent() { @@ -345,7 +329,7 @@ class Kalender extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result)); - $this->terminateWithSuccess('Erfolgreich'); + $this->terminateWithSuccess(getData($result)); } public function addReservierung() @@ -372,7 +356,7 @@ class Kalender extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result)); - $this->terminateWithSuccess('Erfolgreich'); + $this->terminateWithSuccess(getData($result)); } private function _checkFilter($filters) diff --git a/application/controllers/api/frontend/v1/tempus/Reservierung.php b/application/controllers/api/frontend/v1/tempus/Reservierung.php index c7ea79140..c1030d8ee 100644 --- a/application/controllers/api/frontend/v1/tempus/Reservierung.php +++ b/application/controllers/api/frontend/v1/tempus/Reservierung.php @@ -65,7 +65,7 @@ class Reservierung extends FHCAPI_Controller $studiengaenge = $this->_ci->StudiengangModel->loadWhere(array('aktiv' => true)); if (isError($studiengaenge)) - $this->terminateWithError($studiengaenge); + $this->terminateWithError(getError($studiengaenge)); $language = getUserLanguage() == 'German' ? 0 : 1; $this->_ci->KalenderEventRolleModel->addOrder('sort'); $this->_ci->KalenderEventRolleModel->addSelect('rolle_kurzbz, array_to_json(bezeichnung_mehrsprachig::varchar[])->>'. $language. ' as bezeichnung'); @@ -193,7 +193,7 @@ class Reservierung extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result)); - $this->terminateWithSuccess('Erfolgreich'); + $this->terminateWithSuccess($result); } diff --git a/application/libraries/CollisionChecker.php b/application/libraries/CollisionChecker.php index ec87c95ef..b7921a6d9 100644 --- a/application/libraries/CollisionChecker.php +++ b/application/libraries/CollisionChecker.php @@ -40,7 +40,7 @@ class CollisionChecker if (!empty($result)) { - $errors[] = $result; + $errors = array_merge($errors, $result); } } diff --git a/application/libraries/KalenderLib.php b/application/libraries/KalenderLib.php index 9b52cb28a..3b5c60e01 100644 --- a/application/libraries/KalenderLib.php +++ b/application/libraries/KalenderLib.php @@ -22,6 +22,7 @@ class KalenderLib $this->_ci->load->model('organisation/Lehrverband_model', 'LehrverbandModel'); $this->_ci->load->library('CollisionChecker'); + $this->_ci->load->library('PhrasesLib', array('ui')); } @@ -60,7 +61,27 @@ class KalenderLib teilperson.vorname as teilnehmer_vorname, teilperson.nachname as teilnehmer_nachname, - teilbenutzer.uid as teilnehmer_uid'); + teilbenutzer.uid as teilnehmer_uid, + + CASE + WHEN tbl_lehreinheitgruppe.gruppe_kurzbz IS NULL THEN + COALESCE( + UPPER(le_studiengang.typ || le_studiengang.kurzbz) || + COALESCE(tbl_lehreinheitgruppe.semester::varchar, \'\') || + COALESCE(tbl_lehreinheitgruppe.verband::varchar, \'\') || + COALESCE(tbl_lehreinheitgruppe.gruppe, \'\'), + \'\') + ELSE tbl_lehreinheitgruppe.gruppe_kurzbz + END AS lehreinheit_gruppe_bezeichnung, + tbl_lehreinheitgruppe.gruppe_kurzbz as le_gruppe_kurzbz, + tbl_lehreinheitgruppe.studiengang_kz as le_studiengang_kz, + tbl_lehreinheitgruppe.semester as le_semester, + tbl_lehreinheitgruppe.verband as le_verband, + tbl_lehreinheitgruppe.gruppe as le_gruppe, + le_gruppe.direktinskription as le_direktinskription, + + + '); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit', 'tbl_kalender.kalender_id = tbl_kalender_lehreinheit.kalender_id', 'LEFT'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event', 'tbl_kalender.kalender_id = tbl_kalender_event.kalender_id', 'LEFT'); @@ -95,11 +116,22 @@ class KalenderLib $this->_ci->KalenderModel->addJoin('public.tbl_benutzer resevierung_benutzer', 'reservierung_ma.mitarbeiter_uid = resevierung_benutzer.uid', 'LEFT'); $this->_ci->KalenderModel->addJoin('public.tbl_person reservierung_person', 'reservierung_person.person_id = resevierung_benutzer.person_id', 'LEFT'); + $this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitgruppe', 'tbl_lehreinheit.lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id', 'LEFT'); + $this->_ci->KalenderModel->addJoin('public.tbl_gruppe le_gruppe', 'tbl_lehreinheitgruppe.gruppe_kurzbz = le_gruppe.gruppe_kurzbz', 'LEFT'); + $this->_ci->KalenderModel->addJoin('public.tbl_lehrverband le_lehrverband', + 'tbl_lehreinheitgruppe.studiengang_kz = le_lehrverband.studiengang_kz + AND tbl_lehreinheitgruppe.semester = le_lehrverband.semester + AND TRIM(COALESCE(tbl_lehreinheitgruppe.verband::text, \'\')) = TRIM(le_lehrverband.verband::text) + AND TRIM(COALESCE(tbl_lehreinheitgruppe.gruppe::text, \'\')) = TRIM(le_lehrverband.gruppe::text)', + 'LEFT' + ); + $this->_ci->KalenderModel->addJoin('public.tbl_studiengang le_studiengang', 'le_lehrverband.studiengang_kz = le_studiengang.studiengang_kz', 'LEFT'); + $this->_ci->KalenderModel->db->where('tbl_kalender.von >=', $start_date); $this->_ci->KalenderModel->db->where('tbl_kalender.bis <', $end_date); } - private function _mapEvents($data) + private function _mapEvents($data, $collisionCheck = true) { $stundenplan_data = []; @@ -182,6 +214,22 @@ class KalenderLib } } + if ($row->lehreinheit_gruppe_bezeichnung) + { + if (!in_array($row->lehreinheit_gruppe_bezeichnung, array_column($events[$id]->gruppe, 'bezeichnung'))) + { + $events[$id]->gruppe[] = [ + 'bezeichnung' => $row->lehreinheit_gruppe_bezeichnung, + 'gruppe_kurzbz' => $row->le_gruppe_kurzbz, + 'studiengang_kz' => $row->le_studiengang_kz, + 'semester' => $row->le_semester, + 'verband' => $row->le_verband, + 'gruppe' => $row->le_gruppe, + 'direktinskription' => $row->le_direktinskription, + ]; + } + } + if ($row->teilnehmer_uid) { if (!in_array($row->teilnehmer_uid, array_column($events[$id]->teilnehmer_person, 'uid'))) @@ -196,13 +244,16 @@ class KalenderLib } } - $kalender_ids = array_keys($events); - $collisions = $this->_ci->collisionchecker->runAll($kalender_ids); - - foreach ($collisions as $kalender_id => $errors) + if ($collisionCheck) { - if (isset($events[$kalender_id])) - $events[$kalender_id]->collisions = !empty($errors); + $kalender_ids = array_keys($events); + $collisions = $this->_ci->collisionchecker->runAll($kalender_ids); + + foreach ($collisions as $kalender_id => $errors) + { + if (isset($events[$kalender_id])) + $events[$kalender_id]->collisions = !empty($errors); + } } return array_values($events); @@ -217,66 +268,65 @@ class KalenderLib return $this->_mapEvents($data); } - public function getRaumvorschlagByLehreinheitID($start_date, $end_date, $lehreinheit_id) + + + public function getForRaumvorschlag($start_date, $end_date, $lektor_uids = [], $gruppen_kurzbz = [], $lehrverband_gruppen = []) { + $start_date = date('Y-m-d', strtotime($start_date)); $end_date = date('Y-m-d', strtotime($end_date . ' +1 day')); - $lehreinheit = $this->_ci->LehreinheitModel->load(array('lehreinheit_id' => $lehreinheit_id)); - if (isError($lehreinheit)) - return $lehreinheit; + $this->_getBasePlan($start_date, $end_date); - if (!hasData($lehreinheit)) - return error("Not found"); + $this->_ci->KalenderModel->db->where('NOT EXISTS ( + SELECT 1 FROM lehre.tbl_kalender nachfolger + WHERE nachfolger.vorgaenger_kalender_id = tbl_kalender.kalender_id)', null, false); - $lehreinheit = getData($lehreinheit)[0]; + $this->_ci->KalenderModel->db->where_not_in('status_kurzbz', ['deleted']); - $this->_ci->KalenderModel->addDistinct('ort_kurzbz'); - $this->_ci->KalenderModel->addSelect('ort_kurzbz'); - $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'kalender_id'); - $this->_ci->KalenderModel->db->where('tbl_kalender.von >=', $start_date); - $this->_ci->KalenderModel->db->where('tbl_kalender.bis <', $end_date); - $belegte_raeume = $this->_ci->KalenderModel->load(); - - if (isError($belegte_raeume)) - return $belegte_raeume; - - $belegte_orte = hasData($belegte_raeume) ? array_column(getData($belegte_raeume), 'ort_kurzbz') : []; - - $vorschlaege = $this->_getFreieRaeume($lehreinheit->raumtyp, $belegte_orte); - - if (isError($vorschlaege)) - return $vorschlaege; - - $vorschlaege = hasData($vorschlaege) ? getData($vorschlaege) : []; - - if (count($vorschlaege) < 5 && !empty($lehreinheit->raumtypalternativ)) + if (!empty($lektor_uids) || !empty($gruppen_kurzbz) || !empty($lehrverband_gruppen)) { - $bereits_gefunden = array_merge($belegte_orte, array_column($vorschlaege, 'ort_kurzbz')); + $this->_ci->KalenderModel->db->group_start(); - $alternativ_raeume = $this->_getFreieRaeume($lehreinheit->raumtypalternativ, $bereits_gefunden); + if (!empty($lektor_uids)) + $this->_ci->KalenderModel->db->where_in('tbl_lehreinheitmitarbeiter.mitarbeiter_uid', $lektor_uids); - if (!isError($alternativ_raeume) && hasData($alternativ_raeume)) - $vorschlaege = array_merge($vorschlaege, getData($alternativ_raeume)); + if (!empty($gruppen_kurzbz)) + $this->_ci->KalenderModel->db->or_where_in('tbl_lehreinheitgruppe.gruppe_kurzbz', $gruppen_kurzbz); + + foreach ($lehrverband_gruppen as $lvg) + { + $this->_ci->KalenderModel->db->or_group_start(); + $this->_ci->KalenderModel->db->where('tbl_lehreinheitgruppe.studiengang_kz', $lvg['studiengang_kz']); + $this->_ci->KalenderModel->db->where('tbl_lehreinheitgruppe.semester', $lvg['semester']); + $this->_ci->KalenderModel->db->where('tbl_lehreinheitgruppe.verband', $lvg['verband']); + $this->_ci->KalenderModel->db->where('tbl_lehreinheitgruppe.gruppe_kurzbz IS NULL', null, false); + $this->_ci->KalenderModel->db->group_end(); + } + + $this->_ci->KalenderModel->db->group_end(); } - return success($vorschlaege); + $data = $this->_ci->KalenderModel->load(); + return $this->_mapEvents($data, false); } - private function _getFreieRaeume($raumtyp, $belegte_orte) + + public function getByKalenderId($kalender_id) { - $this->_ci->OrtModel->addSelect('ort_kurzbz'); - $this->_ci->OrtModel->addJoin('public.tbl_ortraumtyp', 'ort_kurzbz'); - $this->_ci->OrtModel->db->where('raumtyp_kurzbz', $raumtyp); - $this->_ci->OrtModel->db->where('aktiv', true); - $this->_ci->OrtModel->db->where("ort_kurzbz NOT LIKE '\_%'", null, false); + $kalender_entry = $this->_ci->KalenderModel->load($kalender_id); + if (isError($kalender_entry)) + return $kalender_entry; - if (!empty($belegte_orte)) - $this->_ci->OrtModel->db->where_not_in('ort_kurzbz', $belegte_orte); - $this->_ci->OrtModel->addOrder('hierarchie, ort_kurzbz'); + if (!hasData($kalender_entry)) + return error(''); - return $this->_ci->OrtModel->load(); + $kalender_entry = getData($kalender_entry)[0]; + + $this->_getBasePlan($kalender_entry->von, $kalender_entry->bis); + $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id', $kalender_entry->kalender_id); + $data = $this->_ci->KalenderModel->load(); + return $this->_mapEvents($data); } - public function getPlanForPlanner($start_date, $end_date, $ort = null, $uids = null, $stg = null) { $this->_getBasePlan($start_date, $end_date); @@ -510,7 +560,13 @@ class KalenderLib )); if (hasData($reservierung_vorhanden) && !$this->_ci->permissionlib->isBerechtigt('lehre/reservierungAdvanced')) - return error ('lvplan/bereitsReserviert'); + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'already_reserved'), + 'errorCode' => 'already_reserved' + ]); + + } } $this->_ci->KalenderModel->db->trans_start(); @@ -732,7 +788,12 @@ class KalenderLib if ($kalender_entry->typ === 'lehreinheit') { if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted'))) - return error('Eintrag kann nicht mehr bearbeitet werden'); + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'), + 'errorCode' => 'entry_not_editable' + ]); + } if (in_array($kalender_entry->status_kurzbz, array('live', 'preview'))) return $this->_createHistoryEntry($kalender_entry, $start_date, $end_date); @@ -759,7 +820,12 @@ class KalenderLib if ($kalender_entry->typ === 'lehreinheit') { if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted'))) - return error('Eintrag kann nicht mehr bearbeitet werden'); + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'), + 'errorCode' => 'entry_not_editable' + ]); + } if (in_array($kalender_entry->status_kurzbz, array('live', 'preview'))) return $this->_createHistoryEntry($kalender_entry, null, null, $ort_kurzbz); @@ -794,7 +860,12 @@ class KalenderLib ); if (!isset($allowed[$kalender_entry->status_kurzbz]) || !in_array($status_kurzbz, $allowed[$kalender_entry->status_kurzbz])) - return error('Statuswechsel nicht erlaubt'); + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'status_change_not_allowed'), + 'errorCode' => 'status_change_not_allowed' + ]); + } $result = $this->_ci->KalenderModel->update( array('kalender_id' => $kalender_entry->kalender_id), @@ -864,7 +935,8 @@ class KalenderLib $result = $this->_ci->KalenderModel->load(array('tbl_kalender.kalender_id' => $kalender_id)); if (isError($result)) return $result; - if (!hasData($result)) return error("Not found"); + if (!hasData($result)) + return error(['message' => 'Not found', 'errorCode' => 'not_found']); return success(getData($result)[0]); } @@ -877,7 +949,8 @@ class KalenderLib if (isError($result)) return $result; - if (!hasData($result)) return error("Not found"); + if (!hasData($result)) + return error(['message' => 'Not found', 'errorCode' => 'not_found']); $entry = getData($result)[0]; diff --git a/application/libraries/RaumvorschlagLib.php b/application/libraries/RaumvorschlagLib.php new file mode 100644 index 000000000..3ff37e34b --- /dev/null +++ b/application/libraries/RaumvorschlagLib.php @@ -0,0 +1,242 @@ +_ci =& get_instance(); + + $this->_ci->load->model('ressource/Kalender_model', 'KalenderModel'); + $this->_ci->load->model('ressource/Kalender_Lehreinheit_model', 'KalenderLehreinheitModel'); + $this->_ci->load->model('ressource/Kalender_Event_model', 'KalenderEventModel'); + $this->_ci->load->model('ressource/Kalender_Event_Teilnehmer_model', 'KalenderEventTeilnehmerModel'); + $this->_ci->load->model('ressource/Kalender_Ort_model', 'KalenderOrtModel'); + $this->_ci->load->model('education/Lehreinheit_model', 'LehreinheitModel'); + $this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + $this->_ci->load->model('education/LehreinheitMitarbeiter_model', 'LehreinheitMitarbeiterModel'); + $this->_ci->load->model('ressource/Ort_model', 'OrtModel'); + $this->_ci->load->model('organisation/gruppe_model', 'GruppeModel'); + $this->_ci->load->model('organisation/Lehrverband_model', 'LehrverbandModel'); + + $this->_ci->load->model('education/Lehreinheitgruppe_model', 'LehreinheitgruppeModel'); + $this->_ci->load->model('education/Lehreinheitgruppe_model', 'LehreinheitgruppeModel'); + + $this->_ci->load->library('CollisionChecker'); + $this->_ci->load->library('KalenderLib'); + $this->_ci->load->library('PhrasesLib', array('ui')); + } + + + public function getVorschlaege($kalender_id) + { + $event = $this->_ci->kalenderlib->getByKalenderId($kalender_id); + $event = $event[0]; + + $lektor_uids = array_column($event->lektor, 'mitarbeiter_uid'); + $gruppen_kurzbz = array_values(array_filter(array_column($event->gruppe, 'gruppe_kurzbz'))); + + $lehrverband_gruppen = array_values(array_filter($event->gruppe, function($gruppe) + { + return empty($gruppe['gruppe_kurzbz']); + })); + + $tages_events = $this->_ci->kalenderlib->getForRaumvorschlag( + $event->datum, + $event->datum, + $lektor_uids, + $gruppen_kurzbz, + $lehrverband_gruppen + ); + + $lektor_davor = $this->_getEventDavor($tages_events, $event->isostart, $lektor_uids, 'lektor'); + $gruppen_davor = $this->_getEventDavor($tages_events, $event->isostart, $gruppen_kurzbz, 'gruppe'); + + $lektor_davor_ort = $lektor_davor ? $this->_getOrtDetails($lektor_davor->ort_kurzbz) : null; + $gruppen_davor_ort = $gruppen_davor ? $this->_getOrtDetails($gruppen_davor->ort_kurzbz) : null; + + $kandidaten = $this->_getRaumkandidaten($event); + if (empty($kandidaten)) return []; + + + $ratings = []; + foreach ($kandidaten as $raum) + { + $rating = ['ort_kurzbz' => $raum->ort_kurzbz, 'score' => 100, 'details' => []]; + $this->_rateLektor($rating, $raum, $lektor_davor_ort); + $this->_rateGruppen($rating, $raum, $gruppen_davor_ort); + + Events::trigger('room_rating', + function & () use (&$rating) { + return $rating; + }, + $raum, + $event + ); + $ratings[] = $rating; + } + + usort($ratings, function($a, $b) + { + return $b['score'] - $a['score']; + }); + + return $ratings; + + } + + private function _getOrtDetails($ort_kurzbz) + { + $this->_ci->OrtModel->addSelect('ort_kurzbz, stockwerk, standort_id'); + $this->_ci->OrtModel->db->where('ort_kurzbz', $ort_kurzbz); + $result = $this->_ci->OrtModel->load(); + return hasData($result) ? getData($result)[0] : null; + } + + private function _rateLektor(&$rating, $raum, $lektor_davor_ort) + { + if (!$lektor_davor_ort) return; + + if ($lektor_davor_ort->ort_kurzbz === $raum->ort_kurzbz) + { + $rating['score'] += 20; + $rating['details'][] = '+20 ' . $this->_ci->phraseslib->t('ui', 'lecturer_already_here'); + return; + } + + if ($lektor_davor_ort->standort_id !== $raum->standort_id) + { + $rating['score'] -= 20; + $rating['details'][] = '-20 '. $this->_ci->phraseslib->t('ui', 'lecturer_building_change'); + } + elseif ($lektor_davor_ort->stockwerk !== $raum->stockwerk) + { + $diff = abs($lektor_davor_ort->stockwerk - $raum->stockwerk); + $rating['score'] -= $diff * 5; + $rating['details'][] = '-' . ($diff * 5) . ' ' . $this->_ci->phraseslib->t('ui', 'lecturer_floor_change'); + } + } + + private function _rateGruppen(&$rating, $raum, $gruppen_davor_ort) + { + if (!$gruppen_davor_ort) return; + + if ($gruppen_davor_ort->ort_kurzbz === $raum->ort_kurzbz) + { + $rating['score'] += 10; + $rating['details'][] = '+10 ' . $this->_ci->phraseslib->t('ui', 'student_already_here'); + return; + } + + if ($gruppen_davor_ort->standort_id !== $raum->standort_id) + { + $rating['score'] -= 20; + $rating['details'][] = '-20 '. $this->_ci->phraseslib->t('ui', 'student_building_change'); + } + elseif ($gruppen_davor_ort->stockwerk !== $raum->stockwerk) + { + $diff = abs($gruppen_davor_ort->stockwerk - $raum->stockwerk); + $rating['score'] -= $diff * 5; + $rating['details'][] = '-' . ($diff * 5) . ' '. $this->_ci->phraseslib->t('ui', 'student_floor_change'); + } + + + } + + private function _getEventDavor($events, $von, $uids, $type) + { + $kandidat = null; + + foreach ($events as $event) + { + if ($event->isoend > $von) + continue; + + //Wenn zwischen zwei Events eine 30+ Minuten Pause liegt, wird das Event davor nicht berücksichtigt + if ((strtotime($von) - strtotime($event->isoend)) > 30 * 60) + continue; + + if (empty($event->ort_kurzbz)) + continue; + + if ($type === 'lektor') + $event_uids = array_column($event->lektor, 'mitarbeiter_uid'); + else + $event_uids = array_column($event->gruppe, 'gruppe_kurzbz'); + + if (empty(array_intersect($event_uids, $uids))) + continue; + + if ($kandidat === null || $event->isoend > $kandidat->isoend) + $kandidat = $event; + } + + return $kandidat; + } + + private function _getRaumkandidaten($event) + { + $lehreinheit = $this->_ci->LehreinheitModel->load($event->lehreinheit_id[0]); + if (!hasData($lehreinheit)) return []; + $lehreinheit = getData($lehreinheit)[0]; + + $this->_ci->KalenderModel->addSelect('tbl_kalender_ort.ort_kurzbz'); + $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id'); + $this->_ci->KalenderModel->db->where('tbl_kalender.von <', $event->isoend); + $this->_ci->KalenderModel->db->where('tbl_kalender.bis >', $event->isostart); + $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['deleted']); + $this->_ci->KalenderModel->db->where('tbl_kalender_ort.ort_kurzbz IS NOT NULL', null, false); + $belegte = $this->_ci->KalenderModel->load(); + + $belegte_orte = hasData($belegte) ? array_column(getData($belegte), 'ort_kurzbz') : []; + + if (empty($lehreinheit->raumtyp)) + { + $raeume = $this->_getFreieRaeume(null, $belegte_orte); + return hasData($raeume) ? getData($raeume) : []; + } + + $vorschlaege = []; + + $raeume = $this->_getFreieRaeume($lehreinheit->raumtyp, $belegte_orte); + if (hasData($raeume)) + $vorschlaege = getData($raeume); + + if (count($vorschlaege) < 5 && !empty($lehreinheit->raumtypalternativ)) + { + $bereits_gefunden = array_merge($belegte_orte, array_column($vorschlaege, 'ort_kurzbz')); + $alternativ = $this->_getFreieRaeume($lehreinheit->raumtypalternativ, $bereits_gefunden); + + if (!isError($alternativ) && hasData($alternativ)) + $vorschlaege = array_merge($vorschlaege, getData($alternativ)); + } + + return $vorschlaege; + } + + private function _getFreieRaeume($raumtyp, $belegte_orte) + { + $this->_ci->OrtModel->addSelect('ort_kurzbz, stockwerk, standort_id'); + $this->_ci->OrtModel->addJoin('public.tbl_ortraumtyp', 'ort_kurzbz'); + $this->_ci->OrtModel->db->where('raumtyp_kurzbz', $raumtyp); + $this->_ci->OrtModel->db->where('aktiv', true); + $this->_ci->OrtModel->db->where("ort_kurzbz NOT LIKE '\_%'", null, false); + + if (!empty($belegte_orte)) + $this->_ci->OrtModel->db->where_not_in('ort_kurzbz', $belegte_orte); + $this->_ci->OrtModel->addOrder('hierarchie, ort_kurzbz'); + + return $this->_ci->OrtModel->load(); + } + + + + + + + +} diff --git a/application/libraries/collision/checks/LectureCollisionCheck.php b/application/libraries/collision/checks/LectureCollisionCheck.php index e5ea2f1a5..ff8303adb 100644 --- a/application/libraries/collision/checks/LectureCollisionCheck.php +++ b/application/libraries/collision/checks/LectureCollisionCheck.php @@ -67,8 +67,12 @@ class LectureCollisionCheck implements ICollisionCheck ); $result = $this->_ci->KalenderModel->load(); if (!isError($result) && hasData($result)) + { foreach (getData($result) as $row) + { $grouped[$row->kalender_id][] = true; + } + } $this->_ci->KalenderModel->addSelect('DISTINCT ON (tbl_kalender.kalender_id) tbl_kalender.kalender_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit current_kalender_le', 'current_kalender_le.kalender_id = tbl_kalender.kalender_id'); @@ -90,8 +94,12 @@ class LectureCollisionCheck implements ICollisionCheck ); $result = $this->_ci->KalenderModel->load(); if (!isError($result) && hasData($result)) + { foreach (getData($result) as $row) + { $grouped[$row->kalender_id][] = true; + } + } $this->_ci->KalenderModel->addSelect('DISTINCT ON (tbl_kalender.kalender_id) tbl_kalender.kalender_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit current_kalender_le', 'current_kalender_le.kalender_id = tbl_kalender.kalender_id'); @@ -111,9 +119,12 @@ class LectureCollisionCheck implements ICollisionCheck if (!isError($result) && hasData($result)) + { foreach (getData($result) as $row) + { $grouped[$row->kalender_id][] = true; - + } + } return $grouped; } @@ -174,8 +185,12 @@ class LectureCollisionCheck implements ICollisionCheck if (isError($result) || !hasData($result)) return []; - return array_map(function($row) { - return $this->_ci->phraseslib->t('ui', 'ma_le_kollision') . ': ' . $row->mitarbeiter_uid . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')'; + return array_map(function($row) + { + return [ + 'message' => $this->_ci->phraseslib->t('ui', 'ma_le_kollision') . ': ' . $row->mitarbeiter_uid . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')', + 'errorCode' => 'lector_collision', + ]; }, getData($result)); } @@ -206,8 +221,12 @@ class LectureCollisionCheck implements ICollisionCheck if (isError($result) || !hasData($result)) return []; - return array_map(function($row) { - return $this->_ci->phraseslib->t('ui', 'reservierung_kollision') . ': ' . $row->uid . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')'; + return array_map(function($row) + { + return [ + 'message' => $this->_ci->phraseslib->t('ui', 'reservierung_kollision') . ': ' . $row->uid . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')', + 'errorCode' => 'reservation_collision', + ]; }, getData($result)); } @@ -227,8 +246,13 @@ class LectureCollisionCheck implements ICollisionCheck if (isError($result) || !hasData($result)) return []; - return array_map(function($row) { - return $this->_ci->phraseslib->t('ui', 'ma_zeitsperre_kollision') . ': ' . $row->mitarbeiter_uid . ' (' . date('d.m.Y H:i', strtotime($row->vondatum . ' ' . $row->von_beginn)) . ' - ' . date('d.m.Y H:i', strtotime($row->bisdatum . ' ' . $row->bis_ende)) . ')'; }, getData($result)); + return array_map(function($row) + { + return [ + 'message' => $this->_ci->phraseslib->t('ui', 'ma_zeitsperre_kollision') . ': ' . $row->mitarbeiter_uid . ' (' . date('d.m.Y H:i', strtotime($row->vondatum . ' ' . $row->von_beginn)) . ' - ' . date('d.m.Y H:i', strtotime($row->bisdatum . ' ' . $row->bis_ende)) . ')', + 'errorCode' => 'absences_collision', + ]; + }, getData($result)); } } \ No newline at end of file diff --git a/application/libraries/collision/checks/RoomCollisionCheck.php b/application/libraries/collision/checks/RoomCollisionCheck.php index d75288f0c..4d72ce0cd 100644 --- a/application/libraries/collision/checks/RoomCollisionCheck.php +++ b/application/libraries/collision/checks/RoomCollisionCheck.php @@ -42,7 +42,10 @@ class RoomCollisionCheck implements ICollisionCheck return array_map(function($row) { - return $this->_ci->phraseslib->t('ui', 'raum_kollision') . ': ' . $row->ort_kurzbz . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')'; + return [ + 'errorCode' => 'room_collision', + 'message' => $this->_ci->phraseslib->t('ui', 'raum_kollision') . ': ' . $row->ort_kurzbz . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')' + ]; }, getData($result)); } @@ -68,7 +71,9 @@ class RoomCollisionCheck implements ICollisionCheck $grouped = []; foreach (getData($result) as $row) + { $grouped[$row->kalender_id][] = true; + } return $grouped; } diff --git a/application/libraries/collision/checks/StudentCollisionCheck.php b/application/libraries/collision/checks/StudentCollisionCheck.php index e2d342298..4ffeb071e 100644 --- a/application/libraries/collision/checks/StudentCollisionCheck.php +++ b/application/libraries/collision/checks/StudentCollisionCheck.php @@ -130,12 +130,19 @@ class StudentCollisionCheck implements ICollisionCheck if (isError($result2) || !hasData($result2)) return []; $conflicts = []; - foreach (getData($result2) as $row) { - if (isset($curUids[$row->uid])) { - $conflicts[] = $this->_ci->phraseslib->t('ui', 'student_kollision') - . ': ' . $row->uid - . ' (' . date('d.m.Y H:i', strtotime($row->von)) - . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')'; + foreach (getData($result2) as $row) + { + if (isset($curUids[$row->uid])) + { + $conflicts[] = [ + 'message' => + $this->_ci->phraseslib->t('ui', 'student_kollision') + . ': ' . $row->uid + . ' (' . date('d.m.Y H:i', strtotime($row->von)) + . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')', + 'errorCode' => 'student_collision' + ]; + } } @@ -231,7 +238,9 @@ class StudentCollisionCheck implements ICollisionCheck $grouped = []; foreach (getData($result) as $row) + { $grouped[$row->kalender_id][] = true; + } return $grouped; } diff --git a/application/libraries/collision/checks/VerbandCollisionCheck.php b/application/libraries/collision/checks/VerbandCollisionCheck.php index 32eb8a333..40baf38a2 100644 --- a/application/libraries/collision/checks/VerbandCollisionCheck.php +++ b/application/libraries/collision/checks/VerbandCollisionCheck.php @@ -145,7 +145,12 @@ class VerbandCollisionCheck implements ICollisionCheck if (!isError($result) && hasData($result)) { foreach (getData($result) as $row) - $collisions[] = $this->_ci->phraseslib->t('ui', 'verband_kollision') . ': ' . $row->gruppenname . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')'; + { + $collisions[] = [ + 'message' => $this->_ci->phraseslib->t('ui', 'verband_kollision') . ': ' . $row->gruppenname . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')', + 'errorCode' => 'verband_collision', + ]; + } } } @@ -236,7 +241,12 @@ class VerbandCollisionCheck implements ICollisionCheck if (!isError($result) && hasData($result)) { foreach (getData($result) as $row) - $collisions[] = $this->_ci->phraseslib->t('ui', 'reservierung_kollision') . ': ' . $row->gruppenname . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')'; + { + $collisions[] = [ + 'errorCode' => 'reservation_collision', + 'message' => $this->_ci->phraseslib->t('ui', 'reservierung_kollision') . ': ' . $row->gruppenname . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')' + ]; + } } } @@ -342,7 +352,9 @@ class VerbandCollisionCheck implements ICollisionCheck $grouped = []; foreach (getData($result) as $row) + { $grouped[$row->kalender_id][] = true; + } return $grouped; } diff --git a/public/js/api/factory/tempus/kalender.js b/public/js/api/factory/tempus/kalender.js index ff5d6ff88..434063457 100644 --- a/public/js/api/factory/tempus/kalender.js +++ b/public/js/api/factory/tempus/kalender.js @@ -68,6 +68,12 @@ export default { url: '/api/frontend/v1/tempus/Kalender/getStunden', }; }, + getCalendarHours() { + return { + method: 'get', + url: '/api/frontend/v1/tempus/Kalender/getCalendarHours', + }; + }, updateKalenderEvent(kalender_id, updatedInfos) { return { method: 'post', @@ -82,11 +88,11 @@ export default { params: { lehreinheit_id, ort_kurzbz, start_date, end_date} }; }, - getRaumvorschlag(start_date, end_date, lehreinheit_id) { + getRaumvorschlag(kalender_id) { return { method: 'get', url: '/api/frontend/v1/tempus/Kalender/getRaumvorschlag', - params: { start_date, end_date, lehreinheit_id} + params: { kalender_id } }; }, diff --git a/public/js/apps/Tempus.js b/public/js/apps/Tempus.js index 09cd7d1b8..160aa3383 100644 --- a/public/js/apps/Tempus.js +++ b/public/js/apps/Tempus.js @@ -37,7 +37,8 @@ app .use(router) .use(primevue.config.default, { zIndex: { - overlay: 1100 + overlay: 1100, + tooltip: 99999 } }) .use(Phrasen) diff --git a/public/js/components/Calendar/Base.js b/public/js/components/Calendar/Base.js index b83bf722c..0833bee0d 100644 --- a/public/js/components/Calendar/Base.js +++ b/public/js/components/Calendar/Base.js @@ -29,6 +29,7 @@ export default { locale: Vue.computed(() => this.locale), timezone: Vue.computed(() => this.timezone), timeGrid: Vue.computed(() => this.timeGrid), + hoursPlan: Vue.computed(() => this.hoursPlan), draggableEvents: Vue.computed(() => { if (!this.draggableEvents) return () => false; @@ -118,6 +119,7 @@ export default { default: undefined }, timeGrid: Array, + hoursPlan: Object, draggableEvents: [Boolean, Array, Function], dropableEvents: [Boolean, Array, Function], resizableEvents: [Boolean, Array, Function], diff --git a/public/js/components/Calendar/Mode/Week/View.js b/public/js/components/Calendar/Mode/Week/View.js index 612c4ad55..333559c46 100644 --- a/public/js/components/Calendar/Mode/Week/View.js +++ b/public/js/components/Calendar/Mode/Week/View.js @@ -13,7 +13,8 @@ export default { }, inject: { timeGrid: "timeGrid", - timezone: "timezone" + timezone: "timezone", + hoursPlan: { from: "hoursPlan", default: null }, }, props: { day: { @@ -39,8 +40,9 @@ export default { }; }); } else { - // create 07:00-23:00 - return Array.from({ length: 17 }, (e, i) => luxon.Duration.fromObject({ hours: i + 7 })); + const start = this.hoursPlan?.start ?? 7; + const end = this.hoursPlan?.end ?? 23; + return Array.from({ length: end - start + 1 }, (e, i) => luxon.Duration.fromObject({ hours: i + start })); } } }, diff --git a/public/js/components/Calendar/Tempus.js b/public/js/components/Calendar/Tempus.js index f167a969b..1d4924612 100644 --- a/public/js/components/Calendar/Tempus.js +++ b/public/js/components/Calendar/Tempus.js @@ -86,6 +86,7 @@ export default { }, currentMode: this.mode, teachingunits: null, + hoursplan: null, showRaster: true, }; }, @@ -184,6 +185,15 @@ export default { end: el.ende })); }); + + this.$api + .call(ApiKalender.getCalendarHours()) + .then(res => { + this.hoursplan = { + start: res.data.start, + end: res.data.end + }; + }); }, template: /* html */` { this.raumVorschlaege = result.data ?? []; @@ -956,11 +954,12 @@ export default {
  • - {{ raum.ort_kurzbz }} + {{ raum.ort_kurzbz }} + {{ raum.score }}
  • Keine freien Räume gefunden.

    diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 48dcd66b4..84ff56712 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -47708,6 +47708,186 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'status_change_not_allowed', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Statuswechsel nicht erlaubt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Status change not allowed', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'already_reserved', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bereits reserviert', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Already reserved', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'entry_not_editable', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Eintrag kann nicht mehr bearbeitet werden', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Entry can no longer be edited', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'lecturer_already_here', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'LektorIn bereits hier', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Lecturer already here', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'student_already_here', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studierende bereits hier', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Students already here', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'lecturer_building_change', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Gebäudewechsel LektorIn', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Building change lecturer', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'student_building_change', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Gebäudewechsel Studierende', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Building change students', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'lecturer_floor_change', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Stockwerkwechsel LektorIn', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Floor change lecturer', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'student_floor_change', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Stockwerkwechsel Studierende', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Floor change students', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'abschlusspruefung',