From 5705cd2d667e6a63893ee3f41cc19695d3f451e4 Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Fri, 15 Nov 2024 09:56:22 +0100 Subject: [PATCH 01/27] Note einblenden --- application/controllers/api/frontend/v1/stv/Config.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index c28c49485..ca7d3ef3d 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -91,12 +91,11 @@ class Config extends FHCAPI_Controller 'title' => $this->p->t('stv', 'tab_resources'), 'component' => './Stv/Studentenverwaltung/Details/Betriebsmittel.js' ]; - /* TODO(chris): Ausgeblendet für Testing $result['grades'] = [ 'title' => $this->p->t('stv', 'tab_grades'), 'component' => './Stv/Studentenverwaltung/Details/Noten.js' ]; - */ + Events::trigger('stv_conf_student', function & () use (&$result) { return $result; From da465fffb182766afe360bf02b026bfb108b8b78 Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Fri, 15 Nov 2024 09:56:58 +0100 Subject: [PATCH 02/27] Points to Grade conversion --- .../Notenschluesselaufteilung_model.php | 30 +++++++++ .../Notenschluesselzuordnung_model.php | 66 +++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/application/models/education/Notenschluesselaufteilung_model.php b/application/models/education/Notenschluesselaufteilung_model.php index 5e0f2f05c..5017a2bbe 100644 --- a/application/models/education/Notenschluesselaufteilung_model.php +++ b/application/models/education/Notenschluesselaufteilung_model.php @@ -11,4 +11,34 @@ class Notenschluesselaufteilung_model extends DB_Model $this->dbTable = 'lehre.tbl_notenschluesselaufteilung'; $this->pk = 'notenschluesselaufteilung_id'; } + + /** + * Liefert die Note zu Punkten einer Lehrveranstaltung + * + * @param number $points + * @param integer $lehrveranstaltung_id + * @param string $studiensemester_kurzbz + * + * @return stdClass + */ + public function getNote($points, $lehrveranstaltung_id, $studiensemester_kurzbz) + { + $this->load->model('education/Notenschluesselzuordnung_model', 'NotenschluesselzuordnungModel'); + $notenschluessel_kurzbz = $this->NotenschluesselzuordnungModel->getKurzbzForLv($lehrveranstaltung_id, $studiensemester_kurzbz); + + $this->addSelect("note"); + $this->addOrder("punkte", "DESC"); + $this->addLimit(1); + + $result = $this->loadWhere([ + "notenschluessel_kurzbz" => $notenschluessel_kurzbz, + "punkte <=" => $points + ]); + + if (isError($result)) + return $result; + if (!hasData($result)) + return error("Es wurde kein passender eintrag gefunden"); // TODO(chris): phrase + return success(current(getData($result))->note); + } } diff --git a/application/models/education/Notenschluesselzuordnung_model.php b/application/models/education/Notenschluesselzuordnung_model.php index e6881e12b..972d7adb0 100644 --- a/application/models/education/Notenschluesselzuordnung_model.php +++ b/application/models/education/Notenschluesselzuordnung_model.php @@ -11,4 +11,70 @@ class Notenschluesselzuordnung_model extends DB_Model $this->dbTable = 'lehre.tbl_notenschluesselzuordnung'; $this->pk = 'notenschluesselzuordnung_id'; } + + /** + * Liefert den passenden Notenschluessel zu einer Lehrveranstaltung + * + * @param integer $lehrveranstaltung_id + * @param string $studiensemester_kurzbz + * + * @return integer|null + */ + public function getKurzbzForLv($lehrveranstaltung_id, $studiensemester_kurzbz) + { + $this->addSelect("notenschluessel_kurzbz"); + + $this->db->where("lehrveranstaltung_id", $lehrveranstaltung_id); + if ($studiensemester_kurzbz) { + $this->db->where_in("studiensemester_kurzbz", [$studiensemester_kurzbz, null]); + } else { + $this->db->where("studiensemester_kurzbz", null); + } + + $result = $this->load(); + + if (!isError($result) && hasData($result)) + return current(getData($result))->notenschluessel_kurzbz; + + + $this->addSelect("notenschluessel_kurzbz"); + + $this->addJoin("( + WITH RECURSIVE oes(oe_kurzbz, oe_parent_kurzbz, depth) AS ( + SELECT oe_kurzbz, oe_parent_kurzbz, 1 + FROM public.tbl_organisationseinheit + WHERE oe_kurzbz = ( + SELECT + oe_kurzbz + FROM + lehre.tbl_lehrveranstaltung + WHERE + lehrveranstaltung_id = " . $this->escape($lehrveranstaltung_id) . " + ) + UNION ALL + SELECT o.oe_kurzbz, o.oe_parent_kurzbz, oes.depth+1 AS depth + FROM public.tbl_organisationseinheit o, oes + WHERE o.oe_kurzbz = oes.oe_parent_kurzbz + AND aktiv = true + ) + SELECT * FROM oes + ) oes", "oe_kurzbz"); + + $this->addOrder("depth", "ASC"); + $this->addLimit(1); + + if ($studiensemester_kurzbz) { + $this->db->where_in("studiensemester_kurzbz", [$studiensemester_kurzbz, null]); + $result = $this->load(); + } else { + $result = $this->loadWhere([ + "studiensemester_kurzbz" => null + ]); + } + + if (isError($result) || !hasData($result)) + return null; + + return current(getData($result))->notenschluessel_kurzbz; + } } From d65058741824d14caf7dae3227d2d417c0dd1cf2 Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Mon, 18 Nov 2024 09:58:59 +0100 Subject: [PATCH 03/27] use fhcApi for grades tab --- .../frontend/v1/stv/Grades.php} | 120 +++++++++++------- public/js/api/stv.js | 2 + public/js/api/stv/grades.js | 15 +++ .../Details/Noten/Zeugnis.js | 31 +++-- .../Details/Noten/Zeugnis/Actions.js | 9 +- 5 files changed, 115 insertions(+), 62 deletions(-) rename application/controllers/{components/stv/Noten.php => api/frontend/v1/stv/Grades.php} (57%) create mode 100644 public/js/api/stv/grades.js diff --git a/application/controllers/components/stv/Noten.php b/application/controllers/api/frontend/v1/stv/Grades.php similarity index 57% rename from application/controllers/components/stv/Noten.php rename to application/controllers/api/frontend/v1/stv/Grades.php index fb61de065..d521466cc 100644 --- a/application/controllers/components/stv/Noten.php +++ b/application/controllers/api/frontend/v1/stv/Grades.php @@ -1,36 +1,57 @@ . + */ if (! defined('BASEPATH')) exit('No direct script access allowed'); -class Noten extends Auth_Controller +/** + * This controller operates between (interface) the JS (GUI) and the back-end + * Provides data to the ajax get calls about grades + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Grades extends FHCAPI_Controller { public function __construct() { parent::__construct([ - 'get' => 'student/noten:r', - 'getZeugnis' => 'student/noten:r', - 'update' => ['admin:w', 'assistenz:w'] + 'list' => 'student/noten:r', + 'getCertificate' => 'student/noten:r', + 'updateCertificate' => ['admin:w', 'assistenz:w'], + 'getGradeFromPoints' => 'student/noten:r' ]); // Load Libraries $this->load->library('VariableLib', ['uid' => getAuthUID()]); } - public function get() + public function list() { $this->load->model('codex/Note_model', 'NoteModel'); - $result = $this->NoteModel->addOrder('note'); + $this->NoteModel->addOrder('note'); $result = $this->NoteModel->load(); - if (isError($result)) { - $this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR); - } + + $grades = $this->getDataOrTerminateWithError($result); - return $this->outputJson($result); + $this->terminateWithSuccess($grades); } - public function getZeugnis($prestudent_id, $all = null) + public function getCertificate($prestudent_id, $all = null) { $this->load->model('crm/Student_model', 'StudentModel'); $this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel'); @@ -38,26 +59,25 @@ class Noten extends Auth_Controller $result = $this->StudentModel->loadWhere([ 'prestudent_id' => $prestudent_id ]); - if (isError($result)) { - $this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR); - return $this->outputJson($result); - } - if (!hasData($result)) - return $this->outputJsonSuccess(null); + + $student = $this->getDataOrTerminateWithError($result); + if (!$student) + $this->terminateWithSuccess([]); - $student_uid = current(getData($result))->student_uid; + + $student_uid = current($student)->student_uid; $studiensemester_kurzbz = ($all === null) ? $this->variablelib->getVar('semester_aktuell') : null; - $result = $this->ZeugnisnoteModel->getZeugnisnoten($student_uid, $studiensemester_kurzbz); - if (isError($result)) { - $this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR); - } - return $this->outputJson($result); + $result = $this->ZeugnisnoteModel->getZeugnisnoten($student_uid, $studiensemester_kurzbz); + + $grades = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($grades); } - public function update() + public function updateCertificate() { $this->load->model('crm/Student_model', 'StudentModel'); $this->load->model('organisation/Studienplan_model', 'StudienplanModel'); @@ -66,13 +86,10 @@ class Noten extends Auth_Controller $this->load->library('form_validation'); - $_POST = json_decode(utf8_encode($this->input->raw_input_stream), true); - if (empty($_POST) || !is_array(current($_POST))) { $result = $this->hasPermissionUpdate($this->input->post('lehrveranstaltung_id'), $this->input->post('student_uid')); if (isError($result)) { - $this->output->set_status_header(REST_Controller::HTTP_FORBIDDEN); - return $this->outputJson($result); + $this->terminateWithError(getError($result), self::ERROR_TYPE_AUTH, REST_Controller::HTTP_FORBIDDEN); } $this->form_validation->set_rules('lehrveranstaltung_id', 'Lehrverantaltung ID', 'required|numeric'); @@ -86,8 +103,7 @@ class Noten extends Auth_Controller $uid = isset($data['student_uid']) ? $data['student_uid'] : null; $result = $this->hasPermissionUpdate($lvid, $uid); if (isError($result)) { - $this->output->set_status_header(REST_Controller::HTTP_FORBIDDEN); - return $this->outputJson($result); + $this->terminateWithError(getError($result), self::ERROR_TYPE_AUTH, REST_Controller::HTTP_FORBIDDEN); } $this->form_validation->set_rules($i . '[lehrveranstaltung_id]', '#' . $i . ' Lehrverantaltung ID', 'required|numeric'); @@ -97,13 +113,11 @@ class Noten extends Auth_Controller } $post = $_POST; } - if ($this->form_validation->run() == false) { - $this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST); - return $this->outputJsonError($this->form_validation->error_array()); - } + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); - $final_result = success(); $this->ZeugnisnoteModel->db->trans_start(); + $authUID = getAuthUID(); foreach ($post as $i => $data) { $note = $data['note']; @@ -112,21 +126,39 @@ class Noten extends Auth_Controller 'note' => $note, 'benotungsdatum' => date('c'), 'updateamum' => date('c'), - 'updatevon' => getAuthUID() + 'updatevon' => $authUID ]); - if (isError($result)) { - $final_result = $result; - break; - } + $this->getDataOrTerminateWithError($result); } $this->ZeugnisnoteModel->db->trans_complete(); - if (isError($final_result)) { - $this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR); - } + $this->terminateWithSuccess(true); + } - $this->outputJson($final_result); + public function getGradeFromPoints() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules("lehrveranstaltung_id", "Lehrverantaltung ID", "required|integer"); // TODO(chris): phrase + $this->form_validation->set_rules("points", "Punkte", "required|numeric"); // TODO(chris): phrase + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $this->load->model('education/Notenschluesselaufteilung_model', 'NotenschluesselaufteilungModel'); + + $studiensemester_kurzbz = $this->variablelib->getVar('semester_aktuell'); + + $result = $this->NotenschluesselaufteilungModel->getNote( + $this->input->post('points'), + $this->input->post('lehrveranstaltung_id'), + $studiensemester_kurzbz + ); + + $note = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($note); } protected function hasPermissionUpdate($lehrveranstaltung_id, $student_uid) diff --git a/public/js/api/stv.js b/public/js/api/stv.js index 14fcc6661..9a2f08f1d 100644 --- a/public/js/api/stv.js +++ b/public/js/api/stv.js @@ -2,12 +2,14 @@ import verband from './stv/verband.js'; import students from './stv/students.js'; import filter from './stv/filter.js'; import konto from './stv/konto.js'; +import grades from './stv/grades.js'; export default { verband, students, filter, konto, + grades, configStudent() { return this.$fhcApi.get('api/frontend/v1/stv/config/student'); }, diff --git a/public/js/api/stv/grades.js b/public/js/api/stv/grades.js new file mode 100644 index 000000000..ec34c3b0c --- /dev/null +++ b/public/js/api/stv/grades.js @@ -0,0 +1,15 @@ +export default { + list() { + return this.$fhcApi.get('api/frontend/v1/stv/grades/list'); + }, + getCertificate(prestudent_id, all) { + all = all ? '/all' : ''; + return this.$fhcApi.get('api/frontend/v1/stv/grades/getCertificate/' + prestudent_id + all); + }, + updateCertificate(data) { + return this.$fhcApi.post('api/frontend/v1/stv/grades/updateCertificate', data); + }, + getGradeFromPoints(points) { + return this.$fhcApi.post('api/frontend/v1/stv/grades/getGradeFromPoints', data); + } +} \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js index caeb0cfaa..698d837e2 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js @@ -1,5 +1,4 @@ import {CoreFilterCmpt} from "../../../../filter/Filter.js"; -import {CoreRESTClient} from '../../../../../RESTClient.js'; import ZeugnisActions from './Zeugnis/Actions.js'; const LOCAL_STORAGE_ID = 'stv_details_noten_zeugnis_2024-01-11_stdsem_all'; @@ -20,18 +19,24 @@ export default { }; }, computed: { - ajaxURL() { - return CoreRESTClient._generateRouterURI('components/stv/Noten/getZeugnis/' + this.student.prestudent_id + this.stdsem); - }, tabulatorOptions() { return { - ajaxURL: this.ajaxURL, + ajaxURL: 'dummy', + ajaxRequestFunc: (url, config, params) => { + return this.$fhcApi.factory.stv.grades.getCertificate(params.prestudent_id, params.stdsem); + }, + ajaxParams: () => { + return { + prestudent_id: this.student.prestudent_id, + stdsem: this.stdsem + }; + }, ajaxResponse: (url, params, response) => { - if (!response.retval) + if (!response.data) this.validStudent = false; else this.validStudent = true; - return response.retval || []; + return response.data || []; }, columns: [ { field: 'zeugnis', title: 'Zeugnis', formatter: 'tickCross' }, @@ -63,15 +68,17 @@ export default { } }, watch: { - ajaxURL(n) { - if (this.$refs.table) - this.$refs.table.tabulator.setData(n); + student(n) { + this.$refs.table.reloadTable(); + }, + stdsem(n) { + this.$refs.table.reloadTable(); } }, methods: { setGrades(selected) { - CoreRESTClient - .post('components/stv/Noten/update', selected) + this.$fhcApi.factory + .stv.grades.updateCertificate(selected) .then(this.$refs.table.reloadTable) .catch(this.$fhcAlert.handleFormValidation); }, diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis/Actions.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis/Actions.js index 7e8e2cbe5..a6c37a0ea 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis/Actions.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis/Actions.js @@ -1,5 +1,3 @@ -import {CoreRESTClient} from '../../../../../../RESTClient.js'; - export default { emits: [ 'setGrades' @@ -36,11 +34,10 @@ export default { } }, created() { - CoreRESTClient - .get('components/stv/Noten/get') - .then(result => result.data) + this.$fhcApi.factory + .stv.grades.list() .then(result => { - this.grades = result.retval; + this.grades = result.data; }) .catch(this.$fhcAlert.handleSystemError); }, From 5369c95e77ee22db19ea4b0742b7daa3ece8383f Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Wed, 20 Nov 2024 11:05:05 +0100 Subject: [PATCH 04/27] Scroll Bug Notizen Tab --- .../js/components/Stv/Studentenverwaltung/Details/Notizen.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Notizen.js b/public/js/components/Stv/Studentenverwaltung/Details/Notizen.js index ec8c18711..7fed33d0e 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Notizen.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Notizen.js @@ -8,9 +8,10 @@ export default { modelValue: Object }, template: ` -
+
Date: Wed, 20 Nov 2024 11:06:28 +0100 Subject: [PATCH 05/27] Teacher Proposal --- .../api/frontend/v1/stv/Grades.php | 209 ++++++++++++++++++ .../models/education/Anwesenheit_model.php | 189 ++++++++++++++++ .../education/Lehrveranstaltung_model.php | 31 +++ .../models/education/Lvgesamtnote_model.php | 34 +++ .../models/person/Benutzerfunktion_model.php | 47 ++++ public/js/api/stv/grades.js | 17 ++ .../Stv/Studentenverwaltung/Details/Noten.js | 15 +- 7 files changed, 539 insertions(+), 3 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Grades.php b/application/controllers/api/frontend/v1/stv/Grades.php index d521466cc..f91ace9a1 100644 --- a/application/controllers/api/frontend/v1/stv/Grades.php +++ b/application/controllers/api/frontend/v1/stv/Grades.php @@ -30,7 +30,9 @@ class Grades extends FHCAPI_Controller parent::__construct([ 'list' => 'student/noten:r', 'getCertificate' => 'student/noten:r', + 'getTeacherProposal' => 'student/noten:r', 'updateCertificate' => ['admin:w', 'assistenz:w'], + 'copyTeacherProposalToCertificate' => 'student/noten:w', 'getGradeFromPoints' => 'student/noten:r' ]); @@ -77,6 +79,32 @@ class Grades extends FHCAPI_Controller $this->terminateWithSuccess($grades); } + public function getTeacherProposal($prestudent_id, $all = null) + { + $this->load->model('crm/Student_model', 'StudentModel'); + $this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel'); + + $result = $this->StudentModel->loadWhere([ + 'prestudent_id' => $prestudent_id + ]); + + $student = $this->getDataOrTerminateWithError($result); + if (!$student) + $this->terminateWithSuccess([]); + + + $student_uid = current($student)->student_uid; + + $studiensemester_kurzbz = ($all === null) ? $this->variablelib->getVar('semester_aktuell') : null; + + + $result = $this->LvgesamtnoteModel->getLvGesamtNoten(null, $student_uid, $studiensemester_kurzbz); + + $grades = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($grades); + } + public function updateCertificate() { $this->load->model('crm/Student_model', 'StudentModel'); @@ -136,6 +164,95 @@ class Grades extends FHCAPI_Controller $this->terminateWithSuccess(true); } + public function copyTeacherProposalToCertificate() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules("lehrveranstaltung_id", "Lehrverantaltung ID", "required|integer"); // TODO(chris): phrase + $this->form_validation->set_rules("student_uid", "Student UID", "required"); // TODO(chris): phrase + $this->form_validation->set_rules("studiensemester_kurzbz", "Studiensemester", "required"); // TODO(chris): phrase + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $lehrveranstaltung_id = $this->input->post('lehrveranstaltung_id'); + $student_uid = $this->input->post('student_uid'); + $studiensemester_kurzbz = $this->input->post('studiensemester_kurzbz'); + $authUID = getAuthUID(); + + // NOTE(chris): Stg Permissions + if (!$this->hasPermissionCopy($lehrveranstaltung_id, $student_uid)) + return $this->_outputAuthError([$this->router->method => 'student/noten']); + + $this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel'); + + $result = $this->LvgesamtnoteModel->load([ + $student_uid, + $studiensemester_kurzbz, + $lehrveranstaltung_id + ]); + $teacherGrade = $this->getDataOrTerminateWithError($result); + + if (!$teacherGrade) + show_404(); + + $teacherGrade = current($teacherGrade); + + $data = [ + 'note' => $teacherGrade->note, + 'punkte' => $teacherGrade->punkte, + 'uebernahmedatum' => date('c'), + 'benotungsdatum' => $teacherGrade->benotungsdatum, + 'bemerkung' => $teacherGrade->bemerkung + ]; + + $this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel'); + + $this->ZeugnisnoteModel->addJoin('lehre.tbl_note n', 'note'); + $result = $this->ZeugnisnoteModel->load([ + $studiensemester_kurzbz, + $student_uid, + $lehrveranstaltung_id + ]); + $certificateGrade = $this->getDataOrTerminateWithError($result); + + if ($certificateGrade) { + $certificateGrade = current($certificateGrade); + + if (!$certificateGrade->lkt_ueberschreibbar) + $this->terminateWithError("Nicht überschreibbar"); // TODO(chris): phrase + + // NOTE(chris): update + $data['updateamum'] = $data['uebernahmedatum']; + $data['updatevon'] = $authUID; + + $this->ZeugnisnoteModel->update([ + $studiensemester_kurzbz, + $student_uid, + $lehrveranstaltung_id + ], $data); + } else { + // NOTE(chris): insert + $data['insertamum'] = $data['uebernahmedatum']; + $data['insertvon'] = $authUID; + $data['lehrveranstaltung_id'] = $lehrveranstaltung_id; + $data['student_uid'] = $student_uid; + $data['studiensemester_kurzbz'] = $studiensemester_kurzbz; + + $this->ZeugnisnoteModel->insert($data); + + // TODO(chris): FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN + if (defined('FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN') + && FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN) { + $result = $this->addTestsForGrade($studiensemester_kurzbz, $student_uid, $lehrveranstaltung_id, $teacherGrade->note, $teacherGrade->punkte); + $this->getDataOrTerminateWithError($result);// TODO(chris): terminate? + } + } + + + $this->terminateWithSuccess(); + } + public function getGradeFromPoints() { $this->load->library('form_validation'); @@ -161,6 +278,68 @@ class Grades extends FHCAPI_Controller $this->terminateWithSuccess($note); } + protected function addTestsForGrade($studiensemester_kurzbz, $student_uid, $lehrveranstaltung_id, $note, $punkte) + { + $this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + + // Get Lehreinheit + $result = $this->LehrveranstaltungModel->getLeByStudent($student_uid, $studiensemester_kurzbz, $lehrveranstaltung_id); + + if (isError($result)) + return $result; + if (!hasData($result)) + return error('Fehler beim Ermitteln der Lehreinheit ID'); // TODO(chris): phrase + $le = current(getData($result)); + + // Prepare + $this->load->model('education/LePruefung_model', 'LePruefungModel'); + $data = [ + "student_uid" => $student_uid, + "lehreinheit_id" => $le->lehreinheit_id, + "datum" => date('Y-m-d'), + "pruefungstyp_kurzbz" => "Termin1", // TODO(chris): const? + "note" => $note + ]; + + if (defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE) + $data["punkte"] = $punkte; + + // Get Anwesenheit + $this->load->model('education/Anwesenheit_model', 'AnwesenheitModel'); + $result = $this->AnwesenheitModel->loadAnwesenheitStudiensemester($studiensemester_kurzbz, $student_uid, $lehrveranstaltung_id); + if (isError($result)) + return $result; + $anwesenheit = getData($result); + + if ($anwesenheit && (float)current($anwesenheit)->prozent < FAS_ANWESENHEIT_ROT) { + // Get Anwesenheitsbefreiung + $this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel'); + $result = $this->BenutzerfunktionModel->getBenutzerFunktionByUidInStdsem($student_uid, $studiensemester_kurzbz, 'awbefreit'); + + if (isError($result)) + return $result; + + $anwesenheitsbefreit = hasData($result); + + // Wenn nicht Anwesenheitsbefreit und Anwesenheit unter einem bestimmten Prozentsatz fällt dann wird ein Pruefungsantritt abgezogen + if (!$anwesenheitsbefreit) { + $data2 = $data; + $data2["note"] = 7; // TODO(chris): const? + if (isset($data2["punkte"])) + unset($data2["punkte"]); + + $result = $this->LePruefungModel->insert($data2); + + if (isError($result)) + return $result; + + $data["pruefungstyp_kurzbz"] = "Termin2"; // TODO(chris): const? + } + } + + return $this->LePruefungModel->insert($data); + } + protected function hasPermissionUpdate($lehrveranstaltung_id, $student_uid) { // TODO(chris): error phrases! @@ -197,4 +376,34 @@ class Grades extends FHCAPI_Controller return error('Forbidden'); } + + protected function hasPermissionCopy($lehrveranstaltung_id, $student_uid) + { + if ($lehrveranstaltung_id === null || $student_uid === null) + return true; + + $this->load->model('crm/Student_model', 'StudentModel'); + + $result = $this->StudentModel->load([$student_uid]); + if (isError($result) || !hasData($result)) + return false; + + $student = current(getData($result)); + + if ($this->permissionlib->isBerechtigt('student/noten', 'suid', $student->studiengang_kz)) + return true; + + $this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + + $result = $this->LehrveranstaltungModel->load($lehrveranstaltung_id); + if (isError($result) || !hasData($result)) + return false; + + $oe = current(getData($result)); + + if ($this->permissionlib->isBerechtigt('student/noten', 'suid', $oe->oe_kurzbz)) + return true; + + return false; + } } diff --git a/application/models/education/Anwesenheit_model.php b/application/models/education/Anwesenheit_model.php index 80a1fc111..d323e0957 100644 --- a/application/models/education/Anwesenheit_model.php +++ b/application/models/education/Anwesenheit_model.php @@ -11,4 +11,193 @@ class Anwesenheit_model extends DB_Model $this->dbTable = 'campus.tbl_anwesenheit'; $this->pk = 'anwesenheit_id'; } + + /** + * Laedt die Anwesenheiten in Prozent von Studierenden bei Lehrveranstaltungen + * Wenn die StudentUID uebergeben wird, werden alle Lehrveranstaltungen zu denen der Studierenden zugeteilt ist inkl Prozent der Anwesenheit + * Wenn die LehrveranstaltungID uebergeben wird, werden alle Studierenden geholt die zugeteilt sind inkl Prozent der Anwesenheit + * Es werden pro Student die Anwesenheiten berechnet aufgrund der Lehreinheit zu der sie zugeordnet sind + * + * @param string $studiensemester_kurzbz + * @param string|null (optional) $student_uid + * @param integer|null (optional) $lehrveranstaltung_id + * + * @return stdClass + */ + public function loadAnwesenheitStudiensemester($studiensemester_kurzbz, $student_uid = null, $lehrveranstaltung_id = null) + { + $this->addSelect("vorname"); + $this->addSelect("nachname"); + $this->addSelect("wahlname"); + $this->addSelect("lehrveranstaltung_id"); + $this->addSelect("bezeichnung"); + $this->addSelect("gruppe"); + $this->addSelect("student_uid AS uid"); + $this->addSelect("COUNT(stundenplan_id) AS gesamtstunden"); + $this->addSelect("COALESCE(anwesend.summe, 0) AS anwesend"); + $this->addSelect("COALESCE(nichtanwesend.summe, 0) AS nichtanwesend"); + $this->addSelect("COALESCE(anwesend.summe, 0) + COALESCE(nichtanwesend.summe, 0) AS erfassteanwesenheit"); + $this->addSelect("CASE + WHEN COUNT(stundenplan_id) = 0 OR COALESCE(anwesend.summe, 0) + COALESCE(nichtanwesend.summe, 0) = 0 + THEN 100 + ELSE TRUNC(100-(100/COUNT(stundenplan_id)*COALESCE(nichtanwesend.summe, 0)), 2) + END AS prozent"); + + + $this->db->join("( + SELECT + semester::text AS gruppe, + public.tbl_studentlehrverband.studiensemester_kurzbz, + student_uid, + studiengang_kz + FROM public.tbl_studentlehrverband + WHERE studiensemester_kurzbz = " . $this->escape($studiensemester_kurzbz) . " + + UNION + + SELECT + semester || verband AS gruppe, + public.tbl_studentlehrverband.studiensemester_kurzbz, + student_uid, + studiengang_kz + FROM public.tbl_studentlehrverband + WHERE studiensemester_kurzbz = " . $this->escape($studiensemester_kurzbz) . " + + UNION + + SELECT + semester || verband || gruppe AS gruppe, + public.tbl_studentlehrverband.studiensemester_kurzbz, + student_uid, + studiengang_kz + FROM public.tbl_studentlehrverband + WHERE studiensemester_kurzbz = " . $this->escape($studiensemester_kurzbz) . " + + UNION + + SELECT + gruppe_kurzbz AS gruppe, + public.tbl_benutzergruppe.studiensemester_kurzbz, + uid AS student_uid, + studiengang_kz + FROM public.tbl_benutzergruppe + JOIN public.tbl_gruppe USING (gruppe_kurzbz) + WHERE studiensemester_kurzbz = " . $this->escape($studiensemester_kurzbz) . " + ) a", "gruppe,studiensemester_kurzbz,studiengang_kz", "", false); + $this->addJoin("public.tbl_benutzer b", "b.uid = student_uid"); + $this->addJoin("public.tbl_person p", "person_id"); + $this->db->join("( + SELECT + lehrveranstaltung_id, + studiensemester_kurzbz, uid AS student_uid, + SUM(einheiten) AS summe + FROM campus.tbl_anwesenheit a + JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id) + JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id) + WHERE anwesend = TRUE + AND studiensemester_kurzbz = " . $this->escape($studiensemester_kurzbz) . " + GROUP BY + lehrveranstaltung_id, + bezeichnung, + uid, + studiensemester_kurzbz + ) anwesend", "lehrveranstaltung_id,student_uid,studiensemester_kurzbz", "LEFT", false); + $this->db->join("( + SELECT + lehrveranstaltung_id, + studiensemester_kurzbz, + uid AS student_uid, + SUM(einheiten) AS summe + FROM campus.tbl_anwesenheit a + JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id) + JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id) + WHERE anwesend = FALSE + AND studiensemester_kurzbz = " . $this->escape($studiensemester_kurzbz) . " + GROUP BY + lehrveranstaltung_id, bezeichnung, uid, studiensemester_kurzbz + ) nichtanwesend", "lehrveranstaltung_id,student_uid,studiensemester_kurzbz", "LEFT", false); // TODO(chris): use USING + + $this->addGroupBy("vorname"); + $this->addGroupBy("nachname"); + $this->addGroupBy("wahlname"); + $this->addGroupBy("lehrveranstaltung_id"); + $this->addGroupBy("bezeichnung"); + $this->addGroupBy("gruppe"); + $this->addGroupBy("student_uid"); + $this->addGroupBy("anwesend.summe"); + $this->addGroupBy("nichtanwesend.summe"); + + + $where = [ + "lehrveranstaltung_id >" => 0 + ]; + + if ($student_uid) + $where["student_uid"] = $student_uid; + + if ($lehrveranstaltung_id) + $where["lehrveranstaltung_id"] = $lehrveranstaltung_id; + + if ($lehrveranstaltung_id) { + $this->addOrder("nachname"); + $this->addOrder("vorname"); + } elseif ($student_uid) { + $this->addOrder("bezeichnung"); + } + + + $tmp = $this->dbTable; + + $this->dbTable = "( + SELECT + SUM(stundenplan_id) AS stundenplan_id, + datum, + stunde, + lehrveranstaltung_id, + bezeichnung, + studiensemester_kurzbz, + studiengang_kz, + TRIM( + CASE + WHEN stp.gruppe_kurzbz IS NOT NULL + THEN stp.gruppe_kurzbz + ELSE stp.semester || ( + CASE + WHEN verband IS NULL + THEN '' + ELSE stp.verband + END + ) || ( + CASE + WHEN stp.gruppe IS NULL + THEN '' + ELSE stp.gruppe + END + ) + END + ) AS gruppe + FROM lehre.tbl_lehrveranstaltung lv + JOIN lehre.tbl_lehreinheit le USING (lehrveranstaltung_id) + JOIN lehre.tbl_stundenplan stp USING (lehreinheit_id,studiengang_kz) + WHERE studiensemester_kurzbz = " . $this->escape($studiensemester_kurzbz) . " + AND (titel NOT LIKE '%Nebenprüfung%' OR titel IS NULL) + GROUP BY + datum, + stunde, + lehrveranstaltung_id, + bezeichnung, + studiensemester_kurzbz, + studiengang_kz, + stp.gruppe_kurzbz, + stp.semester, + stp.verband, + stp.gruppe + ) x"; + + $result = $this->loadWhere($where); + + $this->dbTable = $tmp; + + return $result; + } } diff --git a/application/models/education/Lehrveranstaltung_model.php b/application/models/education/Lehrveranstaltung_model.php index de8ebc5c9..ddbf000cb 100644 --- a/application/models/education/Lehrveranstaltung_model.php +++ b/application/models/education/Lehrveranstaltung_model.php @@ -623,6 +623,37 @@ class Lehrveranstaltung_model extends DB_Model return $this->execQuery($query, array($uid, $studiensemester_kurzbz, $lehrveranstaltung_id)); } + /** + * Get Lehreinheit. + * + * @param $student_uid + * @param $studiensemester_kurzbz + * @param $lehrveranstaltung_id + * + * @return stdClass + */ + public function getLeByStudent($student_uid, $studiensemester_kurzbz, $lehrveranstaltung_id) + { + $this->addSelect("lehreinheit_id"); + + $this->addOrder("lehreinheit_id", "ASC"); + + $this->addLimit(1); + + $tmp = $this->dbTable; + $this->dbTable = "campus.vw_student_lehrveranstaltung"; + + $result = $this->loadWhere([ + "uid" => $student_uid, + "lehrveranstaltung_id" => $lehrveranstaltung_id, + "studiensemester_kurzbz" => $studiensemester_kurzbz + ]); + + $this->dbTable = $tmp; + + return $result; + } + /** * Sucht nach LV Templates und gibt Id und Label ("bezeichnung [kurzbz]") aus * Diese funktion ist für autocomplete gedacht diff --git a/application/models/education/Lvgesamtnote_model.php b/application/models/education/Lvgesamtnote_model.php index 975833287..2f6b6cf02 100644 --- a/application/models/education/Lvgesamtnote_model.php +++ b/application/models/education/Lvgesamtnote_model.php @@ -12,4 +12,38 @@ class Lvgesamtnote_model extends DB_Model $this->pk = array('student_uid', 'studiensemester_kurzbz', 'lehrveranstaltung_id'); $this->hasSequence = false; } + + /** + * Laedt die Noten + * + * @param $lehrveranstaltung_id + * @param $student_uid + * @param $studiensemester_kurzbz + * + * @return stdClass + */ + public function getLvGesamtNoten($lehrveranstaltung_id, $student_uid, $studiensemester_kurzbz) + { + $this->addSelect($this->dbTable . ".*"); + $this->addSelect("n.bezeichnung AS note_bezeichnung"); + $this->addSelect("lv.bezeichnung AS lehrveranstaltung_bezeichnung"); + $this->addSelect("lv.studiengang_kz"); + $this->addSelect("UPPER(stg.typ || stg.kurzbz) AS studiengang"); + + $this->addJoin("lehre.tbl_note n", "note"); + $this->addJoin("lehre.tbl_lehrveranstaltung lv", "lehrveranstaltung_id"); + $this->addJoin("public.tbl_studiengang stg", "studiengang_kz"); + + $this->db->where($this->dbTable . ".freigabedatum <", "NOW()", false); + + $where = []; + if ($studiensemester_kurzbz) + $where[$this->dbTable . ".studiensemester_kurzbz"] = $studiensemester_kurzbz; + if ($lehrveranstaltung_id) + $where[$this->dbTable . ".lehrveranstaltung_id"] = $lehrveranstaltung_id; + if ($student_uid) + $where[$this->dbTable . ".student_uid"] = $student_uid; + + return $this->loadWhere($where); + } } diff --git a/application/models/person/Benutzerfunktion_model.php b/application/models/person/Benutzerfunktion_model.php index e44281a92..52eb38b95 100644 --- a/application/models/person/Benutzerfunktion_model.php +++ b/application/models/person/Benutzerfunktion_model.php @@ -50,6 +50,53 @@ class Benutzerfunktion_model extends DB_Model return $this->execQuery($qry, $params); } + /** + * Lädt alle Benutzerfunktionen zu einer UID im Zeitraum eines Studiensemesters + * + * @param string $uid + * @param string $stdsem + * @param string (optional) $funktion_kurzbz + * + * @return stdClass + */ + public function getBenutzerFunktionByUidInStdsem($uid, $stdsem, $funktion_kurzbz = null) + { + $stdsemEscaped = $this->escape($stdsem); + $this->addSelect($this->dbTable . ".*"); + $this->addSelect("oe.bezeichnung AS organisationseinheit_bezeichnung"); + $this->addSelect("oe.organisationseinheittyp_kurzbz"); + + $this->addJoin("public.tbl_organisationseinheit oe", "oe_kurzbz"); + + $this->db->where("uid", $uid); + + if ($funktion_kurzbz !== null) + $this->db->where("funktion_kurzbz", $funktion_kurzbz); + + $this->db->group_start(); + $this->db->where("datum_bis IS NULL"); + $this->db->or_where("datum_bis >=", "( + SELECT start + FROM public.tbl_studiensemester + WHERE studiensemester_kurzbz = " . $stdsemEscaped . " + )", false); + $this->db->group_end(); + + $this->db->group_start(); + $this->db->where("datum_von IS NULL"); + $this->db->or_where("datum_von <=", "( + SELECT ende + FROM public.tbl_studiensemester + WHERE studiensemester_kurzbz = " . $stdsemEscaped . " + )", false); + $this->db->group_end(); + + $this->addOrder("datum_bis", "NULLS LAST"); + $this->addOrder("datum_von", "NULLS LAST"); + + return $this->load(); + } + /** * Get the Benutzerfunktion using the person_id */ diff --git a/public/js/api/stv/grades.js b/public/js/api/stv/grades.js index ec34c3b0c..0a2d15e3b 100644 --- a/public/js/api/stv/grades.js +++ b/public/js/api/stv/grades.js @@ -6,9 +6,26 @@ export default { all = all ? '/all' : ''; return this.$fhcApi.get('api/frontend/v1/stv/grades/getCertificate/' + prestudent_id + all); }, + getTeacherProposal(prestudent_id, all) { + all = all ? '/all' : ''; + return this.$fhcApi.get('api/frontend/v1/stv/grades/getTeacherProposal/' + prestudent_id + all); + }, updateCertificate(data) { return this.$fhcApi.post('api/frontend/v1/stv/grades/updateCertificate', data); }, + copyTeacherProposalToCertificate({lehrveranstaltung_id, student_uid, studiensemester_kurzbz, lehrveranstaltung_bezeichnung}) { + return this.$fhcApi.post( + 'api/frontend/v1/stv/grades/copyTeacherProposalToCertificate', + { + lehrveranstaltung_id, + student_uid, + studiensemester_kurzbz + }, + { + errorHeader: lehrveranstaltung_bezeichnung + } + ); + }, getGradeFromPoints(points) { return this.$fhcApi.post('api/frontend/v1/stv/grades/getGradeFromPoints', data); } diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js index 8e8269ee5..1c00ef9d0 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js @@ -1,8 +1,10 @@ import NotenZeugnis from './Noten/Zeugnis.js'; +import NotenTeacher from './Noten/Teacher.js'; export default { components: { - NotenZeugnis + NotenZeugnis, + NotenTeacher }, props: { modelValue: Object @@ -13,7 +15,14 @@ export default { } }, template: ` -
- +
+
+
+ +
+
+ +
+
` }; \ No newline at end of file From e76d8fb85d142bf25e9d15b18be832679dbcee62 Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Wed, 20 Nov 2024 11:15:18 +0100 Subject: [PATCH 06/27] Phrases & TODO cleanup --- .../api/frontend/v1/stv/Grades.php | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Grades.php b/application/controllers/api/frontend/v1/stv/Grades.php index f91ace9a1..aac11dece 100644 --- a/application/controllers/api/frontend/v1/stv/Grades.php +++ b/application/controllers/api/frontend/v1/stv/Grades.php @@ -38,6 +38,12 @@ class Grades extends FHCAPI_Controller // Load Libraries $this->load->library('VariableLib', ['uid' => getAuthUID()]); + + // Load Phrases + $this->loadPhrases([ + 'person', + 'lehre' + ]); } public function list() @@ -168,9 +174,9 @@ class Grades extends FHCAPI_Controller { $this->load->library('form_validation'); - $this->form_validation->set_rules("lehrveranstaltung_id", "Lehrverantaltung ID", "required|integer"); // TODO(chris): phrase - $this->form_validation->set_rules("student_uid", "Student UID", "required"); // TODO(chris): phrase - $this->form_validation->set_rules("studiensemester_kurzbz", "Studiensemester", "required"); // TODO(chris): phrase + $this->form_validation->set_rules("lehrveranstaltung_id", $this->p->t('lehre', 'lehrverantaltung'), "required|integer"); + $this->form_validation->set_rules("student_uid", $this->p->t('person', 'student'), "required"); + $this->form_validation->set_rules("studiensemester_kurzbz", $this->p->t('lehre', 'studiensemester'), "required"); if (!$this->form_validation->run()) $this->terminateWithValidationErrors($this->form_validation->error_array()); @@ -241,11 +247,16 @@ class Grades extends FHCAPI_Controller $this->ZeugnisnoteModel->insert($data); - // TODO(chris): FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN if (defined('FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN') && FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN) { - $result = $this->addTestsForGrade($studiensemester_kurzbz, $student_uid, $lehrveranstaltung_id, $teacherGrade->note, $teacherGrade->punkte); - $this->getDataOrTerminateWithError($result);// TODO(chris): terminate? + $result = $this->addTestsForGrade( + $studiensemester_kurzbz, + $student_uid, + $lehrveranstaltung_id, + $teacherGrade->note, + $teacherGrade->punkte + ); + $this->getDataOrTerminateWithError($result); } } @@ -257,7 +268,7 @@ class Grades extends FHCAPI_Controller { $this->load->library('form_validation'); - $this->form_validation->set_rules("lehrveranstaltung_id", "Lehrverantaltung ID", "required|integer"); // TODO(chris): phrase + $this->form_validation->set_rules("lehrveranstaltung_id", $this->p->t('lehre', 'lehrverantaltung'), "required|integer"); $this->form_validation->set_rules("points", "Punkte", "required|numeric"); // TODO(chris): phrase if (!$this->form_validation->run()) From e0e123f264906bbea5ab23bbf3eb79c28caf3c67 Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Wed, 20 Nov 2024 13:19:43 +0100 Subject: [PATCH 07/27] Teacher Component --- .../Details/Noten/Teacher.js | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js new file mode 100644 index 000000000..f3c5223ba --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js @@ -0,0 +1,102 @@ +import {CoreFilterCmpt} from "../../../../filter/Filter.js"; + +export default { + components: { + CoreFilterCmpt + }, + emits: [ + "copied" + ], + props: { + student: Object, + stdsem: String + }, + data() { + return { + tabulatorEvents: [] + }; + }, + computed: { + tabulatorOptions() { + return { + ajaxURL: 'dummy', + ajaxRequestFunc: (url, config, params) => { + return this.$fhcApi.factory.stv.grades.getTeacherProposal(params.prestudent_id, params.stdsem); + }, + ajaxParams: () => { + return { + prestudent_id: this.student.prestudent_id, + stdsem: this.stdsem + }; + }, + ajaxResponse: (url, params, response) => { + return response.data || []; + }, + // TODO(chris): phrasen + columns: [ + { field: 'lehrveranstaltung_bezeichnung', title: 'Lehrveranstaltung' }, + { field: 'note_bezeichnung', title: 'Note' }, + { field: 'mitarbeiter_uid', title: 'MitarbeiterInUID', visible: false }, + { field: 'benotungsdatum', title: 'Benotungsdatum', visible: false }, + { field: 'freigabedatum', title: 'Freigabedatum', visible: false }, + { field: 'studiensemester_kurzbz', title: 'Studiensemester', visible: false }, + { field: 'note', title: 'Note', visible: false }, + { field: 'student_uid', title: 'StudentInUID', visible: false }, + { field: 'lehrveranstaltung_id', title: 'Lehrveranstaltung ID', visible: false }, + { field: 'punkte', title: 'Punkte', visible: false } + ], + layout: 'fitDataStretch', + height: '100%', + selectable: true, + selectableRangeMode: 'click', + persistenceID: 'stv-details-noten-teacher' + }; + } + }, + watch: { + student(n) { + this.$refs.table.reloadTable(); + }, + stdsem(n) { + this.$refs.table.reloadTable(); + } + }, + methods: { + copyGrades(selected) { + const promises = selected.map( + grade => this.$fhcApi.factory + .stv.grades.copyTeacherProposalToCertificate(grade) + .then(() => { + this.$refs.table.tabulator.deselectRow(this.$refs.table.tabulator.getRows().find(el => el.getData() == grade).getElement()); + }) + ); + Promise + .allSettled(promises) + .then(results => { + if (results.some(res => res.status == "fulfilled")) { + // TODO(chris): phrase + this.$fhcAlert.alertSuccess("updated"); + this.$emit('copied'); + } + }); + } + }, + template: ` +
+ + + +
` +}; \ No newline at end of file From 334fc870d02bc8863dc80854f140785b954cad82 Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Mon, 25 Nov 2024 10:45:45 +0100 Subject: [PATCH 08/27] Repeater Grades --- .../api/frontend/v1/stv/Grades.php | 89 +++++++++++++++ public/js/api/stv/grades.js | 14 +++ .../Details/Noten/Repeater.js | 105 ++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Noten/Repeater.js diff --git a/application/controllers/api/frontend/v1/stv/Grades.php b/application/controllers/api/frontend/v1/stv/Grades.php index aac11dece..990bbc113 100644 --- a/application/controllers/api/frontend/v1/stv/Grades.php +++ b/application/controllers/api/frontend/v1/stv/Grades.php @@ -31,8 +31,10 @@ class Grades extends FHCAPI_Controller 'list' => 'student/noten:r', 'getCertificate' => 'student/noten:r', 'getTeacherProposal' => 'student/noten:r', + 'getRepeaterGrades' => 'student/noten:r', 'updateCertificate' => ['admin:w', 'assistenz:w'], 'copyTeacherProposalToCertificate' => 'student/noten:w', + 'copyRepeaterGradeToCertificate' => 'student/noten:w', 'getGradeFromPoints' => 'student/noten:r' ]); @@ -111,6 +113,20 @@ class Grades extends FHCAPI_Controller $this->terminateWithSuccess($grades); } + public function getRepeaterGrades($prestudent_id) + { + $this->load->library('AntragLib'); + + $studiensemester_kurzbz = $this->variablelib->getVar('semester_aktuell'); + + + $result = $this->antraglib->getLvsForPrestudent($prestudent_id, $studiensemester_kurzbz); + + $grades = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($grades); + } + public function updateCertificate() { $this->load->model('crm/Student_model', 'StudentModel'); @@ -264,6 +280,79 @@ class Grades extends FHCAPI_Controller $this->terminateWithSuccess(); } + public function copyRepeaterGradeToCertificate() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules("studierendenantrag_lehrveranstaltung_id", "", "required|integer"); // TODO(chris): phrase + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $id = $this->input->post('studierendenantrag_lehrveranstaltung_id'); + $authUID = getAuthUID(); + + $this->load->model('education/Studierendenantraglehrveranstaltung_model', 'StudierendenantraglehrveranstaltungModel'); + + $this->StudierendenantraglehrveranstaltungModel->addSelect("tbl_studierendenantrag_lehrveranstaltung.*"); + $this->StudierendenantraglehrveranstaltungModel->addSelect("student_uid"); + $this->StudierendenantraglehrveranstaltungModel->addJoin("campus.tbl_studierendenantrag", "studierendenantrag_id"); + $this->StudierendenantraglehrveranstaltungModel->addJoin("public.tbl_student", "prestudent_id", "LEFT"); + + $result = $this->StudierendenantraglehrveranstaltungModel->load($id); + $repeaterGrade = $this->getDataOrTerminateWithError($result); + + if (!$repeaterGrade) + show_404(); + + $repeaterGrade = current($repeaterGrade); + + // NOTE(chris): Stg Permissions + // TODO(chris): Are those permissions correct? + if (!$this->hasPermissionCopy($repeaterGrade->lehrveranstaltung_id, $repeaterGrade->student_uid)) + return $this->_outputAuthError([$this->router->method => 'student/noten']); + + $data = [ + 'note' => $repeaterGrade->note, + 'uebernahmedatum' => date('c'), + 'benotungsdatum' => $repeaterGrade->insertamum, + 'bemerkung' => $repeaterGrade->anmerkung + ]; + + $this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel'); + + $result = $this->ZeugnisnoteModel->load([ + $repeaterGrade->studiensemester_kurzbz, + $repeaterGrade->student_uid, + $repeaterGrade->lehrveranstaltung_id + ]); + $certificateGrade = $this->getDataOrTerminateWithError($result); + + if ($certificateGrade) { + // NOTE(chris): update + $data['updateamum'] = $data['uebernahmedatum']; + $data['updatevon'] = $authUID; + + $this->ZeugnisnoteModel->update([ + $repeaterGrade->studiensemester_kurzbz, + $repeaterGrade->student_uid, + $repeaterGrade->lehrveranstaltung_id + ], $data); + } else { + // NOTE(chris): insert + $data['insertamum'] = $data['uebernahmedatum']; + $data['insertvon'] = $authUID; + $data['lehrveranstaltung_id'] = $repeaterGrade->lehrveranstaltung_id; + $data['student_uid'] = $repeaterGrade->student_uid; + $data['studiensemester_kurzbz'] = $repeaterGrade->studiensemester_kurzbz; + + $this->ZeugnisnoteModel->insert($data); + } + + + $this->terminateWithSuccess(); + } + public function getGradeFromPoints() { $this->load->library('form_validation'); diff --git a/public/js/api/stv/grades.js b/public/js/api/stv/grades.js index 0a2d15e3b..8e3e00e28 100644 --- a/public/js/api/stv/grades.js +++ b/public/js/api/stv/grades.js @@ -10,6 +10,9 @@ export default { all = all ? '/all' : ''; return this.$fhcApi.get('api/frontend/v1/stv/grades/getTeacherProposal/' + prestudent_id + all); }, + getRepeaterGrades(prestudent_id) { + return this.$fhcApi.get('api/frontend/v1/stv/grades/getRepeaterGrades/' + prestudent_id); + }, updateCertificate(data) { return this.$fhcApi.post('api/frontend/v1/stv/grades/updateCertificate', data); }, @@ -26,6 +29,17 @@ export default { } ); }, + copyRepeaterGradeToCertificate({studierendenantrag_lehrveranstaltung_id, lv_bezeichnung}) { + return this.$fhcApi.post( + 'api/frontend/v1/stv/grades/copyRepeaterGradeToCertificate', + { + studierendenantrag_lehrveranstaltung_id + }, + { + errorHeader: lv_bezeichnung + } + ); + }, getGradeFromPoints(points) { return this.$fhcApi.post('api/frontend/v1/stv/grades/getGradeFromPoints', data); } diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Repeater.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Repeater.js new file mode 100644 index 000000000..2ff92450f --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Repeater.js @@ -0,0 +1,105 @@ +import {CoreFilterCmpt} from "../../../../filter/Filter.js"; + +export default { + components: { + CoreFilterCmpt + }, + emits: [ + "copied" + ], + props: { + student: Object, + allSemester: Boolean + }, + data() { + return { + tabulatorEvents: [] + }; + }, + computed: { + tabulatorOptions() { + return { + ajaxURL: 'dummy', + ajaxRequestFunc: (url, config, params) => { + return this.$fhcApi.factory.stv.grades.getRepeaterGrades(params.prestudent_id, params.stdsem); + }, + ajaxParams: () => { + return { + prestudent_id: this.student.prestudent_id, + stdsem: this.allSemester + }; + }, + ajaxResponse: (url, params, response) => { + return response.data || []; + }, + // TODO(chris): phrasen + columns: [ + { field: 'lv_bezeichnung', title: 'Lehrveranstaltung' }, + { field: 'note_bezeichnung', title: 'Note' }, + { field: 'insertvon', title: 'MitarbeiterInUID', visible: false }, + { field: 'benotungsdatum', title: 'Benotungsdatum', visible: false }, + { field: 'freigabedatum', title: 'Freigabedatum', visible: false }, + { field: 'studiensemester_kurzbz', title: 'Studiensemester', visible: false }, + { field: 'stg_bezeichnung', title: 'Studiengang', visible: false }, + { field: 'note', title: 'Note', visible: false }, + { field: 'prestudent_uid', title: 'PrestudentID', visible: false }, + { field: 'lehrveranstaltung_id', title: 'Lehrveranstaltung ID', visible: false }, + { field: 'studierendenantrag_lehrveranstaltung_id', title: 'StudstatusLvID', visible: false } + ], + layout: 'fitDataStretch', + height: '100%', + selectable: true, + selectableRangeMode: 'click', + persistenceID: 'stv-details-noten-repeater' + }; + } + }, + watch: { + student(n) { + this.$refs.table.reloadTable(); + }, + allSemester(n) { + this.$refs.table.reloadTable(); + } + }, + methods: { + copyGrades(selected) { + const promises = selected.map( + grade => this.$fhcApi.factory + .stv.grades.copyRepeaterGradeToCertificate(grade) + .then(() => { + this.$refs.table.tabulator.deselectRow(this.$refs.table.tabulator.getRows().find(el => el.getData() == grade).getElement()); + }) + ); + Promise + .allSettled(promises) + .then(results => { + if (results.some(res => res.status == "fulfilled")) { + // TODO(chris): phrase + this.$fhcAlert.alertSuccess("updated"); + this.$emit('copied'); + } + }); + } + }, + template: ` +
+ + + + +
` +}; \ No newline at end of file From 28e445440935e9de0fc5b4022e1458d199cd3a0a Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Mon, 25 Nov 2024 11:20:44 +0100 Subject: [PATCH 09/27] Move Semester Filter to top component --- .../api/frontend/v1/stv/Grades.php | 4 +- ...dierendenantraglehrveranstaltung_model.php | 68 +++++++++++-------- public/js/api/stv/grades.js | 5 +- .../Stv/Studentenverwaltung/Details/Noten.js | 34 ++++++++-- .../Details/Noten/Teacher.js | 10 +-- .../Details/Noten/Zeugnis.js | 22 ++---- 6 files changed, 86 insertions(+), 57 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Grades.php b/application/controllers/api/frontend/v1/stv/Grades.php index 990bbc113..284fadf9b 100644 --- a/application/controllers/api/frontend/v1/stv/Grades.php +++ b/application/controllers/api/frontend/v1/stv/Grades.php @@ -113,11 +113,11 @@ class Grades extends FHCAPI_Controller $this->terminateWithSuccess($grades); } - public function getRepeaterGrades($prestudent_id) + public function getRepeaterGrades($prestudent_id, $all = null) { $this->load->library('AntragLib'); - $studiensemester_kurzbz = $this->variablelib->getVar('semester_aktuell'); + $studiensemester_kurzbz = ($all === null) ? $this->variablelib->getVar('semester_aktuell') : false; $result = $this->antraglib->getLvsForPrestudent($prestudent_id, $studiensemester_kurzbz); diff --git a/application/models/education/Studierendenantraglehrveranstaltung_model.php b/application/models/education/Studierendenantraglehrveranstaltung_model.php index 4318c773e..43083295d 100644 --- a/application/models/education/Studierendenantraglehrveranstaltung_model.php +++ b/application/models/education/Studierendenantraglehrveranstaltung_model.php @@ -66,39 +66,53 @@ class Studierendenantraglehrveranstaltung_model extends DB_Model ); $this->addJoin('public.tbl_student s', 'prestudent_id'); - // NOTE(chris): last offizell note - $this->addJoin('( - SELECT z.* - FROM lehre.tbl_zeugnisnote z - LEFT JOIN public.tbl_studiensemester zs - USING(studiensemester_kurzbz) - JOIN ( - SELECT zi.lehrveranstaltung_id, zi.student_uid, MAX(zis.start) AS start - FROM lehre.tbl_zeugnisnote zi - LEFT JOIN lehre.tbl_note zin - USING(note) - LEFT JOIN public.tbl_studiensemester zis - USING(studiensemester_kurzbz) - WHERE zin.aktiv AND zin.offiziell - GROUP BY zi.lehrveranstaltung_id, zi.student_uid - ) zx - ON ( - z.lehrveranstaltung_id=zx.lehrveranstaltung_id - AND z.student_uid=zx.student_uid - AND zs.start = zx.start - )) z', 'z.lehrveranstaltung_id=lv.lehrveranstaltung_id AND z.student_uid=s.student_uid', 'LEFT'); - $this->addJoin('lehre.tbl_note zn', 'z.note = zn.note', 'LEFT'); - + $this->load->config('studierendenantrag'); $note_intern_angerechntet = $this->config->item('wiederholung_note_angerechnet'); - return $this->loadWhere([ + $where = [ 'ps.prestudent_id' => $prestudent_id, 'a.typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG, 'stat.studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_APPROVED, 'n.note <> ' => 0, - $this->dbTable . '.studiensemester_kurzbz' => $studiensemester_kurzbz, - '(n.note<>' . $this->db->escape($note_intern_angerechntet) . ' OR (z.note IS NOT NULL AND zn.positiv))' => null - ]); + // NOTE(chris): grade "intern angerechnet" needs an official grade beforehand (the subquery gets the last positive offical grade) + "(n.note<>" . $this->db->escape($note_intern_angerechntet) . " OR EXISTS ( + SELECT + 1 + FROM + lehre.tbl_zeugnisnote z + LEFT JOIN public.tbl_studiensemester zs USING(studiensemester_kurzbz) + JOIN ( + SELECT + zi.lehrveranstaltung_id, + zi.student_uid, + MAX(zis.start) AS start + FROM + lehre.tbl_zeugnisnote zi + LEFT JOIN lehre.tbl_note zin USING(note) + LEFT JOIN public.tbl_studiensemester zis USING(studiensemester_kurzbz) + WHERE + zin.aktiv + AND zin.offiziell + GROUP BY + zi.lehrveranstaltung_id, + zi.student_uid + ) zx ON ( + z.lehrveranstaltung_id = zx.lehrveranstaltung_id + AND z.student_uid = zx.student_uid + AND zs.start = zx.start + ) + JOIN lehre.tbl_note zn USING (note) + WHERE + z.lehrveranstaltung_id = lv.lehrveranstaltung_id + AND z.student_uid = s.student_uid + AND zn.positiv + ))" => null + ]; + + if ($studiensemester_kurzbz !== false) + $where[$this->dbTable . '.studiensemester_kurzbz'] = $studiensemester_kurzbz; + + return $this->loadWhere($where); } } diff --git a/public/js/api/stv/grades.js b/public/js/api/stv/grades.js index 8e3e00e28..efbb920c0 100644 --- a/public/js/api/stv/grades.js +++ b/public/js/api/stv/grades.js @@ -10,8 +10,9 @@ export default { all = all ? '/all' : ''; return this.$fhcApi.get('api/frontend/v1/stv/grades/getTeacherProposal/' + prestudent_id + all); }, - getRepeaterGrades(prestudent_id) { - return this.$fhcApi.get('api/frontend/v1/stv/grades/getRepeaterGrades/' + prestudent_id); + getRepeaterGrades(prestudent_id, all) { + all = all ? '/all' : ''; + return this.$fhcApi.get('api/frontend/v1/stv/grades/getRepeaterGrades/' + prestudent_id + all); }, updateCertificate(data) { return this.$fhcApi.post('api/frontend/v1/stv/grades/updateCertificate', data); diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js index 1c00ef9d0..e7aae4c8c 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js @@ -1,27 +1,53 @@ import NotenZeugnis from './Noten/Zeugnis.js'; import NotenTeacher from './Noten/Teacher.js'; +import NotenRepeater from './Noten/Repeater.js'; + +const LOCAL_STORAGE_ID = 'stv_details_noten_2024-11-25_stdsem_all'; export default { components: { NotenZeugnis, - NotenTeacher + NotenTeacher, + NotenRepeater }, props: { modelValue: Object }, + data() { + return { + stdsem: '' + }; + }, methods: { reload() { this.$refs.zeugnis.$refs.table.reloadTable(); + this.$refs.teacher.$refs.table.reloadTable(); + this.$refs.repeater.$refs.table.reloadTable(); + }, + saveStdsem(event) { + window.localStorage.setItem(LOCAL_STORAGE_ID, event.target.value); } }, + created() { + const savedPath = window.localStorage.getItem(LOCAL_STORAGE_ID); + this.stdsem = savedPath || ''; + }, + // TODO(chris): phrasen template: ` -
+
+
+ +
- +
- + +
` diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js index f3c5223ba..c15381f88 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js @@ -9,7 +9,7 @@ export default { ], props: { student: Object, - stdsem: String + allSemester: Boolean }, data() { return { @@ -26,7 +26,7 @@ export default { ajaxParams: () => { return { prestudent_id: this.student.prestudent_id, - stdsem: this.stdsem + stdsem: this.allSemester }; }, ajaxResponse: (url, params, response) => { @@ -57,7 +57,7 @@ export default { student(n) { this.$refs.table.reloadTable(); }, - stdsem(n) { + allSemester(n) { this.$refs.table.reloadTable(); } }, @@ -82,9 +82,11 @@ export default { } }, template: ` -
+
+ { return { prestudent_id: this.student.prestudent_id, - stdsem: this.stdsem + stdsem: this.allSemester }; }, ajaxResponse: (url, params, response) => { @@ -71,7 +70,7 @@ export default { student(n) { this.$refs.table.reloadTable(); }, - stdsem(n) { + allSemester(n) { this.$refs.table.reloadTable(); } }, @@ -81,26 +80,13 @@ export default { .stv.grades.updateCertificate(selected) .then(this.$refs.table.reloadTable) .catch(this.$fhcAlert.handleFormValidation); - }, - saveStdsem(event) { - window.localStorage.setItem(LOCAL_STORAGE_ID, event.target.value ? 'true' : ''); } }, - created() { - const savedPath = window.localStorage.getItem(LOCAL_STORAGE_ID); - this.stdsem = savedPath ? '/all' : ''; - }, // TODO(chris): phrasen template: `
Kein Student