From fe7ca2bf6979b92c0c4a2d2e00359cc10e425afb Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Wed, 20 Nov 2024 11:06:28 +0100 Subject: [PATCH] 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