From beb54c1bc5352c75c20baa90404e1db8cb290040 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Thu, 30 Jul 2026 17:18:51 +0200 Subject: [PATCH] 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/.js, suites/.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; --- .../controllers/api/frontend/v1/Noten.php | 3 +- cypress.config.js | 47 +++ package.json | 15 + system/seeders/000_fixmissing.sql | 28 ++ system/seeders/001_helper_functions.sql | 36 ++ system/seeders/002_mitarbeiter.sql | 40 +++ system/seeders/003_kompetenzfeld.sql | 27 ++ system/seeders/004_ort.sql | 25 ++ system/seeders/005_studenten_stg_1.sql | 114 ++++++ system/seeders/006_studiengang_3.sql | 161 +++++++++ system/seeders/007_studenten_stg_3.sql | 120 +++++++ system/seeders/008_studiengang_4.sql | 239 +++++++++++++ system/seeders/009_studenten_stg_4.sql | 266 ++++++++++++++ .../010_projektarbeiten_studiengang_4.sql | 16 + system/seeders/011_zeitwuensche.sql | 107 ++++++ system/seeders/012_cms_content.sql | 38 ++ system/seeders/013_studiengang_5.sql | 231 ++++++++++++ system/seeders/014_betriebsmittel.sql | 11 + system/seeders/015_kalender.sql | 210 +++++++++++ system/seeders/016_benotungstool_noten.sql | 183 ++++++++++ system/seeders/017_extension_grants.sql | 33 ++ system/seeders/README.txt | 32 ++ system/setup_testinstance.php | 229 ++++++++++++ tests/cypress/.env.example | 56 +++ .../e2e/specs/api/noten/noten.access.cy.js | 197 ++++++++++ .../e2e/specs/api/noten/noten.bulk.cy.js | 223 ++++++++++++ .../e2e/specs/api/noten/noten.freigabe.cy.js | 183 ++++++++++ .../e2e/specs/api/noten/noten.frist.cy.js | 128 +++++++ .../api/noten/noten.notenschluessel.cy.js | 135 +++++++ .../api/noten/noten.notenvorschlag.cy.js | 170 +++++++++ .../api/noten/noten.pruefungsantritte.cy.js | 335 ++++++++++++++++++ tests/cypress/suites/noten.js | 28 ++ tests/cypress/support/api/notenApi.js | 78 ++++ tests/cypress/support/commands.js | 28 ++ tests/cypress/support/e2e.js | 2 + tests/cypress/support/helpers/notenErrors.js | 96 +++++ tests/cypress/support/helpers/notenReset.js | 75 ++++ .../cypress/support/helpers/notenScenario.js | 62 ++++ .../cypress/support/helpers/notenTestData.js | 214 +++++++++++ tests/cypress/tasks/db.js | 150 ++++++++ tests/cypress/tasks/notenDb.js | 129 +++++++ tests/cypress/tasks/sshTunnel.js | 137 +++++++ tests/cypress/tools/checks/noten.js | 117 ++++++ tests/cypress/tools/dbCheck.js | 193 ++++++++++ 44 files changed, 4946 insertions(+), 1 deletion(-) create mode 100644 cypress.config.js create mode 100644 package.json create mode 100644 system/seeders/000_fixmissing.sql create mode 100644 system/seeders/001_helper_functions.sql create mode 100644 system/seeders/002_mitarbeiter.sql create mode 100644 system/seeders/003_kompetenzfeld.sql create mode 100644 system/seeders/004_ort.sql create mode 100644 system/seeders/005_studenten_stg_1.sql create mode 100644 system/seeders/006_studiengang_3.sql create mode 100644 system/seeders/007_studenten_stg_3.sql create mode 100644 system/seeders/008_studiengang_4.sql create mode 100644 system/seeders/009_studenten_stg_4.sql create mode 100644 system/seeders/010_projektarbeiten_studiengang_4.sql create mode 100644 system/seeders/011_zeitwuensche.sql create mode 100644 system/seeders/012_cms_content.sql create mode 100644 system/seeders/013_studiengang_5.sql create mode 100644 system/seeders/014_betriebsmittel.sql create mode 100644 system/seeders/015_kalender.sql create mode 100644 system/seeders/016_benotungstool_noten.sql create mode 100644 system/seeders/017_extension_grants.sql create mode 100644 system/seeders/README.txt create mode 100644 system/setup_testinstance.php create mode 100644 tests/cypress/.env.example create mode 100644 tests/cypress/e2e/specs/api/noten/noten.access.cy.js create mode 100644 tests/cypress/e2e/specs/api/noten/noten.bulk.cy.js create mode 100644 tests/cypress/e2e/specs/api/noten/noten.freigabe.cy.js create mode 100644 tests/cypress/e2e/specs/api/noten/noten.frist.cy.js create mode 100644 tests/cypress/e2e/specs/api/noten/noten.notenschluessel.cy.js create mode 100644 tests/cypress/e2e/specs/api/noten/noten.notenvorschlag.cy.js create mode 100644 tests/cypress/e2e/specs/api/noten/noten.pruefungsantritte.cy.js create mode 100644 tests/cypress/suites/noten.js create mode 100644 tests/cypress/support/api/notenApi.js create mode 100644 tests/cypress/support/commands.js create mode 100644 tests/cypress/support/e2e.js create mode 100644 tests/cypress/support/helpers/notenErrors.js create mode 100644 tests/cypress/support/helpers/notenReset.js create mode 100644 tests/cypress/support/helpers/notenScenario.js create mode 100644 tests/cypress/support/helpers/notenTestData.js create mode 100644 tests/cypress/tasks/db.js create mode 100644 tests/cypress/tasks/notenDb.js create mode 100644 tests/cypress/tasks/sshTunnel.js create mode 100644 tests/cypress/tools/checks/noten.js create mode 100644 tests/cypress/tools/dbCheck.js diff --git a/application/controllers/api/frontend/v1/Noten.php b/application/controllers/api/frontend/v1/Noten.php index 15ea11b11..051257e44 100644 --- a/application/controllers/api/frontend/v1/Noten.php +++ b/application/controllers/api/frontend/v1/Noten.php @@ -373,7 +373,8 @@ class Noten extends FHCAPI_Controller // calculate notenvorschläge from teilnoten foreach($studentenData as $student) { - $student->anwquote = $anwresult[$student->prestudent_id]; + // null when the Anwesenheiten addon is absent - the column stays empty in the UI + $student->anwquote = $anwresult[$student->prestudent_id] ?? null; $g = $grades[$student->uid]['grades']; $note_lv = $grades[$student->uid]['note_lv']; diff --git a/cypress.config.js b/cypress.config.js new file mode 100644 index 000000000..4e250bae4 --- /dev/null +++ b/cypress.config.js @@ -0,0 +1,47 @@ +const { defineConfig } = require("cypress"); + +require("dotenv").config({ path: "tests/cypress/.env" }); + +// The suite being run: its cy.task handlers and its Cypress.env() values. +const suite = require("./tests/cypress/suites/noten"); + +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(", ")}`); + + suite.registerTasks(on); + + // otherwise the SSH forward outlives Cypress + on("after:run", () => require("./tests/cypress/tasks/sshTunnel").closeTunnel()); + + return config; + }, + }, + + env: { + // HTTP Basic + LDAP user the API calls authenticate as. + adminusername: process.env.USER_NAME, + adminpassword: process.env.USER_PASSWORD, + + ...suite.env, + }, +}); diff --git a/package.json b/package.json new file mode 100644 index 000000000..0ec784752 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "scripts": { + "cy:open": "cypress open", + "cy:run": "cypress run", + "cy:noten": "cypress run --spec \"tests/cypress/e2e/specs/api/noten/**/*.cy.js\"", + "cy:noten:p0": "cypress run --spec \"tests/cypress/e2e/specs/api/noten/noten.pruefungsantritte.cy.js,tests/cypress/e2e/specs/api/noten/noten.frist.cy.js,tests/cypress/e2e/specs/api/noten/noten.notenschluessel.cy.js\"", + "noten:check": "node tests/cypress/tools/dbCheck.js noten" + }, + "dependencies": { + "cypress": "^15.11.0", + "dotenv": "^17.4.2", + "pg": "^8.13.1", + "ssh2": "^1.17.0" + } +} diff --git a/system/seeders/000_fixmissing.sql b/system/seeders/000_fixmissing.sql new file mode 100644 index 000000000..31becbf65 --- /dev/null +++ b/system/seeders/000_fixmissing.sql @@ -0,0 +1,28 @@ +UPDATE public.tbl_funktion SET funktion_kurzbz='Student' WHERE funktion_kurzbz='stud'; + +UPDATE public.tbl_benutzer SET uid='demoadmin' WHERE uid='admin'; + +-- Add Missing functions +INSERT INTO public.tbl_funktion (funktion_kurzbz, beschreibung, aktiv, fachbereich, semester, hrrelevant, vertragsrelevant) VALUES('hsv', 'Hochschulvertretung', true, false, false, false, false); +INSERT INTO public.tbl_funktion (funktion_kurzbz, beschreibung, aktiv, fachbereich, semester, hrrelevant, vertragsrelevant) VALUES('jgv', 'Jahrgangsvertretung', true, false, true, false, false); +INSERT INTO public.tbl_funktion (funktion_kurzbz, beschreibung, aktiv, fachbereich, semester, hrrelevant, vertragsrelevant) VALUES('oezuordnung', 'Diszpl-Zuordnung', true, false, false, true, true); +INSERT INTO public.tbl_funktion (funktion_kurzbz, beschreibung, aktiv, fachbereich, semester, hrrelevant, vertragsrelevant) VALUES('fachzuordnung', 'Fachliche Zuordnung', true, false, false, true, true); + +-- Add Permissions to Admin Role +INSERT INTO "system".tbl_rolleberechtigung (berechtigung_kurzbz, rolle_kurzbz, art, anmerkung, insertamum, insertvon) VALUES('basis/cis', 'admin', 'suid', 'Zugriff auf CIS', now(), 'demoadmin'); + +INSERT INTO "system".tbl_benutzerrolle (rolle_kurzbz, berechtigung_kurzbz, uid, funktion_kurzbz, oe_kurzbz, art, studiensemester_kurzbz, "start", ende, negativ, updateamum, updatevon, insertamum, insertvon, kostenstelle_id, anmerkung) VALUES +(NULL, 'dashboard/benutzer', NULL, 'Mitarbeiter', NULL, 'suid', NULL, NULL, NULL, false, NULL, NULL, now(), 'demoadmin', NULL, NULL), +(NULL, 'dashboard/benutzer', NULL, 'Student', NULL, 'suid', NULL, NULL, NULL, false, NULL, NULL, now(), 'demoadmin', NULL, NULL) +; + +INSERT INTO "system".tbl_webservicetyp (webservicetyp_kurzbz, beschreibung) VALUES('content', 'CIS-Content ID'); + +SELECT setval('campus.seq_contentsprache', (select max(contentsprache_id) from campus.tbl_contentsprache), true); +SELECT setval('campus.seq_contentchild', (select max(contentchild_id) from campus.tbl_contentchild), true); + + +INSERT INTO public.tbl_gruppe (gruppe_kurzbz, studiengang_kz, semester, bezeichnung, beschreibung, sichtbar, lehre, aktiv, sort, mailgrp, generiert, updateamum, updatevon, insertamum, insertvon, ext_id, orgform_kurzbz, content_visible, gesperrt, direktinskription, zutrittssystem, aufnahmegruppe) VALUES +('MA', 0, NULL, 'MA', 'MA', false, false, true, NULL, false, false, now(), 'demoadmin', now(), 'demoadmin', NULL, NULL, true, false, false, false, false), +('STUD', 0, NULL, 'STUD', 'STUD', false, false, true, NULL, false, false, now(), 'demoadmin', now(), 'demoadmin', NULL, NULL, true, false, false, false, false) +; diff --git a/system/seeders/001_helper_functions.sql b/system/seeders/001_helper_functions.sql new file mode 100644 index 000000000..b1ed6d484 --- /dev/null +++ b/system/seeders/001_helper_functions.sql @@ -0,0 +1,36 @@ +CREATE OR REPLACE FUNCTION NearestWintersemester(var_entfernung integer) RETURNS varchar(32) AS $$ +DECLARE res varchar(32); + BEGIN + WITH nearestws AS + ( + SELECT studiensemester_kurzbz, start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz LIKE 'W%' AND ende>=now() ORDER BY start DESC LIMIT 1 + ) + SELECT studiensemester_kurzbz into res + FROM + ( + SELECT studiensemester_kurzbz, 0 as "entfernung" FROM nearestws + UNION + SELECT studiensemester_kurzbz, rank() over(order by start desc) as "entfernung" FROM public.tbl_studiensemester where start>(SELECT start FROM nearestws) + UNION + SELECT studiensemester_kurzbz, (-1 * rank() over(order by start desc)) as "entfernung" FROM public.tbl_studiensemester WHERE start<(SELECT start FROM nearestws) + ) x where entfernung=var_entfernung; + + return res; + END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION CurrentSemester() RETURNS varchar(32) AS $$ +DECLARE res varchar(32); + BEGIN + SELECT studiensemester_kurzbz into res FROM public.tbl_studiensemester WHERE start<=now() AND ende>=now() ORDER BY start DESC LIMIT 1; + return res; + END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION LastActiveSemester() RETURNS varchar(32) AS $$ +DECLARE res varchar(32); + BEGIN + SELECT studiensemester_kurzbz into res FROM public.tbl_studiensemester WHERE start<=now() ORDER BY start DESC LIMIT 1; + return res; + END; +$$ LANGUAGE plpgsql; diff --git a/system/seeders/002_mitarbeiter.sql b/system/seeders/002_mitarbeiter.sql new file mode 100644 index 000000000..03ac5cd5f --- /dev/null +++ b/system/seeders/002_mitarbeiter.sql @@ -0,0 +1,40 @@ +-- Demodaten fuer Mitarbeiter +-- Personen +INSERT INTO public.tbl_person (person_id, staatsbuergerschaft,geburtsnation,sprache,anrede,titelpost,titelpre,vorname,nachname,vornamen,gebdatum,gebort,gebzeit,foto,anmerkung,homepage,svnr,ersatzkennzeichen,familienstand,geschlecht,anzahlkinder,aktiv,insertamum,insertvon,updateamum,updatevon,ext_id,bundesland_code,kompetenzen,kurzbeschreibung,zugangscode,foto_sperre,udf_values,bpk,matr_aktiv,matr_nr,zugangscode_timestamp,wahlname,unruly) VALUES +(21,'A','A',NULL,'Herr',NULL,NULL,'Demo','Lektor 1',NULL,'1980-01-15',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c21',false,NULL,NULL,false,NULL,NULL,NULL,false), +(22,'A','A',NULL,'Herr',NULL,NULL,'Demo','Lektor 2',NULL,'1980-01-15',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c22',false,NULL,NULL,false,NULL,NULL,NULL,false), +(23,'A','A',NULL,'Herr',NULL,NULL,'Demo','Lektor 3',NULL,'1980-01-15',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c23',false,NULL,NULL,false,NULL,NULL,NULL,false), +(24,'A','A',NULL,'Herr',NULL,NULL,'Demo','Lektor 4',NULL,'1980-01-15',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c24',false,NULL,NULL,false,NULL,NULL,NULL,false), +(25,'A','A',NULL,'Herr',NULL,NULL,'Demo','Lektor 5',NULL,'1980-01-15',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c25',false,NULL,NULL,false,NULL,NULL,NULL,false), +(26,'A','A',NULL,'Frau',NULL,NULL,'Demo','Assistenz',NULL,'1985-01-15',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c26',false,NULL,NULL,false,NULL,NULL,NULL,false) +; + + +INSERT INTO public.tbl_benutzer (person_id, uid, aktiv, alias, insertamum, insertvon, updateamum, updatevon, updateaktivam, updateaktivvon, aktivierungscode) VALUES +('21', 'demolektor1' , true, null, now(), 'auto', null, null, null, false, null), +('22', 'demolektor2' , true, null, now(), 'auto', null, null, null, false, null), +('23', 'demolektor3' , true, null, now(), 'auto', null, null, null, false, null), +('24', 'demolektor4' , true, null, now(), 'auto', null, null, null, false, null), +('25', 'demolektor5' , true, null, now(), 'auto', null, null, null, false, null), +('26', 'demoassistenz' , true, null, now(), 'auto', null, null, null, false, null) +; + +INSERT INTO public.tbl_mitarbeiter (mitarbeiter_uid, personalnummer, telefonklappe, kurzbz, lektor, fixangestellt, stundensatz, ausbildungcode, ort_kurzbz, insertamum, insertvon, bismelden) VALUES +('demolektor1', 21, null, 'DemoLKT1', true, true, 80, null, null, now(), 'auto', true), +('demolektor2', 22, null, 'DemoLKT2', true, true, 80, null, null, now(), 'auto', true), +('demolektor3', 23, null, 'DemoLKT3', true, true, 80, null, null, now(), 'auto', true), +('demolektor4', 24, null, 'DemoLKT4', true, true, 80, null, null, now(), 'auto', true), +('demolektor5', 25, null, 'DemoLKT5', true, true, 80, null, null, now(), 'auto', true), +('demoassistenz', 26, null, 'DemoAss', true, true, 80, null, null, now(), 'auto', true) +; + + +INSERT INTO public.tbl_benutzergruppe (uid, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id, studiensemester_kurzbz) VALUES +('demoadmin', 'MA', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL), +('demolektor1', 'MA', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL), +('demolektor2', 'MA', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL), +('demolektor3', 'MA', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL), +('demolektor4', 'MA', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL), +('demolektor5', 'MA', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL), +('demoassistenz', 'MA', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL) +; diff --git a/system/seeders/003_kompetenzfeld.sql b/system/seeders/003_kompetenzfeld.sql new file mode 100644 index 000000000..1bcd9ed5d --- /dev/null +++ b/system/seeders/003_kompetenzfeld.sql @@ -0,0 +1,27 @@ +-- Kompetenzfedler +INSERT INTO public.tbl_organisationseinheittyp (organisationseinheittyp_kurzbz, bezeichnung, beschreibung) VALUES('Kompetenzfeld', 'Kompetenzfeld', 'Kompetenzfeld'); + +INSERT INTO public.tbl_organisationseinheit (oe_kurzbz, oe_parent_kurzbz, bezeichnung, organisationseinheittyp_kurzbz, aktiv, mailverteiler, freigabegrenze, kurzzeichen, lehre, standort, warn_semesterstunden_frei, warn_semesterstunden_fix, standort_id) VALUES + ('dep', 'etw', 'Fachabteilung', 'Abteilung', true, false, NULL, NULL, true, NULL, NULL, NULL, NULL), + ('kfSprachen', 'dep', 'Sprachen', 'Kompetenzfeld', true, false, NULL, NULL, true, NULL, NULL, NULL, NULL), + ('kfMath', 'dep', 'Mathematik', 'Kompetenzfeld', true, false, NULL, NULL, true, NULL, NULL, NULL, NULL), + ('kfTech', 'dep', 'Technik', 'Kompetenzfeld', true, false, NULL, NULL, true, NULL, NULL, NULL, NULL); + + INSERT INTO public.tbl_organisationseinheit +(oe_kurzbz, oe_parent_kurzbz, bezeichnung, organisationseinheittyp_kurzbz, aktiv, mailverteiler, freigabegrenze, kurzzeichen, lehre, standort, warn_semesterstunden_frei, warn_semesterstunden_fix, standort_id, fts_bezeichnung) +VALUES('Lehrgaenge', 'etw', 'Lehrgänge', 'Abteilung', true, false, NULL, NULL, false, NULL, NULL, NULL, NULL, '''abteilung'':1 ''lehrgänge'':2'::tsvector); +INSERT INTO public.tbl_organisationseinheit +(oe_kurzbz, oe_parent_kurzbz, bezeichnung, organisationseinheittyp_kurzbz, aktiv, mailverteiler, freigabegrenze, kurzzeichen, lehre, standort, warn_semesterstunden_frei, warn_semesterstunden_fix, standort_id, fts_bezeichnung) +VALUES('studiengaenge', 'etw', 'Studiengänge', 'Abteilung', true, false, NULL, NULL, false, NULL, NULL, NULL, NULL, '''abteilung'':1 ''studiengänge'':2'::tsvector); + +INSERT INTO public.tbl_studienjahr (studienjahr_kurzbz, bezeichnung) VALUES +('2026/27', 'Studienjahr 2026/27'), +('2027/28', 'Studienjahr 2027/28') +; + +INSERT INTO public.tbl_studiensemester (studiensemester_kurzbz, "start", ende, ext_id, bezeichnung, studienjahr_kurzbz, beschreibung, onlinebewerbung) VALUES +('WS2026', '2026-09-01', '2027-01-31', NULL, 'Wintersemester 2026/27', '2026/27', NULL, false), +('SS2027', '2027-02-01', '2027-07-01', NULL, 'Sommersemester 2027', '2026/27', NULL, false), +('WS2027', '2027-09-01', '2028-01-31', NULL, 'Wintersemester 2027/28', '2027/28', NULL, false), +('SS2028', '2028-02-01', '2028-07-01', NULL, 'Sommersemester 2028', '2027/28', NULL, false) +; diff --git a/system/seeders/004_ort.sql b/system/seeders/004_ort.sql new file mode 100644 index 000000000..5c811cb09 --- /dev/null +++ b/system/seeders/004_ort.sql @@ -0,0 +1,25 @@ +INSERT INTO public.tbl_ort +(ort_kurzbz, bezeichnung, planbezeichnung, max_person, lehre, reservieren, aktiv, lageplan, dislozierung, kosten, ausstattung, updateamum, updatevon, insertamum, insertvon, ext_id, stockwerk, telefonklappe, standort_id, content_id, m2, gebteil, oe_kurzbz, arbeitsplaetze) +VALUES('EG03', 'Besprechungsruam EG03', 'EG03', 8, true, true, true, NULL, NULL, NULL, NULL, '2023-08-24 14:18:50.000', 'oesi', '2023-08-24 14:17:30.000', 'oesi', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +INSERT INTO public.tbl_ort +(ort_kurzbz, bezeichnung, planbezeichnung, max_person, lehre, reservieren, aktiv, lageplan, dislozierung, kosten, ausstattung, updateamum, updatevon, insertamum, insertvon, ext_id, stockwerk, telefonklappe, standort_id, content_id, m2, gebteil, oe_kurzbz, arbeitsplaetze) +VALUES('EG01', 'Seminarraum EG01', 'EG01', 10, true, true, true, NULL, NULL, NULL, NULL, '2023-08-24 14:18:52.000', 'oesi', '2023-08-24 14:16:47.000', 'oesi', NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +INSERT INTO public.tbl_ort +(ort_kurzbz, bezeichnung, planbezeichnung, max_person, lehre, reservieren, aktiv, lageplan, dislozierung, kosten, ausstattung, updateamum, updatevon, insertamum, insertvon, ext_id, stockwerk, telefonklappe, standort_id, content_id, m2, gebteil, oe_kurzbz, arbeitsplaetze) +VALUES('EG02', 'Seminarraum EG02', 'EG02', 10, true, true, true, NULL, NULL, NULL, NULL, '2023-08-24 14:18:54.000', 'oesi', '2023-08-24 14:16:56.000', 'oesi', NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +INSERT INTO public.tbl_ort +(ort_kurzbz, bezeichnung, planbezeichnung, max_person, lehre, reservieren, aktiv, lageplan, dislozierung, kosten, ausstattung, updateamum, updatevon, insertamum, insertvon, ext_id, stockwerk, telefonklappe, standort_id, content_id, m2, gebteil, oe_kurzbz, arbeitsplaetze) +VALUES('EG04', 'EDV EG04', 'EG04', 20, true, true, true, NULL, NULL, NULL, NULL, '2023-08-24 14:18:55.000', 'oesi', '2023-08-24 14:17:49.000', 'oesi', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + +INSERT INTO public.tbl_ortraumtyp +(ort_kurzbz, hierarchie, raumtyp_kurzbz) +VALUES('EG01', 1, 'SEM'); +INSERT INTO public.tbl_ortraumtyp +(ort_kurzbz, hierarchie, raumtyp_kurzbz) +VALUES('EG02', 1, 'SEM'); +INSERT INTO public.tbl_ortraumtyp +(ort_kurzbz, hierarchie, raumtyp_kurzbz) +VALUES('EG04', 1, 'EDV2'); +INSERT INTO public.tbl_ortraumtyp +(ort_kurzbz, hierarchie, raumtyp_kurzbz) +VALUES('EG04', 2, 'EDV5/6'); diff --git a/system/seeders/005_studenten_stg_1.sql b/system/seeders/005_studenten_stg_1.sql new file mode 100644 index 000000000..e94249826 --- /dev/null +++ b/system/seeders/005_studenten_stg_1.sql @@ -0,0 +1,114 @@ +-- Demodaten fuer Studierende im Studiengang 1 + +INSERT INTO lehre.tbl_studienordnung +(studienordnung_id, studiengang_kz, "version", gueltigvon, gueltigbis, bezeichnung, ects, studiengangbezeichnung, studiengangbezeichnung_englisch, studiengangkurzbzlang, akadgrad_id, insertamum, insertvon, updateamum, updatevon, ext_id, status_kurzbz, standort_id) +VALUES(1, 1, '1', 'WS2023', NULL, 'BS1-WS2023', 180.00, 'Studiengang 1', 'Studiengang 1', 'STG1', NULL, '2023-08-24 14:25:41.728', 'oesi', NULL, NULL, NULL, 'development', NULL); + +INSERT INTO lehre.tbl_studienplan +(studienplan_id, studienordnung_id, orgform_kurzbz, "version", bezeichnung, regelstudiendauer, sprache, aktiv, semesterwochen, testtool_sprachwahl, insertamum, insertvon, updateamum, updatevon, ext_id, ects_stpl, pflicht_sws, pflicht_lvs, onlinebewerbung_studienplan) +VALUES(1, 1, 'VZ', '1', 'BS1-WS2023-VZ', 6, NULL, true, 15, true, '2023-08-24 14:26:12.751', 'oesi', NULL, NULL, NULL, 120.00, NULL, NULL, true); + +INSERT INTO lehre.tbl_studienplan_semester +( studienplan_id, studiensemester_kurzbz, semester) +VALUES(1, 'WS2025', 1); +INSERT INTO lehre.tbl_studienplan_semester +(studienplan_id, studiensemester_kurzbz, semester) +VALUES(1, 'SS2026', 1); + +-- Personen +INSERT INTO public.tbl_person (person_id, staatsbuergerschaft,geburtsnation,sprache,anrede,titelpost,titelpre,vorname,nachname,vornamen,gebdatum,gebort,gebzeit,foto,anmerkung,homepage,svnr,ersatzkennzeichen,familienstand,geschlecht,anzahlkinder,aktiv,insertamum,insertvon,updateamum,updatevon,ext_id,bundesland_code,kompetenzen,kurzbeschreibung,zugangscode,foto_sperre,udf_values,bpk,matr_aktiv,matr_nr,zugangscode_timestamp,wahlname,unruly) VALUES + +(101,'A','A',NULL,'Herr',NULL,NULL,'Leonardo','DiCaprio',NULL,'1974-11-11',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c101',false,NULL,NULL,false,NULL,NULL,NULL,false), +(102,'A','A',NULL,'Herr',NULL,NULL,'Robert','Downey Jr.',NULL,'1965-04-04',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c102',false,NULL,NULL,false,NULL,NULL,NULL,false), +(103,'A','A',NULL,'Herr',NULL,NULL,'Denzel','Washington',NULL,'1954-12-28',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c103',false,NULL,NULL,false,NULL,NULL,NULL,false), +(104,'A','A',NULL,'Herr',NULL,NULL,'Al','Pacino',NULL,'1940-04-25',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c104',false,NULL,NULL,false,NULL,NULL,NULL,false), +(105,'A','A',NULL,'Herr',NULL,NULL,'Morgan','Freeman',NULL,'1937-06-01',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c105',false,NULL,NULL,false,NULL,NULL,NULL,false), +(106,'A','A',NULL,'Herr',NULL,NULL,'Daniel','Day-Lewis',NULL,'1957-04-29',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c106',false,NULL,NULL,false,NULL,NULL,NULL,false), +(107,'A','A',NULL,'Herr',NULL,NULL,'Marlon','Brando',NULL,'1924-04-03',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c107',false,NULL,NULL,false,NULL,NULL,NULL,false), +(108,'A','A',NULL,'Herr',NULL,NULL,'Tom','Hanks',NULL,'1956-07-09',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c108',false,NULL,NULL,false,NULL,NULL,NULL,false), +(109,'A','A',NULL,'Herr',NULL,NULL,'Johnny','Depp',NULL,'1963-06-09',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c109',false,NULL,NULL,false,NULL,NULL,NULL,false), +(110,'A','A',NULL,'Herr',NULL,NULL,'Brad','Pitt',NULL,'1963-12-18',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c8110',false,NULL,NULL,false,NULL,NULL,NULL,false), +(111,'A','A',NULL,'Herr',NULL,NULL,'Will','Smith',NULL,'1968-09-25',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c111',false,NULL,NULL,false,NULL,NULL,NULL,false), +(112,'A','A',NULL,'Herr',NULL,NULL,'Keanu','Reeves',NULL,'1964-09-02',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c112',false,NULL,NULL,false,NULL,NULL,NULL,false), +(113,'A','A',NULL,'Herr',NULL,NULL,'Robert','De Niro',NULL,'1943-08-17',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647113',false,NULL,NULL,false,NULL,NULL,NULL,false), +(114,'A','A',NULL,'Herr',NULL,NULL,'Harrison','Ford',NULL,'1942-07-13',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c114',false,NULL,NULL,false,NULL,NULL,NULL,false), +(115,'A','A',NULL,'Herr',NULL,NULL,'Samuel L.','Jackson',NULL,'1948-12-21',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c115',false,NULL,NULL,false,NULL,NULL,NULL,false), +(116,'A','A',NULL,'Herr',NULL,NULL,'Christian','Bale',NULL,'1974-01-30',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c116',false,NULL,NULL,false,NULL,NULL,NULL,false), +(117,'A','A',NULL,'Herr',NULL,NULL,'Tom','Cruise',NULL,'1962-07-03',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c1117',false,NULL,NULL,false,NULL,NULL,NULL,false), +(118,'A','A',NULL,'Herr',NULL,NULL,'Arnold','Schwarzenegger',NULL,'1947-07-30',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c118',false,NULL,NULL,false,NULL,NULL,NULL,false), +(119,'A','A',NULL,'Herr',NULL,NULL,'Sylvester','Stallone',NULL,'1946-07-06',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c119',false,NULL,NULL,false,NULL,NULL,NULL,false), +(120,'A','A',NULL,'Herr',NULL,NULL,'Karl','Klammer',NULL,'1963-06-09',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c120',false,NULL,NULL,false,NULL,NULL,NULL,false) +; +SELECT setval('public.tbl_person_person_id_seq', 1000,true); + +-- Prestudenten +INSERT INTO public.tbl_prestudent (prestudent_id, person_id,studiengang_kz,aufmerksamdurch_kurzbz,berufstaetigkeit_code,ausbildungcode,zgv_code,zgvort,zgvdatum,zgvmas_code,zgvmaort,zgvmadatum,aufnahmeschluessel,facheinschlberuf,reihungstest_id,anmeldungreihungstest,reihungstestangetreten,rt_gesamtpunkte,bismelden,insertamum,insertvon,updateamum,updatevon,ext_id,anmerkung,dual,rt_punkte1,rt_punkte2,ausstellungsstaat,rt_punkte3,udf_values,priorisierung,zgvdoktor_code,zgvdoktorort,zgvdoktordatum,mentor,zgvnation,zgvmanation,zgvdoktornation,gsstudientyp_kurzbz,aufnahmegruppe_kurzbz,foerderrelevant,standort_code,zgv_erfuellt,zgvmas_erfuellt,zgvdoktor_erfuellt) VALUES + (1101,101,1,'k.A.',NULL,NULL,9,NULL,'2020-06-01',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1102,102,1,'k.A.',NULL,NULL,9,NULL,'2020-06-02',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1103,103,1,'k.A.',NULL,NULL,9,NULL,'2020-06-03',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1104,104,1,'k.A.',NULL,NULL,9,NULL,'2020-06-04',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1105,105,1,'k.A.',NULL,NULL,9,NULL,'2020-06-05',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1106,106,1,'k.A.',NULL,NULL,9,NULL,'2020-06-06',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1107,107,1,'k.A.',NULL,NULL,9,NULL,'2020-06-07',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1108,108,1,'k.A.',NULL,NULL,9,NULL,'2020-06-08',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1109,109,1,'k.A.',NULL,NULL,9,NULL,'2020-06-09',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1110,110,1,'k.A.',NULL,NULL,9,NULL,'2020-06-10',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1111,111,1,'k.A.',NULL,NULL,9,NULL,'2020-06-01',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1112,112,1,'k.A.',NULL,NULL,9,NULL,'2020-06-02',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1113,113,1,'k.A.',NULL,NULL,9,NULL,'2020-06-03',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1114,114,1,'k.A.',NULL,NULL,9,NULL,'2020-06-04',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1115,115,1,'k.A.',NULL,NULL,9,NULL,'2020-06-05',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1116,116,1,'k.A.',NULL,NULL,9,NULL,'2020-06-06',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1117,117,1,'k.A.',NULL,NULL,9,NULL,'2020-06-07',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1118,118,1,'k.A.',NULL,NULL,9,NULL,'2020-06-08',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1119,119,1,'k.A.',NULL,NULL,9,NULL,'2020-06-09',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (1120,120,1,'k.A.',NULL,NULL,9,NULL,'2020-06-10',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false) +; +SELECT setval('tbl_prestudent_prestudent_id_seq', 2000,true); + +-- Prestudent Status + +WITH status AS ( + SELECT * FROM (VALUES + ('Interessent','WS2025',1,'2025-01-15','2023-08-24 14:56:04','auto',NULL,NULL,NULL,1,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Bewerber','WS2025',1,'2025-02-20','2023-08-24 14:58:11','auto',NULL,NULL,NULL,1,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Aufgenommener','WS2025',1,'2025-05-01','2023-08-24 14:58:14','auto',NULL,NULL,NULL,1,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student','WS2025',1,'2025-08-24','2023-08-24 14:58:16','auto',NULL,NULL,NULL,1,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL) + ) AS t(status_kurzbz,studiensemester_kurzbz,ausbildungssemester,datum,insertamum,insertvon,updateamum,updatevon,orgform_kurzbz,studienplan_id,bestaetigtam,bestaetigtvon,fgm,faktiv,anmerkung,bewerbung_abgeschicktamum,rt_stufe,statusgrund_id) +), +inserts AS ( + SELECT generate_series(1101, 1120) AS prestudent_id,* + FROM status +) +INSERT INTO public.tbl_prestudentstatus (prestudent_id, status_kurzbz,studiensemester_kurzbz,ausbildungssemester,datum,insertamum,insertvon,updateamum,updatevon,orgform_kurzbz,studienplan_id,bestaetigtam,bestaetigtvon,fgm,faktiv,anmerkung,bewerbung_abgeschicktamum,rt_stufe,statusgrund_id) +SELECT prestudent_id, status_kurzbz, studiensemester_kurzbz, ausbildungssemester, datum::date, insertamum::timestamp, insertvon, updateamum::timestamp, updatevon,orgform_kurzbz,studienplan_id,bestaetigtam::date,bestaetigtvon,fgm::smallint,faktiv,anmerkung,bewerbung_abgeschicktamum::timestamp,rt_stufe::smallint,statusgrund_id::int +FROM inserts; + +-- tbl_benutzer +WITH persons AS ( + SELECT generate_series(101, 120) AS person_id +) +INSERT INTO public.tbl_benutzer (person_id, uid, aktiv, alias, insertamum, insertvon, updateamum, updatevon, updateaktivam, updateaktivvon, aktivierungscode) +SELECT person_id, 's125b' || person_id, true, null, now(), 'auto', null, null, null, false, null +FROM persons; + +-- tbl_student +WITH persons AS ( + SELECT generate_series(101, 120) AS person_id +) +INSERT INTO public.tbl_student (student_uid, matrikelnr, prestudent_id, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT 's125b' || person_id, '2510001' || person_id, ('1' || person_id)::integer, '1', '1', '','', null, null, now(), 'auto' +FROM persons; + +-- tbl_studentlehrverband +WITH persons AS ( + SELECT generate_series(101, 120) AS person_id +), +students as ( + SELECT ('s125b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, 'WS2025', '1','1','','', null, null, now(), 'auto' +FROM students; + diff --git a/system/seeders/006_studiengang_3.sql b/system/seeders/006_studiengang_3.sql new file mode 100644 index 000000000..087b741b2 --- /dev/null +++ b/system/seeders/006_studiengang_3.sql @@ -0,0 +1,161 @@ +-- Demodaten fuer Studiengang 3: Organisationseinheit, Studienordnung, Studiengang, Lehrveranstaltungen + +INSERT INTO public.tbl_organisationseinheit (oe_kurzbz, oe_parent_kurzbz, bezeichnung, organisationseinheittyp_kurzbz, aktiv, mailverteiler, freigabegrenze, kurzzeichen, lehre, standort, warn_semesterstunden_frei, warn_semesterstunden_fix, standort_id) +VALUES('stg3', 'studiengaenge', 'Studiengang 3', 'Studiengang', true, false, NULL, NULL, true, NULL, NULL, NULL, NULL); + +INSERT INTO public.tbl_studiengang (studiengang_kz,kurzbz,kurzbzlang,typ,bezeichnung,english,farbe,email,telefon,max_semester,max_verband,max_gruppe,erhalter_kz,bescheid,bescheidbgbl1,bescheidbgbl2,bescheidgz,bescheidvom,titelbescheidvom,aktiv,ext_id,orgform_kurzbz,zusatzinfo_html,moodle,oe_kurzbz,sprache,testtool_sprachwahl,studienplaetze,lgartcode,mischform,projektarbeit_note_anzeige,melderelevant,foerderrelevant,standort_code,onlinebewerbung,melde_studiengang_kz) VALUES + (3,'S3','STG3','b','Studiengang 3','Studiengang 3',NULL,'invalid@example.com',NULL,6,'B','2',5,NULL,NULL,NULL,NULL,NULL,NULL,true,NULL,'VZ','',true,'stg3','German',true,NULL,NULL,false,true,true,true,NULL,false,'0002') +; + +INSERT INTO "system".tbl_benutzerrolle +(rolle_kurzbz, berechtigung_kurzbz, uid, funktion_kurzbz, oe_kurzbz, art, studiensemester_kurzbz, "start", ende, negativ, updateamum, updatevon, insertamum, insertvon, kostenstelle_id, anmerkung) +VALUES('assistenz', NULL, 'demoassistenz', NULL, 'stg3', 'suid', NULL, '2025-09-18', NULL, false, NULL, NULL, '2025-09-18 15:46:02.000', 'demoadmin', NULL, NULL); + +INSERT INTO public.tbl_lehrverband (studiengang_kz, semester, verband, gruppe, aktiv, bezeichnung, ext_id, orgform_kurzbz) VALUES + (3, 1, '','', true, null, null, null), + (3, 1, 'A','', true, null, null, null), + (3, 1, 'B','', true, null, null, null), + (3, 2, '','', true, null, null, null), + (3, 2, 'A','', true, null, null, null), + (3, 2, 'B','', true, null, null, null), + (3, 3, '','', true, null, null, null), + (3, 3, 'A','', true, null, null, null), + (3, 3, 'B','', true, null, null, null), + (3, 4, '','', true, null, null, null), + (3, 4, 'A','', true, null, null, null), + (3, 4, 'B','', true, null, null, null), + (3, 5, '','', true, null, null, null), + (3, 5, 'A','', true, null, null, null), + (3, 5, 'B','', true, null, null, null), + (3, 6, '','', true, null, null, null), + (3, 6, 'A','', true, null, null, null), + (3, 6, 'B','', true, null, null, null) +; + +INSERT INTO lehre.tbl_studienordnung (studienordnung_id, studiengang_kz,"version",gueltigvon,gueltigbis,bezeichnung,ects,studiengangbezeichnung,studiengangbezeichnung_englisch,studiengangkurzbzlang,akadgrad_id,insertamum,insertvon,updateamum,updatevon,ext_id,status_kurzbz,standort_id) VALUES + (301, 3,'1','WS2023',NULL,'BS3-WS2023',180.00,'Studiengang 3','Studiengang 3','STG3',NULL,'2023-08-24 14:25:41.728215','auto',NULL,NULL,NULL,'development',NULL); + +INSERT INTO lehre.tbl_studienplan (studienplan_id, studienordnung_id,orgform_kurzbz,"version",bezeichnung,regelstudiendauer,sprache,aktiv,semesterwochen,testtool_sprachwahl,insertamum,insertvon,updateamum,updatevon,ext_id,ects_stpl,pflicht_sws,pflicht_lvs,onlinebewerbung_studienplan) VALUES + (3011, 301,'VZ','1','BS3-WS2023-VZ',6,NULL,true,15,true,'2023-08-24 14:26:12.751416','auto',NULL,NULL,NULL,180.00,NULL,NULL,true); + +INSERT INTO lehre.tbl_lehrveranstaltung (lehrveranstaltung_id, kurzbz,bezeichnung,studiengang_kz,semester,sprache,ects,semesterstunden,anmerkung,lehre,lehreverzeichnis,aktiv,planfaktor,planlektoren,planpersonalkosten,plankostenprolektor,updateamum,updatevon,insertamum,insertvon,ext_id,sort,zeugnis,koordinator,projektarbeit,lehrform_kurzbz,bezeichnung_english,orgform_kurzbz,incoming,lehrmodus_kurzbz,lehrtyp_kurzbz,oe_kurzbz,raumtyp_kurzbz,anzahlsemester,semesterwochen,lvnr,farbe,old_lehrfach_id,semester_alternativ,sws,lvs,alvs,lvps,las,benotung,lvinfo,lehrauftrag,lehrveranstaltung_template_id) VALUES + (311, 'MOD1.1','Sport',3,1,'German',8.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Communication','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (312, 'MOD1.2','Mathematik',3,1,'German',8.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Mathematics','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (313, 'MOD1.3','Grundlagen',3,1,'German',8.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Basics','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (314, 'MOD1.4','Sprachen',3,1,'English',6.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Communication','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (321, 'MOD2.1','Labor',3,2,'German',10.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Laboratory','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (322, 'MOD2.2','Mathematik',3,2,'German',10.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Mathematics','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (323, 'MOD2.3','Sprachen',3,2,'English',10.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Communication','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (331, 'MOD3.1','Mathematik',3,3,'German',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Mathematics','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (332, 'MOD3.2','Elektrische Signale',3,3,'German',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Electric Signals','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (341, 'MOD4.1','Messtechnik',3,4,'German',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Measurement Technology','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (342, 'MOD4.2','Forschung',3,4,'German',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Research','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (351, 'MOD5.1','Communication',3,5,'English',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Communication','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (352, 'MOD5.2','Regelungstechnik',3,5,'German',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Control Engineering','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (361, 'MOD6.1','Bachelorarbeit',3,6,'German',30.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Bachelor Thesis','VZ',5,'regulaer','modul','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL) + ; + + INSERT INTO lehre.tbl_lehrveranstaltung (lehrveranstaltung_id, kurzbz,bezeichnung,studiengang_kz,semester,sprache,ects,semesterstunden,anmerkung,lehre,lehreverzeichnis,aktiv,planfaktor,planlektoren,planpersonalkosten,plankostenprolektor,updateamum,updatevon,insertamum,insertvon,ext_id,sort,zeugnis,koordinator,projektarbeit,lehrform_kurzbz,bezeichnung_english,orgform_kurzbz,incoming,lehrmodus_kurzbz,lehrtyp_kurzbz,oe_kurzbz,raumtyp_kurzbz,anzahlsemester,semesterwochen,lvnr,farbe,old_lehrfach_id,semester_alternativ,sws,lvs,alvs,lvps,las,benotung,lvinfo,lehrauftrag,lehrveranstaltung_template_id) VALUES + (3111, 'LS','Leistungssport',3,1,'German',8.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'VO','Communication','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3121, 'MAT','Mathematik 1',3,1,'German',8.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Mathematics','VZ',5,'regulaer','lv','kfMath',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3131, 'GL','Grundlagen der Programmierung',3,1,'German',8.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Development Basics','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3141, 'ENG','Englisch 1',3,1,'English',6.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'VO','Communication','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3211, 'EL','Elektronik Labor',3,2,'German',10.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'LAB','Laboratory','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3221, 'MAT','Mathematik 2',3,2,'German',10.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Mathematics','VZ',5,'regulaer','lv','kfMath',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3231, 'ENG','Communcation Englisch',3,2,'English',10.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'VO','Communication','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3311, 'MAT','Mathematik 3',3,3,'German',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Mathematics','VZ',5,'regulaer','lv','kfMath',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3321, 'ES','Elektrische Signale',3,3,'German',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Electric Signals','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3411, 'MT','Messtechnik',3,4,'German',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Measurement Technology','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3421, 'BAN','Bewegungsanalyse',3,4,'German',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Movement Analytics','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3511, 'ENG','Communication',3,5,'English',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'VO','Communication','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3521, 'RT','Regelungstechnik',3,5,'German',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Control Engineering','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (3611, 'BA','Bachelorarbeit',3,6,'German',30.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'SE','Bachelor Thesis','VZ',5,'regulaer','lv','stg3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL) + ; + + INSERT INTO lehre.tbl_studienplan_lehrveranstaltung (studienplan_lehrveranstaltung_id, studienplan_id,lehrveranstaltung_id,semester,studienplan_lehrveranstaltung_id_parent,pflicht,koordinator,insertamum,insertvon,updateamum,updatevon,sort,ext_id,curriculum,export,genehmigung) VALUES + -- MODULE im Studienplan + (301, 3011,311,1,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (302, 3011,312,1,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (303, 3011,313,1,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (304, 3011,314,1,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (305, 3011,321,2,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (306, 3011,322,2,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (307, 3011,323,2,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (308, 3011,331,3,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (309, 3011,332,3,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (310, 3011,341,4,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (311, 3011,342,4,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (312, 3011,351,5,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (313, 3011,352,5,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (314, 3011,361,6,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + + -- LVS zu Modulen + (315, 3011,3111,1,301,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (316, 3011,3121,1,302,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (317, 3011,3131,1,303,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (318, 3011,3141,1,304,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (319, 3011,3211,2,305,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (320, 3011,3221,2,306,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (321, 3011,3231,2,307,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (322, 3011,3311,3,308,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (323, 3011,3321,3,309,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (324, 3011,3411,4,310,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (325, 3011,3421,4,311,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (326, 3011,3511,5,312,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (327, 3011,3521,5,313,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (328, 3011,3611,6,314,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true) +; + +INSERT INTO lehre.tbl_studienplan_semester (studienplan_id, studiensemester_kurzbz, semester) VALUES +(3011, 'WS2023', 1), +(3011, 'SS2024', 2), +(3011, 'WS2024', 1), +(3011, 'WS2024', 3), +(3011, 'SS2025', 2), +(3011, 'SS2025', 4), +(3011, 'WS2025', 1), +(3011, 'WS2025', 3), +(3011, 'WS2025', 5), +(3011, 'SS2026', 2), +(3011, 'SS2026', 4), +(3011, 'SS2026', 6); + + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, anmerkung, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht) +VALUES(31001, 3131, 'WS2025', NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, NULL, 3131, NULL, '2023-08-24 14:54:43.000', 'oesi', '2023-08-24 14:54:43.000', 'oesi', NULL, 3131, 1); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(31001, 3, 1, 'A', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(31001, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'oesi', NULL, 0.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, anmerkung, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht) +VALUES(31002, 3131, 'WS2025', NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, NULL, 3131, NULL, '2023-08-24 14:54:43.000', 'oesi', '2023-08-24 14:54:43.000', 'oesi', NULL, 3131, 1); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(31002, 3, 1, 'B', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(31002, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'oesi', NULL, 0.00, NULL, NULL); + +-- Stundenplan Eintraege +INSERT INTO lehre.tbl_stundenplandev +(stundenplandev_id, lehreinheit_id, unr, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, mitarbeiter_uid, ort_kurzbz, datum, stunde, titel, anmerkung, fix, updateamum, updatevon, insertamum, insertvon) +VALUES(310011, 31001, 31001, 3, 1, 'A', '', NULL, 'demolektor1', 'EG04', '2025-07-31', 7, NULL, NULL, false, '2025-07-31 13:50:49.419', 'demoadmin', '2025-07-31 13:50:49.419', NULL); + +INSERT INTO lehre.tbl_stundenplan +(stundenplan_id, lehreinheit_id, unr, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, mitarbeiter_uid, ort_kurzbz, datum, stunde, titel, anmerkung, fix, updateamum, updatevon, insertamum, insertvon) +VALUES(310011, 31001, 31001, 3, 1, 'A', '', NULL, 'demolektor1', 'EG04', '2025-07-31', 7, NULL, NULL, false, '2025-07-31 13:50:49.419', 'demoadmin', '2025-07-31 13:50:49.419', NULL); + +INSERT INTO lehre.tbl_stundenplandev +(stundenplandev_id, lehreinheit_id, unr, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, mitarbeiter_uid, ort_kurzbz, datum, stunde, titel, anmerkung, fix, updateamum, updatevon, insertamum, insertvon) +VALUES(310021, 31002, 31002, 3, 1, 'B', '', NULL, 'demolektor1', 'EG04', '2025-08-01', 7, NULL, NULL, false, '2025-07-31 13:50:49.419', 'demoadmin', '2025-07-31 13:50:49.419', NULL); + +INSERT INTO lehre.tbl_stundenplan +(stundenplan_id, lehreinheit_id, unr, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, mitarbeiter_uid, ort_kurzbz, datum, stunde, titel, anmerkung, fix, updateamum, updatevon, insertamum, insertvon) +VALUES(310021, 31002, 31002, 3, 1, 'B', '', NULL, 'demolektor1', 'EG04', '2025-08-01', 7, NULL, NULL, false, '2025-07-31 13:50:49.419', 'demoadmin', '2025-07-31 13:50:49.419', NULL); diff --git a/system/seeders/007_studenten_stg_3.sql b/system/seeders/007_studenten_stg_3.sql new file mode 100644 index 000000000..89b0074bf --- /dev/null +++ b/system/seeders/007_studenten_stg_3.sql @@ -0,0 +1,120 @@ +-- Demodaten fuer Studierende im Studiengang 3 +-- Personen +INSERT INTO public.tbl_person (person_id, staatsbuergerschaft,geburtsnation,sprache,anrede,titelpost,titelpre,vorname,nachname,vornamen,gebdatum,gebort,gebzeit,foto,anmerkung,homepage,svnr,ersatzkennzeichen,familienstand,geschlecht,anzahlkinder,aktiv,insertamum,insertvon,updateamum,updatevon,ext_id,bundesland_code,kompetenzen,kurzbeschreibung,zugangscode,foto_sperre,udf_values,bpk,matr_aktiv,matr_nr,zugangscode_timestamp,wahlname,unruly) VALUES + +(301,'A','A',NULL,'Herr',NULL,NULL,'Elon','Musk',NULL,'1974-11-11',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c301',false,NULL,NULL,false,NULL,NULL,NULL,false), +(302,'A','A',NULL,'Herr',NULL,NULL,'Steve','Jobs',NULL,'1965-04-04',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c302',false,NULL,NULL,false,NULL,NULL,NULL,false), +(303,'A','A',NULL,'Herr',NULL,NULL,'Bill','Gates',NULL,'1954-12-28',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c303',false,NULL,NULL,false,NULL,NULL,NULL,false), +(304,'A','A',NULL,'Herr',NULL,NULL,'Linus','Torvalds',NULL,'1940-04-25',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c304',false,NULL,NULL,false,NULL,NULL,NULL,false), +(305,'A','A',NULL,'Herr',NULL,NULL,'Mark','Zuckerberg',NULL,'1937-06-01',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c305',false,NULL,NULL,false,NULL,NULL,NULL,false), +(306,'A','A',NULL,'Frau',NULL,NULL,'Ada','Lovelace',NULL,'1957-04-29',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c306',false,NULL,NULL,false,NULL,NULL,NULL,false), +(307,'A','A',NULL,'Herr',NULL,NULL,'Tim','Berners-Lee',NULL,'1924-04-03',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c307',false,NULL,NULL,false,NULL,NULL,NULL,false), +(308,'A','A',NULL,'Frau',NULL,NULL,'Grace','Hopper',NULL,'1956-07-09',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c308',false,NULL,NULL,false,NULL,NULL,NULL,false), +(309,'A','A',NULL,'Herr',NULL,NULL,'Jeff','Bezos',NULL,'1963-06-09',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c309',false,NULL,NULL,false,NULL,NULL,NULL,false), +(310,'A','A',NULL,'Herr',NULL,NULL,'Larry','Page',NULL,'1963-12-18',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c8310',false,NULL,NULL,false,NULL,NULL,NULL,false), +(311,'A','A',NULL,'Herr',NULL,NULL,'Sergey','Brin',NULL,'1968-09-25',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c311',false,NULL,NULL,false,NULL,NULL,NULL,false), +(312,'A','A',NULL,'Frau',NULL,NULL,'Katherine','Johnson',NULL,'1964-09-02',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c312',false,NULL,NULL,false,NULL,NULL,NULL,false), +(313,'A','A',NULL,'Herr',NULL,NULL,'Dennis','Ritchie',NULL,'1943-08-17',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647313',false,NULL,NULL,false,NULL,NULL,NULL,false), +(314,'A','A',NULL,'Frau',NULL,NULL,'Margaret','Hamilton',NULL,'1942-07-13',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c314',false,NULL,NULL,false,NULL,NULL,NULL,false), +(315,'A','A',NULL,'Herr',NULL,NULL,'Satya','Nadella',NULL,'1948-12-21',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c315',false,NULL,NULL,false,NULL,NULL,NULL,false), +(316,'A','A',NULL,'Frau',NULL,NULL,'Susan','Wojcicki',NULL,'1974-01-30',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c316',false,NULL,NULL,false,NULL,NULL,NULL,false), +(317,'A','A',NULL,'Frau',NULL,NULL,'Sheryl','Sandberg',NULL,'1962-07-03',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c317',false,NULL,NULL,false,NULL,NULL,NULL,false), +(318,'A','A',NULL,'Herr',NULL,NULL,'Jack','Dorsey',NULL,'1947-07-30',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c318',false,NULL,NULL,false,NULL,NULL,NULL,false), +(319,'A','A',NULL,'Frau',NULL,NULL,'Ginni','Rometty',NULL,'1946-07-06',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c319',false,NULL,NULL,false,NULL,NULL,NULL,false), +(320,'A','A',NULL,'Herr',NULL,NULL,'Sundar','Pichai',NULL,'1963-06-09',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c320',false,NULL,NULL,false,NULL,NULL,NULL,false) +; +SELECT setval('public.tbl_person_person_id_seq', 1000,true); + +-- Prestudenten +INSERT INTO public.tbl_prestudent (prestudent_id, person_id,studiengang_kz,aufmerksamdurch_kurzbz,berufstaetigkeit_code,ausbildungcode,zgv_code,zgvort,zgvdatum,zgvmas_code,zgvmaort,zgvmadatum,aufnahmeschluessel,facheinschlberuf,reihungstest_id,anmeldungreihungstest,reihungstestangetreten,rt_gesamtpunkte,bismelden,insertamum,insertvon,updateamum,updatevon,ext_id,anmerkung,dual,rt_punkte1,rt_punkte2,ausstellungsstaat,rt_punkte3,udf_values,priorisierung,zgvdoktor_code,zgvdoktorort,zgvdoktordatum,mentor,zgvnation,zgvmanation,zgvdoktornation,gsstudientyp_kurzbz,aufnahmegruppe_kurzbz,foerderrelevant,standort_code,zgv_erfuellt,zgvmas_erfuellt,zgvdoktor_erfuellt) VALUES + (3301,301,3,'k.A.',NULL,NULL,9,NULL,'2020-06-01',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3302,302,3,'k.A.',NULL,NULL,9,NULL,'2020-06-02',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3303,303,3,'k.A.',NULL,NULL,9,NULL,'2020-06-03',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3304,304,3,'k.A.',NULL,NULL,9,NULL,'2020-06-04',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3305,305,3,'k.A.',NULL,NULL,9,NULL,'2020-06-05',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3306,306,3,'k.A.',NULL,NULL,9,NULL,'2020-06-06',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3307,307,3,'k.A.',NULL,NULL,9,NULL,'2020-06-07',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3308,308,3,'k.A.',NULL,NULL,9,NULL,'2020-06-08',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3309,309,3,'k.A.',NULL,NULL,9,NULL,'2020-06-09',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3310,310,3,'k.A.',NULL,NULL,9,NULL,'2020-06-10',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3311,311,3,'k.A.',NULL,NULL,9,NULL,'2020-06-01',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3312,312,3,'k.A.',NULL,NULL,9,NULL,'2020-06-02',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3313,313,3,'k.A.',NULL,NULL,9,NULL,'2020-06-03',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3314,314,3,'k.A.',NULL,NULL,9,NULL,'2020-06-04',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3315,315,3,'k.A.',NULL,NULL,9,NULL,'2020-06-05',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3316,316,3,'k.A.',NULL,NULL,9,NULL,'2020-06-06',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3317,317,3,'k.A.',NULL,NULL,9,NULL,'2020-06-07',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3318,318,3,'k.A.',NULL,NULL,9,NULL,'2020-06-08',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3319,319,3,'k.A.',NULL,NULL,9,NULL,'2020-06-09',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (3320,320,3,'k.A.',NULL,NULL,9,NULL,'2020-06-10',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false) +; +SELECT setval('tbl_prestudent_prestudent_id_seq', (SELECT max(prestudent_id) FROM public.tbl_prestudent),true); + +-- Prestudent Status + +WITH status AS ( + SELECT * FROM (VALUES + ('Interessent','WS2025',1,'2025-01-15','2023-08-24 14:56:04','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Bewerber','WS2025',1,'2025-02-20','2023-08-24 14:58:11','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Aufgenommener','WS2025',1,'2025-05-01','2023-08-24 14:58:14','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student','WS2025',1,'2025-08-24','2023-08-24 14:58:16','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL) + ) AS t(status_kurzbz,studiensemester_kurzbz,ausbildungssemester,datum,insertamum,insertvon,updateamum,updatevon,orgform_kurzbz,studienplan_id,bestaetigtam,bestaetigtvon,fgm,faktiv,anmerkung,bewerbung_abgeschicktamum,rt_stufe,statusgrund_id) +), +inserts AS ( + SELECT generate_series(3301, 3320) AS prestudent_id,* + FROM status +) +INSERT INTO public.tbl_prestudentstatus (prestudent_id, status_kurzbz,studiensemester_kurzbz,ausbildungssemester,datum,insertamum,insertvon,updateamum,updatevon,orgform_kurzbz,studienplan_id,bestaetigtam,bestaetigtvon,fgm,faktiv,anmerkung,bewerbung_abgeschicktamum,rt_stufe,statusgrund_id) +SELECT prestudent_id, status_kurzbz, studiensemester_kurzbz, ausbildungssemester, datum::date, insertamum::timestamp, insertvon, updateamum::timestamp, updatevon,orgform_kurzbz,studienplan_id,bestaetigtam::date,bestaetigtvon,fgm::smallint,faktiv,anmerkung,bewerbung_abgeschicktamum::timestamp,rt_stufe::smallint,statusgrund_id::int +FROM inserts; + +-- tbl_benutzer +WITH persons AS ( + SELECT generate_series(301, 320) AS person_id +) +INSERT INTO public.tbl_benutzer (person_id, uid, aktiv, alias, insertamum, insertvon, updateamum, updatevon, updateaktivam, updateaktivvon, aktivierungscode) +SELECT person_id, 's325b' || person_id, true, null, now(), 'auto', null, null, null, false, null +FROM persons; + +-- tbl_student +-- A Verband +WITH persons AS ( + SELECT generate_series(301, 310) AS person_id +) +INSERT INTO public.tbl_student (student_uid, matrikelnr, prestudent_id, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT 's325b' || person_id, '2510003' || person_id, ('3' || person_id)::integer, '3', '1', 'A','', null, null, now(), 'auto' +FROM persons; + +-- B Verband +WITH persons AS ( + SELECT generate_series(311, 320) AS person_id +) +INSERT INTO public.tbl_student (student_uid, matrikelnr, prestudent_id, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT 's325b' || person_id, '2510003' || person_id, ('3' || person_id)::integer, '3', '1', 'B','', null, null, now(), 'auto' +FROM persons; + +-- tbl_studentlehrverband +-- A Verband +WITH persons AS ( + SELECT generate_series(301, 310) AS person_id +), +students as ( + SELECT ('s325b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, 'WS2025', '3','1','A','', null, null, now(), 'auto' +FROM students; + +-- B Verband +WITH persons AS ( + SELECT generate_series(311, 320) AS person_id +), +students as ( + SELECT ('s325b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, 'WS2025', '3','1','B','', null, null, now(), 'auto' +FROM students; + diff --git a/system/seeders/008_studiengang_4.sql b/system/seeders/008_studiengang_4.sql new file mode 100644 index 000000000..957e14c81 --- /dev/null +++ b/system/seeders/008_studiengang_4.sql @@ -0,0 +1,239 @@ +-- Demodaten fuer Studiengang 4: Organisationseinheit, Studienordnung, Studiengang, Lehrveranstaltungen, Lehreinheiten +-- Testdaten fuer LV-Verwaltung, Tempus + +INSERT INTO public.tbl_organisationseinheit (oe_kurzbz, oe_parent_kurzbz, bezeichnung, organisationseinheittyp_kurzbz, aktiv, mailverteiler, freigabegrenze, kurzzeichen, lehre, standort, warn_semesterstunden_frei, warn_semesterstunden_fix, standort_id) +VALUES('stg4', 'studiengaenge', 'Tempus Examples', 'Studiengang', true, false, NULL, NULL, true, NULL, NULL, NULL, NULL); + +INSERT INTO public.tbl_studiengang (studiengang_kz,kurzbz,kurzbzlang,typ,bezeichnung,english,farbe,email,telefon,max_semester,max_verband,max_gruppe,erhalter_kz,bescheid,bescheidbgbl1,bescheidbgbl2,bescheidgz,bescheidvom,titelbescheidvom,aktiv,ext_id,orgform_kurzbz,zusatzinfo_html,moodle,oe_kurzbz,sprache,testtool_sprachwahl,studienplaetze,lgartcode,mischform,projektarbeit_note_anzeige,melderelevant,foerderrelevant,standort_code,onlinebewerbung,melde_studiengang_kz) VALUES + (4,'S4','STG4','b','Tempus Beispiele','Tempus Examples',NULL,'invalid@example.com',NULL,6,'B','2',5,NULL,NULL,NULL,NULL,NULL,NULL,true,NULL,'VZ','',true,'stg4','German',true,NULL,NULL,false,true,true,true,NULL,false,'0004') +; + +INSERT INTO public.tbl_lehrverband (studiengang_kz, semester, verband, gruppe, aktiv, bezeichnung, ext_id, orgform_kurzbz) VALUES + (4, 1, '','', true, null, null, null), + (4, 1, 'A','', true, null, null, null), + (4, 1, 'B','', true, null, null, null), + (4, 2, '','', true, null, null, null), + (4, 2, 'A','', true, null, null, null), + (4, 2, 'B','', true, null, null, null), + (4, 3, '','', true, null, null, null), + (4, 3, 'A','', true, null, null, null), + (4, 3, 'B','', true, null, null, null), + (4, 4, '','', true, null, null, null), + (4, 4, 'A','', true, null, null, null), + (4, 4, 'B','', true, null, null, null), + (4, 5, '','', true, null, null, null), + (4, 5, 'A','', true, null, null, null), + (4, 5, 'B','', true, null, null, null), + (4, 6, '','', true, null, null, null), + (4, 6, 'A','', true, null, null, null), + (4, 6, 'B','', true, null, null, null) +; + +INSERT INTO lehre.tbl_studienordnung (studienordnung_id, studiengang_kz,"version",gueltigvon,gueltigbis,bezeichnung,ects,studiengangbezeichnung,studiengangbezeichnung_englisch,studiengangkurzbzlang,akadgrad_id,insertamum,insertvon,updateamum,updatevon,ext_id,status_kurzbz,standort_id) VALUES + (401, 4,'1',NearestWintersemester(-4),NULL,'BS4'||NearestWintersemester(-4),180.00,'Tempus Beispiele','Tempus Examples','STG4',NULL,now(),'anondata',NULL,NULL,NULL,'development',NULL); + +INSERT INTO lehre.tbl_studienplan (studienplan_id, studienordnung_id,orgform_kurzbz,"version",bezeichnung,regelstudiendauer,sprache,aktiv,semesterwochen,testtool_sprachwahl,insertamum,insertvon,updateamum,updatevon,ext_id,ects_stpl,pflicht_sws,pflicht_lvs,onlinebewerbung_studienplan) VALUES + (4011, 401,'VZ','1','BS4-'||NearestWintersemester(-4)||'-VZ',6,NULL,true,15,true,now(),'anondata',NULL,NULL,NULL,180.00,NULL,NULL,true); + +INSERT INTO lehre.tbl_lehrveranstaltung (lehrveranstaltung_id, kurzbz,bezeichnung,studiengang_kz,semester,sprache,ects,semesterstunden,anmerkung,lehre,lehreverzeichnis,aktiv,planfaktor,planlektoren,planpersonalkosten,plankostenprolektor,updateamum,updatevon,insertamum,insertvon,ext_id,sort,zeugnis,koordinator,projektarbeit,lehrform_kurzbz,bezeichnung_english,orgform_kurzbz,incoming,lehrmodus_kurzbz,lehrtyp_kurzbz,oe_kurzbz,raumtyp_kurzbz,anzahlsemester,semesterwochen,lvnr,farbe,old_lehrfach_id,semester_alternativ,sws,lvs,alvs,lvps,las,benotung,lvinfo,lehrauftrag,lehrveranstaltung_template_id) VALUES + (411, 'MOD1.1','Standard',4,1,'German',8.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'MOD','Standard','VZ',5,'regulaer','modul','stg4',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (412, 'MOD1.2','Advanced',4,1,'German',8.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'MOD','Advanced','VZ',5,'regulaer','modul','stg4',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (413, 'MOD1.3','Wahlmodul',4,1,'German',8.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'MOD','Wahlmodul','VZ',5,'regulaer','modul','stg4',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (414, 'MOD1.4','Sonstiges',4,1,'English',6.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'MOD','Sonstiges','VZ',5,'regulaer','modul','stg4',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + + (461, 'MOD6.1','Bachelorarbeit',4,6,'German',8.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'MOD','Bachelorarbeit','VZ',5,'regulaer','modul','stg4',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (462, 'MOD6.2','International Skills',4,6,'English',5.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'MOD','International Skills','VZ',5,'regulaer','modul','stg4',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (463, 'MOD6.3','Berufspraktikum',4,6,'German',17.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'MOD','Berufspraktikum','VZ',5,'regulaer','modul','stg4',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL) + ; + + INSERT INTO lehre.tbl_lehrveranstaltung (lehrveranstaltung_id, kurzbz,bezeichnung,studiengang_kz,semester,sprache,ects,semesterstunden,anmerkung,lehre,lehreverzeichnis,aktiv,planfaktor,planlektoren,planpersonalkosten,plankostenprolektor,updateamum,updatevon,insertamum,insertvon,ext_id,sort,zeugnis,koordinator,projektarbeit,lehrform_kurzbz,bezeichnung_english,orgform_kurzbz,incoming,lehrmodus_kurzbz,lehrtyp_kurzbz,oe_kurzbz,raumtyp_kurzbz,anzahlsemester,semesterwochen,lvnr,farbe,old_lehrfach_id,semester_alternativ,sws,lvs,alvs,lvps,las,benotung,lvinfo,lehrauftrag,lehrveranstaltung_template_id) VALUES + (4111, 'S1','Standardvorlesung',4,1,'German',8.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'VO','Standardvorlesung','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (4121, 'S2','Vorlesung und Uebung',4,1,'German',8.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'ILV','VorlesungUebung','VZ',5,'regulaer','lv','kfMath',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (4131, 'S3','Getrennte Gruppen',4,1,'German',8.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'ILV','Splittet Groups','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (4141, 'A1','Mehrere Lektoren',4,1,'English',6.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'VO','Multilektor','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + + (4611, 'BA','Bachelorarbeit',4,6,'German',8.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'EL','Bachelorarbeit','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (4621, 'INT','International Skills',4,6,'English',5.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'PRJ','International Skills','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (4631, 'BPRAK','Berufspraktikum',4,6,'German',17.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,now(),'anondata',now(),'anondata',NULL,NULL,true,NULL,false,'SO','Berufspraktikum','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL) + ; + + INSERT INTO lehre.tbl_studienplan_lehrveranstaltung (studienplan_lehrveranstaltung_id, studienplan_id,lehrveranstaltung_id,semester,studienplan_lehrveranstaltung_id_parent,pflicht,koordinator,insertamum,insertvon,updateamum,updatevon,sort,ext_id,curriculum,export,genehmigung) VALUES + -- MODULE im Studienplan + (401, 4011,411,1,NULL,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + (402, 4011,412,1,NULL,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + (403, 4011,413,1,NULL,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + (404, 4011,414,1,NULL,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + + (461, 4011,461,6,NULL,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + (462, 4011,462,6,NULL,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + (463, 4011,463,6,NULL,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + + -- LVS zu Modulen + (415, 4011,4111,1,401,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + (416, 4011,4121,1,401,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + (417, 4011,4131,1,401,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + (418, 4011,4141,1,402,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + + (465, 4011,4611,6,461,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + (466, 4011,4621,6,462,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true), + (467, 4011,4631,6,463,true,NULL,now(),'anondata',NULL,NULL,NULL,NULL,true,true,true) +; + +INSERT INTO lehre.tbl_studienplan_semester (studienplan_id, studiensemester_kurzbz, semester) VALUES +(4011, NearestWintersemester(-4), 1), +(4011, NearestWintersemester(-3), 2), +(4011, NearestWintersemester(-2), 1), +(4011, NearestWintersemester(-2), 3), +(4011, NearestWintersemester(-1), 2), +(4011, NearestWintersemester(-1), 4), +(4011, NearestWintersemester(0), 1), +(4011, NearestWintersemester(0), 3), +(4011, NearestWintersemester(0), 5), +(4011, NearestWintersemester(+1), 2), +(4011, NearestWintersemester(+1), 4), +(4011, NearestWintersemester(+1), 6); + +-- ===== Standardvorlesung +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(41001, 4111, NearestWintersemester(0), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4111, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4111, 1, 'Standard ein LV-Teil, ein Lektor, eine Gruppe'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(41001, 4, '1', '', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(41001, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 0.00, NULL, NULL); + +-- ===== VORLESUNG UND UEBUNG +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(41002, 4121, NearestWintersemester(0), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4121, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4121, 1, 'Vorlesung'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(41002, 4, '1', '', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(41002, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 0.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(41003, 4121, NearestWintersemester(0), NULL, 'UE', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4121, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4121, 1, 'Uebung'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(41003, 4, '1', '', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(41003, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 0.00, NULL, NULL); + + +-- ======= GETRENNTE GRUPPEN +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(41004, 4131, NearestWintersemester(0), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4131, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4131, 1, 'Vorlesung 1A'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(41004, 4, '1', 'A', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(41004, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 0.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(41005, 4131, NearestWintersemester(0), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4131, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4131, 1, 'Vorlesung 1B'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(41005, 4, '1', 'B', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(41005, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 0.00, NULL, NULL); + +-- ======= Mehrere Lektoren +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(41006, 4141, NearestWintersemester(0), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4141, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4141, 1, 'Vorlesung 1A und Lektor 2 und 3'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(41006, 4, '1', 'A', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(41006, 'demolektor2', 'Lektor', 8, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 8.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(41006, 'demolektor3', 'Lektor', 8, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 8.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(41007, 4141, NearestWintersemester(0), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4141, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4141, 1, 'Vorlesung 1B und Lektor 4 und 5'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(41007, 4, '1', 'B', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(41007, 'demolektor4', 'Lektor', 8, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 8.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(41007, 'demolektor5', 'Lektor', 8, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 8.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(41008, 4141, NearestWintersemester(0), NULL, 'BE', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4141, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4141, 1, 'LV Leitung die nicht verplant wird'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(41008, 4, '1', 'B', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(41008, 4, '1', 'A', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(41008, 'demolektor4', 'LV-Leitung', 0, NULL, 1.00, NULL, false, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 5.00, NULL, NULL); + + +-- ===== 6. Semester + +-- ===== Bachelorarbeit +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(46111, 4611, NearestWintersemester(1), NULL, 'EL', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4111, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4611, 1, 'keine Verplanung'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(46111, 4, '6', '', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(46111, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 0.00, NULL, NULL); + +-- ===== Internation Skills +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(46211, 4621, NearestWintersemester(1), NULL, 'EL', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4111, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4621, 1, 'nur für Zeugnis'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(46211, 4, '6', '', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(46211, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 0.00, NULL, NULL); + +-- ===== BPRAK +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht, anmerkung) +VALUES(46311, 4631, NearestWintersemester(1), NULL, 'EL', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, 4111, NULL, '2023-08-24 14:54:43.000', 'anondata', '2023-08-24 14:54:43.000', 'anondata', NULL, 4631, 1, 'keine Verplanung'); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(46311, 4, '6', '', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'anondata', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(46311, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'anondata', NULL, 0.00, NULL, NULL); + diff --git a/system/seeders/009_studenten_stg_4.sql b/system/seeders/009_studenten_stg_4.sql new file mode 100644 index 000000000..4230e4d9e --- /dev/null +++ b/system/seeders/009_studenten_stg_4.sql @@ -0,0 +1,266 @@ +-- Demodaten fuer Studierende im Studiengang 4 +-- Personen +INSERT INTO public.tbl_person (person_id, staatsbuergerschaft,geburtsnation,sprache,anrede,titelpost,titelpre,vorname,nachname,vornamen,gebdatum,gebort,gebzeit,foto,anmerkung,homepage,svnr,ersatzkennzeichen,familienstand,geschlecht,anzahlkinder,aktiv,insertamum,insertvon,updateamum,updatevon,ext_id,bundesland_code,kompetenzen,kurzbeschreibung,zugangscode,foto_sperre,udf_values,bpk,matr_aktiv,matr_nr,zugangscode_timestamp,wahlname,unruly) VALUES + +(401,'A','A',NULL,'Herr',NULL,NULL,'Mario','Super',NULL,'1974-11-11',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c401',false,NULL,NULL,false,NULL,NULL,NULL,false), +(402,'A','A',NULL,'Herr',NULL,NULL,'Luigi','Green',NULL,'1965-04-04',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c402',false,NULL,NULL,false,NULL,NULL,NULL,false), +(403,'A','A',NULL,'Frau',NULL,NULL,'Peach','Princess',NULL,'1954-12-28',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c403',false,NULL,NULL,false,NULL,NULL,NULL,false), +(404,'A','A',NULL,'Herr',NULL,NULL,'Toad','Mushroom',NULL,'1940-04-25',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c404',false,NULL,NULL,false,NULL,NULL,NULL,false), +(405,'A','A',NULL,'Herr',NULL,NULL,'Bowser','Koopa',NULL,'1937-06-01',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c405',false,NULL,NULL,false,NULL,NULL,NULL,false), +(406,'A','A',NULL,'Frau',NULL,NULL,'Link','Hyrule',NULL,'1957-04-29',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c406',false,NULL,NULL,false,NULL,NULL,NULL,false), +(407,'A','A',NULL,'Frau',NULL,NULL,'Zelda','Hyrule',NULL,'1924-04-03',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c407',false,NULL,NULL,false,NULL,NULL,NULL,false), +(408,'A','A',NULL,'Herr',NULL,NULL,'Donkey','Kong',NULL,'1956-07-09',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c408',false,NULL,NULL,false,NULL,NULL,NULL,false), +(409,'A','A',NULL,'Herr',NULL,NULL,'Yoshi','Dinosaur',NULL,'1963-06-09',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c409',false,NULL,NULL,false,NULL,NULL,NULL,false), +(410,'A','A',NULL,'Frau',NULL,NULL,'Rosalina','Lumaire',NULL,'1963-12-18',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c8410',false,NULL,NULL,false,NULL,NULL,NULL,false), +(411,'A','A',NULL,'Herr',NULL,NULL,'Wario','Warioh',NULL,'1968-09-25',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'m',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c411',false,NULL,NULL,false,NULL,NULL,NULL,false), +(412,'A','A',NULL,'Frau',NULL,NULL,'Waluigi','Big',NULL,'1964-09-02',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c412',false,NULL,NULL,false,NULL,NULL,NULL,false), + +(413,'A','A',NULL,'Frau',NULL,NULL,'Melanie','Muster',NULL,'2010-04-02',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c413',false,NULL,NULL,false,NULL,NULL,NULL,false), +(414,'A','A',NULL,'Frau',NULL,NULL,'Tanja','Test',NULL,'2009-03-02',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c414',false,NULL,NULL,false,NULL,NULL,NULL,false), +(415,'A','A',NULL,'Frau',NULL,NULL,'Franziska','Fake',NULL,'2003-08-05',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c415',false,NULL,NULL,false,NULL,NULL,NULL,false), +(416,'A','A',NULL,'Herr',NULL,NULL,'Simon','Simulator',NULL,'2007-07-07',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'w',NULL,true,'2023-08-24 14:56:04.509813','auto','2025-05-13 10:57:19.826386',NULL,NULL,NULL,NULL,NULL,'64e753647c416',false,NULL,NULL,false,NULL,NULL,NULL,false) +; +SELECT setval('public.tbl_person_person_id_seq', (SELECT max(person_id) FROM public.tbl_person),true); + +-- Prestudenten +INSERT INTO public.tbl_prestudent (prestudent_id, person_id,studiengang_kz,aufmerksamdurch_kurzbz,berufstaetigkeit_code,ausbildungcode,zgv_code,zgvort,zgvdatum,zgvmas_code,zgvmaort,zgvmadatum,aufnahmeschluessel,facheinschlberuf,reihungstest_id,anmeldungreihungstest,reihungstestangetreten,rt_gesamtpunkte,bismelden,insertamum,insertvon,updateamum,updatevon,ext_id,anmerkung,dual,rt_punkte1,rt_punkte2,ausstellungsstaat,rt_punkte3,udf_values,priorisierung,zgvdoktor_code,zgvdoktorort,zgvdoktordatum,mentor,zgvnation,zgvmanation,zgvdoktornation,gsstudientyp_kurzbz,aufnahmegruppe_kurzbz,foerderrelevant,standort_code,zgv_erfuellt,zgvmas_erfuellt,zgvdoktor_erfuellt) VALUES + (4401,401,4,'k.A.',NULL,NULL,9,NULL,'2020-06-01',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4402,402,4,'k.A.',NULL,NULL,9,NULL,'2020-06-02',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4403,403,4,'k.A.',NULL,NULL,9,NULL,'2020-06-03',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4404,404,4,'k.A.',NULL,NULL,9,NULL,'2020-06-04',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4405,405,4,'k.A.',NULL,NULL,9,NULL,'2020-06-05',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4406,406,4,'k.A.',NULL,NULL,9,NULL,'2020-06-06',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4407,407,4,'k.A.',NULL,NULL,9,NULL,'2020-06-07',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4408,408,4,'k.A.',NULL,NULL,9,NULL,'2020-06-08',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4409,409,4,'k.A.',NULL,NULL,9,NULL,'2020-06-09',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4410,410,4,'k.A.',NULL,NULL,9,NULL,'2020-06-10',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4411,411,4,'k.A.',NULL,NULL,9,NULL,'2020-06-01',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4412,412,4,'k.A.',NULL,NULL,9,NULL,'2020-06-02',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + + (4413,413,4,'k.A.',NULL,NULL,9,NULL,'2020-06-02',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4414,414,4,'k.A.',NULL,NULL,9,NULL,'2020-06-02',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4415,415,4,'k.A.',NULL,NULL,9,NULL,'2020-06-02',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false), + (4416,416,4,'k.A.',NULL,NULL,9,NULL,'2020-06-02',NULL,NULL,NULL,NULL,false,NULL,NULL,true,NULL,true,NULL,NULL,'2023-08-24 14:56:04.509813',NULL,NULL,NULL,false,NULL,NULL,'A',0.0000,NULL,NULL,NULL,NULL,NULL,NULL,'A',NULL,NULL,'Intern',NULL,NULL,NULL,false,false,false) +; +SELECT setval('tbl_prestudent_prestudent_id_seq', (SELECT max(prestudent_id) FROM public.tbl_prestudent),true); + +-- Prestudent Status Drittsemestrige + +WITH status AS ( + SELECT * FROM (VALUES + ('Interessent',NearestWintersemester(-2),1,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-2)),'2023-08-24 14:56:04','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Bewerber',NearestWintersemester(-2),1,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-2)),'2023-08-24 14:58:11','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Aufgenommener',NearestWintersemester(-2),1,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-2)),'2023-08-24 14:58:14','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student',NearestWintersemester(-2),1,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-2)),'2023-08-24 14:58:16','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student',NearestWintersemester(-1),2,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-1)),'2023-08-24 14:58:16','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student',NearestWintersemester(0),3,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(0)),'2023-08-24 14:58:16','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL) + ) AS t(status_kurzbz,studiensemester_kurzbz,ausbildungssemester,datum,insertamum,insertvon,updateamum,updatevon,orgform_kurzbz,studienplan_id,bestaetigtam,bestaetigtvon,fgm,faktiv,anmerkung,bewerbung_abgeschicktamum,rt_stufe,statusgrund_id) +), +inserts AS ( + SELECT generate_series(4401, 4412) AS prestudent_id,* + FROM status +) +INSERT INTO public.tbl_prestudentstatus (prestudent_id, status_kurzbz,studiensemester_kurzbz,ausbildungssemester,datum,insertamum,insertvon,updateamum,updatevon,orgform_kurzbz,studienplan_id,bestaetigtam,bestaetigtvon,fgm,faktiv,anmerkung,bewerbung_abgeschicktamum,rt_stufe,statusgrund_id) +SELECT prestudent_id, status_kurzbz, studiensemester_kurzbz, ausbildungssemester, datum::date, insertamum::timestamp, insertvon, updateamum::timestamp, updatevon,orgform_kurzbz,studienplan_id,bestaetigtam::date,bestaetigtvon,fgm::smallint,faktiv,anmerkung,bewerbung_abgeschicktamum::timestamp,rt_stufe::smallint,statusgrund_id::int +FROM inserts; + +-- tbl_benutzer +WITH persons AS ( + SELECT generate_series(401, 412) AS person_id +) +INSERT INTO public.tbl_benutzer (person_id, uid, aktiv, alias, insertamum, insertvon, updateamum, updatevon, updateaktivam, updateaktivvon, aktivierungscode) +SELECT person_id, 's425b' || person_id, true, null, now(), 'auto', null, null, null, false, null +FROM persons; + +-- tbl_student +-- A Verband +WITH persons AS ( + SELECT generate_series(401, 405) AS person_id +) +INSERT INTO public.tbl_student (student_uid, matrikelnr, prestudent_id, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT 's425b' || person_id, '2510004' || person_id, ('4' || person_id)::integer, '4', '3', 'A','', null, null, now(), 'auto' +FROM persons; + +-- B Verband +WITH persons AS ( + SELECT generate_series(406, 412) AS person_id +) +INSERT INTO public.tbl_student (student_uid, matrikelnr, prestudent_id, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT 's425b' || person_id, '2510004' || person_id, ('4' || person_id)::integer, '4', '3', 'B','', null, null, now(), 'auto' +FROM persons; + +-- tbl_studentlehrverband +-- A Verband +WITH persons AS ( + SELECT generate_series(401, 405) AS person_id +), +students as ( + SELECT ('s425b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(-2), '4','1','A','', null, null, now(), 'auto' +FROM students; +WITH persons AS ( + SELECT generate_series(401, 405) AS person_id +), +students as ( + SELECT ('s425b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(-1), '4','2','A','', null, null, now(), 'auto' +FROM students; + +WITH persons AS ( + SELECT generate_series(401, 405) AS person_id +), +students as ( + SELECT ('s425b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(0), '4','3','A','', null, null, now(), 'auto' +FROM students; + +-- B Verband +WITH persons AS ( + SELECT generate_series(406, 412) AS person_id +), +students as ( + SELECT ('s425b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(-2), '4','1','B','', null, null, now(), 'auto' +FROM students; + +WITH persons AS ( + SELECT generate_series(406, 412) AS person_id +), +students as ( + SELECT ('s425b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(-1), '4','2','B','', null, null, now(), 'auto' +FROM students; + +WITH persons AS ( + SELECT generate_series(406, 412) AS person_id +), +students as ( + SELECT ('s425b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(0), '4','3','B','', null, null, now(), 'auto' +FROM students; + + +---- 6 Semestrige + +WITH status AS ( + SELECT * FROM (VALUES + ('Interessent',NearestWintersemester(-4),1,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-4)),'2023-08-24 14:56:04','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Bewerber',NearestWintersemester(-4),1,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-4)),'2023-08-24 14:58:11','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Aufgenommener',NearestWintersemester(-4),1,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-4)),'2023-08-24 14:58:14','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student',NearestWintersemester(-4),1,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-4)),'2023-08-24 14:58:16','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student',NearestWintersemester(-3),2,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-3)),'2023-08-24 14:58:16','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student',NearestWintersemester(-2),3,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-2)),'2023-08-24 14:58:16','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student',NearestWintersemester(-1),4,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(-1)),'2023-08-24 14:58:16','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student',NearestWintersemester(0),5,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(0)),'2023-08-24 14:58:16','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL), + ('Student',NearestWintersemester(+1),6,(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=NearestWintersemester(+1)),'2023-08-24 14:58:16','auto',NULL,NULL,NULL,3011,'2023-08-24','demoadmin',NULL,false,NULL,NULL,NULL,NULL) + ) AS t(status_kurzbz,studiensemester_kurzbz,ausbildungssemester,datum,insertamum,insertvon,updateamum,updatevon,orgform_kurzbz,studienplan_id,bestaetigtam,bestaetigtvon,fgm,faktiv,anmerkung,bewerbung_abgeschicktamum,rt_stufe,statusgrund_id) +), +inserts AS ( + SELECT generate_series(4413, 4416) AS prestudent_id,* + FROM status +) +INSERT INTO public.tbl_prestudentstatus (prestudent_id, status_kurzbz,studiensemester_kurzbz,ausbildungssemester,datum,insertamum,insertvon,updateamum,updatevon,orgform_kurzbz,studienplan_id,bestaetigtam,bestaetigtvon,fgm,faktiv,anmerkung,bewerbung_abgeschicktamum,rt_stufe,statusgrund_id) +SELECT prestudent_id, status_kurzbz, studiensemester_kurzbz, ausbildungssemester, datum::date, insertamum::timestamp, insertvon, updateamum::timestamp, updatevon,orgform_kurzbz,studienplan_id,bestaetigtam::date,bestaetigtvon,fgm::smallint,faktiv,anmerkung,bewerbung_abgeschicktamum::timestamp,rt_stufe::smallint,statusgrund_id::int +FROM inserts; + +-- tbl_benutzer +WITH persons AS ( + SELECT generate_series(413, 416) AS person_id +) +INSERT INTO public.tbl_benutzer (person_id, uid, aktiv, alias, insertamum, insertvon, updateamum, updatevon, updateaktivam, updateaktivvon, aktivierungscode) +SELECT person_id, 's46b' || person_id, true, null, now(), 'auto', null, null, null, false, null +FROM persons; + +-- tbl_student +-- A Verband +WITH persons AS ( + SELECT generate_series(413, 416) AS person_id +) +INSERT INTO public.tbl_student (student_uid, matrikelnr, prestudent_id, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT 's46b' || person_id, '2510004' || person_id, ('4' || person_id)::integer, '4', '6', 'A','', null, null, now(), 'auto' +FROM persons; + +-- tbl_studentlehrverband +-- A Verband +WITH persons AS ( + SELECT generate_series(413, 416) AS person_id +), +students as ( + SELECT ('s46b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(-4), '4','1','A','', null, null, now(), 'auto' +FROM students; +WITH persons AS ( + SELECT generate_series(413, 416) AS person_id +), +students as ( + SELECT ('s46b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(-3), '4','2','A','', null, null, now(), 'auto' +FROM students; + +WITH persons AS ( + SELECT generate_series(413, 416) AS person_id +), +students as ( + SELECT ('s46b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(-2), '4','3','A','', null, null, now(), 'auto' +FROM students; + +WITH persons AS ( + SELECT generate_series(413, 416) AS person_id +), +students as ( + SELECT ('s46b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(-1), '4','4','A','', null, null, now(), 'auto' +FROM students; + +WITH persons AS ( + SELECT generate_series(413, 416) AS person_id +), +students as ( + SELECT ('s46b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(0), '4','5','A','', null, null, now(), 'auto' +FROM students; + +WITH persons AS ( + SELECT generate_series(413, 416) AS person_id +), +students as ( + SELECT ('s46b' || person_id) as student_uid, person_id + FROM persons +) +INSERT INTO public.tbl_studentlehrverband (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon) +SELECT student_uid, NearestWintersemester(+1), '4','6','A','', null, null, now(), 'auto' +FROM students; diff --git a/system/seeders/010_projektarbeiten_studiengang_4.sql b/system/seeders/010_projektarbeiten_studiengang_4.sql new file mode 100644 index 000000000..214705bc4 --- /dev/null +++ b/system/seeders/010_projektarbeiten_studiengang_4.sql @@ -0,0 +1,16 @@ +INSERT INTO lehre.tbl_projektarbeit +(projektarbeit_id, projekttyp_kurzbz, titel, lehreinheit_id, student_uid, firma_id, note, punkte, beginn, ende, faktor, freigegeben, gesperrtbis, stundensatz, themenbereich, anmerkung, updateamum, updatevon, insertamum, insertvon, ext_id, gesamtstunden, titel_english, sprache, seitenanzahl, abgabedatum, kontrollschlagwoerter, schlagwoerter, schlagwoerter_en, abstract, abstract_en, "final") VALUES +(1, 'Bachelor', 'Auswirkung der Sonneneinstrahlung auf die Gesundheit', 46111, 's46b413', NULL, NULL, NULL, NULL, NULL, NULL, true, NULL, NULL, NULL, NULL, NULL, NULL, '2025-12-12 14:06:10.000', 'anondata', NULL, 3.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, true), +(2, 'Bachelor', 'Neue Unterrichtsmethoden mit KI', 46111, 's46b414', NULL, NULL, NULL, NULL, NULL, NULL, true, NULL, NULL, NULL, NULL, NULL, NULL, '2025-12-12 14:06:10.000', 'anondata', NULL, 3.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, true), +(3, 'Bachelor', 'Betrugsbekämpfung bei Anwesenheitskontrolle', 46111, 's46b415', NULL, NULL, NULL, NULL, NULL, NULL, true, NULL, NULL, NULL, NULL, NULL, NULL, '2025-12-12 14:06:10.000', 'anondata', NULL, 3.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, true), +(4, 'Bachelor', 'Neuronale Netze', 46111, 's46b416', NULL, NULL, NULL, NULL, NULL, NULL, true, NULL, NULL, NULL, NULL, NULL, NULL, '2025-12-12 14:06:10.000', 'anondata', NULL, 3.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, true) +; + +INSERT INTO lehre.tbl_projektbetreuer +(person_id, projektarbeit_id, note, faktor, "name", punkte, stundensatz, updateamum, updatevon, insertamum, insertvon, ext_id, betreuerart_kurzbz, stunden, zugangstoken, zugangstoken_gueltigbis, vertrag_id) VALUES +(21, 1, NULL, NULL, NULL, NULL, 80.00, NULL, NULL, '2025-12-12 14:07:10.000', 'anondata', NULL, 'Begutachter', 4.0000, NULL, NULL, NULL), +(21, 2, NULL, NULL, NULL, NULL, 80.00, NULL, NULL, '2025-12-12 14:07:10.000', 'anondata', NULL, 'Begutachter', 4.0000, NULL, NULL, NULL), +(21, 3, NULL, NULL, NULL, NULL, 80.00, NULL, NULL, '2025-12-12 14:07:10.000', 'anondata', NULL, 'Begutachter', 4.0000, NULL, NULL, NULL), +(21, 4, NULL, NULL, NULL, NULL, 80.00, NULL, NULL, '2025-12-12 14:07:10.000', 'anondata', NULL, 'Begutachter', 4.0000, NULL, NULL, NULL) +; + diff --git a/system/seeders/011_zeitwuensche.sql b/system/seeders/011_zeitwuensche.sql new file mode 100644 index 000000000..8895bf5fe --- /dev/null +++ b/system/seeders/011_zeitwuensche.sql @@ -0,0 +1,107 @@ +INSERT INTO campus.tbl_zeitwunsch_gueltigkeit +(zeitwunsch_gueltigkeit_id, mitarbeiter_uid, von, bis, insertamum, insertvon, updateamum, updatevon) +VALUES(1, 'demolektor1', '2020-09-01', NULL, NOW(), 'demolektor1', NULL, NULL); + +INSERT INTO campus.tbl_zeitwunsch +(stunde, mitarbeiter_uid, tag, gewicht, updateamum, updatevon, insertamum, insertvon, zeitwunsch_id, zeitwunsch_gueltigkeit_id) VALUES +(1, 'demolektor1', 1, -2, now(), 'demolektor1', now(), 'demolektor1', 1, 1), +(2, 'demolektor1', 1, -2, now(), 'demolektor1', now(), 'demolektor1', 2, 1), +(3, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 3, 1), +(4, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 4, 1), +(5, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 5, 1), +(6, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 6, 1), +(7, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 7, 1), +(8, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 8, 1), +(9, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 9, 1), +(10, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 10, 1), +(11, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 11, 1), +(12, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 12, 1), +(13, 'demolektor1', 1, 2, now(), 'demolektor1', now(), 'demolektor1', 13, 1), +(14, 'demolektor1', 1, -1, now(), 'demolektor1', now(), 'demolektor1', 14, 1), +(15, 'demolektor1', 1, -1, now(), 'demolektor1', now(), 'demolektor1', 15, 1), +(16, 'demolektor1', 1, -1, now(), 'demolektor1', now(), 'demolektor1', 16, 1), +(1, 'demolektor1', 2, -2, now(), 'demolektor1', now(), 'demolektor1', 17, 1), +(2, 'demolektor1', 2, -1, now(), 'demolektor1', now(), 'demolektor1', 18, 1), +(3, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 19, 1), +(4, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 20, 1), +(5, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 21, 1), +(6, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 22, 1), +(7, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 23, 1), +(8, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 24, 1), +(9, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 25, 1), +(10, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 26, 1), +(11, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 27, 1), +(12, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 28, 1), +(13, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 29, 1), +(14, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 30, 1), +(15, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 31, 1), +(16, 'demolektor1', 2, 1, now(), 'demolektor1', now(), 'demolektor1', 32, 1), +(1, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 33, 1), +(2, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 34, 1), +(3, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 35, 1), +(4, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 36, 1), +(5, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 37, 1), +(6, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 38, 1), +(7, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 39, 1), +(8, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 40, 1), +(9, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 41, 1), +(10, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 42, 1), +(11, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 43, 1), +(12, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 44, 1), +(13, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 45, 1), +(14, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 46, 1), +(15, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 47, 1), +(16, 'demolektor1', 3, 1, now(), 'demolektor1', now(), 'demolektor1', 48, 1), +(1, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 49, 1), +(2, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 50, 1), +(3, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 51, 1), +(4, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 52, 1), +(5, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 53, 1), +(6, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 54, 1), +(7, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 55, 1), +(8, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 56, 1), +(9, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 57, 1), +(10, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 58, 1), +(11, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 59, 1), +(12, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 60, 1), +(13, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 61, 1), +(14, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 62, 1), +(15, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 63, 1), +(16, 'demolektor1', 4, 1, now(), 'demolektor1', now(), 'demolektor1', 64, 1), +(1, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 65, 1), +(2, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 66, 1), +(3, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 67, 1), +(4, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 68, 1), +(5, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 69, 1), +(6, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 70, 1), +(7, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 71, 1), +(8, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 72, 1), +(9, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 73, 1), +(10, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 74, 1), +(11, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 75, 1), +(12, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 76, 1), +(13, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 77, 1), +(14, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 78, 1), +(15, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 79, 1), +(16, 'demolektor1', 5, 1, now(), 'demolektor1', now(), 'demolektor1', 80, 1), +(1, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 81, 1), +(2, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 82, 1), +(3, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 83, 1), +(4, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 84, 1), +(5, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 85, 1), +(6, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 86, 1), +(7, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 87, 1), +(8, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 88, 1), +(9, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 89, 1), +(10, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 90, 1), +(11, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 91, 1), +(12, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 92, 1), +(13, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 93, 1), +(14, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 94, 1), +(15, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 95, 1), +(16, 'demolektor1', 6, 1, now(), 'demolektor1', now(), 'demolektor1', 96, 1) +; + + +SELECT setval('campus.seq_zeitwunsch_zeitwunsch_id', (select max(zeitwunsch_id) from campus.tbl_zeitwunsch), true); +SELECT setval('campus.seq_zeitwunsch_gueltigkeit_zeitwunsch_gueltigkeit_id', (select max(zeitwunsch_gueltigkeit_id) from campus.tbl_zeitwunsch_gueltigkeit),true); diff --git a/system/seeders/012_cms_content.sql b/system/seeders/012_cms_content.sql new file mode 100644 index 000000000..72c381b93 --- /dev/null +++ b/system/seeders/012_cms_content.sql @@ -0,0 +1,38 @@ +INSERT INTO campus.tbl_content +(content_id, template_kurzbz, oe_kurzbz, insertamum, insertvon, updateamum, updatevon, aktiv, menu_open, beschreibung) VALUES +(30, 'contentmittitel', 'etw', now(), 'demoadmin', now(), 'demoadmin', true, false, NULL), +(32, 'contentmittitel', 'etw', now(), 'demoadmin', now(), 'demoadmin', true, false, NULL), +(33, 'redirect', 'etw', now(), 'demoadmin', now(), 'demoadmin', true, false, NULL), +(34, 'redirect', 'etw', now(), 'demoadmin', now(), 'demoadmin', true, false, NULL), +(35, 'redirect', 'etw', now(), 'demoadmin', now(), 'demoadmin', true, false, NULL), +(36, 'redirect', 'etw', now(), 'demoadmin', now(), 'demoadmin', true, false, NULL), +(37, 'redirect', 'etw', now(), 'demoadmin', now(), 'demoadmin', true, false, NULL), +(38, 'redirect', 'etw', now(), 'demoadmin', now(), 'demoadmin', true, false, NULL) +; + +INSERT INTO campus.tbl_contentsprache (sprache, content_id, "version", sichtbar, "content", reviewvon, reviewamum, updateamum, updatevon, insertamum, insertvon, titel, gesperrt_uid) VALUES +('German', 30, 1, true, '', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'CIS4', NULL), +('German', 32, 1, true, '', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Studium', NULL), +('German', 33, 1, true, '', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'LV-Plan', NULL), +('German', 34, 1, true, '', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Dahsboard', NULL), +('German', 35, 1, true, '', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Meine LVs', NULL), +('German', 36, 1, true, '', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Studium', NULL), +('German', 37, 1, true, '', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Profil', NULL), +('German', 38, 1, true, '', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Dokumente', NULL) +; + +INSERT INTO campus.tbl_contentchild (content_id, child_content_id, insertamum, insertvon, updateamum, updatevon, sort) VALUES +(30, 37, now(), 'demoadmin', NULL, NULL, 5), +(30, 27, now(), 'demoadmin', NULL, NULL, 7), +(30, 32, now(), 'demoadmin', NULL, NULL, 2), +(30, 34, now(), 'demoadmin', NULL, NULL, 1), +(32, 33, now(), 'demoadmin', NULL, NULL, 1), +(32, 36, now(), 'demoadmin', NULL, NULL, 2), +(32, 38, now(), 'demoadmin', NULL, NULL, 3), +(32, 35, now(), 'demoadmin', NULL, NULL, 4) +; + +INSERT INTO campus.tbl_contentgruppe (content_id, gruppe_kurzbz, insertamum, insertvon) VALUES +(30, 'MA', now(), 'demoadmin'), +(30, 'STUD', now(), 'demoadmin') +; diff --git a/system/seeders/013_studiengang_5.sql b/system/seeders/013_studiengang_5.sql new file mode 100644 index 000000000..682ff9325 --- /dev/null +++ b/system/seeders/013_studiengang_5.sql @@ -0,0 +1,231 @@ +-- Demodaten fuer Studiengang 5: Organisationseinheit, Studienordnung, Studiengang, Lehrveranstaltungen + +INSERT INTO public.tbl_organisationseinheit (oe_kurzbz, oe_parent_kurzbz, bezeichnung, organisationseinheittyp_kurzbz, aktiv, mailverteiler, freigabegrenze, kurzzeichen, lehre, standort, warn_semesterstunden_frei, warn_semesterstunden_fix, standort_id) +VALUES('stg5', 'studiengaenge', 'Studiengang 5', 'Studiengang', true, false, NULL, NULL, true, NULL, NULL, NULL, NULL); + +INSERT INTO public.tbl_studiengang (studiengang_kz,kurzbz,kurzbzlang,typ,bezeichnung,english,farbe,email,telefon,max_semester,max_verband,max_gruppe,erhalter_kz,bescheid,bescheidbgbl1,bescheidbgbl2,bescheidgz,bescheidvom,titelbescheidvom,aktiv,ext_id,orgform_kurzbz,zusatzinfo_html,moodle,oe_kurzbz,sprache,testtool_sprachwahl,studienplaetze,lgartcode,mischform,projektarbeit_note_anzeige,melderelevant,foerderrelevant,standort_code,onlinebewerbung,melde_studiengang_kz) VALUES + (5,'S5','STG5','b','Studiengang 5','Studiengang 5',NULL,'invalid@example.com',NULL,6,'B','2',5,NULL,NULL,NULL,NULL,NULL,NULL,true,NULL,'VZ','',true,'stg5','German',true,NULL,NULL,false,true,true,true,NULL,false,'0002') +; + +INSERT INTO "system".tbl_benutzerrolle +(rolle_kurzbz, berechtigung_kurzbz, uid, funktion_kurzbz, oe_kurzbz, art, studiensemester_kurzbz, "start", ende, negativ, updateamum, updatevon, insertamum, insertvon, kostenstelle_id, anmerkung) +VALUES('assistenz', NULL, 'demoassistenz', NULL, 'stg5', 'suid', NULL, '2025-09-18', NULL, false, NULL, NULL, '2025-09-18 15:46:02.000', 'demoadmin', NULL, NULL); + +INSERT INTO public.tbl_lehrverband (studiengang_kz, semester, verband, gruppe, aktiv, bezeichnung, ext_id, orgform_kurzbz) VALUES + (5, 1, '','', true, null, null, null), + (5, 1, 'A','', true, null, null, null), + (5, 1, 'B','', true, null, null, null), + (5, 2, '','', true, null, null, null), + (5, 2, 'A','', true, null, null, null), + (5, 2, 'B','', true, null, null, null), + (5, 3, '','', true, null, null, null), + (5, 3, 'A','', true, null, null, null), + (5, 3, 'B','', true, null, null, null), + (5, 4, '','', true, null, null, null), + (5, 4, 'A','', true, null, null, null), + (5, 4, 'B','', true, null, null, null), + (5, 5, '','', true, null, null, null), + (5, 5, 'A','', true, null, null, null), + (5, 5, 'B','', true, null, null, null), + (5, 6, '','', true, null, null, null), + (5, 6, 'A','', true, null, null, null), + (5, 6, 'B','', true, null, null, null) +; + +INSERT INTO lehre.tbl_studienordnung (studienordnung_id, studiengang_kz,"version",gueltigvon,gueltigbis,bezeichnung,ects,studiengangbezeichnung,studiengangbezeichnung_englisch,studiengangkurzbzlang,akadgrad_id,insertamum,insertvon,updateamum,updatevon,ext_id,status_kurzbz,standort_id) VALUES + (501, 5,'1','WS2023',NULL,'BS5-WS2023',180.00,'Studiengang 5','Studiengang 5','STG5',NULL,'2023-08-24 14:25:41.728215','auto',NULL,NULL,NULL,'development',NULL); + +INSERT INTO lehre.tbl_studienplan (studienplan_id, studienordnung_id,orgform_kurzbz,"version",bezeichnung,regelstudiendauer,sprache,aktiv,semesterwochen,testtool_sprachwahl,insertamum,insertvon,updateamum,updatevon,ext_id,ects_stpl,pflicht_sws,pflicht_lvs,onlinebewerbung_studienplan) VALUES + (5011, 501,'VZ','1','BS5-WS2023-VZ',6,NULL,true,15,true,'2023-08-24 14:26:12.751416','auto',NULL,NULL,NULL,180.00,NULL,NULL,true); + +INSERT INTO lehre.tbl_lehrveranstaltung (lehrveranstaltung_id, kurzbz,bezeichnung,studiengang_kz,semester,sprache,ects,semesterstunden,anmerkung,lehre,lehreverzeichnis,aktiv,planfaktor,planlektoren,planpersonalkosten,plankostenprolektor,updateamum,updatevon,insertamum,insertvon,ext_id,sort,zeugnis,koordinator,projektarbeit,lehrform_kurzbz,bezeichnung_english,orgform_kurzbz,incoming,lehrmodus_kurzbz,lehrtyp_kurzbz,oe_kurzbz,raumtyp_kurzbz,anzahlsemester,semesterwochen,lvnr,farbe,old_lehrfach_id,semester_alternativ,sws,lvs,alvs,lvps,las,benotung,lvinfo,lehrauftrag,lehrveranstaltung_template_id) VALUES + (511, 'MOD1.1','Sport',5,1,'German',8.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Communication','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (512, 'MOD1.2','Mathematik',5,1,'German',8.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Mathematics','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (513, 'MOD1.3','Grundlagen',5,1,'German',8.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Basics','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (514, 'MOD1.4','Sprachen',5,1,'English',6.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Communication','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (521, 'MOD2.1','Labor',5,2,'German',10.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Laboratory','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (522, 'MOD2.2','Mathematik',5,2,'German',10.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Mathematics','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (523, 'MOD2.3','Sprachen',5,2,'English',10.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Communication','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (531, 'MOD3.1','Mathematik',5,3,'German',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Mathematics','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (532, 'MOD3.2','Elektrische Signale',5,3,'German',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Electric Signals','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (541, 'MOD4.1','Messtechnik',5,4,'German',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Measurement Technology','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (542, 'MOD4.2','Forschung',5,4,'German',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Research','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (551, 'MOD5.1','Communication',5,5,'English',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Communication','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (552, 'MOD5.2','Regelungstechnik',5,5,'German',15.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Control Engineering','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL), + (561, 'MOD6.1','Bachelorarbeit',5,6,'German',30.00,NULL,NULL,true,'mod',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'MOD','Bachelor Thesis','VZ',5,'regulaer','modul','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,true,true,NULL) + ; + + INSERT INTO lehre.tbl_lehrveranstaltung (lehrveranstaltung_id, kurzbz,bezeichnung,studiengang_kz,semester,sprache,ects,semesterstunden,anmerkung,lehre,lehreverzeichnis,aktiv,planfaktor,planlektoren,planpersonalkosten,plankostenprolektor,updateamum,updatevon,insertamum,insertvon,ext_id,sort,zeugnis,koordinator,projektarbeit,lehrform_kurzbz,bezeichnung_english,orgform_kurzbz,incoming,lehrmodus_kurzbz,lehrtyp_kurzbz,oe_kurzbz,raumtyp_kurzbz,anzahlsemester,semesterwochen,lvnr,farbe,old_lehrfach_id,semester_alternativ,sws,lvs,alvs,lvps,las,benotung,lvinfo,lehrauftrag,lehrveranstaltung_template_id) VALUES + (5111, 'LS','Leistungssport',5,1,'German',8.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'VO','Communication','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5121, 'MAT','Mathematik 1',5,1,'German',8.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Mathematics','VZ',5,'regulaer','lv','kfMath',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5131, 'GL','Grundlagen der Programmierung',5,1,'German',8.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Development Basics','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5141, 'ENG','Englisch 1',5,1,'English',6.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'VO','Communication','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5211, 'EL','Elektronik Labor',5,2,'German',10.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'LAB','Laboratory','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5221, 'MAT','Mathematik 2',5,2,'German',10.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Mathematics','VZ',5,'regulaer','lv','kfMath',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5231, 'ENG','Communcation Englisch',5,2,'English',10.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'VO','Communication','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5311, 'MAT','Mathematik 3',5,3,'German',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Mathematics','VZ',5,'regulaer','lv','kfMath',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5321, 'ES','Elektrische Signale',5,3,'German',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Electric Signals','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5411, 'MT','Messtechnik',5,4,'German',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Measurement Technology','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5421, 'BAN','Bewegungsanalyse',5,4,'German',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Movement Analytics','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5511, 'ENG','Communication',5,5,'English',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'VO','Communication','VZ',5,'regulaer','lv','kfSprachen',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5521, 'RT','Regelungstechnik',5,5,'German',15.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'ILV','Control Engineering','VZ',5,'regulaer','lv','kfTech',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL), + (5611, 'BA','Bachelorarbeit',5,6,'German',30.00,NULL,NULL,true,'lv',true,NULL,NULL,NULL,NULL,'2023-08-24 14:28:39','oesi','2023-08-24 14:28:39','oesi',NULL,NULL,true,NULL,false,'SE','Bachelor Thesis','VZ',5,'regulaer','lv','stg5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,true,true,true,NULL) + ; + + INSERT INTO lehre.tbl_studienplan_lehrveranstaltung (studienplan_lehrveranstaltung_id, studienplan_id,lehrveranstaltung_id,semester,studienplan_lehrveranstaltung_id_parent,pflicht,koordinator,insertamum,insertvon,updateamum,updatevon,sort,ext_id,curriculum,export,genehmigung) VALUES + -- MODULE im Studienplan + (501, 5011,511,1,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (502, 5011,512,1,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (503, 5011,513,1,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (504, 5011,514,1,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (505, 5011,521,2,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (506, 5011,522,2,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (507, 5011,523,2,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (508, 5011,531,3,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (509, 5011,532,3,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (510, 5011,541,4,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (511, 5011,542,4,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (512, 5011,551,5,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (513, 5011,552,5,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (514, 5011,561,6,NULL,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + + -- LVS zu Modulen + (515, 5011,5111,1,501,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (516, 5011,5121,1,502,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (517, 5011,5131,1,503,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (518, 5011,5141,1,504,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (519, 5011,5211,2,505,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (520, 5011,5221,2,519,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (521, 5011,5231,2,507,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (522, 5011,5311,3,508,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (523, 5011,5321,3,509,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (524, 5011,5411,4,510,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (525, 5011,5421,4,511,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (526, 5011,5511,5,512,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (527, 5011,5521,5,513,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true), + (528, 5011,5611,6,514,true,NULL,'2023-08-24 14:29:03.506117','oesi',NULL,NULL,NULL,NULL,true,true,true) +; + +INSERT INTO lehre.tbl_studienplan_semester (studienplan_id, studiensemester_kurzbz, semester) VALUES +(5011, 'WS2023', 1), +(5011, 'SS2024', 2), +(5011, 'WS2024', 1), +(5011, 'WS2024', 3), +(5011, 'SS2025', 2), +(5011, 'SS2025', 4), +(5011, 'WS2025', 1), +(5011, 'WS2025', 3), +(5011, 'WS2025', 5), +(5011, 'SS2026', 2), +(5011, 'SS2026', 4), +(5011, 'SS2026', 6); + + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, anmerkung, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht) +VALUES(51001, 5221, COALESCE(CurrentSemester(), LastActiveSemester()), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, NULL, 5221, NULL, '2023-08-24 14:54:43.000', 'oesi', '2023-08-24 14:54:43.000', 'oesi', NULL, 5221, 1); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(51001, 5, 1, 'A', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(51001, 5, 1, 'B', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(51001, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'oesi', NULL, 0.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, anmerkung, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht) +VALUES(51002, 5121, COALESCE(CurrentSemester(), LastActiveSemester()), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, NULL, 5121, NULL, '2023-08-24 14:54:43.000', 'oesi', '2023-08-24 14:54:43.000', 'oesi', NULL, 5121, 1); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(51002, 5, 1, 'B', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(51002, 'demolektor2', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'oesi', NULL, 0.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, anmerkung, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht) +VALUES(51003, 5141, COALESCE(CurrentSemester(), LastActiveSemester()), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, NULL, 5141, NULL, '2023-08-24 14:54:43.000', 'oesi', '2023-08-24 14:54:43.000', 'oesi', NULL, 5141, 1); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(51003, 5, 1, 'A', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(51003, 'demolektor3', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'oesi', NULL, 0.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, anmerkung, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht) +VALUES(51004, 5211, COALESCE(CurrentSemester(), LastActiveSemester()), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, NULL, 5211, NULL, '2023-08-24 14:54:43.000', 'oesi', '2023-08-24 14:54:43.000', 'oesi', NULL, 5211, 1); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(51004, 5, 1, 'A', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(51004, 'demolektor4', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'oesi', NULL, 0.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, anmerkung, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht) +VALUES(51005, 5311, COALESCE(CurrentSemester(), LastActiveSemester()), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, NULL, 5311, NULL, '2023-08-24 14:54:43.000', 'oesi', '2023-08-24 14:54:43.000', 'oesi', NULL, 5311, 1); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(51005, 5, 1, 'A', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(51005, 'demolektor4', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'oesi', NULL, 0.00, NULL, NULL); + +INSERT INTO lehre.tbl_lehreinheit (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrfach_id_old, lehrform_kurzbz, stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ, sprache, lehre, anmerkung, unr, lvnr, updateamum, updatevon, insertamum, insertvon, ext_id, lehrfach_id, gewicht) +VALUES(51006, 5321, COALESCE(CurrentSemester(), LastActiveSemester()), NULL, 'VO', 2, 1, NULL, 'Dummy', 'Dummy', 'German', true, NULL, 5321, NULL, '2023-08-24 14:54:43.000', 'oesi', '2023-08-24 14:54:43.000', 'oesi', NULL, 5321, 1); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(51006, 5, 1, 'C', NULL, NULL, NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter +(lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz, planstunden, stundensatz, faktor, anmerkung, bismelden, updateamum, updatevon, insertamum, insertvon, ext_id, semesterstunden, standort_id, vertrag_id) +VALUES(51006, 'demolektor1', 'Lektor', 0, NULL, 1.00, NULL, true, NULL, NULL, '2023-08-24 14:58:40.000', 'oesi', NULL, 0.00, NULL, NULL); + +-- Stundenplan Eintraege +INSERT INTO lehre.tbl_stundenplandev +(stundenplandev_id, lehreinheit_id, unr, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, mitarbeiter_uid, ort_kurzbz, datum, stunde, titel, anmerkung, fix, updateamum, updatevon, insertamum, insertvon) +VALUES(510011, 51001, 51001, 5, 1, 'A', '', NULL, 'demolektor1', 'EG04', '2025-07-31', 7, NULL, NULL, false, '2025-07-31 13:50:49.419', 'demoadmin', '2025-07-31 13:50:49.419', NULL); + +INSERT INTO lehre.tbl_stundenplan +(stundenplan_id, lehreinheit_id, unr, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, mitarbeiter_uid, ort_kurzbz, datum, stunde, titel, anmerkung, fix, updateamum, updatevon, insertamum, insertvon) +VALUES(510011, 51001, 51001, 5, 1, 'A', '', NULL, 'demolektor1', 'EG04', '2025-07-31', 7, NULL, NULL, false, '2025-07-31 13:50:49.419', 'demoadmin', '2025-07-31 13:50:49.419', NULL); + +INSERT INTO lehre.tbl_stundenplandev +(stundenplandev_id, lehreinheit_id, unr, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, mitarbeiter_uid, ort_kurzbz, datum, stunde, titel, anmerkung, fix, updateamum, updatevon, insertamum, insertvon) +VALUES(510021, 31002, 31002, 5, 1, 'B', '', NULL, 'demolektor1', 'EG04', '2025-08-01', 7, NULL, NULL, false, '2025-07-31 13:50:49.419', 'demoadmin', '2025-07-31 13:50:49.419', NULL); + +INSERT INTO lehre.tbl_stundenplan +(stundenplan_id, lehreinheit_id, unr, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, mitarbeiter_uid, ort_kurzbz, datum, stunde, titel, anmerkung, fix, updateamum, updatevon, insertamum, insertvon) +VALUES(510021, 31002, 31002, 5, 1, 'B', '', NULL, 'demolektor1', 'EG04', '2025-08-01', 7, NULL, NULL, false, '2025-07-31 13:50:49.419', 'demoadmin', '2025-07-31 13:50:49.419', NULL); + +INSERT INTO public.tbl_studentlehrverband +(student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES('s125b101', LastActiveSemester(), 5, 2, ' ', ' ', NULL, NULL, '2026-06-11 14:33:58.403', 'auto', NULL); + +INSERT INTO public.tbl_gruppe (gruppe_kurzbz, studiengang_kz, semester, bezeichnung, beschreibung, sichtbar, lehre, aktiv, sort, mailgrp, generiert, updateamum, updatevon, insertamum, insertvon, ext_id, orgform_kurzbz, content_visible, gesperrt, direktinskription, zutrittssystem, aufnahmegruppe) VALUES +('GRP_51003', 5, 2, 'BS5 2 ENG', null, false, false, true, NULL, false, false, now(), 'demoadmin', now(), 'demoadmin', NULL, NULL, true, false, true, false, false), +('GRP_51004', 5, 2, 'BS5 2 EL', null, false, false, true, NULL, false, false, now(), 'demoadmin', now(), 'demoadmin', NULL, NULL, true, false, true, false, false) +; + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(51003, 5, 2, NULL, NULL, 'GRP_51003', NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO lehre.tbl_lehreinheitgruppe +(lehreinheit_id, studiengang_kz, semester, verband, gruppe, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id) +VALUES(51004, 5, 2, NULL, NULL, 'GRP_51004', NULL, NULL, '2023-08-24 14:58:26.000', 'oesi', NULL); + +INSERT INTO public.tbl_benutzergruppe (uid, gruppe_kurzbz, updateamum, updatevon, insertamum, insertvon, ext_id, studiensemester_kurzbz) VALUES +('s125b101', 'GRP_51003', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL), +('s125b101', 'GRP_51004', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL) +; diff --git a/system/seeders/014_betriebsmittel.sql b/system/seeders/014_betriebsmittel.sql new file mode 100644 index 000000000..6239ccfa9 --- /dev/null +++ b/system/seeders/014_betriebsmittel.sql @@ -0,0 +1,11 @@ +INSERT INTO wawi.tbl_betriebsmitteltyp +(betriebsmitteltyp, beschreibung, anzahl, kaution, typ_code, mastershapename) +VALUES('Inventar', NULL, NULL, NULL, NULL, NULL); + +INSERT INTO wawi.tbl_betriebsmittel +(betriebsmittel_id, beschreibung, betriebsmitteltyp, nummer, reservieren, updateamum, updatevon, insertamum, insertvon, ext_id, inventarnummer, oe_kurzbz, ort_kurzbz, hersteller, seriennummer, bestellung_id, bestelldetail_id, afa, verwendung, anmerkung, leasing_bis, inventuramum, inventurvon, anschaffungswert, anschaffungsdatum, tiefe, hoehe, breite, nummer2, verplanen) +VALUES +(1, 'pc', 'Laptop', NULL, false, '2024-03-07 08:17:33.000', 'demoadmin', '2015-12-01 14:55:32.000', 'demoadmin', NULL, '00001', NULL, NULL, 'pc', NULL, NULL, NULL, 3, NULL, 'Angelegt von demoadmin für die test', NULL, '2015-12-01 14:55:32.000', 'demoadmin', NULL, NULL, NULL, NULL, NULL, NULL, true), +(2, 'projector', 'Inventar', NULL, false, '2024-03-07 08:17:33.000', 'demoadmin', '2015-12-01 14:55:32.000', 'demoadmin', NULL, '00002', NULL, NULL, 'projector', NULL, NULL, NULL, 3, NULL, 'Angelegt von demoadmin für die test', NULL, '2015-12-01 14:55:32.000', 'demoadmin', NULL, NULL, NULL, NULL, NULL, NULL, true), +(3, 'workstation', 'Inventar', NULL, false, '2024-03-07 08:17:33.000', 'demoadmin', '2015-12-01 14:55:32.000', 'demoadmin', NULL, '00003', NULL, NULL, 'workstation', NULL, NULL, NULL, 3, NULL, 'Angelegt von demoadmin für die test', NULL, '2015-12-01 14:55:32.000', 'demoadmin', NULL, NULL, NULL, NULL, NULL, NULL, true), +(4, 'pc2', 'Laptop', NULL, false, '2024-03-07 08:17:33.000', 'demoadmin', '2015-12-01 14:55:32.000', 'demoadmin', NULL, '00004', NULL, NULL, 'pc2', NULL, NULL, NULL, 3, NULL, 'Angelegt von demoadmin für die test', NULL, '2015-12-01 14:55:32.000', 'demoadmin', NULL, NULL, NULL, NULL, NULL, NULL, true); diff --git a/system/seeders/015_kalender.sql b/system/seeders/015_kalender.sql new file mode 100644 index 000000000..345e003a3 --- /dev/null +++ b/system/seeders/015_kalender.sql @@ -0,0 +1,210 @@ +-- Rooms setup for calendar tests +INSERT INTO public.tbl_adresse +(adresse_id, person_id, "name", strasse, plz, ort, gemeinde, nation, typ, heimatadresse, zustelladresse, firma_id, updateamum, updatevon, insertamum, insertvon, ext_id, rechnungsadresse, anmerkung, co_name) +VALUES(1, NULL, 'Hauptwohnsitz', 'Stromstraße 18-20/1/10', '1200', 'Wien', 'Wien', 'A', 'h', true, true, NULL, '2007-08-13 10:53:40.298', 'demoadmin', '2007-08-10 10:42:02.579', 'demoadmin', 14470, false, NULL, NULL); + +INSERT INTO public.tbl_standort +(kurzbz, adresse_id, standort_id, bezeichnung, insertvon, insertamum, updatevon, updateamum, ext_id, firma_id, code) +VALUES('Vienna Universit', 1, 1, 'Vienna University of Technology, Institute of Design and Assessment of Technology', 'demoadmin', '2012-02-02 10:24:11.803', 'demoadmin', '2015-05-27 09:05:53.894', NULL, 6033, NULL); + +INSERT INTO public.tbl_ort +(ort_kurzbz, bezeichnung, planbezeichnung, max_person, lehre, reservieren, aktiv, lageplan, dislozierung, kosten, ausstattung, updateamum, updatevon, insertamum, insertvon, ext_id, stockwerk, telefonklappe, standort_id, content_id, m2, gebteil, oe_kurzbz, arbeitsplaetze) +VALUES +('Bro_F7.37', 'Büro', 'F7.37', NULL, true, true, true, NULL, NULL, NULL, NULL, '2023-09-19 10:35:20.000', 'demoadmin', '2012-09-05 15:20:25.979', 'demoadmin', NULL, 7, NULL, 1, NULL, 21.58, 'F', 'etw', NULL), +('Bro_F7.38', 'Büro', 'F7.38', NULL, true, true, true, NULL, NULL, NULL, NULL, '2023-09-19 10:35:20.000', 'demoadmin', '2012-09-05 15:20:25.979', 'demoadmin', NULL, 7, NULL, 1, NULL, 21.58, 'F', 'etw', NULL), +('Bro_F7.39', 'Büro', 'F7.39', NULL, true, true, true, NULL, NULL, NULL, NULL, '2023-09-19 10:35:20.000', 'demoadmin', '2012-09-05 15:20:25.979', 'demoadmin', NULL, 7, NULL, 1, NULL, 21.58, 'F', 'etw', NULL), +('Bro_F7.40', 'Büro', 'F7.40', NULL, true, true, true, NULL, NULL, NULL, NULL, '2023-09-19 10:35:20.000', 'demoadmin', '2012-09-05 15:20:25.979', 'demoadmin', NULL, 7, NULL, 1, NULL, 21.58, 'F', 'etw', NULL), +('Bro_F7.41', 'Büro', 'F7.41', NULL, true, true, true, NULL, NULL, NULL, NULL, '2023-09-19 10:35:20.000', 'demoadmin', '2012-09-05 15:20:25.979', 'demoadmin', NULL, 7, NULL, 1, NULL, 21.58, 'F', 'etw', NULL); + +INSERT INTO public.tbl_ortraumtyp +(ort_kurzbz, hierarchie, raumtyp_kurzbz) +VALUES +('Bro_F7.37', 1, 'Dummy'), +('Bro_F7.38', 1, 'Dummy'), +('Bro_F7.39', 1, 'Dummy'), +('Bro_F7.40', 1, 'Dummy'), +('Bro_F7.41', 1, 'Dummy'); +-- //////////////////////////////////////////////////////////////////////////////////////////////////// + + +-- Calendar setup for event drag and drop tests +INSERT INTO lehre.tbl_kalender +(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, insertamum, insertvon, updateamum, updatevon) VALUES +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '08:00:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '08:45:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '08:45:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '09:30:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '09:40:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '10:25:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '10:25:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '11:10:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '09:40:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '10:25:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '09:40:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '10:25:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '10:25:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '11:10:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '10:25:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '11:10:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin') +; + +INSERT INTO lehre.tbl_kalender_ort +(location, ort_kurzbz, kalender_id) +VALUES(NULL, 'Bro_F7.37', 1); + +INSERT INTO lehre.tbl_kalender_lehreinheit +(lehreinheit_id, kalender_id) +VALUES +(51001, 1), +(51002, 2), +(51003, 3), +(51003, 4), +(51002, 5), +(51006, 6), +(51002, 7), +(51006, 8); +-- //////////////////////////////////////////////////////////////////////////////////////////////////// + + +-- Calendar setup for resize tests +INSERT INTO lehre.tbl_kalender +(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, insertamum, insertvon, updateamum, updatevon) VALUES +(date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '08:00:00', date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '08:45:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '4 day' + TIME '10:25:00', date_trunc('week', CURRENT_DATE) + INTERVAL '4 day' + TIME '11:10:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '4 day' + TIME '10:25:00', date_trunc('week', CURRENT_DATE) + INTERVAL '4 day' + TIME '11:10:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin') +; + +INSERT INTO lehre.tbl_kalender_lehreinheit +(lehreinheit_id, kalender_id) +VALUES +(51001, 9), +(51003, 10), +(51002, 11); +-- //////////////////////////////////////////////////////////////////////////////////////////////////// + + +-- Calendar setup for role preview tests +INSERT INTO lehre.tbl_kalender +(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, insertamum, insertvon, updateamum, updatevon) VALUES +(date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '13:35:00', date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '14:20:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '13:35:00', date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '14:20:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin') +; + +INSERT INTO lehre.tbl_kalender_lehreinheit +(lehreinheit_id, kalender_id) +VALUES +(51001, 12), +(51002, 13); +-- //////////////////////////////////////////////////////////////////////////////////////////////////// + +-- Calendar setup for API event creation tests +INSERT INTO lehre.tbl_kalender +(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, insertamum, insertvon, updateamum, updatevon) VALUES +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '18:35:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '19:20:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '18:35:00', date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '19:20:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin') +; + +INSERT INTO lehre.tbl_kalender_lehreinheit +(lehreinheit_id, kalender_id) +VALUES +(51003, 14), +(51005, 15); +-- //////////////////////////////////////////////////////////////////////////////////////////////////// + + +-- Calendar setup for API event update tests +INSERT INTO lehre.tbl_kalender +(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, insertamum, insertvon, updateamum, updatevon) +VALUES +(date_trunc('week', CURRENT_DATE) + INTERVAL '1 day' + TIME '19:30:00', date_trunc('week', CURRENT_DATE) + INTERVAL '1 day' + TIME '20:15:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '19:30:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '20:15:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '20:15:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '21:00:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '19:30:00', date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '20:15:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '20:15:00', date_trunc('week', CURRENT_DATE) + INTERVAL '3 day' + TIME '21:00:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '4 day' + TIME '19:30:00', date_trunc('week', CURRENT_DATE) + INTERVAL '4 day' + TIME '20:15:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '4 day' + TIME '19:30:00', date_trunc('week', CURRENT_DATE) + INTERVAL '4 day' + TIME '20:15:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '4 day' + TIME '20:15:00', date_trunc('week', CURRENT_DATE) + INTERVAL '4 day' + TIME '21:00:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '5 day' + TIME '19:30:00', date_trunc('week', CURRENT_DATE) + INTERVAL '5 day' + TIME '20:15:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '5 day' + TIME '20:15:00', date_trunc('week', CURRENT_DATE) + INTERVAL '5 day' + TIME '21:00:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '5 day' + TIME '20:15:00', date_trunc('week', CURRENT_DATE) + INTERVAL '5 day' + TIME '21:00:00', 'reservierung', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin') +; + +INSERT INTO lehre.tbl_kalender_ort +(location, ort_kurzbz, kalender_id) +VALUES +(NULL, 'Bro_F7.37', 17), +(NULL, 'Bro_F7.37', 18); + +INSERT INTO lehre.tbl_kalender_lehreinheit +(lehreinheit_id, kalender_id) +VALUES +(51001, 16), +(51001, 17), +(51003, 18), +(51001, 19), +(51003, 20), +(51003, 21), +(51006, 22), +(51004, 23), +(51005, 24), +(51004, 25), +(51006, 26); +-- //////////////////////////////////////////////////////////////////////////////////////////////////// + + +-- Calendar setup for Stundenraster disabled tests +INSERT INTO lehre.tbl_kalender +(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, insertamum, insertvon, updateamum, updatevon) VALUES +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '11:20:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '12:05:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '11:20:00', date_trunc('week', CURRENT_DATE) + INTERVAL '2 day' + TIME '12:05:00', 'lehreinheit', 'live', NULL, now(), 'demoadmin', NULL, 'demoadmin') +; + +INSERT INTO lehre.tbl_kalender_lehreinheit +(lehreinheit_id, kalender_id) +VALUES +(51002, 27), +(51006, 28); +-- //////////////////////////////////////////////////////////////////////////////////////////////////// + + +-- Calendar setup for resources tests +INSERT INTO lehre.tbl_kalender +(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, eindeutige_gruppen_id, insertamum, insertvon, updateamum, updatevon) +VALUES +(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '19:30:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '20:15:00', 'lehreinheit', 'live', NULL, 'ec1bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '20:15:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '21:00:00', 'lehreinheit', 'live', NULL, 'ec2bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '17:50:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '18:35:00', 'lehreinheit', 'live', NULL, 'ec3bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin'), +(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '18:35:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '19:20:00', 'lehreinheit', 'live', NULL, 'ec4bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin') +; + +INSERT INTO lehre.tbl_kalender_lehreinheit +(lehreinheit_id, kalender_id) +VALUES +(51004, 29), +(51006, 30), +(51006, 31), +(51004, 32); + +-- INSERT INTO public.tbl_variablenname +-- (name, defaultwert) +-- VALUES('ignore_resources_collisions', 'false'); + +INSERT INTO public.tbl_variable +(name, uid, wert) +VALUES('ignore_resources_collisions', 'demoadmin', 'false'); + +INSERT INTO lehre.tbl_betriebsmittel_kalender +(eindeutige_kalender_gruppen_id, betriebsmittel_id, anmerkung, quelle, insertamum, insertvon, updateamum, updatevon) +VALUES +('ec1bd001-facf-4b21-a671-bcb2d4f037fd'::uuid, 1, '', 'tempus_neu', '2026-06-17 16:48:59.669', 'demoadmin', '2026-06-17 17:13:23.000', 'demoadmin'), +('ec2bd001-facf-4b21-a671-bcb2d4f037fd'::uuid, 1, '', 'tempus_neu', '2026-06-17 16:48:59.669', 'demoadmin', '2026-06-17 17:13:23.000', 'demoadmin'), +('ec3bd001-facf-4b21-a671-bcb2d4f037fd'::uuid, 2, '', 'tempus_neu', '2026-06-17 16:48:59.669', 'demoadmin', '2026-06-17 17:13:23.000', 'demoadmin'), +('ec4bd001-facf-4b21-a671-bcb2d4f037fd'::uuid, 3, '', 'tempus_neu', '2026-06-17 16:48:59.669', 'demoadmin', '2026-06-17 17:13:23.000', 'demoadmin'); +-- //////////////////////////////////////////////////////////////////////////////////////////////////-- + + +INSERT INTO lehre.tbl_kalender_event +(kalender_id, titel, beschreibung) +VALUES(26, 'Test reservation', NULL); + +INSERT INTO lehre.tbl_kalender_event_teilnehmer +(kalender_id, rolle_kurzbz, uid, studiensemester_kurzbz, gruppe_kurzbz, studiengang_kz, semester, verband, gruppe, studentenlehrverband_id) +VALUES(26, 'teilnehmer', 'demolektor4', NULL, NULL, NULL, NULL, NULL, NULL, NULL); + +INSERT INTO campus.tbl_zeitsperre +(zeitsperre_id, zeitsperretyp_kurzbz, mitarbeiter_uid, bezeichnung, vondatum, vonstunde, bisdatum, bisstunde, vertretung_uid, updateamum, updatevon, insertamum, insertvon, erreichbarkeit_kurzbz, freigabeamum, freigabevon) +VALUES(1, 'DienstV', 'demolektor1', 'Test', date_trunc('week', CURRENT_DATE) + INTERVAL '5 day', NULL, date_trunc('week', CURRENT_DATE) + INTERVAL '5 day', NULL, 'demoadmin', '2007-04-02 11:04:31.000', 'demoadmin', '2007-04-02 11:04:31.000', 'demoadmin', 't', NULL, NULL); + + diff --git a/system/seeders/016_benotungstool_noten.sql b/system/seeders/016_benotungstool_noten.sql new file mode 100644 index 000000000..1b254ef03 --- /dev/null +++ b/system/seeders/016_benotungstool_noten.sql @@ -0,0 +1,183 @@ +-- Demo data for the Benotungstool / Gesamtnoteneingabe test suite. +-- +-- campus.vw_student_lehrveranstaltung, the source of getStudentsByLv(), links students to +-- Lehreinheiten through tbl_lehreinheitgruppe.gruppe_kurzbz = tbl_benutzergruppe.gruppe_kurzbz AND +-- a matching Studiensemester. +-- +-- RESULT: demolektor1 -> LV 5221 (12 students), demolektor2 -> LV 5121 (6, the foreign LV for the +-- access tests). + +BEGIN; + +-- Test semester: the running one, else the last started. Same rule as the suite. +CREATE TEMP TABLE sem AS +SELECT studiensemester_kurzbz AS kurzbz + FROM public.tbl_studiensemester + WHERE start <= now() + ORDER BY (ende >= now()) DESC, start DESC + LIMIT 1; + +-- The two LV fixtures. Own Lehreinheiten (511xx) so the tempus fixtures 51001-51006 stay untouched. +-- members_up_to = highest person_id that joins this group. +CREATE TEMP TABLE lv AS +SELECT v.*, l.semester + FROM (VALUES + (true, 5221, 51101, 'GRP_CYNOTEN_H', 'Cypress Noten Haupt', 'demolektor1', 512), + (false, 5121, 51102, 'GRP_CYNOTEN_F', 'Cypress Noten Fremd', 'demolektor2', 506) + ) AS v(main, lv_id, le_id, grp, grp_text, lektor, members_up_to) + JOIN lehre.tbl_lehrveranstaltung l ON l.lehrveranstaltung_id = v.lv_id; + +-- The 12 students. person_id is the anchor for every derived key. +CREATE TEMP TABLE stud AS +SELECT id AS person_id, 5000 + id AS prestudent_id, 's525b' || id AS uid + FROM generate_series(501, 512) AS g(id); + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM sem) THEN + RAISE EXCEPTION 'No Studiensemester found, the base dump is missing.'; + END IF; + IF (SELECT count(*) FROM lv) <> 2 THEN + RAISE EXCEPTION 'LV 5221/5121 missing, apply 013_studiengang_5.sql.'; + END IF; +END $$; + +-- Termin3 is absent from the base dump; without it every Termin3 insert hits a foreign key error. +INSERT INTO lehre.tbl_pruefungstyp (pruefungstyp_kurzbz, beschreibung) +VALUES ('Termin3', '3. Termin') + ON CONFLICT DO NOTHING; + +-- ------------------------------------------------------------------------------------- students +INSERT INTO public.tbl_person (person_id, vorname, nachname, gebdatum, geschlecht, aktiv) +SELECT person_id, 'Noten', 'Student ' || (person_id - 500), '2000-01-15', 'm', true + FROM stud + ON CONFLICT DO NOTHING; + +INSERT INTO public.tbl_prestudent (prestudent_id, person_id, studiengang_kz) +SELECT prestudent_id, person_id, 5 + FROM stud + ON CONFLICT DO NOTHING; + +INSERT INTO public.tbl_prestudentstatus + (prestudent_id, status_kurzbz, studiensemester_kurzbz, ausbildungssemester) +SELECT s.prestudent_id, 'Student', sem.kurzbz, lv.semester + FROM stud s, sem, lv + WHERE lv.main + ON CONFLICT DO NOTHING; + +INSERT INTO public.tbl_benutzer (uid, person_id, aktiv) +SELECT uid, person_id, true + FROM stud + ON CONFLICT DO NOTHING; + +-- matrikelnr is UNIQUE; scheme of the existing seeders is '251000' + studiengang_kz + person_id +INSERT INTO public.tbl_student + (student_uid, matrikelnr, prestudent_id, studiengang_kz, semester, verband, gruppe) +SELECT s.uid, '2510005' || s.person_id, s.prestudent_id, 5, lv.semester, '', '' + FROM stud s, lv + WHERE lv.main + ON CONFLICT DO NOTHING; + +-- Lehrverband assignment; (5, , '', '') is created by 013_studiengang_5.sql. +INSERT INTO public.tbl_studentlehrverband + (student_uid, studiensemester_kurzbz, studiengang_kz, semester, verband, gruppe) +SELECT s.uid, sem.kurzbz, 5, lv.semester, '', '' + FROM stud s, sem, lv + WHERE lv.main + ON CONFLICT DO NOTHING; + +-- ------------------------------------------------------------------- Lehreinheiten and Lektoren +INSERT INTO lehre.tbl_lehreinheit + (lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrform_kurzbz, + wochenrythmus, raumtyp, raumtypalternativ, sprache, lehrfach_id) +SELECT lv.le_id, lv.lv_id, sem.kurzbz, 'VO', 1, 'Dummy', 'Dummy', 'German', lv.lv_id + FROM lv, sem + ON CONFLICT DO NOTHING; + +-- Fix the semester if the Lehreinheit is left over from a run in a different one. +UPDATE lehre.tbl_lehreinheit le + SET studiensemester_kurzbz = sem.kurzbz + FROM lv, sem + WHERE le.lehreinheit_id = lv.le_id + AND le.studiensemester_kurzbz IS DISTINCT FROM sem.kurzbz; + +INSERT INTO lehre.tbl_lehreinheitmitarbeiter (lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz) +SELECT le_id, lektor, 'Lektor' + FROM lv + ON CONFLICT DO NOTHING; + +-- --------------------------------------------------------------------------------------- groups +INSERT INTO public.tbl_gruppe (gruppe_kurzbz, studiengang_kz, semester, bezeichnung, aktiv) +SELECT grp, 5, semester, grp_text, true + FROM lv + ON CONFLICT DO NOTHING; + +-- The row the existing seeders lack: attach the group to the Lehreinheit with gruppe_kurzbz set. +-- The primary key is a serial, so NOT EXISTS instead of ON CONFLICT. +INSERT INTO lehre.tbl_lehreinheitgruppe (lehreinheit_id, studiengang_kz, semester, gruppe_kurzbz) +SELECT lv.le_id, 5, lv.semester, lv.grp + FROM lv + WHERE NOT EXISTS (SELECT 1 FROM lehre.tbl_lehreinheitgruppe g + WHERE g.lehreinheit_id = lv.le_id AND g.gruppe_kurzbz = lv.grp); + +-- Membership WITH a semester. The primary key is (uid, gruppe_kurzbz) and excludes the semester, +-- so DO UPDATE repairs a row from an earlier run instead of duplicating it. +INSERT INTO public.tbl_benutzergruppe (uid, gruppe_kurzbz, studiensemester_kurzbz) +SELECT s.uid, lv.grp, sem.kurzbz + FROM stud s, lv, sem + WHERE s.person_id <= lv.members_up_to + ON CONFLICT (uid, gruppe_kurzbz) DO UPDATE + SET studiensemester_kurzbz = EXCLUDED.studiensemester_kurzbz; + +-- -------------------------------------------------------------------------------- Notenschluessel +INSERT INTO lehre.tbl_notenschluessel (notenschluessel_kurzbz, bezeichnung) +VALUES ('CYNOTEN', 'Cypress Testnotenschluessel') + ON CONFLICT DO NOTHING; + +-- getNote() takes the row with the largest punkte value <= the points given. The 0 threshold makes +-- sure every value maps to a grade. Serial primary key, so NOT EXISTS. +INSERT INTO lehre.tbl_notenschluesselaufteilung (notenschluessel_kurzbz, note, punkte) +SELECT 'CYNOTEN', a.note, a.punkte + FROM (VALUES (5, 0.0), (4, 51.0), (3, 64.0), (2, 77.0), (1, 90.0)) AS a(note, punkte) + WHERE NOT EXISTS (SELECT 1 FROM lehre.tbl_notenschluesselaufteilung x + WHERE x.notenschluessel_kurzbz = 'CYNOTEN' AND x.punkte = a.punkte); + +-- Set the semester explicitly: getKurzbzForLv() builds "... AND studiensemester_kurzbz = ? OR +-- studiensemester_kurzbz IS NULL" without parentheses, so a NULL-semester assignment would apply +-- to every Lehrveranstaltung through AND/OR precedence. +INSERT INTO lehre.tbl_notenschluesselzuordnung + (notenschluessel_kurzbz, lehrveranstaltung_id, studiensemester_kurzbz) +SELECT 'CYNOTEN', lv.lv_id, sem.kurzbz + FROM lv, sem + WHERE lv.main + AND NOT EXISTS (SELECT 1 FROM lehre.tbl_notenschluesselzuordnung z + WHERE z.notenschluessel_kurzbz = 'CYNOTEN' + AND z.lehrveranstaltung_id = lv.lv_id + AND z.studiensemester_kurzbz = sem.kurzbz); + +COMMIT; + +-- Fires if the view stays empty anyway, which is the bug this seeder exists to fix. +DO $$ +DECLARE v_count integer; +BEGIN + SELECT count(*) INTO v_count + FROM campus.vw_student_lehrveranstaltung v, sem + WHERE v.lehrveranstaltung_id = 5221 AND v.studiensemester_kurzbz = sem.kurzbz; + + IF v_count = 0 THEN + RAISE EXCEPTION 'No students in LV 5221 (%), fixture unusable.', (SELECT kurzbz FROM sem); + END IF; + + RAISE NOTICE 'OK: % students in LV 5221 (%)', v_count, (SELECT kurzbz FROM sem); +END $$; + +SELECT (SELECT kurzbz FROM sem) AS semester, + (SELECT count(*) FROM campus.vw_student_lehrveranstaltung v, sem + WHERE v.lehrveranstaltung_id = 5221 AND v.studiensemester_kurzbz = sem.kurzbz) AS lv5221, + (SELECT count(*) FROM campus.vw_student_lehrveranstaltung v, sem + WHERE v.lehrveranstaltung_id = 5121 AND v.studiensemester_kurzbz = sem.kurzbz) AS lv5121, + (SELECT note FROM lehre.tbl_note WHERE bezeichnung = 'entschuldigt') AS entschuldigt, + (SELECT note FROM lehre.tbl_note WHERE bezeichnung = 'Noch nicht eingetragen') AS noch_nicht, + (SELECT count(*) FROM lehre.tbl_notenschluesselaufteilung + WHERE notenschluessel_kurzbz = 'CYNOTEN') AS notenschluessel; diff --git a/system/seeders/017_extension_grants.sql b/system/seeders/017_extension_grants.sql new file mode 100644 index 000000000..46ee620ce --- /dev/null +++ b/system/seeders/017_extension_grants.sql @@ -0,0 +1,33 @@ +-- Grant all db users access to the "extension" schema. +-- +-- The dump creates the schema and gives it no grants, unlike public/lehre/campus, and +-- truncate_db.sql only grants on public. Any application not connecting as the owner then fails +-- every query in that schema with "permission denied for schema extension" + +DO $$ +DECLARE + v_role text; + v_roles text[] := ARRAY['web', 'vilesci', 'wawi', 'admin']; + v_done integer := 0; +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'extension') THEN + RAISE NOTICE 'Schema "extension" does not exist, skipped.'; + RETURN; + END IF; + + FOREACH v_role IN ARRAY v_roles + LOOP + CONTINUE WHEN NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = v_role); + + EXECUTE format('GRANT USAGE ON SCHEMA extension TO %I', v_role); + EXECUTE format('GRANT SELECT ON ALL TABLES IN SCHEMA extension TO %I', v_role); + EXECUTE format('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA extension TO %I', v_role); + -- and for whatever an addon creates later + EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA extension GRANT SELECT ON TABLES TO %I', v_role); + EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA extension GRANT EXECUTE ON FUNCTIONS TO %I', v_role); + + v_done := v_done + 1; + END LOOP; + + RAISE NOTICE 'Granted on schema "extension" for % role(s).', v_done; +END $$; diff --git a/system/seeders/README.txt b/system/seeders/README.txt new file mode 100644 index 000000000..22480e725 --- /dev/null +++ b/system/seeders/README.txt @@ -0,0 +1,32 @@ +== README == +Dieser Ordner enthält Anonyme Demo Daten +Diese können auf einen leeren FH-Complete Dump angewandt werden um Demo/Testdaten einzufügen. + +Die Datensätze werden mit fixen IDs erstellt um die Verknüpfung der Elemente Untereinander zu ermöglichen. +Danach werden die Sequences entsprechend aktualisiert. + +Die Inserts können daher nur auf einen leeren Dump gespielt werden da es sonst zu ID Konflikten kommmen kann. + +== Logik der ID Generierung == + +Studiengangskennzahl 1 +Studierende des Studiengangs 1 haben Person ID 3-Stellig mit Studiengangskennzahl startend : 101, 102, 103 +PrestudentID ist 4-Stellig mit Studiengangskennzahl+PersonID: 1101, 1102, 1103 +Dadurch können Inserts leichter erstellt werden. + +Bei Lehrveranstaltungen gilt ähnliches Konzept für +Studienordnung +Studienplan +Modul +Lehrveranstaltung + +== Helper Funktionen == +Damit die Daten immer im aktuellen Semester angelegt werden gibt es eine Helper Funktion damit das Studiensemester nicht hartcodiert werden muss: + +=== NearestWintersemester === + +NearestWintersemester(0) -> Liefert das näheste Wintersemester +NearestWintersemester(-1) -> Liefert Studiensemester VOR dem nähesten Wintersemester +NearestWintersemester(+1) -> Liefert Studiensemester NACH dem nähesten Wintersemester + + diff --git a/system/setup_testinstance.php b/system/setup_testinstance.php new file mode 100644 index 000000000..f76191be0 --- /dev/null +++ b/system/setup_testinstance.php @@ -0,0 +1,229 @@ + FHComplete-3.3.sql -> + * checksystem.php -> seeders. This script only adds what db_setup.sh does not cover — addon + * dbupdates, extension SQL and a closing health check. + * + * needs psql and db_setup.sh. Connection data comes from config/system.config.inc.php. + */ + +if (php_sapi_name() !== 'cli') { + http_response_code(403); + exit("Command line only.\n"); +} + +error_reporting(E_ALL & ~E_DEPRECATED & ~E_NOTICE & ~E_WARNING); +set_time_limit(0); +ini_set('memory_limit', '1024M'); + +define('SETUP_ROOT', dirname(__DIR__)); +define('DB_SETUP_SH', '/home/testing/fhc-coreutils/tests/automated/db_setup.sh'); + +function out($msg = '') { fwrite(STDOUT, $msg . PHP_EOL); } +function step($msg) { out(''); out('=== ' . $msg . ' ' . str_repeat('=', max(0, 62 - strlen($msg)))); } +function info($msg) { out(' ' . $msg); } +function ok($msg) { out(' + ' . $msg); } +function warn($msg) { out(' ! ' . $msg); } +function bail($msg) { out(''); out(' X ' . $msg); out(''); exit(1); } + +function loadConfig() +{ + $file = SETUP_ROOT . '/config/system.config.inc.php'; + if (!is_file($file)) bail("Config not found: $file"); + + require_once($file); + + // the FHC config sets error_reporting(E_ALL); restore ours, or every include of the legacy + // classes prints deprecation notices over the output + error_reporting(E_ALL & ~E_DEPRECATED & ~E_NOTICE & ~E_WARNING); + + foreach (['DB_HOST', 'DB_NAME', 'DB_USER'] as $const) { + if (!defined($const)) bail("$const is not defined in $file."); + } + putenv('PGPASSWORD=' . (defined('DB_PASSWORD') ? DB_PASSWORD : '')); +} + +function connectDb() +{ + $conn = @pg_connect(sprintf( + 'host=%s port=%s dbname=%s user=%s password=%s', + DB_HOST, defined('DB_PORT') ? DB_PORT : '5432', DB_NAME, DB_USER, + defined('DB_PASSWORD') ? DB_PASSWORD : '' + )); + if ($conn === false) bail('Cannot connect to ' . DB_NAME . '@' . DB_HOST . ' as ' . DB_USER . '.'); + return $conn; +} + +function scalar($conn, $sql) +{ + $res = @pg_query($conn, $sql); + if ($res === false) return null; + $row = pg_fetch_row($res); + return $row ? $row[0] : null; +} + +/** Runs one .sql file. Returns the error count; "already exists" is normal on a repeat run. */ +function psqlFile($file) +{ + exec(sprintf('psql -h %s -p %s -U %s -d %s -f %s 2>&1', + escapeshellarg(DB_HOST), escapeshellarg(defined('DB_PORT') ? DB_PORT : '5432'), + escapeshellarg(DB_USER), escapeshellarg(DB_NAME), escapeshellarg($file)), $lines); + + $errors = 0; + foreach ($lines as $line) { + if (stripos($line, 'ERROR') !== false && stripos($line, 'already exists') === false) $errors++; + } + return $errors; +} + +/** Guards, before anything is dropped: --setup empties the target, so refuse anything but cy-*. */ +function preflight() +{ + step('preflight'); + + if (!shell_exec('command -v psql')) bail('psql is missing. Run this on the dev machine.'); + if (!is_file(DB_SETUP_SH)) bail('db_setup.sh not found: ' . DB_SETUP_SH); + + if (strpos(DB_NAME, 'cy-') !== 0) { + bail("Target is '" . DB_NAME . "'. --setup drops every schema and only touches cy-* databases."); + } + + $seeders = glob(SETUP_ROOT . '/system/seeders/*.sql'); + if (!$seeders) bail('No seeders in ' . SETUP_ROOT . '/system/seeders/ — the rebuild would leave an empty database.'); + + ok(count($seeders) . ' seeders, target ' . DB_NAME); +} + +/** Output is passed through verbatim, checksystem.php's HTML included. */ +function rebuild() +{ + step('db_setup.sh'); + info('truncate -> FHComplete-3.3.sql -> checksystem.php -> seeders'); + out(''); + + passthru(escapeshellarg(DB_SETUP_SH) . ' ' . escapeshellarg(SETUP_ROOT), $code); + if ($code !== 0) bail('db_setup.sh failed with exit code ' . $code . '.'); + + ok('database rebuilt'); +} + +/** + * Addon DB updates. Their install.php files are web installers and empty stubs; the DDL sits in + * addons//system/dbupdate_*.php and expects CHECKSYSTEM and a $db in scope. + */ +function runAddons() +{ + step('addons'); + + $files = glob(SETUP_ROOT . '/addons/*/system/dbupdate_*.php'); + if (!$files) { info('none found'); return; } + + if (!defined('CHECKSYSTEM')) define('CHECKSYSTEM', 'setup_testinstance'); + require_once(SETUP_ROOT . '/include/basis_db.class.php'); + $db = new basis_db(); + + foreach ($files as $file) { + $name = basename(dirname(dirname($file))) . '/' . basename($file); + ob_start(); + try { + require($file); + ob_end_clean(); + ok($name); + } catch (Throwable $t) { + ob_end_clean(); + warn($name . ': ' . $t->getMessage()); + } + } +} + +/** Extension SQL, for an instance that has the files but never ran their sql//*.sql. */ +function runExtensions() +{ + step('extensions'); + + $dirs = glob(SETUP_ROOT . '/application/extensions/*/sql', GLOB_ONLYDIR); + if (!$dirs) { info('none found'); return; } + + foreach ($dirs as $dir) { + // every version the extension ships, oldest first; a test instance always wants the newest schema + $files = glob($dir . '/*/*.sql') ?: []; + sort($files, SORT_NATURAL); + + $errors = 0; + foreach ($files as $file) $errors += psqlFile($file); + + $label = sprintf('%s (%d file(s)%s)', basename(dirname($dir)), count($files), $errors ? ", $errors errors" : ''); + if ($errors) warn($label); else ok($label); + } +} + +/** + * General signs of a usable instance only. Whether a given suite's fixture is in place is that + * suite's own business (see tests/cypress/tools/dbCheck.js). + */ +function verify($conn) +{ + step('verify'); + + $checks = [ + // dump applied + ['Tables', "SELECT count(*) FROM information_schema.tables + WHERE table_schema IN ('public','lehre','campus')", function ($v) { return $v > 200; }], + // checksystem.php ran + ['Phrases', "SELECT count(*) FROM system.tbl_phrase", function ($v) { return $v > 0; }], + ['Permissions', "SELECT count(*) FROM system.tbl_berechtigung", function ($v) { return $v > 0; }], + // seeders ran + ['Studiensemester', "SELECT count(*) FROM public.tbl_studiensemester + WHERE start <= now()", function ($v) { return $v > 0; }], + ['Mitarbeiter', "SELECT count(*) FROM public.tbl_mitarbeiter", function ($v) { return $v > 0; }], + ['Studenten', "SELECT count(*) FROM public.tbl_student", function ($v) { return $v > 0; }], + ]; + + $failed = 0; + foreach ($checks as list($label, $sql, $test)) { + $value = scalar($conn, $sql); + $passed = $test($value); + if (!$passed) $failed++; + out(sprintf(' [%s] %-20s %s', $passed ? 'OK ' : 'XX ', $label, $value === null ? '(null)' : $value)); + } + + return $failed === 0; +} + +$flags = array_slice($argv, 1); +$setup = in_array('--setup', $flags, true); +$verify = in_array('--verify', $flags, true); + +// --setup must be spelled out: the run empties the target database. +if (!$setup && !$verify) { + out(''); + out(' php system/setup_testinstance.php --setup rebuild the database, then verify'); + out(' php system/setup_testinstance.php --verify verify only, write nothing'); + out(''); + exit(0); +} + +loadConfig(); + +out(''); +out(' Target : ' . DB_NAME . ' @ ' . DB_HOST); + +if ($setup) { + preflight(); + rebuild(); + runAddons(); + runExtensions(); +} + +$success = verify(connectDb()); + +out(''); +out($success ? ' Done.' : ' Finished with findings, see above.'); +out(''); + +exit($success ? 0 : 1); diff --git a/tests/cypress/.env.example b/tests/cypress/.env.example new file mode 100644 index 000000000..ba505a998 --- /dev/null +++ b/tests/cypress/.env.example @@ -0,0 +1,56 @@ +# Copy to tests/cypress/.env (gitignored) and fill in. +# THIS FILE IS COMMITTED - never put a real password in it. + +# ---- required ------------------------------------------------------------------------------- + +# Your own instance, e.g. https://c3p0.dev.technikum-wien.at//fhcompletecis4 +# No default anywhere: empty fails the run rather than pointing it at a colleague's installation. +BASE_URL= + +# HTTP Basic + LDAP user the API calls authenticate as. Use the LECTURER under test +# (e.g. demolektor1) - getBenotungstoolContext resolves that user's own Lehrveranstaltungen, +# and an admin account usually teaches nothing and would resolve none. +USER_NAME= +USER_PASSWORD= + +# ---- required: fixture reset ------------------------------------------------------------------ +# The suite resets its fixture over a direct database connection; there is no HTTP fallback, so +# the specs fail without it. TEST_ENV_PREFIX selects the _DB_* / _SSH_* names. +TEST_ENV_PREFIX=NOTEN + +NOTEN_DB_HOST=db.dev.technikum-wien.at +NOTEN_DB_PORT=5432 +NOTEN_DB_NAME= +NOTEN_DB_USER= +NOTEN_DB_PASSWORD= +NOTEN_DB_ALLOW_WRITES=true + +# ---- required from a workstation: SSH tunnel -------------------------------------------------- +# pg_hba.conf admits only the application server. NOTEN_DB_HOST above stays the REAL database - +# the tunnel rewrites the connection to 127.0.0.1 itself. +# +# Two things that are easy to get wrong: +# - the SSH host is usually NOT the web hostname +# - a key whose filename is not an OpenSSH default is NEVER tried automatically; name it here, +# or leave NOTEN_SSH_KEY empty to use your SSH agent +NOTEN_SSH_TUNNEL=true +NOTEN_SSH_HOST= +NOTEN_SSH_USER= +NOTEN_SSH_KEY= + +# ---- optional --------------------------------------------------------------------------------- + +# Semester and LV are discovered at runtime. Pin them only when discovery picks the wrong one. +NOTEN_SEM= +NOTEN_LV_ID= + +# Access-control specs: this API has no impersonation, so scoping needs a second real login. +# NOTEN_FOREIGN_LV_ID must be an LV this teacher does NOT teach. Unset -> those specs skip. +NOTEN_TEACHER_USER= +NOTEN_TEACHER_PASSWORD= +NOTEN_FOREIGN_LV_ID= + +# saveStudentenNoten always sends the Notenfreigabe mail (not behind a config flag). Leave off +# unless outbound mail on this instance is a dead end. Unset -> the happy-path specs skip. +NOTEN_FREIGABE_ENABLED=false +NOTEN_FREIGABE_PASSWORD= diff --git a/tests/cypress/e2e/specs/api/noten/noten.access.cy.js b/tests/cypress/e2e/specs/api/noten/noten.access.cy.js new file mode 100644 index 000000000..4f1e368fc --- /dev/null +++ b/tests/cypress/e2e/specs/api/noten/noten.access.cy.js @@ -0,0 +1,197 @@ +/** + * Access control (P2) - assertLvAccess scoping (Noten.php:243). + * + * There is no impersonation in this API, so testing teacher scoping needs that teacher's real + * credentials: NOTEN_TEACHER_USER / NOTEN_TEACHER_PASSWORD / NOTEN_FOREIGN_LV_ID. + */ + +import { notenApi } from "../../../../support/api/notenApi"; +import { + expectAuthError, + expectNotenError, + expectNotenSuccess, +} from "../../../../support/helpers/notenErrors"; +import { loadNotenContext } from "../../../../support/helpers/notenTestData"; + +const NOTEN_API = "/index.ci.php/api/frontend/v1/Noten"; + +const teacherConfigured = () => + Boolean(Cypress.env("NOTEN_TEACHER_USER") && Cypress.env("NOTEN_FOREIGN_LV_ID")); + +/** A getStudentenNoten call as an explicitly chosen user (or with no credentials at all). */ +const getStudentenNotenAs = (auth, lvId, semKurzbz) => + cy.request({ + method: "GET", + url: `${NOTEN_API}/getStudentenNoten`, + qs: { lv_id: lvId, sem_kurzbz: semKurzbz }, + auth, + failOnStatusCode: false, + }); + +describe("Noten API - access control", () => { + let ctx; + + before(() => { + loadNotenContext().then((context) => { + ctx = context; + }); + }); + + describe("authentication", () => { + it("rejects an unauthenticated request", () => { + // Without this the auth is still active from last login + cy.clearAllCookies(); + + cy.request({ + method: "GET", + url: `${NOTEN_API}/getCisConfig`, + failOnStatusCode: false, + }).then((response) => { + expectAuthError(response); + }); + }); + + it("rejects invalid credentials", () => { + cy.request({ + method: "GET", + url: `${NOTEN_API}/getCisConfig`, + auth: { username: "no-such-user", password: "no-such-password" }, + failOnStatusCode: false, + }).then((response) => { + expectAuthError(response); + }); + }); + }); + + describe("the configured API user", () => { + it("can read the students of the test LV", () => { + notenApi.getStudentenNoten(ctx.lvId, ctx.semKurzbz).then((response) => { + const data = expectNotenSuccess(response, "getStudentenNoten on the own/admin LV"); + expect(data[0], "student list").to.be.an("array").and.not.be.empty; + }); + }); + }); + + describe("teacher scoping", () => { + beforeEach(function () { + if (!teacherConfigured()) { + cy.log( + "Skipped: needs NOTEN_TEACHER_USER / NOTEN_TEACHER_PASSWORD and NOTEN_FOREIGN_LV_ID " + + "(an LV that teacher does NOT teach). This API has no impersonation, so a second " + + "real login is required.", + ); + this.skip(); + } + }); + + const teacherAuth = () => ({ + username: Cypress.env("NOTEN_TEACHER_USER"), + password: Cypress.env("NOTEN_TEACHER_PASSWORD"), + }); + + it("lets a teacher read an LV they teach", () => { + notenApi.getBenotungstoolContext(ctx.semKurzbz).then(() => { + cy.request({ + method: "GET", + url: `${NOTEN_API}/getBenotungstoolContext`, + qs: { sem_kurzbz: ctx.semKurzbz }, + auth: teacherAuth(), + failOnStatusCode: false, + }).then((response) => { + const context = expectNotenSuccess(response, "teacher getBenotungstoolContext"); + const lvs = context.lehrveranstaltungen || []; + + expect( + lvs.length, + `${Cypress.env("NOTEN_TEACHER_USER")} must teach at least one LV in ` + + `${ctx.semKurzbz} for this test to mean anything`, + ).to.be.greaterThan(0); + + getStudentenNotenAs(teacherAuth(), lvs[0].lehrveranstaltung_id, ctx.semKurzbz).then( + (studentsResponse) => { + expectNotenSuccess(studentsResponse, "teacher reading their own LV"); + }, + ); + }); + }); + }); + + it("denies a teacher access to an LV they do not teach", () => { + getStudentenNotenAs( + teacherAuth(), + Cypress.env("NOTEN_FOREIGN_LV_ID"), + ctx.semKurzbz, + ).then((response) => { + expectAuthError(response); + }); + }); + + it("denies a teacher writing a grade in an LV they do not teach", () => { + cy.request({ + method: "POST", + url: `${NOTEN_API}/saveNotenvorschlag`, + body: { + lv_id: Cypress.env("NOTEN_FOREIGN_LV_ID"), + sem_kurzbz: ctx.semKurzbz, + student_uid: ctx.students[0].uid, + note: ctx.gradeNotes[0], + punkte: null, + }, + auth: teacherAuth(), + failOnStatusCode: false, + }).then((response) => { + expectAuthError(response); + }); + }); + }); + + describe("getLvForStudiengang (Assistenz flow)", () => { + it("refuses a Studiengang the caller is not entitled for", () => { + // 0 is not a real studiengang_kz, so no caller can be entitled for it. Admins bypass the + // check entirely, in which case the request succeeds with an empty list instead. + notenApi.getLvForStudiengang(0, ctx.semKurzbz).then((response) => { + expect(response.status, "either denied (500) or allowed-as-admin (200)").to.be.oneOf([ + 200, 500, + ]); + + if (response.status === 500) { + expect(response.body).to.have.nested.property("meta.status", "error"); + expect(response.body.errors, "a denial carries a message").to.be.an("array").and.not + .be.empty; + } else { + expect(response.body.data, "an admin gets an empty LV list").to.be.an("array"); + } + }); + }); + + it("rejects a call without the required parameters", () => { + cy.request({ + method: "GET", + url: `${NOTEN_API}/getLvForStudiengang`, + qs: { sem_kurzbz: ctx.semKurzbz }, + auth: { + username: Cypress.env("adminusername"), + password: Cypress.env("adminpassword"), + }, + failOnStatusCode: false, + }).then((response) => { + expect(response.status, "missing studiengang_kz").to.eq(500); + expect(response.body).to.have.nested.property("meta.status", "error"); + }); + }); + }); + + describe("getBenotungstoolContext shape", () => { + it("returns the role-determining payload", () => { + notenApi.getBenotungstoolContext(ctx.semKurzbz).then((response) => { + const context = expectNotenSuccess(response, "getBenotungstoolContext"); + + expect(context).to.have.property("isAssistenz"); + expect(context.isAssistenz, "isAssistenz is a boolean").to.be.a("boolean"); + expect(context.studiengaenge, "studiengaenge").to.be.an("array"); + expect(context.lehrveranstaltungen, "lehrveranstaltungen").to.be.an("array"); + expect(context).to.have.property("preselectStudiengang_kz"); + }); + }); + }); +}); diff --git a/tests/cypress/e2e/specs/api/noten/noten.bulk.cy.js b/tests/cypress/e2e/specs/api/noten/noten.bulk.cy.js new file mode 100644 index 000000000..11bab1016 --- /dev/null +++ b/tests/cypress/e2e/specs/api/noten/noten.bulk.cy.js @@ -0,0 +1,223 @@ +/** + * Bulk / import paths (P2). + * + * Same validatePruefungAdd guards as the single save, but reported differently: HTTP 200 with the + * error string in data[uid] per row (Noten.php:1600/:1656). createPruefungen is the main vehicle + * because it always writes "Noch nicht eingetragen" and never touches punkte. + */ + +import { notenApi } from "../../../../support/api/notenApi"; +import { + expectBulkRowAccepted, + expectBulkRowError, + expectNotenSuccess, +} from "../../../../support/helpers/notenErrors"; +import { + attemptDate, + loadNotenContext, + readLvGesamtnote, + requireDbReset, + resetNotenState, + seedBaseline, +} from "../../../../support/helpers/notenTestData"; +import { + addPruefung, + attemptsOfTyp, + enabledRetakeTypes, + readState, +} from "../../../../support/helpers/notenScenario"; + +describe("Noten API - bulk paths", () => { + let ctx; + + before(() => { + requireDbReset(); + loadNotenContext().then((context) => { + ctx = context; + }); + }); + + describe("createPruefungen", () => { + it("rejects only the row that breaks a §1 rule and accepts the rest", () => { + const atCap = ctx.students[0]; + const fresh = ctx.students[1]; + const retakes = enabledRetakeTypes(ctx); + + resetNotenState(ctx); + seedBaseline(ctx, atCap.uid, { freigegeben: true }); + seedBaseline(ctx, fresh.uid, { freigegeben: true }); + + // drive the first student up to the cap + retakes.forEach((typ, i) => { + addPruefung(ctx, atCap, { + note: ctx.gradeNotes[0], + datum: attemptDate(ctx, i + 1), + typ, + }).then((response) => { + expectNotenSuccess(response, `bring ${atCap.uid} to the cap via ${typ}`); + }); + }); + + const datum = attemptDate(ctx, retakes.length + 2); + + notenApi + .createPruefungen( + [ + { uid: atCap.uid, typ: retakes[retakes.length - 1], lehreinheit_id: atCap.lehreinheit_id }, + { uid: fresh.uid, typ: "Termin2", lehreinheit_id: fresh.lehreinheit_id }, + ], + datum, + ctx.lvId, + ctx.semKurzbz, + ) + .then((response) => { + // the request as a whole succeeds - errors are per row + const data = expectNotenSuccess(response, "createPruefungen"); + + expectBulkRowError(data, atCap.uid, "maxAntritteReached"); + expectBulkRowAccepted(data, fresh.uid); + }); + + readState(ctx).then((data) => { + const created = attemptsOfTyp(data, fresh.uid, "Termin2"); + expect( + created.map((p) => String(p.datum).slice(0, 10)), + "the accepted row was really created", + ).to.include(datum); + }); + }); + + it("applies the chronological rule per row", () => { + const student = ctx.students[2]; + + resetNotenState(ctx); + seedBaseline(ctx, student.uid, { freigegeben: true }); + + // an open (uncounted) attempt occupies a date without consuming an Antritt + addPruefung(ctx, student, { + note: ctx.notes.nochNichtEingetragen, + datum: attemptDate(ctx, 3), + typ: "Termin2", + }).then((response) => { + expectNotenSuccess(response, "seed an open Termin2"); + }); + + notenApi + .createPruefungen( + [{ uid: student.uid, typ: "Termin2", lehreinheit_id: student.lehreinheit_id }], + attemptDate(ctx, 2), // earlier than the existing attempt + ctx.lvId, + ctx.semKurzbz, + ) + .then((response) => { + const data = expectNotenSuccess(response, "createPruefungen out of order"); + expectBulkRowError(data, student.uid, "pruefungDatumBeforeExisting"); + }); + }); + + it("rejects a call without the required parameters", () => { + cy.request({ + method: "POST", + url: "/index.ci.php/api/frontend/v1/Noten/createPruefungen", + body: { lva_id: ctx.lvId, sem_kurzbz: ctx.semKurzbz }, + auth: { + username: Cypress.env("adminusername"), + password: Cypress.env("adminpassword"), + }, + failOnStatusCode: false, + }).then((response) => { + expect(response.status, "missing uids/datum").to.eq(500); + expect(response.body).to.have.nested.property("meta.status", "error"); + }); + }); + }); + + describe("savePruefungenBulk", () => { + beforeEach(function () { + if (ctx.cisConfig.CIS_GESAMTNOTE_PUNKTE) { + // With punkte mode on, this endpoint unconditionally re-derives every row's note from + // the Notenschlüssel (Noten.php:1638-1643), so a row would need valid punkte rather than + // a grade. Covering that needs a points-based fixture; skip rather than assert nonsense. + cy.log( + "Skipped: CIS_GESAMTNOTE_PUNKTE is enabled, so savePruefungenBulk grades by points " + + "and this grade-based scenario does not apply.", + ); + this.skip(); + } + requireDbReset(); + }); + + it("applies the §1 rules per row", () => { + const atCap = ctx.students[0]; + const fresh = ctx.students[1]; + const retakes = enabledRetakeTypes(ctx); + + resetNotenState(ctx); + seedBaseline(ctx, atCap.uid, { freigegeben: true }); + seedBaseline(ctx, fresh.uid, { freigegeben: true }); + + retakes.forEach((typ, i) => { + addPruefung(ctx, atCap, { + note: ctx.gradeNotes[0], + datum: attemptDate(ctx, i + 1), + typ, + }); + }); + + const datum = attemptDate(ctx, retakes.length + 2); + + notenApi + .savePruefungenBulk(ctx.lvId, ctx.semKurzbz, [ + { + uid: atCap.uid, + typ: retakes[retakes.length - 1], + note: ctx.gradeNotes[0], + punkte: null, + datum, + lehreinheit_id: atCap.lehreinheit_id, + }, + { + uid: fresh.uid, + typ: "Termin2", + note: ctx.gradeNotes[0], + punkte: null, + datum, + lehreinheit_id: fresh.lehreinheit_id, + }, + ]) + .then((response) => { + const data = expectNotenSuccess(response, "savePruefungenBulk"); + expectBulkRowError(data, atCap.uid, "maxAntritteReached"); + expectBulkRowAccepted(data, fresh.uid); + }); + }); + }); + + describe("saveNotenvorschlagBulk", () => { + it("writes an LV note for every row", () => { + const [a, b] = ctx.students; + + resetNotenState(ctx); + + notenApi + .saveNotenvorschlagBulk(ctx.lvId, ctx.semKurzbz, [ + { uid: a.uid, note: ctx.gradeNotes[0], punkte: null }, + { uid: b.uid, note: ctx.gradeNotes[1], punkte: null }, + ]) + .then((response) => { + const data = expectNotenSuccess(response, "saveNotenvorschlagBulk"); + expect(data, "one lvgesamtnote row per input row").to.be.an("array").and.have.length(2); + }); + + readLvGesamtnote(ctx, a.uid).then((row) => { + expect(row, `row for ${a.uid}`).to.not.be.null; + expect(String(row.note)).to.eq(String(ctx.gradeNotes[0])); + }); + + readLvGesamtnote(ctx, b.uid).then((row) => { + expect(row, `row for ${b.uid}`).to.not.be.null; + expect(String(row.note)).to.eq(String(ctx.gradeNotes[1])); + }); + }); + }); +}); diff --git a/tests/cypress/e2e/specs/api/noten/noten.freigabe.cy.js b/tests/cypress/e2e/specs/api/noten/noten.freigabe.cy.js new file mode 100644 index 000000000..1c1580da5 --- /dev/null +++ b/tests/cypress/e2e/specs/api/noten/noten.freigabe.cy.js @@ -0,0 +1,183 @@ +/** + * Notenfreigabe (P1, cases 12-14). + * + * A successful saveStudentenNoten ALWAYS sends the Notenfreigabe mail (Noten.php:585, not behind a + * config flag), so the happy path is opt-in via NOTEN_FREIGABE_ENABLED. The wrong-password test + * needs no gate: the check runs before any write and before the mail. + */ + +import { notenApi } from "../../../../support/api/notenApi"; +import { expectNotenError, expectNotenSuccess } from "../../../../support/helpers/notenErrors"; +import { + baselineBenotungsdatum, + loadNotenContext, + readLvGesamtnote, + requireDbReset, + resetNotenState, + seedBaseline, +} from "../../../../support/helpers/notenTestData"; +import { lvNoteOf, readState } from "../../../../support/helpers/notenScenario"; + +const freigabeEnabled = () => String(Cypress.env("NOTEN_FREIGABE_ENABLED")).toLowerCase() === "true"; +const freigabePassword = () => + Cypress.env("NOTEN_FREIGABE_PASSWORD") || Cypress.env("adminpassword"); + +/** The payload shape saveStudentenNoten expects per student (see the studlist builder). */ +const notenPayload = (student, noteBezeichnung) => ({ + uid: student.uid, + matrikelnr: student.matrikelnr || student.matr_nr || "", + kuerzel: student.kuerzel || "", + nachname: student.nachname || "", + vorname: student.vorname || "", + noteBezeichnung: noteBezeichnung || "", +}); + +describe("Noten API - Notenfreigabe", () => { + let ctx; + + before(() => { + loadNotenContext().then((context) => { + ctx = context; + }); + }); + + describe("password gate", () => { + it("rejects a Freigabe with a wrong password and changes nothing", () => { + requireDbReset(); + + const student = ctx.students[0]; + + resetNotenState(ctx); + seedBaseline(ctx, student.uid, { + note: ctx.gradeNotes[0], + freigegeben: false, + benotungsdatum: baselineBenotungsdatum(ctx), + }); + + notenApi + .saveStudentenNoten( + "definitely-not-the-password", + [notenPayload(student)], + ctx.lvId, + ctx.semKurzbz, + ) + .then((response) => { + expectNotenError(response, "wrongPassword"); + }); + + readLvGesamtnote(ctx, student.uid).then((row) => { + expect(row, "the row still exists").to.not.be.null; + expect(row.freigabedatum, "a rejected Freigabe must not stamp freigabedatum").to.be.null; + }); + }); + + it("rejects a Freigabe without the required parameters", () => { + cy.request({ + method: "POST", + url: "/index.ci.php/api/frontend/v1/Noten/saveStudentenNoten", + body: { lv_id: ctx.lvId, sem_kurzbz: ctx.semKurzbz }, + auth: { + username: Cypress.env("adminusername"), + password: Cypress.env("adminpassword"), + }, + failOnStatusCode: false, + }).then((response) => { + expect(response.status, "missing password/noten").to.eq(500); + expect(response.body).to.have.nested.property("meta.status", "error"); + }); + }); + }); + + describe("happy path (sends mail - opt in via NOTEN_FREIGABE_ENABLED)", () => { + beforeEach(function () { + if (!freigabeEnabled()) { + cy.log( + "Skipped: saveStudentenNoten sends the Notenfreigabe email on success. " + + "Set NOTEN_FREIGABE_ENABLED=true only on an environment where that mail is harmless.", + ); + this.skip(); + } + requireDbReset(); + }); + + it("stamps freigabedatum on a changed note", () => { + const student = ctx.students[0]; + + resetNotenState(ctx); + seedBaseline(ctx, student.uid, { + note: ctx.gradeNotes[0], + freigegeben: false, + benotungsdatum: baselineBenotungsdatum(ctx), + }); + + notenApi + .saveStudentenNoten( + freigabePassword(), + [notenPayload(student)], + ctx.lvId, + ctx.semKurzbz, + ) + .then((response) => { + const data = expectNotenSuccess(response, "saveStudentenNoten"); + expect(data, "freigegebene rows are reported back").to.be.an("array"); + + const entry = data.find((r) => r.uid === student.uid); + expect(entry, `${student.uid} must be reported as freigegeben`).to.exist; + expect(entry.freigabedatum, "freigabedatum is stamped").to.exist; + }); + + readState(ctx).then((data) => { + const grades = lvNoteOf(data, student.uid); + expect(grades.freigabedatum, "the note is now freigegeben").to.exist; + }); + }); + + it("only touches rows whose benotungsdatum is newer than their freigabedatum", () => { + const changed = ctx.students[0]; + const alreadyReleased = ctx.students[1]; + + resetNotenState(ctx); + + // changed: benotungsdatum after freigabedatum -> must be re-freigegeben + seedBaseline(ctx, changed.uid, { + note: ctx.gradeNotes[0], + freigegeben: true, + freigabedatum: `${ctx.semKurzbz.slice(2, 6)}-01-05 08:00:00`, + benotungsdatum: `${ctx.semKurzbz.slice(2, 6)}-01-10 08:00:00`, + }); + + // already freigegeben and untouched since -> must be left alone + seedBaseline(ctx, alreadyReleased.uid, { + note: ctx.gradeNotes[0], + freigegeben: true, + freigabedatum: `${ctx.semKurzbz.slice(2, 6)}-01-10 08:00:00`, + benotungsdatum: `${ctx.semKurzbz.slice(2, 6)}-01-10 08:00:00`, + }); + + notenApi + .saveStudentenNoten( + freigabePassword(), + [notenPayload(changed), notenPayload(alreadyReleased)], + ctx.lvId, + ctx.semKurzbz, + ) + .then((response) => { + const data = expectNotenSuccess(response, "selective Freigabe"); + const uids = data.map((r) => r.uid); + + expect(uids, "the changed note is freigegeben").to.include(changed.uid); + expect( + uids, + "an unchanged, already freigegebene note must not be freigegeben again", + ).to.not.include(alreadyReleased.uid); + }); + + readLvGesamtnote(ctx, alreadyReleased.uid).then((row) => { + expect( + new Date(row.freigabedatum).toISOString().slice(0, 10), + "the untouched row keeps its original freigabedatum", + ).to.eq(`${ctx.semKurzbz.slice(2, 6)}-01-10`); + }); + }); + }); +}); diff --git a/tests/cypress/e2e/specs/api/noten/noten.frist.cy.js b/tests/cypress/e2e/specs/api/noten/noten.frist.cy.js new file mode 100644 index 000000000..29b7f7312 --- /dev/null +++ b/tests/cypress/e2e/specs/api/noten/noten.frist.cy.js @@ -0,0 +1,128 @@ +/** + * Prüfungsordnung §1 - Noteneintragungsfrist (P0, cases 8-9). + * + * enforceNoteneintragungsfrist runs after assertLvAccess but before any write, so a request naming + * a past-deadline semester is rejected without touching a row. The message carries the derived + * deadline, which doubles as a check of computeNoteneintragungsfrist + * (SS -> 15.11.yyyy, WS -> 15.05.yyyy+1). + */ + +import { notenApi } from "../../../../support/api/notenApi"; +import { expectNotenError, expectNotenSuccess } from "../../../../support/helpers/notenErrors"; +import { + attemptDate, + expectedFristString, + fristHasPassed, + loadNotenContext, + requireDbReset, + semesterWithPastFrist, +} from "../../../../support/helpers/notenTestData"; +import { addPruefung, givenBaseline } from "../../../../support/helpers/notenScenario"; + +// Deadlines computeNoteneintragungsfrist derives: SS -> 15.11.yyyy, WS -> 15.05.yyyy+1. +const fristConfig = () => ({ ss: { month: 11, day: 15 }, ws: { month: 5, day: 15 } }); + +describe("Noten API - Noteneintragungsfrist (Prüfungsordnung §1)", () => { + let ctx; + let enforced; + + before(() => { + loadNotenContext().then((context) => { + ctx = context; + enforced = Boolean(ctx.cisConfig.CIS_GESAMTNOTE_NOTENEINTRAGUNGSFRIST); + cy.log(`CIS_GESAMTNOTE_NOTENEINTRAGUNGSFRIST = ${enforced}`); + }); + }); + + describe("deadline has passed", () => { + // A rejected request writes nothing, so these need no DB reset. + [ + { type: "SS", label: "Sommersemester (deadline in the same year)" }, + { type: "WS", label: "Wintersemester (deadline in the following year)" }, + ].forEach(({ type, label }) => { + it(`rejects grade entry for a ${label}`, function () { + // no flag, no deadline - skip rather than pretend it passed + if (!enforced) this.skip(); + + const sem = semesterWithPastFrist(type); + const cfg = fristConfig(); + const expectedDeadline = expectedFristString(sem, cfg.ss, cfg.ws); + + expect(fristHasPassed(sem), `${sem} deadline is in the past`).to.be.true; + + notenApi + .saveStudentPruefung({ + student_uid: ctx.students[0].uid, + note: ctx.gradeNotes[0], + punkte: null, + datum: attemptDate(ctx, 1), + lva_id: ctx.lvId, + lehreinheit_id: ctx.students[0].lehreinheit_id, + sem_kurzbz: sem, + typ: "Termin2", + pruefung_id: null, + }) + .then((response) => { + expectNotenError(response, "noteneintragungsfristVorbei"); + + const message = response.body.errors.map((e) => e.message).join(" | "); + expect( + message, + `the ${type} deadline the server derived for ${sem}`, + ).to.include(expectedDeadline); + }); + }); + }); + + it("rejects saveNotenvorschlag past the deadline as well", function () { + if (!enforced) this.skip(); + + const sem = semesterWithPastFrist("SS"); + + notenApi + .saveNotenvorschlag(ctx.lvId, sem, ctx.students[0].uid, ctx.gradeNotes[0]) + .then((response) => { + expectNotenError(response, "noteneintragungsfristVorbei"); + }); + }); + }); + + describe("deadline is still ahead", () => { + before(() => { + requireDbReset(); + }); + + it("accepts grade entry in a semester whose deadline has not passed", function () { + if (enforced && fristHasPassed(ctx.semKurzbz)) { + // writing is impossible here; the whole mutating suite would be blocked too + throw new Error( + `Noteneintragungsfrist enforcement is ON and the deadline for the test semester ` + + `${ctx.semKurzbz} (${expectedFristString(ctx.semKurzbz)}) has already passed, so no ` + + `grade can be entered. Point NOTEN_SEM at a semester whose deadline is still ahead.`, + ); + } + + const student = ctx.students[0]; + + givenBaseline(ctx, student); + + addPruefung(ctx, student, { + note: ctx.gradeNotes[0], + datum: attemptDate(ctx, 1), + typ: "Termin2", + }).then((response) => { + expectNotenSuccess(response, `grade entry within the deadline for ${ctx.semKurzbz}`); + }); + }); + }); + + describe("deadline computation", () => { + it("derives the documented SS and WS deadlines", () => { + const cfg = fristConfig(); + + // pins the mapping, independent of the clock + expect(expectedFristString("SS2025", cfg.ss, cfg.ws)).to.eq("15.11.2025"); + expect(expectedFristString("WS2025", cfg.ss, cfg.ws)).to.eq("15.05.2026"); + }); + }); +}); diff --git a/tests/cypress/e2e/specs/api/noten/noten.notenschluessel.cy.js b/tests/cypress/e2e/specs/api/noten/noten.notenschluessel.cy.js new file mode 100644 index 000000000..a0e4ad871 --- /dev/null +++ b/tests/cypress/e2e/specs/api/noten/noten.notenschluessel.cy.js @@ -0,0 +1,135 @@ +/** + * Notenschlüssel - points to grade (P0, case 10). Read-only. + * + * getNote() picks the aufteilung row with the highest punkte <= input, and returns null when the LV + * has no Notenschlüssel. Rather than re-implementing that lookup, this sweeps the range and asserts + * the properties a correct scale must have: total, monotonic, exact at each boundary. + */ + +import { notenApi } from "../../../../support/api/notenApi"; +import { expectNotenSuccess } from "../../../../support/helpers/notenErrors"; +import { loadNotenContext } from "../../../../support/helpers/notenTestData"; + +const MAX_PUNKTE = 100; + +describe("Noten API - Notenschlüssel (getNoteByPunkte)", () => { + let ctx; + let lvId; + + before(() => { + loadNotenContext().then((context) => { + ctx = context; + lvId = context.lvId; + }); + }); + + /** Sweeps 0..MAX_PUNKTE and returns [{ punkte, note }] as the server reports it. */ + const sweepScale = () => { + const observed = []; + + for (let punkte = 0; punkte <= MAX_PUNKTE; punkte += 1) { + notenApi.getNoteByPunkte(punkte, lvId, ctx.semKurzbz).then((response) => { + const note = expectNotenSuccess(response, `getNoteByPunkte(${punkte})`); + observed.push({ punkte, note }); + }); + } + + return cy.then(() => observed); + }; + + it("maps every point value through a monotonic, exact grade scale", () => { + sweepScale().then((observed) => { + const graded = observed.filter((entry) => entry.note !== null && entry.note !== undefined); + + expect( + graded.length, + `LV ${lvId} in ${ctx.semKurzbz} has no Notenschlüssel - getNoteByPunkte returned null ` + + `for every value in 0..${MAX_PUNKTE}. Pin NOTEN_LV_ID to an LV that has one.`, + ).to.be.greaterThan(0); + + // (4) below the lowest threshold the model answers null, and that region must be a + // contiguous prefix - a null appearing *after* a graded value would mean a gap in the scale + const firstGradedIndex = observed.findIndex((e) => e.note !== null && e.note !== undefined); + observed.slice(firstGradedIndex).forEach((entry) => { + expect( + entry.note, + `points ${entry.punkte} fall inside the graded range and must map to a grade`, + ).to.not.be.oneOf([null, undefined]); + }); + + // (2) monotonic: grade PKs run 1 (best) .. 5 (worst), so the number must never increase + for (let i = 1; i < graded.length; i += 1) { + expect( + Number(graded[i].note), + `grade at ${graded[i].punkte} points must not be worse than at ${graded[i - 1].punkte}`, + ).to.be.at.most(Number(graded[i - 1].note)); + } + + // (3) exact boundaries: report each step so a shifted threshold is visible in the output + const boundaries = []; + for (let i = 1; i < graded.length; i += 1) { + if (Number(graded[i].note) !== Number(graded[i - 1].note)) { + boundaries.push({ + punkte: graded[i].punkte, + note: graded[i].note, + previousNote: graded[i - 1].note, + }); + } + } + + cy.log( + `Notenschlüssel of LV ${lvId}: ${boundaries + .map((b) => `>=${b.punkte} -> ${b.note}`) + .join(", ")}`, + ); + + expect( + boundaries.length, + "a usable Notenschlüssel must have at least one grade boundary in the swept range", + ).to.be.greaterThan(0); + + // re-assert each boundary individually so a failure names the exact threshold + boundaries.forEach((boundary) => { + notenApi.getNoteByPunkte(boundary.punkte, lvId, ctx.semKurzbz).then((response) => { + const note = expectNotenSuccess(response, `boundary ${boundary.punkte}`); + expect(Number(note), `grade exactly at the ${boundary.punkte}-point boundary`).to.eq( + Number(boundary.note), + ); + }); + + notenApi.getNoteByPunkte(boundary.punkte - 1, lvId, ctx.semKurzbz).then((response) => { + const note = expectNotenSuccess(response, `just below ${boundary.punkte}`); + expect( + Number(note), + `one point below the ${boundary.punkte}-point boundary must still be the worse grade`, + ).to.eq(Number(boundary.previousNote)); + }); + }); + }); + }); + + it("reports no grade for an LV without a Notenschlüssel", () => { + // A non-existent LV id can have no Notenschlüssel assigned, so the model's success(null) + // contract is observable without depending on a particular fixture. + notenApi.getNoteByPunkte(50, 0, ctx.semKurzbz).then((response) => { + const note = expectNotenSuccess(response, "getNoteByPunkte for an LV without a schluessel"); + expect(note, "no Notenschlüssel -> no grade").to.be.oneOf([null, undefined]); + }); + }); + + it("rejects a request without the required parameters", () => { + cy.request({ + method: "POST", + url: "/index.ci.php/api/frontend/v1/Noten/getNoteByPunkte", + body: { punkte: 50 }, + auth: { + username: Cypress.env("adminusername"), + password: Cypress.env("adminpassword"), + }, + failOnStatusCode: false, + }).then((response) => { + expect(response.status, "missing lv_id/sem_kurzbz").to.eq(500); + expect(response.body).to.have.nested.property("meta.status", "error"); + }); + }); +}); diff --git a/tests/cypress/e2e/specs/api/noten/noten.notenvorschlag.cy.js b/tests/cypress/e2e/specs/api/noten/noten.notenvorschlag.cy.js new file mode 100644 index 000000000..2625b720d --- /dev/null +++ b/tests/cypress/e2e/specs/api/noten/noten.notenvorschlag.cy.js @@ -0,0 +1,170 @@ +/** + * Notenvorschlag - LV note write and resulting state (P1, case 11). + * + * State comes from two timestamps (Benotungstool.js::checkFreigabe): no freigabedatum -> offen, + * benotungsdatum > freigabedatum -> changed, else freigegeben. The "changed" case is produced by + * seeding an already-freigegebene baseline, which keeps this spec free of the LDAP password check + * and the Freigabe mail. + * + * Caveat: getLvGesamtNoten() (behind getStudentenNoten) filters `freigabedatum < NOW()`, and + * NULL < NOW() is NULL - so offene notes are dropped, while saveNotenvorschlag writes through the + * unfiltered getLvGesamtNoteVorschlag(). The tests below separate write from read for that reason. + */ + +import { notenApi } from "../../../../support/api/notenApi"; +import { expectNotenSuccess } from "../../../../support/helpers/notenErrors"; +import { + baselineBenotungsdatum, + loadNotenContext, + readLvGesamtnote, + requireDbReset, + resetNotenState, + seedBaseline, +} from "../../../../support/helpers/notenTestData"; +import { lvNoteOf, readState } from "../../../../support/helpers/notenScenario"; + +describe("Noten API - Notenvorschlag", () => { + let ctx; + + before(() => { + requireDbReset(); + loadNotenContext().then((context) => { + ctx = context; + }); + }); + + const readRow = (uid) => readLvGesamtnote(ctx, uid); + + describe("saving a Notenvorschlag on a student without a grade", () => { + it("returns the stored note with a benotungsdatum and no freigabedatum", () => { + const student = ctx.students[0]; + + resetNotenState(ctx); + + notenApi + .saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.gradeNotes[0]) + .then((response) => { + const data = expectNotenSuccess(response, "saveNotenvorschlag"); + expect(data, "saveNotenvorschlag returns [ lvgesamtnote ]").to.be.an("array").and.not + .be.empty; + + const row = data[0]; + expect(String(row.note), "stored note").to.eq(String(ctx.gradeNotes[0])); + expect(row.benotungsdatum, "benotungsdatum is stamped on save").to.exist; + expect(row.freigabedatum, "a fresh Notenvorschlag is not freigegeben").to.be.oneOf([ + null, + undefined, + "", + ]); + }); + }); + + it("persists the row with freigabedatum NULL (state: offen)", () => { + const student = ctx.students[0]; + + resetNotenState(ctx); + notenApi.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.gradeNotes[0]); + + readRow(student.uid).then((row) => { + expect(row, "the lvgesamtnote row must exist in the database").to.not.be.null; + expect(String(row.note)).to.eq(String(ctx.gradeNotes[0])); + expect(row.benotungsdatum, "benotungsdatum").to.exist; + expect(row.freigabedatum, "freigabedatum stays NULL until Freigabe").to.be.null; + }); + }); + + // EXPECTED TO FAIL while getLvGesamtNoten filters `freigabedatum < NOW()`: the row exists + // (test above proves it) but the read model drops it until the note is freigegeben. + it("reports the offen note back through getStudentenNoten", () => { + const student = ctx.students[0]; + + resetNotenState(ctx); + notenApi.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.gradeNotes[0]); + + readState(ctx).then((data) => { + const grades = lvNoteOf(data, student.uid); + expect(grades, `grades entry for ${student.uid}`).to.exist; + expect( + String(grades.note_lv), + "a saved (not yet freigegebene) Notenvorschlag must be readable again - if this is " + + "null, getLvGesamtNoten's `freigabedatum < NOW()` filter dropped the row", + ).to.eq(String(ctx.gradeNotes[0])); + expect(grades.freigabedatum, "still offen").to.be.oneOf([null, undefined, ""]); + }); + }); + }); + + describe("re-grading an already freigegebene note", () => { + it("moves the state from freigegeben to changed", () => { + const student = ctx.students[1]; + + resetNotenState(ctx); + seedBaseline(ctx, student.uid, { + note: ctx.gradeNotes[0], + freigegeben: true, + benotungsdatum: baselineBenotungsdatum(ctx), + }); + + // baseline: benotungsdatum == freigabedatum -> freigegeben, not changed + readState(ctx).then((data) => { + const grades = lvNoteOf(data, student.uid); + expect(grades.note_lv, "seeded freigegebene note is visible").to.exist; + expect( + new Date(grades.benotungsdatum) > new Date(grades.freigabedatum), + "baseline must not already look changed", + ).to.be.false; + }); + + notenApi + .saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.gradeNotes[1]) + .then((response) => { + expectNotenSuccess(response, "re-grade after Freigabe"); + }); + + readState(ctx).then((data) => { + const grades = lvNoteOf(data, student.uid); + + expect(String(grades.note_lv), "the new grade is stored").to.eq( + String(ctx.gradeNotes[1]), + ); + expect(grades.freigabedatum, "the old Freigabe timestamp is kept").to.exist; + expect( + new Date(grades.benotungsdatum) > new Date(grades.freigabedatum), + "benotungsdatum must now be newer than freigabedatum (state: changed)", + ).to.be.true; + }); + }); + + it("overwrites the grade rather than adding a second row", () => { + const student = ctx.students[1]; + + resetNotenState(ctx); + seedBaseline(ctx, student.uid, { note: ctx.gradeNotes[0], freigegeben: true }); + + notenApi.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.gradeNotes[1]); + + readRow(student.uid).then((row) => { + expect(row, "the single lvgesamtnote row").to.not.be.null; + expect(String(row.note)).to.eq(String(ctx.gradeNotes[1])); + }); + }); + }); + + describe("parameter validation", () => { + it("rejects a call without student_uid", () => { + cy.request({ + method: "POST", + url: "/index.ci.php/api/frontend/v1/Noten/saveNotenvorschlag", + body: { lv_id: ctx.lvId, sem_kurzbz: ctx.semKurzbz, note: ctx.gradeNotes[0] }, + auth: { + username: Cypress.env("adminusername"), + password: Cypress.env("adminpassword"), + }, + failOnStatusCode: false, + }).then((response) => { + expect(response.status, "missing student_uid").to.eq(500); + expect(response.body).to.have.nested.property("meta.status", "error"); + }); + }); + }); +}); diff --git a/tests/cypress/e2e/specs/api/noten/noten.pruefungsantritte.cy.js b/tests/cypress/e2e/specs/api/noten/noten.pruefungsantritte.cy.js new file mode 100644 index 000000000..b16d34b96 --- /dev/null +++ b/tests/cypress/e2e/specs/api/noten/noten.pruefungsantritte.cy.js @@ -0,0 +1,335 @@ +/** + * Prüfungsordnung §1 - Prüfungsantritte (P0, cases 1-7). + * + * Rules fire in order A (max Antritte) -> B (chronology) -> C (occurrence limit), so each test + * builds a state where exactly one can fire; otherwise an earlier rule masks the one under test. + * + * Bookkeeping: the first Termin2 add also auto-creates a Termin1 row with the original LV note, so + * one call produces two attempts. A later Termin2 add updates the existing non-excused Termin2 + * instead of inserting. "entschuldigt" and "Noch nicht eingetragen" never count towards the cap. + */ + +import { expectNotenError, expectNotenSuccess } from "../../../../support/helpers/notenErrors"; +import { + attemptDate, + baselineDate, + loadNotenContext, + requireDbReset, + shiftDate, +} from "../../../../support/helpers/notenTestData"; +import { + addPruefung, + attemptsOfTyp, + editPruefung, + enabledRetakeTypes, + givenBaseline, + readState, +} from "../../../../support/helpers/notenScenario"; + +describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => { + let ctx; + + before(() => { + requireDbReset(); + loadNotenContext().then((context) => { + ctx = context; + + cy.log( + `LV ${ctx.lvId} / ${ctx.semKurzbz} | maxAntritte=${ctx.maxAntritte} | ` + + `retakes=${enabledRetakeTypes(ctx).join(",") || "none"} | ` + + `entschuldigt=${ctx.notes.entschuldigt}`, + ); + + expect( + enabledRetakeTypes(ctx).length, + "needs TERMIN2 or TERMIN3 enabled, else saveStudentPruefung only answers wrongPruefungType", + ).to.be.greaterThan(0); + }); + }); + + // one student per test, so a leaked row cannot reach the next scenario + const studentFor = (index) => ctx.students[index % ctx.students.length]; + + describe("Rule A - maximum number of Prüfungsantritte", () => { + it("rejects an attempt once the configured maximum is reached", () => { + const student = studentFor(0); + const retakes = enabledRetakeTypes(ctx); + + givenBaseline(ctx, student); + + // Antritt 1 is the seeded LV note; each retake type adds one row -> count reaches maxAntritte + retakes.forEach((typ, i) => { + addPruefung(ctx, student, { + note: ctx.gradeNotes[0], + datum: attemptDate(ctx, i + 1), + typ, + }).then((response) => { + expectNotenSuccess(response, `add ${typ} #${i + 1} (still below the cap)`); + }); + }); + + // one more attempt of the last enabled type -> the cap must now bite + const lastTyp = retakes[retakes.length - 1]; + addPruefung(ctx, student, { + note: ctx.gradeNotes[0], + datum: attemptDate(ctx, retakes.length + 1), + typ: lastTyp, + }).then((response) => { + expectNotenError(response, "maxAntritteReached"); + }); + + // and nothing was written for the rejected attempt + readState(ctx).then((data) => { + const attempts = attemptsOfTyp(data, student.uid, lastTyp); + expect( + attempts.map((p) => String(p.datum).slice(0, 10)), + `no ${lastTyp} may carry the rejected date`, + ).to.not.include(attemptDate(ctx, retakes.length + 1)); + }); + }); + }); + + describe("Rule B - attempts are taken in chronological order", () => { + // "Noch nicht eingetragen" occupies a date without counting towards the cap, so Rule A + // cannot fire first and mask Rule B. + const givenOpenAttemptOn = (student, datum) => { + givenBaseline(ctx, student); + addPruefung(ctx, student, { + note: ctx.notes.nochNichtEingetragen, + datum, + typ: "Termin2", + }).then((response) => { + expectNotenSuccess(response, "seed an open (uncounted) Termin2"); + }); + }; + + it("rejects a new attempt dated on the same day as an existing one", () => { + const student = studentFor(1); + const existing = attemptDate(ctx, 2); + + givenOpenAttemptOn(student, existing); + + addPruefung(ctx, student, { + note: ctx.gradeNotes[0], + datum: existing, + typ: "Termin2", + }).then((response) => { + expectNotenError(response, "pruefungDatumBeforeExisting"); + }); + }); + + it("rejects a new attempt dated before an existing one", () => { + const student = studentFor(1); + const existing = attemptDate(ctx, 2); + + givenOpenAttemptOn(student, existing); + + addPruefung(ctx, student, { + note: ctx.gradeNotes[0], + datum: attemptDate(ctx, 1), + typ: "Termin2", + }).then((response) => { + expectNotenError(response, "pruefungDatumBeforeExisting"); + }); + }); + + it("accepts a new attempt dated after every existing one", () => { + const student = studentFor(1); + + givenOpenAttemptOn(student, attemptDate(ctx, 2)); + + addPruefung(ctx, student, { + note: ctx.gradeNotes[0], + datum: attemptDate(ctx, 3), + typ: "Termin2", + }).then((response) => { + expectNotenSuccess(response, "attempt strictly after the existing one"); + }); + }); + }); + + describe("Rule C - 'entschuldigt' may be assigned only once", () => { + it("rejects a second entschuldigt attempt", () => { + const student = studentFor(2); + + givenBaseline(ctx, student); + + addPruefung(ctx, student, { + note: ctx.notes.entschuldigt, + datum: attemptDate(ctx, 1), + typ: "Termin2", + }).then((response) => { + expectNotenSuccess(response, "first entschuldigt attempt"); + }); + + addPruefung(ctx, student, { + note: ctx.notes.entschuldigt, + datum: attemptDate(ctx, 2), + typ: "Termin2", + }).then((response) => { + expectNotenError(response, "noteOccuranceLimitReached"); + }); + + readState(ctx).then((data) => { + const excused = attemptsOfTyp(data, student.uid, "Termin2").filter( + (p) => String(p.note) === String(ctx.notes.entschuldigt), + ); + expect(excused, "exactly one excused Termin2 may exist").to.have.length(1); + }); + }); + }); + + describe("entschuldigt does not consume an attempt", () => { + it("still accepts a real grade after an excused attempt", () => { + const student = studentFor(0); + + givenBaseline(ctx, student); + + addPruefung(ctx, student, { + note: ctx.notes.entschuldigt, + datum: attemptDate(ctx, 1), + typ: "Termin2", + }).then((response) => { + expectNotenSuccess(response, "excused attempt"); + }); + + // excused must not count, so this real grade is still within the cap + addPruefung(ctx, student, { + note: ctx.gradeNotes[0], + datum: attemptDate(ctx, 2), + typ: "Termin2", + }).then((response) => { + expectNotenSuccess( + response, + "a real grade after an excused attempt (excused must not count towards the cap)", + ); + }); + }); + + it("preserves the excused entry as its own dated row instead of overwriting it", () => { + const student = studentFor(0); + + givenBaseline(ctx, student); + + addPruefung(ctx, student, { + note: ctx.notes.entschuldigt, + datum: attemptDate(ctx, 1), + typ: "Termin2", + }); + addPruefung(ctx, student, { + note: ctx.gradeNotes[0], + datum: attemptDate(ctx, 2), + typ: "Termin2", + }); + + readState(ctx).then((data) => { + const termin2 = attemptsOfTyp(data, student.uid, "Termin2"); + + expect(termin2, "the excused row and the new row must coexist").to.have.length(2); + + expect(String(termin2[0].note), "older row keeps the excused grade").to.eq( + String(ctx.notes.entschuldigt), + ); + expect(String(termin2[0].datum).slice(0, 10)).to.eq(attemptDate(ctx, 1)); + + expect(String(termin2[1].note), "newer row carries the real grade").to.eq( + String(ctx.gradeNotes[0]), + ); + expect(String(termin2[1].datum).slice(0, 10)).to.eq(attemptDate(ctx, 2)); + }); + }); + }); + + describe("edit guards", () => { + /** + * Builds: Termin1 (baseline date) < excused Termin2 (attempt 1) < real Termin2 (attempt 2) + * and yields the excused row, which now has both an earlier and a later neighbour. + */ + const givenThreeAttempts = (student) => { + givenBaseline(ctx, student); + + addPruefung(ctx, student, { + note: ctx.notes.entschuldigt, + datum: attemptDate(ctx, 1), + typ: "Termin2", + }); + addPruefung(ctx, student, { + note: ctx.gradeNotes[0], + datum: attemptDate(ctx, 2), + typ: "Termin2", + }); + + return readState(ctx).then((data) => { + const excused = attemptsOfTyp(data, student.uid, "Termin2").find( + (p) => String(p.note) === String(ctx.notes.entschuldigt), + ); + expect(excused, "excused Termin2 to edit").to.exist; + return excused; + }); + }; + + it("rejects changing the grade once a later attempt exists", () => { + const student = studentFor(1); + + givenThreeAttempts(student).then((excused) => { + editPruefung(ctx, student, { + pruefungId: excused.pruefung_id, + note: ctx.gradeNotes[1], // different grade -> locked + datum: attemptDate(ctx, 1), + typ: "Termin2", + }).then((response) => { + expectNotenError(response, "pruefungNoteLocked"); + }); + }); + }); + + it("still allows a date-only correction between the neighbouring attempts", () => { + const student = studentFor(1); + + givenThreeAttempts(student).then((excused) => { + // same note -> the lock does not apply; date stays strictly inside (baseline, attempt2) + editPruefung(ctx, student, { + pruefungId: excused.pruefung_id, + note: excused.note, + datum: shiftDate(attemptDate(ctx, 1), 3), + typ: "Termin2", + }).then((response) => { + // NOTE: savePruefungstermin() does not receive pruefung_id - it re-derives the target + // as "the first non-excused Termin2" (Noten.php:943-968). The validation under test + // is what this asserts; the resulting row placement is covered separately. + expectNotenSuccess(response, "date-only correction inside the neighbour bounds"); + }); + }); + }); + + it("rejects a date on or before the previous attempt", () => { + const student = studentFor(2); + + givenThreeAttempts(student).then((excused) => { + editPruefung(ctx, student, { + pruefungId: excused.pruefung_id, + note: excused.note, + datum: baselineDate(ctx), // == the Termin1 date -> not strictly after + typ: "Termin2", + }).then((response) => { + expectNotenError(response, "pruefungDatumOutOfRange"); + }); + }); + }); + + it("rejects a date on or after the following attempt", () => { + const student = studentFor(2); + + givenThreeAttempts(student).then((excused) => { + editPruefung(ctx, student, { + pruefungId: excused.pruefung_id, + note: excused.note, + datum: attemptDate(ctx, 2), // == the later Termin2 -> not strictly before + typ: "Termin2", + }).then((response) => { + expectNotenError(response, "pruefungDatumOutOfRange"); + }); + }); + }); + }); +}); diff --git a/tests/cypress/suites/noten.js b/tests/cypress/suites/noten.js new file mode 100644 index 000000000..4869d3371 --- /dev/null +++ b/tests/cypress/suites/noten.js @@ -0,0 +1,28 @@ +/** + * Wires the Gesamtnoteneingabe suite into cypress.config.js: its cy.task handlers plus the + * Cypress.env() values its specs read. + * + * The split is the point -- nothing NOTEN_* belongs in the config, and nothing generic belongs + * here. A second suite adds its own file next to this one and the config changes by one require. + */ + +const { registerNotenDbTasks } = require("../tasks/notenDb"); + +module.exports = { + registerTasks: registerNotenDbTasks, + + env: { + // Optional pins; semester and LV are discovered at runtime when unset. + NOTEN_SEM: process.env.NOTEN_SEM || null, + NOTEN_LV_ID: process.env.NOTEN_LV_ID || null, + + // saveStudentenNoten always sends the Notenfreigabe mail - opt in explicitly. + NOTEN_FREIGABE_ENABLED: process.env.NOTEN_FREIGABE_ENABLED || "false", + NOTEN_FREIGABE_PASSWORD: process.env.NOTEN_FREIGABE_PASSWORD || null, + + // Access-control specs need real per-role credentials: this API has no impersonation. + NOTEN_TEACHER_USER: process.env.NOTEN_TEACHER_USER || null, + NOTEN_TEACHER_PASSWORD: process.env.NOTEN_TEACHER_PASSWORD || null, + NOTEN_FOREIGN_LV_ID: process.env.NOTEN_FOREIGN_LV_ID || null, + }, +}; diff --git a/tests/cypress/support/api/notenApi.js b/tests/cypress/support/api/notenApi.js new file mode 100644 index 000000000..53eb17d7a --- /dev/null +++ b/tests/cypress/support/api/notenApi.js @@ -0,0 +1,78 @@ +/** + * Wrappers over the Benotungstool API. Mirrors public/js/api/factory/noten.js. + * + * GET sends factory params as query string, POST as JSON body (the controller reads + * getPostJSON()). Envelope: 200 {data, meta.status:'success'} / 500 {errors[], meta.status:'error'}. + */ + +const NOTEN_API = "/index.ci.php/api/frontend/v1/Noten"; + +const authOptions = () => ({ + username: Cypress.env("adminusername"), + password: Cypress.env("adminpassword"), +}); + +// Basic auth on every call rather than relying on the session cookie, so requests stay independent. +const apiGet = (path, qs) => + cy.request({ method: "GET", url: `${NOTEN_API}/${path}`, qs, auth: authOptions(), failOnStatusCode: false }); + +const apiPost = (path, body) => + cy.request({ method: "POST", url: `${NOTEN_API}/${path}`, body, auth: authOptions(), failOnStatusCode: false }); + +export const notenApi = { + getCisConfig: () => apiGet("getCisConfig"), + + getNoten: () => apiGet("getNoten"), + + getBenotungstoolContext: (sem_kurzbz, lv_id = null) => + apiGet("getBenotungstoolContext", { sem_kurzbz, lv_id }), + + getLvForStudiengang: (studiengang_kz, sem_kurzbz) => + apiGet("getLvForStudiengang", { studiengang_kz, sem_kurzbz }), + + /** + * data is a POSITIONAL array (Noten.php:431): + * [studenten, pruefungen, DOMAIN, grades-by-uid, anwesenheiten] + */ + getStudentenNoten: (lv_id, sem_kurzbz) => apiGet("getStudentenNoten", { lv_id, sem_kurzbz }), + + /** data -> [lvgesamtnote] */ + saveNotenvorschlag: (lv_id, sem_kurzbz, student_uid, note, punkte = null) => + apiPost("saveNotenvorschlag", { lv_id, sem_kurzbz, student_uid, note, punkte }), + + /** data -> [savedPruefung, lvgesamtnote, extraPruefung(auto-created Termin1)] */ + saveStudentPruefung: ({ + student_uid, note, punkte = null, datum, lva_id, lehreinheit_id, sem_kurzbz, typ, pruefung_id = null, + }) => + apiPost("saveStudentPruefung", { + student_uid, note, punkte, datum, lva_id, lehreinheit_id, sem_kurzbz, typ, pruefung_id, + }), + + /** LDAP-password gated. data -> [{uid, freigabedatum, benotungsdatum}] */ + saveStudentenNoten: (password, noten, lv_id, sem_kurzbz) => + apiPost("saveStudentenNoten", { password, noten, lv_id, sem_kurzbz }), + + getNoteByPunkte: (punkte, lv_id, sem_kurzbz) => + apiPost("getNoteByPunkte", { punkte, lv_id, sem_kurzbz }), + + // Bulk paths answer 200 and put the error string in data[uid] per row - they never fail the + // whole request (Noten.php:1600/:1656). + saveNotenvorschlagBulk: (lv_id, sem_kurzbz, noten) => + apiPost("saveNotenvorschlagBulk", { lv_id, sem_kurzbz, noten }), + + savePruefungenBulk: (lv_id, sem_kurzbz, pruefungen) => + apiPost("savePruefungenBulk", { lv_id, sem_kurzbz, pruefungen }), + + createPruefungen: (uids, datum, lva_id, sem_kurzbz) => + apiPost("createPruefungen", { uids, datum, lva_id, sem_kurzbz }), +}; + +// --- selectors over the getStudentenNoten payload --- + +export const pruefungenOf = (data, uid) => + (data[1] || []).filter((p) => p.student_uid === uid); + +export const pruefungenOfTyp = (data, uid, typ) => + pruefungenOf(data, uid).filter((p) => p.pruefungstyp_kurzbz === typ); + +export const gradesOf = (data, uid) => (data[3] || {})[uid]; diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js new file mode 100644 index 000000000..f0fce73f8 --- /dev/null +++ b/tests/cypress/support/commands.js @@ -0,0 +1,28 @@ +// Custom commands: https://on.cypress.io/custom-commands + +/** Visits a page and fails if any request returned >= 400. */ +Cypress.Commands.add("checkPageResources", (url) => { + const failedRequests = []; + + cy.intercept("**", (req) => { + req.on("response", (res) => { + if (res.statusCode >= 400) { + failedRequests.push({ url: req.url, status: res.statusCode }); + } + }); + }); + + cy.env(["adminusername", "adminpassword"]).then(({ adminusername, adminpassword }) => { + cy.visit(url, { auth: { username: adminusername, password: adminpassword } }); + }); + + cy.then(() => { + if (failedRequests.length > 0) { + const message = failedRequests.map((r) => `${r.status} - ${r.url}`).join("\n"); + throw new Error(`Broken resources detected:\n${message}`); + } + }); +}); + +// don't fail tests on uncaught app exceptions +Cypress.on("uncaught:exception", () => false); diff --git a/tests/cypress/support/e2e.js b/tests/cypress/support/e2e.js new file mode 100644 index 000000000..b36e2e7bd --- /dev/null +++ b/tests/cypress/support/e2e.js @@ -0,0 +1,2 @@ +// Loaded automatically before every spec. +import "./commands"; diff --git a/tests/cypress/support/helpers/notenErrors.js b/tests/cypress/support/helpers/notenErrors.js new file mode 100644 index 000000000..e06e36bb0 --- /dev/null +++ b/tests/cypress/support/helpers/notenErrors.js @@ -0,0 +1,96 @@ +/** + * Assertions for the Noten error envelope. + * + * These errors carry no errorCode, only a localized message (terminateWithError + p->t()), so we + * match against the de/en phrase templates from system/phrasesupdate.php. Both languages accepted. + * Adding an errorCode server-side would remove this coupling. + */ + +const PHRASES = { + maxAntritteReached: { + de: "Für Studierenden {0} sind bereits die maximal zulässigen Prüfungsantritte ({1}) vergeben. Es wurde keine Prüfung angelegt.", + en: "Student {0} has already used the maximum number of exam attempts ({1}). No exam was created.", + }, + pruefungDatumBeforeExisting: { + de: "Das Prüfungsdatum für Studierenden {0} darf nicht vor oder am selben Tag wie ein bereits bestehender Prüfungstermin liegen.", + en: "The exam date for student {0} must not be on or before an existing exam date.", + }, + noteOccuranceLimitReached: { + de: "Die gewählte Note darf für Studierenden {0} nicht so oft vergeben werden. Es wurde keine Prüfung angelegt.", + en: "The selected grade may not be assigned that many times for student {0}. No exam was created.", + }, + pruefungNoteLocked: { + de: "Die Note für Studierenden {0} kann nicht mehr geändert werden, da bereits eine spätere Prüfung oder eine Prüfung mit höherem Antritt existiert. Das Prüfungsdatum kann weiterhin angepasst werden.", + en: "The grade for student {0} can no longer be changed because a later exam or an exam of a higher attempt already exists. The exam date can still be adjusted.", + }, + pruefungDatumOutOfRange: { + de: "Das Prüfungsdatum für Studierenden {0} muss zwischen dem vorherigen und dem nachfolgenden Prüfungstermin liegen.", + en: "The exam date for student {0} must be between the previous and the following exam date.", + }, + noteneintragungsfristVorbei: { + de: "Die Noteneintragungsfrist ({0}) für dieses Studiensemester ist abgelaufen. Es können keine Noten oder Prüfungen mehr eingetragen werden.", + en: "The grade entry deadline ({0}) for this study semester has passed. Grades and exams can no longer be entered.", + }, + keineBerechtigungNoten: { + de: 'Keine Berechtigung, um in der Lehrveranstaltung "{0}" im Semester {1} Noten einzutragen.', + en: 'No permission to enter grades for the course "{0}" in semester {1}.', + }, + wrongPassword: { de: "Falsches Passwort", en: "Wrong password" }, +}; + +const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + +// Split on the {n} placeholders first, then escape - the other order breaks on the braces. +const toPattern = (text) => + new RegExp(text.split(/\{\d+\}/).map(escapeRegExp).join("[\\s\\S]*?")); + +const patternsFor = (key) => { + const phrase = PHRASES[key]; + if (!phrase) throw new Error(`Unknown phrase key "${key}". Known: ${Object.keys(PHRASES).join(", ")}`); + return [toPattern(phrase.de), toPattern(phrase.en)]; +}; + +export const messageMatchesPhrase = (message, key) => + typeof message === "string" && patternsFor(key).some((re) => re.test(message)); + +export const expectNotenError = (response, key) => { + expect(response.status, `HTTP status for expected "${key}"`).to.eq(500); + expect(response.body).to.have.nested.property("meta.status", "error"); + expect(response.body.errors).to.be.an("array").and.not.be.empty; + + const messages = response.body.errors.map((e) => e.message); + expect( + messages.some((m) => messageMatchesPhrase(m, key)), + `expected phrase "${key}", got: ${JSON.stringify(messages)}`, + ).to.be.true; +}; + +export const expectNotenSuccess = (response, context = "request") => { + // carry the server's message into the assertion, or a 500 says only "expected 200" + const errors = (response.body && response.body.errors || []).map((e) => e.message).join(" | "); + expect(response.status, `HTTP status for ${context}${errors ? ` -- ${errors}` : ""}`).to.eq(200); + expect(response.body, context).to.have.nested.property("meta.status", "success"); + return response.body.data; +}; + +/** Auth failures short-circuit before any error is added, so the body carries only meta. */ +export const expectAuthError = (response) => { + expect(response.status, "HTTP status for auth rejection").to.eq(401); + expect(response.body).to.have.nested.property("meta.status", "error"); +}; + +export const expectBulkRowError = (data, uid, key) => { + expect(data).to.have.property(uid); + expect( + messageMatchesPhrase(data[uid], key), + `expected row "${uid}" to carry "${key}", got: ${JSON.stringify(data[uid])}`, + ).to.be.true; +}; + +export const expectBulkRowAccepted = (data, uid) => { + expect(data).to.have.property(uid); + const value = data[uid]; + const isError = + typeof value === "string" && Object.keys(PHRASES).some((k) => messageMatchesPhrase(value, k)); + expect(isError, `expected row "${uid}" accepted, got: ${JSON.stringify(value)}`).to.be.false; +}; diff --git a/tests/cypress/support/helpers/notenReset.js b/tests/cypress/support/helpers/notenReset.js new file mode 100644 index 000000000..fa1299497 --- /dev/null +++ b/tests/cypress/support/helpers/notenReset.js @@ -0,0 +1,75 @@ +/** + * Fixture reset over a direct DB connection - the only mechanism. + * + * A gated HTTP reset endpoint existed briefly and was removed: it deleted grade data, and no + * gating makes that as safe as not having it. Consequence: the runner needs DB reachability, which + * from a workstation means the SSH tunnel (cypress/tasks/sshTunnel.js). + */ + +let available = null; + +/** Probes the DB once per run. Never throws; the caller decides how loudly to fail. */ +export const resolveResetStrategy = () => { + if (available) return cy.wrap(available, { log: false }); + + return cy.task("noten:db:available", null, { log: false }).then((status) => { + available = { strategy: status.available ? "db" : null, reason: status.reason }; + if (status.available) cy.log("Fixture reset via **direct database**"); + return cy.wrap(available, { log: false }); + }); +}; + +export const describeFailure = (state) => + ` ${state.reason}\n\n` + + " Needs a database connection. From a workstation: NOTEN_SSH_TUNNEL=true plus\n" + + " NOTEN_SSH_HOST / NOTEN_SSH_USER (and NOTEN_SSH_KEY, unless an agent holds the key)."; + +/** + * uid to stamp as mitarbeiter_uid / freigabevon_uid. + * Not Cypress.env("adminusername"): LDAP accepts "Demolektor1" but tbl_benutzer.uid is + * "demolektor1" and the FKs are case sensitive. + */ +let cachedAuthUid = null; + +const authUid = () => { + if (cachedAuthUid) return cy.wrap(cachedAuthUid, { log: false }); + + return cy + .request({ + method: "GET", + url: "/index.ci.php/api/frontend/v1/AuthInfo/getAuthUID", + auth: { username: Cypress.env("adminusername"), password: Cypress.env("adminpassword") }, + failOnStatusCode: false, + }) + .then((response) => { + const uid = response.body?.data?.uid; + expect(uid, "AuthInfo/getAuthUID uid").to.be.a("string").and.not.be.empty; + cachedAuthUid = uid; + return uid; + }); +}; + +export const performReset = (context, studentUids) => + cy.task("noten:db:reset", { + lvId: context.lvId, + semKurzbz: context.semKurzbz, + studentUids, + }); + +export const performSeed = (context, studentUid, payload) => + authUid().then((uid) => + cy.task("noten:db:seedLvGesamtnote", { + lvId: context.lvId, + semKurzbz: context.semKurzbz, + studentUid, + mitarbeiterUid: uid, + ...payload, + }), + ); + +export const performRead = (context, studentUid) => + cy.task("noten:db:readLvGesamtnote", { + lvId: context.lvId, + semKurzbz: context.semKurzbz, + studentUid, + }); diff --git a/tests/cypress/support/helpers/notenScenario.js b/tests/cypress/support/helpers/notenScenario.js new file mode 100644 index 000000000..b60441b04 --- /dev/null +++ b/tests/cypress/support/helpers/notenScenario.js @@ -0,0 +1,62 @@ +/** Building attempt histories and reading the resulting state. */ + +import { notenApi, pruefungenOfTyp, gradesOf } from "../api/notenApi"; +import { expectNotenSuccess } from "./notenErrors"; +import { resetNotenState, seedBaseline } from "./notenTestData"; + +/** Enabled in-tool retake types, in attempt order. maxAntritte == 1 + this list's length. */ +export const enabledRetakeTypes = (context) => { + const types = []; + if (context.cisConfig.CIS_GESAMTNOTE_PRUEFUNG_TERMIN2) types.push("Termin2"); + if (context.cisConfig.CIS_GESAMTNOTE_PRUEFUNG_TERMIN3) types.push("Termin3"); + return types; +}; + +/** + * Adds an attempt (no pruefung_id -> server runs validatePruefungAdd). + * punkte stays null: with CIS_GESAMTNOTE_PUNKTE on, a punkte >= 0 makes the controller re-derive + * the note from the Notenschlüssel and override the grade under test (Noten.php:692). + */ +export const addPruefung = (context, student, { note, datum, typ = "Termin2" }) => + notenApi.saveStudentPruefung({ + student_uid: student.uid, + note, + punkte: null, + datum, + lva_id: context.lvId, + lehreinheit_id: student.lehreinheit_id, + sem_kurzbz: context.semKurzbz, + typ, + pruefung_id: null, + }); + +/** Edits an attempt (pruefung_id set -> server runs validatePruefungEdit). */ +export const editPruefung = (context, student, { pruefungId, note, datum, typ = "Termin2" }) => + notenApi.saveStudentPruefung({ + student_uid: student.uid, + note, + punkte: null, + datum, + lva_id: context.lvId, + lehreinheit_id: student.lehreinheit_id, + sem_kurzbz: context.semKurzbz, + typ, + pruefung_id: pruefungId, + }); + +export const readState = (context) => + notenApi + .getStudentenNoten(context.lvId, context.semKurzbz) + .then((response) => expectNotenSuccess(response, "getStudentenNoten")); + +/** Attempts of one student and type, oldest first (the API returns datum DESC). */ +export const attemptsOfTyp = (data, uid, typ) => + [...pruefungenOfTyp(data, uid, typ)].sort((a, b) => + String(a.datum).slice(0, 10).localeCompare(String(b.datum).slice(0, 10)), + ); + +export const lvNoteOf = (data, uid) => gradesOf(data, uid); + +/** Clears the suite's rows and re-seeds the Antritt-1 baseline for one student. */ +export const givenBaseline = (context, student, options = {}) => + resetNotenState(context).then(() => seedBaseline(context, student.uid, options)); diff --git a/tests/cypress/support/helpers/notenTestData.js b/tests/cypress/support/helpers/notenTestData.js new file mode 100644 index 000000000..5d1bf281f --- /dev/null +++ b/tests/cypress/support/helpers/notenTestData.js @@ -0,0 +1,214 @@ +/** + * Runtime discovery + fixture helpers. + * + * No hardcoded fixture ids: the rule parameters (maxAntritte, the special note PKs) come from + * server-side config that is not in the repo, and Noten.php resolves the special notes by + * Bezeichnung. Everything is therefore read back from the API at runtime. + * Override via NOTEN_SEM / NOTEN_LV_ID. + */ + +import { notenApi } from "../api/notenApi"; +import { expectNotenSuccess } from "./notenErrors"; +import { describeFailure, performRead, performReset, performSeed, resolveResetStrategy } from "./notenReset"; + +const BEZ_ENTSCHULDIGT = "entschuldigt"; +const BEZ_NOCH_NICHT = "Noch nicht eingetragen"; + +let cachedContext = null; + +// --- dates --- + +export const pad2 = (n) => String(n).padStart(2, "0"); + +export const toDateString = (date) => + `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`; + +export const shiftDate = (dateString, days) => { + const [y, m, d] = dateString.split("-").map(Number); + const date = new Date(Date.UTC(y, m - 1, d)); + date.setUTCDate(date.getUTCDate() + days); + return `${date.getUTCFullYear()}-${pad2(date.getUTCMonth() + 1)}-${pad2(date.getUTCDate())}`; +}; + +/** Deadline the server derives (computeNoteneintragungsfrist): SS -> 15.11.yyyy, WS -> 15.05.yyyy+1. */ +export const expectedFristString = ( + semKurzbz, + ssConfig = { month: 11, day: 15 }, + wsConfig = { month: 5, day: 15 }, +) => { + const type = semKurzbz.slice(0, 2).toUpperCase(); + const year = Number(semKurzbz.slice(2, 6)); + const cfg = type === "SS" ? ssConfig : wsConfig; + return `${pad2(cfg.day)}.${pad2(cfg.month)}.${type === "SS" ? year : year + 1}`; +}; + +export const fristHasPassed = (semKurzbz) => { + const type = semKurzbz.slice(0, 2).toUpperCase(); + const year = Number(semKurzbz.slice(2, 6)); + if (!["SS", "WS"].includes(type) || !year) return false; + const deadline = + type === "SS" ? new Date(year, 10, 15, 23, 59, 59) : new Date(year + 1, 4, 15, 23, 59, 59); + return new Date() > deadline; +}; + +export const semesterWithPastFrist = (type = "SS") => { + const year = new Date().getFullYear(); + for (let y = year; y >= year - 6; y -= 1) { + if (fristHasPassed(`${type}${y}`)) return `${type}${y}`; + } + return `${type}${year - 6}`; +}; + +// --- discovery --- + +const resolveSemester = () => { + const configured = Cypress.env("NOTEN_SEM"); + if (configured) return cy.wrap(configured, { log: false }); + + return cy + .request({ + method: "GET", + url: "/index.ci.php/api/frontend/v1/organisation/Studiensemester/getAll", + qs: { order: "DESC" }, + auth: { username: Cypress.env("adminusername"), password: Cypress.env("adminpassword") }, + failOnStatusCode: false, + }) + .then((response) => { + const semesters = expectNotenSuccess(response, "Studiensemester/getAll"); + const today = toDateString(new Date()); + const active = semesters + .filter((s) => (s.start || "").slice(0, 10) && (s.start || "").slice(0, 10) <= today) + .sort((a, b) => (b.start || "").localeCompare(a.start || ""))[0]; + + expect(active, `an active studiensemester starting on or before ${today}`).to.exist; + return active.studiensemester_kurzbz; + }); +}; + +const resolveLehrveranstaltung = (semKurzbz) => { + const configured = Cypress.env("NOTEN_LV_ID"); + if (configured) return cy.wrap(Number(configured), { log: false }); + + return notenApi.getBenotungstoolContext(semKurzbz).then((response) => { + const lvs = expectNotenSuccess(response, "getBenotungstoolContext").lehrveranstaltungen || []; + expect( + lvs.length, + `No Lehrveranstaltungen for ${semKurzbz}: the logged-in user teaches nothing here ` + + "(admins usually don't). Set NOTEN_LV_ID.", + ).to.be.greaterThan(0); + return lvs[0].lehrveranstaltung_id; + }); +}; + +/** maxAntritte as the server computes it: 1 + each enabled retake type. */ +const computeMaxAntritte = (cisConfig) => + 1 + + (cisConfig.CIS_GESAMTNOTE_PRUEFUNG_TERMIN2 ? 1 : 0) + + (cisConfig.CIS_GESAMTNOTE_PRUEFUNG_TERMIN3 ? 1 : 0); + +/** -> { semKurzbz, lvId, cisConfig, maxAntritte, notes, gradeNotes, students, studentUids } */ +export const loadNotenContext = () => { + if (cachedContext) return cy.wrap(cachedContext, { log: false }); + + const context = {}; + + return resolveSemester() + .then((sem) => { + context.semKurzbz = sem; + return resolveLehrveranstaltung(sem); + }) + .then((lvId) => { + context.lvId = lvId; + return notenApi.getCisConfig(); + }) + .then((response) => { + context.cisConfig = expectNotenSuccess(response, "getCisConfig"); + context.maxAntritte = computeMaxAntritte(context.cisConfig); + return notenApi.getNoten(); + }) + .then((response) => { + const noten = expectNotenSuccess(response, "getNoten"); + const byBezeichnung = (bez) => noten.find((n) => n.bezeichnung === bez); + + const entschuldigt = byBezeichnung(BEZ_ENTSCHULDIGT); + const nochNicht = byBezeichnung(BEZ_NOCH_NICHT); + + expect(entschuldigt, `tbl_note needs bezeichnung "${BEZ_ENTSCHULDIGT}"`).to.exist; + expect(nochNicht, `tbl_note needs bezeichnung "${BEZ_NOCH_NICHT}"`).to.exist; + + // Restricted to the 1..5 scale: the wider lehre-Noten set contains note 0 ("Teilnote"), + // and saveStudentPruefung's `if($note=='')` guard rewrites a 0 to "Noch nicht eingetragen". + const specialPks = [entschuldigt.note, nochNicht.note]; + const usable = noten.filter((n) => n.lehre && !specialPks.includes(n.note)); + const ordinary = usable + .map((n) => n.note) + .filter((note) => Number(note) >= 1 && Number(note) <= 5) + .sort((a, b) => Number(a) - Number(b)); + + context.gradeNotes = + ordinary.length > 1 + ? ordinary + : usable.map((n) => n.note).filter((n) => Number(n) !== 0).sort((a, b) => Number(a) - Number(b)); + + expect(context.gradeNotes.length, "need two ordinary grades to vary one on edit").to.be.greaterThan(1); + + context.notes = { entschuldigt: entschuldigt.note, nochNichtEingetragen: nochNicht.note }; + + return notenApi.getStudentenNoten(context.lvId, context.semKurzbz); + }) + .then((response) => { + const data = expectNotenSuccess(response, `getStudentenNoten(${context.lvId})`); + const students = data[0] || []; + + expect(students.length, `LV ${context.lvId} needs >=3 enrolled students`).to.be.greaterThan(2); + students.forEach((s) => expect(s.lehreinheit_id, `lehreinheit_id of ${s.uid}`).to.exist); + + context.students = students; + context.studentUids = students.map((s) => s.uid); + + cachedContext = context; + return context; + }); +}; + +// --- fixture state --- + +export const resetNotenState = (context, studentUids) => + performReset(context, studentUids || context.studentUids); + +/** + * Seeds the Antritt-1 baseline. Defaults to freigegeben:true because validatePruefungAdd reads the + * implicit first Antritt through getLvGesamtNoten(), which filters `freigabedatum < NOW()` - an + * offene note is invisible there and would not count as Antritt 1. + */ +export const seedBaseline = (context, studentUid, options = {}) => + performSeed(context, studentUid, { + note: options.note !== undefined ? options.note : context.gradeNotes[0], + punkte: options.punkte !== undefined ? options.punkte : null, + benotungsdatum: options.benotungsdatum || baselineBenotungsdatum(context), + freigegeben: options.freigegeben !== undefined ? options.freigegeben : true, + freigabedatum: options.freigabedatum || null, + }); + +/** Raw row, bypassing the freigabedatum filter of getLvGesamtNoten. */ +export const readLvGesamtnote = (context, studentUid) => performRead(context, studentUid); + +/** Anchor date; attempt dates derive from it so ordering is known. */ +export const baselineBenotungsdatum = (context) => `${Number(context.semKurzbz.slice(2, 6))}-01-10 08:00:00`; + +export const baselineDate = (context) => baselineBenotungsdatum(context).slice(0, 10); + +/** attemptDate(ctx,1) < attemptDate(ctx,2) < ... , all after the baseline. */ +export const attemptDate = (context, index) => shiftDate(baselineDate(context), 30 * index); + +/** + * Hard failure rather than a skip: these are the regulatory rules, and a green run that never + * exercised them is worse than a red one. + */ +export const requireDbReset = () => + resolveResetStrategy().then((state) => { + expect( + state.strategy, + `Fixture reset unavailable.\n\n${describeFailure(state)}\n\n See tests/cypress/.env.example.\n`, + ).to.not.be.null; + }); diff --git a/tests/cypress/tasks/db.js b/tests/cypress/tasks/db.js new file mode 100644 index 000000000..b14337c94 --- /dev/null +++ b/tests/cypress/tasks/db.js @@ -0,0 +1,150 @@ +/** + * Postgres plumbing for cy.task fixture resets. Suite-agnostic. + * + * Connection, write guard, pooling and teardown live here. What to delete or seed belongs in a + * suite-specific task file -- see notenDb.js. + * + * Settings come from _DB_* and _SSH_*, where PREFIX is TEST_ENV_PREFIX. That + * indirection is the point: another suite sets its own prefix and reuses this file untouched. + */ + +const { ensureTunnel, closeTunnel, tunnelPort } = require("./sshTunnel"); + +const PREFIX = process.env.TEST_ENV_PREFIX || "TEST"; + +const env = (key) => process.env[`${PREFIX}_DB_${key}`]; +const sshEnv = (key) => process.env[`${PREFIX}_SSH_${key}`]; + +const dbConfigured = () => Boolean(env("HOST") && env("NAME") && env("USER")); +const writesAllowed = () => String(env("ALLOW_WRITES")).toLowerCase() === "true"; + +let pool = null; + +const getPool = () => { + if (pool) return pool; + + // lazy require so the suite loads without pg when no DB task is used + // eslint-disable-next-line global-require + const { Pool } = require("pg"); + + // with a tunnel up, bind to its local end - that is what gets us past pg_hba + const forwarded = tunnelPort(); + + pool = new Pool({ + host: forwarded ? "127.0.0.1" : env("HOST"), + port: forwarded || Number(env("PORT") || 5432), + database: env("NAME"), + user: env("USER"), + password: env("PASSWORD"), + max: 4, + ssl: String(env("SSL")).toLowerCase() === "true" ? { rejectUnauthorized: false } : undefined, + }); + return pool; +}; + +const assertWritable = () => { + if (!dbConfigured()) { + throw new Error(`db refused - set ${PREFIX}_DB_HOST / ${PREFIX}_DB_NAME / ${PREFIX}_DB_USER.`); + } + if (!writesAllowed()) { + throw new Error(`db refused - set ${PREFIX}_DB_ALLOW_WRITES=true (test databases only).`); + } +}; + +/** Runs fn with a pooled client and always releases it. */ +const withClient = async (fn) => { + const client = await getPool().connect(); + try { + return await fn(client); + } finally { + client.release(); + } +}; + +/** Same, in a transaction that rolls back on any throw. */ +const inTransaction = (fn) => + withClient(async (client) => { + await client.query("BEGIN"); + try { + const result = await fn(client); + await client.query("COMMIT"); + return result; + } catch (error) { + await client.query("ROLLBACK"); + throw error; + } + }); + +/** + * The pg_hba case is the one worth spelling out: it is server-side, so no local setting fixes it + * and the tunnel is the only workstation answer. + */ +const explainFailure = (error) => { + const msg = String(error && error.message ? error.message : error); + + if (/no pg_hba\.conf entry/i.test(msg)) { + const host = (msg.match(/for host "([^"]+)"/) || [])[1] || "this machine"; + return ( + `the server refused the connection from ${host} - pg_hba.conf has no rule allowing it. ` + + `Use the SSH tunnel (${PREFIX}_SSH_TUNNEL=true) or have an entry added. Details: ${msg}` + ); + } + if (/authentication failed/i.test(msg)) return `credentials rejected. Details: ${msg}`; + if (/database .* does not exist/i.test(msg)) return `${PREFIX}_DB_NAME does not exist. Details: ${msg}`; + if (/ECONNREFUSED/i.test(msg)) return `nothing listening on ${PREFIX}_DB_HOST:${PREFIX}_DB_PORT. Details: ${msg}`; + if (/ETIMEDOUT|EHOSTUNREACH|ENETUNREACH/i.test(msg)) return `host unreachable (firewall/VPN?). Details: ${msg}`; + return msg; +}; + +/** Opens the tunnel if configured, then proves the connection works. */ +const checkAvailability = async ({ requireWrites = true } = {}) => { + if (!dbConfigured()) return { available: false, reason: "not-configured" }; + if (requireWrites && !writesAllowed()) return { available: false, reason: "writes-disabled" }; + + if (String(sshEnv("TUNNEL")).toLowerCase() === "true") { + const tunnel = await ensureTunnel({ + sshHost: sshEnv("HOST"), + sshPort: Number(sshEnv("PORT") || 22), + sshUser: sshEnv("USER"), + keyPath: sshEnv("KEY"), + passphrase: sshEnv("PASSPHRASE"), + agent: sshEnv("AGENT"), + dbHost: env("HOST"), + dbPort: Number(env("PORT") || 5432), + }); + if (!tunnel.tunnelled) return { available: false, reason: `ssh-tunnel-failed: ${tunnel.reason}` }; + } + + try { + await withClient((client) => client.query("SELECT 1")); + return { available: true }; + } catch (error) { + // a failed pool is poisoned - drop it so a retry reconnects cleanly + if (pool) { + try { await pool.end(); } catch (e) { /* ignore */ } + pool = null; + } + return { available: false, reason: explainFailure(error) }; + } +}; + +const closeDb = async () => { + if (pool) { + await pool.end(); + pool = null; + } + await closeTunnel(); + return null; +}; + +module.exports = { + PREFIX, + env, + dbConfigured, + writesAllowed, + assertWritable, + withClient, + inTransaction, + checkAvailability, + closeDb, +}; diff --git a/tests/cypress/tasks/notenDb.js b/tests/cypress/tasks/notenDb.js new file mode 100644 index 000000000..022525292 --- /dev/null +++ b/tests/cypress/tasks/notenDb.js @@ -0,0 +1,129 @@ +/** + * cy.task implementations for the Gesamtnoteneingabe fixture. Connection and guards come from + * db.js; this file only knows which rows the §1 specs write and how to undo them. + * + * Needed because the Noten API has no delete endpoint, while every §1 test writes + * campus.tbl_lvgesamtnote and lehre.tbl_pruefung. Without a reset the suite is single-shot. + * + * Every statement is scoped by an explicit student_uid list plus the test LV and semester. + * No unscoped DELETE in this file. + */ + +const { + assertWritable, dbConfigured, withClient, inTransaction, checkAvailability, closeDb, +} = require("./db"); + +const REQUIRED_SCOPE = ["lvId", "semKurzbz", "studentUids"]; + +const assertScope = (scope) => { + if (!scope || typeof scope !== "object") throw new Error("noten:db scope object is required"); + + REQUIRED_SCOPE.forEach((key) => { + if (scope[key] === undefined || scope[key] === null || scope[key] === "") { + throw new Error(`noten:db refused - missing scope key "${key}"`); + } + }); + + if (!Array.isArray(scope.studentUids) || scope.studentUids.length === 0) { + throw new Error("noten:db refused - studentUids must be a non-empty array"); + } + if (scope.studentUids.some((uid) => typeof uid !== "string" || uid.trim() === "")) { + throw new Error("noten:db refused - studentUids must all be non-empty strings"); + } +}; + +const resetStudents = async (scope) => { + assertWritable(); + assertScope(scope); + + const { lvId, semKurzbz, studentUids } = scope; + + return inTransaction(async (client) => { + // pruefungen hang off lehreinheiten, so scope through the LV's lehreinheiten in this semester + const pruefungen = await client.query( + `DELETE FROM lehre.tbl_pruefung + WHERE student_uid = ANY($1::varchar[]) + AND lehreinheit_id IN ( + SELECT lehreinheit_id FROM lehre.tbl_lehreinheit + WHERE lehrveranstaltung_id = $2 AND studiensemester_kurzbz = $3)`, + [studentUids, lvId, semKurzbz], + ); + + const noten = await client.query( + `DELETE FROM campus.tbl_lvgesamtnote + WHERE student_uid = ANY($1::varchar[]) + AND lehrveranstaltung_id = $2 AND studiensemester_kurzbz = $3`, + [studentUids, lvId, semKurzbz], + ); + + return { deletedPruefungen: pruefungen.rowCount, deletedLvGesamtnoten: noten.rowCount }; + }); +}; + +/** + * Seeds the Antritt-1 baseline. `freigegeben` matters: getLvGesamtNoten() filters + * `freigabedatum < NOW()`, so an offene note is invisible to validatePruefungAdd. + */ +const seedLvGesamtnote = async (scope) => { + assertWritable(); + assertScope({ ...scope, studentUids: [scope.studentUid] }); + + const { + lvId, semKurzbz, studentUid, note, punkte = null, mitarbeiterUid, + benotungsdatum, freigegeben = false, freigabedatum = null, + } = scope; + + if (note === undefined || note === null) throw new Error("noten:db seed - note is required"); + if (!benotungsdatum) throw new Error("noten:db seed - benotungsdatum is required"); + if (!mitarbeiterUid) throw new Error("noten:db seed - mitarbeiterUid is required"); + + // default the Freigabe to the benotungsdatum: earlier would read as "changed" + const resolvedFreigabe = freigegeben ? freigabedatum || benotungsdatum : null; + + return withClient(async (client) => { + await client.query( + `DELETE FROM campus.tbl_lvgesamtnote + WHERE student_uid = $1 AND lehrveranstaltung_id = $2 AND studiensemester_kurzbz = $3`, + [studentUid, lvId, semKurzbz], + ); + + await client.query( + `INSERT INTO campus.tbl_lvgesamtnote + (student_uid, lehrveranstaltung_id, studiensemester_kurzbz, note, punkte, + mitarbeiter_uid, benotungsdatum, freigabedatum, freigabevon_uid, insertamum, insertvon) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW(), $6)`, + [ + studentUid, lvId, semKurzbz, note, punkte, mitarbeiterUid, + benotungsdatum, resolvedFreigabe, resolvedFreigabe ? mitarbeiterUid : null, + ], + ); + + return { studentUid, note, benotungsdatum, freigabedatum: resolvedFreigabe }; + }); +}; + +/** Raw row, without the `freigabedatum < NOW()` filter getLvGesamtNoten applies. */ +const readLvGesamtnote = async ({ lvId, semKurzbz, studentUid }) => { + if (!dbConfigured()) throw new Error("noten:db refused - database is not configured"); + + return withClient(async (client) => { + const res = await client.query( + `SELECT * FROM campus.tbl_lvgesamtnote + WHERE student_uid = $1 AND lehrveranstaltung_id = $2 AND studiensemester_kurzbz = $3`, + [studentUid, lvId, semKurzbz], + ); + return res.rows[0] || null; + }); +}; + +const registerNotenDbTasks = (on) => { + on("task", { + "noten:db:available": () => checkAvailability(), + "noten:db:reset": (scope) => resetStudents(scope), + "noten:db:seedLvGesamtnote": (scope) => seedLvGesamtnote(scope), + "noten:db:readLvGesamtnote": (scope) => readLvGesamtnote(scope), + "noten:db:close": () => closeDb(), + }); +}; + +module.exports = { registerNotenDbTasks }; diff --git a/tests/cypress/tasks/sshTunnel.js b/tests/cypress/tasks/sshTunnel.js new file mode 100644 index 000000000..6407f0d8a --- /dev/null +++ b/tests/cypress/tasks/sshTunnel.js @@ -0,0 +1,137 @@ +/** + * Local port forward over SSH, opened inside the node process. + * + * Knows nothing about the environment: the caller passes host, user and credential. + */ + +const fs = require("fs"); +const net = require("net"); + +let server = null; +let sshClient = null; +let localPort = null; + +/** Local end of the forward, or null. Lets the pool bind without going through the environment. */ +const tunnelPort = () => localPort; + +const resolveAgent = (agent) => { + if (agent && agent !== "true") return agent; + if (process.env.SSH_AUTH_SOCK) return process.env.SSH_AUTH_SOCK; + if (process.platform === "win32") return "\\\\.\\pipe\\openssh-ssh-agent"; + return null; +}; + +/** + * Key file or agent, either is enough. A configured but missing key path is an error rather than a + * silent fallback to the agent, since that is nearly always a typo. + */ +const resolveAuth = ({ keyPath, passphrase, agent }) => { + if (keyPath) { + const resolved = keyPath.startsWith("~") + ? keyPath.replace(/^~/, process.env.HOME || process.env.USERPROFILE || "") + : keyPath; + + if (!fs.existsSync(resolved)) { + throw new Error(`SSH key does not exist: ${resolved}. Fix the path, or clear it to use an agent.`); + } + return { + privateKey: fs.readFileSync(resolved), + passphrase: passphrase || undefined, + description: `key ${resolved}`, + }; + } + + const resolvedAgent = resolveAgent(agent); + if (resolvedAgent) return { agent: resolvedAgent, description: `agent ${resolvedAgent}` }; + + throw new Error("No SSH credential: set a key path, or ssh-add your key and leave it empty."); +}; + +/** Idempotent - repeated calls reuse the existing tunnel. */ +const openTunnel = (cfg) => + new Promise((resolve, reject) => { + if (localPort) return resolve(localPort); + if (!cfg.sshHost || !cfg.sshUser) return reject(new Error("Tunnel needs an SSH host and user.")); + + // eslint-disable-next-line global-require + const { Client } = require("ssh2"); + + let auth; + try { + auth = resolveAuth(cfg); + } catch (error) { + return reject(error); + } + + sshClient = new Client(); + + sshClient + .on("ready", () => { + server = net.createServer((socket) => { + sshClient.forwardOut( + socket.remoteAddress || "127.0.0.1", + socket.remotePort || 0, + cfg.dbHost, + cfg.dbPort, + (err, stream) => { + if (err) return socket.destroy(); + socket.pipe(stream).pipe(socket); + }, + ); + }); + + server.on("error", reject); + + // port 0: let the OS pick, so a leftover process cannot collide + server.listen(0, "127.0.0.1", () => { + localPort = server.address().port; + // eslint-disable-next-line no-console + console.log( + `[db] SSH tunnel up: 127.0.0.1:${localPort} -> ${cfg.dbHost}:${cfg.dbPort} ` + + `via ${cfg.sshUser}@${cfg.sshHost} (${auth.description})`, + ); + resolve(localPort); + }); + }) + .on("error", (err) => + reject( + new Error( + `SSH to ${cfg.sshUser}@${cfg.sshHost} using ${auth.description} failed: ${err.message}. ` + + "The SSH host is often not the web hostname, and a non-default key filename is never " + + "tried automatically.", + ), + ), + ) + .connect({ + host: cfg.sshHost, + port: cfg.sshPort || 22, + username: cfg.sshUser, + privateKey: auth.privateKey, + passphrase: auth.passphrase, + agent: auth.agent, + readyTimeout: 15000, + }); + }); + +const closeTunnel = async () => { + if (server) { + await new Promise((resolve) => server.close(resolve)); + server = null; + } + if (sshClient) { + sshClient.end(); + sshClient = null; + } + localPort = null; + return null; +}; + +const ensureTunnel = async (cfg) => { + try { + return { tunnelled: true, localPort: await openTunnel(cfg) }; + } catch (error) { + return { tunnelled: false, reason: error.message }; + } +}; + +module.exports = { ensureTunnel, closeTunnel, tunnelPort }; diff --git a/tests/cypress/tools/checks/noten.js b/tests/cypress/tools/checks/noten.js new file mode 100644 index 000000000..adeef4627 --- /dev/null +++ b/tests/cypress/tools/checks/noten.js @@ -0,0 +1,117 @@ +/** + * Fixture preconditions for the Gesamtnoteneingabe suite. Data only -- dbCheck.js runs it. + * + * Every precondition of the chain + * getBenotungstoolContext -> getStudentenNoten -> saveStudentPruefung + * is its own query, so a failure names the exact missing link instead of "no students". + */ + +const path = require("path"); + +// tests/cypress/tools/checks -> repo root is four levels up +const seeder = (name) => path.join(__dirname, "..", "..", "..", "..", "system", "seeders", name); + +/** Test semester: the running one, else the last started. Same rule as the suite and seeder 016. */ +const SEM = `(SELECT studiensemester_kurzbz FROM public.tbl_studiensemester + WHERE start <= now() ORDER BY (ende >= now()) DESC, start DESC LIMIT 1)`; + +module.exports = { + title: "Gesamtnoteneingabe", + + sqlFiles: { + seed: seeder("016_benotungstool_noten.sql"), + }, + + checks: [ + { + label: "Test semester (latest started)", + sql: `SELECT ${SEM} AS value`, + ok: (r) => Boolean(r.value), + hint: "No studiensemester with start <= now(). Apply the base dump + 003_kompetenzfeld.sql.", + }, + { + label: "demolektor1 exists", + sql: `SELECT COUNT(*)::int AS value FROM public.tbl_mitarbeiter WHERE mitarbeiter_uid = 'demolektor1'`, + ok: (r) => r.value > 0, + hint: "Apply 002_mitarbeiter.sql.", + }, + { + label: "STG5 Lehrveranstaltungen (5121 / 5221)", + sql: `SELECT COUNT(*)::int AS value FROM lehre.tbl_lehrveranstaltung + WHERE lehrveranstaltung_id IN (5121, 5221)`, + ok: (r) => r.value === 2, + hint: "Apply 013_studiengang_5.sql.", + }, + { + label: "Lehreinheiten of demolektor1 in the test semester", + sql: `SELECT COUNT(*)::int AS value + FROM lehre.tbl_lehreinheit le + JOIN lehre.tbl_lehreinheitmitarbeiter USING (lehreinheit_id) + WHERE mitarbeiter_uid = 'demolektor1' AND le.studiensemester_kurzbz = ${SEM}`, + ok: (r) => r.value > 0, + hint: + "demolektor1 teaches nothing in the CURRENT test semester. The 013 seeder pins its " + + "Lehreinheiten to the semester that was active when it ran. Apply 016_benotungstool_noten.sql.", + }, + { + label: "Lehreinheitgruppe rows with a non-NULL gruppe_kurzbz", + sql: `SELECT COUNT(*)::int AS value FROM lehre.tbl_lehreinheitgruppe + WHERE gruppe_kurzbz IS NOT NULL`, + ok: (r) => r.value > 0, + hint: + "campus.vw_student_lehrveranstaltung joins ONLY on gruppe_kurzbz. Lehrverband-style rows " + + "(verband set, gruppe_kurzbz NULL) never match. Apply 016_benotungstool_noten.sql.", + }, + { + label: "Group memberships carrying a studiensemester", + sql: `SELECT COUNT(*)::int AS value FROM public.tbl_benutzergruppe + WHERE studiensemester_kurzbz IS NOT NULL`, + ok: (r) => r.value > 0, + hint: + "tbl_benutzergruppe.studiensemester_kurzbz must equal tbl_lehreinheit.studiensemester_kurzbz. " + + "NULL never matches. Apply 016_benotungstool_noten.sql.", + }, + { + label: "Students visible in LV 5221 (the test LV)", + sql: `SELECT COUNT(*)::int AS value FROM campus.vw_student_lehrveranstaltung + WHERE lehrveranstaltung_id = 5221 AND studiensemester_kurzbz = ${SEM}`, + ok: (r) => r.value >= 3, + hint: "The suite needs at least 3 enrolled students. Apply 016_benotungstool_noten.sql.", + }, + { + label: "tbl_note 'entschuldigt' (resolved by Bezeichnung)", + sql: `SELECT COALESCE((SELECT note::text FROM lehre.tbl_note + WHERE bezeichnung = 'entschuldigt'), '') AS value`, + ok: (r) => r.value !== "", + hint: "Noten.php resolves this by Bezeichnung, not by the config PK. Apply the base inserts.", + }, + { + label: "tbl_note 'Noch nicht eingetragen'", + sql: `SELECT COALESCE((SELECT note::text FROM lehre.tbl_note + WHERE bezeichnung = 'Noch nicht eingetragen'), '') AS value`, + ok: (r) => r.value !== "", + hint: "Missing from the base dump. Rebuild with system/setup_testinstance.php --setup.", + }, + { + label: "Pruefungstypen Termin1/Termin2/Termin3/kommPruef", + sql: `SELECT COALESCE(string_agg(pruefungstyp_kurzbz, ',' ORDER BY pruefungstyp_kurzbz), '') AS value + FROM lehre.tbl_pruefungstyp + WHERE pruefungstyp_kurzbz IN ('Termin1','Termin2','Termin3','kommPruef')`, + ok: (r) => r.value.includes("Termin1") && r.value.includes("Termin2"), + hint: + "Termin3 is absent from the base dump. Harmless while CIS_GESAMTNOTE_PRUEFUNG_TERMIN3 is " + + "off, but a Termin3 insert would hit a foreign key error. 016 adds it.", + }, + { + label: "Notenschluessel assigned to LV 5221", + sql: `SELECT COUNT(*)::int AS value + FROM lehre.tbl_notenschluesselzuordnung z + JOIN lehre.tbl_notenschluesselaufteilung a USING (notenschluessel_kurzbz) + WHERE z.lehrveranstaltung_id = 5221`, + ok: (r) => r.value > 0, + hint: + "The Notenschluessel tables are empty in the base dump, so getNoteByPunkte always returns " + + "null. Apply 016_benotungstool_noten.sql.", + }, + ], +}; diff --git a/tests/cypress/tools/dbCheck.js b/tests/cypress/tools/dbCheck.js new file mode 100644 index 000000000..ca032ef5b --- /dev/null +++ b/tests/cypress/tools/dbCheck.js @@ -0,0 +1,193 @@ +#!/usr/bin/env node +/** + * Diagnose or seed the database fixture a Cypress suite needs. Suite-agnostic runner. + * + * node tests/cypress/tools/dbCheck.js noten report what is present and what is missing + * node tests/cypress/tools/dbCheck.js noten seed apply the suite's seeder, then re-check + * node tests/cypress/tools/dbCheck.js noten env print the .env block to use + * + * A suite contributes tools/checks/.js exporting { title, checks, sqlFiles }. Connection, + * tunnel and output are shared and live here. + * + * `check` is read-only; `seed` needs _DB_ALLOW_WRITES=true and an owner role. + */ + +const fs = require("fs"); +const path = require("path"); + +try { + require("dotenv").config({ path: path.join(__dirname, "..", ".env") }); +} catch (e) { + // dotenv missing -> fall back to the ambient environment +} + +const db = require("../tasks/db"); + +const COL = 48; +const CHECKS_DIR = path.join(__dirname, "checks"); + +const usage = (suite, mode) => `node tests/cypress/tools/dbCheck.js ${suite} ${mode}`; + +const loadSuite = (name) => { + const file = path.join(CHECKS_DIR, `${name}.js`); + if (!fs.existsSync(file)) { + const available = fs.existsSync(CHECKS_DIR) + ? fs.readdirSync(CHECKS_DIR).map((f) => f.replace(/\.js$/, "")).join(", ") + : "(none)"; + console.error(`\nUnknown suite "${name}". Available: ${available}\n`); + process.exit(2); + } + // eslint-disable-next-line global-require, import/no-dynamic-require + return require(file); +}; + +const runChecks = async (client, suite) => { + console.log(`\n${suite.title} fixture check\n${"=".repeat(70)}`); + + const failures = []; + for (const check of suite.checks) { + let row; + try { + row = (await client.query(check.sql)).rows[0] || {}; + } catch (error) { + console.log(` [ERR ] ${check.label}\n ${error.message}`); + failures.push(check); + continue; + } + + const passed = check.ok(row); + const value = row.value === "" ? "(none)" : String(row.value); + console.log(` [${passed ? "PASS" : "FAIL"}] ${check.label.padEnd(COL)} ${value}`); + if (!passed) failures.push(check); + } + + console.log("=".repeat(70)); + + if (!failures.length) { + console.log("\nAll preconditions met.\n"); + return 0; + } + + console.log(`\n${failures.length} problem(s):\n`); + failures.forEach((f) => console.log(` - ${f.label}\n ${f.hint}\n`)); + console.log(`Fix with: ${usage(suite.name, "seed")}\n`); + return 1; +}; + +const applySqlFile = async (client, file) => { + if (!db.writesAllowed()) { + console.error(`\nRefusing to write: set ${db.PREFIX}_DB_ALLOW_WRITES=true first.\n`); + return 2; + } + if (!fs.existsSync(file)) { + console.error(`\nFile not found: ${file}\n`); + return 2; + } + + console.log(`\nApplying ${path.relative(process.cwd(), file)} ...\n`); + + // One connection, one call: the seeders rely on running as a unit (temp tables, DO blocks and a + // closing verification query). + let result; + try { + result = await client.query(fs.readFileSync(file, "utf8")); + } catch (error) { + // `check` gets by as the application role; seeding creates and grants objects. + if (/permission denied/i.test(error.message)) { + console.error(`${error.message}\n`); + console.error(`Seeding needs the database owner, but ${db.PREFIX}_DB_USER is "${db.env("USER")}".`); + console.error("Point it at the owner role, or seed via system/setup_testinstance.php.\n"); + return 2; + } + throw error; + } + + // Print whatever the last statement returned, whatever its shape. + const results = Array.isArray(result) ? result : [result]; + const last = results.filter((r) => r && r.rows && r.rows.length).pop(); + if (last) { + console.log("Verification\n" + "-".repeat(50)); + last.rows.forEach((row) => + Object.entries(row).forEach(([k, v]) => console.log(` ${k.padEnd(24)} ${v}`))); + } + + console.log(`\nDone: ${path.basename(file)}\n`); + return 0; +}; + +const printEnv = () => { + const p = db.PREFIX; + console.log(` +Add to tests/cypress/.env (gitignored): + + TEST_ENV_PREFIX=${p} + ${p}_DB_HOST= + ${p}_DB_PORT=5432 + ${p}_DB_NAME= + ${p}_DB_USER= + ${p}_DB_PASSWORD= + ${p}_DB_ALLOW_WRITES=true + +If pg_hba.conf does not admit this machine, add the tunnel: + + ${p}_SSH_TUNNEL=true + ${p}_SSH_HOST= + ${p}_SSH_USER= + ${p}_SSH_KEY= +`); +}; + +const main = async () => { + const suiteName = process.argv[2]; + const mode = process.argv[3] || "check"; + + if (!suiteName || suiteName.startsWith("-")) { + console.error(`\n ${usage("", "[check|seed|env]")}\n`); + return 2; + } + if (mode === "env") { + printEnv(); + return 0; + } + if (!["check", "seed"].includes(mode)) { + console.error(`Unknown mode "${mode}". Use: check | seed | env`); + return 2; + } + + const suite = loadSuite(suiteName); + suite.name = suiteName; + + if (mode !== "check" && !(suite.sqlFiles && suite.sqlFiles[mode])) { + console.error(`Suite "${suiteName}" defines no SQL file for "${mode}".`); + return 2; + } + + // Opens the SSH tunnel when configured, so a workstation run needs no manual forward. + const status = await db.checkAvailability({ requireWrites: mode !== "check" }); + if (!status.available) { + console.error(`\nCould not connect: ${status.reason}\n`); + if (status.reason === "not-configured") printEnv(); + return 2; + } + + try { + return await db.withClient(async (client) => { + client.on("notice", (n) => console.log(` NOTICE: ${n.message}`)); + + if (mode !== "check") { + const code = await applySqlFile(client, suite.sqlFiles[mode]); + if (code !== 0) return code; + } + return runChecks(client, suite); + }); + } finally { + await db.closeDb(); + } +}; + +main() + .then((code) => process.exit(code)) + .catch((error) => { + console.error(`\n${error.message}\n`); + process.exit(1); + });