Ferien: added import from government csv

This commit is contained in:
Alexei Karpenko
2026-09-28 18:40:07 +02:00
parent 1c6912776b
commit 85bdd3a686
5 changed files with 192 additions and 44 deletions
+5
View File
@@ -0,0 +1,5 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
$config['ferien_import_link'] = 'https://www.wien.gv.at/gogv/l9ogdfeiertagskalender';
@@ -27,6 +27,7 @@ if (!defined('BASEPATH')) exit('No direct script access allowed');
class Ferien extends FHCAPI_Controller
{
const DEFAULT_BERECHTIGUNG = 'basis/ferien';
const FERIENTYP_KURZBZ = 'Feiertag';
/**
* Calls the parent's constructor and prepares libraries and phrases
@@ -41,7 +42,8 @@ class Ferien extends FHCAPI_Controller
'getFerientypen' => self::DEFAULT_BERECHTIGUNG.':r',
'insert' => self::DEFAULT_BERECHTIGUNG.':w',
'update' => self::DEFAULT_BERECHTIGUNG.':w',
'delete' => self::DEFAULT_BERECHTIGUNG.':w'
'delete' => self::DEFAULT_BERECHTIGUNG.':w',
'importFerien' => self::DEFAULT_BERECHTIGUNG.':w'
]);
// Load models
@@ -50,6 +52,7 @@ class Ferien extends FHCAPI_Controller
$this->load->library('PermissionLib');
$this->load->config('ferienimport');
// Load language phrases
$this->loadPhrases([
@@ -301,6 +304,85 @@ class Ferien extends FHCAPI_Controller
$this->terminateWithSuccess();
}
/**
*
* @param
* @return object success or error
*/
public function importFerien()
{
$filterVonDatum = $this->input->get('filterVonDatum');
$filterBisDatum = $this->input->get('filterBisDatum');
if (isset($filterVonDatum) && !is_valid_date($filterVonDatum))
return $this->terminateWithError($this->p->t('ui', 'error_invalid_date'), self::ERROR_TYPE_GENERAL);
if (isset($filterBisDatum) && !is_valid_date($filterBisDatum))
return $this->terminateWithError($this->p->t('ui', 'error_invalid_date'), self::ERROR_TYPE_GENERAL);
$noImported = 0;
$data = file_get_contents($this->config->item('ferien_import_link'));
if (!$data) return $this->terminateWithError($this->p->t('ferien', 'csvNichtGelesen'), self::ERROR_TYPE_GENERAL);
$rows = explode("\n", $data);
$headers = [];
$ferien = [];
$headerRow = true;
foreach($rows as $row)
{
/*
* Wenn escape auf etwas anderes als eine leere Zeichenkette ("") gesetzt wird, kann dies zu einer CSV-Datei führen,
* die nicht mit » RFC 4180 konform ist oder die den Umlauf durch die PHP-CSV-Funktionen nicht übersteht.
* Der Standardwert für escape ist "\\", weshalb empfohlen wird, diesen Parameter explizit auf eine leere Zeichenkette zu setzen.
*/
$fe = str_getcsv($row, ",", "\"", '');
if ($headerRow)
{
$headers = $fe;
}
else
{
$importedFerien = [];
foreach ($fe as $idx => $value)
{
if (!isset($headers[$idx])) continue;
$importedFerien[$headers[$idx]] = $value;
}
$ferien = $this->_mapImportFerien($importedFerien);
if (
!$ferien
|| ($filterVonDatum != null && new DateTime($filterVonDatum) > new DateTime($ferien['bisdatum']))
|| ($filterBisDatum != null && new DateTime($filterBisDatum) < new DateTime($ferien['vondatum']))
) continue;
$result = $this->FerienModel->loadWhere([
'vondatum' => $ferien['vondatum'],
'bisdatum' => $ferien['bisdatum'],
'bezeichnung' => $ferien['bezeichnung'],
'oe_kurzbz' => null
]);
if (isError($result)) return $this->terminateWithError($this->p->t('ferien', 'fehlerBeimLaden'), self::ERROR_TYPE_GENERAL);
// add Ferien if there have been
if (!hasData($result))
{
$result = $this->FerienModel->insert($ferien);
if (isError($result)) return $this->terminateWithError($this->p->t('ferien', 'fehlerBeimHinzufuegen'), self::ERROR_TYPE_GENERAL);
$noImported++;
}
}
$headerRow = false;
}
$this->terminateWithSuccess($noImported);
}
/**
* Validate ferien post input.
*/
@@ -368,4 +450,32 @@ class Ferien extends FHCAPI_Controller
return $data;
}
/**
*
* @param
* @return object success or error
*/
private function _mapImportFerien($importedFerien)
{
$mappings = [
'vondatum' => 'DATUM',
'bisdatum' => 'DATUM',
'bezeichnung' => 'TEXT'
];
$ferien = [
'ferientyp_kurzbz' => self::FERIENTYP_KURZBZ,
'insertamum' => date('c'),
'insertvon' => getAuthUID()
];
foreach ($mappings as $key => $name)
{
if (!isset($importedFerien[$name])) return null;
$ferien[$key] = $importedFerien[$name];
}
return $ferien;
}
}
@@ -75,5 +75,15 @@ export default {
url: 'api/frontend/v1/education/ferien/delete',
params: { ferien_id }
};
}
},
importFerien(filterVonDatum, filterBisDatum) {
return {
method: 'get',
url: 'api/frontend/v1/education/ferien/importFerien',
params: {
filterVonDatum,
filterBisDatum
}
};
},
};
@@ -156,47 +156,33 @@ export default {
event: 'tableBuilt',
handler: async () => {
//await this.$p.loadCategory(['ferien', 'ui']);
await this.$p.loadCategory(['global', 'ferien']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('ferien_id').component.updateDefinition({
title: this.$p.t('ferien', 'ferienId'),
});
cm.getColumnByField('vondatum').component.updateDefinition({
title: this.$p.t('ferien', 'vondatum'),
});
cm.getColumnByField('bisdatum').component.updateDefinition({
title: this.$p.t('ferien', 'bisdatum'),
});
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('global', 'bezeichnung'),
});
cm.getColumnByField('oe_kurzbz').component.updateDefinition({
title: this.$p.t('ferien', 'oeKurzbezeichnung'),
});
cm.getColumnByField('oe_bezeichnung').component.updateDefinition({
title: this.$p.t('ferien', 'oeBezeichnung'),
});
cm.getColumnByField('studienplan_bezeichnung').component.updateDefinition({
title: this.$p.t('ferien', 'studienplanBezeichnung'),
});
cm.getColumnByField('ferientyp_kurzbz').component.updateDefinition({
title: this.$p.t('ferien', 'ferientypKurzbz'),
});
cm.getColumnByField('mitarbeiterrelevant').component.updateDefinition({
title: this.$p.t('ferien', 'mitarbeiterrelevant'),
});
cm.getColumnByField('studierendenrelevant').component.updateDefinition({
title: this.$p.t('ferien', 'studierendenrelevant'),
});
cm.getColumnByField('lehre').component.updateDefinition({
title: this.$p.t('ferien', 'lehrePlanbar'),
});
cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('ferien_id', this.$p.t('ferien', 'ferienId'));
setHeader('vondatum', this.$p.t('ferien', 'vondatum'));
setHeader('bisdatum', this.$p.t('ferien', 'bisdatum'));
setHeader('bezeichnung', this.$p.t('global', 'bezeichnung'));
setHeader('oe_kurzbz', this.$p.t('ferien', 'oeKurzbezeichnung'));
setHeader('oe_bezeichnung', this.$p.t('ferien', 'oeBezeichnung'));
setHeader('studienplan_bezeichnung', this.$p.t('global', 'studienplanBezeichnung'));
setHeader('ferientyp_kurzbz', this.$p.t('ferien', 'ferientypKurzbz'));
setHeader('mitarbeiterrelevant', this.$p.t('ferien', 'mitarbeiterrelevant'));
setHeader('studierendenrelevant', this.$p.t('ferien', 'studierendenrelevant'));
setHeader('lehre', this.$p.t('ferien', 'lehrePlanbar'));
setHeader('actions', this.$p.t('global', 'aktionen'));
}
}
]
@@ -210,6 +196,19 @@ export default {
},
actionNew() {
this.$refs.modal.open();
},
importFerien() {
this.$api
.call(ApiFerienverwaltung.importFerien(this.filterVonDatum, this.filterBisDatum))
.then(result => {
this.$fhcAlert.alertSuccess(this.$p.t('ferien', 'importiert') + ": " + result.data);
this.reload();
}
)
.catch(error => {
if (error)
this.$fhcAlert.handleSystemError(error);
});
}
},
created() {
@@ -235,8 +234,8 @@ export default {
</core-navigation-cmpt>
<div class="h-100 d-flex flex-column">
<div class="row justify-content-center">
<div class="col-5">
<div class="row">
<div class="col-4">
<form-input
type="DatePicker"
v-model="filterVonDatum"
@@ -249,7 +248,7 @@ export default {
>
</form-input>
</div>
<div class="col-5">
<div class="col-4">
<form-input
type="DatePicker"
v-model="filterBisDatum"
@@ -262,7 +261,7 @@ export default {
>
</form-input>
</div>
<div class="col-1 align-self-end">
<div class="col-2 align-self-end">
<button
class="btn btn-primary"
@click="reload()"
@@ -272,6 +271,16 @@ export default {
{{ $p.t('ui/anzeigen') }}
</button>
</div>
<div class="col-2 align-self-end justify-content-end">
<button
class="btn btn-secondary"
@click="importFerien()"
:disabled="loading"
>
<i v-if="loading" class="fa fa-spinner fa-spin"></i>
{{ $p.t('lehre/ferienImportieren') }}
</button>
</div>
</div>
<div class="row mt-3">
<div class="col">
@@ -204,3 +204,17 @@ if (!$result = @$db->db_query('SELECT 0 FROM lehre.tbl_ferientyp WHERE 0 = 1'))
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_ferientyp';
}
// Add UNIQUE constraint on fehlercode and fehler_kurzbz on system.tbl_fehler
if ($result = @$db->db_query("SELECT conname FROM pg_constraint WHERE conname = 'uk_tbl_ferien_vondatum_bisdatum_oe_bezeichnung'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "ALTER TABLE lehre.tbl_ferien ADD CONSTRAINT uk_tbl_ferien_vondatum_bisdatum_oe_bezeichnung UNIQUE (vondatum, bisdatum, oe_kurzbz, bezeichnung);";
if (!$db->db_query($qry))
echo '<strong>lehre.tbl_ferien '.$db->db_last_error().'</strong><br>';
else
echo '<br>added UNIQUE constraint on "vondatum", "bisdatum" and "bezeichnung" for lehre.tbl_ferien<br>';
}
}