- added issue management functionality - adding, and displaying issues with filter widget.

This commit is contained in:
KarpAlex
2021-09-21 00:58:29 +02:00
parent 493bf0edda
commit 4c21d5364c
11 changed files with 1102 additions and 48 deletions
+7
View File
@@ -133,6 +133,13 @@ $config['navigation_header'] = array(
'expand' => true,
'sort' => 20,
'requiredPermissions' => 'system/developer:r'
),
'errormonitoring' => array(
'link' => site_url('system/issues/Issues'),
'description' => 'Fehler Monitoring',
'expand' => true,
'sort' => 20,
'requiredPermissions' => 'system/issues_verwalten:r'
)
)
)
@@ -0,0 +1,120 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Issues extends Auth_Controller
{
private $_uid;
const FUNKTION_KURZBZ = 'ass'; // // user having this funktion can see issues for oes assigned with this funktion
const BERECHTIGUNG_KURZBZ = 'system/issues_verwalten'; // user having this permission can see issues for oes assigned with this permission
public function __construct()
{
parent::__construct(
array(
'index' => array(self::BERECHTIGUNG_KURZBZ.':r'),
'changeIssueStatus' => array(self::BERECHTIGUNG_KURZBZ.':r')
)
);
// Load libraries
$this->load->library('IssuesLib');
$this->load->library('PermissionLib');
$this->load->library('WidgetLib');
$this->loadPhrases(
array(
'global',
'ui',
'filter'
)
);
// Load models
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
$this->_setAuthUID(); // sets property uid
}
public function index()
{
$oes_for_issues = $this->_getOesForIssues();
$this->load->view(
'system/issues/issues',
$oes_for_issues
);
}
/**
* Initializes issues status change
*/
public function changeIssueStatus()
{
$issue_ids = $this->input->post('issue_ids');
$status_kurzbz = $this->input->post('status_kurzbz');
$verarbeitetvon = $this->_uid;
$errors = array();
foreach ($issue_ids as $issue_id)
{
$issueRes = $this->issueslib->changeIssueStatus($issue_id, $status_kurzbz, $verarbeitetvon);
if (isError($issueRes))
$errors[] = getError($issueRes);
}
if (!isEmptyArray($errors))
$this->outputJsonError(implode(", ", $errors));
else
$this->outputJsonSuccess("Status erfolgreich aktualisiert");
}
/**
* 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');
}
/**
* Gets oes of logged in user, which are needed to display issues of the user.
* This includes oes assigned by a funktio and as the issue permission.
* @return array
*/
private function _getOesForIssues()
{
// get oes of uid for which there is a current funktion
$all_oe_kurzbz_with_funktionen = array();
$oe_kurzbz_for_funktion = array();
$benutzerfunktionRes = $this->BenutzerfunktionModel->getBenutzerFunktionByUid($this->_uid, null, date('Y-m-d'), date('Y-m-d'));
if (isError($benutzerfunktionRes))
show_error(getError($benutzerfunktionRes));
if (hasData($benutzerfunktionRes))
{
foreach (getData($benutzerfunktionRes) as $benutzerfunktion)
{
$all_oe_kurzbz_with_funktionen[$benutzerfunktion->oe_kurzbz][] = $benutzerfunktion->funktion_kurzbz;
if ($benutzerfunktion->funktion_kurzbz == self::FUNKTION_KURZBZ) // separate oes for the funktion needed for displaying issues
$oe_kurzbz_for_funktion[] = $benutzerfunktion->oe_kurzbz;
}
}
// add oes for which there is the issues_verwalten Berechtigung
if (!$oe_kurzbz_berechtigt = $this->permissionlib->getOE_isEntitledFor(self::BERECHTIGUNG_KURZBZ))
show_error('Keine Berechtigung oder Fehler bei Berechtigungsprüfung');
$all_oe_kurzbz_berechtigt = array_unique(array_merge($oe_kurzbz_for_funktion, $oe_kurzbz_berechtigt));
return array(
'all_oe_kurzbz_with_funktionen' => $all_oe_kurzbz_with_funktionen,
'all_oe_kurzbz_berechtigt' => $all_oe_kurzbz_berechtigt
);
}
}
+231
View File
@@ -0,0 +1,231 @@
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* Library for writing and reading issues (problems in fhcomplete system which need resolving)
*/
class IssuesLib
{
private $_ci; // Code igniter instance
const APP_INDEX = 'app';
const INSERTVON_INDEX = 'insertvon';
const FALLBACK_FEHLERCODE_INDEX = 'fallbackFehlercode';
const STATUS_NEU = 'new';
const STATUS_IN_BEARBEITUNG = 'inProgress';
const STATUS_BEHOBEN = 'resolved';
const ERRORTYPE_CODE = 'error';
const WARNINGTYPE_CODE = 'warning';
public function __construct($params = null)
{
$this->_ci =& get_instance();
// Properties default values
$this->_app = 'core';
$this->_insertvon = 'system';
$this->_fallbackFehlercode = 'UNKNOWN_ERROR';
// If parameters are given then overwrite the default values
if (!isEmptyArray($params)) $this->setConfigs($params);
// load models
$this->_ci->load->model('system/Issue_model', 'IssueModel');
$this->_ci->load->model('system/Fehler_model', 'FehlerModel');
}
// --------------------------------------------------------------------------------------------------------------
// Public methods
/**
* Store configuration parameters for this lib
*/
public function setConfigs($params)
{
// If parameters are given then overwrite the default values
if (!isEmptyArray($params))
{
if (isset($params[self::APP_INDEX])) $this->_app = $params[self::APP_INDEX];
if (isset($params[self::INSERTVON_INDEX])) $this->_insertvon = $params[self::INSERTVON_INDEX];
if (isset($params[self::FALLBACK_FEHLERCODE_INDEX])) $this->_fallbackFehlercode = $params[self::FALLBACK_FEHLERCODE_INDEX];
}
}
/**
* Adds an Fhc issue, i.e. an internal, self-defined issue.
* @param string $fehler_kurzbz short unique text name of the issue
* @param int $person_id
* @param string $oe_kurzbz
* @param array $fehlertext_params params for sprint replace of error text in system.tbl_fehler
* @return object success or error
*/
public function addFhcIssue($fehler_kurzbz, $person_id = null, $oe_kurzbz = null, $fehlertext_params = null)
{
$fehlerRes = $this->_ci->FehlerModel->loadWhere(array('fehler_kurzbz' => $fehler_kurzbz));
if (hasData($fehlerRes))
{
$fehlercode = getData($fehlerRes)[0]->fehlercode;
return $this->_addIssue($fehlercode, $person_id, $oe_kurzbz, $fehlertext_params);
}
else
return error("Fehler nicht gefunden");
}
/**
* Adds an external issue, already defined externally by another system.
* @param string $fehlercode_extern the error code in the external system
* @param string $inhalt_extern error text in external system
* @param int $person_id
* @param int $oe_kurzbz
* @param array $fehlertext_params params for sprint replace of error text in system.tbl_fehler
* @return object success or error
*/
public function addExternalIssue($fehlercode_extern, $inhalt_extern, $person_id = null, $oe_kurzbz = null, $fehlertext_params = null)
{
if (isEmptyString($fehlercode_extern))
return error("fehlercode_extern fehlt");
// get external fehlercode (unique for each app)
$this->_ci->FehlerModel->addSelect('fehlercode');
$fehlerRes = $this->_ci->FehlerModel->loadWhere(array('fehlercode_extern' => $fehlercode_extern, 'app' => $this->_app));
if (isError($fehlerRes))
return $fehlerRes;
// check if there is a predefined custom error for the external issue
if (hasData($fehlerRes))
{
// if found, use the code
$fehlercode = getData($fehlerRes)[0]->fehlercode;
}
else
{
// if predefined error is not found, insert with fallback code
$fehlercode = $this->_fallbackFehlercode;
}
// add external issue
return $this->_addIssue($fehlercode, $person_id, $oe_kurzbz, $fehlertext_params, $fehlercode_extern, $inhalt_extern);
}
/**
* Changes status of an issue.
* @param int $issue_id
* @param string $status_kurzbz the new status
* @param string $verarbeitetvon uid of person changing the status (needed for in Bearbeitung and behoben)
* @return success or error
*/
public function changeIssueStatus($issue_id, $status_kurzbz, $verarbeitetvon = null)
{
if (!isset($issue_id) || !is_numeric($issue_id))
return error("Issue Id muss korrekt gesetzt sein.");
// check if given status is same as existing
$this->_ci->IssueModel->addSelect('status_kurzbz');
$currStatus = $this->_ci->IssueModel->load($issue_id);
if (hasData($currStatus))
{
if (getData($currStatus)[0]->status_kurzbz == $status_kurzbz)
return success("Gleicher Status bereits gesetzt");
}
else
return error("Fehler beim Holen des Status");
$data = array(
'status_kurzbz' => $status_kurzbz,
'updatevon' => $verarbeitetvon,
'updateamum' => date('Y-m-d H:i:s')
);
if ($status_kurzbz == self::STATUS_NEU)
{
$data['verarbeitetvon'] = null;
}
if ($status_kurzbz == self::STATUS_NEU || $status_kurzbz == self::STATUS_IN_BEARBEITUNG)
{
$data['verarbeitetamum'] = null;
}
if ($status_kurzbz == self::STATUS_IN_BEARBEITUNG || $status_kurzbz == self::STATUS_BEHOBEN)
{
if (isset($verarbeitetvon))
$data['verarbeitetvon'] = $verarbeitetvon;
else
return error("Verarbeitetvon nicht gesetzt");
}
if ($status_kurzbz == self::STATUS_BEHOBEN)
$data['verarbeitetamum'] = date('Y-m-d H:i:s');
return $this->_ci->IssueModel->update(
array(
'issue_id' => $issue_id
),
$data
);
}
/**
* Adds an issue.
* @param $fehlercode
* @param int $person_id
* @param string $oe_kurzbz
* @param array $fehlertext_params
* @param string $fehlercode_extern
* @param string $inhalt_extern
* @return array|stdClass
*/
private function _addIssue($fehlercode, $person_id = null, $oe_kurzbz = null, $fehlertext_params = null, $fehlercode_extern = null, $inhalt_extern = null)
{
if (isEmptyString($person_id) && isEmptyString($oe_kurzbz))
return error("Person_id oder oe_kurzbz muss gesetzt sein.");
// get fehlertextVorlage and replace it with params
$fehlerRes = $this->_ci->FehlerModel->load($fehlercode);
if (hasData($fehlerRes))
{
$fehlertextVorlage = getData($fehlerRes)[0]->fehlertext;
$fehlertext = isEmptyArray($fehlertext_params) ? $fehlertextVorlage : vsprintf($fehlertextVorlage, $fehlertext_params);
$openIssuesCountRes = $this->_ci->IssueModel->getOpenIssueCount($fehlercode, $person_id, $oe_kurzbz, $fehlercode_extern);
if (hasData($openIssuesCountRes))
{
// don't insert if issue is already open
// already open - status new with same fehlercode or same fehlercode-extern (if set)
$openIssueCount = getData($openIssuesCountRes)[0]->anzahl_open_issues;
if ($openIssueCount == 0)
{
return $this->_ci->IssueModel->insert(
array(
'fehlercode' => $fehlercode,
'fehlercode_extern' => $fehlercode_extern,
'inhalt' => $fehlertext,
'inhalt_extern' => $inhalt_extern,
'person_id' => $person_id,
'oe_kurzbz' => $oe_kurzbz,
'datum' => date('Y-m-d H:i:s'),
'status_kurzbz' => self::STATUS_NEU,
'insertvon' => $this->_insertvon
)
);
}
else
return success($openIssueCount);
}
else
return error("Anzahl offener Issues konnte nicht ermittelt werden.");
}
else
return error("Fehler nicht gefunden");
}
}
@@ -0,0 +1,14 @@
<?php
class Fehler_model extends DB_Model
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'system.tbl_fehler';
$this->pk = 'fehlercode';
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
class Issue_model extends DB_Model
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'system.tbl_issue';
$this->pk = 'issue_id';
}
/**
* Gets number of open (non-resolved) issues.
* @param string $fehlercode unique error code
* @param int $person_id if provided, only issues with this person_id are counted.
* @param string $oe_kurzbz if provided, only issues with this oe_kurzbz are counted.
* @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)
{
$params = array($fehlercode);
// issue exists for a fehlercode (or fehlercode_extern), person_id, oe_kurzbz, if not verarbeitet yet
$qry = 'SELECT count(*) as anzahl_open_issues FROM system.tbl_issue
WHERE fehlercode = ?
AND verarbeitetamum IS NULL';
if (!isEmptyString($fehlercode_extern))
{
$qry .= ' AND fehlercode_extern = ?';
$params[] = $fehlercode_extern;
}
if (isset($person_id))
{
$qry .= ' AND person_id = ?';
$params[] = $person_id;
}
if (isset($oe_kurzbz))
{
$qry .= ' AND oe_kurzbz = ?';
$params[] = $oe_kurzbz;
}
return $this->execQuery($qry, $params);
}
}
@@ -0,0 +1,48 @@
<?php
$this->load->view(
'templates/FHC-Header',
array(
'title' => 'Fehler Monitoring',
'jquery' => true,
'jqueryui' => true,
'jquerycheckboxes' => true,
'bootstrap' => true,
'fontawesome' => true,
'sbadmintemplate' => true,
'tablesorter' => true,
'ajaxlib' => true,
'filterwidget' => true,
'navigationwidget' => true,
'dialoglib' => true,
'phrases' => array(
'ui' => array('bitteEintragWaehlen')
),
'customCSSs' => array('public/css/issues/issuesDataset.css', 'public/css/sbadmin2/tablesort_bootstrap.css'),
'customJSs' => array('public/js/issues/issuesDataset.js', 'public/js/bootstrapper.js')
)
);
?>
<body>
<div id="wrapper">
<?php echo $this->widgetlib->widget('NavigationWidget'); ?>
<div id="page-wrapper">
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h3 class="page-header">
Fehler Monitoring
</h3>
</div>
</div>
<div>
<?php $this->load->view('system/issues/issuesData.php'); ?>
</div>
</div>
</div>
</div>
</body>
<?php $this->load->view('templates/FHC-Footer'); ?>
@@ -0,0 +1,161 @@
<?php
$PERSON_ID = getAuthPersonId();
$ALL_OE_KURZBZ = "('" . implode("','", array_keys($all_oe_kurzbz_with_funktionen)) . "')";
$ALL_OE_KURZBZ_BERECHTIGT = "('" . implode("','", $all_oe_kurzbz_berechtigt) . "')";
$RELEVANT_PRESTUDENT_STATUS = "('Aufgenommener', 'Student', 'Incoming', 'Diplomand', 'Abbrecher', 'Unterbrecher', 'Absolvent')";
// get issues for the oes of the uid or for the persons (students, oe-zuordnung) of the oes
$query = "SELECT issue_id, fehlercode AS \"Fehlercode\", iss.fehlercode_extern AS \"Fehlercode extern\", datum AS \"Datum\",
inhalt AS \"Inhalt\", inhalt_extern AS \"Inhalt extern\", iss.person_id AS \"PersonId\", iss.oe_kurzbz AS \"OE\",
ftyp.bezeichnung_mehrsprachig[1] AS \"Fehlertyp\", stat.bezeichnung_mehrsprachig[1] AS \"Fehlerstatus\",
verarbeitetvon AS \"Verarbeitet von\",verarbeitetamum AS \"Verarbeitet am\", fr.app AS \"Applikation\",
fr.fehlertyp_kurzbz as \"Fehlertypcode\", iss.status_kurzbz AS \"Statuscode\"
FROM system.tbl_issue iss
JOIN system.tbl_fehler fr USING (fehlercode)
JOIN system.tbl_fehlertyp ftyp USING (fehlertyp_kurzbz)
JOIN system.tbl_issue_status stat USING (status_kurzbz)
WHERE EXISTS (
SELECT 1 FROM system.tbl_fehler_zustaendigkeiten
WHERE fehlercode = iss.fehlercode
AND (
person_id = ".$PERSON_ID." /* person_id in fehler_zustaendigkeit for individual persons */";
if (!isEmptyArray($all_oe_kurzbz_with_funktionen))
{
$query .= " OR (oe_kurzbz IN $ALL_OE_KURZBZ AND funktion_kurzbz IS NULL) /* if oe is specified in fehler_zustaendigkeiten */";
// check for each oe for each function if zustaendig
foreach ($all_oe_kurzbz_with_funktionen as $oe_kurzbz => $funktionen_kurzbz)
{
foreach ($funktionen_kurzbz as $funktion_kurzbz)
{
$query .= " OR (oe_kurzbz = '$oe_kurzbz' AND funktion_kurzbz = '$funktion_kurzbz')";
}
}
}
$query .= "))"; // close AND of exists, and exists
// show issue if it is assigend to oe of uid or to student of oe of uid
if (!isEmptyArray($all_oe_kurzbz_berechtigt))
{
$query .= " OR oe_kurzbz IN $ALL_OE_KURZBZ_BERECHTIGT /* if error is for studiengang oe */";
$query .= " OR (oe_kurzbz IS NULL AND EXISTS ( /* if person_id of error is a student of studiengang oe */
SELECT 1 FROM public.tbl_prestudent ps
JOIN public.tbl_prestudentstatus pss USING (prestudent_id)
JOIN public.tbl_studiengang stg USING (studiengang_kz)
WHERE person_id = iss.person_id
AND stg.oe_kurzbz IN $ALL_OE_KURZBZ_BERECHTIGT
AND pss.status_kurzbz IN $RELEVANT_PRESTUDENT_STATUS
AND NOT EXISTS (SELECT 1
FROM public.tbl_prestudentstatus ps_finished
WHERE prestudent_id = ps.prestudent_id /* irrelevant if already finished studies and studied a while ago */
AND status_kurzbz IN ('Absolvent','Abbrecher','Abgewiesener')
AND datum::date + interval '2 months' < NOW()
AND EXISTS (SELECT 1 FROM public.tbl_prestudent /* if more recent prestudent exists, their oe should get the issue */
JOIN public.tbl_prestudentstatus USING (prestudent_id)
WHERE tbl_prestudentstatus.status_kurzbz IN $RELEVANT_PRESTUDENT_STATUS
AND person_id = ps.person_id
AND prestudent_id <> ps_finished.prestudent_id
AND datum::date >= ps_finished.datum::date)
)
)
)";
}
$query .= " ORDER BY CASE
WHEN iss.status_kurzbz = '".IssuesLib::STATUS_NEU."' THEN 0
WHEN iss.status_kurzbz = '".IssuesLib::STATUS_IN_BEARBEITUNG."' THEN 1
ELSE 2
END, datum DESC, fehlercode, issue_id DESC";
$filterWidgetArray = array(
'query' => $query,
'app' => 'core',
'datasetName' => 'issues',
'filter_id' => $this->input->get('filter_id'),
'tableUniqueId' => 'issues',
'requiredPermissions' => 'admin',
'datasetRepresentation' => 'tablesorter',
'checkboxes' => 'issue_id',
'columnsAliases' => array(
'ID',
'Fehlercode',
'Fehlercode extern',
'Datum',
'Inhalt',
'Inhalt extern',
'PersonId',
'OE',
'Fehlertyp',
'Fehlerstatus',
'Verarbeitet von',
'Verarbeitet am',
'Applikation',
'Fehlertypcode',
'Statuscode',
),
'formatRow' => function($datasetRaw) {
if ($datasetRaw->{'Fehlercode extern'} == null)
{
$datasetRaw->{'Fehlercode extern'} = '-';
}
if ($datasetRaw->{'Inhalt'} == null)
{
$datasetRaw->{'Inhalt'} = '-';
}
if ($datasetRaw->{'Inhalt extern'} == null)
{
$datasetRaw->{'Inhalt extern'} = '-';
}
if ($datasetRaw->{'PersonId'} == null)
{
$datasetRaw->{'PersonId'} = '-';
}
if ($datasetRaw->{'OE'} == null)
{
$datasetRaw->{'OE'} = '-';
}
if ($datasetRaw->{'Verarbeitet am'} == null)
{
$datasetRaw->{'Verarbeitet am'} = '-';
}
if ($datasetRaw->{'Verarbeitet von'} == null)
{
$datasetRaw->{'Verarbeitet von'} = '-';
}
return $datasetRaw;
},
'markRow' => function($datasetRaw) {
$mark = '';
if ($datasetRaw->Statuscode == IssuesLib::STATUS_BEHOBEN)
$mark = "text-success";
elseif ($datasetRaw->Statuscode == IssuesLib::STATUS_NEU || $datasetRaw->Statuscode == IssuesLib::STATUS_IN_BEARBEITUNG)
{
if ($datasetRaw->Fehlertypcode == IssuesLib::ERRORTYPE_CODE)
{
$mark = "text-danger";
}
elseif ($datasetRaw->Fehlertypcode == IssuesLib::WARNINGTYPE_CODE)
{
$mark = "text-warning";
}
}
return $mark;
}
);
echo $this->widgetlib->widget('FilterWidget', $filterWidgetArray);