Merge branch 'feature-77973/StudVw-Feedback_Finetuning_bhmod' into studvw_lvvw_2026_08_rc

This commit is contained in:
Harald Bamberger
2026-08-03 11:18:34 +02:00
12 changed files with 555 additions and 13 deletions
+2
View File
@@ -171,6 +171,8 @@ $config['stv_prestudent_tags'] = [
'unterbrecher_auto' => ['readonly' => true],
'stbtr_erh_auto' => ['readonly' => true],
'jgv_auto' => ['readonly' => true],
'in_auto' => ['readonly' => true],
'preabbr_auto' => ['readonly' => true],
];
$config['userAutomatedTags'] = 'sftest';
+17 -6
View File
@@ -349,16 +349,27 @@ class Tag_Controller extends FHCAPI_Controller
public function rebuildTagsForTypeId()
{
$id = $this->input->post('id');
$ids = $this->input->post('ids');
$typeId = $this->input->post('typeId');
$semester = $this->input->post('sem');
$result = $this->taglib->rebuildTagsForTypeId($typeId, $id, $semester);
$idsSuccess = [];
$idsError = [];
$outputResult = [];
if (isError($result))
return error ('Error occurred during updateAutomatedTags');
foreach ($ids as $id)
{
$result = $this->taglib->rebuildTagsForTypeId($typeId, $id, $semester);
$this->terminateWithSuccess($result);
if (isError($result))
$idsError[] = $id;
else
$idsSuccess[] = $id;
$outputResult[] = $result;
}
$this->terminateWithSuccess([$outputResult, $idsSuccess, $idsError]);
}
private function _setAuthUID()
@@ -401,4 +412,4 @@ class Tag_Controller extends FHCAPI_Controller
));
}
}
}
@@ -0,0 +1,63 @@
<?php
/**
* Description of out_auto
*
* @author ma0068
*/
class CoreIncomingTagLib
{
protected $ci;
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('codex/Bisio_model', 'BisioModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'idArray' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$result = $this->ci->BisioModel->getIncomingsOfSemester($semester);
$data = $result->retval;
$outgoing_data = array_map(function($item) {
return [
'id' => $item->prestudent_id,
'von' => $item->von,
'bis' => $item->bis
];
}, $data);
return (object) array(
'data' => $outgoing_data,
'typeId' => 'prestudent_id',
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$result = $this->ci->BisioModel->isPrestudentIncoming($semester, $prestudent_id);
if (hasData($result)) {
return $result;
}
else
return null;
}
}
@@ -0,0 +1,80 @@
<?php
/**
* Description of prewh_auto
*
* @author ma0068
*/
class CorePreabbrecherTagLib
{
protected $ci;
const TYP_ABMELDUNG = ['Abmeldung', 'AbmeldungStgl'];
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('education/Studierendenantrag_model', 'StudierendenantragModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'idArray' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$this->ci->StudierendenantragModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$this->ci->StudierendenantragModel->db->where_in('typ', self::TYP_ABMELDUNG);
$this->ci->StudierendenantragModel->db->where('studiensemester_kurzbz', $semester);
$result = $this->ci->StudierendenantragModel->getWithLastStatusWhere([
'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_CREATED
]);
$data = $result->retval;
$prewiederholer_data = array_map(function($item) {
return [
'id' => $item->prestudent_id,
'von' => $item->start,
'bis' => $item->ende
];
}, $data);
return (object) array(
'data' => $prewiederholer_data,
'typeId' => 'prestudent_id',
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$this->ci->StudierendenantragModel->addSelect('prestudent_id');
$this->ci->StudierendenantragModel->addSelect('start as von');
$this->ci->StudierendenantragModel->addSelect('ende as bis');
$this->ci->StudierendenantragModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$this->ci->StudierendenantragModel->db->where_in('typ', self::TYP_ABMELDUNG);
$this->ci->StudierendenantragModel->db->where('studiensemester_kurzbz', $semester);
$this->ci->StudierendenantragModel->db->where('prestudent_id', $prestudent_id);
$result = $this->ci->StudierendenantragModel->getWithLastStatusWhere([
'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_CREATED
]);
if(hasData($result))
{
return $result;
}
else
return null;
}
}
+53
View File
@@ -73,6 +73,34 @@ class Bisio_model extends DB_Model
return $this->execQuery($query, array($studiensemester_kurzbz));
}
/**
* Gets incoming students of certain Semester
* @param String studiensemester_kurzbz
* @return array of prestudent_ids
*/
public function getIncomingsOfSemester($studiensemester_kurzbz)
{
$query = "
SELECT DISTINCT ps.prestudent_id, tbl_bisio.von, tbl_bisio.bis
FROM bis.tbl_bisio
JOIN public.tbl_student USING (student_uid)
JOIN public.tbl_prestudent ps USING (prestudent_id)
JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id)
JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz)
WHERE ss.studiensemester_kurzbz = ?
AND nation_code = 'A'
AND (
tbl_bisio.von <= ss.ende
AND (
tbl_bisio.bis >= ss.start
OR tbl_bisio.bis IS NULL
)
)
";
return $this->execQuery($query, array($studiensemester_kurzbz));
}
public function isPrestudentOutgoing($studiensemester_kurzbz, $prestudent_id)
{
$query = "
@@ -97,4 +125,29 @@ class Bisio_model extends DB_Model
return $this->execQuery($query, array($studiensemester_kurzbz, $prestudent_id));
}
public function isPrestudentIncoming($studiensemester_kurzbz, $prestudent_id)
{
$query = "
SELECT
ps.prestudent_id, tbl_bisio.von, tbl_bisio.bis
FROM bis.tbl_bisio
JOIN public.tbl_student USING (student_uid)
JOIN public.tbl_prestudent ps USING (prestudent_id)
JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id)
JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz)
WHERE ss.studiensemester_kurzbz = ?
AND nation_code = 'A'
AND (
tbl_bisio.von <= ss.ende
AND (
tbl_bisio.bis >= ss.start
OR tbl_bisio.bis IS NULL
)
)
AND ps.prestudent_id = ?
";
return $this->execQuery($query, array($studiensemester_kurzbz, $prestudent_id));
}
}
@@ -154,6 +154,7 @@
generateJSsInclude('vendor/npm-asset/primevue/confirmdialog/confirmdialog.min.js');
generateJSsInclude('vendor/npm-asset/primevue/confirmationservice/confirmationservice.min.js');
generateJSsInclude('vendor/npm-asset/primevue/tieredmenu/tieredmenu.min.js');
generateJSsInclude('vendor/npm-asset/primevue/progressbar/progressbar.min.js');
}
if($vuedatepicker11) generateJSsInclude('vendor/vuejs/vuedatepicker_js11/vue-datepicker.iife.js');
+9
View File
@@ -91,6 +91,15 @@
color: black;
}
.tag_skyblue {
background-color: #90D5FF;
color: black;
}
.tag_blush {
background-color: #A83E3E;
color: white;
}
.tag_done {
text-decoration: line-through;
}
@@ -323,8 +323,10 @@ export default {
this.reload();
},
rebuildPrestudentTags(){
const id = [this.headerData[0].prestudent_id];
const params = {
id : this.headerData[0].prestudent_id,
ids: id,
typeId: 'prestudent_id',
sem: this.currentSemester
};
@@ -11,6 +11,7 @@ import {
} from "../../../tabulator/filters/extendedHeaderFilter.js";
import { addTagInTable, deleteTagInTable, updateTagInTable } from "../../../../js/helpers/TagHelper.js";
import { tagFormatter } from "../../../../js/tabulator/formatter/tags.js";
import ModalLoading from "./List/ModalLoading.js";
import ApiTag from "../../../api/factory/stv/tag.js";
import ListFilter from './List/Filter.js';
@@ -30,7 +31,8 @@ export default {
CoreFilterCmpt,
ListNew,
CoreTag,
ListFilter
ListFilter,
ModalLoading
},
directives: {
draggable
@@ -218,7 +220,12 @@ export default {
oldScrollUrl: '',
oldScrollLeft: 0,
oldScrollTop: 0,
allPrestudents: []
allPrestudents: [],
rebuildData: [],
isLoading: false,
progress: -1,
total: -1,
processed: -1
}
},
computed: {
@@ -658,6 +665,73 @@ export default {
this.oldScrollUrl = this.$refs.table.tabulator.getAjaxUrl();
}
},
async rebuild(selected){
const maxbulksize = 25;
const minbulksize = 5;
let bulksize = minbulksize;
this.isLoading = true;
let prestudentIds = [];
if(!selected.length)
prestudentIds = this.$refs.table.tabulator
.getData()
.map(row => row.prestudent_id);
else
prestudentIds = selected.map(item => item.prestudent_id);
this.progress = 0;
this.total = prestudentIds.length;
this.processed = 0;
this.rebuildData = [
[],
[],
[]
];
this.showModal();
while(this.processed < this.total)
{
let iteration_prestudentIds = prestudentIds.slice(
this.processed,
(this.processed + bulksize)
);
const params = {
ids: iteration_prestudentIds,
typeId: 'prestudent_id',
sem: this.studiensemesterKurzbz
};
try
{
const result = await this.$api.call(ApiTag.rebuildTagsforTypeId(params));
this.rebuildData[1] = [...this.rebuildData[1], ...result.data[1]];
this.rebuildData[2] = [...this.rebuildData[2], ...result.data[2]];
}
catch(error) {
this.$fhcAlert.handleSystemError(error);
};
this.processed += iteration_prestudentIds.length;
this.progress = Math.round(this.processed * 100 / this.total);
bulksize = bulksize * 2;
if(bulksize > maxbulksize) bulksize = maxbulksize;
}
if(this.rebuildData[1].length > 0)
{
this.$fhcAlert.alertSuccess(this.$p.t('tag', 'alertSuccessRebuild', { count: this.rebuildData[1].length }));
}
if(this.rebuildData[2].length > 0)
{
this.$fhcAlert.alertError(this.$p.t('tag', 'alertErrorRebuild') + this.rebuildData[2].toString());
}
this.isLoading = false;
},
showModal(){
this.$refs.modalLocked.open();
},
},
created() {
if (this.tagsEnabled) {
@@ -690,6 +764,16 @@ export default {
// TODO(chris): filter component column chooser has no accessibilty features
template: /* html */`
<div class="stv-list h-100 pt-3">
<modal-loading
ref="modalLocked"
:isLoading="isLoading"
:progress="progress"
:total="total"
:processed="processed"
:message="$p.t('tag','messageModalWait')"
>
</modal-loading>
<div
class="tabulator-container d-flex flex-column h-100"
:class="{'has-filter': filter.length}"
@@ -748,6 +832,23 @@ export default {
zuordnung_typ="prestudent_id"
></core-tag>
<button
v-if="!selected.length"
class="btn btn-outline btn-light mb-1"
@click="rebuild(selected)"
:title="$p.t('tag','rebuild_tags') + ' Stg ' + currentSemester"
>
<i class="fa-solid fa-refresh pe-1"></i> STG
</button>
<button
v-else
class="btn btn-outline btn-light mb-1"
@click="rebuild(selected)"
:title="$p.t('tag','rebuild_tags') + ' ' + $p.t('ui','selection') + ' ' + currentSemester"
>
<i class="fa-solid fa-refresh pe-1"></i> {{selected.length}}
</button>
<template v-if="filter.length || headerFilterActive">
<div class="d-flex justify-content-center align-items-center gap-2 ps-4 position-absolute start-50 translate-middle-x">
<p class="text-danger mb-0">
@@ -0,0 +1,84 @@
import BsModal from "../../../Bootstrap/Modal.js";
import FormForm from "../../../Form/Form.js";
export default {
name: "modalLoading",
components: {
BsModal,
FormForm,
PvProgressBar: primevue.progressbar
},
props: {
isLoading: {
type: Boolean,
required: true
},
message: {
type: String,
required: true
},
progress: {
type: Number,
default: -1
},
total: {
type: Number,
default: -1
},
processed: {
type: Number,
default: -1
}
},
data(){
return{}
},
methods:{
open(){
this.$refs.modalLoading.show();
},
closeModal(){
this.$refs.modalLoading.hide();
}
},
template: `
<bs-modal
ref="modalLoading"
noCloseBtn
backdrop='static'
:keyboard='false'
dialog-class="modal-dialog-scrollable"
>
<template #title>
{{ $p.t('ui/loading') }}
</template>
<form-form
ref="formModal"
>
<div class="mt-3">
<div v-if="isLoading">
{{message}}
</div>
<div v-else>
{{$p.t('ui', 'fenster_schliessen')}}
</div>
</div>
</form-form>
<pv-progress-bar v-if="progress >= 0" class="my-3" :value="progress">{{processed}} / {{total}}</pv-progress-bar>
<template #footer>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<button
class="btn btn-primary"
:disabled="isLoading"
@click="closeModal()">
{{$p.t('ui', 'schliessen')}}
</button>
</div>
</template>
</bs-modal>
`,
}
@@ -16,7 +16,7 @@ if(!@$db->db_query("SELECT taglib FROM public.tbl_notiz_typ LIMIT 1"))
$tags = [
[
'typ_kurzbz' => 'wh_auto',
'bezeichnung' => '{WH, R}',
'bezeichnung' => '{WH, WH}',
'style' => 'tag_dark_grey',
'prioritaet' => 11,
'taglib' => 'tags/CoreWiederholerTagLib'
@@ -30,14 +30,14 @@ $tags = [
],
[
'typ_kurzbz' => 'out_auto',
'bezeichnung' => '{Out, Out}',
'bezeichnung' => '{Outgoing, Outgoing}',
'style' => 'tag_limette',
'prioritaet' => 12,
'taglib' => 'tags/CoreOutgoingTagLib'
],
[
'typ_kurzbz' => 'prewh_auto',
'bezeichnung' => '{Pre-WH, pre-R}',
'bezeichnung' => '{Pre-WH, pre-WH}',
'style' => 'tag_light_grey',
'prioritaet' => 13,
'taglib' => 'tags/CorePrewiederholerTagLib'
@@ -51,7 +51,7 @@ $tags = [
],
[
'typ_kurzbz' => 'unterbrecher_auto',
'bezeichnung' => '{UB, I}',
'bezeichnung' => '{UB, UB}',
'style' => 'tag_turquoise',
'prioritaet' => 15,
'taglib' => 'tags/CoreUnterbrecherTagLib'
@@ -70,6 +70,20 @@ $tags = [
'prioritaet' => 17,
'taglib' => 'tags/CoreJgvTagLib'
],
[
'typ_kurzbz' => 'preabbr_auto',
'bezeichnung' => '{Pre-Abbr, pre-Abbr}',
'style' => 'tag_blush',
'prioritaet' => 18,
'taglib' => 'tags/CorePreabbrecherTagLib'
],
[
'typ_kurzbz' => 'in_auto',
'bezeichnung' => '{Incoming, Incoming}',
'style' => 'tag_skyblue',
'prioritaet' => 19,
'taglib' => 'tags/CoreIncomingTagLib'
],
];
+122
View File
@@ -61599,6 +61599,128 @@ I have been informed that I am under no obligation to consent to the transmissio
)
)
),
//Start Finetuning StudVW
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'fenster_schliessen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sie können nun das Fenster schließen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'This window can be closed now',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tag',
'phrase' => 'rebuild_tags',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Neuberechnen Tags',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Rebuild Tags',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'selection',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Auswahl',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'selection',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tag',
'phrase' => 'messageModalWait',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bitte warten Sie, bis alle Tags neu berechnet wurden!',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Please wait until all tags have been recalculated!',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tag',
'phrase' => 'alertSuccessRebuild',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Tags erfolgreich neuberechnet ({count} Prestudent*innnen)',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Tags successfully recalculated ({count} prestudents)',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tag',
'phrase' => 'alertErrorRebuild',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Fehler bei Neuberechnen Tags, PrestudentId(s): ',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Error during Recalculating tags, PrestudentId(s): ',
'description' => '',
'insertvon' => 'system'
)
)
),
//End Finetuning StudVW
);