Compare commits

...
Author SHA1 Message Date
Johann Hoffmann aabec8d6fd adapt Mobilitaet_model.php formatZusatz for the new int/ext naming convention. 2026-09-23 16:04:29 +02:00
Johann Hoffmann 0d82753833 Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	system/phrasesupdate.php
2026-09-23 12:05:11 +02:00
Johann Hoffmann a9b9cd59b0 noten api cleanup to enforce naming consistency; client really just reads server decisions to avoid duplicity as much as possible; avoided constant config fallbacks since high configurability is kind of the point of the last changes; fixed a few small bugs with freigabe frist config checking, saveLvNote, Antritt Dategap rules on edit, moved certain ui configs also to server; splitup of Benotungstool component; test suite additions; updated readme_local.txt and readme_noten.txt; WIP: decide which config setup we actually like to use ;) 2026-09-23 11:45:01 +02:00
Andreas Österreicher 2050f5ae65 Merge branch 'feature-75891/Electronic_Onboarding_Infocenter_Tool' 2026-09-23 11:14:01 +02:00
Andreas Österreicher 587e523990 Merge branch 'feature-67251/infocenter_zum_reihungstest_freigegeben_neue_spalte_eqk' 2026-09-23 11:03:20 +02:00
Andreas Österreicher 5ec1a1c54c Merge branch 'feature-77686/infocentertool_status_setzen_fuer_zgv_pruefung_soll_message_ausloesen' 2026-09-23 10:56:54 +02:00
Andreas Österreicher 2c65ccc8da Merge branch 'bug-77684/reihungstest_auswertung_freischalten_sperren' 2026-09-23 09:40:37 +02:00
Andreas Österreicher 787308aad4 Merge branch 'feature-77356/teilgenommen_setzen_beim_reihungstest_punkte_uebertragen' 2026-09-23 09:37:41 +02:00
Harald Bamberger ba92eea7eb studvw: fix lvtermine student_lehrverband 2026-09-22 14:09:51 +02:00
Harald Bamberger 50951dfdcf anrechnungen: add pdf file extension to content disposition filename 2026-09-22 13:00:28 +02:00
Harald Bamberger be7527e994 increase length of column taglib in table public.tbl_notiz_typ 2026-09-22 09:52:09 +02:00
Johann Hoffmann c1939d0e23 hint after a passed Antritt; split Cypress setup into global, suite and local layers; if configured a pass closes the Antritt chain: the next exam cell shows "Positiv abgeschlossen" with a tooltip, and an add attempt shows pruefungNachBestandenerNote; 8 new specs with 55 tests: kettenregeln, terminregeln, scope, rollenmatrix, antrittrollen, nebenlaeufigkeit, UI ablehnung and navigation; WIP documenting testsuite caveats in readme files; WIP streamlining layers and removing redundant code/fallbacks; 2026-09-21 16:59:31 +02:00
Harald Bamberger d5c1721ab1 Merge branch 'feature-83457/cis4_redirect_blank_target' 2026-09-18 17:14:49 +02:00
Harald Bamberger 8eb27b194f add target attribute to redirect content menu entries, use entry target attribute if set when rendering cis4 menu 2026-09-18 17:12:30 +02:00
Harald Bamberger 579a55a00d fix nested ternary operators 2026-09-17 14:18:10 +02:00
Andreas Österreicher 0a00caa1e4 Merge branch 'feature-82683/lvverwaltung_zusaetzliche_spalten_bei_lehrenden' 2026-09-17 14:00:07 +02:00
Andreas Österreicher c53040ed5c Zusätzliche Datumsberechnung für Zutrittskarten hinzugefügt 2026-09-17 11:21:23 +02:00
Harald Bamberger f7465047cb Merge branch 'feature-75469/LeOptional4Projektarbeit' 2026-09-16 17:35:55 +02:00
Alexei Karpenko 0dadaece08 Merge branch 'master' into feature-75891/Electronic_Onboarding_Infocenter_Tool 2026-09-16 16:59:09 +02:00
Johann Hoffmann 73c63c791d seeder consolidation into branch naming convention; consolidated helper functions; fixed maxAntrittCount & brauchtNeueLVNote helpers in notenRules outside the cypress suite; more careful usage of db connection pools; cypress code cleanup & lint; WIP seperating branch specific configs/env setup from global cypress settings & ongoing test refinement; 2026-09-16 13:56:11 +02:00
Johann Hoffmann 7c9bcb6fd2 Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	cis/private/lehre/notenliste.xls.php
#	public/js/api/factory/studiensemester.js
#	system/phrasesupdate.php
2026-09-15 14:44:45 +02:00
Johann Hoffmann 69fd223358 fix load projektarbeit.studiensemester_kurzbz for fetchProjektarbeitById in order to query getDefaultStundensaetze successfully 2026-09-15 11:49:19 +02:00
Johann Hoffmann 4399857f4a WIP 2026-09-15 10:59:09 +02:00
Harald Bamberger 9c43374267 Merge branch 'master' into feature-75469/LeOptional4Projektarbeit 2026-09-15 07:53:10 +02:00
Paolo ea7c0cccde Added base seeders for testing/staging/demoing 2026-09-14 16:36:12 +02:00
Harald Bamberger 6dfe292776 API LV: extend regex to allow studiengang_kz 0 2026-09-14 11:09:45 +02:00
Johann Hoffmann 3212800643 started extracting every meaningful/realistic decision in the grading workflow into configs; groups: exam date guard logic, grades/pruefungchain interactions, deadlines, role configuration & permission configurability, freigabe, import & display; lots of code bugfixes & general cleanup effort; WIP testing and finding bugs i introduced with this new & large configuration scheme; 2026-09-11 15:19:39 +02:00
ma0048 c90f7a31bb columns hinzugefuegt: insertvon/updatevon insertamum/updateamum 2026-09-11 12:03:06 +02:00
Alexei Karpenko 073160dde4 Merge branch 'master' into feature-75891/Electronic_Onboarding_Infocenter_Tool 2026-09-10 20:08:08 +02:00
Johann Hoffmann 6d2cbf4896 adapt import to also parse note.anmerkung (entschuldigt - en, nicht beurteilt - nb, etc) if configured; new config if the benotungstool is allowed to create kommissionelle prüfungen or if that is a job for assistenz in stv; coded a kommPruef as the last pruefungsattempt and not a special case so it follows the same ordering rules in the table; changed headers from "Antritte" to "Prüfungstermine" to keep the wording aligned with the domain objects definitions; tooltip explaining the current antritt while hovering over the badge (1,2,3-K) (maybe make the badges contents configurable?); changed the notenOption sort to resemble stv: order 1-5 by value and afterwards alphabetically; actually removed redundant configs for defining which noten count for antritte -> only go by bezeichnung and fetch the matching primary keys from each db as they can have mixed pk's; vorschlag übernehmen now upserts the first prüfungsattempt, aswell as the notenfreigabe(if no other pruefungen exist) which should fix the possibility of creating a pruefungseries that starts with termin2, vorschlag also carries a datepicker writing the date of that first prüfung but not the benotungsdatum -> that needs to stay the entry date since the freigabemechanismus hinges on it; added a flag handling the password inputs browser prompt to not appear on other inputs; WIP coming up with even more configurability in order to cover uncertain workflow this tool is supposed to represent; 2026-09-10 13:04:11 +02:00
Johann Hoffmann 56fe8c9d09 Merge remote-tracking branch 'origin/feature-75469/LeOptional4Projektarbeit' into feature-75469/LeOptional4Projektarbeit 2026-09-10 11:51:26 +02:00
Johann Hoffmann 9a3532265f information schema guard check for the data migration & table alter statements aswell, so they dont run on every checksystem.php invocation 2026-09-10 11:50:51 +02:00
Harald Bamberger 4abe6f0d24 Merge branch 'bugfix_missing-studvw-filter-clear-action-button' 2026-09-09 16:15:39 +02:00
adisposkofh 9fbbd6b855 moved studvw 'clear filter' action button into 'actions' slot in the filter component 2026-09-09 15:14:20 +02:00
Andreas Österreicher 59b4b2bac9 Merge branch 'feature-71639/StudVw_Noten_Renaming_Infopopup' 2026-09-09 12:40:42 +02:00
Andreas Österreicher 5f546fca2a Merge branch 'feature-82630/Infocenter_Verlinkung_StudVW' 2026-09-09 12:37:33 +02:00
Andreas Österreicher 1bf2b707ef Merge branch 'master' into feature-75469/LeOptional4Projektarbeit 2026-09-09 09:43:11 +02:00
Andreas Österreicher f74b885253 Abfrage von Verträgen korrigiert für Fälle wo der Monatsletzte gleichzeitig der Start des neuen Studiensemesters ist (zB 31.08.2026) damit die Abrechnung korrekt funktioniert (BFI) 2026-09-09 08:55:24 +02:00
Andreas Österreicher 3ab402adef Merge branch 'bugfix-82884_grades-export-group-names' 2026-09-08 14:43:17 +02:00
Manuela Thamer 52f24645fb update link StudVW 2026-09-08 14:23:59 +02:00
Manuela Thamer 4ac108174b rename title delete button 2026-09-08 08:29:08 +02:00
adisposkofh 6378b80d87 fixed issue where exported gradelist excel file formatted group names as numbers 2026-09-07 15:24:38 +02:00
Andreas Österreicher 658c8d6bd4 Schreibberechtigungsprüfung bei Anrechnungen angepasst damit diese korrekt angezeigt wird wenn oe_kurzb nicht gleich der Kurzbz des Studiegangs ist 2026-09-03 08:44:10 +02:00
Andreas Österreicher bf29faa5a7 Merge branch 'feature-82292/Cis4StudiumLVohneModulAnzeigen' 2026-09-02 15:20:28 +02:00
Andreas Österreicher 2ada167557 Merge branch 'bug-82614/StudVw_International_Incoming_Outgoing_GS_wird_nicht_geladen' 2026-09-02 14:56:27 +02:00
chfhtw cc9d1f983e bugfix: missing call to getStudiensemesterKurzbz 2026-09-01 10:39:02 +02:00
Johann Hoffmann 57c48f2c4a cis4 studium page dont filter away lehrveranstaltungen without a module and give them their own box in the ui; 2026-08-26 13:15:16 +02:00
Andreas Österreicher e32691994c Merge branch 'master' into feature-75469/LeOptional4Projektarbeit 2026-08-26 08:21:30 +02:00
Johann Hoffmann 46f8d0d07d remove all remaining projektarbeit-lehreinheit joins across the codebase;
master merge reverted the LE removal in Projektarbeit_model -> re-applied that fix and went through every file that touches tbl_projektarbeit to find remaining issues.

Projektarbeit_model, stv Projektarbeit controller, Vertrag_model, vertrag.class, lehrauftragsliste_gst.xls.php, zeitaufzeichnung.class.php, sammelzeugnis.rdf.php, zeugnis.rdf.php, projektarbeit.rdf.php,
lehrauftrag.xml.php, personal_lektorenohnelehrauftrag.php, personal_generateverwendung.php

tbl_lehreinheit joins remain in tbl_lehreinheitmitarbeiter contexts where lehreinheit_id is always set.
2026-08-12 18:05:45 +02:00
Johann Hoffmann 9274743df3 Merge branch 'master' into feature-75469/LeOptional4Projektarbeit
# Conflicts:
#	application/controllers/api/frontend/v1/Abgabe.php
#	application/controllers/api/frontend/v1/stv/Projektarbeit.php
#	application/models/education/Projektarbeit_model.php
#	public/css/Studentenverwaltung.css
#	public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js
#	public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js
#	public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js
#	public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js
2026-08-12 11:26:51 +02:00
Johann Hoffmann 97be497fda refactored the core pruefungslogic to revolve around PruefungsverlaufLib.php instead of tbl_pruefungstyp which was highly inflexible for any adjustments. could be reused in Stv/Pruefungen aswell since there invalid inserts are very much possible; typen are still being used but are irrelevant for sequencing the attempts correctly; incorporated kommissionelle Pruefung into Gesamtnoteneingabe as the last possible antritt a student makes; updated cypress suites accordingly and added some ui tests aswell; documented usage in a readme_benotungstool.txt for the suite; 2026-08-11 17:35:06 +02:00
Johann Hoffmann 3c9f46941c Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2026-08-10 07:48:11 +02:00
Johann Hoffmann c9c92f82d3 WIP refactor 2026-08-06 10:33:46 +02:00
Johann Hoffmann 56fcb23275 WIP 2026-08-04 17:40:07 +02:00
Johann Hoffmann dba199b531 Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2026-08-04 15:31:19 +02:00
Johann Hoffmann 193dd166c3 finalizing current state of benotungstool api testsuite before touching implementation again; access suite and pruefungstermin suite have a few known failing tests, rest should run fine on a freshly resetted database! reset with 'php system/setup_testinstance.php --setup' for wrapped db setup routine; 2026-08-04 14:05:17 +02:00
Johann Hoffmann 431e0bb341 extended noten testsuite to cover more cases; added seedPruefung task to achieve difficult/impossible states for noten Api alone like kommPruef inserts or some entschuldigt/termin2 setups; WIP getting to test lkt_ueberschreibbar on zeugnisnote (needs updated seeders); WIP testing some logic/rules that only live in js frontend; 2026-07-31 13:32:47 +02:00
Johann Hoffmann beb54c1bc5 Add Cypress API test suite for the Gesamtnoteneingabe. Tests the grade-entry rules against /api/frontend/v1/Noten, where they are enforced
server-side.

Generic and reusable by the next suite: setup_testinstance.php (--setup/--verify,
delegating the rebuild to paolos db_setup.sh), tasks/db.js, tasks/sshTunnel.js,
tools/dbCheck.js and cypress.config.js carry no suite-specific knowledge. A suite
adds tools/checks/<name>.js, suites/<name>.js and its own task file.

Fixture state resets over a direct Postgres connection through an SSH tunnel the
runner opens itself.

Tests: 37 passing, 5 pending, 1 failing. Pending are opt-in (Freigabe sends
real mail; the Frist config is off). The failing spec is left red on purpose: it
documents that getLvGesamtNoten filters freigabedatum < NOW(), so the read path
hides un-freigegebene notes while the write path uses the unfiltered getter. Not really certain if that is a feature or a bug;

Working on more actual testsuites now that the setup is running as intended;
2026-07-30 17:18:51 +02:00
Johann Hoffmann 4e6a2d5218 Merge branch 'master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/config/routes.php
#	application/controllers/api/frontend/v1/Lehre.php
#	application/views/CisRouterView/CisRouterView.php
#	cis/private/lehre/notenliste.xls.php
#	public/js/api/factory/lehre.js
#	public/js/apps/Cis/Cis.js
#	public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js
#	public/js/helpers/StringHelpers.js
#	system/phrasesupdate.php
2026-07-27 15:41:29 +02:00
Johann Hoffmann 273f00a9c4 added accept/cancel prompt to Notenvorschlag übernehmen spelling out the current benotungsdatum to the user; also added a list over all changedNoten which are about to be freigegeben in the Notenfreigabe Modal before entering a password; fixed/added some phrasen; 2026-07-24 13:52:05 +02:00
Johann Hoffmann 32de4aac6b assistenz recht & features gesamtnoteneingabe; load stg array for assistenzen to choose any lva from; added configurable datelimit for each semester with defaults as per upcoming prüfungsordnung; 2026-07-23 16:59:40 +02:00
ma0048 66e400013a bei status setzen fuer zgv pruefung message rausschicken 2026-07-14 13:30:32 +02:00
ma0048 3f95bfd82f boolean auf fhc_boolean geandert 2026-07-14 11:31:23 +02:00
ma0048 dc22ac6b06 teilgenommen setzen beim uebetragen der punkte 2026-06-30 17:03:43 +02:00
Johann Hoffmann 8bf18b0d37 prüfung/notenimport enabled per config + adapt notenliste.xls.php highlighting as per config; check if higher antritt prüfungen exist when editing existing ones (changing grades is not allowed, dates inbetween the other 2 antritt termine allowed); prüfungstermine with entschuldigt note are being preserved since the whole series can have 1max; frozen columns selection dropdown + 4 columns freezable (selector, uid, vorname, nachname) -> setting saved in local storage; better mirroring of new pruefung studenten dropdown with actual table sort + update the label there to be uid + name aswell; fixed the note_vorschlag select input cell; table sort doesnt clear selection; show the NotenlisteLinks.js in import modal and filter it based on LE selection; 2026-06-26 12:52:18 +02:00
Johann Hoffmann 79c0f31ddc lv_id & sem_kurzbz are now optional route params; defer loading of LehreinheitenModule.js; fetch orgform for lva dropdown; added selected|filtered|total count to table; dropdowns have responsive labels now; left aligned columns with text; AuthInfo Api for uid; tableLayout without WidthGrow, fixed rowHeight at 30 for now; selectAll + filter interaction fixed; load studiensemester similar to abgabetool; clearFilter fix on LE deselect; modal formatting & loading toggle; 2026-06-23 18:59:51 +02:00
Johann Hoffmann 49d61342c2 lv_id & sem_kurzbz are now optional route params; defer loading of LehreinheitenModule.js; fetch orgform for lva dropdown; added selected|filtered|total count to table; dropdowns have responsive labels now; left aligned columns with text; AuthInfo Api for uid; tableLayout without WidthGrow, fixed rowHeight at 30 for now; selectAll + filter interaction fixed; load studiensemester similar to abgabetool; clearFilter fix on LE deselect; modal formatting & loading toggle; 2026-06-23 18:59:36 +02:00
Johann Hoffmann be0943f1ba Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2026-06-22 18:12:29 +02:00
Johann Hoffmann f4cc311806 getFormData (which loads lehrveranstaltungen) on editProjektarbeit + always query with student.studiengang_kz to have the same set of options when creating the projektarbeit; should work for incomings aswell. first attempt: lva in studienplan of last studentenstatus; if that yields no options just take the full lv list; 2026-06-17 10:44:55 +02:00
Johann Hoffmann df46b8ebdd Merge remote-tracking branch 'origin/master' into feature-75469/LeOptional4Projektarbeit 2026-06-16 10:46:44 +02:00
Johann Hoffmann 870c36e76b Merge remote-tracking branch 'origin/master' into feature-75469/LeOptional4Projektarbeit
# Conflicts:
#	system/dbupdate_3.4.php
2026-05-20 16:22:31 +02:00
Harald Bamberger c56064d189 js import add missing file extension 2026-05-06 18:36:05 +02:00
Alexei Karpenko 96b2d88d39 Merge branch 'master' into feature-75891/Electronic_Onboarding_Infocenter_Tool 2026-04-27 18:19:42 +02:00
Alexei Karpenko 474fc1fc57 Notiz model: possible to add specific insertvon 2026-04-27 18:17:29 +02:00
Alexei Karpenko 3825e2fb38 adresse class: added method to get austrian adress 2026-04-27 17:56:18 +02:00
Harald Bamberger f6747713a1 Merge branch 'master' into feature-60873/GesamtnoteneingabeCis4 2026-04-20 11:13:14 +02:00
Johann Hoffmann 4c0635be7c adapt lehrauftrag.xml.php queries to find projektbetreuer mitarbeiter_uid of projektarbeiten without le in future semesters for the lehrauftrag dokument (single lehrauftrag dokument curated for one betreuer) and also for the aggregated statistik listing the costs of all lehraufträge in a studiengang - resulting in the same data; 2026-04-02 16:45:03 +02:00
Johann Hoffmann 67b863f9db implemented the same studienplan lva_id check for stv projektarbeit anlage inside lva.rdf and lva.class; should result in the same data even though the quereies are written a second time; 2026-04-02 11:51:37 +02:00
Johann Hoffmann 7628ebc9ca remove unnecessary LE join in diplomasupplement.xml.php; fix studiensemester_kurzbz col datatype in dbupdate script; stv projektarbeit dropdown selection also by studienplan of current prestudent_status history entry 2026-04-01 16:05:51 +02:00
Johann Hoffmann deb6dad7ad WIP trying to get diplomasupplement.xml.php to print a thesis title; remove console log stv; removed le from getProjektarbeitStudiensemester in projektarbeit.class.php; 2026-03-30 18:45:01 +02:00
Johann Hoffmann 077f052112 stv projektarbeit getFormData call on actionEditProjektarbeit 2026-03-30 14:26:58 +02:00
Johann Hoffmann e7f57c9ddd remove last pa-le join from statistik/lvplanung.php 2026-03-30 13:52:50 +02:00
Johann Hoffmann 095e2bf581 revert lvgesamtnoteverwalten.php change 2026-03-27 11:29:04 +01:00
Johann Hoffmann 25345a0522 remove le join from vertrag.class.php; lehrauftrag_annehmen.xml.php fix 2nd query; 2026-03-27 11:24:38 +01:00
Johann Hoffmann e2b5201b5a remove le join from vertrag.class.php 2026-03-27 11:09:34 +01:00
Johann Hoffmann 5c80a382bf remove le join from lehrauftragsliste_gst.xls.php 2026-03-27 11:01:17 +01:00
Johann Hoffmann aa940683d6 remove le join from lvgesamtnoteverwalten.php 2026-03-27 10:49:13 +01:00
Johann Hoffmann 843c65d52b Lehraufträge workflow wurde angepasst & getestet für zukünftige Projektarbeiten ohne hinterlegte LE. Subqueries "Projektbetreuungsaufträge and -vertragsstati" verwenden KEINE Lehreinheit table mehr, join funktioniert via pb -> pa -> lva & where clauses wurden entsprechend auf pa.lva & pa.sem_kurzbz angepasst für orderLehrauftragData.php, approveLehrauftragData.php & acceptLehrauftragData.php.
Weitere Anpassung in Vertrag_model: prüfung auf lehreinheit nur wenn vertragstyp nicht 'Betreuung' ist.

