diff --git a/application/config/navigation.php b/application/config/navigation.php index 3680930d0..d57cd697a 100755 --- 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/codex/UHSTAT1.php b/application/controllers/codex/UHSTAT1.php index 4486f9d74..ff59ef41a 100755 --- a/application/controllers/codex/UHSTAT1.php +++ b/application/controllers/codex/UHSTAT1.php @@ -5,8 +5,12 @@ if (! defined("BASEPATH")) exit("No direct script access allowed"); class UHSTAT1 extends FHC_Controller { const BERECHTIGUNG_UHSTAT_VERWALTEN = 'student/uhstat1daten_verwalten'; + const LOGIN_SESSION_INDEX = 'bewerbung/user'; const PERSON_ID_SESSION_INDEX = 'bewerbung/personId'; const CODEX_OESTERREICH = 'A'; + const CODEX_UNKNOWN_YEAR = 9999; + const CODEX_UNKNOWN_NATION = 'XXX'; + const CODEX_UNKNOWN_BILDUNGMAX = 999; const LOWER_BOUNDARY_YEARS = 160; const UPPER_BOUNDARY_YEARS = 20; @@ -210,7 +214,9 @@ class UHSTAT1 extends FHC_Controller else return true; - if (!isset($bildungsstaat)) return true; + // if no Bildungsstaat or Bildungmax unknown - valid + if (!isset($bildungsstaat) || $bildungmax == self::CODEX_UNKNOWN_BILDUNGMAX) return true; + // find out if abschluss is in Austria $this->AbschlussModel->addSelect("in_oesterreich"); @@ -219,8 +225,11 @@ class UHSTAT1 extends FHC_Controller if (hasData($abschlussRes)) { $in_oesterreich = getData($abschlussRes)[0]->in_oesterreich; - // invalid if abschluss in Austria, but not Bildungsstaat, or abschluss not in Austria, but Bildungsstaat in Austria - return ($in_oesterreich && $bildungsstaat == self::CODEX_OESTERREICH) || (!$in_oesterreich && $bildungsstaat != self::CODEX_OESTERREICH); + + // valid if Bildungsstaat and Abschluss in Austria, or Bildungsstaat and Abschluss not in Austria + // (or Abschluss not in Austria and Bildungsstaat unknown) + return ($in_oesterreich && $bildungsstaat == self::CODEX_OESTERREICH) + || (!$in_oesterreich && ($bildungsstaat != self::CODEX_OESTERREICH || $bildungsstaat == self::CODEX_UNKNOWN_NATION)); } return false; @@ -363,7 +372,11 @@ class UHSTAT1 extends FHC_Controller // get realistic birth years, dated back from current year $currYear = date("Y"); - $formMetaData['jahre'] = range($currYear - self::UPPER_BOUNDARY_YEARS, $currYear - self::LOWER_BOUNDARY_YEARS); + $yearRange = range($currYear - self::UPPER_BOUNDARY_YEARS, $currYear - self::LOWER_BOUNDARY_YEARS); + $formMetaData['jahre'] = array_combine($yearRange, $yearRange); + + // add "unknown" option + $formMetaData['jahre'][self::CODEX_UNKNOWN_YEAR] = 'unbekannt'; return success($formMetaData); } @@ -411,7 +424,10 @@ class UHSTAT1 extends FHC_Controller private function _getValidPersonId($berechtigungsArt) { // if coming from bewerbungstool - person id is in session (person must be logged in bewerbungstool) - if (isset($_SESSION[self::PERSON_ID_SESSION_INDEX]) && is_numeric($_SESSION[self::PERSON_ID_SESSION_INDEX])) + if (isset($_SESSION[self::PERSON_ID_SESSION_INDEX]) + && is_numeric($_SESSION[self::PERSON_ID_SESSION_INDEX]) + && isset($_SESSION[self::LOGIN_SESSION_INDEX]) + ) return $_SESSION[self::PERSON_ID_SESSION_INDEX]; // if person id passed directly... diff --git a/application/controllers/jobs/IssueResolver.php b/application/controllers/jobs/IssueResolver.php index be66a6b23..ca07439c3 100755 --- a/application/controllers/jobs/IssueResolver.php +++ b/application/controllers/jobs/IssueResolver.php @@ -9,8 +9,8 @@ class IssueResolver extends IssueResolver_Controller { parent::__construct(); - // set fehler codes which can be resolved by the job - // structure: fehlercode => class (library) name for resolving + // set fehler codes which can be resolved by the job, with own resolver defined + // structure: fehlercode => class (library) name for resolving in "resolvers" folder $this->_codeLibMappings = array( 'CORE_ZGV_0001' => 'CORE_ZGV_0001', 'CORE_ZGV_0002' => 'CORE_ZGV_0002', @@ -30,7 +30,6 @@ class IssueResolver extends IssueResolver_Controller 'CORE_STG_0002' => 'CORE_STG_0002', 'CORE_STG_0003' => 'CORE_STG_0003', 'CORE_STG_0004' => 'CORE_STG_0004', - 'CORE_STUDENTSTATUS_0001' => 'CORE_STUDENTSTATUS_0001', 'CORE_STUDENTSTATUS_0002' => 'CORE_STUDENTSTATUS_0002', 'CORE_STUDENTSTATUS_0003' => 'CORE_STUDENTSTATUS_0003', 'CORE_STUDENTSTATUS_0004' => 'CORE_STUDENTSTATUS_0004', @@ -51,5 +50,11 @@ class IssueResolver extends IssueResolver_Controller 'CORE_PERSON_0003' => 'CORE_PERSON_0003', 'CORE_PERSON_0004' => 'CORE_PERSON_0004' ); + + // fehler which are resolved by the job the same way as they are produced + // structure: fehlercode => class (library) name for resolving in "plausichecks" folder + $this->_codeProducerLibMappings = array( + 'CORE_STUDENTSTATUS_0001' => 'AbbrecherAktiv', + ); } } diff --git a/application/controllers/jobs/OneTimeMessages.php b/application/controllers/jobs/OneTimeMessages.php index 58bc1fb7c..525f63c3b 100755 --- a/application/controllers/jobs/OneTimeMessages.php +++ b/application/controllers/jobs/OneTimeMessages.php @@ -51,7 +51,7 @@ class OneTimeMessages extends JOB_Controller FROM public.tbl_prestudent p JOIN public.tbl_prestudentstatus ps USING (prestudent_id) JOIN public.tbl_studiengang s USING (studiengang_kz) - WHERE ps.status_kurzbz = \'Wartender\' + WHERE get_rolle_prestudent(ps.prestudent_id, NULL) = \'Wartender\' AND ps.studiensemester_kurzbz = ? AND ps.datum <= NOW() - \''.$days.' days\'::interval AND s.typ = ? 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/core/IssueResolver_Controller.php b/application/core/IssueResolver_Controller.php index 758064ed8..ea278ddb0 100755 --- a/application/core/IssueResolver_Controller.php +++ b/application/core/IssueResolver_Controller.php @@ -5,21 +5,18 @@ */ abstract class IssueResolver_Controller extends JOB_Controller { - const CI_PATH = 'application'; - const CI_LIBRARY_FOLDER = 'libraries'; - const EXTENSIONS_FOLDER = 'extensions'; - const ISSUE_RESOLVERS_FOLDER = 'issues/resolvers'; - const CHECK_ISSUE_RESOLVED_METHOD_NAME = 'checkIfIssueIsResolved'; + // mappings in form fehlercode -> resolverlibrary name, fehler which have explicit resolver class defined + protected $_codeLibMappings = []; - protected $_codeLibMappings; + // mappings in form fehlercode -> producer library name, fehler which are resolved the same way they are produced + protected $_codeProducerLibMappings = []; public function __construct() { parent::__construct(); + // pass extension name if calling from extension $this->load->model('system/Issue_model', 'IssueModel'); - - $this->load->library('IssuesLib'); } /** @@ -27,97 +24,29 @@ abstract class IssueResolver_Controller extends JOB_Controller */ public function run() { + $this->load->library( + 'issues/PlausicheckResolverLib', + [ + 'extensionName' => $this->_extensionName ?? null, + 'codeLibMappings' => $this->_codeLibMappings, + 'codeProducerLibMappings' => $this->_codeProducerLibMappings + ] + ); + $this->logInfo("Issue resolve job started"); // load open issues with given errorcodes - $openIssuesRes = $this->IssueModel->getOpenIssues(array_keys($this->_codeLibMappings)); + $openIssuesRes = $this->IssueModel->getOpenIssues( + array_merge(array_keys($this->_codeLibMappings), array_keys($this->_codeProducerLibMappings)) + ); - // log error if occured - if (isError($openIssuesRes)) - { - $this->logError(getError($openIssuesRes)); - } - else - { - // log info if no data found - if (!hasData($openIssuesRes)) - { - $this->logInfo("No open issues found"); - } - else - { - $openIssues = getData($openIssuesRes); + $openIssues = hasData($openIssuesRes) ? getData($openIssuesRes) : []; - foreach ($openIssues as $issue) - { - // ignore if Fehlercode is not in libmappings (shouldn't be checked) - if (!isset($this->_codeLibMappings[$issue->fehlercode])) continue; + $result = $this->plausicheckresolverlib->resolvePlausicheckIssues($openIssues); - $libName = $this->_codeLibMappings[$issue->fehlercode]; - - // add person id and oe kurzbz automatically as params, merge it with additional params - // decode bewerbung_parameter into assoc array - $params = array_merge( - array('issue_id' => $issue->issue_id, 'issue_person_id' => $issue->person_id, 'issue_oe_kurzbz' => $issue->oe_kurzbz), - isset($issue->behebung_parameter) ? json_decode($issue->behebung_parameter, true) : array() - ); - - // if called from extension (extension name set), path includes extension names - $libRootPath = isset($this->_extensionName) ? self::EXTENSIONS_FOLDER . '/' . $this->_extensionName . '/' : ''; - - // path for loading issue library - $issuesLibPath = $libRootPath . self::ISSUE_RESOLVERS_FOLDER . '/'; - - // file path of library for check if file exists - $issuesLibFilePath = DOC_ROOT . self::CI_PATH - . '/' . $libRootPath . self::CI_LIBRARY_FOLDER . '/' . self::ISSUE_RESOLVERS_FOLDER . '/' . $libName . '.php'; - - // check if library file exists - if (!file_exists($issuesLibFilePath)) - { - // log error and continue with next issue if not - $this->logError("Issue library file " . $issuesLibFilePath . " does not exist"); - continue; - } - - // load library connected to fehlercode - $this->load->library($issuesLibPath . $libName); - - $lowercaseLibName = mb_strtolower($libName); - - // check if method is defined in library class - if (!is_callable(array($this->{$lowercaseLibName}, self::CHECK_ISSUE_RESOLVED_METHOD_NAME))) - { - // log error and continue with next issue if not - $this->logError("Method " . self::CHECK_ISSUE_RESOLVED_METHOD_NAME . " is not defined in library $lowercaseLibName"); - continue; - } - - // call the function for checking for issue resolution - $issueResolvedRes = $this->{$lowercaseLibName}->{self::CHECK_ISSUE_RESOLVED_METHOD_NAME}($params); - - if (isError($issueResolvedRes)) - { - $this->logError(getError($issueResolvedRes)); - } - else - { - $issueResolvedData = getData($issueResolvedRes); - - if ($issueResolvedData === true) - { - // set issue to resolved if needed - $behobenRes = $this->issueslib->setBehoben($issue->issue_id, null); - - if (isError($behobenRes)) - $this->logError(getError($behobenRes)); - else - $this->logInfo("Issue " . $issue->issue_id . " successfully resolved"); - } - } - } - } - } + // log if error, or log info if inserted new issue + foreach ($result->errors as $error) $this->logError($error); + foreach ($result->infos as $info) $this->logInfo($info); $this->logInfo("Issue resolve job ended"); } diff --git a/application/core/PlausiIssueProducer_Controller.php b/application/core/PlausiIssueProducer_Controller.php index 0dce7a487..5216d284c 100755 --- a/application/core/PlausiIssueProducer_Controller.php +++ b/application/core/PlausiIssueProducer_Controller.php @@ -5,61 +5,23 @@ */ abstract class PlausiIssueProducer_Controller extends JOB_Controller { - protected $_fehlerLibMappings; + protected $_fehlerLibMappings = []; protected $_app; - public function __construct($app = null) - { - parent::__construct(); - - // pass extension name if calling from extension - $extensionName = isset($this->_extensionName) ? $this->_extensionName : null; - - // load libraries - $this->load->library('issues/PlausicheckProducerLib', array('extensionName' => $extensionName, 'app' => $this->_app)); - $this->load->library('IssuesLib'); - } - protected function producePlausicheckIssues($params) { + $this->load->library( + 'issues/PlausicheckProducerLib', + ['extensionName' => $this->_extensionName ?? null, 'app' => $this->_app, 'fehlerLibMappings' => $this->_fehlerLibMappings] + ); + $this->logInfo("Plausicheck issue producer job started"); - // get the data returned by Plausicheck - foreach ($this->_fehlerLibMappings as $fehler_kurzbz => $libName) - { - // execute the check - $this->logInfo("Checking " . $fehler_kurzbz . "..."); - $plausicheckRes = $this->plausicheckproducerlib->producePlausicheckIssue( - $libName, - $fehler_kurzbz, - $params - ); + $result = $this->plausicheckproducerlib->producePlausicheckIssues($params); - if (isError($plausicheckRes)) $this->logError(getError($plausicheckRes)); - - if (hasData($plausicheckRes)) - { - $plausicheckData = getData($plausicheckRes); - - foreach ($plausicheckData as $plausiData) - { - // get the data needed for issue production - $person_id = isset($plausiData['person_id']) ? $plausiData['person_id'] : null; - $oe_kurzbz = isset($plausiData['oe_kurzbz']) ? $plausiData['oe_kurzbz'] : null; - $fehlertext_params = isset($plausiData['fehlertext_params']) ? $plausiData['fehlertext_params'] : null; - $resolution_params = isset($plausiData['resolution_params']) ? $plausiData['resolution_params'] : null; - - // write the issue - $addIssueRes = $this->issueslib->addFhcIssue($fehler_kurzbz, $person_id, $oe_kurzbz, $fehlertext_params, $resolution_params); - - // log if error, or log info if inserted new issue - if (isError($addIssueRes)) - $this->logError(getError($addIssueRes)); - elseif (hasData($addIssueRes) && is_integer(getData($addIssueRes))) - $this->logInfo("Plausicheck issue " . $fehler_kurzbz . " successfully produced, person_id: " . $person_id); - } - } - } + // log if error, or log info if inserted new issue + foreach ($result->errors as $error) $this->logError($error); + foreach ($result->infos as $info) $this->logInfo($info); $this->logInfo("Plausicheck issue producer job stopped"); } diff --git a/application/libraries/AntragLib.php b/application/libraries/AntragLib.php index c1649587d..e45c4ad34 100755 --- a/application/libraries/AntragLib.php +++ b/application/libraries/AntragLib.php @@ -1463,12 +1463,6 @@ class AntragLib elseif($antrag->status == Studierendenantragstatus_model::STATUS_APPROVED && $antrag->datum > $datumStatus) return success(-2); } - if ($antrag->typ == Studierendenantrag_model::TYP_UNTERBRECHUNG) - { - // NOTE(chris): Ignore canceled ones - if ($antrag->status == Studierendenantragstatus_model::STATUS_CANCELLED) - continue; - } if ($antrag->typ == Studierendenantrag_model::TYP_WIEDERHOLUNG) { if($antrag->status == Studierendenantragstatus_model::STATUS_PASS) diff --git a/application/libraries/IssuesLib.php b/application/libraries/IssuesLib.php index 9b3a9de6e..f38303b3c 100755 --- a/application/libraries/IssuesLib.php +++ b/application/libraries/IssuesLib.php @@ -246,7 +246,27 @@ class IssuesLib $fehlertext = vsprintf($fehlertextVorlage, $fehlertext_params); } - $openIssuesCountRes = $this->_ci->IssueModel->getOpenIssueCount($fehlercode, $person_id, $oe_kurzbz, $fehlercode_extern); + if (isset($resolution_params)) + { + if (is_array($resolution_params)) + { + foreach ($resolution_params as $resolution_key => $resolution_param) + { + if (!is_string($resolution_key)) + return error("Invalid parameter for resolution, must be an associative array"); + } + } + else + return error("Invalid parameters for resolution"); + } + + $openIssuesCountRes = $this->_ci->IssueModel->getOpenIssueCount( + $fehlercode, + $person_id, + $oe_kurzbz, + $fehlercode_extern, + $resolution_params + ); if (hasData($openIssuesCountRes)) { @@ -256,20 +276,6 @@ class IssuesLib if ($openIssueCount == 0) { - if (isset($resolution_params)) - { - if (is_array($resolution_params)) - { - foreach ($resolution_params as $resolution_key => $resolution_param) - { - if (!is_string($resolution_key)) - return error("Invalid parameter for resolution, must be an associative array"); - } - } - else - return error("Invalid parameters for resolution"); - } - // insert new issue return $this->_ci->IssueModel->insert( array( diff --git a/application/libraries/issues/PlausicheckProducerLib.php b/application/libraries/issues/PlausicheckProducerLib.php index 3a51e2b1e..cea9967fb 100755 --- a/application/libraries/issues/PlausicheckProducerLib.php +++ b/application/libraries/issues/PlausicheckProducerLib.php @@ -12,18 +12,25 @@ class PlausicheckProducerLib private $_ci; // ci instance private $_extensionName; // name of extension - private $_konfiguration = array(); // configuration parameters + private $_konfiguration = []; // configuration parameters + private $_fehlerLibMappings = []; // mappings of fehler and libraries for producing them + private $_isForResolutionCheck = false; // mappings of fehler and libraries for producing them public function __construct($params = null) { // set extension name if called from extension if (isset($params['extensionName'])) $this->_extensionName = $params['extensionName']; + if (isset($params['fehlerLibMappings'])) $this->_fehlerLibMappings = $params['fehlerLibMappings']; + if (isset($params['isForResolutionCheck'])) $this->_isForResolutionCheck = $params['isForResolutionCheck']; // set application $app = isset($params['app']) ? $params['app'] : null; $this->_ci =& get_instance(); // get ci instance + // load libraries + $this->_ci->load->library('IssuesLib'); + // load models $this->_ci->load->model('system/Fehlerkonfiguration_model', 'FehlerkonfigurationModel'); @@ -41,6 +48,52 @@ class PlausicheckProducerLib } } + /** + * Produces multiple plausicheck issues at once and saved them to db. + * @param array $params passed to each plausicheck + * @return result object with occured error and info + */ + public function producePlausicheckIssues($params) + { + $result = new StdClass(); + $result->errors = []; + $result->infos = []; + + foreach ($this->_fehlerLibMappings as $fehler_kurzbz => $libName) + { + $plausicheckRes = $this->producePlausicheckIssue( + $libName, + $fehler_kurzbz, + $params + ); + + if (hasData($plausicheckRes)) + { + $plausicheckData = getData($plausicheckRes); + + foreach ($plausicheckData as $plausiData) + { + // get the data needed for issue production + $person_id = isset($plausiData['person_id']) ? $plausiData['person_id'] : null; + $oe_kurzbz = isset($plausiData['oe_kurzbz']) ? $plausiData['oe_kurzbz'] : null; + $fehlertext_params = isset($plausiData['fehlertext_params']) ? $plausiData['fehlertext_params'] : null; + $resolution_params = isset($plausiData['resolution_params']) ? $plausiData['resolution_params'] : null; + + // write the issue + $addIssueRes = $this->_ci->issueslib->addFhcIssue($fehler_kurzbz, $person_id, $oe_kurzbz, $fehlertext_params, $resolution_params); + + // log if error, or log info if inserted new issue + if (isError($addIssueRes)) + $result->errors[] = getError($addIssueRes); + elseif (hasData($addIssueRes) && is_integer(getData($addIssueRes))) + $result->infos[] = "Plausicheck issue " . $fehler_kurzbz . " successfully produced, person_id: " . $person_id; + } + } + } + + return $result; + } + /** * Executes plausicheck using a given library, returns the result. * @param $libName string name of library producing the issue @@ -66,7 +119,10 @@ class PlausicheckProducerLib $config = isset($this->_konfiguration[$fehler_kurzbz]) ? $this->_konfiguration[$fehler_kurzbz] : null; // load library connected to fehlercode - $this->_ci->load->library($issuesLibPath . $libName, $config); + $this->_ci->load->library( + $issuesLibPath . $libName, + ['configurationParams' => $config, 'isForResolutionCheck' => $this->_isForResolutionCheck] + ); $lowercaseLibName = mb_strtolower($libName); diff --git a/application/libraries/issues/PlausicheckResolverLib.php b/application/libraries/issues/PlausicheckResolverLib.php new file mode 100644 index 000000000..26da985f6 --- /dev/null +++ b/application/libraries/issues/PlausicheckResolverLib.php @@ -0,0 +1,136 @@ +_extensionName = $params['extensionName']; + if (isset($params['codeLibMappings'])) $this->_codeLibMappings = $params['codeLibMappings']; + if (isset($params['codeProducerLibMappings'])) $this->_codeProducerLibMappings = $params['codeProducerLibMappings']; + + $this->_ci =& get_instance(); // get ci instance + + $this->_ci->load->library('IssuesLib'); + $this->_ci->load->library('issues/PlausicheckProducerLib', ['extensionName' => $this->_extensionName, 'isForResolutionCheck' => true]); + } + + /** + * Reseolves multiple plausicheck issues at once. + * @param array $codeLibMappings contains fehler type to check and library responsible for check (fehlercode => libName) + * @param array $openIssues passed issues to resolve + * @return result object with occured error and info + */ + public function resolvePlausicheckIssues($openIssues) + { + $result = new StdClass(); + $result->errors = []; + $result->infos = []; + + foreach ($openIssues as $issue) + { + // add person id and oe kurzbz automatically as params, merge it with additional params + // decode bewerbung_parameter into assoc array + $params = array_merge( + array('issue_id' => $issue->issue_id, 'issue_person_id' => $issue->person_id, 'issue_oe_kurzbz' => $issue->oe_kurzbz), + isset($issue->behebung_parameter) ? json_decode($issue->behebung_parameter, true) : array() + ); + + $issueResolved = false; + + // ignore if Fehlercode is not in libmappings (shouldn't be checked) + if (isset($this->_codeLibMappings[$issue->fehlercode])) + { + $libName = $this->_codeLibMappings[$issue->fehlercode]; + + // if called from extension (extension name set), path includes extension names + $libRootPath = isset($this->_extensionName) ? self::EXTENSIONS_FOLDER . '/' . $this->_extensionName . '/' : ''; + + // path for loading issue library + $issuesLibPath = $libRootPath . self::ISSUE_RESOLVERS_FOLDER . '/'; + + // file path of library for check if file exists + $issuesLibFilePath = DOC_ROOT . self::CI_PATH + . '/' . $libRootPath . self::CI_LIBRARY_FOLDER . '/' . self::ISSUE_RESOLVERS_FOLDER . '/' . $libName . '.php'; + + // check if library file exists + if (!file_exists($issuesLibFilePath)) + { + // log error and continue with next issue if not + $result->errors[] = "Issue library file " . $issuesLibFilePath . " does not exist"; + continue; + } + + // load library connected to fehlercode + $this->_ci->load->library($issuesLibPath . $libName); + + $lowercaseLibName = mb_strtolower($libName); + + // check if method is defined in library class + if (!is_callable(array($this->_ci->{$lowercaseLibName}, self::CHECK_ISSUE_RESOLVED_METHOD_NAME))) + { + // log error and continue with next issue if not + $result->errors[] = "Method " . self::CHECK_ISSUE_RESOLVED_METHOD_NAME . " is not defined in library $lowercaseLibName"; + continue; + } + + // call the function for checking for issue resolution + $issueResolvedRes = $this->_ci->{$lowercaseLibName}->{self::CHECK_ISSUE_RESOLVED_METHOD_NAME}($params); + + if (isError($issueResolvedRes)) + { + $result->errors[] = getError($issueResolvedRes); + } + else + { + $issueResolved = getData($issueResolvedRes) === true; + } + } + elseif (isset($this->_codeProducerLibMappings[$issue->fehlercode])) + { + $libName = $this->_codeProducerLibMappings[$issue->fehlercode]; + + $issueResolvedRes = $this->_ci->plausicheckproducerlib->producePlausicheckIssue( + $libName, + $issue->fehler_kurzbz, + $params + ); + + if (isError($issueResolvedRes)) + { + $result->errors[] = getError($issueResolvedRes); + } + else + { + $issueResolved = !hasData($issueResolvedRes); + } + } + + // set issue to resolved if needed + if ($issueResolved) + { + $behobenRes = $this->_ci->issueslib->setBehoben($issue->issue_id, null); + + if (isError($behobenRes)) + $result->errors[] = getError($behobenRes); + else + $result->infos[] = "Issue " . $issue->issue_id . " successfully resolved"; + } + } + + return $result; + } +} diff --git a/application/libraries/issues/plausichecks/AbbrecherAktiv.php b/application/libraries/issues/plausichecks/AbbrecherAktiv.php index 9eae389b5..464b77a30 100755 --- a/application/libraries/issues/plausichecks/AbbrecherAktiv.php +++ b/application/libraries/issues/plausichecks/AbbrecherAktiv.php @@ -9,83 +9,21 @@ require_once('PlausiChecker.php'); */ class AbbrecherAktiv extends PlausiChecker { - public function executePlausiCheck($params) - { - $results = array(); + protected $_base_sql = " + SELECT + pre.person_id, pre.prestudent_id, stg.oe_kurzbz AS prestudent_stg_oe_kurzbz + FROM + public.tbl_prestudentstatus pre_status + JOIN public.tbl_prestudent pre USING(prestudent_id) + JOIN public.tbl_student student USING(prestudent_id) + JOIN public.tbl_benutzer benutzer on(benutzer.uid=student.student_uid) + JOIN public.tbl_studiengang stg ON pre.studiengang_kz = stg.studiengang_kz + WHERE + pre_status.status_kurzbz ='Abbrecher' + AND benutzer.aktiv=true"; - // get parameters from config - $exkludierte_studiengang_kz = isset($this->_config['exkludierteStudiengaenge']) ? $this->_config['exkludierteStudiengaenge'] : null; - - // pass parameters needed for plausicheck - $studiengang_kz = isset($params['studiengang_kz']) ? $params['studiengang_kz'] : null; - - // get all students failing the plausicheck - $prestudentRes = $this->getAbbrecherAktiv($studiengang_kz, null, $exkludierte_studiengang_kz); - - if (isError($prestudentRes)) return $prestudentRes; - - if (hasData($prestudentRes)) - { - $prestudents = getData($prestudentRes); - - // populate results with data necessary for writing issues - foreach ($prestudents as $prestudent) - { - $results[] = array( - 'person_id' => $prestudent->person_id, - 'oe_kurzbz' => $prestudent->prestudent_stg_oe_kurzbz, - 'fehlertext_params' => array('prestudent_id' => $prestudent->prestudent_id), - 'resolution_params' => array('prestudent_id' => $prestudent->prestudent_id) - ); - } - } - - // return the results - return success($results); - } - - /** - * Abbrecher cannot be active. - * @param studiengang_kz int if check is to be executed for certain Studiengang - * @param prestudent_id int if check is to be executed only for one prestudent - * @param exkludierte_studiengang_kz array if certain Studiengänge have to be excluded from check - * @return success with prestudents or error - */ - public function getAbbrecherAktiv($studiengang_kz = null, $prestudent_id = null, $exkludierte_studiengang_kz = null) - { - $params = array(); - - $qry = " - SELECT - pre.person_id, pre.prestudent_id, stg.oe_kurzbz AS prestudent_stg_oe_kurzbz - FROM - public.tbl_prestudentstatus pre_status - JOIN public.tbl_prestudent pre USING(prestudent_id) - JOIN public.tbl_student student USING(prestudent_id) - JOIN public.tbl_benutzer benutzer on(benutzer.uid=student.student_uid) - JOIN public.tbl_studiengang stg ON pre.studiengang_kz = stg.studiengang_kz - WHERE - pre_status.status_kurzbz ='Abbrecher' - AND benutzer.aktiv=true"; - - if (isset($studiengang_kz)) - { - $qry .= " AND stg.studiengang_kz = ?"; - $params[] = $studiengang_kz; - } - - if (isset($prestudent_id)) - { - $qry .= " AND pre.prestudent_id = ?"; - $params[] = $prestudent_id; - } - - if (isset($exkludierte_studiengang_kz) && !isEmptyArray($exkludierte_studiengang_kz)) - { - $qry .= " AND stg.studiengang_kz NOT IN ?"; - $params[] = $exkludierte_studiengang_kz; - } - - return $this->_db->execReadOnlyQuery($qry, $params); - } + protected $_config_params = ['exkludierteStudiengaenge' => " AND stg.studiengang_kz NOT IN ?"]; + protected $_params_for_checking = ['studiengang_kz' => " AND stg.studiengang_kz = ?", 'prestudent_id' => " AND pre.prestudent_id = ?"]; + protected $_fehlertext_params = ['prestudent_id']; + protected $_resolution_params = ['prestudent_id']; } diff --git a/application/libraries/issues/plausichecks/PlausiChecker.php b/application/libraries/issues/plausichecks/PlausiChecker.php index 086a44cc4..7e5fe7db0 100755 --- a/application/libraries/issues/plausichecks/PlausiChecker.php +++ b/application/libraries/issues/plausichecks/PlausiChecker.php @@ -6,15 +6,25 @@ abstract class PlausiChecker { protected $_ci; // code igniter instance - protected $_config; // configuration parameters for this plausicheck + protected $_config; // all applicable configuration parameters for this plausicheck protected $_db; // database for queries - public function __construct($configurationParams = null) + protected $_isForResolutionCheck; // if true, additional parameters only needed for resolution are checked + + protected $_config_params = []; // name of all config params which should be applied for this plausicheck, with sql [name] => [sql] + protected $_params_for_checking = []; // name of all passed params for checking, with sql [name] => [sql] + + protected $_fehlertext_params = []; // parameter names for fehlertext params used for this plausicheck + protected $_resolution_params = []; // parameter names for resolution params used for this plausicheck + + public function __construct($params = null) { $this->_ci =& get_instance(); // get code igniter instance // set configuration - $this->_config = $configurationParams; + $this->_config = $params['configurationParams'] ?? []; + + $this->_isForResolutionCheck = $params['isForResolutionCheck'] ?? false; // get database for queries $this->_db = new DB_Model(); @@ -25,5 +35,83 @@ abstract class PlausiChecker * @param $paramsForChecking array parameters needed for executing the check * @return array with objects which failed the plausi check */ - abstract public function executePlausiCheck($paramsForChecking); + public function executePlausiCheck($paramsForChecking) + { + $results = []; + $params = []; + $qry = $this->_base_sql; + + if ($this->_isForResolutionCheck == true) + { + foreach ($this->_resolution_params as $resParam) + { + if (!isset($paramsForChecking[$resParam])) + return error("$resParam missing".(isset($paramsForChecking['issue_id']) ? ", issue ID: ".$paramsForChecking['issue_id'] : "")); + } + } + + // add config params to query + if (isset($this->_config_params) && !isEmptyArray($this->_config_params)) + { + foreach ($this->_config_params as $param_name => $param_sql) + { + if (isset($this->_config[$param_name])) + { + $qry .= $param_sql; + $params[] = $this->_config[$param_name]; + } + } + } + + // add check params to query + if (isset($this->_params_for_checking) && !isEmptyArray($this->_params_for_checking)) + { + foreach ($this->_params_for_checking as $param_name => $param_sql) + { + if (isset($paramsForChecking[$param_name])) + { + $qry .= $param_sql; + $params[] = $paramsForChecking[$param_name]; + } + } + } + + $result = $this->_db->execReadOnlyQuery($qry, $params); + + if (isError($result)) return $result; + + if (hasData($result)) + { + $data = getData($result); + + // populate results with data necessary for writing issues + foreach ($data as $d) + { + $fehlertext_params = []; + $resolution_params = []; + + // add params for error texts + foreach ($this->_fehlertext_params as $param) + { + if (isset($d->{$param})) $fehlertext_params[$param] = $d->{$param}; + } + + // add params for resolution of issue + foreach ($this->_resolution_params as $param) + { + if (isset($d->{$param})) $resolution_params[$param] = $d->{$param}; + } + + $results[] = array( + 'person_id' => $d->person_id, + 'oe_kurzbz' => $d->prestudent_stg_oe_kurzbz, + 'fehlertext_params' => $fehlertext_params, + 'resolution_params' => $resolution_params + ); + } + } + + // return the results + return success($results); + } } diff --git a/application/models/education/Lehrveranstaltung_model.php b/application/models/education/Lehrveranstaltung_model.php index b7241ce04..f5bda513b 100755 --- 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/education/Lvgesamtnote_model.php b/application/models/education/Lvgesamtnote_model.php index f0c1883de..975833287 100755 --- a/application/models/education/Lvgesamtnote_model.php +++ b/application/models/education/Lvgesamtnote_model.php @@ -10,5 +10,6 @@ class Lvgesamtnote_model extends DB_Model parent::__construct(); $this->dbTable = 'campus.tbl_lvgesamtnote'; $this->pk = array('student_uid', 'studiensemester_kurzbz', 'lehrveranstaltung_id'); + $this->hasSequence = false; } } diff --git a/application/models/education/Paabgabe_model.php b/application/models/education/Paabgabe_model.php index b876030a6..5fb58cc81 100755 --- a/application/models/education/Paabgabe_model.php +++ b/application/models/education/Paabgabe_model.php @@ -25,7 +25,7 @@ class Paabgabe_model extends DB_Model WHERE projektarbeit_id = ? AND paabgabetyp_kurzbz = 'end' AND paabg.abgabedatum IS NOT NULL - ORDER BY paabg.abgabedatum, paabg.datum DESC + ORDER BY paabg.abgabedatum DESC, paabg.datum DESC LIMIT 1"; return $this->execQuery($qry, array($projektarbeit_id)); diff --git a/application/models/education/Studierendenantrag_model.php b/application/models/education/Studierendenantrag_model.php index e138d1a1c..43edb2449 100755 --- a/application/models/education/Studierendenantrag_model.php +++ b/application/models/education/Studierendenantrag_model.php @@ -96,7 +96,8 @@ class Studierendenantrag_model extends DB_Model Studierendenantragstatus_model::STATUS_REJECTED, Studierendenantragstatus_model::STATUS_OBJECTION_DENIED, Studierendenantragstatus_model::STATUS_DEREGISTERED, - Studierendenantragstatus_model::STATUS_PAUSE + Studierendenantragstatus_model::STATUS_PAUSE, + Studierendenantragstatus_model::STATUS_REMINDERSENT ]); $this->db->or_group_start(); $this->db->where('s.studierendenantrag_statustyp_kurzbz', Studierendenantragstatus_model::STATUS_APPROVED); @@ -410,7 +411,7 @@ class Studierendenantrag_model extends DB_Model FROM campus.tbl_studierendenantrag LEFT JOIN public.tbl_studiensemester USING(studiensemester_kurzbz) WHERE typ=? - AND campus.get_status_studierendenantrag(studierendenantrag_id) != ? + AND campus.get_status_studierendenantrag(studierendenantrag_id) NOT IN ? AND prestudent_id=? ) a ON (s.start < a.ende AND s.ende > a.start) WHERE s.start >= (" . $subquery . ") @@ -420,7 +421,10 @@ class Studierendenantrag_model extends DB_Model return $this->execQuery($sql, [ $max_length, self::TYP_UNTERBRECHUNG, - Studierendenantragstatus_model::STATUS_CANCELLED, + array( + Studierendenantragstatus_model::STATUS_CANCELLED, + Studierendenantragstatus_model::STATUS_REJECTED + ), $prestudent_id, $studiensemester ?: $prestudent_id, $max_length * $max_starters diff --git a/application/models/organisation/Geschaeftsjahr_model.php b/application/models/organisation/Geschaeftsjahr_model.php index 4f0d03b73..fdd774a8c 100755 --- a/application/models/organisation/Geschaeftsjahr_model.php +++ b/application/models/organisation/Geschaeftsjahr_model.php @@ -32,11 +32,20 @@ class Geschaeftsjahr_model extends DB_Model * Gets next Geschaeftsjahr, as determined by its start date * @return array|null */ - public function getNextGeschaeftsjahr() + public function getNextGeschaeftsjahr($offsetDays=null) { $query = 'SELECT * - FROM public.tbl_geschaeftsjahr - WHERE start > now() + FROM public.tbl_geschaeftsjahr WHERE '; + + if(!is_null($offsetDays)) + { + $query .= "start > now() - '".$offsetDays." days'::interval"; + } + else + { + $query .= 'start > now()'; + } + $query .= ' ORDER BY start LIMIT 1'; diff --git a/application/models/organisation/Studienjahr_model.php b/application/models/organisation/Studienjahr_model.php index 3bbe3a07f..a6e1bc575 100755 --- 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/models/system/Issue_model.php b/application/models/system/Issue_model.php index ab1f9ba6e..331b0f050 100755 --- a/application/models/system/Issue_model.php +++ b/application/models/system/Issue_model.php @@ -22,13 +22,23 @@ class Issue_model extends DB_Model */ public function getOpenIssues($fehlercodes, $person_id = null, $oe_kurzbz = null, $fehlercode_extern = null) { - $params = array($fehlercodes); + $params = array(); // issue exists for a fehlercode (or fehlercode_extern), person_id, oe_kurzbz, if not verarbeitet yet - $qry = 'SELECT issue_id, fehlercode, inhalt, fehlercode_extern, inhalt_extern, person_id, oe_kurzbz, - behebung_parameter, datum, verarbeitetvon, verarbeitetamum - FROM system.tbl_issue - WHERE fehlercode IN ? - AND verarbeitetamum IS NULL'; + $qry = 'SELECT + iss.issue_id, iss.fehlercode, fe.fehler_kurzbz, iss.inhalt, iss.fehlercode_extern, + iss.inhalt_extern, iss.person_id, iss.oe_kurzbz, iss.behebung_parameter, + iss.datum, iss.verarbeitetvon, iss.verarbeitetamum + FROM + system.tbl_issue iss + JOIN system.tbl_fehler fe USING (fehlercode) + WHERE + verarbeitetamum IS NULL'; + + if (!isEmptyArray($fehlercodes)) + { + $qry .= ' AND fehlercode IN ?'; + $params[] = $fehlercodes; + } if (!isEmptyString($fehlercode_extern)) { @@ -59,7 +69,7 @@ class Issue_model extends DB_Model * @param string $fehlercode_extern if provided, only issues with this external fehlercode are counted (for identifying issues from external systems). * @return Object success with number of issues or error */ - public function getOpenIssueCount($fehlercode, $person_id = null, $oe_kurzbz = null, $fehlercode_extern = null) + public function getOpenIssueCount($fehlercode, $person_id = null, $oe_kurzbz = null, $fehlercode_extern = null, $behebung_parameter = null) { $params = array($fehlercode); // issue exists for a fehlercode (or fehlercode_extern), person_id, oe_kurzbz, if not verarbeitet yet @@ -85,6 +95,19 @@ class Issue_model extends DB_Model $params[] = $oe_kurzbz; } + if (isset($behebung_parameter) && !isEmptyArray($behebung_parameter)) + { + // convert array to JSON string for postgres + $behebung_parameter_string = json_encode($behebung_parameter); + + if ($behebung_parameter_string) + { + // check if jsonb value is equal to the passed parameters array (if value contains array and array contains value) + $qry .= ' AND behebung_parameter @> ? AND behebung_parameter <@ ?'; + $params = array_merge($params, array($behebung_parameter_string, $behebung_parameter_string)); + } + } + return $this->execQuery($qry, $params); } } diff --git a/application/models/system/Recipient_model.php b/application/models/system/Recipient_model.php index d74d03243..1c7811f93 100755 --- a/application/models/system/Recipient_model.php +++ b/application/models/system/Recipient_model.php @@ -327,7 +327,7 @@ class Recipient_model extends DB_Model pr.nachname, ms.person_id, mrou.token - ORDER BY sent DESC'; + ORDER BY sent DESC LIMIT 1500'; return $this->execQuery($sql, array($person_id, $functions, $person_id)); } diff --git a/application/models/vertragsbestandteil/GehaltsTyp_model.php b/application/models/vertragsbestandteil/GehaltsTyp_model.php new file mode 100644 index 000000000..c933a3b38 --- /dev/null +++ b/application/models/vertragsbestandteil/GehaltsTyp_model.php @@ -0,0 +1,28 @@ +dbTable = 'hr.tbl_gehaltstyp'; + $this->pk = 'gehaltstyp_kurzbz'; + } + + /** + * Gets Gehaltstyp for a Gehaltsbestandteil. + * @param int gehaltsbestandteil_id + * @return object the typ + */ + public function getGehaltstypByGehaltsbestandteil($gehaltsbestandteil_id) + { + $gehaltsTyp = null; + $this->addJoin('hr.tbl_gehaltsbestandteil', 'gehaltstyp_kurzbz'); + $result = $this->loadWhere(['gehaltsbestandteil_id' => $gehaltsbestandteil_id]); + + if (hasData($result)) $gehaltsTyp = getData($result)[0]; + + return $gehaltsTyp; + } +} diff --git a/application/views/codex/uhstat1.php b/application/views/codex/uhstat1.php index 78a30b3e5..a255781f1 100755 --- a/application/views/codex/uhstat1.php +++ b/application/views/codex/uhstat1.php @@ -74,16 +74,23 @@ $saved = isset($saved) && $saved === true;
- +
+ + + + +
@@ -94,16 +101,23 @@ $saved = isset($saved) && $saved === true; p->t('uhstat', 'inDenHeutigenGrenzen')).')' ?>
- +
+ + + + +
@@ -114,16 +128,23 @@ $saved = isset($saved) && $saved === true; p->t('uhstat', 'inDenHeutigenGrenzen')).')' ?>
- +
+ + + + +
@@ -161,16 +182,23 @@ $saved = isset($saved) && $saved === true;
- > + + $jahr): ?> + - - + + + + + +
@@ -181,16 +209,23 @@ $saved = isset($saved) && $saved === true; p->t('uhstat', 'inDenHeutigenGrenzen')).')' ?>
- +
+ + + + +
@@ -201,16 +236,23 @@ $saved = isset($saved) && $saved === true; p->t('uhstat', 'inDenHeutigenGrenzen')).')' ?>
- +
+ + + + +
@@ -271,7 +313,7 @@ $saved = isset($saved) && $saved === true; - - + + load->view('templates/FHC-Footer'); ?> 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 100755 --- 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/dbupdate_3.4/41150_oe-pfad_db_view.php b/system/dbupdate_3.4/41150_oe-pfad_db_view.php index c108fcd77..65686dbcb 100644 --- a/system/dbupdate_3.4/41150_oe-pfad_db_view.php +++ b/system/dbupdate_3.4/41150_oe-pfad_db_view.php @@ -1,15 +1,11 @@ db_query("SELECT * FROM information_schema.views WHERE table_catalog = '" . DB_NAME . "' AND table_schema = 'public' AND table_name = 'vw_oe_path'")) -{ - if($db->db_num_rows($result) == 0) - { - $qry = " +$qry = " CREATE OR REPLACE VIEW public.vw_oe_path AS - WITH RECURSIVE vw_oe_path(oe_kurzbz, bezeichnung, oe_parent_kurzbz, organisationseinheittyp_kurzbz, oetyp_bezeichnung, depth, path) AS ( + WITH RECURSIVE vw_oe_path(oe_kurzbz, bezeichnung, oe_parent_kurzbz, organisationseinheittyp_kurzbz, oetyp_bezeichnung, depth, path, path_kurzbz) AS ( SELECT - oe.oe_kurzbz, oe.bezeichnung, oe.oe_parent_kurzbz, oe.organisationseinheittyp_kurzbz, oetyp.bezeichnung AS oetyp_bezeichnung, 0, '/' || oetyp.bezeichnung || ' ' || oe.bezeichnung AS path + oe.oe_kurzbz, oe.bezeichnung, oe.oe_parent_kurzbz, oe.organisationseinheittyp_kurzbz, oetyp.bezeichnung AS oetyp_bezeichnung, 0, '/' || oetyp.bezeichnung || ' ' || oe.bezeichnung AS path, '/' || oe.oe_kurzbz AS path_kurzbz FROM public.tbl_organisationseinheit oe JOIN @@ -18,7 +14,7 @@ if ($result = $db->db_query("SELECT * FROM information_schema.views WHERE table_ oe.oe_parent_kurzbz IS NULL UNION ALL SELECT - oe.oe_kurzbz, oe.bezeichnung, oe.oe_parent_kurzbz, oe.organisationseinheittyp_kurzbz, oetyp.bezeichnung AS oetyp_bezeichnung, depth + 1, oet.path || '/' || oetyp.bezeichnung || ' ' || oe.bezeichnung + oe.oe_kurzbz, oe.bezeichnung, oe.oe_parent_kurzbz, oe.organisationseinheittyp_kurzbz, oetyp.bezeichnung AS oetyp_bezeichnung, depth + 1, oet.path || '/' || oetyp.bezeichnung || ' ' || oe.bezeichnung, oet.path_kurzbz || '/' || oe.oe_kurzbz AS path_kurzbz FROM public.tbl_organisationseinheit oe, vw_oe_path oet JOIN @@ -29,11 +25,27 @@ if ($result = $db->db_query("SELECT * FROM information_schema.views WHERE table_ SELECT * FROM vw_oe_path ORDER BY path, depth; GRANT SELECT ON public.vw_oe_path TO vilesci; - "; - +"; + + +if ($result = $db->db_query("SELECT * FROM information_schema.views WHERE table_catalog = '" . DB_NAME . "' AND table_schema = 'public' AND table_name = 'vw_oe_path'")) +{ + if($db->db_num_rows($result) == 0) + { if (!$db->db_query($qry)) echo 'public.vw_oe_path: ' . $db->db_last_error() . '
'; else echo 'public.vw_oe_path: erstellt
'; } } + +if ($result = $db->db_query("SELECT * FROM information_schema.columns WHERE table_catalog = '" . DB_NAME . "' AND table_schema = 'public' AND table_name = 'vw_oe_path' AND column_name = 'path_kurzbz'")) +{ + if($db->db_num_rows($result) == 0) + { + if (!$db->db_query($qry)) + echo 'public.vw_oe_path: ' . $db->db_last_error() . '
'; + else + echo 'public.vw_oe_path: neu erstellt mit zusätzlicher spalte path_kurzbz
'; + } +} diff --git a/system/filtersupdate.php b/system/filtersupdate.php index e885b4e14..51dd86314 100755 --- 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 1aac799c5..4fb6b194e 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -18188,6 +18188,26 @@ array( ) ) ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'alledurchgefuehrten', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Alle durchgeführten anzeigen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Show all performed', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'international', 'category' => 'international', @@ -18703,6 +18723,366 @@ array( ) ) ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'statusGeplant', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'geplant', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'planned', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'statusGeplantDesc', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studierende hat die Maßnahme geplant.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The student has planned the measure.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'statusAkzeptiert', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'akzeptiert', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'accepted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'statusAkzeptiertDesc', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Die geplante Maßnahme wurde akzeptiert.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The planned measure has been accepted.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'statusDurchgefuehrt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'durchgeführt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'performed', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'statusDurchgefuehrtDesc', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Eine Bestätigung wurde hochgeladen.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'A confirmation has been uploaded.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'statusBestaetigt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'bestätigt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'confirmed', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'statusBestaetigtDesc', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Die hochgeladene Bestätigung wurde akzeptiert.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The uploaded confirmation has been accepted.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'statusAbgelehnt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'abgelehnt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'declined', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'statusAbgelehntDesc', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Die Maßnahme wurde abgelehnt.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The measure was rejected.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'ampelRed', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Es wurden keine Maßnahmen geplant oder alle wurden abgelehnt.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No measures have been planned, or all have been rejected.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'ampelYellow', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Mindestens eine Maßnahme wurde akzeptiert.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'At least one measure has been accepted.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'ampelOrange', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Es gibt mindestens eine geplante oder durchgeführte Maßnahme.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'There is at least one planned or performed measure.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'ampelGreenyellow', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nur bestätigte Maßnahmen vorhanden, aber weniger als 5 ECTs.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Only confirmed measures, but fewer than 5 ECTs.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'ampelGreen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Es wurden 5 ECTs erreicht.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => '5 ECTs points have been achieved.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'mailMeldungzuviele', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Die Anzahl der Empfänger überschreitet 50. Bitte verwenden Sie einen Filter (z.B. Semester), um die Empfängeranzahl zu reduzieren.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The number of recipients exceeds 50. Please use a filter (e.g., semester) to reduce the number of recipients.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'mailMeldung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Keine Studierenden ohne geplante Maßnahmen gefunden.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No students without planned measures were found.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'mailButton', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'E-Mail nur an Studierende, die keine Maßnahmen geplant haben.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Send email only to students who have no planned measures.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'international', 'category' => 'international', @@ -18743,6 +19123,26 @@ array( ) ) ), + array( + 'app' => 'international', + 'category' => 'international', + 'phrase' => 'mailversenden', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Mail versenden', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Send mail', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'international', 'category' => 'international', @@ -31765,6 +32165,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', @@ -33132,6 +33972,26 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'uhstat', + 'phrase' => 'unbekannt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'unbekannt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'unknown', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ) // AMPELN PHRASEN ----------------------------------------------------------------------------- array( 'app' => 'core', diff --git a/vilesci/lehre/lehreinheiten_vorrueckung.php b/vilesci/lehre/lehreinheiten_vorrueckung.php index 5afe1f3df..49fa02b48 100755 --- a/vilesci/lehre/lehreinheiten_vorrueckung.php +++ b/vilesci/lehre/lehreinheiten_vorrueckung.php @@ -106,7 +106,7 @@ echo ' -

Lehreinheiten Vorrückung

+

LV-Teile Vorrückung

'; echo '
'; echo 'Studiengang:
'; + die (); + } + elseif ($anzahl_von > 0 && $anzahl_nach >= $anzahl_von && !isset($_GET['continue'])) + { + echo '

Es sind schon '.$anzahl_nach.' LV-Teile fuer das '.$stsem_nach.' in '.$stg_arr[$studiengang_kz].' '.$semester.' vorhanden. Trotzdem fortsetzen?

'; @@ -553,15 +561,15 @@ if ($studiengang_kz != '' && $stsem_von != '' && $stsem_nach != '') } else { - $text .= 'Fehler beim Laden der Lehreinheiten '.$db->db_last_error(); + $text .= 'Fehler beim Laden der LV-Teile '.$db->db_last_error(); $error_lehreinheit++; } echo "

"; - echo "Vorgerueckte Lehreinheiten: $anzahl_lehreinheiten
"; + echo "Vorgerueckte LV-Teile: $anzahl_lehreinheiten
"; echo "Vorgerueckte LEMitarbeiter: $anzahl_lehreinheitmitarbeiter
"; echo "Vorgerueckte LEGruppen: $anzahl_lehreinheitgruppe
"; - echo "Fehler bei Lehreinheiten: $error_lehreinheit
"; + echo "Fehler bei LV-Teil: $error_lehreinheit
"; echo "Fehler bei LEMitarbeiter: $error_lehreinheitmitarbeiter
"; echo "Fehler bei LEGruppen: $error_lehreinheitmitarbeiter
";