Added missing "setFormData" Method in stv Projektarbeit Details component
2026-03-25 11:56:19 +01:00
Johann Hoffmann e26f4262df LEFT join tbl_projektarbeit with tbl_lehreinheit to account for nullability 2026-03-24 16:08:20 +01:00
Johann Hoffmann dde586e004 Merge remote-tracking branch 'origin/master' into feature-75469/LeOptional4Projektarbeit 2026-03-23 15:37:57 +01:00
Johann Hoffmann 506c23ae89 fix joins in lehrauftragData files from 'pa JOIN lv JOIN le' to 'pa JOIN le JOIN lv'; fix join in projektarbeit.class.php "getProjektarbeitStudiensemester" to also correctly join on le table first but with lva_id & sem_kurzbz as join conditions; push all the relevant changes from "feature-75838/UXImproveProjektarbeitSTVSprint247" since we apparently still want them in the LE optional branch; fix FAS issue where lva dropdown loaded a limited list when existing projektarbeit was loaded into UI -> now query the lehrveranstaltung.rdf with the stg_kz selected and then set the value to achieve desired behaviour; 2026-03-23 15:29:16 +01:00
Alexei Karpenko e90e03e0cc infocenter: added info if registered with electronic onboarding, or login with Electronic Onboarding. controller: moved public methods up to public section, hand cursor appear on all links in Stammdaten section 2026-03-18 13:47:24 +01:00
Johann Hoffmann 335671b46a query fix 2026-03-17 17:03:18 +01:00
Johann Hoffmann 19b8c16057 specify lehrveranstaltung_id in 2 rdf queries 2026-03-17 16:15:47 +01:00
Johann Hoffmann dea3aac222 WIP stv projektarbeit detail modal ux rework 2026-03-16 17:20:31 +01:00
Johann Hoffmann 5d7281e470 Merge branch 'master' into feature-75469/LeOptional4Projektarbeit
# Conflicts:
#	application/controllers/api/frontend/v1/Abgabe.php
#	public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js
#	public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js
#	public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js
#	public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js
#	system/phrasesupdate.php
2026-03-16 12:36:30 +01:00
Johann Hoffmann a7d925a177 even more legacy query projektarbeit join updates le_id -> lva_id & sem_kurzbz; WIP somehow touching the holy statistik queries 2026-03-13 12:58:55 +01:00
Johann Hoffmann 4ef4a06a7e more legacy query projektarbeit join updates le_id -> lva_id & sem_kurzbz 2026-03-12 18:30:12 +01:00
Johann Hoffmann 29515491a2 WIP legacy queries refactor projektarbeit lehreinheit optional 2026-03-12 14:02:52 +01:00
Johann Hoffmann 6cb8d0a9d1 projektarbeit fas tab lva_id & sem_kurzbz required; le optional 2026-03-11 15:06:11 +01:00
Johann Hoffmann 2c4574d93b form input bugfix where 2 inputs where needed for select elements after invalid validation; Abgabetool Download Latest Action; WIP stv projektarbeit details/betreuer considerations; WIP adding le_id -> lva_id & sem_kurzbz to FAS code; 2026-03-10 16:45:15 +01:00
Johann Hoffmann 2fde630971 adjust abgabetool queries Studentenverwaltung Projektarbeit Tab: lehreinheit required -> lehreinheit optional, lehrveranstaltung & studiensemester_kurbz required; changed occurences of the typo "Studiensemster" rigorously; 2026-03-09 15:25:44 +01:00
Johann Hoffmann fce5ee7a16 adjust abgabetool queries lehreinheit -> lehrveranstaltung table; always redraw with scrollX/Y preserve in abgabetool 2026-03-09 11:24:17 +01:00
Johann Hoffmann 8ccb9cd892 db migrate skript from le_id -> lva_id & stsem_kurzbz via existing references 2026-03-06 11:47:09 +01:00
Johann Hoffmann d1af44bcdc dbupdate entry; WIP table altering statements for lehre.tbl_projektarbeit 2026-03-05 17:33:18 +01:00
Johann Hoffmann 5a6d20f817 fixed scrollOffset discrepancy issue by UNDEFINING rowHeight and thus let tabulator detect the actual size of a row itself and not trust a sligthly wrong rowHeight value; define centered formatter in js/tabulator/formatter/centered and use it on unformatted columns for consistency; 2026-03-05 15:47:19 +01:00
Johann Hoffmann ce5da22180 import error msg phrase; config for noten which dount count towards prüfungsantritt & load & use that; 2026-03-03 16:47:17 +01:00
Johann Hoffmann 05b2c3c42b persistence of order/width/visibility of notenTable predefined columns before applying dynamic pruefung columns in setupData and other table col manipulating routines; fix antrittCalculation reactivity and resulting selection eligibility and the reactivity of that also; added custom sorter for pruefung cols; some minor UX tweaks; 2026-03-03 12:05:05 +01:00
Johann Hoffmann 2dd732b924 Merge branch 'master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/views/CisRouterView/CisRouterView.php
2026-02-27 10:38:24 +01:00
Johann Hoffmann 110f73e622 WIP notentool table persistence with dyn cols 2026-02-17 14:09:57 +01:00
Johann Hoffmann bb0d118284 Pruefung Termin1/original Note column fixed logic, variable column creation reserved for termin2/termin3 mixtures; remove redundant termintypen from student row in table if backend sent a succesful update response since their cant be multiple termin2/termin3; block notenvorschlag übernehmen once a pruefung exists; 2026-02-13 12:25:55 +01:00
Johann Hoffmann 701ccadff3 approveGrades phrase with counter; computed maxAntritte by config settings; block illegal pruefung creation in frontend (backend was already working); 2026-02-12 13:56:56 +01:00
Johann Hoffmann 34555504df Notenfreigabe email template insert in dbupdate3.4; notenvorschlag übernehmen cellHandler adjustment; WIP testing pruefungen & pruefungs config 2026-02-11 10:20:53 +01:00
Johann Hoffmann d1f5220925 notenschluessel availability check; WIP lvgesamtnote model method that actually fetches the notenvorschlag without join over zeugnisnote; 2026-02-10 12:57:38 +01:00
Johann Hoffmann decd514b22 WIP improving notenimport with punktefeature 2026-02-09 09:50:22 +01:00
Johann Hoffmann 6cf7093293 testing/implementing more config flags; dont select on certain cols; certain cols only available with certain flags; 2026-02-05 16:49:26 +01:00
Johann Hoffmann 17f11fa871 Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/controllers/api/frontend/v1/Lehre.php
#	application/models/education/Lehrveranstaltung_model.php
#	application/models/education/Note_model.php
#	application/views/CisRouterView/CisRouterView.php
#	public/js/api/factory/studiensemester.js
#	public/js/components/Bootstrap/Offcanvas.js
#	public/js/components/Overlay/FhcOverlay.js
2026-02-04 15:40:34 +01:00
Johann Hoffmann 96812868a4 WIP 2026-02-04 10:31:20 +01:00
Johann Hoffmann 40c79158f7 punkte feature basically finished; WIP testing & import rewrite 2026-02-03 17:38:33 +01:00
Johann Hoffmann 054663ee00 WIP punkte 2026-02-03 11:34:59 +01:00
Johann Hoffmann 81eee814e9 yellow dropdown styling only on editable tabulator colums for note_vorschlag; fetch note for punkte for notenvorschlag and pruefungsnote if certain config is set; added debounce helper file/function; WIP persisting punkte in backend 2026-02-02 17:07:16 +01:00
Johann Hoffmann 390a3c0d5a Notenschluessel Model + WIP making sense of legacy config flags 2026-01-29 14:50:37 +01:00
Johann Hoffmann 695dd655c0 WIP implementing getNotenvorschlagStudent, currently only works for whole lva/sem 2026-01-26 14:47:21 +01:00
Johann Hoffmann 957da460a6 shorter passwort freigabe text; loadCisConfig for Benotungstool via api; anw% in notentable via event; WIP incorporating CIS config into actual noten logic; 2025-12-18 15:24:08 +01:00
Johann Hoffmann 2cee36d7b5 try/catch around moodle event & proper error message 2025-12-17 14:29:05 +01:00
Johann Hoffmann 43925e3088 custom sticky css; offcanvas mobility legende; mobiltiy zusatz in seperate column; added getMobilityZusatzForUids & formatZusatz similar to digital anw mobility zusaetze; 2025-11-25 17:23:49 +01:00
Johann Hoffmann 1c236cce02 Merge branch 'master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/config/routes.php
#	application/models/crm/Prestudent_model.php
#	application/models/education/Lehreinheit_model.php
#	application/models/education/Lehrveranstaltung_model.php
#	public/js/apps/Dashboard/Fhc.js
#	system/phrasesupdate.php
2025-11-25 10:50:26 +01:00
Johann Hoffmann 4956a517ca loading overlay, notenfreigabe available phrase with counter; placeholder phrasen & date format; WIP handling moodle API errors well 2025-11-25 10:31:52 +01:00
ma0048 efc67d14a5 qulikurs spalte hinzugekommen bei infocenter freigegebendata 2025-11-06 12:38:13 +01:00
Johann Hoffmann a6167583a3 hinweistexte import/freigabe; distinct css for editable table cols notenvorschlag & freigabe; trigger 'getEntschuldigungsStatusForStudentOnDate' event when saving a pruefungstermin -> if akzeptierte entschuldigung is found for student on pruefungsdate it is automatically set to entschuldigt; fix event unmount lifecycle; 2025-10-07 16:26:58 +02:00
Johann Hoffmann 1e68eb0b90 berechtigungsprüfung 'lehre/benotungstool:rw' in Noten Controller; API Method documentation; removed addMeta statements; removed Tabulator Event logging; 2025-08-21 13:35:38 +02:00
Johann Hoffmann 332efd4106 selection fixes; phrases; preserve scroll after redraw when pressing action buttons on far right; take nav offset into account for width calculation; 2025-08-20 16:25:21 +02:00
Johann Hoffmann f303191c54 alternative email per cis global config 2025-08-19 16:33:12 +02:00
Johann Hoffmann d6c7f16ceb Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2025-08-19 13:43:23 +02:00
Johann Hoffmann f1912fe739 custom selection handling due to bugged tab5 rowSelect, should work as intended now, WIP illegal emails 2025-08-19 13:32:18 +02:00
Johann Hoffmann 2f7fe05d21 mobility legende; TopCalc Row (sum, negative, prueflinge); fix row selection issues; 2025-08-18 11:25:38 +02:00
Johann Hoffmann ee4b61f549 recommit branch; event naming changes; 2025-08-18 09:13:14 +02:00
Johann Hoffmann 511a4256bc moved LE loading and infoString setup into LehreinheitenModule, which can be bound to an instance of Primevue3 Dropdown via v-bind. WIP modularizing other common selections like LVA & Semester Kurzbz; 2025-08-08 13:35:01 +02:00
Johann Hoffmann 3c9db86df2 Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/config/routes.php
#	application/controllers/api/frontend/v1/Lehre.php
#	application/models/education/Lehrveranstaltung_model.php
#	application/views/CisRouterView/CisRouterView.php
#	public/js/apps/Dashboard/Fhc.js
#	system/phrasesupdate.php
2025-08-07 15:17:10 +02:00
Johann Hoffmann 367204a1ee removed legacy classes (except mobility) and moved crud functionality to LePruefungModel, LVgesamtnoteModel & LehrveranstaltungModel; 2025-08-07 14:54:41 +02:00
Johann Hoffmann bbe55a75ea noten/pruefungen import; import validation for nr of antritte and date/antritt chronological order; 2025-08-04 14:27:33 +02:00
Johann Hoffmann e58bf3a8cf WIP noten/pruefung import 2025-08-01 09:48:46 +02:00
Johann Hoffmann 1f2f866c61 positiv/negativ/unbenotet filter; reload lva/le correctly on sem/lva dropdown selection change; 2025-07-30 17:10:43 +02:00
Johann Hoffmann 6ccbc95697 createPruefung entry with "noch nicht eingetragen" note as default for a selection of students at given date; filterHeaders in noten cols; antrittCount col; pruefungsformatter with antritt highlighting; 2025-07-29 17:32:07 +02:00
Johann Hoffmann 52d9e0a195 pruefungen columns pro datum nicht pro termintyp; row Selection & modal newPruefungForSelectedStudents mit linked multiselect dropdown; WIP antritte berechnen 2025-07-25 12:50:51 +02:00
Johann Hoffmann 6a3982347b teilnoten/punkte berechnen für notenvorschläge; vorschläge übernehmen; noten freigabe mit passwort; prüfungen generisch anzeigen nach datum gruppiert; prüfungen anlegen/bearbeiten auf mapping termin1/2/3 möglich. 2025-07-24 17:02:57 +02:00
Johann Hoffmann fe7feeb74e Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2025-07-15 09:51:11 +02:00
Johann Hoffmann 658fe79ad7 load Teilnoten via moodle Event trigger; WIP further noten/punkte logic; 2025-05-12 16:45:33 +02:00
Johann Hoffmann 5bbf05ac8a WIP Gesamtnoteneingabe Notenberechnung endpoint 2025-05-12 12:50:56 +02:00
179 changed files with 21582 additions and 501 deletions
+1
View File
@@ -3,3 +3,4 @@
if (! defined('BASEPATH')) exit('No direct script access allowed');
$config['infocenter_studiengang_kz'] = '10021, 10027, 10002';
$config['infocenter_qualifikationskurs_kz'] = '10002';
+216
View File
@@ -0,0 +1,216 @@
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
// The configuration of the Benotungstool. The tool reads two sources:
// this file $this->config->item('KEY')
// global.config.inc.php define() flags of the old tool: CIS_GESAMTNOTE_PUNKTE, CIS_GESAMTNOTE_GEWICHTUNG,
// CIS_GESAMTNOTE_FREIGABEMAIL_NOTE, CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE and
// CIS_GESAMTNOTE_PRUEFUNG_TERMIN2/TERMIN3/KOMMPRUEF (see CIS_GESAMTNOTE_MAX_ANTRITTE)
//
// The prefixes in this file:
// NOTEN_, NOTE_ name a Note by its Bezeichnung. The server finds its key in lehre.tbl_note.
// PRUEFUNG_ name a Pruefungstyp from lehre.tbl_pruefungstyp.
// CIS_GESAMTNOTE_ the switches and values of the tool.
//
// Every key must be present. The code has no default values.
// --- Noten ---------------------------------------------------------------------------------------
// Noten that use no Antritt.
$config['NOTEN_OHNE_ANTRITT_BEZEICHNUNGEN'] = ['Noch nicht eingetragen', 'entschuldigt'];
// Anrechnung Noten: an earlier result counts for this LV. As a Zeugnisnote they block every Pruefung.
$config['NOTEN_ANRECHNUNG_BEZEICHNUNGEN'] = ['angerechnet', 'intern angerechnet'];
// How often a Note may occur in the Pruefungen of one student.
$config['NOTEN_OCCURRENCE_LIMIT_MAP'] = ['entschuldigt' => 1];
// The Note 'entschuldigt'. An addon can report a Pruefung date as entschuldigt.
$config['NOTE_ENTSCHULDIGT_BEZEICHNUNG'] = 'entschuldigt';
// The Note of a Pruefung without a result.
$config['NOTE_NICHT_EINGETRAGEN_BEZEICHNUNG'] = 'Noch nicht eingetragen';
// Noten with no better one. They always close the Antritt chain.
$config['NOTEN_ABSCHLIESSEND_BEZEICHNUNGEN'] = ['Sehr Gut', 'Bestanden', 'Approbiert', 'Erfolgreich absolviert'];
// The order of the Noten, best first. tbl_note.notenwert is NULL everywhere, so the order cannot come
// from the database. A Note outside this list is not comparable.
$config['NOTEN_RANGFOLGE_BEZEICHNUNGEN'] = ['Sehr Gut', 'Gut', 'Befriedigend', 'Genügend', 'Nicht Genügend'];
// The order of the Noten in the lists: 'skala' = 1-5 first, then alphabetical; 'bezeichnung' = alphabetical.
$config['NOTEN_SORTIERUNG'] = 'skala';
// --- Antritte -------------------------------------------------------------------------------------
// The maximum number of Antritte, Antritt 1 and the kommissionelle Pruefung included.
// null = 1 + CIS_GESAMTNOTE_PRUEFUNG_TERMIN2 + CIS_GESAMTNOTE_PRUEFUNG_TERMIN3 + CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF.
// The Pruefungsordnung permits three Antritte: TERMIN2 and KOMMPRUEF give 3, TERMIN3 adds one more.
$config['CIS_GESAMTNOTE_MAX_ANTRITTE'] = null;
// The Antritt from which the Pruefung is kommissionell:
// 'letzter' the last Antritt (CIS_GESAMTNOTE_MAX_ANTRITTE)
// 0 never
// 3 from Antritt 3 on
// A chain of one Antritt is never kommissionell.
$config['CIS_GESAMTNOTE_KOMMISSIONELL_AB_ANTRITT'] = 'letzter';
// The Benotungstool may create the kommissionelle Pruefung. Some installations enter it in the StV.
// false: the tool shows an existing kommissionelle Pruefung, but it creates none. The chain then ends
// one Antritt earlier for this tool.
$config['CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF'] = true;
// Allow another Antritt after a positive Note. It uses an Antritt like any repeat.
$config['CIS_GESAMTNOTE_NOTENVERBESSERUNG'] = false;
// A worse repeat keeps the better LV-Note. The Pruefung keeps its own Note.
$config['CIS_GESAMTNOTE_VERBESSERUNG_BESSERE_GEWINNT'] = false;
// Create Antritt 1 as its own Pruefung when a Note becomes the LV-Note: the proposal, the import, the
// Freigabe, and the Notenuebernahme in the StV.
$config['CIS_GESAMTNOTE_ERSTANTRITT_BEI_UEBERNAHME'] = true;
// --- Pruefungstypen ---------------------------------------------------------------------------------
// The tool derives the position of a Pruefung from its date. The type marks a kommissionelle Pruefung,
// sorts old Pruefungen without a date, separates Antritt 1 from a repeat (verlauf.hasRepeat), and feeds
// the old reports of the StV.
// The Pruefungstypen of a kommissionelle Pruefung.
$config['PRUEFUNG_KOMMISSIONELL_TYPEN'] = ['kommPruef', 'zusKommPruef'];
// The Pruefungstyp that the tool writes for a kommissionelle Pruefung.
$config['PRUEFUNG_TYP_KOMMISSIONELL'] = 'kommPruef';
// Pruefungstypen that never use an Antritt. A zusKommPruef repeats a kommissionelle Pruefung with a
// procedural fault. The StV enters it.
$config['PRUEFUNG_TYPEN_OHNE_ANTRITT'] = ['zusKommPruef'];
// The Pruefungstyp that the tool writes for each Antritt. A type that tbl_pruefungstyp does not have
// is skipped (Termin3 is absent by default).
$config['PRUEFUNG_TYP_JE_ANTRITT'] = [1 => 'Termin1', 2 => 'Termin2', 3 => 'Termin3'];
// --- Pruefung dates -------------------------------------------------------------------------------
// Allow a new Pruefung on the day of an existing one.
$config['CIS_GESAMTNOTE_PRUEFUNG_GLEICHER_TAG'] = false;
// A later Pruefung locks the Note of an earlier one. Its date stays editable.
$config['CIS_GESAMTNOTE_NOTE_SPERRE_BEI_SPAETERER_PRUEFUNG'] = true;
// Allow a Benotungsdatum in the future: the day of Antritt 1 when the LV-Note is written. A Pruefung
// may always lie in the future: without a Note it waits for its result.
$config['CIS_GESAMTNOTE_DATUM_ZUKUNFT'] = false;
// The minimum number of days between two Antritte. A Pruefung without an Antritt does not count.
// null = off
$config['CIS_GESAMTNOTE_ANTRITT_MIN_ABSTAND_TAGE'] = null;
// The maximum number of days between two Antritte. null = off
$config['CIS_GESAMTNOTE_ANTRITT_MAX_ABSTAND_TAGE'] = null;
// --- Frist ----------------------------------------------------------------------------------------
// The Frist (Pruefungsordnung §1) is a day in the year of the Studiensemester:
// Sommersemester SSyyyy -> the same year
// Wintersemester WSyyyy -> the next year
$config['NOTENEINTRAGUNGSFRIST_SS'] = ['month' => 11, 'day' => 15];
$config['NOTENEINTRAGUNGSFRIST_WS'] = ['month' => 5, 'day' => 15];
// No entry after the Frist.
$config['CIS_GESAMTNOTE_FRIST_EINGABE'] = true;
// No Pruefung date after the Frist.
// Example: a Pruefung must take place before the Frist, but the Note may come later -> EINGABE false.
$config['CIS_GESAMTNOTE_FRIST_PRUEFUNGSDATUM'] = true;
// Permissions that may enter after the Frist. The Pruefung date has no exception.
// Example: ['admin', 'lehre/benotungstool_assistenz']
$config['CIS_GESAMTNOTE_FRIST_AUSNAHME'] = [];
// --- Roles and Freigabe ---------------------------------------------------------------------------
// The actions of each permission. The keys are also the permissions that open the tool.
// lvnote write the LV-Note (the proposal)
// pruefung create or edit a Pruefung
// kommpruef create the kommissionelle Pruefung (also needs CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF)
// freigabe the Freigabe
// import the two imports
// A user with several permissions gets all their actions.
$config['CIS_GESAMTNOTE_ROLLENMATRIX'] = [
'lehre/benotungstool' => ['lvnote', 'pruefung', 'kommpruef', 'freigabe', 'import'],
'lehre/benotungstool_assistenz' => ['lvnote', 'pruefung', 'kommpruef', 'freigabe', 'import']
];
// A Lektor sees only the LVs that the Lektor teaches.
$config['CIS_GESAMTNOTE_LEKTOR_NUR_EIGENE_LV'] = true;
// The Freigabe asks for the password of the user.
$config['CIS_GESAMTNOTE_FREIGABE_PASSWORT'] = true;
// The Freigabe sends a mail.
$config['CIS_GESAMTNOTE_FREIGABEMAIL'] = true;
// The recipients of the mail: 'studiengang' = the addresses of the Studiengang, 'aufrufer' = the user.
// An entry with an '@' is a fixed address.
$config['CIS_GESAMTNOTE_FREIGABEMAIL_EMPFAENGER'] = ['studiengang', 'aufrufer'];
// The Sancho template of the mail. The text is in the database.
$config['CIS_GESAMTNOTE_FREIGABEMAIL_VORLAGE'] = 'Notenfreigabe';
// A freigegeben LV-Note is final. No path can change it.
$config['CIS_GESAMTNOTE_FREIGABE_FINAL'] = false;
// A new or changed Pruefung cancels the Freigabe of the LV-Note: it sets the benotungsdatum.
$config['CIS_GESAMTNOTE_PRUEFUNG_HEBT_FREIGABE_AUF'] = true;
// --- LV-Note and proposal -------------------------------------------------------------------------
// An LV-Note must be a Note of the Lehre (tbl_note.lehre).
$config['CIS_GESAMTNOTE_LVNOTE_NUR_LEHRENOTEN'] = true;
// The proposal stays open after a repeat. false: after a repeat the Lektor changes the LV-Note
// through the Pruefung.
$config['CIS_GESAMTNOTE_VORSCHLAG_NACH_WIEDERHOLUNG'] = false;
// The rounding of the Teilnoten average. The smaller number is the better Note:
// 'kaufmaennisch' 2.5 -> 3
// 'besser' 2.5 -> 2
// 'schlechter' 2.1 -> 3
$config['CIS_GESAMTNOTE_VORSCHLAG_RUNDUNG'] = 'kaufmaennisch';
// The decimals of the Punkte average before the Notenschluessel applies.
$config['CIS_GESAMTNOTE_VORSCHLAG_PUNKTE_STELLEN'] = 2;
// --- Import and display ---------------------------------------------------------------------------
// The two imports. With both on, the tool shows two buttons.
$config['CIS_GESAMTNOTE_PRUEFUNGSIMPORT'] = true; // one Pruefung per row: Kennung, Datum, Note
$config['CIS_GESAMTNOTE_NOTENIMPORT'] = false; // one LV-Note per row: Kennung, Note
// The Note column of an import accepts the key of lehre.tbl_note. true also accepts the short form
// from lehre.tbl_note.anmerkung ('nb', 'ea', 'en') of the Excel Notenliste.
$config['CIS_GESAMTNOTE_IMPORT_NOTENKUERZEL'] = false;
// The column order of an import. Allowed: 'kennung', 'datum', 'note'.
$config['CIS_GESAMTNOTE_IMPORT_SPALTEN_NOTEN'] = ['kennung', 'note'];
$config['CIS_GESAMTNOTE_IMPORT_SPALTEN_PRUEFUNG'] = ['kennung', 'datum', 'note'];
// The date format of an import: 'dd.MM.yyyy' or 'yyyy-MM-dd'.
$config['CIS_GESAMTNOTE_IMPORT_DATUMSFORMAT'] = 'dd.MM.yyyy';
// An import stops at the first rejected row. false: it reports the row and writes the others.
$config['CIS_GESAMTNOTE_IMPORT_ABBRUCH'] = false;
// The Pruefung columns of the table. The user can switch the layout; the browser keeps the choice.
// 'antritt' one column per Antritt, the date is in the cell
// 'datum' one column per Pruefung date
$config['CIS_GESAMTNOTE_PRUEFUNGSSPALTEN'] = 'antritt';
// The badge in a Pruefung cell; {n} is the Antritt number.
$config['CIS_GESAMTNOTE_ANTRITT_ZEICHEN'] = [
'kommissionell' => '{n}-K',
'kommissionell_ohne_antritt' => 'K',
'antritt' => '{n}',
'ohne_antritt' => '–'
];
+1
View File
@@ -64,6 +64,7 @@ $route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
$route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1';
$route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1';
$route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1';
$route['Cis/Benotungstool/.*'] = 'Cis/Benotungstool/index/$1';
$route['Cis/OtherLvPlan/.*'] = 'Cis/OtherLvPlan/index/$1';
//Route for LV Plan Stg/Semester/Verband/Gruppe
$route['Cis/StgOrgLvPlan/.*'] = 'Cis/StgOrgLvPlan/index/$1';
@@ -0,0 +1,35 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
// the constructor needs the permissions before CodeIgniter can load a helper
require_once APPPATH . 'helpers/hlp_benotungstool_helper.php';
/**
* The page of the Benotungstool. The same permissions open it as the Noten API.
*/
class Benotungstool extends Auth_Controller
{
public function __construct()
{
parent::__construct([
'index' => benotungstoolPermissions()
]);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$viewData = array(
'uid'=>getAuthUID(),
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Benotungstool']);
}
}
@@ -91,6 +91,8 @@ class CisMenu extends FHCAPI_Controller
$doc->loadXML($menuItem->content);
$urlElem = $doc->getElementsByTagName('url')->item(0);
$this->menuItemUrlTargetHelper($menuItem, $doc);
if (!$urlElem) {
return '';
}
@@ -122,5 +124,18 @@ class CisMenu extends FHCAPI_Controller
return $url;
}
private function menuItemUrlTargetHelper($menuItem, $xmldoc)
{
if($menuItem->template_kurzbz === 'redirect')
{
$targetElem = $xmldoc->getElementsByTagName('target')->item(0);
$target = ($targetElem) ? $targetElem->textContent : '';
if(!in_array($target, array('_self', '_top', '_blank')))
{
$target = '_self';
}
$menuItem->target = $target;
}
}
}
@@ -18,18 +18,9 @@
if (! defined('BASEPATH')) exit('No direct script access allowed');
//require_once('../../../include/studiengang.class.php');
//require_once('../../../include/student.class.php');
//require_once('../../../include/datum.class.php');
//require_once('../../../include/mail.class.php');
//require_once('../../../include/benutzerberechtigung.class.php');
//require_once('../../../include/phrasen.class.php');
//require_once('../../../include/projektarbeit.class.php');
//require_once('../../../include/projektbetreuer.class.php');
class Lehre extends FHCAPI_Controller
{
/**
* Object initialization
*/
@@ -41,37 +32,53 @@ class Lehre extends FHCAPI_Controller
'Pruefungen' => self::PERM_LOGGED,
'semesterAverageGrade' => self::PERM_LOGGED,
]);
$this->load->library('PhrasesLib');
$this->loadPhrases(
array(
'global',
'ui',
'abgabetool'
)
);
$this->load->helper('hlp_sancho_helper');
require_once(FHCPATH . 'include/studiengang.class.php');
require_once(FHCPATH . 'include/student.class.php');
require_once(FHCPATH . 'include/projektarbeit.class.php');
require_once(FHCPATH . 'include/projektbetreuer.class.php');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
/**
* constructs the emails of the groups from a lehrveranstaltung
*/
public function lvStudentenMail()
public function lvStudentenMail()
{
$lehreinheit_id = $this->input->get("lehreinheit_id",TRUE);
// return early if the required parameter is missing
if(!isset($lehreinheit_id))
{
$this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL);
}
$lehreinheit_id = $this->input->get("lehreinheit_id",TRUE);
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id);
// return early if the required parameter is missing
if(!isset($lehreinheit_id))
{
$this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL);
}
$studentenMails = $this->getDataOrTerminateWithError($studentenMails);
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id);
$studentenMails = $this->getDataOrTerminateWithError($studentenMails);
//convert array of objects into array of strings
$studentenMails = array_map(function($element){
return $element->mail;
}, $studentenMails);
$this->terminateWithSuccess($studentenMails);
$this->terminateWithSuccess($studentenMails);
}
public function LV($studiensemester_kurzbz, $lehrveranstaltung_id)
@@ -81,13 +88,13 @@ class Lehre extends FHCAPI_Controller
$result = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(getAuthUID(), $studiensemester_kurzbz, getUserLanguage(), $lehrveranstaltung_id);
$result = current($this->getDataOrTerminateWithError($result));
$this->terminateWithSuccess($result);
}
/**
* fetches all Pruefungen of a student for a specific lehrveranstaltung
* if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database
* if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database
* @param mixed $lehrveranstaltung_id
* @return void
*/
@@ -145,5 +152,5 @@ class Lehre extends FHCAPI_Controller
$this->terminateWithSuccess(['average_grade' => $averageGrade, 'weighted_average_grade' => $weightedAverageGrade]);
}
}
}
@@ -81,7 +81,7 @@ class Lehrveranstaltung extends FHCAPI_Controller
{
$studiengang_kz = $this->getStudiengangKz($stg_kz);
if (is_null($studiengang_kz) || !preg_match("/^-?[1-9][0-9]*$/", (string)$studiengang_kz))
if (is_null($studiengang_kz) || !preg_match("/^(-?[1-9][0-9]*|0)$/", (string)$studiengang_kz))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$verband = null;
File diff suppressed because it is too large Load Diff
@@ -167,7 +167,9 @@ class Studium extends FHCAPI_Controller
if($aktuelles_studienplan){
$lehrveranstaltungen = $this->computeStudienplanLehrveranstaltungen($aktuelles_studienplan->studienplan_id, $aktuelles_semester);
foreach($lehrveranstaltungen as $lehrv){
foreach($lehrv->lehrveranstaltungen as $lv){
// a lehrveranstaltung without a modul has no children and gets its lektoren directly
$lvs = isset($lehrv->lehrveranstaltungen) ? $lehrv->lehrveranstaltungen : [$lehrv];
foreach($lvs as $lv){
$lvLektoren =$this->computeLektorenFromLehrveranstaltung($lv->lehrveranstaltung_id,$aktuelles_semester, $aktuelles_studiengang->studiengang_kz, $aktuelles_studiensemester->studiensemester_kurzbz);
$lv->lektoren = $lvLektoren;
}
@@ -286,6 +288,7 @@ SELECT tbl_lehrveranstaltung.*,
$lehrveranstaltungen = $this->LehrveranstaltungModel->execReadOnlyQuery($query,[$studienplan_id, $semester]);
$lehrveranstaltungen = $this->getDataOrTerminateWithError($lehrveranstaltungen);
usort($lehrveranstaltungen, function($a, $b){
if($a->lehrtyp_kurzbz == "modul"){
return -1;
@@ -302,12 +305,17 @@ SELECT tbl_lehrveranstaltung.*,
}
else{
$parent =array_filter($carry, function($item)use($lehrv){
return $item->studienplan_lehrveranstaltung_id == $lehrv->studienplan_lehrveranstaltung_id_parent;
return isset($item->lehrveranstaltungen)
&& $item->studienplan_lehrveranstaltung_id == $lehrv->studienplan_lehrveranstaltung_id_parent;
});
$parent = current($parent);
if($parent){
$parent->lehrveranstaltungen[] = $lehrv;
}
else{
// lehrveranstaltung without a modul: keep it on the top level
array_push($carry, $lehrv);
}
}
return $carry;
}, []);
@@ -41,6 +41,9 @@ class Grades extends FHCAPI_Controller
// Load Libraries
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
$this->load->library('PruefungsverlaufLib', null, 'VerlaufLib');
$this->load->config('noten');
// Load Phrases
$this->loadPhrases([
@@ -355,23 +358,24 @@ class Grades extends FHCAPI_Controller
$data['lehrveranstaltung_id'] = $lehrveranstaltung_id;
$data['student_uid'] = $student_uid;
$data['studiensemester_kurzbz'] = $studiensemester_kurzbz;
$this->ZeugnisnoteModel->insert($data);
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);
}
$this->ZeugnisnoteModel->insert($data);
}
// Der erste Antritt wird bereits bei der Übernahme als eigene Prüfung angelegt, damit der
// Prüfungsverlauf von Anfang an vollständig ist und das Benotungstool ihn nicht erst beim
// Anlegen einer Wiederholung nachtragen muss. Läuft bewusst in BEIDEN Zweigen (auch beim
// Überschreiben einer bestehenden Zeugnisnote) und ist idempotent.
if ($this->config->item('CIS_GESAMTNOTE_ERSTANTRITT_BEI_UEBERNAHME')) {
$result = $this->addErstantrittForGrade(
$studiensemester_kurzbz,
$student_uid,
$lehrveranstaltung_id,
$teacherGrade
);
$this->getDataOrTerminateWithError($result);
}
$this->terminateWithSuccess(true);
}
@@ -491,20 +495,32 @@ class Grades extends FHCAPI_Controller
}
/**
* Helper function that adds tests for a student
* (Entries in lehre.tbl_pruefung)
* Legt bei der Notenübernahme den ersten Prüfungsantritt als eigenen Datensatz an
* (Eintrag in lehre.tbl_pruefung).
*
* @param string $studiensemester_kurzbz
* @param string $student_uid
* @param integer $lehrveranstaltung_id
* @param integer $note
* @param numeric $punkte
* Idempotent: existiert für den Studenten in dieser LV bereits ein Termin, passiert nichts -
* die Übernahme darf beliebig oft laufen, ohne den Verlauf zu verfälschen.
*
* Datiert auf das Freigabedatum der LV-Note: das ist der Zeitpunkt, zu dem die
* Leistungsfeststellung verbindlich wurde. Das Benotungsdatum ändert sich bei jeder Korrektur,
* der Übernahmezeitpunkt ist reine Verwaltungsarbeit - beide taugen nicht als Termindatum.
*
* @param string $studiensemester_kurzbz
* @param string $student_uid
* @param integer $lehrveranstaltung_id
* @param stdClass $teacherGrade die übernommene LV-Gesamtnote
*
* @return stdClass
*/
protected function addTestsForGrade($studiensemester_kurzbz, $student_uid, $lehrveranstaltung_id, $note, $punkte)
protected function addErstantrittForGrade($studiensemester_kurzbz, $student_uid, $lehrveranstaltung_id, $teacherGrade)
{
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('education/LePruefung_model', 'LePruefungModel');
// bereits ein Termin vorhanden -> der Verlauf beginnt schon, nichts nachzutragen
$vorhanden = $this->VerlaufLib->getPruefungen($student_uid, $lehrveranstaltung_id, $studiensemester_kurzbz);
if (count($vorhanden) > 0)
return success(null);
// Get Lehreinheit
$result = $this->LehrveranstaltungModel->getLeByStudent($student_uid, $studiensemester_kurzbz, $lehrveranstaltung_id);
@@ -515,19 +531,24 @@ class Grades extends FHCAPI_Controller
return error($this->p->t("stv", "grades_error_lehreinheit_id"));
$le = current(getData($result));
$datum = $teacherGrade->freigabedatum ?: ($teacherGrade->benotungsdatum ?: date('Y-m-d'));
$now = date('Y-m-d H:i:s');
// 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",
"note" => $note
"mitarbeiter_uid" => $teacherGrade->mitarbeiter_uid,
"datum" => $datum,
"pruefungstyp_kurzbz" => $this->VerlaufLib->legacyTypFuerAntritt(1),
"note" => $teacherGrade->note,
"insertamum" => $now,
"insertvon" => getAuthUID()
];
if (defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE)
$data["punkte"] = $punkte;
$data["punkte"] = $teacherGrade->punkte;
// Get Anwesenheit
$this->load->model('education/Anwesenheit_model', 'AnwesenheitModel');
$result = $this->AnwesenheitModel->loadAnwesenheitStudiensemester($studiensemester_kurzbz, $student_uid, $lehrveranstaltung_id);
@@ -535,7 +556,8 @@ class Grades extends FHCAPI_Controller
return $result;
$anwesenheit = getData($result);
if ($anwesenheit && (float)current($anwesenheit)->prozent < FAS_ANWESENHEIT_ROT) {
if (defined('FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN') && FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN
&& $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');
@@ -545,7 +567,10 @@ class Grades extends FHCAPI_Controller
$anwesenheitsbefreit = hasData($result);
// Wenn nicht Anwesenheitsbefreit und Anwesenheit unter einem bestimmten Prozentsatz fällt dann wird ein Pruefungsantritt abgezogen
// Bei zu geringer Anwesenheit ohne Befreiung wird ein zusätzlicher Termin
// 'Nicht beurteilt' vor dem eigentlichen Antritt festgehalten. ACHTUNG: diese Note
// zählt nicht mehr als Antritt (NOTEN_OHNE_ANTRITT_BEZEICHNUNGEN) - der frühere
// "Antritt abziehen"-Effekt entfällt damit, der Eintrag ist reine Dokumentation.
if (!$anwesenheitsbefreit) {
$data2 = $data;
$data2["note"] = 7;
@@ -556,8 +581,6 @@ class Grades extends FHCAPI_Controller
if (isError($result))
return $result;
$data["pruefungstyp_kurzbz"] = "Termin2";
}
}
@@ -370,6 +370,7 @@ class LvTermine extends FHCAPI_Controller
$result->semester = $item->semester;
$result->verband = $item->verband;
$result->gruppe = $item->gruppe;
$result->studiensemester_kurzbz = $item->studiensemester_kurzbz;
return $result;
},
$lehrverband_query_result));
@@ -42,6 +42,8 @@ class Projektarbeit extends FHCAPI_Controller
$this->load->model('education/Note_model', 'NoteModel');
$this->load->model('education/Projektbetreuer_model', 'BetreuerModel');
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel');
// load libraries
$this->load->library('PermissionLib');
@@ -100,16 +102,16 @@ class Projektarbeit extends FHCAPI_Controller
private function fetchProjektarbeitById($projektarbeit_id) {
$this->ProjektarbeitModel->resetQuery();
$this->ProjektarbeitModel->addSelect(
'lehre.tbl_projektarbeit.projektarbeit_id, titel, titel_english, themenbereich, projekttyp_kurzbz, lehrveranstaltung_id, lehreinheit_id,
firma_id, beginn, ende, gesperrtbis, note, final, freigegeben, tbl_projektarbeit.anmerkung, fa.name AS firma_name'
'lehre.tbl_projektarbeit.projektarbeit_id, titel, titel_english, themenbereich, projekttyp_kurzbz, lehrveranstaltung_id, lehreinheit_id,
tbl_projektarbeit.studiensemester_kurzbz, firma_id, beginn, ende, gesperrtbis, note, final, freigegeben, tbl_projektarbeit.anmerkung, fa.name AS firma_name'
);
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id');
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id');
$this->ProjektarbeitModel->addJoin('public.tbl_firma fa', 'firma_id', 'LEFT');
return $this->ProjektarbeitModel->loadWhere(
array('projektarbeit_id' => $projektarbeit_id)
);
}
/**
@@ -246,14 +248,38 @@ class Projektarbeit extends FHCAPI_Controller
$additional_lehrveranstaltung_id = $this->input->get('additional_lehrveranstaltung_id');
if (!isset($student_uid)) $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Student UID']), self::ERROR_TYPE_GENERAL);
if (!isset($studiensemester_kurzbz)) $this->terminateWithError('Studiensemster missing', self::ERROR_TYPE_GENERAL);
if (!isset($studiensemester_kurzbz)) $this->terminateWithError('Studiensemester missing', self::ERROR_TYPE_GENERAL);
// get Lvs
// get prestudent_id from student model
$studentRes = $this->StudentModel->load(['student_uid' => $student_uid]);
if(hasData($studentRes)) {
$student = getData($studentRes);
} else {
$this->terminateWithError($studentRes, self::ERROR_TYPE_GENERAL);
}
// to get prestudent_status history
$historyRes = $this->PrestudentstatusModel->getHistoryPrestudent($student[0]->prestudent_id);
if(hasData($historyRes)) {
$history = getData($historyRes);
} else {
$this->terminateWithError($historyRes, self::ERROR_TYPE_GENERAL);
}
// query for every lva of a studiengang that has projektarbeit flag true
$lvsResult = $this->LehrveranstaltungModel->getLvsForProjektarbeit($student_uid, $studiengang_kz, $additional_lehrveranstaltung_id);
if (isError($lvsResult)) return $this->terminateWithError($lvsResult, self::ERROR_TYPE_GENERAL);
$lvs = hasData($lvsResult) ? getData($lvsResult) : [];
$lvsIDs = array_column($lvs, 'lehrveranstaltung_id');
// get current studienplan_id from status history to find out which lva options are actually assigned to the students studienplan
// important so the projektarbeit has the correct lva assigned to be able to find and print the thesis title on the degree documents!
$currentStatus = $history[0];
$lvByStudienPlanRes = $this->LehrveranstaltungModel->getLvsByStudienplanByLvaIDs($currentStatus->studienplan_id, $lvsIDs);
if(hasData($lvByStudienPlanRes)) {
$lvs = getData($lvByStudienPlanRes);
}
foreach ($lvs as $lv)
{
@@ -320,11 +346,18 @@ class Projektarbeit extends FHCAPI_Controller
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Projekttyp'])
]);
$this->form_validation->set_rules('lehreinheit_id', 'Lehreinheit', 'required|is_natural', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehreinheit']),
$this->form_validation->set_rules('lehreinheit_id', 'Lehreinheit', 'is_natural', [
'is_natural' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => 'Lehreinheit'])
]);
$this->form_validation->set_rules('lehrveranstaltung_id', 'Lehrveranstaltung', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehrveranstaltung'])
]);
$this->form_validation->set_rules('studiensemester_kurzbz', 'Studiensemester', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Studiensemester'])
]);
$this->form_validation->set_rules('beginn', 'Beginn', 'is_valid_date', [
'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'Beginn'])
]);
@@ -354,6 +387,8 @@ class Projektarbeit extends FHCAPI_Controller
'projekttyp_kurzbz' => $formData['projekttyp_kurzbz'],
'firma_id' => $formData['firma_id'] ?? null,
'lehreinheit_id' => $formData['lehreinheit_id'],
'lehrveranstaltung_id' => $formData['lehrveranstaltung_id'],
'studiensemester_kurzbz' => $formData['studiensemester_kurzbz'],
'beginn' => isset($formData['beginn']) && !isEmptyString($formData['beginn']) ? $formData['beginn'] : null,
'ende' => isset($formData['ende']) && !isEmptyString($formData['ende']) ? $formData['ende'] : null,
'note' => $formData['note'] ?? null,
@@ -108,6 +108,7 @@ class Students extends FHCAPI_Controller
'studiensemester_kurzbz' => $studiensemester_kurzbz
]);
$studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz);
$this->studentlistlib->addJoin(
"(
@@ -153,7 +154,8 @@ class Students extends FHCAPI_Controller
$this->addMeta('ci_params', [
'studiensemester_kurzbz' => $studiensemester_kurzbz
]);
$studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz);
$this->studentlistlib->addJoin(
"(
@@ -213,6 +215,7 @@ class Students extends FHCAPI_Controller
'studiensemester_kurzbz' => $studiensemester_kurzbz
]);
$studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz);
$this->studentlistlib->addJoin(
"(
@@ -803,7 +803,7 @@ class ReihungstestJob extends JOB_Controller
{
if (!isset($studiensemester) || isEmptyString($studiensemester))
{
$this->logError("Studiensemster not passed as parameter");
$this->logError("Studiensemester not passed as parameter");
return;
}
@@ -34,6 +34,9 @@ class InfoCenter extends Auth_Controller
const PREV_FILTER_ID = 'prev_filter_id';
const KEEP_TABLESORTER_FILTER = 'keepTsFilter';
const ONBOARDING_INSERTVON = 'onboarding';
const ONBOARDING_KENNZEICHENTYP = 'eobRegistrierungsId';
private $_uid; // contains the UID of the logged user
// Used to log with PersonLogLib
@@ -175,6 +178,7 @@ class InfoCenter extends Auth_Controller
$this->load->model('person/Kontakt_model', 'KontaktModel');
$this->load->model('person/Geschlecht_model', 'GeschlechtModel');
$this->load->model('person/adresse_model', 'AdresseModel');
$this->load->model('person/Kennzeichen_model', 'KennzeichenModel');
// Loads libraries
$this->load->library('PersonLogLib');
@@ -1452,6 +1456,100 @@ class InfoCenter extends Auth_Controller
$this->outputJsonSuccess("Done!");
}
public function getAbsageData()
{
$stg_typ = $this->getStudienArtBerechtigung(['b', 'm']);
if (!is_null($stg_typ))
{
$statusgruende = $this->StatusgrundModel->getStatus(self::ABGEWIESENERSTATUS, true)->retval;
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
$studiengaenge = $this->StudiengangModel->getStudiengaengeWithOrgForm(array_column($stg_typ, 'typ'), $studienSemester);
$data = array (
'statusgruende' => $statusgruende,
'studiengaenge' => $studiengaenge->retval
);
$this->outputJsonSuccess($data);
}
else
$this->outputJsonSuccess(null);
}
public function getStudienArtBerechtigung($typ = null)
{
$studiengang_kz_all = $this->permissionlib->getSTG_isEntitledFor('infocenter');
$stg_typ = $this->StudiengangModel->getStudiengangTyp($studiengang_kz_all, $typ);
return getData($stg_typ);
}
public function getStudienartData()
{
$this->outputJsonSuccess($this->getStudienArtBerechtigung(['b', 'm', 'l']));
}
public function saveAbsageForAll()
{
$statusgrund = $this->input->post('statusgrund');
$studiengang = $this->input->post('studiengang');
$abgeschickt = $this->input->post('abgeschickt');
$personen = $this->input->post('personen');
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
if ($statusgrund === 'null' || $studiengang === 'null' || $abgeschickt === 'null' || empty($personen))
$this->terminateWithJsonError("Bitte füllen Sie alle Felder aus");
if ($studiengang === 'all' && $abgeschickt === 'all')
{
foreach($personen as $person)
{
$prestudenten = $this->PrestudentModel->getByPersonWithoutLehrgang($person, $studienSemester);
if (!hasData($prestudenten))
continue;
$prestudentenData = getData($prestudenten);
foreach ($prestudentenData as $prestudent)
{
$this->saveAbsage($prestudent->prestudent_id, $statusgrund);
}
}
}
else
{
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$this->StudienplanModel->addSelect('1');
$this->StudienplanModel->addJoin('lehre.tbl_studienordnung so', 'studienordnung_id');
$escaped = $this->StudienplanModel->db->escape(strtoupper($studiengang));
$this->StudienplanModel->db->where("UPPER(so.studiengangkurzbzlang || ':' || tbl_studienplan.orgform_kurzbz) = $escaped");
$this->StudienplanModel->addLimit(1);
$studiengangResult = $this->StudienplanModel->load();
if (hasData($studiengangResult))
{
foreach($personen as $person)
{
$prestudent = $this->PrestudentModel->getPrestudentByStudiengangAndPerson($studiengang, $person, $studienSemester, $abgeschickt, $abgeschickt === 'all');
if (!hasData($prestudent))
continue;
$prestudentData = getData($prestudent);
$this->saveAbsage($prestudentData[0]->prestudent_id, $statusgrund);
}
}
else
$this->terminateWithJsonError("Falschen Studiengang übergeben!");
}
$this->outputJsonSuccess("Success");
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
@@ -1946,6 +2044,17 @@ class InfoCenter extends Auth_Controller
show_error(getError($user_person));
}
// add info about first electronig onboarding login
$this->KennzeichenModel->addSelect('insertamum');
$onboarding_first_login = $this->KennzeichenModel->loadWhere(
array('person_id' => $person_id, 'kennzeichentyp_kurzbz' => self::ONBOARDING_KENNZEICHENTYP)
);
if (isError($onboarding_first_login))
{
show_error(getError($onboarding_first_login));
}
$data = array (
'lockedby' => $lockedby,
'lockedbyother' => $lockedbyother,
@@ -1955,7 +2064,9 @@ class InfoCenter extends Auth_Controller
'messages' => $messages->retval,
'logs' => $logs,
'notizen' => $notizen->retval,
'notizenbewerbung' => $notizen_bewerbung->retval
'notizenbewerbung' => $notizen_bewerbung->retval,
'created_by_onboarding' => $stammdaten->retval->insertvon == self::ONBOARDING_INSERTVON,
'onboarding_first_login' => hasData($onboarding_first_login) ? getData($onboarding_first_login)[0]->insertamum : null
);
return $data;
@@ -2375,97 +2486,4 @@ class InfoCenter extends Auth_Controller
$this->loglib->logError('Studiengang has no mail for sending Freigabe mail');
}
}
public function getAbsageData()
{
$stg_typ = $this->getStudienArtBerechtigung(['b', 'm']);
if (!is_null($stg_typ))
{
$statusgruende = $this->StatusgrundModel->getStatus(self::ABGEWIESENERSTATUS, true)->retval;
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
$studiengaenge = $this->StudiengangModel->getStudiengaengeWithOrgForm(array_column($stg_typ, 'typ'), $studienSemester);
$data = array (
'statusgruende' => $statusgruende,
'studiengaenge' => $studiengaenge->retval
);
$this->outputJsonSuccess($data);
}
else
$this->outputJsonSuccess(null);
}
public function getStudienArtBerechtigung($typ = null)
{
$studiengang_kz_all = $this->permissionlib->getSTG_isEntitledFor('infocenter');
$stg_typ = $this->StudiengangModel->getStudiengangTyp($studiengang_kz_all, $typ);
return getData($stg_typ);
}
public function getStudienartData()
{
$this->outputJsonSuccess($this->getStudienArtBerechtigung(['b', 'm', 'l']));
}
public function saveAbsageForAll()
{
$statusgrund = $this->input->post('statusgrund');
$studiengang = $this->input->post('studiengang');
$abgeschickt = $this->input->post('abgeschickt');
$personen = $this->input->post('personen');
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
if ($statusgrund === 'null' || $studiengang === 'null' || $abgeschickt === 'null' || empty($personen))
$this->terminateWithJsonError("Bitte füllen Sie alle Felder aus");
if ($studiengang === 'all' && $abgeschickt === 'all')
{
foreach($personen as $person)
{
$prestudenten = $this->PrestudentModel->getByPersonWithoutLehrgang($person, $studienSemester);
if (!hasData($prestudenten))
continue;
$prestudentenData = getData($prestudenten);
foreach ($prestudentenData as $prestudent)
{
$this->saveAbsage($prestudent->prestudent_id, $statusgrund);
}
}
}
else
{
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$this->StudienplanModel->addSelect('1');
$this->StudienplanModel->addJoin('lehre.tbl_studienordnung so', 'studienordnung_id');
$escaped = $this->StudienplanModel->db->escape(strtoupper($studiengang));
$this->StudienplanModel->db->where("UPPER(so.studiengangkurzbzlang || ':' || tbl_studienplan.orgform_kurzbz) = $escaped");
$this->StudienplanModel->addLimit(1);
$studiengangResult = $this->StudienplanModel->load();
if (hasData($studiengangResult))
{
foreach($personen as $person)
{
$prestudent = $this->PrestudentModel->getPrestudentByStudiengangAndPerson($studiengang, $person, $studienSemester, $abgeschickt, $abgeschickt === 'all');
if (!hasData($prestudent))
continue;
$prestudentData = getData($prestudent);
$this->saveAbsage($prestudentData[0]->prestudent_id, $statusgrund);
}
}
else
$this->terminateWithJsonError("Falschen Studiengang übergeben!");
}
$this->outputJsonSuccess("Success");
}
}
@@ -7,6 +7,8 @@ class Rueckstellung extends Auth_Controller
private $_ci; // Code igniter instance
private $_uid;
const ZGV_UEBERPRUEFUNG_MAIL_VORLAGE = 'ZGVUEP';
public function __construct()
{
parent::__construct(
@@ -23,6 +25,7 @@ class Rueckstellung extends Auth_Controller
$this->load->model('crm/RueckstellungStatus_model', 'RueckstellungStatusModel');
$this->load->model('person/Person_model', 'PersonModel');
$this->load->library('PersonLogLib');
$this->load->library('MessageLib');
$this->_setAuthUID(); // sets property uid
@@ -78,6 +81,8 @@ class Rueckstellung extends Auth_Controller
$this->_log($person_id, $status_kurzbz);
if ($status_kurzbz === 'onhold_zgv')
$this->sendRueckstellungMessage($person_id);
$this->outputJson($result);
}
@@ -104,6 +109,10 @@ class Rueckstellung extends Auth_Controller
if (isError($result))
$this->terminateWithJsonError(getError($result));
$this->_log($person, $status_kurzbz);
if ($status_kurzbz === 'onhold_zgv')
$this->sendRueckstellungMessage($person);
}
$this->outputJsonSuccess("Erfolgreich gespeichert!");
}
@@ -160,4 +169,26 @@ class Rueckstellung extends Auth_Controller
$this->_uid
);
}
private function sendRueckstellungMessage($person_id)
{
$person = $this->_ci->PersonModel->load($person_id);
if (hasData($person))
{
$person = getData($person)[0];
return $this->messagelib->sendMessageUserTemplate(
$person_id,
self::ZGV_UEBERPRUEFUNG_MAIL_VORLAGE,
array(
'anrede' => $person->anrede,
'nachname' => $person->nachname
),
null,
getAuthPersonId(),
'infocenter'
);
}
}
}
@@ -0,0 +1,21 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* The permissions that open the Benotungstool: the keys of CIS_GESAMTNOTE_ROLLENMATRIX. The page and
* the Noten API read this one list. A constructor needs it before CodeIgniter can load a configuration,
* so this reads the file itself.
*
* @return array one 'permission:rw' entry per key
*/
function benotungstoolPermissions()
{
$config = array();
include APPPATH . 'config/noten.php';
$permissions = array();
foreach (array_keys($config['CIS_GESAMTNOTE_ROLLENMATRIX']) as $permission) $permissions[] = $permission . ':rw';
return $permissions;
}
+1 -1
View File
@@ -741,7 +741,7 @@ class AnrechnungLib
$fullname = hasData($result) ? $result->retval[0]->vorname. $result->retval[0]->nachname : '';
// Return filename
return 'Anrechnungsantrag'. $orgform_kurzbz .'_LV-'. $lehrveranstaltung_id. '_'. $fullname;
return 'Anrechnungsantrag'. $orgform_kurzbz .'_LV-'. $lehrveranstaltung_id. '_'. $fullname . '.pdf';
}
public function LVhasLector($anrechnung_id)
@@ -0,0 +1,765 @@
<?php
/**
* Copyright (C) 2026 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* The Verlauf of one student in one LV: the order of the Pruefungen, the Antritte and the limits.
*
* This class holds the rules of the Antritt chain. The controller holds access, Frist and Freigabe.
* The client only reads the Verlauf.
*
* The StV calls getPruefungen() and legacyTypFuerAntritt(). Keep their names and signatures.
*/
class PruefungsverlaufLib
{
/** Antritt 1. Without a Pruefung the LV-Note itself is Antritt 1. */
const ROLE_ERSTANTRITT = 'erstantritt';
/** Each Antritt after the first one. */
const ROLE_PRUEFUNG = 'pruefung';
/** The Antritt before a commission (CIS_GESAMTNOTE_KOMMISSIONELL_AB_ANTRITT). */
const ROLE_KOMMISSIONELL = 'kommissionell';
/** upsertErstantritt(): an existing Antritt 1 keeps its date. */
const KEEP_DATUM = false;
/** upsertErstantritt(): an existing Antritt 1 takes the new date. */
const SET_DATUM = true;
private $_ci;
private $_noteCache = [];
private $_specialNotes = null;
private $_types = null;
public function __construct()
{
$this->_ci =& get_instance();
$this->_ci->load->model('education/LePruefung_model', 'LePruefungModel');
$this->_ci->load->model('education/Note_model', 'NoteModel');
$this->_ci->load->model('education/Pruefungstyp_model', 'PruefungstypModel');
$this->_ci->load->config('noten');
}
/**
* Builds the Verlauf. Each Pruefung gets position, is_antritt, antritt_nr, kommissionell and
* note_locked. The rules and the client read this one object.
*
* @param array $pruefungen the Pruefungen of one student in one LV
* @param mixed|null $lvNote Antritt 1 while no Pruefung is an Antritt
* @param mixed|null $zeugnisnote an Anrechnung blocks every Pruefung
* @return stdClass
*/
public function buildVerlauf($pruefungen, $lvNote, $zeugnisnote)
{
$pruefungen = $this->sortPruefungen($pruefungen);
// a later Pruefung locks the Note of an earlier one; its date stays editable
$lockedByLater = $this->configBool('CIS_GESAMTNOTE_NOTE_SPERRE_BEI_SPAETERER_PRUEFUNG');
$antrittCount = 0;
$terminal = false;
$entries = [];
foreach ($pruefungen as $i => $pruefung) {
$isAntritt = $this->isAntritt($pruefung->note, $pruefung->pruefungstyp_kurzbz);
if ($isAntritt) $antrittCount++;
// A kommissionelle Pruefung closes the chain, also if its Note uses no Antritt.
$kommissionell = $this->isKommissionell($pruefung->pruefungstyp_kurzbz);
if ($kommissionell) $terminal = true;
$entry = clone $pruefung;
$entry->position = $i + 1;
$entry->is_antritt = $isAntritt;
$entry->antritt_nr = $isAntritt ? $antrittCount : null;
$entry->kommissionell = $kommissionell;
$entry->note_locked = $lockedByLater && $i < count($pruefungen) - 1;
$entries[] = $entry;
}
// without a Pruefung that is an Antritt, the LV-Note itself is Antritt 1
$implicit = ($antrittCount === 0 && $lvNote !== null && $this->isAntritt($lvNote));
if ($implicit) $antrittCount = 1;
// the deciding Note: the last Antritt, else the LV-Note
$currentNote = null;
foreach ($entries as $entry) {
if ($entry->is_antritt) $currentNote = $entry->note;
}
if ($currentNote === null && $implicit) $currentNote = $lvNote;
// a closing Note always ends the chain, any other positive Note only without Notenverbesserung
$bestanden = $this->isClosingNote($currentNote)
|| (!$this->allowsImprovement() && $this->isPositiveNote($currentNote));
if ($bestanden) $terminal = true;
$verlauf = new stdClass();
$verlauf->pruefungen = $entries;
$verlauf->antrittCount = $antrittCount;
$verlauf->maxAntritte = $this->getMaxAntritte();
$verlauf->terminal = $terminal;
// closed by a pass, not by the limit
$verlauf->bestanden = $bestanden;
// the row stays visible, but nobody can enter anything
$verlauf->angerechnet = $this->isAnrechnungNote($zeugnisnote);
$verlauf->implicitErstantritt = $implicit;
$verlauf->canAdd = !$terminal && !$verlauf->angerechnet && $antrittCount < $verlauf->maxAntritte;
// a second Pruefung, or one Pruefung that is not Antritt 1 (next to an implicit Antritt 1).
// This is the only rule that reads the legacy Pruefungstyp back.
$verlauf->hasRepeat = count($entries) > 1
|| (count($entries) === 1 && $entries[0]->pruefungstyp_kurzbz !== $this->legacyTypFuerAntritt(1));
// The role follows from the position (§17 Abs 1), so the user interface offers no type.
if (count($entries) === 0 && !$implicit) {
$verlauf->nextRole = self::ROLE_ERSTANTRITT;
} else {
// a Pruefung exists: the next one is a repeat, also if no Pruefung is an Antritt yet
$verlauf->nextRole = $this->roleForAntritt(max(2, $antrittCount + 1));
}
// the next Antritt is kommissionell, and this tool may not create it
$verlauf->kommPruefLocked = $verlauf->canAdd
&& $verlauf->nextRole === self::ROLE_KOMMISSIONELL
&& !$this->canCreateKommPruef();
if ($verlauf->kommPruefLocked) $verlauf->canAdd = false;
$verlauf->earliestNewDay = $this->earliestNewDay($entries);
$verlauf->notenAtLimit = $this->notenAtLimit($entries);
return $verlauf;
}
/**
* @param mixed|null $lvNote see buildVerlauf
* @param mixed|null $zeugnisnote see buildVerlauf
* @return stdClass
*/
public function getVerlauf($student_uid, $lv_id, $sem_kurzbz, $lvNote, $zeugnisnote)
{
return $this->buildVerlauf($this->getPruefungen($student_uid, $lv_id, $sem_kurzbz), $lvNote, $zeugnisnote);
}
/** All Pruefungen of one student in one LV, in the order of the Verlauf. The StV calls it. @return array */
public function getPruefungen($student_uid, $lv_id, $sem_kurzbz)
{
$result = $this->_ci->LePruefungModel->getPruefungenByStudent($student_uid, $lv_id, $sem_kurzbz);
if (isError($result) || !hasData($result)) return [];
return $this->sortPruefungen(getData($result));
}
/**
* Checks a NEW Antritt: A the limit, B the order of the dates, C the occurrence limit, D the gap.
*
* @param stdClass $verlauf from buildVerlauf
* @return array|null [phraseKey, params] or null
*/
public function validateAdd($verlauf, $note, $datum)
{
if ($verlauf->angerechnet) return ['c4angerechnetKeinePruefung', []];
if ($verlauf->kommPruefLocked) return ['kommPruefNichtErlaubt', []];
// A: Antritt 1 only writes the LV-Note down, so it needs no free Antritt
if ($verlauf->nextRole !== self::ROLE_ERSTANTRITT && !$verlauf->canAdd) {
if ($verlauf->bestanden) return ['pruefungNachBestandenerNote', []];
return ['maxAntritteReached', [$verlauf->maxAntritte]];
}
// one Pruefung without a result per Verlauf; more of them break the order of the Antritte
if ($this->isOpen($note) && $this->hasOpenPruefung($verlauf->pruefungen)) return ['pruefungOhneErgebnis', []];
// B: no Pruefung before an existing one
$newDay = substr((string) $datum, 0, 10);
if ($verlauf->earliestNewDay !== null && $newDay < $verlauf->earliestNewDay) return ['pruefungDatumBeforeExisting', []];
// C: for example one 'entschuldigt' only
if ($this->exceedsNoteLimit($verlauf->pruefungen, $note)) return ['noteOccuranceLimitReached', []];
// D
return $this->checkAntrittGap($verlauf, $newDay);
}
/**
* Checks a CHANGED Pruefung. The date must stay between its neighbours, and a later Pruefung locks
* the Note.
*
* @param stdClass $verlauf from buildVerlauf
* @return array|null [phraseKey, params] or null
*/
public function validateEdit($verlauf, $pruefung_id, $note, $datum)
{
if ($verlauf->angerechnet) return ['c4angerechnetKeinePruefung', []];
// only a Pruefung of this student in this LV; the StV owns the types without an Antritt
$current = $this->findPruefung($verlauf->pruefungen, $pruefung_id);
if ($current === null || in_array($current->pruefungstyp_kurzbz, $this->getTypesWithoutAntritt())) {
return ['pruefungNichtBearbeitbar', []];
}
if ($this->isOpen($note) && $this->hasOpenPruefung($verlauf->pruefungen, $pruefung_id)) return ['pruefungOhneErgebnis', []];
if ($current->note_locked && $note != $current->note) return ['pruefungNoteLocked', []];
// the date must stay strictly between the dates of the neighbours
$currentDay = substr((string) $current->datum, 0, 10);
$newDay = substr((string) $datum, 0, 10);
$lower = null;
$upper = null;
foreach ($verlauf->pruefungen as $pruefung) {
$day = substr((string) $pruefung->datum, 0, 10);
if ($pruefung->pruefung_id == $current->pruefung_id || $day === '') continue;
if ($day < $currentDay && ($lower === null || $day > $lower)) $lower = $day;
if ($day > $currentDay && ($upper === null || $day < $upper)) $upper = $day;
}
if (($lower !== null && $newDay <= $lower) || ($upper !== null && $newDay >= $upper)) {
return ['pruefungDatumOutOfRange', []];
}
if ($this->exceedsNoteLimit($verlauf->pruefungen, $note, $current->pruefung_id)) {
return ['noteOccuranceLimitReached', []];
}
// only a new date: an old gap outside the rule must not block a Note correction
if ($newDay === $currentDay) return null;
return $this->checkAntrittGap($verlauf, $newDay, $current->pruefung_id);
}
/**
* The LV-Note after one Pruefung write: the Note of the last Antritt. Without an Antritt the
* implicit Antritt 1 stays, else nothing is entered. The LV-Note is never 'entschuldigt'.
*
* With CIS_GESAMTNOTE_VERBESSERUNG_BESSERE_GEWINNT a worse new Antritt keeps the old LV-Note.
*
* @param stdClass $verlauf from buildVerlauf, before this write
* @param mixed $pruefung_id set = the changed Pruefung, empty = a new Pruefung after all others
* @return array [note, punkte]
*/
public function deriveLvNote($verlauf, $pruefung_id, $note, $punkte, $lvNote, $lvPunkte)
{
$isNew = $pruefung_id === null || $pruefung_id === '';
$result = null;
foreach ($verlauf->pruefungen as $pruefung) {
$row = (!$isNew && $pruefung->pruefung_id == $pruefung_id) ? [$note, $punkte] : [$pruefung->note, $pruefung->punkte];
if ($this->isAntritt($row[0], $pruefung->pruefungstyp_kurzbz)) $result = $row;
}
if ($isNew && $this->isAntritt($note)) $result = [$note, $punkte];
if ($result === null) {
if ($verlauf->implicitErstantritt) return [$lvNote, $lvPunkte];
return [$this->getNoteNichtEingetragen(), null];
}
if ($result[0] == $note
&& $this->configBool('CIS_GESAMTNOTE_VERBESSERUNG_BESSERE_GEWINNT')
&& $this->isWorse($note, $lvNote)) {
return [$lvNote, $lvPunkte];
}
return $result;
}
/**
* Writes Antritt 1 as its own Pruefung. An LV-Note without a Pruefung IS Antritt 1. While that row
* is missing, the next Pruefung becomes Antritt 2 and the Pruefungstypen of the chain shift.
*
* Writes nothing once a repeat exists, and nothing into a Pruefung with a result of its own.
*
* @param bool $datumMode self::SET_DATUM or self::KEEP_DATUM for an existing Antritt 1
* @return stdClass|null the Pruefung, or null if nothing was written
*/
public function upsertErstantritt($student_uid, $lv_id, $sem_kurzbz, $note, $punkte, $datum, $datumMode, $mitarbeiter_uid, $lehreinheit_id)
{
$pruefungen = $this->getPruefungen($student_uid, $lv_id, $sem_kurzbz);
// hasRepeat reads only the Pruefungen
if ($this->buildVerlauf($pruefungen, null, null)->hasRepeat) return null;
$now = date('Y-m-d H:i:s');
$day = substr((string) $datum, 0, 10);
if (count($pruefungen) === 1) {
if (!$this->canOverwriteErstantritt($pruefungen[0])) return null;
$data = [
'note' => $note,
'punkte' => $punkte,
'updateamum' => $now,
'updatevon' => getAuthUID()
];
if ($datumMode === self::SET_DATUM) $data['datum'] = $day;
$this->_ci->LePruefungModel->update($pruefungen[0]->pruefung_id, $data);
return $this->loadPruefung($pruefungen[0]->pruefung_id);
}
if ($lehreinheit_id === null) return null;
$result = $this->_ci->LePruefungModel->insert([
'lehreinheit_id' => $lehreinheit_id,
'student_uid' => $student_uid,
'mitarbeiter_uid' => $mitarbeiter_uid,
'note' => $note,
'punkte' => $punkte,
'pruefungstyp_kurzbz' => $this->legacyTypFuerAntritt(1),
'datum' => $day,
'anmerkung' => '',
'insertamum' => $now,
'insertvon' => getAuthUID(),
'updateamum' => null,
'updatevon' => null,
'ext_id' => null
]);
return $result ? $this->loadPruefung($result->retval) : null;
}
/**
* The Pruefungstyp that the tool writes for an Antritt. The StV still reads that column and calls
* this method. A type that tbl_pruefungstyp does not have breaks the foreign key, so step down.
*
* @return string
*/
public function legacyTypFuerAntritt($antrittNr)
{
$antrittNr = max(1, (int) $antrittNr);
$types = $this->getTypes();
$kommissionell = $this->_ci->config->item('PRUEFUNG_TYP_KOMMISSIONELL');
if ($this->roleForAntritt($antrittNr) === self::ROLE_KOMMISSIONELL && isset($types[$kommissionell])) {
return $kommissionell;
}
$typePerAntritt = $this->configArray('PRUEFUNG_TYP_JE_ANTRITT');
for ($nr = $antrittNr; $nr >= 1; $nr--) {
if (isset($typePerAntritt[$nr]) && isset($types[$typePerAntritt[$nr]])) return $typePerAntritt[$nr];
}
return $typePerAntritt[1];
}
/** The Pruefungstyp of a new repeat; never below Antritt 2. @return string */
public function legacyTypeForRepeat($verlauf)
{
return $this->legacyTypFuerAntritt(max(2, $verlauf->antrittCount + 1));
}
/** Antritte, Antritt 1 and the kommissionelle Pruefung included. @return int */
public function getMaxAntritte()
{
$max = $this->_ci->config->item('CIS_GESAMTNOTE_MAX_ANTRITTE');
if ($max !== null) return (int) $max;
// the flags of the old tool: Antritt 1 plus one Antritt per flag
return 1 + (int) CIS_GESAMTNOTE_PRUEFUNG_TERMIN2 + (int) CIS_GESAMTNOTE_PRUEFUNG_TERMIN3 + (int) CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF;
}
/** The Antritt from which a Pruefung is kommissionell, or null. @return int|null */
public function getKommissionellFromAntritt()
{
$value = $this->_ci->config->item('CIS_GESAMTNOTE_KOMMISSIONELL_AB_ANTRITT');
if ($value === 'letzter') {
$max = $this->getMaxAntritte();
// a chain of one has nothing to close
return $max > 1 ? $max : null;
}
return (int) $value >= 1 ? (int) $value : null;
}
/** May this tool create the kommissionelle Pruefung? It always shows an existing one. @return bool */
public function canCreateKommPruef()
{
return $this->configBool('CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF');
}
/** May a student take another Antritt after a positive Note? @return bool */
public function allowsImprovement()
{
return $this->configBool('CIS_GESAMTNOTE_NOTENVERBESSERUNG');
}
/** @return array the Pruefungstypen that never use an Antritt */
public function getTypesWithoutAntritt()
{
return $this->configArray('PRUEFUNG_TYPEN_OHNE_ANTRITT');
}
/**
* The special Noten. The configuration names them, lehre.tbl_note gives their keys: a key means
* something else in every installation. An unknown name is dropped.
*
* @return array
*/
public function getSpecialNotes()
{
if ($this->_specialNotes !== null) return $this->_specialNotes;
$entschuldigt = $this->getNoteByBezeichnung($this->_ci->config->item('NOTE_ENTSCHULDIGT_BEZEICHNUNG'));
$ohneAntritt = $this->resolveNoten('NOTEN_OHNE_ANTRITT_BEZEICHNUNGEN');
if ($entschuldigt !== null && !in_array($entschuldigt, $ohneAntritt)) $ohneAntritt[] = $entschuldigt;
// the configuration names a Note, the rules work with its key
$limitMap = [];
foreach ($this->configArray('NOTEN_OCCURRENCE_LIMIT_MAP') as $bezeichnung => $limit) {
$note = $this->getNoteByBezeichnung($bezeichnung);
if ($note !== null) $limitMap[$note] = $limit;
}
$this->_specialNotes = [
'entschuldigt' => $entschuldigt,
'ohneAntritt' => array_values($ohneAntritt),
'anrechnung' => $this->resolveNoten('NOTEN_ANRECHNUNG_BEZEICHNUNGEN'),
'abschliessend' => $this->resolveNoten('NOTEN_ABSCHLIESSEND_BEZEICHNUNGEN'),
'nichtEingetragen' => $this->getNoteByBezeichnung($this->_ci->config->item('NOTE_NICHT_EINGETRAGEN_BEZEICHNUNG')),
// best first
'rangfolge' => $this->resolveNoten('NOTEN_RANGFOLGE_BEZEICHNUNGEN'),
'limitMap' => $limitMap
];
return $this->_specialNotes;
}
/** The Note of a Pruefung without a result ('Noch nicht eingetragen'). @return mixed|null */
public function getNoteNichtEingetragen()
{
return $this->getSpecialNotes()['nichtEingetragen'];
}
/** 'angerechnet' or 'intern angerechnet'. Ask it about the Zeugnisnote. @return bool */
public function isAnrechnungNote($note)
{
if ($note === null || $note === '') return false;
return in_array($note, $this->getSpecialNotes()['anrechnung']);
}
/** Would one more $note cross NOTEN_OCCURRENCE_LIMIT_MAP? $pruefung_id skips the changed row. @return bool */
public function exceedsNoteLimit($pruefungen, $note, $pruefung_id = null)
{
$limit = null;
foreach ($this->getSpecialNotes()['limitMap'] as $limitNote => $value) {
if ($limitNote == $note) $limit = $value;
}
if ($limit === null) return false;
$count = 0;
foreach ($pruefungen as $pruefung) {
if ($pruefung_id !== null && $pruefung->pruefung_id == $pruefung_id) continue;
if ($pruefung->note == $note) $count++;
}
return $count + 1 > $limit;
}
// --- private --------------------------------------------------------------------------------
/** The role of one Antritt: 1 = erstantritt, later = pruefung, from the commission on = kommissionell. @return string */
private function roleForAntritt($antrittNr)
{
$nr = max(1, (int) $antrittNr);
$fromAntritt = $this->getKommissionellFromAntritt();
if ($fromAntritt !== null && $nr >= $fromAntritt) return self::ROLE_KOMMISSIONELL;
return $nr === 1 ? self::ROLE_ERSTANTRITT : self::ROLE_PRUEFUNG;
}
/**
* Does this Note use an Antritt? The Note decides; the type only through PRUEFUNG_TYPEN_OHNE_ANTRITT.
*
* @return bool
*/
private function isAntritt($note, $type = null)
{
if ($type !== null && in_array($type, $this->getTypesWithoutAntritt())) return false;
if ($note === null || $note === '') return false;
if (in_array($note, $this->getSpecialNotes()['ohneAntritt'])) return false;
// an Anrechnung is no assessment
if ($this->isAnrechnungNote($note)) return false;
$noteRow = $this->getNote($note);
return $noteRow ? (bool) $noteRow->lehre : false;
}
/** Is this Pruefungstyp kommissionell? @return bool */
private function isKommissionell($type)
{
return in_array($type, $this->configArray('PRUEFUNG_KOMMISSIONELL_TYPEN'));
}
/** Is the new Note worse? A Note outside NOTEN_RANGFOLGE_BEZEICHNUNGEN is never worse. @return bool */
private function isWorse($newNote, $oldNote)
{
$rangfolge = $this->getSpecialNotes()['rangfolge'];
$newRank = array_search($newNote, $rangfolge);
$oldRank = array_search($oldNote, $rangfolge);
if ($newRank === false || $oldRank === false) return false;
return $newRank > $oldRank;
}
/** The order of the Verlauf: datum, then tbl_pruefungstyp.sort (old rows without a date), then pruefung_id. */
private function sortPruefungen($pruefungen)
{
$pruefungen = array_values($pruefungen);
$types = $this->getTypes();
usort($pruefungen, function ($a, $b) use ($types) {
$dayA = substr((string) $a->datum, 0, 10);
$dayB = substr((string) $b->datum, 0, 10);
if ($dayA !== $dayB) {
if ($dayA === '') return -1;
if ($dayB === '') return 1;
return ($dayA < $dayB) ? -1 : 1;
}
$sortA = isset($types[$a->pruefungstyp_kurzbz]) ? (int) $types[$a->pruefungstyp_kurzbz]->sort : 0;
$sortB = isset($types[$b->pruefungstyp_kurzbz]) ? (int) $types[$b->pruefungstyp_kurzbz]->sort : 0;
if ($sortA !== $sortB) return ($sortA < $sortB) ? -1 : 1;
return ((int) $a->pruefung_id < (int) $b->pruefung_id) ? -1 : 1;
});
return $pruefungen;
}
/**
* May the LV-Note go into this Pruefung? Antritt 1 and the LV-Note are the same result, and an
* empty Pruefung waits for its result. A Pruefung with a result of its own ('entschuldigt',
* 'Nicht beurteilt') keeps it: the LV-Note then stays the implicit Antritt 1.
*
* @return bool
*/
private function canOverwriteErstantritt($pruefung)
{
if (in_array($pruefung->pruefungstyp_kurzbz, $this->getTypesWithoutAntritt())) return false;
if ($this->isAntritt($pruefung->note, $pruefung->pruefungstyp_kurzbz)) return true;
return $this->isOpen($pruefung->note);
}
/**
* The first day for a new Pruefung: it comes after all others. CIS_GESAMTNOTE_PRUEFUNG_GLEICHER_TAG
* allows the day of the last one.
*
* @return string|null Y-m-d, or null without a dated Pruefung
*/
private function earliestNewDay($entries)
{
$lastDay = null;
foreach ($entries as $entry) {
$day = substr((string) $entry->datum, 0, 10);
if ($day !== '' && ($lastDay === null || $day > $lastDay)) $lastDay = $day;
}
if ($lastDay === null || $this->configBool('CIS_GESAMTNOTE_PRUEFUNG_GLEICHER_TAG')) return $lastDay;
return date('Y-m-d', strtotime($lastDay . ' +1 day'));
}
/** The Noten that NOTEN_OCCURRENCE_LIMIT_MAP allows no more time in this Verlauf. @return array Note => limit */
private function notenAtLimit($entries)
{
$atLimit = [];
foreach ($this->getSpecialNotes()['limitMap'] as $limitNote => $limit) {
$count = 0;
foreach ($entries as $entry) {
if ($entry->note == $limitNote) $count++;
}
if ($count >= $limit) $atLimit[$limitNote] = $limit;
}
return $atLimit;
}
/**
* The gaps between a Pruefung on $newDay and the Antritte next to it: the last one on or before
* the day, and the first one after it. A new Pruefung has no Antritt after it. A Pruefung without
* an Antritt does not count: an entschuldigt date must not push the next Antritt away.
*
* @param mixed $pruefung_id the changed Pruefung, which is no neighbour of itself; null for a new one
* @return array|null [phraseKey, params] or null
*/
private function checkAntrittGap($verlauf, $newDay, $pruefung_id = null)
{
$min = $this->_ci->config->item('CIS_GESAMTNOTE_ANTRITT_MIN_ABSTAND_TAGE');
$max = $this->_ci->config->item('CIS_GESAMTNOTE_ANTRITT_MAX_ABSTAND_TAGE');
if (($min === null && $max === null) || $newDay === '') return null;
$before = null;
$after = null;
foreach ($verlauf->pruefungen as $pruefung) {
$day = substr((string) $pruefung->datum, 0, 10);
if (!$pruefung->is_antritt || $day === '' || ($pruefung_id !== null && $pruefung->pruefung_id == $pruefung_id)) continue;
if ($day <= $newDay && ($before === null || $day > $before)) $before = $day;
if ($day > $newDay && ($after === null || $day < $after)) $after = $day;
}
foreach ([[$before, $newDay], [$newDay, $after]] as list($from, $to)) {
$gap = ($from !== null && $to !== null) ? $this->daysBetween($from, $to) : null;
if ($gap === null) continue;
if ($min !== null && $gap < (int) $min) return ['pruefungAbstandZuKurz', [(int) $min]];
if ($max !== null && $gap > (int) $max) return ['pruefungAbstandZuLang', [(int) $max]];
}
return null;
}
/** @return array the list under $key, or an empty list */
private function configArray($key)
{
$value = $this->_ci->config->item($key);
return is_array($value) ? $value : [];
}
/** @return bool */
private function configBool($key)
{
return (bool) $this->_ci->config->item($key);
}
/** Whole days from $from to $to (Y-m-d). @return int|null */
private function daysBetween($from, $to)
{
$a = DateTime::createFromFormat('Y-m-d|', $from);
$b = DateTime::createFromFormat('Y-m-d|', $to);
if ($a === false || $b === false) return null;
$diff = $a->diff($b);
return $diff->invert ? -((int) $diff->days) : (int) $diff->days;
}
/** @return stdClass|null */
private function findPruefung($pruefungen, $pruefung_id)
{
foreach ($pruefungen as $pruefung) {
if ($pruefung->pruefung_id == $pruefung_id) return $pruefung;
}
return null;
}
/** @return stdClass|null the tbl_note row */
private function getNote($note)
{
if (!array_key_exists($note, $this->_noteCache)) {
$result = $this->_ci->NoteModel->load($note);
$this->_noteCache[$note] = (!isError($result) && hasData($result)) ? getData($result)[0] : null;
}
return $this->_noteCache[$note];
}
/** @return mixed|null the key of the Note, or null if no Note has this Bezeichnung */
private function getNoteByBezeichnung($bezeichnung)
{
if (!is_string($bezeichnung) || trim($bezeichnung) === '') return null;
$result = $this->_ci->NoteModel->loadWhere(['bezeichnung' => $bezeichnung]);
return (!isError($result) && hasData($result)) ? getData($result)[0]->note : null;
}
/** lehre.tbl_pruefungstyp by pruefungstyp_kurzbz. @return array */
private function getTypes()
{
if ($this->_types !== null) return $this->_types;
$this->_types = [];
$result = $this->_ci->PruefungstypModel->load();
if (!isError($result) && hasData($result)) {
foreach (getData($result) as $type) $this->_types[$type->pruefungstyp_kurzbz] = $type;
}
return $this->_types;
}
/** Does a Pruefung other than $pruefung_id still wait for its result? @return bool */
private function hasOpenPruefung($pruefungen, $pruefung_id = null)
{
foreach ($pruefungen as $pruefung) {
if ($pruefung->pruefung_id != $pruefung_id && $this->isOpen($pruefung->note)) return true;
}
return false;
}
/** A Note with no better one ends the chain for good. @return bool */
private function isClosingNote($note)
{
if ($note === null || $note === '') return false;
return in_array($note, $this->getSpecialNotes()['abschliessend']);
}
/** 'Noch nicht eingetragen': the Pruefung waits for its result. @return bool */
private function isOpen($note)
{
$open = $this->getNoteNichtEingetragen();
return $open !== null && $note !== null && $note !== '' && $note == $open;
}
/**
* Is this Note a pass? tbl_note.positiv is also true for 'entschuldigt' and 'Teilgenommen', so ask
* it only about a Note that uses an Antritt.
*
* @return bool
*/
private function isPositiveNote($note)
{
if ($note === null || $note === '') return false;
$noteRow = $this->getNote($note);
return $noteRow ? (bool) $noteRow->positiv : false;
}
/** @return stdClass|null */
private function loadPruefung($pruefung_id)
{
$result = $this->_ci->LePruefungModel->load($pruefung_id);
return (!isError($result) && hasData($result)) ? getData($result)[0] : null;
}
/** The keys of the Noten that one configuration key names. @return array */
private function resolveNoten($key)
{
$noten = [];
foreach ($this->configArray($key) as $bezeichnung) {
$note = $this->getNoteByBezeichnung($bezeichnung);
if ($note !== null && !in_array($note, $noten)) $noten[] = $note;
}
return $noten;
}
}
+24 -19
View File
@@ -31,18 +31,20 @@ class Vertrag_model extends DB_Model
*/
public function save($person_id, $mitarbeiter_uid, $lehrveranstaltung_id, $lehreinheit_id, $projektarbeit_id = null, $vertragsstunden, $betrag, $studiensemester_kurzbz)
{
$person_id = (isset($person_id) && is_numeric($person_id))
$vertragstyp_kurzbz = (is_null($projektarbeit_id)) ? 'Lehrauftrag' : 'Betreuung';
$person_id = (isset($person_id) && is_numeric($person_id))
? $person_id
: show_error('peron_id must be set and numeric.');
$lehreinheit_id = (isset($lehreinheit_id) && is_numeric($lehreinheit_id))
? $lehreinheit_id
: show_error('lehreinheit_id must be set and numeric.');
$lehrveranstaltung_id = (isset($lehrveranstaltung_id) && is_numeric($lehrveranstaltung_id))
? $lehrveranstaltung_id
: show_error('lehrveranstaltung_id must be set and numeric.');
$projektarbeit_id = (isset($projektarbeit_id) && is_numeric($projektarbeit_id))
? $projektarbeit_id
: null;
$lehreinheit_id = (isset($lehreinheit_id) && is_numeric($lehreinheit_id))
? $lehreinheit_id
: (($vertragstyp_kurzbz == 'Betreuung') ? null : show_error('lehreinheit_id must be set and numeric.'));
$vertragsstunden = (isset($vertragsstunden) && is_numeric($vertragsstunden))
? $vertragsstunden
: 0;
@@ -53,7 +55,7 @@ class Vertrag_model extends DB_Model
? $mitarbeiter_uid
: show_error('mitarbeiter_uid must be set and a string value.');;
$vertragstyp_kurzbz = (is_null($projektarbeit_id)) ? 'Lehrauftrag' : 'Betreuung';
// First check if Vertrag already exists for that Lehrauftrag or for that Projektbetreuerauftrag
if ($vertragstyp_kurzbz == 'Lehrauftrag')
@@ -257,7 +259,7 @@ class Vertrag_model extends DB_Model
}
elseif ($vertragstyp_kurzbz == 'Betreuung')
{
$this->addSelect('lehreinheit_id');
$this->addSelect('lehreinheit_id, lehre.tbl_projektarbeit.lehrveranstaltung_id, lehre.tbl_projektarbeit.studiensemester_kurzbz');
$this->addJoin('lehre.tbl_projektbetreuer', 'vertrag_id');
$this->addJoin('lehre.tbl_projektarbeit', 'projektarbeit_id');
if ($result = $this->loadWhere(array('vertrag_id' => $vertrag_id)))
@@ -281,17 +283,22 @@ class Vertrag_model extends DB_Model
{
if ($result = getData($this->getLehreinheitID($vertrag_id)))
{
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$this->LehreinheitModel->addSelect($select);
if (!empty($result[0]->lehreinheit_id))
{
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$this->LehreinheitModel->addSelect($select);
if($result = $this->LehreinheitModel->load($result[0]->lehreinheit_id))
{
return success($result->retval);
}
else
{
return error('Fehler beim Laden der Lehreinheit');
if ($result = $this->LehreinheitModel->load($result[0]->lehreinheit_id))
{
return success($result->retval);
}
else
{
return error('Fehler beim Laden der Lehreinheit');
}
}
return success($result);
}
else
{
@@ -563,14 +570,13 @@ SELECT
null::integer as pruefung_id,
projektarbeit_id,
(tbl_projektbetreuer.stunden*tbl_projektbetreuer.stundensatz) as betrag1,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
tbl_projektbetreuer.betreuerart_kurzbz,
(SELECT nachname || ' ' || vorname FROM public.tbl_person JOIN public.tbl_benutzer USING(person_id) WHERE uid=tbl_projektarbeit.student_uid)
as bezeichnung
FROM
lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
tbl_projektbetreuer.person_id=?
AND vertrag_id IS NULL
@@ -619,14 +625,13 @@ SELECT
null::integer as pruefung_id,
projektarbeit_id,
(tbl_projektbetreuer.stunden * tbl_projektbetreuer.stundensatz) as betrag,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
tbl_projektbetreuer.betreuerart_kurzbz,
(SELECT nachname || ' ' || vorname FROM public.tbl_person JOIN public.tbl_benutzer USING(person_id) WHERE uid=tbl_projektarbeit.student_uid)
as bezeichnung, vertrag_id
FROM
lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
tbl_projektbetreuer.person_id=?
AND vertrag_id = ?
@@ -11,4 +11,73 @@ class Mobilitaet_model extends DB_Model
$this->dbTable = 'bis.tbl_mobilitaet';
$this->pk = 'mobilitaet_id';
}
public function getMobilityZusatzForUids($uids) {
$qry = "SELECT distinct on(nachname, vorname, public.tbl_benutzer.person_id) uid,
tbl_mitarbeiter.mitarbeiter_uid,
tbl_note.lkt_ueberschreibbar, tbl_note.anmerkung,
tbl_mobilitaet.mobilitaetstyp_kurzbz,
(CASE WHEN bis.tbl_mobilitaet.studiensemester_kurzbz = vw_student_lehrveranstaltung.studiensemester_kurzbz THEN 1 ELSE 0 END) as doubledegree,
public.tbl_prestudent.gsstudientyp_kurzbz as ddtype,
(SELECT status_kurzbz FROM public.tbl_prestudentstatus
WHERE prestudent_id=tbl_student.prestudent_id
ORDER BY datum DESC, insertamum DESC, ext_id DESC LIMIT 1) as studienstatus
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
JOIN public.tbl_person USING(person_id)
LEFT JOIN public.tbl_student ON(uid=student_uid)
LEFT JOIN public.tbl_mitarbeiter ON(uid=mitarbeiter_uid)
LEFT JOIN public.tbl_studentlehrverband USING(student_uid,studiensemester_kurzbz)
LEFT JOIN lehre.tbl_zeugnisnote on(vw_student_lehrveranstaltung.lehrveranstaltung_id=tbl_zeugnisnote.lehrveranstaltung_id
AND tbl_zeugnisnote.student_uid=tbl_student.student_uid
AND tbl_zeugnisnote.studiensemester_kurzbz=tbl_studentlehrverband.studiensemester_kurzbz)
LEFT JOIN lehre.tbl_note USING (note)
LEFT JOIN bis.tbl_bisio ON(uid=tbl_bisio.student_uid)
LEFT JOIN public.tbl_studiengang ON(tbl_student.studiengang_kz=tbl_studiengang.studiengang_kz)
LEFT JOIN bis.tbl_mobilitaet USING(prestudent_id)
LEFT JOIN public.tbl_prestudent USING(prestudent_id)
WHERE uid IN ?";
return $this->execReadOnlyQuery($qry, [$uids]);
}
public function formatZusatz($entry, $erhalter_kz) {
$zusatz = '';
if (isset($entry->studienstatus) && $entry->studienstatus === 'Incoming') {
$zusatz = '(i)';
}
if (isset($entry->lkt_ueberschreibbar) && $entry->lkt_ueberschreibbar === false) {
$zusatz .= ' (' . ($entry->anmerkung ?? '') . ')';
}
if (isset($entry->mitarbeiter_uid) && $entry->mitarbeiter_uid !== null) {
$zusatz .= ' (ma)';
}
if (isset($entry->stg_kz_student) && $entry->stg_kz_student == $erhalter_kz) {
$zusatz .= ' (a.o.)';
}
if (
isset($entry->mobilitaetstyp_kurzbz) && $entry->mobilitaetstyp_kurzbz &&
isset($entry->doubledegree) && $entry->doubledegree === 1
) {
$zusatz .= ' (d.d.';
$ddtype = $entry->ddtype ?? null;
if ($ddtype == 'Intern') {
$zusatz .= 'int.)';
} elseif ($ddtype == 'Extern') {
$zusatz .= 'ext.)';
} else {
$zusatz .= ')';
}
}
return $zusatz;
}
}
@@ -177,7 +177,7 @@ class Prestudentstatus_model extends DB_Model
* @param prestudent_id ID of the Prestudent.
* @param $status_kurzbz kurzbz of the status.
* @param $ausbildungssemester ausbildungssemester of the status.
* @param $studiensemester_kurzbz studiensemster of the status.
* @param $studiensemester_kurzbz studiensemester of the status.
*
* @return result object with all the status entries
*/
@@ -52,4 +52,47 @@ class LePruefung_model extends DB_Model
'student_uid' => $student_uid
]);
}
/**
* All Pruefungen of one LV in one Studiensemester.
*
* Note and Pruefungstyp are LEFT JOINs: a Pruefung with a type that tbl_pruefungstyp does not know
* stays in the Verlauf. Else the rules count one Antritt too few.
*
* No ORDER BY: PruefungsverlaufLib sorts the rows into the order of the Verlauf.
*/
public function getPruefungenByLvStudiensemester($lv_id, $sem_kurzbz) {
$qry = "SELECT tbl_pruefung.*, tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_note.bezeichnung as note_bezeichnung, tbl_pruefungstyp.beschreibung as typ_beschreibung, tbl_lehreinheit.studiensemester_kurzbz as studiensemester_kurzbz
FROM lehre.tbl_pruefung
JOIN lehre.tbl_lehreinheit ON (tbl_pruefung.lehreinheit_id = tbl_lehreinheit.lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung ON (tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id)
LEFT JOIN lehre.tbl_note ON (tbl_pruefung.note = tbl_note.note)
LEFT JOIN lehre.tbl_pruefungstyp ON (tbl_pruefung.pruefungstyp_kurzbz = tbl_pruefungstyp.pruefungstyp_kurzbz)
WHERE tbl_lehrveranstaltung.lehrveranstaltung_id = ?
AND tbl_lehreinheit.studiensemester_kurzbz = ?";
return $this->execReadOnlyQuery($qry, array($lv_id, $sem_kurzbz));
}
/**
* All Pruefungen of one student in one LV.
*
* Note and Pruefungstyp are LEFT JOINs. PruefungsverlaufLib derives the Antritt numbers from this
* list, and a missing row shifts the whole chain.
*/
public function getPruefungenByStudent($student_uid, $lv_id, $sem_kurzbz) {
$qry = "SELECT tbl_pruefung.*, tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_note.bezeichnung as note_bezeichnung, tbl_pruefungstyp.beschreibung as typ_beschreibung, tbl_lehreinheit.studiensemester_kurzbz as studiensemester_kurzbz
FROM lehre.tbl_pruefung
JOIN lehre.tbl_lehreinheit ON (tbl_pruefung.lehreinheit_id = tbl_lehreinheit.lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung ON (tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id)
LEFT JOIN lehre.tbl_note ON (tbl_pruefung.note = tbl_note.note)
LEFT JOIN lehre.tbl_pruefungstyp ON (tbl_pruefung.pruefungstyp_kurzbz = tbl_pruefungstyp.pruefungstyp_kurzbz)
WHERE tbl_pruefung.student_uid = ?
AND tbl_lehrveranstaltung.lehrveranstaltung_id = ?
AND tbl_lehreinheit.studiensemester_kurzbz = ?";
return $this->execReadOnlyQuery($qry, array($student_uid, $lv_id, $sem_kurzbz));
}
}
@@ -741,4 +741,28 @@ EOSQL;
)";
}
public function getLehreinheitenForLv($lv_id, $sem_kurzbz)
{
// every Lehreinheit of the LV; the caller checks the access
$query = "SELECT DISTINCT tbl_lehreinheit.lehreinheit_id, tbl_lehreinheit.lehrveranstaltung_id, tbl_lehreinheit.lehrform_kurzbz,
tbl_gruppe.direktinskription,
tbl_lehreinheitgruppe.semester,
tbl_lehreinheitgruppe.verband,
tbl_lehreinheitgruppe.gruppe,
tbl_lehreinheitgruppe.gruppe_kurzbz,
tbl_lehrveranstaltung.kurzbz,
tbl_studiengang.kurzbzlang,
(SELECT COUNT(DISTINCT datum) FROM campus.vw_stundenplan WHERE lehreinheit_id = lehre.tbl_lehreinheit.lehreinheit_id) as termincount,
(SELECT COUNT(*) FROM campus.vw_student_lehrveranstaltung WHERE lehreinheit_id = lehre.tbl_lehreinheit.lehreinheit_id) as studentcount
FROM lehre.tbl_lehreinheit
JOIN lehre.tbl_lehreinheitgruppe USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang ON (tbl_lehreinheitgruppe.studiengang_kz = tbl_studiengang.studiengang_kz)
LEFT JOIN public.tbl_gruppe ON (tbl_gruppe.gruppe_kurzbz = tbl_lehreinheitgruppe.gruppe_kurzbz)
WHERE lehrveranstaltung_id = ? AND tbl_lehreinheit.studiensemester_kurzbz = ?
ORDER BY tbl_lehreinheitgruppe.gruppe_kurzbz";
return $this->execReadOnlyQuery($query, [$lv_id, $sem_kurzbz]);
}
}
@@ -85,9 +85,18 @@ class Lehreinheitmitarbeiter_model extends DB_Model
public function getLektorenByLe($lehreinheit_id)
{
$this->addSelect('vorname, nachname, tbl_lehreinheitmitarbeiter.*, stundenplan.verplant');
$this->addSelect('tbl_person.vorname, tbl_person.nachname, tbl_lehreinheitmitarbeiter.*, stundenplan.verplant,
COALESCE(insertvonperson.vorname || \' \' || insertvonperson.nachname, tbl_lehreinheitmitarbeiter.insertvon) as insertvon,
COALESCE(updatevonperson.vorname || \' \' || updatevonperson.nachname, tbl_lehreinheitmitarbeiter.updatevon) as updatevon');
$this->addJoin('tbl_benutzer', 'uid = mitarbeiter_uid');
$this->addJoin('tbl_person', 'person_id');
$this->addJoin('tbl_benutzer insertvonbenutzer', 'insertvonbenutzer.uid = tbl_lehreinheitmitarbeiter.insertvon', 'LEFT');
$this->addJoin('tbl_person insertvonperson', 'insertvonperson.person_id = insertvonbenutzer.person_id', 'LEFT');
$this->addJoin('tbl_benutzer updatevonbenutzer', 'updatevonbenutzer.uid = tbl_lehreinheitmitarbeiter.updatevon', 'LEFT');
$this->addJoin('tbl_person updatevonperson', 'updatevonperson.person_id = updatevonbenutzer.person_id', 'LEFT');
$this->addJoin('(
SELECT 1 as verplant, lehreinheit_id, mitarbeiter_uid
@@ -317,7 +317,7 @@ class Lehrveranstaltung_model extends DB_Model
tbl_bisio.bisio_id, tbl_bisio.von, tbl_bisio.bis, tbl_student.studiengang_kz AS stg_kz_student,
tbl_zeugnisnote.note, tbl_mitarbeiter.mitarbeiter_uid, tbl_person.matr_nr, tbl_benutzer.uid,
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as kuerzel, tbl_studiengang.orgform_kurzbz, vw_student_lehrveranstaltung.semester, vw_student_lehrveranstaltung.studiensemester_kurzbz, vw_student_lehrveranstaltung.bezeichnung,
tbl_student.prestudent_id
tbl_student.prestudent_id, campus.vw_student_lehrveranstaltung.lehreinheit_id
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
@@ -685,10 +685,22 @@ class Lehrveranstaltung_model extends DB_Model
]);
$this->dbTable = $tmp;
return $result;
}
/**
* All Lehreinheiten of a student in a Lehrveranstaltung, lowest id first.
*/
public function getLeIdsByStudent($student_uid, $studiensemester_kurzbz, $lehrveranstaltung_id)
{
$qry = "SELECT DISTINCT lehreinheit_id FROM campus.vw_student_lehrveranstaltung
WHERE uid = ? AND lehrveranstaltung_id = ? AND studiensemester_kurzbz = ?
ORDER BY lehreinheit_id";
return $this->execReadOnlyQuery($qry, array($student_uid, $lehrveranstaltung_id, $studiensemester_kurzbz));
}
/**
* Sucht nach LV Templates und gibt Id und Label ("bezeichnung [kurzbz]") aus
* Diese funktion ist für autocomplete gedacht
@@ -1263,6 +1275,39 @@ class Lehrveranstaltung_model extends DB_Model
";
}
/**
* Gets lehrveranstaltungen of Studienplan of a certain lva_id set
* used if a bigger set of lva id is already available and it needs to be checked against a certain studienplan aswell
* @param $studienplan_id ID des Studienplans
* @param $lva_ids LVA_ID welche abgegleicht werden sollen
* @return array|null
*/
public function getLvsByStudienplanByLvaIDs($studienplan_id, $lva_ids)
{
$params = array($studienplan_id, $lva_ids);
$qry = "SELECT tbl_lehrveranstaltung.*,
tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id,
tbl_studienplan_lehrveranstaltung.semester as stpllv_semester,
tbl_studienplan_lehrveranstaltung.pflicht as stpllv_pflicht,
tbl_studienplan_lehrveranstaltung.koordinator as stpllv_koordinator,
tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id_parent,
tbl_studienplan_lehrveranstaltung.sort stpllv_sort,
tbl_studienplan_lehrveranstaltung.curriculum,
tbl_studienplan_lehrveranstaltung.export,
tbl_studienplan_lehrveranstaltung.genehmigung
FROM lehre.tbl_lehrveranstaltung
JOIN lehre.tbl_studienplan_lehrveranstaltung
USING(lehrveranstaltung_id)
WHERE tbl_studienplan_lehrveranstaltung.studienplan_id = ?
AND tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id IN ?
";
$qry .= " ORDER BY stpllv_sort, semester, sort";
return $this->execQuery($qry, $params);
}
public function getAllOe($lv_id)
{
$qry = "SELECT DISTINCT oe_kurzbz
@@ -1346,4 +1391,73 @@ class Lehrveranstaltung_model extends DB_Model
return $this->execQuery($qry, $params);
}
public function getLvForLektorInSemester($sem_kurzbz, $uid) {
$qry = "SELECT DISTINCT (tbl_lehrveranstaltung.lehrveranstaltung_id),
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as stg_kurzbz,
tbl_studiengang.studiengang_kz,
tbl_lehrveranstaltung.semester as lv_semester,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
(SELECT kurzbz FROM public.tbl_mitarbeiter
WHERE mitarbeiter_uid=tbl_lehreinheitmitarbeiter.mitarbeiter_uid) as lektor,
lehre.tbl_lehrveranstaltung.orgform_kurzbz as orgform
FROM
lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang USING(studiengang_kz)
JOIN lehre.tbl_lehrveranstaltung as lehrfach ON(tbl_lehreinheit.lehrfach_id=lehrfach.lehrveranstaltung_id)
WHERE
tbl_lehreinheit.studiensemester_kurzbz = ?
AND mitarbeiter_uid = ?
ORDER BY stg_kurzbz,lv_semester,lv_bezeichnung";
return $this->execReadOnlyQuery($qry, array($sem_kurzbz, $uid));
}
/**
* All Lehrveranstaltungen of a Studiengang offered in a Studiensemester.
*/
public function getLvForStudiengangInSemester($sem_kurzbz, $studiengang_kz) {
$qry = "SELECT DISTINCT (tbl_lehrveranstaltung.lehrveranstaltung_id),
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as stg_kurzbz,
tbl_studiengang.studiengang_kz,
tbl_lehrveranstaltung.semester as lv_semester,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
tbl_lehrveranstaltung.orgform_kurzbz as orgform
FROM
lehre.tbl_lehrveranstaltung JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE
tbl_lehrveranstaltung.studiengang_kz = ?
AND tbl_lehrveranstaltung.aktiv = TRUE
AND EXISTS (
SELECT 1 FROM lehre.tbl_lehreinheit le
WHERE le.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
AND le.studiensemester_kurzbz = ?
)
ORDER BY stg_kurzbz,lv_semester,lv_bezeichnung";
return $this->execReadOnlyQuery($qry, array($studiengang_kz, $sem_kurzbz));
}
/**
* Number of Lehreinheit assignments the given mitarbeiter has for a Lehrveranstaltung (i.e. whether
* they teach it), optionally restricted to a Studiensemester. Returns a single row with a `teaches`
* count - check `teaches > 0`. Used to scope teacher access in the Benotungstool to their own LVs.
*/
public function getLektorIsTeachingLva($lva_id, $ma_uid, $studiensemester_kurzbz = null)
{
$params = array($lva_id, $ma_uid);
$query = "SELECT COUNT(*) AS teaches
FROM lehre.tbl_lehreinheitmitarbeiter
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
WHERE lehrveranstaltung_id = ? AND mitarbeiter_uid = ?";
if ($studiensemester_kurzbz !== null && $studiensemester_kurzbz !== '') {
$query .= " AND tbl_lehreinheit.studiensemester_kurzbz = ?";
$params[] = $studiensemester_kurzbz;
}
return $this->execReadOnlyQuery($query, $params);
}
}
@@ -50,4 +50,27 @@ class Lvgesamtnote_model extends DB_Model
return $this->loadWhere($where);
}
/**
* The LV-Note of one student, without the Freigabe filter of getLvGesamtNoten().
*/
public function getByStudent($lehrveranstaltung_id, $student_uid, $studiensemester_kurzbz)
{
return $this->loadWhere(array(
'student_uid' => $student_uid,
'lehrveranstaltung_id' => $lehrveranstaltung_id,
'studiensemester_kurzbz' => $studiensemester_kurzbz
));
}
/**
* All LV-Noten of one LV in one Studiensemester, without the Freigabe filter.
*/
public function getByLvStudiensemester($lehrveranstaltung_id, $studiensemester_kurzbz)
{
return $this->loadWhere(array(
'lehrveranstaltung_id' => $lehrveranstaltung_id,
'studiensemester_kurzbz' => $studiensemester_kurzbz
));
}
}
+22 -3
View File
@@ -11,12 +11,31 @@ class Note_model extends DB_Model
$this->dbTable = 'lehre.tbl_note';
$this->pk = 'note';
}
public function getAllActive() {
/**
* Sorted like the Notenliste of the StV: the scale 1-5 by its value first, every other Note
* alphabetically after it.
*
* @param string|null $sortOrder 'skala' or 'bezeichnung'. The caller holds the configuration.
* Without a value the model reads NOTEN_SORTIERUNG itself, for the
* callers outside the Benotungstool.
*/
public function getAllActive($sortOrder = null) {
$qry ="SELECT *
FROM lehre.tbl_note
WHERE aktiv = true";
if($sortOrder === null) {
$this->config->load('noten', FALSE, TRUE);
$sortOrder = $this->config->item('NOTEN_SORTIERUNG');
}
$qry .= ($sortOrder === 'bezeichnung')
? " ORDER BY bezeichnung"
: " ORDER BY CASE WHEN note BETWEEN 1 AND 5 THEN 0 ELSE 1 END,
CASE WHEN note BETWEEN 1 AND 5 THEN note END,
bezeichnung";
return $this->execReadOnlyQuery($qry);
}
}
}
@@ -13,7 +13,7 @@ class Notenschluesselaufteilung_model extends DB_Model
}
/**
* Liefert die Note zu Punkten einer Lehrveranstaltung
* Gives the Note for the Punkte of an LV.
*
* @param number $points
* @param integer $lehrveranstaltung_id
@@ -23,9 +23,18 @@ class Notenschluesselaufteilung_model extends DB_Model
*/
public function getNote($points, $lehrveranstaltung_id, $studiensemester_kurzbz)
{
// Without valid Punkte no Note follows. Without this guard the query builder makes a bad
// statement from "punkte <=" => null ('punkte' < 'IS' 'NULL'), and the request fails with
// HTTP 500 instead of an answer that says "no Note".
if (!is_numeric($points))
return success(null);
$this->load->model('education/Notenschluesselzuordnung_model', 'NotenschluesselzuordnungModel');
$notenschluessel_kurzbz = $this->NotenschluesselzuordnungModel->getKurzbzForLv($lehrveranstaltung_id, $studiensemester_kurzbz);
if($notenschluessel_kurzbz == null)
return success(null);
$this->addSelect("note");
$this->addOrder("punkte", "DESC");
$this->addLimit(1);
@@ -50,8 +50,7 @@ class Paabgabe_model extends DB_Model
JOIN lehre.tbl_projektbetreuer USING(projektarbeit_id)
JOIN public.tbl_benutzer bn_student ON(tbl_projektarbeit.student_uid=bn_student.uid)
JOIN public.tbl_person person_student ON(bn_student.person_id=person_student.person_id)
JOIN lehre.tbl_lehreinheit ON(tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung ON(tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang ON(tbl_lehrveranstaltung.studiengang_kz=tbl_studiengang.studiengang_kz)
JOIN campus.tbl_paabgabetyp USING(paabgabetyp_kurzbz)
WHERE
@@ -68,14 +67,13 @@ class Paabgabe_model extends DB_Model
campus.tbl_paabgabe.insertvon, campus.tbl_paabgabe.insertamum, campus.tbl_paabgabe.updatevon, campus.tbl_paabgabe.updateamum,
campus.tbl_paabgabe.note, upload_allowed, beurteilungsnotiz, student_uid, tbl_projektarbeit.note, lehre.tbl_projektarbeit.titel,
UPPER(tbl_studiengang.typ) as stgtyp, UPPER(tbl_studiengang.kurzbz) as stgkz, public.tbl_studiengang.studiengang_kz,
public.tbl_studiengang.oe_kurzbz as stg_oe_kurzbz, tbl_lehreinheit.studiensemester_kurzbz,
public.tbl_studiengang.oe_kurzbz as stg_oe_kurzbz, tbl_projektarbeit.studiensemester_kurzbz,
public.tbl_person.anrede, public.tbl_person.titelpre, public.tbl_person.vorname, public.tbl_person.nachname, public.tbl_person.titelpost
FROM campus.tbl_paabgabe
JOIN campus.tbl_paabgabetyp USING (paabgabetyp_kurzbz)
JOIN lehre.tbl_projektarbeit USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit using(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung using(lehrveranstaltung_id)
JOIN public.tbl_studiengang on(lehre.tbl_lehrveranstaltung.studiengang_kz = public.tbl_studiengang.studiengang_kz)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang ON(lehre.tbl_lehrveranstaltung.studiengang_kz = public.tbl_studiengang.studiengang_kz)
JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid)
JOIN public.tbl_person USING (person_id)
@@ -93,7 +91,7 @@ class Paabgabe_model extends DB_Model
campus.tbl_paabgabe.insertvon, campus.tbl_paabgabe.insertamum, campus.tbl_paabgabe.updatevon, campus.tbl_paabgabe.updateamum,
campus.tbl_paabgabe.note, upload_allowed, beurteilungsnotiz, student_uid, tbl_projektarbeit.note, lehre.tbl_projektarbeit.titel,
UPPER(tbl_studiengang.typ) as stgtyp, UPPER(tbl_studiengang.kurzbz) as stgkz, public.tbl_studiengang.studiengang_kz,
public.tbl_studiengang.oe_kurzbz as stg_oe_kurzbz, tbl_lehreinheit.studiensemester_kurzbz,
public.tbl_studiengang.oe_kurzbz as stg_oe_kurzbz, tbl_projektarbeit.studiensemester_kurzbz,
lehre.tbl_projektbetreuer.betreuerart_kurzbz, lehre.tbl_projektbetreuer.person_id,
public.tbl_person.anrede, public.tbl_person.titelpre, public.tbl_person.vorname, public.tbl_person.nachname, public.tbl_person.titelpost
@@ -101,8 +99,7 @@ class Paabgabe_model extends DB_Model
JOIN campus.tbl_paabgabetyp USING (paabgabetyp_kurzbz)
JOIN lehre.tbl_projektarbeit USING (projektarbeit_id)
JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit using(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung using(lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang on(lehre.tbl_lehrveranstaltung.studiengang_kz = public.tbl_studiengang.studiengang_kz)
JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid)
JOIN public.tbl_person ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
@@ -25,7 +25,7 @@ class Projektarbeit_model extends DB_Model
{
$qry = "SELECT
pa.*, tbl_projekttyp.bezeichnung,
tbl_lehreinheit.studiensemester_kurzbz, tbl_lehrveranstaltung.lehrveranstaltung_id,
pa.studiensemester_kurzbz, pa.lehrveranstaltung_id,
tbl_firma.name AS firma_name,
(
SELECT
@@ -41,7 +41,6 @@ class Projektarbeit_model extends DB_Model
FROM
lehre.tbl_projektarbeit pa
JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_firma USING (firma_id)
WHERE
@@ -57,7 +56,7 @@ class Projektarbeit_model extends DB_Model
if (isset($studiensemester_kurzbz))
{
$qry .= ' AND tbl_lehreinheit.studiensemester_kurzbz=?';
$qry .= ' AND tbl_projektarbeit.studiensemester_kurzbz=?';
$params[] = $studiensemester_kurzbz;
}
@@ -134,7 +133,7 @@ class Projektarbeit_model extends DB_Model
projektarbeit_id,
lehre.tbl_projekttyp.bezeichnung as projekttypbezeichnung,
lehre.tbl_projekttyp.projekttyp_kurzbz as projekttypkurzbz,
lehre.tbl_lehreinheit.studiensemester_kurzbz,
lehre.tbl_projektarbeit.studiensemester_kurzbz,
lehre.tbl_lehrveranstaltung.studiengang_kz,
public.tbl_studiengang.kurzbzlang,
lehre.tbl_projektarbeit.note as note,
@@ -157,7 +156,6 @@ class Projektarbeit_model extends DB_Model
LEFT JOIN public.tbl_benutzer USING(person_id)
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
LEFT JOIN lehre.tbl_betreuerart USING(betreuerart_kurzbz)
LEFT JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
LEFT JOIN public.tbl_mitarbeiter ON(public.tbl_mitarbeiter.mitarbeiter_uid = public.tbl_benutzer.uid)
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
@@ -366,7 +364,7 @@ class Projektarbeit_model extends DB_Model
tbl_studiengang.typ, tbl_studiengang.kurzbz,
tbl_projektarbeit.projekttyp_kurzbz, tbl_projekttyp.bezeichnung,
tbl_projektarbeit.titel, tbl_projektarbeit.projektarbeit_id, tbl_projektarbeit.note,
student_benutzer.uid, tbl_student.matrikelnr, tbl_lehreinheit.studiensemester_kurzbz, public.tbl_student.student_uid,
student_benutzer.uid, tbl_student.matrikelnr, tbl_projektarbeit.studiensemester_kurzbz, public.tbl_student.student_uid,
lehre.tbl_projektarbeit.sprache,
lehre.tbl_projektarbeit.seitenanzahl,
@@ -430,7 +428,6 @@ class Projektarbeit_model extends DB_Model
LEFT JOIN public.tbl_benutzer student_benutzer ON student_benutzer.uid = lehre.tbl_projektarbeit.student_uid
LEFT JOIN public.tbl_person student_person ON student_benutzer.person_id = student_person.person_id
LEFT JOIN public.tbl_student ON student_benutzer.uid = public.tbl_student.student_uid
LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang ON public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
@@ -503,7 +500,7 @@ class Projektarbeit_model extends DB_Model
student_person.vorname AS student_vorname,
student_person.nachname AS student_nachname,
public.tbl_student.matrikelnr,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
betreuer_benutzer.uid AS betreuer_benutzer_uid,
betreuer_person.titelpre AS betreuer_titelpre,
betreuer_person.vorname AS betreuer_vorname,
@@ -565,7 +562,6 @@ class Projektarbeit_model extends DB_Model
LEFT JOIN public.tbl_benutzer student_benutzer ON student_benutzer.uid = lehre.tbl_projektarbeit.student_uid
LEFT JOIN public.tbl_person student_person ON student_benutzer.person_id = student_person.person_id
LEFT JOIN public.tbl_student ON student_benutzer.uid = public.tbl_student.student_uid
LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang ON public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
@@ -633,7 +629,7 @@ class Projektarbeit_model extends DB_Model
student_person.vorname AS student_vorname,
student_person.nachname AS student_nachname,
public.tbl_student.matrikelnr,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
betreuer_benutzer.uid AS betreuer_benutzer_uid,
betreuer_person.titelpre AS betreuer_titelpre,
betreuer_person.vorname AS betreuer_vorname,
@@ -695,7 +691,6 @@ class Projektarbeit_model extends DB_Model
LEFT JOIN public.tbl_benutzer student_benutzer ON student_benutzer.uid = lehre.tbl_projektarbeit.student_uid
LEFT JOIN public.tbl_person student_person ON student_benutzer.person_id = student_person.person_id
LEFT JOIN public.tbl_student ON student_benutzer.uid = public.tbl_student.student_uid
LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang ON public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
@@ -306,4 +306,5 @@ class Pruefung_model extends DB_Model
return $this->loadWhereCommitteeExamsFailed();
}
}
@@ -59,6 +59,37 @@ class Studienplan_model extends DB_Model
'tbl_studienplan_lehrveranstaltung.semester' => $semester
));
}
public function getStudienplanByLvaSemKurzbz($lehrveranstaltung_id, $studiensemester_kurzbz) {
$qry= "
SELECT
DISTINCT tbl_studienplan.*
FROM
lehre.tbl_studienplan
JOIN lehre.tbl_studienplan_lehrveranstaltung
USING(studienplan_id)
WHERE
tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id IN (
SELECT
lv.lehrveranstaltung_id
FROM
lehre.tbl_lehrveranstaltung AS lv
LEFT JOIN lehre.tbl_lehrveranstaltung AS t ON t.lehrveranstaltung_id=lv.lehrveranstaltung_template_id
WHERE
lv.lehrtyp_kurzbz<>'tpl'
AND (lv.lehrveranstaltung_id= ? OR (lv.lehrveranstaltung_template_id= ? AND t.lehrtyp_kurzbz='tpl'))
)
AND EXISTS (
SELECT 1
FROM
lehre.tbl_studienplan_semester
WHERE studienplan_id=tbl_studienplan.studienplan_id
AND studiensemester_kurzbz= ?
AND semester = tbl_studienplan_lehrveranstaltung.semester)
ORDER BY bezeichnung";
return $this->execReadOnlyQuery($qry, array($lehrveranstaltung_id, $lehrveranstaltung_id, $studiensemester_kurzbz));
}
public function getStudienplanLehrveranstaltungForPrestudent($studienplan_id, $semester, $prestudent_id)
{
@@ -218,7 +218,7 @@ class Studiensemester_model extends DB_Model
*/
public function getByDate($date)
{
// gets the studiensemster of a date or the next closest previous studiensemester if a date is not within a studiensemester
// gets the studiensemester of a date or the next closest previous studiensemester if a date is not within a studiensemester
$query = "
SELECT studiensemester_kurzbz, start, ende
FROM public.tbl_studiensemester
+10 -3
View File
@@ -98,7 +98,7 @@ class Notiz_model extends DB_Model
/**
* Add a Notiz for a given person
*/
public function addNotizForPerson($person_id, $titel, $text, $erledigt, $verfasser_uid)
public function addNotizForPerson($person_id, $titel, $text, $erledigt, $verfasser_uid, $insertvon = null)
{
// Loads model Notizzuordnung_model
$this->load->model('person/Notizzuordnung_model', 'NotizzuordnungModel');
@@ -106,8 +106,15 @@ class Notiz_model extends DB_Model
// Start DB transaction
$this->db->trans_start(false);
$result = $this->insert(array('titel' => $titel, 'text' => $text, 'erledigt' => $erledigt, 'verfasser_uid' => $verfasser_uid,
"insertvon" => $verfasser_uid));
$result = $this->insert(
array(
'titel' => $titel,
'text' => $text,
'erledigt' => $erledigt,
'verfasser_uid' => $verfasser_uid,
'insertvon' => $insertvon ?? $verfasser_uid
)
);
if (isSuccess($result))
{
@@ -23,7 +23,8 @@ $includesArray = array(
'public/css/components/FormUnderline.css',
'public/css/components/abgabetool/abgabe.css',
'public/css/Cis4/Cms.css',
'public/css/Cis4/Studium.css'
'public/css/Cis4/Studium.css',
'public/css/Cis4/Benotungstool.css'
),
'customJSs' => array(
'vendor/npm-asset/primevue/accordion/accordion.min.js',
@@ -37,11 +38,15 @@ $includesArray = array(
'vendor/npm-asset/primevue/timeline/timeline.min.js',
'vendor/npm-asset/primevue/inplace/inplace.min.js',
'vendor/npm-asset/primevue/message/message.min.js',
'vendor/npm-asset/primevue/divider/divider.min.js',
'vendor/npm-asset/primevue/password/password.js',
'vendor/npm-asset/primevue/multiselect/multiselect.js',
'vendor/npm-asset/primevue/tieredmenu/tieredmenu.js',
'vendor/moment/luxonjs/luxon.min.js'
),
'customJSModules' => array(
'public/js/apps/Cis/Cis.js',
'vendor/olifolkerd/tabulator5/src/js/modules/ColumnCalcs/ColumnCalcs.js'
),
);
@@ -16,7 +16,7 @@ $query = '
anrechnung.begruendung_id,
anrechnung.dms_id,
CASE
WHEN stg.typ || stg.kurzbz IN (' . $ORGANISATIONSEINHEITEN_SCHREIBBERECHTIGT . ') THEN TRUE
WHEN stg.oe_kurzbz IN (' . $ORGANISATIONSEINHEITEN_SCHREIBBERECHTIGT . ') THEN TRUE
ELSE FALSE
END "schreibberechtigt",
anrechnung.studiensemester_kurzbz,
@@ -232,8 +232,8 @@ FROM
SELECT
pa.lehreinheit_id,
lv.lehrveranstaltung_id,
pa.projektarbeit_id::text AS "projektarbeit_id",
le.studiensemester_kurzbz,
pa.projektarbeit_id::text AS "projektarbeit_id",
pa.studiensemester_kurzbz,
stg.studiengang_kz,
upper(stg.typ || stg.kurzbz) AS "stg_typ_kurzbz",
lv.orgform_kurzbz,
@@ -268,8 +268,7 @@ FROM
FROM
lehre.tbl_projektbetreuer pb
JOIN lehre.tbl_projektarbeit pa USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung lv ON (pa.lehrveranstaltung_id = lv.lehrveranstaltung_id)
JOIN PUBLIC.tbl_organisationseinheit oe USING (oe_kurzbz)
JOIN PUBLIC.tbl_person person USING (person_id)
LEFT JOIN lehre.tbl_vertrag vertrag USING (vertrag_id)
@@ -280,7 +279,7 @@ FROM
/* filter projektbetreuuer */
pb.person_id = \'' . $PERSON_ID . '\'
/* filter studiensemester */
AND le.studiensemester_kurzbz = \'' . $STUDIENSEMESTER . '\'
AND pa.studiensemester_kurzbz = \'' . $STUDIENSEMESTER . '\'
/* filter active lehrveranstaltungen */
AND lv.aktiv = TRUE
/* filter active organisationseinheiten */
@@ -242,8 +242,8 @@ FROM
pa.lehreinheit_id,
lv.lehrveranstaltung_id,
lv.bezeichnung AS "lv_bezeichnung",
(pa.projektarbeit_id::text) AS "projektarbeit_id",
le.studiensemester_kurzbz,
(pa.projektarbeit_id::text) AS "projektarbeit_id",
pa.studiensemester_kurzbz,
stg.studiengang_kz,
upper(stg.typ || stg.kurzbz) AS "stg_typ_kurzbz",
lv.orgform_kurzbz,
@@ -270,7 +270,7 @@ FROM
ELSE (oe.organisationseinheittyp_kurzbz ||
\' \' || oe.bezeichnung)
END AS "lv_oe_kurzbz",
(nachname || \' \' || vorname) AS "lektor",
(nachname || \' \' || vorname) AS "lektor",
TRUNC(pb.stunden, 1) AS "stunden",
pb.stundensatz,
TRUNC((pb.stunden * pb.stundensatz), 2) AS "betrag",
@@ -280,8 +280,7 @@ FROM
FROM
lehre.tbl_projektbetreuer pb
JOIN lehre.tbl_projektarbeit pa USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung lv ON (pa.lehrveranstaltung_id = lv.lehrveranstaltung_id)
JOIN PUBLIC.tbl_organisationseinheit oe USING (oe_kurzbz)
JOIN PUBLIC.tbl_person person USING (person_id)
LEFT JOIN lehre.tbl_vertrag vertrag USING (vertrag_id)
@@ -291,7 +290,7 @@ FROM
/* filter organisationseinheit */
lv.oe_kurzbz IN (\'' . implode('\',\'', $ORGANISATIONSEINHEIT) . '\')
/* filter studiensemester */
AND le.studiensemester_kurzbz = \'' . $STUDIENSEMESTER . '\'
AND pa.studiensemester_kurzbz = \'' . $STUDIENSEMESTER . '\'
/* filter active lehrveranstaltungen */
AND lv.aktiv = TRUE
/* filter active organisationseinheiten */
@@ -272,8 +272,8 @@ FROM
pa.lehreinheit_id,
lv.lehrveranstaltung_id,
lv.bezeichnung AS "lv_bezeichnung",
pa.projektarbeit_id::text AS "projektarbeit_id",
le.studiensemester_kurzbz,
pa.projektarbeit_id::text AS "projektarbeit_id",
pa.studiensemester_kurzbz,
stg.studiengang_kz,
upper(stg.typ || stg.kurzbz) AS "stg_typ_kurzbz",
lv.orgform_kurzbz,
@@ -310,8 +310,7 @@ FROM
FROM
lehre.tbl_projektbetreuer pb
JOIN lehre.tbl_projektarbeit pa USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung lv ON (pa.lehrveranstaltung_id = lv.lehrveranstaltung_id)
JOIN PUBLIC.tbl_organisationseinheit oe USING (oe_kurzbz)
JOIN PUBLIC.tbl_person person USING (person_id)
LEFT JOIN lehre.tbl_vertrag vertrag USING (vertrag_id)
@@ -321,7 +320,7 @@ FROM
/* filter studiengang */
lv.studiengang_kz IN ('. implode(',', $STUDIENGANG) . ')
/* filter studiensemester */
AND le.studiensemester_kurzbz = \''. $STUDIENSEMESTER. '\'
AND pa.studiensemester_kurzbz = \''. $STUDIENSEMESTER. '\'
/* filter active lehrveranstaltungen */
AND lv.aktiv = TRUE
/* filter ausbildungssemester */
@@ -57,7 +57,7 @@
<div class="row<?php if ($lockedbyother) echo ' alert-danger' ?>">
<div class="col-lg-8">
<h3 class="page-header">
Infocenter Details: <a target="_blank" title="Studentenverwaltung" href="<?php echo site_url('/Studentenverwaltung/' . $studiensemester . '/person/' . $stammdaten->person_id) ?>"><?php echo $stammdaten->vorname.' '.$stammdaten->nachname ?> <i class="fa fa-external-link" style="font-size:small"></i></a>
Infocenter Details: <a target="_blank" title="Studentenverwaltung" href="<?php echo site_url('/studvw/stdsem/' . $studiensemester . '/person/' . $stammdaten->person_id) ?>"><?php echo $stammdaten->vorname.' '.$stammdaten->nachname ?> <i class="fa fa-external-link" style="font-size:small"></i></a>
</h3>
</div>
<div class="col-lg-4">
@@ -8,6 +8,7 @@
$LOGDATA_NAME = '\'Login with code\', \'Login with user\', \'Attempt to register with existing mailadress\', \'Access code sent\', \'Personal data saved\'';
$REJECTED_STATUS = '\'Abgewiesener\'';
$ADDITIONAL_STG = $this->config->item('infocenter_studiengang_kz');
$QUALIFIKATIONSKURS = $this->config->item('infocenter_qualifikationskurs_kz');
$STATUS_KURZBZ = '\'Wartender\', \'Bewerber\', \'Aufgenommener\', \'Student\'';
$STUDIENSEMESTER = '\''.$this->variablelib->getVar('infocenter_studiensemester').'\'';
$ORG_NAME = '\'InfoCenter\'';
@@ -277,7 +278,26 @@ $query = '
WHERE konto.person_id = p.person_id
AND konto.studiensemester_kurzbz = '. $STUDIENSEMESTER .'
AND konto.buchungstyp_kurzbz = '. $KAUTION_DRITT_STAAT .'
) AS "Kaution"
) AS "Kaution",
(
SELECT 1
FROM public.tbl_prestudentstatus pss
JOIN public.tbl_prestudent ps USING(prestudent_id)
JOIN public.tbl_studiengang sg USING(studiengang_kz)
JOIN lehre.tbl_studienplan sp USING(studienplan_id)
JOIN lehre.tbl_studienordnung so USING(studienordnung_id)
WHERE pss.status_kurzbz = '.$INTERESSENT_STATUS.'
AND pss.bewerbung_abgeschicktamum IS NOT NULL
AND ps.person_id = p.person_id
AND sg.studiengang_kz in('.$QUALIFIKATIONSKURS.')
AND NOT EXISTS (
SELECT 1
FROM tbl_prestudentstatus spss
WHERE spss.prestudent_id = ps.prestudent_id
AND spss.status_kurzbz = '.$REJECTED_STATUS.'
)
LIMIT 1
) AS "Qualikurs"
FROM public.tbl_person p
LEFT JOIN (
SELECT tpl.person_id,
@@ -351,7 +371,8 @@ $query = '
'ZGV Nation MA',
'InfoCenter Mitarbeiter',
'Identitätsnachweis',
ucfirst($this->p->t('infocenter', 'kaution'))
ucfirst($this->p->t('infocenter', 'kaution')),
'Qualikurs'
),
'formatRow' => function($datasetRaw) {
@@ -480,6 +501,14 @@ $query = '
$datasetRaw->{'Kaution'} = 'Offen';
}
if ($datasetRaw->{'Qualikurs'} === null)
{
$datasetRaw->{'Qualikurs'} = 'Nein';
}
else
{
$datasetRaw->{'Qualikurs'} = 'Ja';
}
return $datasetRaw;
},
'markRow' => function($datasetRaw) {
@@ -92,6 +92,14 @@
</table>
</div>
<div class="col-lg-6 table-responsive">
<?php if ($onboarding_first_login): ?>
<div class="row" id="onboardingRow">
<?php if ($created_by_onboarding): ?>
<div class="col-xs-5"><strong><?php echo $this->p->t('infocenter','onboardingRegistriert') ?></strong></div>
<?php endif; ?>
<div class="col-xs-7<?php echo $created_by_onboarding ? ' text-right' : '' ?>"><strong><?php echo $this->p->t('infocenter','ersterOnboardingLogin').': '.date_format(date_create($onboarding_first_login), 'd.m.Y') ?></strong></div>
</div>
<?php endif; ?>
<table class="table table-bordered stammdaten_form">
<thead>
<tr>
@@ -191,12 +199,12 @@
</div>
<?php endif; ?>
<div class="col-xs-6">
<a class="editStammdaten">
<a class="editStammdaten" href="javascript:void(0);">
<i class="fa fa-edit"></i>&nbsp;<?php echo $this->p->t('ui','bearbeiten'); ?></a>
<div class="editActionStammdaten" style="display:none">
<a class="cancelStammdaten">
<a class="cancelStammdaten" href="javascript:void(0);">
<i class="fa fa-trash"></i>&nbsp;<?php echo $this->p->t('ui','abbrechen');?></a>
<a class="saveStammdaten">
<a class="saveStammdaten" href="javascript:void(0);">
<i class="fa fa-save"></i>&nbsp;<?php echo $this->p->t('ui','speichern'); ?></a>
</div>
</div>
@@ -1824,7 +1824,7 @@ $htmlstring .= "
</table>
";
if ($anzahlChanged > 0)
if (isset($anzahlChanged) && $anzahlChanged > 0)
echo '<div id="notenWarning" class="warning">' . $p->t('benotungstool/geaenderteNotenVorhanden') . '</div>';
echo $htmlstring;
+35
View File
@@ -159,6 +159,41 @@ if (!$berechtigung->isBerechtigt('admin')
// let's merge
$format_title->setAlign('merge');
// Importkonfiguration aus dem CI-Config (application/config/noten.php) lesen, damit nur die
// Spalten des jeweils aktiven Importtyps hervorgehoben (grau) werden.
$CIS_GESAMTNOTE_NOTENIMPORT = false;
$CIS_GESAMTNOTE_PRUEFUNGSIMPORT = true;
$notenConfigFile = __DIR__.'/../../../application/config/noten.php';
if(is_file($notenConfigFile))
{
if(!defined('BASEPATH')) define('BASEPATH', true);
$config = array();
include($notenConfigFile);
if(isset($config['CIS_GESAMTNOTE_NOTENIMPORT'])) $CIS_GESAMTNOTE_NOTENIMPORT = $config['CIS_GESAMTNOTE_NOTENIMPORT'];
if(isset($config['CIS_GESAMTNOTE_PRUEFUNGSIMPORT'])) $CIS_GESAMTNOTE_PRUEFUNGSIMPORT = $config['CIS_GESAMTNOTE_PRUEFUNGSIMPORT'];
unset($config);
}
// plain (non-highlighted) variants used when an import type is disabled
$format_plain =& $workbook->addFormat();
$format_plain->setAlign('left');
$format_plain->setNumFormat(49);
$format_plainright =& $workbook->addFormat();
$format_plainright->setAlign('right');
$format_plainright->setNumFormat(49);
// highlight only the cells belonging to an enabled import flow:
// - Notenimport -> Personenkennzeichen + Note (main block)
// - Prüfungsimport -> Personenkennzeichen + Datum + Note (Termin2/Termin3 blocks)
$fmtNoteImportPkz = $CIS_GESAMTNOTE_NOTENIMPORT ? $format_highlight : $format_plain;
$fmtNoteImportNote = $CIS_GESAMTNOTE_NOTENIMPORT ? $format_highlightright : $format_plainright;
$fmtPruefImportPkz = $CIS_GESAMTNOTE_PRUEFUNGSIMPORT ? $format_highlight : $format_plain;
$fmtPruefImportDate = $CIS_GESAMTNOTE_PRUEFUNGSIMPORT ? $format_highlightright_date : $format_plainright;
$fmtPruefImportNote = $CIS_GESAMTNOTE_PRUEFUNGSIMPORT ? $format_highlightright : $format_plainright;
$lvobj = new lehrveranstaltung($lvid);
$worksheet->write(0,0,$p->t('anwesenheitsliste/notenliste')." ".($sprache=='English'?$lvobj->bezeichnung_english:$lvobj->bezeichnung),$format_bold);
$stg_obj = new studiengang($stg);
+3 -4
View File
@@ -345,11 +345,10 @@ $qry = "SELECT
WHERE uid=student_uid) as student, tbl_lehrveranstaltung.studiengang_kz, tbl_lehrveranstaltung.semester,
tbl_studiengang.email, tbl_betreuerart.beschreibung AS beutreuerart_beschreibung, tbl_projektbetreuer.stunden
FROM
lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_projektarbeit, lehre.tbl_projektbetreuer, public.tbl_studiengang, lehre.tbl_betreuerart
lehre.tbl_lehrveranstaltung, lehre.tbl_projektarbeit, lehre.tbl_projektbetreuer, public.tbl_studiengang, lehre.tbl_betreuerart
WHERE
tbl_lehreinheit.lehreinheit_id=tbl_projektarbeit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($stdsem)." AND
tbl_lehrveranstaltung.lehrveranstaltung_id=tbl_projektarbeit.lehrveranstaltung_id AND
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($stdsem)." AND
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
tbl_lehrveranstaltung.studiengang_kz=tbl_studiengang.studiengang_kz AND
tbl_projektbetreuer.betreuerart_kurzbz=tbl_betreuerart.betreuerart_kurzbz AND
-2
View File
@@ -143,7 +143,6 @@ if($aktion!='zip')
JOIN campus.tbl_paabgabe USING(projektarbeit_id)
LEFT JOIN public.tbl_benutzer ON(uid=student_uid)
LEFT JOIN public.tbl_person ON(tbl_benutzer.person_id=tbl_person.person_id)
LEFT JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE (projekttyp_kurzbz='Bachelor' OR projekttyp_kurzbz='Diplom')
@@ -214,7 +213,6 @@ if(isset($_REQUEST['ok']) || (isset($_REQUEST['aktion']) && $_REQUEST['aktion']=
JOIN campus.tbl_paabgabe USING(projektarbeit_id)
LEFT JOIN public.tbl_benutzer ON(uid=student_uid)
LEFT JOIN public.tbl_person ON(tbl_benutzer.person_id=tbl_person.person_id)
LEFT JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE (projekttyp_kurzbz='Bachelor' OR projekttyp_kurzbz='Diplom')
@@ -87,7 +87,6 @@ cellSeparator (default value: "|")
JOIN campus.tbl_paabgabe USING(projektarbeit_id)
LEFT JOIN public.tbl_benutzer ON(uid=student_uid)
LEFT JOIN public.tbl_person ON(tbl_benutzer.person_id=tbl_person.person_id)
LEFT JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE (projekttyp_kurzbz='Bachelor' OR projekttyp_kurzbz='Diplom')
+1 -2
View File
@@ -122,8 +122,7 @@ foreach ($project_arr as $project)
// * get studiensemester
$pa = new Projektarbeit($pa_id);
$le = new Lehreinheit($pa->lehreinheit_id);
$studiensemester_kurzbz = $le->studiensemester_kurzbz;
$studiensemester_kurzbz = $pa->studiensemester_kurzbz;
// * get begin- and end date of studiensemester
$ss = new Studiensemester($studiensemester_kurzbz);
+3 -5
View File
@@ -107,16 +107,14 @@ SELECT
FROM
lehre.tbl_projektarbeit,
lehre.tbl_lehrveranstaltung,
lehre.tbl_lehreinheit,
lehre.tbl_projektbetreuer,
public.tbl_person
WHERE
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
tbl_lehreinheit.lehreinheit_id=tbl_projektarbeit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektbetreuer.person_id=tbl_person.person_id AND
tbl_projektbetreuer.stunden<>0 AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($stsem)."
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($stsem)."
GROUP BY studiengang_kz,geschlecht";
if(!$result = $db->db_query($qry))
@@ -231,7 +229,7 @@ if($format=='xls')
if(!isset($stunden['w']))
$stunden['w']=0;
$summe_w+=$stunden['w'];
if(!isset($summe_oe[$oe]['w']))
if(!isset($summe_oe[$oe]['w']))
$summe_oe[$oe]['w']=0;
$summe_oe[$oe]['w']+=$stunden['w'];
$worksheet->write($zeile,$organisationseinheiten_idx[$oe]+1,$stunden['w'],$format_data);
@@ -85,7 +85,7 @@ $db = new basis_db();
//Daten holen
$qry = "SELECT * FROM (
SELECT
tbl_lehreinheit.*, tbl_person.vorname, tbl_person.nachname, tbl_person.titelpre,
tbl_person.vorname, tbl_person.nachname, tbl_person.titelpre,
tbl_mitarbeiter.personalnummer, tbl_person.person_id, tbl_mitarbeiter.mitarbeiter_uid,
tbl_mitarbeiter.fixangestellt,
tbl_lehreinheitmitarbeiter.stundensatz as stundensatz,
@@ -109,21 +109,20 @@ if($semester!='')
//Projektsbetreuungen
$qry.= " UNION
SELECT
tbl_lehreinheit.*, tbl_person.vorname, tbl_person.nachname, tbl_person.titelpre,
tbl_person.vorname, tbl_person.nachname, tbl_person.titelpre,
tbl_mitarbeiter.personalnummer, tbl_person.person_id, tbl_mitarbeiter.mitarbeiter_uid,
tbl_mitarbeiter.fixangestellt,
0 as stundensatz,
0 as semesterstunden
FROM
lehre.tbl_lehreinheit, lehre.tbl_projektarbeit, lehre.tbl_projektbetreuer,
lehre.tbl_projektarbeit, lehre.tbl_projektbetreuer,
public.tbl_mitarbeiter, public.tbl_benutzer, lehre.tbl_lehrveranstaltung, public.tbl_person
WHERE
tbl_mitarbeiter.mitarbeiter_uid = tbl_benutzer.uid AND
tbl_benutzer.person_id = tbl_projektbetreuer.person_id AND
tbl_projektarbeit.projektarbeit_id = tbl_projektbetreuer.projektarbeit_id AND
tbl_projektarbeit.lehreinheit_id = tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)." AND
tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)." AND
tbl_lehrveranstaltung.studiengang_kz = ".$db->db_add_param($studiengang_kz, FHC_INTEGER)." AND
tbl_person.person_id = tbl_projektbetreuer.person_id";
if($semester!='')
@@ -164,16 +163,15 @@ if($result = $db->db_query($qry))
$qry = "
SELECT
tbl_projektbetreuer.stunden, tbl_projektbetreuer.stundensatz
FROM lehre.tbl_projektbetreuer, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung,
FROM lehre.tbl_projektbetreuer, lehre.tbl_lehrveranstaltung,
public.tbl_benutzer, lehre.tbl_projektarbeit, campus.vw_student
WHERE
tbl_projektbetreuer.person_id=tbl_benutzer.person_id
AND tbl_benutzer.uid=".$db->db_add_param($uid)."
AND tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id
AND student_uid=vw_student.uid
AND tbl_lehreinheit.lehreinheit_id=tbl_projektarbeit.lehreinheit_id
AND tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
AND tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
AND tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER);
if($semester!='')
$qry.=" AND tbl_lehrveranstaltung.semester=".$db->db_add_param($semester, FHC_INTEGER);
@@ -99,10 +99,9 @@ $qry_stg = "SELECT distinct studiengang_kz, typ, kurzbz
studiengang_kz
FROM
lehre.tbl_projektbetreuer JOIN lehre.tbl_projektarbeit ON tbl_projektbetreuer.projektarbeit_id=tbl_projektarbeit.projektarbeit_id
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_projektarbeit.lehreinheit_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_lehreinheit.lehrveranstaltung_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_projektarbeit.lehrveranstaltung_id
WHERE
lehre.tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
lehre.tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
) as foo
JOIN public.tbl_studiengang USING (studiengang_kz)
";
@@ -506,10 +505,9 @@ if ($result_stg = $db->db_query($qry_stg))
JOIN public.tbl_benutzer ON tbl_benutzer.person_id=tbl_person.person_id
JOIN public.tbl_mitarbeiter ON tbl_mitarbeiter.mitarbeiter_uid=tbl_benutzer.uid
JOIN lehre.tbl_projektarbeit ON tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id=tbl_projektarbeit.lehreinheit_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_lehreinheit.lehrveranstaltung_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_projektarbeit.lehrveranstaltung_id
WHERE
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)." AND
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)." AND
tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER)." AND
NOT EXISTS (SELECT
mitarbeiter_uid
@@ -589,12 +587,11 @@ if ($result_stg = $db->db_query($qry_stg))
FROM
lehre.tbl_projektbetreuer JOIN public.tbl_benutzer ON tbl_projektbetreuer.person_id = tbl_benutzer.person_id
JOIN lehre.tbl_projektarbeit ON tbl_projektarbeit.projektarbeit_id = tbl_projektbetreuer.projektarbeit_id
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_projektarbeit.lehreinheit_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
JOIN campus.vw_student ON vw_student.uid = student_uid
WHERE
tbl_benutzer.uid = ".$db->db_add_param($uid)."
AND tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
AND tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
AND tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER);
if ($result = $db->db_query($qry))
@@ -825,15 +822,14 @@ if ($result_stg = $db->db_query($qry_stg))
$worksheet =& $workbook->addWorksheet('Betreuerstunden');
$worksheet->setInputEncoding('utf-8');
$qry = "SELECT
studiensemester_kurzbz, nachname, vorname, sum(stunden) AS stunden, titelpre,
lehre.tbl_projektarbeit.studiensemester_kurzbz, nachname, vorname, sum(stunden) AS stunden, titelpre,
sum(tbl_projektbetreuer.stundensatz*tbl_projektbetreuer.stunden)::numeric(8, 2) AS euro, person_id
FROM
public.tbl_person JOIN lehre.tbl_projektbetreuer USING (person_id)
JOIN lehre.tbl_projektarbeit USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
WHERE
studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)." AND
lehre.tbl_projektarbeit.studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)." AND
stunden > 0";
if (count($stg_arr) > 0)
@@ -841,7 +837,7 @@ if ($result_stg = $db->db_query($qry_stg))
$qry .= "
GROUP BY
studiensemester_kurzbz, person_id, nachname, vorname, titelpre
lehre.tbl_projektarbeit.studiensemester_kurzbz, person_id, nachname, vorname, titelpre
ORDER BY
nachname, vorname
";
+10 -13
View File
@@ -304,15 +304,14 @@ if($studiengang_kz!='')
$qry = "SELECT
*
FROM
lehre.tbl_projektarbeit, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung,
lehre.tbl_projektarbeit, lehre.tbl_lehrveranstaltung,
lehre.tbl_projektbetreuer, public.tbl_person
WHERE
tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
tbl_person.person_id=tbl_projektbetreuer.person_id AND
tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER)." AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
";
if($semester!='')
@@ -323,14 +322,13 @@ elseif($mitarbeiter_uid!='')
$qry = "SELECT
*
FROM
lehre.tbl_projektarbeit, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_projektbetreuer, public.tbl_person
lehre.tbl_projektarbeit, lehre.tbl_lehrveranstaltung, lehre.tbl_projektbetreuer, public.tbl_person
WHERE
tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
tbl_person.person_id=tbl_projektbetreuer.person_id AND
tbl_projektbetreuer.person_id=".$db->db_add_param($mitarbeiter->person_id, FHC_INTEGER)." AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
";
}
elseif($oe_kurzbz!='')
@@ -338,15 +336,14 @@ elseif($oe_kurzbz!='')
$qry = "SELECT
*
FROM
lehre.tbl_projektarbeit, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_projektbetreuer,
lehre.tbl_projektarbeit, lehre.tbl_lehrveranstaltung, lehre.tbl_projektbetreuer,
public.tbl_person, lehre.tbl_lehrveranstaltung as lehrfach
WHERE
tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
tbl_lehreinheit.lehrfach_id=lehrfach.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=lehrfach.lehrveranstaltung_id AND
tbl_person.person_id=tbl_projektbetreuer.person_id AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)." AND
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)." AND
lehrfach.oe_kurzbz=".$db->db_add_param($oe_kurzbz)."
";
}
+2 -4
View File
@@ -490,15 +490,13 @@ if($result = $db->db_query($qry))
tbl_projektbetreuer.person_id,
tbl_lehrveranstaltung.orgform_kurzbz
FROM lehre.tbl_projektarbeit,
lehre.tbl_lehreinheit,
lehre.tbl_lehrveranstaltung,
lehre.tbl_projektbetreuer,
PUBLIC.tbl_person
WHERE tbl_projektarbeit.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
AND tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
WHERE tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_projektarbeit.projektarbeit_id = tbl_projektbetreuer.projektarbeit_id
AND tbl_person.person_id = tbl_projektbetreuer.person_id
AND tbl_lehreinheit.studiensemester_kurzbz = ".$db->db_add_param($studiensemester_kurzbz);
AND tbl_projektarbeit.studiensemester_kurzbz = ".$db->db_add_param($studiensemester_kurzbz);
if($uid!=='')
{
@@ -84,15 +84,14 @@ $qry = "SELECT
ende, CASE WHEN freigegeben THEN 'Ja' ELSE 'Nein' END, gesperrtbis, themenbereich,
tbl_projektarbeit.anmerkung, projektarbeit_id
FROM
lehre.tbl_projektarbeit, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung,
lehre.tbl_projektarbeit, lehre.tbl_lehrveranstaltung,
public.tbl_benutzer, public.tbl_person, lehre.tbl_projekttyp
WHERE
tbl_projektarbeit.lehreinheit_id = tbl_lehreinheit.lehreinheit_id AND
tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_lehreinheit.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.student_uid = tbl_benutzer.uid AND
tbl_benutzer.person_id = tbl_person.person_id AND
tbl_projektarbeit.projekttyp_kurzbz = tbl_projekttyp.projekttyp_kurzbz AND
tbl_lehreinheit.studiensemester_kurzbz = ".$db->db_add_param($studiensemester_kurzbz)." AND
tbl_projektarbeit.studiensemester_kurzbz = ".$db->db_add_param($studiensemester_kurzbz)." AND
tbl_lehrveranstaltung.studiengang_kz = ".$db->db_add_param($studiengang_kz)." AND
tbl_projektarbeit.projekttyp_kurzbz IN ('Bachelor','Diplom','Projekt')";
+2
View File
@@ -4374,6 +4374,8 @@ if(!$error)
$projektarbeit->titel = $_POST['titel'];
$projektarbeit->titel_english = $_POST['titel_english'];
$projektarbeit->lehreinheit_id = $_POST['lehreinheit_id'];
$projektarbeit->lehrveranstaltung_id = $_POST['lehrveranstaltung_id'];
$projektarbeit->studiensemester_kurzbz = $_POST['studiensemester_kurzbz'];
$projektarbeit->student_uid = $_POST['student_uid'];
$projektarbeit->firma_id = $_POST['firma_id'];
$projektarbeit->note = $_POST['note'];
+23 -9
View File
@@ -239,6 +239,7 @@ function StudentProjektarbeitDetailDisableFields(val)
document.getElementById('student-projektarbeit-button-speichern').disabled=val;
document.getElementById('student-projektarbeit-menulist-projekttyp').disabled=val;
document.getElementById('student-projektarbeit-menulist-lehrveranstaltung').disabled=val;
document.getElementById('student-projektarbeit-menulist-studiensemester').disabled=val;
document.getElementById('student-projektarbeit-menulist-lehreinheit').disabled=val;
document.getElementById('student-projektarbeit-menulist-firma').disabled=val;
document.getElementById('student-projektarbeit-menulist-note').disabled=val;
@@ -290,6 +291,7 @@ function StudentProjektarbeitResetFields()
document.getElementById('student-projektarbeit-menulist-firma').value='';
document.getElementById('student-projektarbeit-menulist-note').value='';
document.getElementById('student-projektarbeit-checkbox-final').checked=true;
document.getElementById('student-projektarbeit-menulist-studiensemester').value=getStudiensemester();
var stg_kz = document.getElementById('student-detail-menulist-studiengang_kz').value;
@@ -379,6 +381,7 @@ function StudentProjektarbeitAuswahl()
lehreinheit_id = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#lehreinheit_id" ));
lehrveranstaltung_id = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#lehrveranstaltung_id" ));
lehreinheit_stsem = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#lehreinheit_stsem" ));
projektarbeit_stsem = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#projektarbeit_stsem" ));
student_uid = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#student_uid" ));
firma_id = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#firma_id" ));
note = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#note" ));
@@ -399,7 +402,7 @@ function StudentProjektarbeitAuswahl()
//Lehrveranstaltung DropDown laden
var LvDropDown = document.getElementById('student-projektarbeit-menulist-lehrveranstaltung');
url='<?php echo APP_ROOT;?>rdf/lehrveranstaltung.rdf.php?uid='+student_uid+"&projektarbeit=true&withlv="+lehrveranstaltung_id+"&"+gettimestamp();
url='<?php echo APP_ROOT;?>rdf/lehrveranstaltung.rdf.php?uid='+student_uid+"&stg_kz="+stg_kz+"&projektarbeit=true&withlv="+lehrveranstaltung_id+"&"+gettimestamp();
//Alte DS entfernen
var oldDatasources = LvDropDown.database.GetDataSources();
@@ -420,7 +423,7 @@ function StudentProjektarbeitAuswahl()
// Lehreinheit Drop Down laden
var LeDropDown = document.getElementById('student-projektarbeit-menulist-lehreinheit');
url='<?php echo APP_ROOT;?>rdf/lehreinheit.rdf.php?lehrveranstaltung_id='+lehrveranstaltung_id+"&studiensemester_kurzbz="+lehreinheit_stsem+"&"+gettimestamp();
url='<?php echo APP_ROOT;?>rdf/lehreinheit.rdf.php?lehrveranstaltung_id='+lehrveranstaltung_id+"&studiensemester_kurzbz="+projektarbeit_stsem+"&"+gettimestamp();
//Alte DS entfernen
var oldDatasources = LeDropDown.database.GetDataSources();
@@ -442,6 +445,7 @@ function StudentProjektarbeitAuswahl()
//Werte setzen
document.getElementById('student-projektarbeit-textbox-projektarbeit_id').value=projektarbeit_id;
document.getElementById('student-projektarbeit-textbox-lehreinheit_stsem').value=lehreinheit_stsem;
document.getElementById('student-projektarbeit-menulist-studiensemester').value=projektarbeit_stsem;
document.getElementById('student-projektarbeit-menulist-projekttyp').value=projekttyp_kurzbz;
document.getElementById('student-projektarbeit-menulist-lehrveranstaltung').value=lehrveranstaltung_id;
document.getElementById('student-projektarbeit-menulist-lehreinheit').value=lehreinheit_id;
@@ -539,6 +543,7 @@ function StudentProjektarbeitSpeichern()
projektarbeit_id = document.getElementById('student-projektarbeit-textbox-projektarbeit_id').value;
projekttyp_kurzbz = document.getElementById('student-projektarbeit-menulist-projekttyp').value;
lehrveranstaltung_id = document.getElementById('student-projektarbeit-menulist-lehrveranstaltung').value;
studiensemester_kurzbz = document.getElementById('student-projektarbeit-menulist-studiensemester').value;
lehreinheit_id = document.getElementById('student-projektarbeit-menulist-lehreinheit').value;
titel = document.getElementById('student-projektarbeit-textbox-titel').value;
titel_english = document.getElementById('student-projektarbeit-textbox-titel_english').value;
@@ -585,12 +590,18 @@ function StudentProjektarbeitSpeichern()
return false;
}
if(lehreinheit_id=='')
if(lehrveranstaltung_id=='')
{
alert('Bitte einen LV-Teil auswaehlen');
alert('Bitte eine Lehrveranstaltung auswaehlen');
return false;
}
if(studiensemester_kurzbz=='')
{
alert('Bitte ein Studiensemester auswaehlen');
return false;
}
if(isNaN(gesamtstunden))
gesamtstunden=0;
@@ -603,6 +614,8 @@ function StudentProjektarbeitSpeichern()
req.add('projekttyp_kurzbz', projekttyp_kurzbz );
req.add('titel', titel);
req.add('titel_english', titel_english);
req.add('lehrveranstaltung_id', lehrveranstaltung_id);
req.add('studiensemester_kurzbz', studiensemester_kurzbz);
req.add('lehreinheit_id', lehreinheit_id);
req.add('student_uid', student_uid);
req.add('firma_id', firma_id);
@@ -738,7 +751,7 @@ function StudentProjektarbeitLVAChange()
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
lehrveranstaltung_id = document.getElementById('student-projektarbeit-menulist-lehrveranstaltung').value;
studiensemester_kurzbz = getStudiensemester();
studiensemester_kurzbz = document.getElementById('student-projektarbeit-menulist-studiensemester').value;
// Lehreinheit Drop Down laden
var LeDropDown = document.getElementById('student-projektarbeit-menulist-lehreinheit');
@@ -897,8 +910,8 @@ function StudentProjektbetreuerAuswahl()
document.getElementById('student-projektbetreuer-textbox-person_id').value=person_id;
document.getElementById('student-projektbetreuer-checkbox-neu').checked=false;
var lehreinheitstsem = document.getElementById('student-projektarbeit-textbox-lehreinheit_stsem').value;
var default_stundensatz = StudentProjektbetreuerLoadStundensatz(person_id, lehreinheitstsem);
var projektarbeitstsem = document.getElementById('student-projektarbeit-menulist-studiensemester').value;
var default_stundensatz = StudentProjektbetreuerLoadStundensatz(person_id, projektarbeitstsem);
if (default_stundensatz != '')
default_stundensatz = 'Stundensatz (Default '+default_stundensatz+'):';
@@ -1410,9 +1423,10 @@ function StudentProjektbetreuerLoadStundensatz(person_id, studiensemester)
function StudentProjektbetreuerLoadMitarbeiterDaten()
{
var person_id = MenulistGetSelectedValue('student-projektbetreuer-menulist-person');
var lehreinheitstsem = document.getElementById('student-projektarbeit-textbox-lehreinheit_stsem').value;
var projektarbeitstsem = document.getElementById('student-projektarbeit-menulist-studiensemester').value;
var stundensatz = StudentProjektbetreuerLoadStundensatz(person_id, lehreinheitstsem);
var stundensatz = StudentProjektbetreuerLoadStundensatz(person_id, projektarbeitstsem);
if (stundensatz != '')
default_stundensatz = 'Stundensatz (Default '+stundensatz+'):';
@@ -104,6 +104,10 @@ $is_hidden = (!defined('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN')
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/projektarbeit/rdf#projektarbeit_id" />
<splitter class="tree-splitter"/>
<treecol id="student-projektarbeit-tree-lehrveranstaltung_id" label="LehrveranstaltungID" flex="2" hidden="true"
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/projektarbeit/rdf#lehrveranstaltung_id" />
<splitter class="tree-splitter"/>
<treecol id="student-projektarbeit-tree-lehreinheit_id" label="LehreinheitID" flex="2" hidden="true"
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/projektarbeit/rdf#lehreinheit_id" />
@@ -131,7 +135,7 @@ $is_hidden = (!defined('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN')
<treeitem uri="rdf:*">
<treerow>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#bezeichnung"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#lehreinheit_stsem"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#projektarbeit_stsem"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#titel"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#note"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#beginn"/>
@@ -141,6 +145,7 @@ $is_hidden = (!defined('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN')
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#themenbereich"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#anmerkung"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#projektarbeit_id"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#lehrveranstaltung_id"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#lehreinheit_id"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#student_uid"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#firma_id"/>
@@ -249,6 +254,20 @@ $is_hidden = (!defined('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN')
</template>
</menulist>
</row>
<row>
<label value="Studiensemester" control="student-projektarbeit-menulist-studiensemester"/>
<menulist id="student-projektarbeit-menulist-studiensemester" disabled="true"
datasources="<?php echo APP_ROOT ?>rdf/studiensemester.rdf.php?order=desc" flex="1"
ref="http://www.technikum-wien.at/studiensemester/liste">
<template>
<menupopup>
<menuitem value="rdf:http://www.technikum-wien.at/studiensemester/rdf#kurzbz"
label="rdf:http://www.technikum-wien.at/studiensemester/rdf#kurzbz"
uri="rdf:*"/>
</menupopup>
</template>
</menulist>
</row>
<row>
<label value="LV-Teil" control="student-projektarbeit-menulist-lehreinheit"/>
<menulist id="student-projektarbeit-menulist-lehreinheit" disabled="true"
+2 -1
View File
@@ -271,7 +271,8 @@ foreach ($uid_arr as $uid)
'matr_nr' => $person->matr_nr,
'ausstellungsdatum' => date('M.Y'),
'gueltigbis' => $datum_obj->formatDatum($gueltigbis, 'd.m.Y'),
'gueltigbis_3jahre' => date('d.m.Y', strtotime('+3 years'))
'gueltigbis_3jahre' => date('d.m.Y', strtotime('+3 years')),
'gueltigbis_2jahre' => date('d.m.Y', strtotime('+2 years'))
);
}
}
+37
View File
@@ -0,0 +1,37 @@
const { defineConfig } = require("cypress");
require("dotenv").config({ path: "tests/cypress/.env" });
module.exports = defineConfig({
// Cypress 15 deprecates Cypress.env() inside cy.request options, which the API helpers use.
allowCypressEnv: true,
// The suite lives under tests/cypress, so every default folder has to be pointed there.
fixturesFolder: "tests/cypress/fixtures",
screenshotsFolder: "tests/cypress/screenshots",
videosFolder: "tests/cypress/videos",
downloadsFolder: "tests/cypress/downloads",
e2e: {
baseUrl: process.env.BASE_URL,
specPattern: "tests/cypress/e2e/**/*.cy.{js,jsx,ts,tsx}",
supportFile: "tests/cypress/support/e2e.js",
defaultCommandTimeout: 20000,
pageLoadTimeout: 20000,
retries: { runMode: 0, openMode: 0 },
setupNodeEvents(on, config) {
// No defaults anywhere: an empty .env fails the run instead of hitting someone else's instance.
const missing = ["BASE_URL", "USER_NAME", "USER_PASSWORD"].filter((key) => !process.env[key]);
if (missing.length) throw new Error(`Missing in tests/cypress/.env: ${missing.join(", ")}`);
// each suite registers its own tasks and Cypress.env() values
return require("./tests/cypress/suites/noten").setupNodeEvents(on, config);
},
},
env: {
adminusername: process.env.USER_NAME,
adminpassword: process.env.USER_PASSWORD,
},
});
+58
View File
@@ -521,5 +521,63 @@ class adresse extends basis_db
}
return true;
}
/**
* Laedt die österreichische Zustell Adresse einer Person
* @param $person_id ID der Person
* @return true wenn ok, false im Fehlerfall
*/
public function loadOesterreichischeZustellAdresse($person_id, $typ = null)
{
//Pruefen ob person_id eine gueltige Zahl ist
if(!is_numeric($person_id) || $person_id == '')
{
$this->errormsg = 'PersonID muss eine Zahl sein';
return false;
}
$typ_clause = '';
if (isset($typ)) $typ_clause = "AND typ=".$this->db_add_param($typ);
//Daten aus der Datenbank lesen
$qry = "SELECT * FROM public.tbl_adresse WHERE person_id=".$this->db_add_param($person_id, FHC_INTEGER, false)."
AND nation = 'A'
{$typ_clause}
ORDER BY zustelladresse DESC, adresse_id DESC LIMIT 1";
if(!$this->db_query($qry))
{
$this->errormsg = 'Fehler bei einer Datenbankabfrage';
return false;
}
if($row = $this->db_fetch_object())
{
$this->adresse_id = $row->adresse_id;
$this->heimatadresse = $this->db_parse_bool($row->heimatadresse);
$this->zustelladresse = $this->db_parse_bool($row->zustelladresse);
$this->gemeinde = $row->gemeinde;
$this->name = $row->name;
$this->nation = $row->nation;
$this->ort = $row->ort;
$this->person_id = $row->person_id;
$this->plz = $row->plz;
$this->strasse = $row->strasse;
$this->typ = $row->typ;
$this->updateamum = $row->updateamum;
$this->updatevon = $row->updatevon;
$this->insertamum = $row->insertamum;
$this->insertvon = $row->insertvon;
$this->firma_id = $row->firma_id;
$this->rechnungsadresse = $this->db_parse_bool($row->rechnungsadresse);
$this->anmerkung = $row->anmerkung;
$this->co_name = $row->co_name;
}
else
{
$this->errormsg = 'Es ist kein Datensatz mit dieser ID vorhanden';
return false;
}
return true;
}
}
?>
+2 -2
View File
@@ -48,7 +48,7 @@ class bisarchiv extends basis_db
*/
protected function validate()
{
if($this->studiensemster_kurzbz == null || mb_strlen($this->studiensemster_kurzbz) > 6)
if($this->studiensemester_kurzbz == null || mb_strlen($this->studiensemester_kurzbz) > 6)
{
$this->errormsg = "Studiensemester ist ungueltig";
return false;
@@ -107,7 +107,7 @@ class bisarchiv extends basis_db
return false;
$qry = "INSERT INTO bis.tbl_archiv (studiensemester_kurzbz, meldung, html, studiengang_kz, insertamum, insertvon, typ) VALUES ("
. $this->db_add_param($this->studiensemster_kurzbz) . ","
. $this->db_add_param($this->studiensemester_kurzbz) . ","
. $this->db_add_param($this->meldung) . ","
. $this->db_add_param($this->html) . ","
. $this->db_add_param($this->studiengang_kz) . ","
+172 -1
View File
@@ -2088,7 +2088,7 @@ class lehrveranstaltung extends basis_db
/**
* lädt die Lehrveranstaltungen zum zugehörigen Mitarbeiter
* @param String $uid User ID des Mitarbeiters
* @param String $studiensemster_kurzbz Kurzbezeichnung des Studiensemesters
* @param String $studiensemester_kurzbz Kurzbezeichnung des Studiensemesters
*/
public function getLVByMitarbeiter($uid, $studiensemester_kurzbz = null)
{
@@ -2911,5 +2911,176 @@ class lehrveranstaltung extends basis_db
return null;
}
/**
* Filtert die geladenen Lehrveranstaltungen gegen den aktuellen Studienplan des Studenten.
* Ermittelt den Studienplan über den Prestudent-Status (aktuellster Eintrag) und gleicht
* die vorhandenen IDs mit der Tabelle tbl_studienplan_lehrveranstaltung ab.
* Die interne Liste wird nur bei gefundenen Treffern aktualisiert.
*
* @param string $student_uid Student uid.
* @return bool True bei erfolgreicher Abwicklung oder leeren Ausgangsdaten, False bei Datenbankfehlern.
*/
public function checkLvaAgainstStudentCurrentStudienplan($student_uid) {
$qryStudent = "SELECT prestudent_id FROM tbl_student WHERE student_uid = ".$this->db_add_param($student_uid)."";
if($result = $this->db_query($qryStudent))
{
if($row = $this->db_fetch_object($result))
{
$prestudent_id = $row->prestudent_id;
}
else
{
$this->errormsg = 'Fehler beim Ermitteln der des aktuellen studienplan für studenten über prestudent';
return false;
}
}
else
{
$this->errormsg='Fehler bei Datenbankabfrage ' .$this->db_last_error();
return false;
}
$qryHistory = "SELECT studienplan_id
FROM public.tbl_prestudentstatus
WHERE prestudent_id = ".$this->db_add_param($prestudent_id)."
ORDER BY public.tbl_prestudentstatus.datum DESC";
if($result = $this->db_query($qryHistory))
{
if($row = $this->db_fetch_object($result))
{
$studienplan_id = $row->studienplan_id;
}
else
{
$this->errormsg = 'Fehler beim Ermitteln der des aktuellen studienplan für studenten über prestudentstatus';
return false;
}
}
else
{
$this->errormsg='Fehler bei Datenbankabfrage ' .$this->db_last_error();
return false;
}
// needs to have lva_ids from previous query in this context
$lvsIDs = array_column($this->lehrveranstaltungen, 'lehrveranstaltung_id');
if (empty($lvsIDs)) {
// exit without modifying object state if no lva_id to query by are found
return true;
}
$ids = $this->db_implode4SQL($lvsIDs);
$qry = "SELECT tbl_lehrveranstaltung.*,
tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id,
tbl_studienplan_lehrveranstaltung.semester as stpllv_semester,
tbl_studienplan_lehrveranstaltung.pflicht as stpllv_pflicht,
tbl_studienplan_lehrveranstaltung.koordinator as stpllv_koordinator,
tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id_parent,
tbl_studienplan_lehrveranstaltung.sort stpllv_sort,
tbl_studienplan_lehrveranstaltung.curriculum,
tbl_studienplan_lehrveranstaltung.export,
tbl_studienplan_lehrveranstaltung.genehmigung
FROM lehre.tbl_lehrveranstaltung
JOIN lehre.tbl_studienplan_lehrveranstaltung
USING(lehrveranstaltung_id)
WHERE tbl_studienplan_lehrveranstaltung.studienplan_id = ".$this->db_add_param($studienplan_id)."
AND tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id IN (".$ids.");";
if($result = $this->db_query($qry))
{
// reset the lva array and fill it with the new set
$temp_lvas = array();
while($row = $this->db_fetch_object($result))
{
$obj = new lehrveranstaltung();
$obj->lehrveranstaltung_id = $row->lehrveranstaltung_id;
$obj->studiengang_kz = $row->studiengang_kz;
$obj->bezeichnung = $row->bezeichnung;
$obj->kurzbz = $row->kurzbz;
$obj->lehrform_kurzbz = $row->lehrform_kurzbz;
$obj->semester = $row->semester;
$obj->ects = $row->ects;
$obj->semesterstunden = $row->semesterstunden;
$obj->anmerkung = $row->anmerkung;
$obj->lehre = $this->db_parse_bool($row->lehre);
$obj->lehreverzeichnis = $row->lehreverzeichnis;
$obj->aktiv = $this->db_parse_bool($row->aktiv);
$obj->ext_id = $row->ext_id;
$obj->insertamum = $row->insertamum;
$obj->insertvon = $row->insertvon;
$obj->planfaktor = $row->planfaktor;
$obj->planlektoren = $row->planlektoren;
$obj->planpersonalkosten = $row->planpersonalkosten;
$obj->plankostenprolektor = $row->plankostenprolektor;
$obj->updateamum = $row->updateamum;
$obj->updatevon = $row->updatevon;
$obj->sprache = $row->sprache;
$obj->sort = $row->sort;
$obj->incoming = $row->incoming;
$obj->zeugnis = $this->db_parse_bool($row->zeugnis);
$obj->projektarbeit = $this->db_parse_bool($row->projektarbeit);
$obj->koordinator = $row->koordinator;
$obj->bezeichnung_english = $row->bezeichnung_english;
$obj->orgform_kurzbz = $row->orgform_kurzbz;
$obj->lehrtyp_kurzbz = $row->lehrtyp_kurzbz;
$obj->lehrmodus_kurzbz = $row->lehrmodus_kurzbz;
$obj->oe_kurzbz = $row->oe_kurzbz;
$obj->raumtyp_kurzbz = $row->raumtyp_kurzbz;
$obj->anzahlsemester = $row->anzahlsemester;
$obj->semesterwochen = $row->semesterwochen;
$obj->lvnr = $row->lvnr;
$obj->semester_alternativ = $row->semester_alternativ;
$obj->farbe = $row->farbe;
$obj->lehrveranstaltung_template_id = $row->lehrveranstaltung_template_id;
$obj->stpllv_sort = $row->stpllv_sort;
$obj->benotung = $this->db_parse_bool($row->benotung);
$obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$obj->bezeichnung_arr['German'] = $row->bezeichnung;
$obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
if ($obj->bezeichnung_arr['English'] == '')
$obj->bezeichnung_arr['English'] = $obj->bezeichnung_arr['German'];
$obj->sws = $row->sws;
$obj->lvs = $row->lvs;
$obj->alvs = $row->alvs;
$obj->lvps = $row->lvps;
$obj->las = $row->las;
$obj->stpllv_semester = $row->stpllv_semester;
$obj->stpllv_pflicht = $this->db_parse_bool($row->stpllv_pflicht);
$obj->stpllv_koordinator = $row->stpllv_koordinator;
$obj->studienplan_lehrveranstaltung_id = $row->studienplan_lehrveranstaltung_id;
$obj->studienplan_lehrveranstaltung_id_parent = $row->studienplan_lehrveranstaltung_id_parent;
$obj->curriculum = $this->db_parse_bool($row->curriculum);
$obj->export = $this->db_parse_bool($row->export);
$obj->genehmigung = $this->db_parse_bool($row->genehmigung);
$obj->new = false;
$temp_lvas[] = $obj;
}
// Only update the class property if we actually found matching records.
// If $temp_lvas is empty, $this->lehrveranstaltungen remains unchanged.
if (!empty($temp_lvas)) {
$this->lehrveranstaltungen = $temp_lvas;
}
return true;
}
else
{
$this->errormsg='Fehler bei Datenbankabfrage ' .$this->db_last_error();
return false;
}
}
}
?>
+24 -9
View File
@@ -37,6 +37,8 @@ class projektarbeit extends basis_db
public $titel; // string
public $titel_english; // string
public $lehreinheit_id; // integer
public $lehrveranstaltung_id; // integer
public $studiensemester_kurzbz; // string
public $student_uid; // integer
public $firma_id; // integer
public $note; // integer
@@ -129,6 +131,8 @@ class projektarbeit extends basis_db
$this->titel = $row->titel;
$this->titel_english = $row->titel_english;
$this->lehreinheit_id = $row->lehreinheit_id;
$this->lehrveranstaltung_id = $row->lehrveranstaltung_id;
$this->studiensemester_kurzbz = $row->studiensemester_kurzbz;
$this->student_uid = $row->student_uid;
$this->firma_id = $row->firma_id;
$this->note = $row->note;
@@ -178,9 +182,13 @@ class projektarbeit extends basis_db
{
$this->errormsg='Projekttyp_kurzbz darf nicht NULL sein!';
}
if ($this->lehreinheit_id==null)
if ($this->lehrveranstaltung_id==null)
{
$this->errormsg='Lehreinheit_id darf nicht NULL sein!';
$this->errormsg='Lehrveranstaltung_id darf nicht NULL sein!';
}
if ($this->studiensemester_kurzbz==null)
{
$this->errormsg='Studiensemester_kurzbz darf nicht NULL sein!';
}
if(mb_strlen($this->projekttyp_kurzbz)>16)
{
@@ -261,12 +269,15 @@ class projektarbeit extends basis_db
{
//Neuen Datensatz einfuegen
$qry='BEGIN; INSERT INTO lehre.tbl_projektarbeit (projekttyp_kurzbz, titel, lehreinheit_id, student_uid, firma_id, note, punkte,
$qry='BEGIN; INSERT INTO lehre.tbl_projektarbeit (projekttyp_kurzbz, titel, lehreinheit_id,
lehrveranstaltung_id, studiensemester_kurzbz, student_uid, firma_id, note, punkte,
beginn, ende, faktor, freigegeben, gesperrtbis, stundensatz, gesamtstunden, themenbereich, anmerkung,
insertamum, insertvon, updateamum, updatevon, titel_english, final) VALUES('.
$this->db_add_param($this->projekttyp_kurzbz).', '.
$this->db_add_param($this->titel).', '.
$this->db_add_param($this->lehreinheit_id, FHC_INTEGER).', '.
$this->db_add_param($this->lehrveranstaltung_id, FHC_INTEGER).', '.
$this->db_add_param($this->studiensemester_kurzbz).', '.
$this->db_add_param($this->student_uid).', '.
$this->db_add_param($this->firma_id, FHC_INTEGER).', '.
$this->db_add_param($this->note).', '.
@@ -301,6 +312,8 @@ class projektarbeit extends basis_db
'titel='.$this->db_add_param($this->titel).', '.
'titel_english='.$this->db_add_param($this->titel_english).', '.
'lehreinheit_id='.$this->db_add_param($this->lehreinheit_id, FHC_INTEGER).', '.
'lehrveranstaltung_id='.$this->db_add_param($this->lehrveranstaltung_id, FHC_INTEGER).', '.
'studiensemester_kurzbz='.$this->db_add_param($this->studiensemester_kurzbz).', '.
'student_uid='.$this->db_add_param($this->student_uid).', '.
'firma_id='.$this->db_add_param($this->firma_id, FHC_INTEGER).', '.
'note='.$this->db_add_param($this->note).', '.
@@ -406,6 +419,8 @@ class projektarbeit extends basis_db
$obj->titel = $row->titel;
$obj->titel_english = $row->titel_english;
$obj->lehreinheit_id = $row->lehreinheit_id;
$obj->lehrveranstaltung_id = $row->lehrveranstaltung_id;
$obj->studiensemester_kurzbz = $row->studiensemester_kurzbz;
$obj->student_uid = $row->student_uid;
$obj->firma_id = $row->firma_id;
$obj->note = $row->note;
@@ -449,14 +464,13 @@ class projektarbeit extends basis_db
tbl_projektarbeit.* , tbl_projekttyp.bezeichnung
FROM
lehre.tbl_projektarbeit
JOIN
lehre.tbl_projekttyp USING (projekttyp_kurzbz), lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung
JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
JOIN lehre.tbl_lehrveranstaltung ON (lehre.tbl_projektarbeit.lehrveranstaltung_id = lehre.tbl_lehrveranstaltung.lehrveranstaltung_id)
WHERE
tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_lehrveranstaltung.studiengang_kz=".$this->db_add_param($studiengang_kz, FHC_INTEGER)." AND
tbl_lehreinheit.studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz);
tbl_projektarbeit.studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz);
if($this->db_query($qry))
{
@@ -470,6 +484,8 @@ class projektarbeit extends basis_db
$obj->titel = $row->titel;
$obj->titel_english = $row->titel_english;
$obj->lehreinheit_id = $row->lehreinheit_id;
$obj->lehrveranstaltung_id = $row->lehrveranstaltung_id;
$obj->studiensemester_kurzbz = $row->studiensemester_kurzbz;
$obj->student_uid = $row->student_uid;
$obj->firma_id = $row->firma_id;
$obj->note = $row->note;
@@ -534,7 +550,6 @@ class projektarbeit extends basis_db
pa.projekttyp_kurzbz
FROM
lehre.tbl_projektarbeit pa
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
JOIN public.tbl_studiensemester sem USING(studiensemester_kurzbz)
LEFT JOIN (
SELECT
+11 -15
View File
@@ -121,7 +121,7 @@ class vertrag extends basis_db
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
vertrag_id=tbl_vertrag.vertrag_id
AND studiensemester_kurzbz in (SELECT studiensemester_kurzbz FROM public.tbl_studiensemester WHERE start>=".$this->db_add_param($datum).")
AND studiensemester_kurzbz in (SELECT studiensemester_kurzbz FROM public.tbl_studiensemester WHERE start>".$this->db_add_param($datum).")
)
)";
}
@@ -371,14 +371,14 @@ class vertrag extends basis_db
null::integer as pruefung_id,
projektarbeit_id,
(tbl_projektbetreuer.stunden*tbl_projektbetreuer.stundensatz) as betrag,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
tbl_projektbetreuer.betreuerart_kurzbz,
(SELECT nachname || ' ' || vorname FROM public.tbl_person JOIN public.tbl_benutzer USING(person_id) WHERE uid=tbl_projektarbeit.student_uid)
as bezeichnung
FROM
lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
tbl_projektbetreuer.person_id=".$this->db_add_param($person_id, FHC_INTEGER)."
AND vertrag_id IS NULL";
@@ -461,9 +461,8 @@ class vertrag extends basis_db
OR
EXISTS (SELECT 1 FROM lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($studiensemester_kurzbz)."
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($studiensemester_kurzbz)."
AND tbl_projektbetreuer.person_id=tbl_person.person_id
AND tbl_projektbetreuer.vertrag_id IS NULL
)
@@ -535,12 +534,12 @@ class vertrag extends basis_db
UNION
SELECT
'Betreuung' as type,
tbl_projektarbeit.lehreinheit_id as lehreinheit_id,
NULL as lehreinheit_id,
null as mitarbeiter_uid,
null::integer as pruefung_id,
projektarbeit_id,
(tbl_projektbetreuer.stunden*tbl_projektbetreuer.stundensatz) as betrag,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
tbl_projektbetreuer.betreuerart_kurzbz,
(SELECT nachname || ' ' || vorname FROM public.tbl_person JOIN public.tbl_benutzer USING(person_id) WHERE uid=tbl_projektarbeit.student_uid)
as bezeichnung,
@@ -549,7 +548,6 @@ class vertrag extends basis_db
FROM
lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
JOIN lehre.tbl_vertrag USING (vertrag_id)
WHERE
vertrag_id=".$this->db_add_param($vertrag_id, FHC_INTEGER).";";
@@ -942,17 +940,16 @@ class vertrag extends basis_db
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
vertrag_id=tbl_vertrag.vertrag_id
AND studiensemester_kurzbz=".$this->db_add_param($stsem).")
AND lehre.tbl_lehreinheit.studiensemester_kurzbz=".$this->db_add_param($stsem).")
OR
EXISTS (SELECT
1
FROM
lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
vertrag_id=tbl_vertrag.vertrag_id
AND studiensemester_kurzbz=".$this->db_add_param($stsem).")
AND lehre.tbl_projektarbeit.studiensemester_kurzbz=".$this->db_add_param($stsem).")
OR
(NOT EXISTS (SELECT
1
@@ -980,7 +977,7 @@ class vertrag extends basis_db
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
vertrag_id=tbl_vertrag.vertrag_id
AND studiensemester_kurzbz=".$this->db_add_param($stsem).")
AND lehre.tbl_lehreinheit.studiensemester_kurzbz=".$this->db_add_param($stsem).")
)
AND tbl_benutzer.uid=".$this->db_add_param($mitarbeiter_uid);
@@ -1077,7 +1074,7 @@ class vertrag extends basis_db
JOIN lehre.tbl_lehreinheitmitarbeiter USING(vertrag_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz)."
lehre.tbl_lehreinheit.studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz)."
AND tbl_lehreinheitmitarbeiter.mitarbeiter_uid NOT IN(SELECT uid FROM public.tbl_benutzer WHERE person_id=tbl_vertrag.person_id)
UNION
SELECT
@@ -1086,9 +1083,8 @@ class vertrag extends basis_db
lehre.tbl_vertrag
JOIN lehre.tbl_projektbetreuer USING(vertrag_id)
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz)."
tbl_projektarbeit.studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz)."
AND tbl_projektbetreuer.person_id!=tbl_vertrag.person_id";
if($result = $this->db_query($qry))
+6 -9
View File
@@ -748,17 +748,14 @@ or not exists
UNION
SELECT sum(pb.stunden) AS semstunden
FROM
lehre.tbl_projektarbeit pa,
lehre.tbl_projektbetreuer pb,
public.tbl_benutzer b,
lehre.tbl_lehreinheit l
JOIN
lehre.tbl_lehrveranstaltung lv using (lehrveranstaltung_id)
JOIN
public.tbl_studiengang s using (studiengang_kz)
lehre.tbl_projektarbeit pa
JOIN lehre.tbl_projektbetreuer pb ON(pa.projektarbeit_id = pb.projektarbeit_id)
JOIN public.tbl_benutzer b ON(pb.person_id = b.person_id)
JOIN lehre.tbl_lehreinheit l ON(pa.lehrveranstaltung_id = l.lehrveranstaltung_id AND pa.studiensemester_kurzbz = l.studiensemester_kurzbz)
JOIN lehre.tbl_lehrveranstaltung lv ON (l.lehrveranstaltung_id = lv.lehrveranstaltung_id)
JOIN public.tbl_studiengang s USING (studiengang_kz)
WHERE
pa.lehreinheit_id = l.lehreinheit_id AND
pb.projektarbeit_id = pa.projektarbeit_id AND
pb.person_id = b.person_id AND
b.uid = ".$this->db_add_param($user)." AND
+16
View File
@@ -0,0 +1,16 @@
{
"scripts": {
"noten:check": "node tests/cypress/tools/dbCheck.js noten",
"noten:api": "cypress run --spec \"tests/cypress/e2e/specs/api/**/*.cy.js\"",
"noten:ui": "cypress run --browser chrome --spec \"tests/cypress/e2e/specs/ui/**/*.cy.js\""
},
"//": [
"pg: the suite resets and seeds the test database directly (tasks/db.js, tools/dbCheck.js)",
"dotenv: cypress.config.js and the suite read URL, accounts and DB access from the .env files"
],
"dependencies": {
"cypress": "^15.21.1",
"dotenv": "^17.4.2",
"pg": "^8.13.1"
}
}
+178
View File
@@ -0,0 +1,178 @@
/* Pinned columns (tableLayout.js). The four columns that can be pinned always have .sticky-col.
A `sticky-on-<field>` class on #notentable pins one of them, and --sl-<field> is its left offset,
so that the pinned columns stand next to each other. */
#notentable .tabulator-header .tabulator-col.sticky-col,
#notentable .tabulator-tableholder .tabulator-row .tabulator-cell.sticky-col {
background-color: #fff; /* the cells below do not show through */
}
/* also on row hover */
#notentable .tabulator-row:hover .tabulator-cell.sticky-col {
background-color: #ccc;
}
/* the container pins a column */
#notentable.sticky-on-selectCol .sticky-col[tabulator-field="selectCol"],
#notentable.sticky-on-uid .sticky-col[tabulator-field="uid"],
#notentable.sticky-on-vorname .sticky-col[tabulator-field="vorname"],
#notentable.sticky-on-nachname .sticky-col[tabulator-field="nachname"] {
position: sticky;
z-index: 10; /* above the Pruefung columns */
border-right: 1px solid #ddd;
}
#notentable.sticky-on-selectCol .sticky-col[tabulator-field="selectCol"] { left: var(--sl-selectCol, 0); }
#notentable.sticky-on-uid .sticky-col[tabulator-field="uid"] { left: var(--sl-uid, 0); }
#notentable.sticky-on-vorname .sticky-col[tabulator-field="vorname"] { left: var(--sl-vorname, 0); }
#notentable.sticky-on-nachname .sticky-col[tabulator-field="nachname"] { left: var(--sl-nachname, 0); }
/* the content of a cell, from the formatters in Benotungstool.js */
#notentable .cell-start,
#notentable .cell-center {
display: flex;
align-items: center;
height: 100%;
}
#notentable .cell-start { justify-content: start; }
#notentable .cell-center { justify-content: center; }
/* a Zeugnisnote that differs from the LV-Note */
#notentable .zeugnisnote-differs {
color: red;
border: 1px solid red;
}
/* a proposal that the user cannot change */
#notentable .proposal-locked {
color: gray;
font-style: italic;
background-color: #f0f0f0;
pointer-events: none;
opacity: 0.6;
user-select: none;
cursor: not-allowed;
}
#notentable .teilnote-negativ { color: red; }
#notentable .freigegeben-icon { color: green; }
#notentable .mail-icon { color: #00649C; }
/* the editable proposal columns */
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="proposed_punkte"] {
position: relative;
background-color: rgba(255, 255, 157, 0.73);
}
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="proposed_note"] {
position: relative;
background-color: rgba(255, 255, 157, 0.73);
cursor: pointer;
}
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="proposed_note"]::after {
content: "▾";
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%) rotate(0deg);
transform-origin: center;
color: rgba(120, 120, 60, 0.85);
font-size: 0.9rem;
line-height: 1;
pointer-events: none;
transition: transform 0.15s ease;
}
/* the arrow points up while the list is open */
#notentable .tabulator-tableholder .tabulator-cell.tabulator-editing[tabulator-field="proposed_note"]::after {
transform: translateY(-50%) rotate(180deg);
}
/* A Pruefung cell: fixed tracks for Note | date | action, so that they align in all rows. */
.pruefung-cell {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr) 6.5rem; /* Note | action */
align-items: center;
column-gap: 0.5rem;
height: 100%;
padding-left: 44px;
padding-right: 6px;
text-align: left;
}
.pruefung-cell.with-datum {
grid-template-columns: minmax(0, 1fr) 4.75rem 6.5rem; /* Note | datum | action */
}
.pruefung-cell.without-action {
grid-template-columns: minmax(0, 1fr); /* Note */
}
.pruefung-cell.without-action.with-datum {
grid-template-columns: minmax(0, 1fr) 4.75rem; /* Note | datum */
}
.pruefung-cell .pruefung-note {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* the reason instead of a button, for example after a pass */
.pruefung-cell .pruefung-hint {
grid-column: 1 / -1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.8rem;
font-style: italic;
opacity: 0.7;
cursor: help;
}
.pruefung-cell .pruefung-datum {
font-size: 0.75rem;
white-space: nowrap;
text-align: right;
opacity: 0.7;
}
/* always the last track, also in an empty cell with only the add button */
.pruefung-cell .pruefung-action {
grid-column: -2 / -1;
display: flex;
justify-content: flex-end;
}
.pruefung-cell .pruefung-action .btn {
white-space: nowrap;
}
.pruefung-badge::before {
content: attr(data-antritt);
position: absolute;
left: 6px;
top: 50%;
transform: translateY(-50%);
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
padding: 0 5px;
border-radius: 10px;
color: #fff;
font-size: 0.75rem;
font-weight: bold;
white-space: nowrap;
}
.pruefung-badge.antritt-1::before { background: #0072B2; }
.pruefung-badge.antritt-2::before { background: #E69F00; }
.pruefung-badge.antritt-3::before { background: #CC79A7; }
.pruefung-badge.antritt-k::before { background: #D55E00; }
.pruefung-badge.antritt-none::before { background: #767676; }
@@ -107,4 +107,9 @@
.gesperrtoption {
color: gray;
}
#onboardingRow
{
margin-bottom: 15px;
}
+125
View File
@@ -0,0 +1,125 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
getCisConfig() {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getCisConfig'
};
},
getNoten() {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getNoten'
};
},
getBenotungstoolContext(sem_kurzbz, lv_id = null) {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getBenotungstoolContext',
params: { sem_kurzbz, lv_id }
};
},
getLvForStudiengang(studiengang_kz, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getLvForStudiengang',
params: { studiengang_kz, sem_kurzbz }
};
},
getLehreinheitenForLv(lv_id, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getLehreinheitenForLv',
params: { lv_id, sem_kurzbz }
};
},
getLektorenForLehreinheit(lv_id, sem_kurzbz, lehreinheit_id) {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getLektorenForLehreinheit',
params: { lv_id, sem_kurzbz, lehreinheit_id }
};
},
// -> { students, domain }
getStudentenNoten(lv_id, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getStudentenNoten',
params: { lv_id, sem_kurzbz }
};
},
getNoteByPunkte(lv_id, sem_kurzbz, punkte) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/getNoteByPunkte',
params: { lv_id, sem_kurzbz, punkte }
};
},
// Each write answers { <uid>: { lvgesamtnote, verlauf, pruefung } }. A rejected row of a bulk
// write is { <uid>: { error: { code, message } } }.
// datum: the day of Antritt 1 (YYYY-MM-DD); without it the server takes today
saveLvNote(lv_id, sem_kurzbz, student_uid, note, punkte, datum) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveLvNote',
params: { lv_id, sem_kurzbz, student_uid, note, punkte, datum }
};
},
// lv_noten: [{ uid, note, punkte }]
importLvNoten(lv_id, sem_kurzbz, lv_noten) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/importLvNoten',
params: { lv_id, sem_kurzbz, lv_noten }
};
},
// pruefung: { pruefung_id (null = a new Pruefung), lehreinheit_id, datum, note, punkte, mitarbeiter_uid }
savePruefung(lv_id, sem_kurzbz, student_uid, pruefung) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/savePruefung',
params: { lv_id, sem_kurzbz, student_uid, ...pruefung }
};
},
// students: [{ uid, lehreinheit_id }]; pruefung: { datum, note, punkte, mitarbeiter_uid }
createPruefungen(lv_id, sem_kurzbz, students, pruefung) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/createPruefungen',
params: { lv_id, sem_kurzbz, students, ...pruefung }
};
},
// pruefungen: [{ uid, lehreinheit_id, datum, note, punkte }]
importPruefungen(lv_id, sem_kurzbz, pruefungen) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/importPruefungen',
params: { lv_id, sem_kurzbz, pruefungen }
};
},
saveFreigabe(lv_id, sem_kurzbz, password, uids) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveFreigabe',
params: { lv_id, sem_kurzbz, password, uids }
};
}
};
+9 -1
View File
@@ -17,6 +17,7 @@ import AbgabetoolMitarbeiter from "../../components/Cis/Abgabetool/AbgabetoolMit
import AbgabetoolAssistenz from "../../components/Cis/Abgabetool/AbgabetoolAssistenz.js";
import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.js";
import Studium from "../../components/Cis/Studium/Studium.js";
import Benotungstool from "../../components/Cis/Benotungstool/Benotungstool.js";
import StgOrgLvPlan from "../../components/Cis/LvPlan/StgOrg.js";
import OtherLvPlan from "../../components/Cis/LvPlan/OtherLvPlan.js";
@@ -77,6 +78,13 @@ const router = VueRouter.createRouter({
component: Raumsuche,
props: true
},
{
// semester first: a URL without a course must not read the semester as lv_id
path: `/Cis/Benotungstool/:sem_kurzbz?/:lv_id?`,
name: 'Benotungstool',
component: Benotungstool,
props: true
},
// Redirect old links to new format
{
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
@@ -152,7 +160,7 @@ const router = VueRouter.createRouter({
path: `/Cis/MyLv/:studiensemester?`,
name: 'MyLv',
component: MylvStudent,
props: true,
props: true
},
{
path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`,
+1 -1
View File
@@ -148,4 +148,4 @@ export default {
</div>
</div>
`
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,100 @@
import BsModal from '../../Bootstrap/Modal.js';
/**
* The Freigabe dialog: the changed LV-Noten and the password. It emits the password; the parent
* writes the Freigabe.
*
* open() -> 'save-freigabe' (password)
*/
export default {
name: "BenotungstoolFreigabeDialog",
components: {
BsModal,
Password: primevue.password
},
props: {
// the rows with an LV-Note that changed since the last Freigabe
lvNoten: { type: Array, default: () => [] },
notenOptions: { type: Array, default: () => [] }
},
emits: ['save-freigabe'],
data() {
return {
visible: false,
password: ''
};
},
computed: {
/** The Freigabe writes the LV-Note, not the Zeugnisnote. So the dialog shows both. */
summary() {
return this.lvNoten.map(s => ({
uid: s.uid,
name: `${s.vorname} ${s.nachname}`,
zeugnisnote: this.bezeichnungOf(s.zeugnisnote) ?? s.zeugnisnote ?? '—',
lvNote: this.bezeichnungOf(s.lv_note) ?? s.lv_note ?? '—',
changed: (s.zeugnisnote ?? '') != (s.lv_note ?? '')
}));
}
},
methods: {
open() {
this.$refs.modal.show();
},
submit() {
const password = this.password;
// the password leaves the memory with the dialog
this.password = '';
this.$refs.modal.hide();
this.$emit('save-freigabe', password);
},
bezeichnungOf(note) {
return this.notenOptions.find(n => n.note == note)?.bezeichnung;
}
},
template: `
<bs-modal data-cy="modal-freigabe" ref="modal" class="bootstrap-prompt" dialogClass="modal-lg" bodyClass="px-4 py-4"
@hideBsModal="visible = false"
@showBsModal="visible = true">
<template v-slot:title>{{ $p.t('benotungstool/noteneingabeSpeichern') }}</template>
<template v-slot:default>
<div v-html="$capitalize($p.t('benotungstool/notenfreigabeHinweistextv4'))"></div>
<div v-if="summary.length" class="mt-3">
<div class="fw-bold mb-1">{{ $p.t('benotungstool/c4freigabeSummaryHeading', [summary.length]) }}</div>
<div class="border rounded" style="max-height: 250px; overflow-y: auto;">
<table class="table table-sm table-hover align-middle mb-0">
<thead>
<tr>
<th class="bg-body sticky-top">{{ $capitalize($p.t('benotungstool/c4student')) }}</th>
<th class="bg-body sticky-top">{{ $capitalize($p.t('benotungstool/c4freigabeSummaryCurrent')) }}</th>
<th class="bg-body sticky-top"></th>
<th class="bg-body sticky-top">{{ $capitalize($p.t('benotungstool/c4freigabeSummaryReleased')) }}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in summary" :key="row.uid" :data-cy="'freigabe-row-' + row.uid">
<td>{{ row.name }} <span class="text-muted">({{ row.uid }})</span></td>
<td>{{ row.zeugnisnote }}</td>
<td class="text-center"><i class="fa fa-arrow-right"></i></td>
<td data-cy="freigabe-row-lvnote" :class="{ 'fw-bold text-success': row.changed }">{{ row.lvNote }}</td>
</tr>
</tbody>
</table>
</div>
<div class="small text-muted mt-1">{{ $p.t('benotungstool/c4freigabeSummaryLegend') }}</div>
</div>
<div v-else class="mt-3 text-center text-muted" data-cy="freigabe-summary-empty">{{ $p.t('benotungstool/c4freigabeSummaryEmpty') }}</div>
<!-- The password field exists only while the dialog is open. Else the password manager
offers its entries on every other text field, for example the date of the proposal. -->
<div v-if="visible" class="mt-3 d-flex justify-content-center" data-cy="freigabe-password">
<Password v-model="password" :feedback="false" showIcon="fa fa-eye" :toggleMask="true" :promptLabel="$p.t('benotungstool/passwort')"></Password>
</div>
</template>
<template v-slot:footer>
<button type="button" data-cy="freigabe-submit" class="btn btn-primary" :disabled="!summary.length" @click="submit">{{ $p.t('benotungstool/noteneingabeBestätigen') }}</button>
</template>
</bs-modal>
`
};
@@ -0,0 +1,204 @@
import BsModal from '../../Bootstrap/Modal.js';
import NotenlisteLinks from "./NotenlisteLinks.js";
import {dmyToIso, isoToDmy} from "../../../helpers/DateHelpers.js";
/**
* The two import dialogs. The user pastes lines from Excel: one row per line, the cells separated by a
* tab. The dialog finds the student and checks the format of each cell. It warns for a bad line and
* emits the other rows. The parent checks the Antritt chain and sends the rows.
*
* Pruefung import Kennung, Datum, Note -> 'import-pruefungen' [{ uid, lehreinheit_id, datum, note, punkte }]
* Noten import Kennung, Note -> 'import-lv-noten' [{ uid, note, punkte }]
*
* CIS_GESAMTNOTE_IMPORT_SPALTEN_* gives the column order. In the Punkte mode the Note cell holds Punkte.
*/
export default {
name: "BenotungstoolImport",
components: {
BsModal,
NotenlisteLinks,
Textarea: primevue.textarea
},
props: {
students: { type: Array, default: () => [] },
config: { type: Object, default: null },
notenOptions: { type: Array, default: () => [] },
lehrveranstaltung: { type: Object, default: null },
semKurzbz: { type: String, default: null },
lehreinheiten: { type: Array, default: () => [] },
selectedLehreinheit: { type: Object, default: null }
},
emits: ['import-pruefungen', 'import-lv-noten'],
data() {
return {
pruefungenText: '',
lvNotenText: ''
};
},
computed: {
pruefungenHint() {
return this.$capitalize(this.$p.t('benotungstool/notenimportHinweistextv6')) + this.kuerzelHint;
},
lvNotenHint() {
return this.$capitalize(this.$p.t('benotungstool/notenimportHinweistextv5')) + this.kuerzelHint;
},
/** One more bullet while the short form of tbl_note.anmerkung is allowed. */
kuerzelHint() {
if (!this.config?.CIS_GESAMTNOTE_IMPORT_NOTENKUERZEL) return '';
return '• ' + this.$capitalize(this.$p.t('benotungstool/c4importNotenkuerzel')) + '<br>';
}
},
methods: {
openPruefungen() {
this.$refs.modalPruefungen.show();
},
openLvNoten() {
this.$refs.modalLvNoten.show();
},
submitPruefungen() {
const rows = this.parseLines(this.pruefungenText, 3, 'c4importRowNotDateFormat', this.parsePruefungRow);
this.$refs.modalPruefungen.hide();
this.$emit('import-pruefungen', rows);
},
submitLvNoten() {
const rows = this.parseLines(this.lvNotenText, 2, 'c4importRowNotNoteFormat', this.parseLvNoteRow);
this.$refs.modalLvNoten.hide();
this.$emit('import-lv-noten', rows);
},
/** Splits the text into rows. A line with the wrong number of cells names only its line number. */
parseLines(text, cellCount, formatPhrase, parseRow) {
const rows = [];
text.split('\n').forEach((line, index) => {
if (line.trim() === '') return;
const lineNr = index + 1;
const cells = line.split('\t');
if (cells.length !== cellCount) {
this.$fhcAlert.alertWarning(this.$p.t('benotungstool/' + formatPhrase, [lineNr]));
return;
}
const row = parseRow(cells, lineNr);
if (row) rows.push(row);
});
return rows;
},
parsePruefungRow(cells, lineNr) {
const kennung = cells[this.columnIndex('pruefung', 'kennung')];
const student = this.findStudent(kennung);
if (!student) return this.warnLine('c4importNoStudentFoundForIdInRow', kennung, lineNr);
const datum = this.parseDatum(cells[this.columnIndex('pruefung', 'datum')]);
if (!datum) return this.warnLine('c4importInvalidDateFoundForIdInRow', kennung, lineNr);
const value = this.parseNoteCell(cells[this.columnIndex('pruefung', 'note')]);
if (!value) return this.warnLine('c4importNoGradeFoundForIdInRow', kennung, lineNr);
return { uid: student.uid, lehreinheit_id: student.lehreinheit_id, datum, ...value };
},
parseLvNoteRow(cells, lineNr) {
const kennung = cells[this.columnIndex('noten', 'kennung')];
const student = this.findStudent(kennung);
if (!student) return this.warnLine('c4importNoStudentFoundForIdInRow', kennung, lineNr);
const value = this.parseNoteCell(cells[this.columnIndex('noten', 'note')]);
if (!value) return this.warnLine('c4importNoGradeFoundForIdInRow', kennung, lineNr);
return { uid: student.uid, ...value };
},
/** @param {'pruefung'|'noten'} kind */
columnIndex(kind, name) {
const key = kind === 'pruefung' ? 'CIS_GESAMTNOTE_IMPORT_SPALTEN_PRUEFUNG' : 'CIS_GESAMTNOTE_IMPORT_SPALTEN_NOTEN';
return this.config[key].indexOf(name);
},
/** A Kennung is a uid (it starts with a letter) or a Matrikelnummer (it starts with a digit). */
findStudent(kennung) {
const value = String(kennung ?? '').trim();
if (/^[0-9]/.test(value)) return this.students.find(s => s.matrikelnr?.trim() === value);
if (/^[a-zA-Z]/.test(value)) return this.students.find(s => s.uid?.trim() === value);
return null;
},
/** @returns {string|null} 'yyyy-MM-dd', or null if the value does not match CIS_GESAMTNOTE_IMPORT_DATUMSFORMAT */
parseDatum(value) {
const text = String(value ?? '').trim();
if (this.config.CIS_GESAMTNOTE_IMPORT_DATUMSFORMAT === 'yyyy-MM-dd') {
// the round trip rejects a day that does not exist, for example 2026-02-31
return dmyToIso(isoToDmy(text)) === text ? text : null;
}
return dmyToIso(text);
},
/**
* The Note cell: in the Punkte mode a number (a decimal comma counts as a point), else a Note of
* the Lehre, by its key or, with CIS_GESAMTNOTE_IMPORT_NOTENKUERZEL, by its short form.
*
* @returns {{note, punkte}|null}
*/
parseNoteCell(value) {
const text = String(value ?? '').trim();
if (this.config.CIS_GESAMTNOTE_PUNKTE) return { note: null, punkte: Number.parseFloat(text.replace(',', '.')) };
const option = this.findNote(text);
return option?.lehre ? { note: option.note, punkte: null } : null;
},
findNote(text) {
if (text === '') return null;
const byKey = this.notenOptions.find(n => String(n.note).trim() === text);
if (byKey || !this.config.CIS_GESAMTNOTE_IMPORT_NOTENKUERZEL) return byKey ?? null;
const byKuerzel = this.notenOptions.filter(n => String(n.anmerkung ?? '').trim().toLowerCase() === text.toLowerCase());
return byKuerzel.length === 1 ? byKuerzel[0] : null;
},
warnLine(phrase, kennung, lineNr) {
this.$fhcAlert.alertWarning(this.$p.t('benotungstool/' + phrase, [kennung, lineNr]));
return null;
}
},
template: `
<bs-modal data-cy="modal-pruefung-import" ref="modalPruefungen" class="bootstrap-prompt" dialogClass="modal-lg" bodyClass="px-4 py-4">
<template v-slot:title>{{ $capitalize($p.t('benotungstool/c4pruefungImportieren')) }}</template>
<template v-slot:default>
<div v-html="pruefungenHint"></div>
<Textarea data-cy="pruefung-import-text" v-model="pruefungenText" rows="5" class="w-100 mt-3" :placeholder="$p.t('benotungstool/c4importPlaceholder')"></Textarea>
<NotenlisteLinks class="mt-3" :lehrveranstaltung="lehrveranstaltung" :sem_kurzbz="semKurzbz" :lehreinheiten="lehreinheiten" :selected-lehreinheit="selectedLehreinheit" />
</template>
<template v-slot:footer>
<button type="button" data-cy="pruefung-import-submit" class="btn btn-primary" @click="submitPruefungen">{{ $capitalize($p.t('benotungstool/c4import')) }}</button>
</template>
</bs-modal>
<bs-modal data-cy="modal-lvnoten-import" ref="modalLvNoten" class="bootstrap-prompt" dialogClass="modal-lg" bodyClass="px-4 py-4">
<template v-slot:title>{{ $capitalize($p.t('benotungstool/c4notenImportieren')) }}</template>
<template v-slot:default>
<div v-html="lvNotenHint"></div>
<Textarea data-cy="lvnoten-import-text" v-model="lvNotenText" rows="5" class="w-100 mt-3" :placeholder="$p.t('benotungstool/c4importNotePlaceholder')"></Textarea>
<NotenlisteLinks class="mt-3" :lehrveranstaltung="lehrveranstaltung" :sem_kurzbz="semKurzbz" :lehreinheiten="lehreinheiten" :selected-lehreinheit="selectedLehreinheit" />
</template>
<template v-slot:footer>
<button type="button" data-cy="lvnoten-import-submit" class="btn btn-primary" @click="submitLvNoten">{{ $capitalize($p.t('benotungstool/c4import')) }}</button>
</template>
</bs-modal>
`
};
@@ -0,0 +1,66 @@
// The links to the Excel Notenliste of the LV and of each Lehreinheit.
export const NotenlisteLinks = {
name: "NotenlisteLinks",
props: {
lehrveranstaltung: { type: Object, default: null },
sem_kurzbz: { type: String, default: null },
lehreinheiten: { type: Array, default: () => [] },
selectedLehreinheit: { type: Object, default: null }
},
computed: {
/** A selected Lehreinheit shows only its own link. */
shownLehreinheiten() {
if (this.selectedLehreinheit?.lehreinheit_id == null) return this.lehreinheiten;
return this.lehreinheiten.filter(le => le.lehreinheit_id === this.selectedLehreinheit.lehreinheit_id);
},
baseUrl() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'cis/private/lehre/notenliste.xls.php';
},
ready() {
const lv = this.lehrveranstaltung;
return !!(lv && lv.studiengang_kz != null && lv.lv_semester != null
&& lv.lehrveranstaltung_id != null && this.sem_kurzbz);
},
fullListUrl() {
if (!this.ready) return null;
return this.buildUrl();
}
},
methods: {
buildUrl(lehreinheit_id = null) {
const lv = this.lehrveranstaltung;
const params = new URLSearchParams({
stg: lv.studiengang_kz,
sem: lv.lv_semester,
lvid: lv.lehrveranstaltung_id,
stsem: this.sem_kurzbz
});
if (lehreinheit_id != null) params.set('lehreinheit_id', lehreinheit_id);
return this.baseUrl + '?' + params.toString();
}
},
template: `
<div v-if="ready">
<div class="fw-bold mb-2">{{ $capitalize($p.t('benotungstool/c4notenlisten')) }}</div>
<div class="mb-1">
<a class="Item" :href="fullListUrl" target="_blank" rel="noopener">
{{ $capitalize($p.t('benotungstool/c4gesamtliste')) }} {{ lehrveranstaltung.lv_bezeichnung }}
</a>
</div>
<div v-for="le in shownLehreinheiten" :key="le.lehreinheit_id" class="mb-1" style="padding-left: 1.5rem;">
<a class="Item" :href="buildUrl(le.lehreinheit_id)" target="_blank" rel="noopener">
{{ le.infoString }}
</a>
</div>
</div>
<div v-else class="text-muted">
{{ $capitalize($p.t('benotungstool/c4keineStudentenGefunden')) }}
</div>
`
};
export default NotenlisteLinks;
@@ -0,0 +1,295 @@
import BsModal from '../../Bootstrap/Modal.js';
import VueDatePicker from '../../vueDatepicker.js.php';
import ApiNoten from "../../../api/factory/noten.js";
import {debounce} from "../../../helpers/debounce.js";
import {today, toIsoDate, parseIsoDate, addDays} from "../../../helpers/DateHelpers.js";
/**
* The Pruefung dialog. It collects the input and emits it. The parent checks the Antritt chain and
* writes.
*
* openForCell(student, pruefung, field) a new or a changed Pruefung of one student -> 'save-pruefung' (student, pruefung)
* openForSelection() one new Pruefung on one day for the selection -> 'create-pruefungen' (pruefung)
*
* The table holds the selection. The multiselect emits 'select-students', the parent selects the rows,
* and the prop selectedStudents follows.
*/
export default {
name: "BenotungstoolPruefungDialog",
components: {
BsModal,
Datepicker: VueDatePicker,
Dropdown: primevue.dropdown,
InputNumber: primevue.inputnumber,
Multiselect: primevue.multiselect
},
props: {
config: { type: Object, default: null },
notenOptions: { type: Array, default: () => [] },
lvId: { default: null },
semKurzbz: { type: String, default: null },
// the students that can get a Pruefung, in the order of the table
studentOptions: { type: Array, default: () => [] },
selectedStudents: { type: Array, default: () => [] }
},
emits: ['save-pruefung', 'create-pruefungen', 'select-students'],
data() {
return {
// the student of the cell; null in the dialog for the selection
student: null,
pruefung: null,
selectedDate: today(),
selectedNote: null,
selectedPunkte: null,
// a later Pruefung locks the Note; the date stays editable
noteLocked: false,
dateMin: null,
dateMax: null,
// the Pruefung names the Lektor of the Lehreinheit, not the user; with several Lektoren the user selects one
lektoren: [],
selectedLektor: null,
debouncedShowNoteForPunkte: null
};
},
computed: {
title() {
if (!this.student) return this.$capitalize(this.$p.t('benotungstool/c4addNewPruefung'));
const phrase = this.pruefung ? 'benotungstool/editPruefungFor' : 'benotungstool/createPruefungFor';
return this.$capitalize(this.$p.t(phrase)) + ' ' + this.student.vorname + ' ' + this.student.nachname;
},
lehreNoten() {
return this.notenOptions.filter(n => n.lehre === true);
},
/** The students that get the Pruefung. */
students() {
return this.student ? [this.student] : this.selectedStudents;
},
/** The hint: for these students the new Pruefung also writes the LV-Note. */
withoutLvNote() {
return this.pruefung ? [] : this.students.filter(s => !s.verlauf.hasLvNote);
}
},
watch: {
/** Warns early if the selected Note reached its limit (verlauf.notenAtLimit); the server checks it too. */
selectedNote(option, previous) {
if (!option || !this.student) return;
// the changed Pruefung may keep its own Note
const limit = this.student.verlauf.notenAtLimit[option.note];
if (limit == null || option.note == this.pruefung?.note) return;
this.$fhcAlert.alertWarning(this.$capitalize(this.$p.t('benotungstool/c4noteLimitUeberschritten', [
option.bezeichnung, this.student.uid, limit
])));
this.selectedNote = previous;
}
},
created() {
this.debouncedShowNoteForPunkte = debounce(this.showNoteForPunkte, 500);
},
methods: {
openForCell(student, pruefung, field) {
this.student = student;
this.pruefung = pruefung;
this.loadLektoren([student.lehreinheit_id]);
// the 'antritt' column has no date, so a new Pruefung starts with today
const datum = pruefung?.datum ?? (/^\d{4}-\d{2}-\d{2}$/.test(field) ? field : null);
this.selectedDate = datum ? parseIsoDate(datum) : today();
this.selectedNote = pruefung ? this.notenOptions.find(n => n.note == pruefung.note) ?? null : null;
this.selectedPunkte = pruefung?.punkte ?? null;
// the server locks the Note of a Pruefung with a later one
this.noteLocked = !!pruefung?.note_locked;
const bounds = this.pruefungDateBounds(student, pruefung, field);
this.dateMin = bounds.min;
this.dateMax = bounds.max;
this.$refs.modal.show();
},
openForSelection() {
this.student = null;
this.pruefung = null;
this.loadLektoren(this.selectedStudents.map(s => s.lehreinheit_id));
// empty, without a Note
this.selectedDate = today();
this.selectedNote = null;
this.selectedPunkte = null;
this.noteLocked = false;
this.dateMin = null;
this.dateMax = null;
this.$refs.modal.show();
},
/**
* The date of a Pruefung stays strictly between its neighbours, so the order of the Antritte
* stays. A column without a date ('antritt_2') means a new Pruefung after all others.
*
* @returns {{min: Date|null, max: Date|null}} the inclusive bounds of the date picker
*/
pruefungDateBounds(student, pruefung, field) {
const own = String(pruefung?.datum ?? field ?? '').slice(0, 10);
const reference = /^\d{4}-\d{2}-\d{2}$/.test(own) ? own : '9999-12-31';
let lower = null;
let upper = null;
(student.verlauf.pruefungen ?? []).forEach(p => {
if (pruefung && p.pruefung_id === pruefung.pruefung_id) return;
const day = String(p.datum ?? '').slice(0, 10);
if (!day) return;
if (day < reference && (lower === null || day > lower)) lower = day;
if (day > reference && (upper === null || day < upper)) upper = day;
});
return {
min: lower ? addDays(parseIsoDate(lower), 1) : null,
max: upper ? addDays(parseIsoDate(upper), -1) : null
};
},
submit() {
if (this.student) this.submitForCell(); else this.submitForSelection();
},
submitForCell() {
// a typed date can pass the bounds of the date picker
if ((this.dateMin && this.selectedDate < this.dateMin) || (this.dateMax && this.selectedDate > this.dateMax)) {
this.$fhcAlert.alertWarning(this.$capitalize(this.$p.t('benotungstool/pruefungDatumOutOfRangeHint')));
return;
}
// Without a selection the Note stays empty; the server writes 'Noch nicht eingetragen'. Without
// the Punkte mode a 0 would be a value: the server derives the Note from it.
const pruefung = {
pruefung_id: this.pruefung?.pruefung_id ?? null,
lehreinheit_id: this.student.lehreinheit_id,
datum: toIsoDate(this.selectedDate),
note: this.noteLocked ? this.pruefung.note : (this.selectedNote?.note ?? ''),
punkte: this.config.CIS_GESAMTNOTE_PUNKTE ? (this.noteLocked ? this.pruefung.punkte : this.selectedPunkte) ?? null : null,
mitarbeiter_uid: this.selectedLektor
};
this.$refs.modal.hide();
this.$emit('save-pruefung', this.student, pruefung);
},
submitForSelection() {
// a missing LV-Note blocks nothing: the server writes it with the Note of this Pruefung
const pruefung = {
datum: toIsoDate(this.selectedDate),
note: this.selectedNote?.note ?? null,
punkte: this.selectedPunkte ?? null,
mitarbeiter_uid: this.selectedLektor
};
this.$refs.modal.hide();
this.$emit('create-pruefungen', pruefung);
},
/** The parent selects the rows in the table; the Lektoren follow the new selection. */
selectStudents(students) {
this.$emit('select-students', students);
this.loadLektoren(students.map(s => s.lehreinheit_id));
},
/** The Lektoren of the Lehreinheit. With exactly one Lektor the selection stays empty; the server sets that Lektor. */
loadLektoren(lehreinheitIds) {
this.lektoren = [];
this.selectedLektor = null;
// a Lektor selection makes sense only if all students share one Lehreinheit
const distinct = [...new Set(lehreinheitIds.filter(id => id != null))];
if (distinct.length !== 1) return;
this.$api.call(ApiNoten.getLektorenForLehreinheit(this.lvId, this.semKurzbz, distinct[0])).then(res => {
this.lektoren = res.data.map(l => ({
...l,
name: `${l.vorname ?? ''} ${l.nachname ?? ''}`.trim() || l.mitarbeiter_uid
}));
// a selection only if there is something to select
if (this.lektoren.length > 1) {
const current = this.pruefung?.mitarbeiter_uid;
this.selectedLektor = this.lektoren.some(l => l.mitarbeiter_uid === current) ? current : this.lektoren[0].mitarbeiter_uid;
}
});
},
/** In the Punkte mode the dialog shows the Note of the Punkte, from the Notenschluessel of the LV. */
showNoteForPunkte(event) {
const punkte = event.value === '' ? null : event.value;
this.$api.call(ApiNoten.getNoteByPunkte(this.lvId, this.semKurzbz, punkte)).then(res => {
if (res.data >= 0) this.selectedNote = this.notenOptions.find(n => n.note == res.data);
});
},
/** The multiselect label: "uid – Nachname Vorname – Antritte: n". */
studentLabel(student) {
return `${student.uid} – ${student.nachname} ${student.vorname} – ${this.$capitalize(this.$p.t('benotungstool/c4antrittCountv2'))}: ${student.verlauf.antrittCount}`;
}
},
template: `
<bs-modal data-cy="modal-pruefung" ref="modal" class="bootstrap-prompt" dialogClass="modal-lg" bodyClass="px-4 py-4">
<template v-slot:title>{{ title }}</template>
<template v-slot:default>
<div class="row align-items-center justify-content-center">
<div class="col-3 text-center">{{ $capitalize($p.t('benotungstool/c4date')) }}:</div>
<div class="col-6" data-cy="pruefung-datum">
<datepicker v-model="selectedDate" :clearable="false" :enableTimePicker="false" format="dd.MM.yyyy" placeholder="TT.MM.JJJJ"
:min-date="dateMin" :max-date="dateMax" :text-input="true" :auto-apply="true">
</datepicker>
</div>
</div>
<div v-if="noteLocked" class="row mt-2 justify-content-center">
<div class="col-9 text-center text-muted small" data-cy="pruefung-note-locked">{{ $capitalize($p.t('benotungstool/pruefungNoteLockedHint')) }}</div>
</div>
<div v-if="config?.CIS_GESAMTNOTE_PUNKTE" class="row mt-3 align-items-center justify-content-center">
<div class="col-3 text-center">{{ $capitalize($p.t('benotungstool/c4punkte')) }}:</div>
<div class="col-6">
<InputNumber data-cy="pruefung-punkte" v-model="selectedPunkte" @input="debouncedShowNoteForPunkte" :disabled="noteLocked"
inputId="pruefungPunkte" :min="0" :max="100000" class="w-100">
</InputNumber>
</div>
</div>
<div class="row mt-3 align-items-center justify-content-center">
<div class="col-3 text-center">{{ $capitalize($p.t('lehre/note')) }}:</div>
<div class="col-6">
<Dropdown data-cy="pruefung-note" :placeholder="$capitalize($p.t('lehre/note'))" :disabled="config?.CIS_GESAMTNOTE_PUNKTE || noteLocked"
:style="{'width': '100%'}" optionLabel="bezeichnung" v-model="selectedNote" :options="lehreNoten" showClear>
</Dropdown>
</div>
</div>
<div v-if="!student" class="row mt-3 align-items-center justify-content-center">
<div class="col-3 text-center">{{ $capitalize($p.t('benotungstool/prueflingSelectionv2')) }}:</div>
<div class="col-6">
<Multiselect data-cy="pruefung-students" :modelValue="selectedStudents" @update:modelValue="selectStudents" :options="studentOptions" :optionLabel="studentLabel"
:placeholder="$capitalize($p.t('benotungstool/prueflingSelectionv2'))" :maxSelectedLabels="3" showToggleAll class="w-100" />
</div>
</div>
<div v-if="lektoren.length > 1" class="row mt-3 align-items-center justify-content-center">
<div class="col-3 text-center">{{ $capitalize($p.t('benotungstool/c4lektor')) }}:</div>
<div class="col-6">
<Dropdown data-cy="pruefung-lektor" :style="{'width': '100%'}" v-model="selectedLektor" :options="lektoren"
optionLabel="name" optionValue="mitarbeiter_uid">
</Dropdown>
</div>
</div>
<div v-if="withoutLvNote.length" class="alert alert-info mt-3 mb-0 py-2" data-cy="pruefung-without-lvnote">
<div>{{ $p.t('benotungstool/c4lvNoteWirdAngelegtHinweis') }}</div>
<div v-if="!student" class="small mt-1">{{ withoutLvNote.map(s => s.vorname + ' ' + s.nachname + ' (' + s.uid + ')').join(', ') }}</div>
</div>
</template>
<template v-slot:footer>
<button type="button" data-cy="pruefung-submit" class="btn btn-primary" @click="submit">
{{ student ? $capitalize($p.t('global/speichern')) : $capitalize($p.t('benotungstool/c4addNewPruefung')) }}
</button>
</template>
</bs-modal>
`
};
@@ -0,0 +1,122 @@
/**
* The layout of the Benotungstool table: what the browser keeps (column order, width, visibility,
* filters, sort), the sticky columns, and the scroll position during a render.
*/
/** Reads a value that the browser keeps. Private mode or a broken value give `fallback`. */
export function loadKept(key, fallback) {
try {
const value = localStorage.getItem(key);
return value === null ? fallback : JSON.parse(value);
} catch (e) {
return fallback;
}
}
export function saveKept(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (e) {
// private mode: the value lasts until the next load
}
}
/** Keeps the layout of the columns in `fields`. The Pruefung columns change per LV, so they are left out. */
export function saveLayout(key, table, fields) {
saveKept(key, {
columns: table.getColumnLayout()
.filter(col => fields.includes(col.field))
.map(col => ({ field: col.field, visible: col.visible, width: col.width })),
sort: table.getSorters().map(s => ({ field: s.field, dir: s.dir })),
filters: table.getFilters(),
headerFilters: table.getHeaderFilters()
});
}
/**
* The column definitions in the kept order, with the kept width and visibility. New columns come last.
* Apply it BEFORE Tabulator takes the columns: after that the definitions and the Vue titles conflict.
*/
export function restoreColumns(columns, layout) {
if (!layout?.columns) return columns;
const byField = new Map(columns.map(c => [c.field, c]));
const restored = [];
layout.columns.forEach(kept => {
const column = byField.get(kept.field);
if (!column) return;
restored.push({ ...column, width: kept.width, visible: kept.visible });
byField.delete(kept.field);
});
byField.forEach(column => restored.push(column));
return restored;
}
/** Applies the kept filters and sort. Call it once, after the first rows: then the Pruefung columns exist. */
export function restoreFiltersAndSort(table, layout) {
if (layout?.filters?.length) table.setFilter(layout.filters);
(layout?.headerFilters ?? []).forEach(hf => table.setHeaderFilterValue(hf.field, hf.value));
// a Pruefung column of another LV does not exist in this table
const fields = table.getColumns().map(column => column.getField());
const sorters = (layout?.sort ?? [])
.filter(s => fields.includes(s.field))
.map(s => ({ column: s.field, dir: s.dir }));
if (sorters.length) table.setSort(sorters);
}
/**
* Pins the selected columns to the left. A `sticky-on-<field>` class on the container switches one
* column on (see Benotungstool.css), and `--sl-<field>` is its left offset: the width of the pinned
* columns before it, so that the pinned columns stand next to each other.
*/
export function applyStickyColumns(element, table, fields, selected) {
if (!element || !table) return;
let offset = 0;
// the display order, so a moved column keeps the right offset
table.getColumns().forEach(column => {
const field = column.getField();
if (!fields.includes(field)) return;
const pinned = selected.includes(field);
element.classList.toggle('sticky-on-' + field, pinned);
element.style.setProperty('--sl-' + field, offset + 'px');
if (pinned) offset += column.getWidth();
});
}
/**
* Keeps the scroll position while `operation` renders the table again.
*
* Read and write .tabulator-tableholder directly: Tabulator does not update rowManager.scrollLeft.
* Set the position again in the next two frames: Tabulator renders later and resets it, and
* Vue.nextTick is too early.
*/
export function preserveScroll(table, operation) {
const holder = table?.element?.querySelector('.tabulator-tableholder');
const left = holder?.scrollLeft ?? 0;
const top = holder?.scrollTop ?? 0;
const result = operation();
// nothing to keep; do not work against a scroll to the top that the user wants
if (!holder || (!left && !top)) return result;
const restore = () => {
holder.scrollLeft = left;
holder.scrollTop = top;
};
restore();
requestAnimationFrame(() => { restore(); requestAnimationFrame(restore); });
// setData returns a promise; set the position again after the load
if (result && typeof result.then === 'function') result.then(() => requestAnimationFrame(restore));
return result;
}
+3 -13
View File
@@ -72,20 +72,10 @@ export default {
return this.entry.url.startsWith(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router)
},
target() {
if (this.entry.template_kurzbz == 'redirect') {
if (!this.entry.content)
return '';
let xmlDoc = (new DOMParser()).parseFromString(this.entry.content,"text/xml");
let target = xmlDoc.getElementsByTagName('target')[0];
if (!target)
return '';
target = target.childNodes[0].nodeValue + "";
if (target == 'content' || target == '_self')
target = "";
return target;
if (this.entry?.target) {
return this.entry.target;
}
return ''
return '_self';
},
hasChilds() {
return this.entry.childs && this.entry.childs.length !== 0;
@@ -206,6 +206,8 @@ export default {
});
this.lehrveranstaltungen.forEach((lehrveranstaltung)=>{
// a lehrveranstaltung without a modul has no children
if(!Array.isArray(lehrveranstaltung.lehrveranstaltungen)) return;
lehrveranstaltung.lehrveranstaltungen.sort((lv1,lv2)=>{
if (lv1.bezeichnung.toLowerCase() > lv2.bezeichnung.toLowerCase()) {
return 1;
@@ -393,6 +395,16 @@ export default {
</ul>
</div>
</div>
<div class="card" v-else-if="!Array.isArray(lehrveranstaltung.lehrveranstaltungen)">
<div class="card-body">
<ul class="list-group list-group-flush">
<li class="d-flex list-group-item">
<a class="fhc-link-color d-block me-auto" href="#" @click="openLvUebersicht(lehrveranstaltung)">{{isGermanLanguage ? lehrveranstaltung.bezeichnung : lehrveranstaltung.bezeichnung_english}}</a>
<p>{{lehrveranstaltung.lehrform_kurzbz}}</p>
</li>
</ul>
</div>
</div>
</template>
</div>
@@ -49,6 +49,52 @@ export default{
{title: this.$p.t('person', 'nachname'), field:"nachname"},
{title: this.$p.t('person', 'vorname'), field:"vorname"},
{title: this.$p.t('person', 'uid'), field:"mitarbeiter_uid"},
{title: this.$p.t('global/insertvon'), field: 'insertvon', visible: false},
{
title: this.$p.t('global/insertamum'),
field: "insertamum",
visible: false,
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}
},
{title: this.$p.t('global/updatevon'), field: 'updatevon', visible: false},
{
title: this.$p.t('global/updateamum'),
field: "updateamum",
visible: false,
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}
},
{title: this.$p.t('lehre', 'lehreinheit_id'), field:"lehreinheit_id"},
{
title: this.$p.t('lehre', 'verplant'),
@@ -102,7 +148,7 @@ export default{
selectable: true,
selectableRangeMode: 'click',
selectableRows:1,
persistenceID: 'lehrveranstaltungen_lektor_table_2025_05_27_v1',
persistenceID: 'lehrveranstaltungen_lektor_table_2026_09_11_v1',
}},
},
mounted(){
+20
View File
@@ -0,0 +1,20 @@
export const MobilityLegende = {
name: 'MobilityLegende',
template:`
<div class="col-auto" style="max-width: 60vw">
<!-- TODO: phrasen definieren & verwenden-->
<div class="row" style="font-weight: bold"><h6>Legende</h6></div>
<div class="row"><h6>(i) ... Incoming</h6></div>
<div class="row"><h6>(o) ... Outgoing</h6></div>
<div class="row"><h6>(ar) ... angerechnet </h6></div>
<div class="row"><h6>(iar) ... intern angerechnet</h6></div>
<div class="row"><h6>(nz) ... nicht zugelassen</h6></div>
<div class="row"><h6>(ma) ... MitarbeiterIn</h6></div>
<div class="row"><h6>(a.o.) ... Außerordentliche/r HörerIn</h6></div>
<div class="row"><h6>(d.d.) ... Double Degree Program</h6></div>
</div>
`
};
export default MobilityLegende;
@@ -255,7 +255,7 @@ export default {
deleteButton.className = 'btn btn-outline-secondary';
const icon = document.createElement('i');
icon.className = 'fa fa-trash';
icon.title = this.$p.t('ui/loeschen');
icon.title = this.$p.t('lehre/info_deleteMarkEntry');
deleteButton.append(icon);
deleteButton.addEventListener('click', evt => {
evt.stopPropagation();
@@ -193,7 +193,7 @@ export default {
return this.$api
.call(ApiStvProjektarbeit.getLehrveranstaltungen(
this.student.uid,
this.newProjektarbeit ? this.student.studiengang_kz : null,
this.student.studiengang_kz,
this.studiensemester,
this.additional_lehrveranstaltung_id
))
@@ -227,7 +227,8 @@ export default {
if (preparedFormData.projektarbeit_id == null) delete(preparedFormData.projektarbeit_id);
delete(preparedFormData.firma);
delete(preparedFormData.firma_name);
delete(preparedFormData.lehrveranstaltung_id);
preparedFormData.studiensemester_kurzbz = this.studiensemester
return preparedFormData;
}
@@ -290,7 +291,7 @@ export default {
<div class="row mb-3">
<div class="col-10">
<div class="col-9">
<form-input
container-class="stv-details-projektarbeit-firma"
:label="$p.t('projektarbeit', 'firma')"
@@ -304,7 +305,7 @@ export default {
>
</form-input>
</div>
<div class="col-2 align-content-center">
<div class="col-3 align-content-center">
<a :href="firmenverwaltungLink" target="_blank">
{{ $p.t('projektarbeit', 'zurFirmenverwaltung') }}
</a>
@@ -313,14 +314,13 @@ export default {
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-lv col-10"
container-class="stv-details-projektarbeit-lv col-9"
:label="$p.t('projektarbeit', 'lehrveranstaltung')"
type="select"
v-model="formData.lehrveranstaltung_id"
name="lehrveranstaltung_id"
@change="lvChanged($event)"
>
<option :value="null"> -- {{$p.t('fehlermonitoring', 'keineAuswahl')}} -- </option>
<option
v-for="lv in arrLvs"
:key="lv.lehrveranstaltung_id"
@@ -330,14 +330,13 @@ export default {
</option>
</form-input>
<form-input
container-class="col-2"
container-class="col-3"
:label="$p.t('lehre', 'studiensemester')"
type="select"
v-model="studiensemester"
name="studiensemester"
@change="studiensemesterChanged"
>
<option :value="null"> -- {{$p.t('fehlermonitoring', 'keineAuswahl')}} -- </option>
<option
v-for="sem in arrStudiensemester"
:key="sem.studiensemester_kurzbz"
@@ -42,16 +42,16 @@ export default {
return {
tabulatorOptions: {
columns: [
{title: "Nachname", field: "nachname"},
{title: "Vorname", field: "vorname"},
{title: "Note", field: "note"},
{title: "Punkte", field: "punkte"},
{title: "Stunden", field: "stunden"},
{title: "Stundensatz", field: "stundensatz", visible: false},
{title: "Art", field: "betreuerart_kurzbz", visible: false},
{title: "Person ID", field: "person_id", visible: false},
{title: "Vertrag ID", field: "vertrag_id", visible: false},
{title: "Projektarbeit ID", field: "projektarbeit_id", visible: false},
{title: "Nachname", field: "nachname", widthGrow: 2},
{title: "Vorname", field: "vorname", widthGrow: 2},
{title: "Note", field: "note", widthGrow: 1},
{title: "Punkte", field: "punkte", widthGrow: 1},
{title: "Stunden", field: "stunden", widthGrow: 1},
{title: "Stundensatz", field: "stundensatz", visible: false, widthGrow: 1},
{title: "Art", field: "betreuerart_kurzbz", visible: false, widthGrow: 2},
{title: "Person ID", field: "person_id", visible: false, widthGrow: 1},
{title: "Vertrag ID", field: "vertrag_id", visible: false, widthGrow: 1},
{title: "Projektarbeit ID", field: "projektarbeit_id", visible: false, widthGrow: 1},
{
title: 'Aktionen',
field: 'actions',
@@ -822,18 +822,33 @@ export default {
@headerFilterOn="handleHeaderFilter"
>
<!--
<template #actions>
<div>
<button
class="btn btn-outline-success sm mb-1"
:title="'Export ' + selectedPrestudents.length + ' prestudent(s) to Excel'"
>
<i class="fas fa-file-excel fa-xl"></i>
</button>
</div>
</template>
-->
<template #actions>
<!-- <div>
<button
class="btn btn-outline-success sm mb-1"
:title="'Export ' + selectedPrestudents.length + ' prestudent(s) to Excel'"
>
<i class="fas fa-file-excel fa-xl"></i>
</button>
</div> -->
<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">
<strong>{{$p.t('filter','filterActive')}}</strong>
</p>
<button
class="btn btn-outline-danger sm"
:title="$p.t('filter/filterDelete')"
@click="resetFilter"
>
<span class="fa-solid fa-filter-circle-xmark"></span>
</button>
</div>
</template>
</template>
<template #additional>
<div class="pe-1">
@@ -873,22 +888,7 @@ export default {
<i class="fa-solid fa-refresh pe-1"></i> {{selected.length}}
</button>
</div>
</template>
<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">
<strong>{{$p.t('filter','filterActive')}}</strong>
</p>
<button
class="btn btn-outline-danger sm"
:title="$p.t('filter/filterDelete')"
@click="resetFilter"
>
<span class="fa-solid fa-filter-circle-xmark"></span>
</button>
</div>
</template>
<template #filter>
+109
View File
@@ -59,3 +59,112 @@ export function formatDate(d) {
throw new TypeError("The parameter provided for this function is not a string or a Date object, please refere to the function formatDate in the DateHelpers.js file");
}
}
/**
* today in the time zone of the instance, as a Date at midnight
*
* The browser can stand in another time zone than the server. A date from the browser then names the
* next day, and the server refuses the entry as a date in the future.
*
* @returns {Date} today at 00:00 local time, with the day of the instance time zone.
*/
export function today() {
const zone = (typeof FHC_JS_DATA_STORAGE_OBJECT !== "undefined")
? FHC_JS_DATA_STORAGE_OBJECT.timezone
: null;
if (zone && typeof luxon !== "undefined")
{
const now = luxon.DateTime.local().setZone(zone);
if (now.isValid) return new Date(now.year, now.month - 1, now.day);
}
// without the time zone of the instance the browser decides
const local = new Date();
return new Date(local.getFullYear(), local.getMonth(), local.getDate());
}
/**
* Date -> 'YYYY-MM-DD' in local time. An API takes the day, never a timestamp.
*
* @param {Date} date
* @returns {string}
*/
export function toIsoDate(date) {
return `${date.getFullYear()}-${numberPadding(date.getMonth() + 1)}-${numberPadding(date.getDate())}`;
}
/**
* 'YYYY-MM-DD' (a longer string is cut) -> Date at 00:00 local time.
*
* @param {string} iso
* @returns {Date|null}
*/
export function parseIsoDate(iso) {
const parts = String(iso ?? '').slice(0, 10).split('-');
if (parts.length !== 3) return null;
return new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
}
/**
* 'YYYY-MM-DD HH:MM:SS' or 'YYYY-MM-DD' -> Date in local time.
*
* @param {string} timestamp
* @returns {Date|null}
*/
export function parseTimestamp(timestamp) {
if (!timestamp) return null;
const [datePart, timePart] = String(timestamp).trim().split(' ');
const [year, month, day] = (datePart ?? '').split('-').map(Number);
if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day)) return null;
const [hour, minute, second] = (timePart ?? '').split(':').map(Number);
return new Date(year, month - 1, day,
Number.isFinite(hour) ? hour : 0,
Number.isFinite(minute) ? minute : 0,
Number.isFinite(second) ? second : 0);
}
/**
* 'YYYY-MM-DD' (a longer string is cut) -> 'DD.MM.YYYY'. A string operation: no time zone applies.
*
* @param {string} iso
* @returns {string} an empty string for an invalid value
*/
export function isoToDmy(iso) {
const parts = String(iso ?? '').slice(0, 10).split('-');
if (parts.length !== 3) return '';
return `${parts[2]}.${parts[1]}.${parts[0]}`;
}
/**
* 'DD.MM.YYYY' -> 'YYYY-MM-DD'.
*
* @param {string} dmy
* @returns {string|null} null if the value has another format or the day does not exist (31.02.)
*/
export function dmyToIso(dmy) {
const match = String(dmy ?? '').trim().match(/^(\d{2})\.(\d{2})\.(\d{4})$/);
if (!match) return null;
const [, day, month, year] = match.map(Number);
const date = new Date(year, month - 1, day);
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) return null;
return `${match[3]}-${match[2]}-${match[1]}`;
}
/**
* @param {Date} date
* @param {number} days
* @returns {Date} a new Date, $days later
*/
export function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
+12
View File
@@ -1,3 +1,15 @@
/**
* Makes a value safe for an HTML string. A Tabulator formatter string becomes innerHTML, therefore
* a value from the database or from an addon must not carry markup.
*/
export function escapeHtml(value) {
if (value === null || value === undefined) return '';
const replacements = {'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'};
return String(value).replace(/[&<>"']/g, (character) => replacements[character]);
}
export function capitalize(string) {
if (!string) return "";
return string[0].toUpperCase() + string.slice(1);
+9
View File
@@ -0,0 +1,9 @@
export function debounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn(...args)
}, delay);
};
}
+1
View File
@@ -75,6 +75,7 @@ var Rueckstellung = {
if (FHC_AjaxClient.hasData(data))
{
Rueckstellung.get(personid);
InfocenterDetails._refreshMessages();
InfocenterDetails._refreshLog()
}
else
+25
View File
@@ -0,0 +1,25 @@
import {escapeHtml} from "../../helpers/StringHelpers.js";
export function centeredTextFormatter(cell) {
const longForm = cell.getValue()
if(!longForm) return
const data = cell.getData()
const entry = Object.entries(data).find(entry => entry[1] == longForm)
// shortFormKey must have same keyname as longForm but with 'Short' appended
const shortForm = data[entry[0]+'Short']
if(shortForm && longForm) {
return `<div style="display: flex; justify-content: start; align-items: center; height: 100%; width: 100%;">
<span class="full-text" style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">
${escapeHtml(longForm)}
</span>
<span class="short-text" style="font-weight: bold; display: none;">
${escapeHtml(shortForm)}
</span>
</div>`;
} else {
return '<div style="display: flex; justify-content: start; align-items: center; height: 100%">' +
'<p style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">'+escapeHtml(longForm)+'</p></div>'
}
}
+1 -2
View File
@@ -192,8 +192,7 @@ function draw_content_xml($row)
$note = (isset($note_arr[$row_proj->note])?$note_arr[$row_proj->note]:$row_proj->note);
$datum_projekt = $datum_obj->convertISODate($row_proj->ende);
$projektarbeit = new projektarbeit($row_proj->projektarbeit_id);
$lehreinheit = new lehreinheit($projektarbeit->lehreinheit_id);
$lehrveranstaltung = new lehrveranstaltung($lehreinheit->lehrveranstaltung_id);
$lehrveranstaltung = new lehrveranstaltung($projektarbeit->lehrveranstaltung_id);
$projektnote = new note($note);
}
+14 -8
View File
@@ -998,12 +998,11 @@ if (isset($_REQUEST["xmlformat"]) && $_REQUEST["xmlformat"] == "xml")
tbl_firma.name, lehrveranstaltung_id, firma_id
FROM
lehre.tbl_projektarbeit
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
JOIN public.tbl_firma USING(firma_id)
WHERE
student_uid=".$db->db_add_param($uid_arr[$i])."
AND projekttyp_kurzbz in('Praktikum', 'Praxis')
AND tbl_lehreinheit.lehrveranstaltung_id=".$db->db_add_param($row_stud->lehrveranstaltung_id)."
AND tbl_projektarbeit.lehrveranstaltung_id=".$db->db_add_param($row_stud->lehrveranstaltung_id)."
ORDER BY beginn ASC, projektarbeit_id ASC;";
if($result_praktikum = $db->db_query($qry))
@@ -1019,14 +1018,14 @@ if (isset($_REQUEST["xmlformat"]) && $_REQUEST["xmlformat"] == "xml")
// Aber kein Auslandssemester war, sonst wirds spaeter hinzugefügt
$qry = "
SELECT
lehrveranstaltung_id, titel, themenbereich, note, titel_english
lehre.tbl_projektarbeit.lehrveranstaltung_id, titel, themenbereich, note, titel_english
FROM
lehre.tbl_projektarbeit
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
student_uid=".$db->db_add_param($uid_arr[$i])."
AND projekttyp_kurzbz in('Bachelor', 'Diplom')
AND lehrveranstaltung_id=".$db->db_add_param($row_stud->lehrveranstaltung_id)."
AND lehre.tbl_projektarbeit.lehrveranstaltung_id=".$db->db_add_param($row_stud->lehrveranstaltung_id)."
AND NOT EXISTS(SELECT 1
FROM bis.tbl_bisio
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
@@ -1039,6 +1038,10 @@ if (isset($_REQUEST["xmlformat"]) && $_REQUEST["xmlformat"] == "xml")
while($row_thesis = $db->db_fetch_object($result_thesis))
{
$bezeichnung.= ": \"".$row_thesis->titel."\"";
// hier sollt nicht $titel_english verwendet werden, da in der projektarbeitsbeurteilung ein
// feature zum aktualisieren des projektarbeit titel existiert und nur das feld 'titel' editiert.
// um divergente thesisnamen zu vermeiden wird in beiden Fällen der (ohnehin öfters englische) Wert
// aus 'titel' verwendet.
$bezeichnung_englisch.= ": \"".$row_thesis->titel."\"";
}
}
@@ -1086,9 +1089,12 @@ if (isset($_REQUEST["xmlformat"]) && $_REQUEST["xmlformat"] == "xml")
SELECT
studiensemester_kurzbz, ort, ects, semesterstunden, von, bis,
universitaet, lehrveranstaltung_id, tbl_lehrveranstaltung.sws,
(SELECT titel_english FROM lehre.tbl_projektarbeit
WHERE lehreinheit_id=tbl_bisio.lehreinheit_id
AND student_uid = ".$db->db_add_param($uid_arr[$i])." limit 1) as projektarbeitstitel
(SELECT titel_english
FROM lehre.tbl_projektarbeit
WHERE lehre.tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
AND lehre.tbl_projektarbeit.studiensemester_kurzbz = tbl_lehreinheit.studiensemester_kurzbz
AND student_uid = ".$db->db_add_param($uid_arr[$i])."
LIMIT 1) as projektarbeitstitel
FROM
bis.tbl_bisio
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
+1 -2
View File
@@ -95,8 +95,7 @@ echo "<gesamt_note>".$note->bezeichnung."</gesamt_note>";
if(!empty($projektarbeit->result))
{
$lehreinheit = new lehreinheit($projektarbeit->result[0]->lehreinheit_id);
$lehrveranstaltung = new lehrveranstaltung($lehreinheit->lehrveranstaltung_id);
$lehrveranstaltung = new lehrveranstaltung($projektarbeit->result[0]->lehrveranstaltung_id);
$note = new note($projektarbeit->result[0]->note);
echo "<projektarbeit_titel>".$projektarbeit->result[0]->titel."</projektarbeit_titel>";
echo "<projektarbeit_note>".$note->bezeichnung."</projektarbeit_note>";
+17 -25
View File
@@ -120,16 +120,15 @@ if ($uid == null)
SELECT
tbl_benutzer.uid as mitarbeiter_uid
FROM
lehre.tbl_projektbetreuer, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung,
lehre.tbl_projektbetreuer, lehre.tbl_lehrveranstaltung,
public.tbl_benutzer, lehre.tbl_projektarbeit, campus.vw_student, public.tbl_mitarbeiter
WHERE
tbl_projektbetreuer.person_id=tbl_benutzer.person_id AND
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
student_uid=vw_student.uid AND
tbl_benutzer.uid = tbl_mitarbeiter.mitarbeiter_uid AND
tbl_lehreinheit.lehreinheit_id=tbl_projektarbeit.lehreinheit_id AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($ss)." AND
tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_lehrveranstaltung.lehrveranstaltung_id=tbl_projektarbeit.lehrveranstaltung_id AND
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($ss)." AND
tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER)." AND
tbl_projektbetreuer.stunden!='0'
) as mitarbeiter ORDER BY mitarbeiter_uid";
@@ -394,30 +393,23 @@ function drawLehrauftrag($uid)
,tbl_projektbetreuer.stunden
,tbl_projektbetreuer.stundensatz
,tbl_lehrveranstaltung.semester
,vorname
,nachname
,vw_student.studiengang_kz
,campus.vw_student.vorname
,campus.vw_student.nachname
,tbl_student.studiengang_kz
,projekttyp_kurzbz
,lehrfach.oe_kurzbz
,lehre.tbl_lehrveranstaltung.oe_kurzbz
FROM lehre.tbl_projektbetreuer
,lehre.tbl_lehreinheit
,lehre.tbl_lehrveranstaltung AS lehrfach
,lehre.tbl_lehrveranstaltung
,public.tbl_organisationseinheit
,public.tbl_benutzer
,lehre.tbl_projektarbeit
,campus.vw_student
WHERE tbl_projektbetreuer.person_id = tbl_benutzer.person_id
AND tbl_benutzer.uid = ".$db->db_add_param($uid)."
AND tbl_projektarbeit.projektarbeit_id = tbl_projektbetreuer.projektarbeit_id
AND student_uid = vw_student.uid
AND tbl_organisationseinheit.oe_kurzbz = tbl_lehrveranstaltung.oe_kurzbz
AND tbl_lehreinheit.lehreinheit_id = tbl_projektarbeit.lehreinheit_id
AND tbl_lehreinheit.lehrfach_id = lehrfach.lehrveranstaltung_id
AND tbl_lehreinheit.studiensemester_kurzbz = ".$db->db_add_param($ss)."
AND tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id";
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_organisationseinheit USING (oe_kurzbz)
JOIN public.tbl_benutzer USING (person_id)
JOIN public.tbl_student ON (tbl_projektarbeit.student_uid = tbl_student.student_uid)
JOIN campus.vw_student ON (campus.vw_student.uid = tbl_student.student_uid)
WHERE tbl_benutzer.uid = ".$db->db_add_param($uid)."
AND lehre.tbl_projektarbeit.studiensemester_kurzbz = ".$db->db_add_param($ss);
if ($studiengang_kz != '')
$qry .= " AND tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER);
$qry .= " AND lehre.tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER);
if ($result = $db->db_query($qry))
{
while ($row = $db->db_fetch_object($result))
+5 -7
View File
@@ -124,16 +124,15 @@ if ($uid == null)
SELECT
tbl_benutzer.uid as mitarbeiter_uid
FROM
lehre.tbl_projektbetreuer, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung,
lehre.tbl_projektbetreuer, lehre.tbl_lehrveranstaltung,
public.tbl_benutzer, lehre.tbl_projektarbeit, campus.vw_student, public.tbl_mitarbeiter
WHERE
tbl_projektbetreuer.person_id=tbl_benutzer.person_id AND
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
student_uid=vw_student.uid AND
tbl_benutzer.uid = tbl_mitarbeiter.mitarbeiter_uid AND
tbl_lehreinheit.lehreinheit_id=tbl_projektarbeit.lehreinheit_id AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($ss)." AND
tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_lehrveranstaltung.lehrveranstaltung_id=tbl_projektarbeit.lehrveranstaltung_id AND
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($ss)." AND
tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER)." AND
tbl_projektbetreuer.stunden!='0'
) as mitarbeiter ORDER BY mitarbeiter_uid";
@@ -428,8 +427,7 @@ function drawLehrauftrag($uid)
vertragsstatus_kurzbz
FROM lehre.tbl_projektbetreuer pb
JOIN lehre.tbl_projektarbeit pa USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung lv ON (pa.lehrveranstaltung_id = lv.lehrveranstaltung_id)
JOIN PUBLIC.tbl_organisationseinheit oe USING (oe_kurzbz)
JOIN public.tbl_benutzer benutzer ON pb.person_id = benutzer.person_id
JOIN campus.vw_student student ON pa.student_uid = student.uid
@@ -438,7 +436,7 @@ function drawLehrauftrag($uid)
WHERE pb.vertrag_id IS NOT NULL
AND vvst.vertragsstatus_kurzbz = \'akzeptiert\'
AND benutzer.uid = '.$db->db_add_param($uid).'
AND le.studiensemester_kurzbz = '.$db->db_add_param($ss);
AND pa.studiensemester_kurzbz = '.$db->db_add_param($ss);
if ($studiengang_kz != '')
{
+6
View File
@@ -92,6 +92,12 @@ elseif($lehrveranstaltung_kompatibel_id!='')
else
$lehrveranstaltung->load_lva($stg_kz,$sem);
// try to filter projektarbeit lva selection by those lva that are also assigned to students current studienplan
// so it is more likely that the thesis name gets found by the diplomasupplement script
if(isset($_GET['projektarbeit']) && isset($_GET['uid'])) {
$lehrveranstaltung->checkLvaAgainstStudentCurrentStudienplan($student_uid);
}
$rdf_url='http://www.technikum-wien.at/lehrveranstaltung/';
echo '
+4 -3
View File
@@ -70,7 +70,7 @@ else
function draw_content($row)
{
global $rdf_url, $datum_obj;
$lehreinheit = new lehreinheit($row->lehreinheit_id);
$lehreinheit = $row->lehreinheit_id ? new lehreinheit($row->lehreinheit_id) : null;
echo '
<RDF:li>
<RDF:Description id="'.$row->projektarbeit_id.'" about="'.$rdf_url.'/'.$row->projektarbeit_id.'" >
@@ -80,8 +80,9 @@ function draw_content($row)
<PROJEKTARBEIT:titel><![CDATA['.xmlclean($row->titel).']]></PROJEKTARBEIT:titel>
<PROJEKTARBEIT:titel_english><![CDATA['.xmlclean($row->titel_english).']]></PROJEKTARBEIT:titel_english>
<PROJEKTARBEIT:lehreinheit_id><![CDATA['.$row->lehreinheit_id.']]></PROJEKTARBEIT:lehreinheit_id>
<PROJEKTARBEIT:lehreinheit_stsem><![CDATA['.$lehreinheit->studiensemester_kurzbz.']]></PROJEKTARBEIT:lehreinheit_stsem>
<PROJEKTARBEIT:lehrveranstaltung_id><![CDATA['.$lehreinheit->lehrveranstaltung_id.']]></PROJEKTARBEIT:lehrveranstaltung_id>
<PROJEKTARBEIT:lehreinheit_stsem><![CDATA['.($lehreinheit ? $lehreinheit->studiensemester_kurzbz : $row->studiensemester_kurzbz).']]></PROJEKTARBEIT:lehreinheit_stsem>
<PROJEKTARBEIT:projektarbeit_stsem><![CDATA['.$row->studiensemester_kurzbz.']]></PROJEKTARBEIT:projektarbeit_stsem>
<PROJEKTARBEIT:lehrveranstaltung_id><![CDATA['.$row->lehrveranstaltung_id.']]></PROJEKTARBEIT:lehrveranstaltung_id>
<PROJEKTARBEIT:student_uid><![CDATA['.$row->student_uid.']]></PROJEKTARBEIT:student_uid>
<PROJEKTARBEIT:firma_id><![CDATA['.$row->firma_id.']]></PROJEKTARBEIT:firma_id>
<PROJEKTARBEIT:note><![CDATA['.$row->note.']]></PROJEKTARBEIT:note>
+2 -3
View File
@@ -124,9 +124,8 @@ if(isset($_REQUEST['xmlformat']) && $_REQUEST['xmlformat']=="xml")
}
}
$qry_projektarbeit = "SELECT lehrveranstaltung_id, titel, themenbereich, note, titel_english
FROM lehre.tbl_projektarbeit
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
$qry_projektarbeit = "SELECT lehre.tbl_projektarbeit.lehrveranstaltung_id, titel, themenbereich, note, titel_english
FROM lehre.tbl_projektarbeit
WHERE student_uid='$uid'
AND projekttyp_kurzbz in('Bachelor', 'Diplom')
ORDER BY beginn ASC, projektarbeit_id ASC;";
+6 -8
View File
@@ -211,15 +211,14 @@ if (isset($_REQUEST["xmlformat"]) && $_REQUEST["xmlformat"] == "xml")
$qry_proj = "
SELECT
lehrveranstaltung_id, titel, themenbereich, note, titel_english,
lehre.tbl_projektarbeit.lehrveranstaltung_id, titel, themenbereich, note, titel_english,
tbl_projekttyp.bezeichnung, projekttyp_kurzbz
FROM
lehre.tbl_projektarbeit
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
WHERE
student_uid=".$db->db_add_param($uid_arr[$i])."
AND studiensemester_kurzbz=".$db->db_add_param($studiensemester_kurzbz)."
AND lehre.tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($studiensemester_kurzbz)."
AND projekttyp_kurzbz in('Bachelor', 'Diplom')
ORDER BY beginn ASC, projektarbeit_id ASC";
@@ -297,13 +296,12 @@ if (isset($_REQUEST["xmlformat"]) && $_REQUEST["xmlformat"] == "xml")
//Firma fuer Berufspraktikum
$qry = "SELECT tbl_firma.name
FROM
lehre.tbl_projektarbeit, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, public.tbl_firma
lehre.tbl_projektarbeit
JOIN lehre.tbl_lehrveranstaltung ON(lehre.tbl_projektarbeit.lehrveranstaltung_id = lehre.tbl_lehrveranstaltung.lehrveranstaltung_id)
JOIN public.tbl_firma ON(lehre.tbl_projektarbeit.firma_id = public.tbl_firma.firma_id)
WHERE
tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.firma_id = tbl_firma.firma_id AND
tbl_projektarbeit.student_uid=".$db->db_add_param($uid_arr[$i])." AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($studiensemester_kurzbz)." AND
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($studiensemester_kurzbz)." AND
tbl_lehrveranstaltung.lehrveranstaltung_id=".$db->db_add_param($row->lehrveranstaltung_id);
$firma = '';

Some files were not shown because too many files have changed in this diff Show More