seeder consolidation into branch naming convention; consolidated helper functions; fixed maxAntrittCount & brauchtNeueLVNote helpers in notenRules outside the cypress suite; more careful usage of db connection pools; cypress code cleanup & lint; WIP seperating branch specific configs/env setup from global cypress settings & ongoing test refinement;

This commit is contained in:
Johann Hoffmann
2026-09-16 13:56:11 +02:00
parent 7c9bcb6fd2
commit 73c63c791d
54 changed files with 1370 additions and 2595 deletions
+6 -3
View File
@@ -33,10 +33,13 @@ module.exports = defineConfig({
// the suite can switch the instance to a configuration profile for the run
on("before:run", () => suite.beforeRun(config));
// the profile goes back first; otherwise the SSH forward outlives Cypress
// the profile goes back first; the pool and the SSH forward close even if that fails
on("after:run", async () => {
await suite.afterRun(config);
await require("./tests/cypress/tasks/sshTunnel").closeTunnel();
try {
await suite.afterRun(config);
} finally {
await require("./tests/cypress/tasks/db").closeDb();
}
});
return config;
+12
View File
@@ -1,5 +1,9 @@
{
"scripts": {
"lint": "eslint tests/cypress cypress.config.js eslint.config.js",
"lint:fix": "eslint tests/cypress cypress.config.js eslint.config.js --fix",
"format": "prettier --write \"tests/cypress/**/*.{js,md}\"",
"format:check": "prettier --check \"tests/cypress/**/*.{js,md}\"",
"cy:open": "cypress open",
"cy:run": "cypress run",
"cy:noten": "cypress run --spec \"tests/cypress/e2e/specs/**/*.cy.js\"",
@@ -18,5 +22,13 @@
"dotenv": "^17.4.2",
"pg": "^8.13.1",
"ssh2": "^1.17.0"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"eslint": "^10.10.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-cypress": "^7.0.2",
"globals": "^17.12.0",
"prettier": "^3.9.7"
}
}
@@ -8,8 +8,26 @@
* The local fallback applies only while the client has no `verlauf` for a row.
*/
/** The maximum number of attempts that count in this tool. The server derives this value. */
export const maxAntrittCount = (config) => config?.CIS_GESAMTNOTE_MAX_ANTRITTE ?? 1;
/**
* Is the value set? Never test a grade or a count for truthy: the grade key 0 ("Teilnote") is a
* grade and a configured 0 is a number, but both are falsy. The server rewrites an empty grade
* with `if($note=='')`, which catches a 0 under PHP 7 but not under PHP 8, so a 0 does reach us.
*/
const isSet = (value) => value !== null && value !== undefined && value !== "";
/**
* The maximum number of attempts that count in this tool. The server derives the value and sends
* an integer. The conversion keeps it a number if it ever arrives as a string, because the callers
* compare it (`<`) and the tests compare it strictly. A configured 0 stays 0 and blocks every
* attempt; only a missing or unusable value falls back to 1.
*/
export const maxAntrittCount = (config) => {
const configured = config?.CIS_GESAMTNOTE_MAX_ANTRITTE;
if (!isSet(configured)) return 1;
const count = Number(configured);
return Number.isFinite(count) ? count : 1;
};
/**
* The attempts that count. The NOTE decides, not the exam type: the grades in NOTEN_OHNE_ANTRITT
@@ -25,7 +43,7 @@ export const antrittCountStudent = (student, config, notenOptions) => {
if (!ohneAntritt.find((pk) => pk == p.note)) count++;
}
if (count === 0 && student.note && !ohneAntritt.find((pk) => pk == student.note)) {
if (count === 0 && isSet(student.note) && !ohneAntritt.find((pk) => pk == student.note)) {
const noteOption = notenOptions?.find((n) => n.note == student.note);
return noteOption?.lehre ? 1 : 0;
}
@@ -45,7 +63,8 @@ export const canAddPruefung = (student, config) => {
* not from `lv_note`, because `lv_note` contains RELEASED grades only. An entered grade that is
* not released still exists. The user interface uses this for a hint only.
*/
export const brauchtNeueLvNote = (student) => student.verlauf ? !student.verlauf.hatLvNote : !student.lv_note;
export const brauchtNeueLvNote = (student) =>
student.verlauf ? !student.verlauf.hatLvNote : !isSet(student.lv_note);
/**
* The grade state from the two timestamps: offen = no grade entered (an empty row), changed = a
-28
View File
@@ -1,28 +0,0 @@
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)
;
-36
View File
@@ -1,36 +0,0 @@
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;
-40
View File
@@ -1,40 +0,0 @@
-- 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)
;
-27
View File
@@ -1,27 +0,0 @@
-- 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)
;
-25
View File
@@ -1,25 +0,0 @@
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');
-114
View File
@@ -1,114 +0,0 @@
-- 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;
-161
View File
@@ -1,161 +0,0 @@
-- 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,'[email protected]',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);
-120
View File
@@ -1,120 +0,0 @@
-- 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;
-239
View File
@@ -1,239 +0,0 @@
-- 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,'[email protected]',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);
-266
View File
@@ -1,266 +0,0 @@
-- 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;
@@ -1,16 +0,0 @@
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)
;
-107
View File
@@ -1,107 +0,0 @@
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);
-38
View File
@@ -1,38 +0,0 @@
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, '<content></content>', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'CIS4', NULL),
('German', 32, 1, true, '<content></content>', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Studium', NULL),
('German', 33, 1, true, '<content><url><![CDATA[../cis.php/Cis/Stundenplan]]></url><target><![CDATA[]]></target></content>', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'LV-Plan', NULL),
('German', 34, 1, true, '<content><url><![CDATA[../cis.php/Cis4]]></url><target><![CDATA[]]></target></content>', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Dahsboard', NULL),
('German', 35, 1, true, '<content><url><![CDATA[../cis.php/Cis/MyLv]]></url><target><![CDATA[]]></target></content>', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Meine LVs', NULL),
('German', 36, 1, true, '<content><url><![CDATA[../cis.php/Cis/Studium]]></url><target><![CDATA[]]></target></content>', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Studium', NULL),
('German', 37, 1, true, '<content><url><![CDATA[../cis.php/Cis/Profil]]></url><target><![CDATA[]]></target></content>', NULL, NULL, now(), 'demoadmin', now(), 'demoadmin', 'Profil', NULL),
('German', 38, 1, true, '<content><url><![CDATA[../cis.php/Cis/Documents]]></url><target><![CDATA[]]></target></content>', 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')
;
-11
View File
@@ -1,11 +0,0 @@
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);
-210
View File
@@ -1,210 +0,0 @@
-- 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);
-183
View File
@@ -1,183 +0,0 @@
-- 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, <semester>, '', '') 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;
-40
View File
@@ -1,40 +0,0 @@
-- Vollzugriff auf ALLE Anwendungsschemata für alle DB-Rollen.
DO $$
DECLARE
v_grantee text;
v_target text;
v_schema text;
v_grantees text[] := ARRAY['web', 'vilesci', 'wawi', 'admin', 'PUBLIC'];
v_done integer := 0;
BEGIN
FOREACH v_grantee IN ARRAY v_grantees
LOOP
IF v_grantee = 'PUBLIC' THEN
v_target := 'PUBLIC';
ELSE
CONTINUE WHEN NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = v_grantee);
v_target := quote_ident(v_grantee);
END IF;
FOR v_schema IN
SELECT nspname
FROM pg_namespace
WHERE nspname NOT LIKE 'pg\_%' AND nspname <> 'information_schema'
ORDER BY nspname
LOOP
EXECUTE format('GRANT ALL ON SCHEMA %I TO %s', v_schema, v_target);
EXECUTE format('GRANT ALL ON ALL TABLES IN SCHEMA %I TO %s', v_schema, v_target);
EXECUTE format('GRANT ALL ON ALL SEQUENCES IN SCHEMA %I TO %s', v_schema, v_target);
EXECUTE format('GRANT ALL ON ALL FUNCTIONS IN SCHEMA %I TO %s', v_schema, v_target);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON TABLES TO %s', v_schema, v_target);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON SEQUENCES TO %s', v_schema, v_target);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON FUNCTIONS TO %s', v_schema, v_target);
v_done := v_done + 1;
END LOOP;
END LOOP;
RAISE NOTICE 'Grants auf % Schema/Rollen-Kombinationen vergeben.', v_done;
END $$;
@@ -1,3 +1,25 @@
-- Seeder of feature-60873/GesamtnoteneingabeCis4. A "-- ==== <group> ====" line starts a group; tests/cypress/tools/dbCheck.js applies single groups.
-- ==== helper_functions ====
-- Semester helpers for group studiengang_5. BS001_helper_functions.sql does not define them.
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;
-- ==== studiengang_5 ====
-- 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)
@@ -229,3 +251,470 @@ INSERT INTO public.tbl_benutzergruppe (uid, gruppe_kurzbz, updateamum, updatevon
('s125b101', 'GRP_51003', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL),
('s125b101', 'GRP_51004', now(), 'demoadmin', now(), 'demoadmin', NULL, NULL)
;
-- ==== benotungstool_noten ====
-- 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 group studiengang_5.';
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, <semester>, '', '') is created by group studiengang_5.
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;
-- ==== schema_grants ====
-- Vollzugriff auf ALLE Anwendungsschemata für alle DB-Rollen.
DO $$
DECLARE
v_grantee text;
v_target text;
v_schema text;
v_grantees text[] := ARRAY['web', 'vilesci', 'wawi', 'admin', 'PUBLIC'];
v_done integer := 0;
BEGIN
FOREACH v_grantee IN ARRAY v_grantees
LOOP
IF v_grantee = 'PUBLIC' THEN
v_target := 'PUBLIC';
ELSE
CONTINUE WHEN NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = v_grantee);
v_target := quote_ident(v_grantee);
END IF;
FOR v_schema IN
SELECT nspname
FROM pg_namespace
WHERE nspname NOT LIKE 'pg\_%' AND nspname <> 'information_schema'
ORDER BY nspname
LOOP
EXECUTE format('GRANT ALL ON SCHEMA %I TO %s', v_schema, v_target);
EXECUTE format('GRANT ALL ON ALL TABLES IN SCHEMA %I TO %s', v_schema, v_target);
EXECUTE format('GRANT ALL ON ALL SEQUENCES IN SCHEMA %I TO %s', v_schema, v_target);
EXECUTE format('GRANT ALL ON ALL FUNCTIONS IN SCHEMA %I TO %s', v_schema, v_target);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON TABLES TO %s', v_schema, v_target);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON SEQUENCES TO %s', v_schema, v_target);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON FUNCTIONS TO %s', v_schema, v_target);
v_done := v_done + 1;
END LOOP;
END LOOP;
RAISE NOTICE 'Grants auf % Schema/Rollen-Kombinationen vergeben.', v_done;
END $$;
-- ==== benotungstool_berechtigungen ====
-- Benotungstool permissions for the demo accounts. Without them every endpoint answers 403.
--
-- demolektor1 teacher; no oe_kurzbz - assertLvAccess scopes him by the Lehreinheiten he teaches
-- demoassistenz programme assistant for stg3 and stg5 (test courses 5221 and 5121 live in stg5)
--
-- Idempotent.
BEGIN;
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM public.tbl_benutzer WHERE uid = 'demolektor1') THEN
RAISE EXCEPTION 'Benutzer demolektor1 fehlt, die Basisdaten sind unvollstaendig.';
END IF;
IF NOT EXISTS (SELECT 1 FROM public.tbl_benutzer WHERE uid = 'demoassistenz') THEN
RAISE EXCEPTION 'Benutzer demoassistenz fehlt, die Basisdaten sind unvollstaendig.';
END IF;
END $$;
-- Der Lektor: darf das Werkzeug öffnen, sieht aber nur seine eigenen Lehrveranstaltungen.
INSERT INTO system.tbl_benutzerrolle (uid, berechtigung_kurzbz, art, oe_kurzbz, insertvon, insertamum)
SELECT 'demolektor1', 'lehre/benotungstool', 'suid', NULL, 'seeder', now()
WHERE NOT EXISTS (
SELECT 1 FROM system.tbl_benutzerrolle
WHERE uid = 'demolektor1'
AND berechtigung_kurzbz = 'lehre/benotungstool'
AND oe_kurzbz IS NULL
);
-- Die Assistenz: darf die Lehrveranstaltungen ihrer Studiengänge benoten.
INSERT INTO system.tbl_benutzerrolle (uid, berechtigung_kurzbz, art, oe_kurzbz, insertvon, insertamum)
SELECT 'demoassistenz', 'lehre/benotungstool_assistenz', 'suid', oe.oe_kurzbz, 'seeder', now()
FROM (VALUES ('stg3'), ('stg5')) AS oe(oe_kurzbz)
WHERE NOT EXISTS (
SELECT 1 FROM system.tbl_benutzerrolle b
WHERE b.uid = 'demoassistenz'
AND b.berechtigung_kurzbz = 'lehre/benotungstool_assistenz'
AND b.oe_kurzbz = oe.oe_kurzbz
);
COMMIT;
-- Zurücknehmen:
-- DELETE FROM system.tbl_benutzerrolle
-- WHERE insertvon = 'seeder' AND berechtigung_kurzbz LIKE 'lehre/benotungstool%';
-- ==== benotungstool_fixture_erweitert ====
-- Extensions of the Benotungstool fixture. Apply after group benotungstool_noten.
--
-- LE 51103 a second Lehreinheit of LV 5221 with two Lektoren and two students of its own. The write
-- paths replace a foreign Lehreinheit, and the grader rules need a Lehreinheit with more
-- than one Lektor. demoadmin sorts before demolektor1, so "the caller" and "the first
-- Lektor" give different results.
-- LE 51104 demolektor1 in LV 5221 in the last Sommersemester with a passed grade entry deadline.
-- The deadline tests need a semester in which the user teaches.
-- Vorlagen demo texts for 'Notenfreigabe' and 'Sancho_Mail_Template'. Without them every release
-- mail has an empty body. The dbupdate script creates only the tbl_vorlage row.
--
-- The new students sort after 'Student 1'..'Student 12', so the first twelve students of the suite
-- stay the same. Idempotent.
BEGIN;
-- group benotungstool_noten creates temp tables with the same names when both groups run in one session
DROP TABLE IF EXISTS pg_temp.sem, pg_temp.sem_frist, pg_temp.lv, pg_temp.stud;
-- Test semester: the running one, else the last started. Same rule as the suite and group benotungstool_noten.
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 last Sommersemester after its deadline. NOTENEINTRAGUNGSFRIST_SS defaults to 15 November.
CREATE TEMP TABLE sem_frist AS
SELECT studiensemester_kurzbz AS kurzbz
FROM public.tbl_studiensemester
WHERE studiensemester_kurzbz ~ '^SS[0-9]{4}$'
AND make_date(substring(studiensemester_kurzbz FROM 3 FOR 4)::int, 11, 15) < current_date
ORDER BY start DESC
LIMIT 1;
CREATE TEMP TABLE lv AS
SELECT lehrveranstaltung_id AS lv_id, semester
FROM lehre.tbl_lehrveranstaltung
WHERE lehrveranstaltung_id = 5221;
CREATE TEMP TABLE stud AS
SELECT id AS person_id, 5000 + id AS prestudent_id, 's525b' || id AS uid, vorname
FROM (VALUES (513, 'Eins'), (514, 'Zwei')) AS g(id, vorname);
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM sem) THEN
RAISE EXCEPTION 'No Studiensemester found, the base dump is missing.';
END IF;
IF NOT EXISTS (SELECT 1 FROM sem_frist) THEN
RAISE EXCEPTION 'No Sommersemester with a passed grade entry deadline.';
END IF;
IF NOT EXISTS (SELECT 1 FROM lv) THEN
RAISE EXCEPTION 'LV 5221 missing, apply group studiengang_5.';
END IF;
IF NOT EXISTS (SELECT 1 FROM lehre.tbl_lehreinheit WHERE lehreinheit_id = 51101) THEN
RAISE EXCEPTION 'Lehreinheit 51101 missing, apply group benotungstool_noten first.';
END IF;
IF (SELECT count(*) FROM public.tbl_mitarbeiter WHERE mitarbeiter_uid IN ('demoadmin', 'demolektor1')) <> 2 THEN
RAISE EXCEPTION 'demoadmin or demolektor1 missing, apply BS002_mitarbeiter.sql.';
END IF;
END $$;
-- ------------------------------------------------------------------------------------- students
INSERT INTO public.tbl_person (person_id, vorname, nachname, gebdatum, geschlecht, aktiv)
SELECT person_id, vorname, 'Zweitgruppe', '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
ON CONFLICT DO NOTHING;
INSERT INTO public.tbl_benutzer (uid, person_id, aktiv)
SELECT uid, person_id, true
FROM stud
ON CONFLICT DO NOTHING;
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
ON CONFLICT DO NOTHING;
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
ON CONFLICT DO NOTHING;
-- ------------------------------------------------------------------------------ Lehreinheit 51103
INSERT INTO lehre.tbl_lehreinheit
(lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrform_kurzbz,
wochenrythmus, raumtyp, raumtypalternativ, sprache, lehrfach_id)
SELECT 51103, lv.lv_id, sem.kurzbz, 'VO', 1, 'Dummy', 'Dummy', 'German', lv.lv_id
FROM lv, sem
ON CONFLICT DO NOTHING;
UPDATE lehre.tbl_lehreinheit le
SET studiensemester_kurzbz = sem.kurzbz
FROM sem
WHERE le.lehreinheit_id = 51103
AND le.studiensemester_kurzbz IS DISTINCT FROM sem.kurzbz;
INSERT INTO lehre.tbl_lehreinheitmitarbeiter (lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz)
SELECT 51103, l.uid, 'Lektor'
FROM (VALUES ('demoadmin'), ('demolektor1')) AS l(uid)
ON CONFLICT DO NOTHING;
INSERT INTO public.tbl_gruppe (gruppe_kurzbz, studiengang_kz, semester, bezeichnung, aktiv)
SELECT 'GRP_CYNOTEN_Z', 5, semester, 'Cypress Noten Zweitgruppe', true
FROM lv
ON CONFLICT DO NOTHING;
INSERT INTO lehre.tbl_lehreinheitgruppe (lehreinheit_id, studiengang_kz, semester, gruppe_kurzbz)
SELECT 51103, 5, lv.semester, 'GRP_CYNOTEN_Z'
FROM lv
WHERE NOT EXISTS (SELECT 1 FROM lehre.tbl_lehreinheitgruppe g
WHERE g.lehreinheit_id = 51103 AND g.gruppe_kurzbz = 'GRP_CYNOTEN_Z');
-- the students of 51103 are members of this group only, so each has exactly one Lehreinheit
INSERT INTO public.tbl_benutzergruppe (uid, gruppe_kurzbz, studiensemester_kurzbz)
SELECT s.uid, 'GRP_CYNOTEN_Z', sem.kurzbz
FROM stud s, sem
ON CONFLICT (uid, gruppe_kurzbz) DO UPDATE
SET studiensemester_kurzbz = EXCLUDED.studiensemester_kurzbz;
-- ------------------------------------------------------------------------------ Lehreinheit 51104
-- getLvForLektorInSemester needs no group, only the Lehreinheit and its Lektor
INSERT INTO lehre.tbl_lehreinheit
(lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrform_kurzbz,
wochenrythmus, raumtyp, raumtypalternativ, sprache, lehrfach_id)
SELECT 51104, lv.lv_id, sf.kurzbz, 'VO', 1, 'Dummy', 'Dummy', 'German', lv.lv_id
FROM lv, sem_frist sf
ON CONFLICT DO NOTHING;
UPDATE lehre.tbl_lehreinheit le
SET studiensemester_kurzbz = sf.kurzbz
FROM sem_frist sf
WHERE le.lehreinheit_id = 51104
AND le.studiensemester_kurzbz IS DISTINCT FROM sf.kurzbz;
INSERT INTO lehre.tbl_lehreinheitmitarbeiter (lehreinheit_id, mitarbeiter_uid, lehrfunktion_kurzbz)
VALUES (51104, 'demolektor1', 'Lektor')
ON CONFLICT DO NOTHING;
-- ---------------------------------------------------------------------------------------- Vorlagen
INSERT INTO public.tbl_vorlage (vorlage_kurzbz, bezeichnung, anmerkung, mimetype)
VALUES ('Notenfreigabe', 'Notenfreigabe', NULL, 'text/html'),
('Sancho_Mail_Template', 'Sancho Mail Template', 'Demo layout of the test instance', 'text/html')
ON CONFLICT (vorlage_kurzbz) DO NOTHING;
-- Placeholders of Noten::sendFreigabeEmail: lektor, lvaname, neuenotencount, studlist, adressen.
-- An existing active text stays. Studiengang 0 and oe 'etw' are the root, as for the other Vorlagen.
INSERT INTO public.tbl_vorlagestudiengang
(vorlage_kurzbz, studiengang_kz, version, text, oe_kurzbz, aktiv, insertamum, insertvon)
SELECT v.kurzbz, 0, 1, v.text, 'etw', true, now(), 'seeder'
FROM (VALUES
('Notenfreigabe',
'<p>{lektor} hat {neuenotencount} Note(n) freigegeben: {lvaname}</p>{studlist}<p>Empf&auml;nger: {adressen}</p>'),
('Sancho_Mail_Template', '<html><body>{content}</body></html>')
) AS v(kurzbz, text)
WHERE NOT EXISTS (SELECT 1 FROM public.tbl_vorlagestudiengang x
WHERE x.vorlage_kurzbz = v.kurzbz AND x.aktiv);
COMMIT;
DO $$
BEGIN
IF (SELECT count(*) FROM campus.vw_student_lehrveranstaltung v, sem
WHERE v.lehreinheit_id = 51103 AND v.studiensemester_kurzbz = sem.kurzbz) = 0 THEN
RAISE EXCEPTION 'No students in Lehreinheit 51103 (%), fixture unusable.', (SELECT kurzbz FROM sem);
END IF;
END $$;
SELECT (SELECT kurzbz FROM sem) AS semester,
(SELECT kurzbz FROM sem_frist) AS semester_frist,
(SELECT count(*) FROM campus.vw_student_lehrveranstaltung v, sem
WHERE v.lehreinheit_id = 51103 AND v.studiensemester_kurzbz = sem.kurzbz) AS le51103_studenten,
(SELECT count(*) FROM lehre.tbl_lehreinheitmitarbeiter
WHERE lehreinheit_id = 51103) AS le51103_lektoren,
(SELECT count(*) FROM public.tbl_vorlagestudiengang
WHERE vorlage_kurzbz IN ('Notenfreigabe', 'Sancho_Mail_Template') AND aktiv) AS vorlagen;
-229
View File
@@ -1,229 +0,0 @@
<?php
/**
* Rebuilds a throwaway test database into a usable FHComplete instance.
*
* php system/setup_testinstance.php --setup rebuild the database, then verify
* php system/setup_testinstance.php --verify verify only, write nothing
*
* The database itself is built by db_setup.sh: truncate -> 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/<name>/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/<version>/*.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);
@@ -6,40 +6,30 @@
*/
import { notenApi } from "../../../../support/api/notenApi";
import {
expectAuthError,
expectNotenError,
expectNotenSuccess,
} from "../../../../support/helpers/notenErrors";
import { expectAuthError, expectNotenError, expectNotenSuccess } from "../../../../support/helpers/notenErrors";
import {
attemptDate,
loadNotenContext,
readLvGesamtnote,
readLvGesamtnoteViaDb,
requireDbReset,
resetNotenState,
seedBaseline,
} from "../../../../support/helpers/notenTestData";
import { skipWenn } from "../../../../support/helpers/notenConfig";
import {
assistenzAuth,
assistenzConfigured,
assistenzKontext,
assistenzLvMitLehreinheiten,
requireAssistenz,
} from "../../../../support/helpers/notenAssistenz";
const NOTEN_API = "/index.ci.php/api/frontend/v1/Noten";
const teacherConfigured = () =>
Boolean(Cypress.env("NOTEN_TEACHER_USER") && Cypress.env("NOTEN_FOREIGN_LV_ID"));
const teacherConfigured = () => Boolean(Cypress.env("NOTEN_TEACHER_USER") && Cypress.env("NOTEN_FOREIGN_LV_ID"));
const getAs = (auth, path, qs) =>
cy.request({ method: "GET", url: `${NOTEN_API}/${path}`, qs, auth, failOnStatusCode: false });
const skipOhneAssistenz = (test) => {
if (assistenzConfigured()) return;
Cypress.log({ name: "skip", message: "Übersprungen: NOTEN_ASSISTENZ_USER / NOTEN_ASSISTENZ_PASSWORD fehlen." });
test.skip();
};
/** A getStudentenNoten call as an explicitly chosen user (or with no credentials at all). */
const getStudentenNotenAs = (auth, lvId, semKurzbz) =>
cy.request({
@@ -50,7 +40,7 @@ const getStudentenNotenAs = (auth, lvId, semKurzbz) =>
failOnStatusCode: false,
});
describe("Noten API - access control", () => {
describe("Noten API - Zugriffsschutz", () => {
let ctx;
before(() => {
@@ -59,11 +49,11 @@ describe("Noten API - access control", () => {
});
});
it("rejects requests that are not authenticated", () => {
it("lehnt eine Anfrage ohne Anmeldung ab", () => {
cy.clearAllCookies(); // otherwise the previous login is still active
cy.request({ method: "GET", url: `${NOTEN_API}/getCisConfig`, failOnStatusCode: false }).then(
(response) => expectAuthError(response),
cy.request({ method: "GET", url: `${NOTEN_API}/getCisConfig`, failOnStatusCode: false }).then((response) =>
expectAuthError(response),
);
cy.request({
@@ -74,8 +64,8 @@ describe("Noten API - access control", () => {
}).then((response) => expectAuthError(response));
});
describe("the configured API user", () => {
it("can read the students of the test LV", () => {
describe("der konfigurierte API-Benutzer", () => {
it("liest die Studierenden der 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;
@@ -83,18 +73,15 @@ describe("Noten API - access control", () => {
});
});
describe("teacher scoping", () => {
describe("Scope der Lehrperson", () => {
beforeEach(function () {
if (!teacherConfigured()) {
Cypress.log({
name: "skip",
message:
"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();
}
skipWenn(
this,
!teacherConfigured(),
"Übersprungen: braucht NOTEN_TEACHER_USER / NOTEN_TEACHER_PASSWORD und NOTEN_FOREIGN_LV_ID " +
"(eine LV, die diese Lehrperson NICHT unterrichtet). Die API kennt keine Identitätsübernahme, " +
"deshalb braucht der Test ein zweites echtes Konto.",
);
// cy.request reuses the session cookie of the previous (admin) call and the server prefers
// it over the Basic header - without this every test here silently runs as the admin user.
cy.clearAllCookies();
@@ -106,17 +93,13 @@ describe("Noten API - access control", () => {
});
// assertLvAccess denies through terminateWithError -> 500 + phrase, not a 401.
it("denies a teacher access to an LV they do not teach", () => {
getStudentenNotenAs(
teacherAuth(),
Cypress.env("NOTEN_FOREIGN_LV_ID"),
ctx.semKurzbz,
).then((response) => {
it("verweigert einer Lehrperson eine fremde LV", () => {
getStudentenNotenAs(teacherAuth(), Cypress.env("NOTEN_FOREIGN_LV_ID"), ctx.semKurzbz).then((response) => {
expectNotenError(response, "keineBerechtigungNoten");
});
});
it("denies a teacher writing a grade in an LV they do not teach", () => {
it("verweigert einer Lehrperson die Note in einer fremden LV", () => {
cy.request({
method: "POST",
url: `${NOTEN_API}/saveNotenvorschlag`,
@@ -134,7 +117,7 @@ describe("Noten API - access control", () => {
});
});
it("denies a teacher the Lehreinheiten of an LV they do not teach", () => {
it("verweigert einer Lehrperson die Lehreinheiten einer fremden LV", () => {
getAs(teacherAuth(), "getLehreinheitenFuerLv", {
lv_id: Cypress.env("NOTEN_FOREIGN_LV_ID"),
sem_kurzbz: ctx.semKurzbz,
@@ -147,7 +130,7 @@ describe("Noten API - access control", () => {
describe("getBenotungstoolContext als Assistenz", () => {
beforeEach(function () {
skipOhneAssistenz(this);
requireAssistenz(this);
cy.clearAllCookies();
});
@@ -223,12 +206,12 @@ describe("Noten API - access control", () => {
i >= ctx.students.length
? cy.wrap(null, { log: false })
: cy
.task("noten:db:andereLvNoten", {
lvId: ctx.lvId,
semKurzbz: ctx.semKurzbz,
studentUid: ctx.students[i].uid,
})
.then((anzahl) => (anzahl === 0 ? ctx.students[i] : suche(i + 1)));
.task("noten:db:andereLvNoten", {
lvId: ctx.lvId,
semKurzbz: ctx.semKurzbz,
studentUid: ctx.students[i].uid,
})
.then((anzahl) => (anzahl === 0 ? ctx.students[i] : suche(i + 1)));
suche(0).then((found) => {
student = found;
@@ -236,21 +219,18 @@ describe("Noten API - access control", () => {
});
it("ändert bei lv_id=null keine LV-Note", function () {
if (!student) {
Cypress.log({ name: "skip", message: "Übersprungen: jeder Studierende hat LV-Noten in anderen LVs." });
this.skip();
}
skipWenn(this, !student, "Übersprungen: jeder Studierende hat LV-Noten in anderen LVs.");
const g2 = ctx.gradeNotes.find((n) => String(n) !== String(ctx.notes.negativ));
resetNotenState(ctx);
seedBaseline(ctx, student.uid);
seedBaseline(ctx, student);
notenApi
.saveNotenvorschlag(null, ctx.semKurzbz, student.uid, g2)
.then((response) => expectNotenError(response, "wrongParameters"));
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((row) => {
expect(String(row.note), "die LV-Note bleibt").to.eq(String(ctx.notes.negativ));
});
});
@@ -260,7 +240,9 @@ describe("Noten API - access control", () => {
// W10: Der Endpunkt liefert alle Lehreinheiten der LV, für jede Rolle mit Zugriff auf die LV.
describe("getLehreinheitenFuerLv", () => {
const lehreinheitenDerLv = (lvId) =>
cy.task("noten:db:lehreinheitenDerLv", { lvId, semKurzbz: ctx.semKurzbz }).then((ids) => ids.map(String).sort());
cy
.task("noten:db:lehreinheitenDerLv", { lvId, semKurzbz: ctx.semKurzbz })
.then((ids) => ids.map(String).sort());
// die Abfrage liefert eine Zeile je Gruppe, eine Lehreinheit kann also mehrfach vorkommen
const idsOf = (rows) => [...new Set(rows.map((r) => String(r.lehreinheit_id)))].sort();
@@ -272,8 +254,18 @@ describe("Noten API - access control", () => {
const rows = expectNotenSuccess(response, "getLehreinheitenFuerLv");
expect(rows, "Lehreinheiten").to.be.an("array").and.not.be.empty;
expect(rows[0], "Felder einer Lehreinheit").to.include.all.keys(
"lehreinheit_id", "lehrveranstaltung_id", "lehrform_kurzbz", "direktinskription", "semester",
"verband", "gruppe", "gruppe_kurzbz", "kurzbz", "kurzbzlang", "termincount", "studentcount",
"lehreinheit_id",
"lehrveranstaltung_id",
"lehrform_kurzbz",
"direktinskription",
"semester",
"verband",
"gruppe",
"gruppe_kurzbz",
"kurzbz",
"kurzbzlang",
"termincount",
"studentcount",
);
lehreinheitenDerLv(ctx.lvId).then((ids) => {
@@ -283,7 +275,7 @@ describe("Noten API - access control", () => {
});
it("liefert einer Assistenz die Lehreinheiten einer LV ihres Studiengangs", function () {
skipOhneAssistenz(this);
requireAssistenz(this);
cy.clearAllCookies();
assistenzLvMitLehreinheiten(ctx.semKurzbz).then((ziel) => {
@@ -4,31 +4,24 @@
*/
import { notenApi } from "../../../../support/api/notenApi";
import {
expectBulkRowAccepted,
expectBulkRowError,
expectNotenSuccess,
} from "../../../../support/helpers/notenErrors";
import { expectBulkRowAccepted, expectBulkRowError, expectNotenSuccess } from "../../../../support/helpers/notenErrors";
import {
requireKonfiguration,
requireNotenModus,
requirePunkteModus,
requireWiederholung,
} from "../../../../support/helpers/notenConfig";
import {
attemptDate,
loadNotenContext,
readLvGesamtnote,
readLvGesamtnoteViaDb,
requireDbReset,
resetNotenState,
seedBaseline,
} from "../../../../support/helpers/notenTestData";
import {
addPruefung,
attemptsOfStudent,
readState,
} from "../../../../support/helpers/notenScenario";
import { addPruefung, attemptsOfStudent, readStateViaApi } from "../../../../support/helpers/notenScenario";
describe("Noten API - bulk paths", () => {
describe("Noten API - Sammelpfade", () => {
let ctx;
before(() => {
@@ -58,13 +51,13 @@ describe("Noten API - bulk paths", () => {
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF", true);
});
it("rejects only the row that breaks a §1 rule and accepts the rest", () => {
it("lehnt nur die Zeile gegen eine §1-Regel ab und nimmt die übrigen an", () => {
const atCap = ctx.students[0];
const fresh = ctx.students[1];
resetNotenState(ctx);
seedBaseline(ctx, atCap.uid);
seedBaseline(ctx, fresh.uid);
seedBaseline(ctx, atCap);
seedBaseline(ctx, fresh);
const datum = bisZurGrenze(atCap);
@@ -86,7 +79,7 @@ describe("Noten API - bulk paths", () => {
expectBulkRowAccepted(data, fresh.uid);
});
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const created = attemptsOfStudent(data, fresh.uid);
expect(
created.map((p) => String(p.datum).slice(0, 10)),
@@ -94,7 +87,6 @@ describe("Noten API - bulk paths", () => {
).to.include(datum);
});
});
});
describe("savePruefungenBulk", () => {
@@ -104,20 +96,17 @@ describe("Noten API - bulk paths", () => {
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF", true);
});
it("applies the §1 rules per row", function () {
if (ctx.cisConfig.CIS_GESAMTNOTE_PUNKTE) {
// im Punktemodus leitet der Endpunkt die Note aus den Punkten ab; die Regelprüfung
// deckt der punktebasierte Test unten ab
Cypress.log({ name: "skip", message: "Skipped: CIS_GESAMTNOTE_PUNKTE ist aktiv, siehe den punktebasierten Test." });
this.skip();
}
it("wendet die §1-Regeln je Zeile an", function () {
// im Punktemodus leitet der Endpunkt die Note aus den Punkten ab; die Regelprüfung
// deckt der punktebasierte Test unten ab
requireNotenModus(this, ctx);
const atCap = ctx.students[0];
const fresh = ctx.students[1];
resetNotenState(ctx);
seedBaseline(ctx, atCap.uid);
seedBaseline(ctx, fresh.uid);
seedBaseline(ctx, atCap);
seedBaseline(ctx, fresh);
const datum = bisZurGrenze(atCap);
@@ -157,8 +146,8 @@ describe("Noten API - bulk paths", () => {
const punkte = 70;
resetNotenState(ctx);
seedBaseline(ctx, atCap.uid);
seedBaseline(ctx, fresh.uid);
seedBaseline(ctx, atCap);
seedBaseline(ctx, fresh);
const datum = bisZurGrenze(atCap);
const zeile = (student) => ({
@@ -178,8 +167,10 @@ describe("Noten API - bulk paths", () => {
notenApi.getNoteByPunkte(punkte, ctx.lvId, ctx.semKurzbz).then((antwort) => {
const erwartet = antwort.body.data;
readState(ctx).then((data) => {
const neu = attemptsOfStudent(data, fresh.uid).find((p) => String(p.datum).slice(0, 10) === datum);
readStateViaApi(ctx).then((data) => {
const neu = attemptsOfStudent(data, fresh.uid).find(
(p) => String(p.datum).slice(0, 10) === datum,
);
expect(neu, "der neue Termin").to.exist;
expect(String(neu.note), "die aus den Punkten abgeleitete Note").to.eq(String(erwartet));
});
@@ -189,12 +180,9 @@ describe("Noten API - bulk paths", () => {
});
describe("saveNotenvorschlagBulk", () => {
it("writes an LV note for every row", function () {
if (ctx.cisConfig.CIS_GESAMTNOTE_PUNKTE) {
// im Punktemodus kommt die Note aus dem Notenschlüssel, siehe den Punktemodus-Block
Cypress.log({ name: "skip", message: "Skipped: CIS_GESAMTNOTE_PUNKTE ist aktiv." });
this.skip();
}
it("schreibt für jede Zeile eine LV-Note", function () {
// im Punktemodus kommt die Note aus dem Notenschlüssel, siehe den Punktemodus-Block
requireNotenModus(this, ctx);
const [a, b] = ctx.students;
@@ -212,12 +200,12 @@ describe("Noten API - bulk paths", () => {
expectBulkRowAccepted(data, b.uid);
});
readLvGesamtnote(ctx, a.uid).then((row) => {
readLvGesamtnoteViaDb(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) => {
readLvGesamtnoteViaDb(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]));
});
@@ -246,7 +234,7 @@ describe("Noten API - bulk paths", () => {
expectBulkRowAccepted(data, mitPunkten.uid);
});
readLvGesamtnote(ctx, ohnePunkte.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, ohnePunkte.uid).then((row) => {
expect(row, "die übersprungene Zeile bleibt ungeschrieben").to.be.null;
});
});
@@ -18,7 +18,7 @@ import {
attemptDate,
baselineBenotungsdatum,
loadNotenContext,
readLvGesamtnote,
readLvGesamtnoteViaDb,
requireDbReset,
resetNotenState,
seedBaseline,
@@ -29,12 +29,11 @@ import {
attemptsOfStudent,
givenBaseline,
lvNoteOf,
readState,
readStateViaApi,
verlaufOfStudent,
} from "../../../../support/helpers/notenScenario";
const freigabePassword = () =>
Cypress.env("NOTEN_FREIGABE_PASSWORD") || Cypress.env("adminpassword");
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) => ({
@@ -55,8 +54,8 @@ describe("Noten API - Notenfreigabe", () => {
});
});
describe("password gate", () => {
it("rejects a Freigabe with a wrong password and changes nothing", function () {
describe("Passwortschutz", () => {
it("lehnt eine Freigabe mit falschem Passwort ab und ändert nichts", function () {
// ohne Passwortpflicht gibt der Aufruf frei
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_FREIGABE_PASSWORT", true);
requireDbReset();
@@ -64,53 +63,42 @@ describe("Noten API - Notenfreigabe", () => {
const student = ctx.students[0];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, {
seedBaseline(ctx, student, {
note: ctx.gradeNotes[0],
freigegeben: false,
benotungsdatum: baselineBenotungsdatum(ctx),
});
notenApi
.saveStudentenNoten(
"definitely-not-the-password",
[notenPayload(student)],
ctx.lvId,
ctx.semKurzbz,
)
.saveStudentenNoten("definitely-not-the-password", [notenPayload(student)], ctx.lvId, ctx.semKurzbz)
.then((response) => {
expectNotenError(response, "wrongPassword");
});
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(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;
});
});
});
describe("happy path", () => {
describe("der normale Ablauf", () => {
beforeEach(() => {
requireDbReset();
});
it("stamps freigabedatum on a changed note", () => {
it("setzt das freigabedatum auf einer geänderten Note", () => {
const student = ctx.students[0];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, {
seedBaseline(ctx, student, {
note: ctx.gradeNotes[0],
freigegeben: false,
benotungsdatum: baselineBenotungsdatum(ctx),
});
notenApi
.saveStudentenNoten(
freigabePassword(),
[notenPayload(student)],
ctx.lvId,
ctx.semKurzbz,
)
.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");
@@ -122,13 +110,13 @@ describe("Noten API - Notenfreigabe", () => {
expect(entry.verlauf.pruefungen, "inklusive der Termine").to.be.an("array");
});
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const grades = lvNoteOf(data, student.uid);
expect(grades.freigabedatum, "the note is now freigegeben").to.exist;
});
// C1: Die Freigabe brach früher vor dem Schreiben ab.
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((row) => {
expect(row.freigabedatum, "C1: die Freigabe schreibt freigabedatum").to.not.be.null;
});
});
@@ -139,17 +127,19 @@ describe("Noten API - Notenfreigabe", () => {
const student = ctx.students[0];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ, freigegeben: false });
seedBaseline(ctx, student, { note: ctx.notes.negativ, freigegeben: false });
notenApi
.saveStudentenNoten(freigabePassword(), [notenPayload(student)], ctx.lvId, ctx.semKurzbz)
.then((response) => {
const entry = expectNotenSuccess(response, "saveStudentenNoten").find((r) => r.uid === student.uid);
// ohne den Verlauf in der Antwort zeigt die Tabelle den neuen Termin erst nach einem Reload
expect(entry.verlauf.pruefungen, "der erste Antritt kommt mit der Antwort zurück").to.have.length(1);
expect(entry.verlauf.pruefungen, "der erste Antritt kommt mit der Antwort zurück").to.have.length(
1,
);
});
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const attempts = attemptsOfStudent(data, student.uid);
expect(attempts, "genau ein Termin").to.have.length(1);
expect(attempts[0].antritt_nr, "als Antritt 1").to.eq(1);
@@ -162,7 +152,7 @@ describe("Noten API - Notenfreigabe", () => {
const student = ctx.students[0];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ, freigegeben: false });
seedBaseline(ctx, student, { note: ctx.notes.negativ, freigegeben: false });
notenApi
.saveStudentenNoten(freigabePassword(), [notenPayload(student)], ctx.lvId, ctx.semKurzbz)
@@ -171,7 +161,7 @@ describe("Noten API - Notenfreigabe", () => {
expect(entry.freigabedatum, "die Freigabe gilt trotzdem").to.exist;
});
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
expect(attemptsOfStudent(data, student.uid), "kein Termin").to.have.length(0);
expect(verlaufOfStudent(data, student.uid).antrittCount, "die LV-Note zählt als Antritt 1").to.eq(1);
});
@@ -183,32 +173,35 @@ describe("Noten API - Notenfreigabe", () => {
const student = ctx.students[1];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ, freigegeben: false });
seedBaseline(ctx, student, { note: ctx.notes.negativ, freigegeben: false });
notenApi.saveStudentenNoten("", [notenPayload(student)], ctx.lvId, ctx.semKurzbz).then((response) => {
const data = expectNotenSuccess(response, "Freigabe ohne Passwort");
expect(data.map((r) => r.uid), "freigegebene Zeilen").to.include(student.uid);
expect(
data.map((r) => r.uid),
"freigegebene Zeilen",
).to.include(student.uid);
});
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((row) => {
expect(row.freigabedatum, "freigabedatum").to.not.be.null;
});
});
// W7: Eine einzelne Wiederholung ist nicht Antritt 1. Die Freigabe überschreibt ihre Note nicht.
it("keeps the grade of a single repeat", () => {
it("behält die Note einer einzelnen Wiederholung", () => {
const student = ctx.students[1];
const g2 = ctx.gradeNotes.find((n) => String(n) !== String(ctx.notes.negativ));
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ, freigegeben: false, erstantritt: false });
seedBaseline(ctx, student, { note: ctx.notes.negativ, freigegeben: false, erstantritt: false });
seedPruefung(ctx, student, { note: g2, datum: attemptDate(ctx, 1), typ: "Termin2" });
notenApi
.saveStudentenNoten(freigabePassword(), [notenPayload(student)], ctx.lvId, ctx.semKurzbz)
.then((response) => expectNotenSuccess(response, "saveStudentenNoten"));
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const attempts = attemptsOfStudent(data, student.uid);
expect(attempts, "the Freigabe adds no exam").to.have.length(1);
expect(String(attempts[0].note), "the repeat keeps its grade").to.eq(String(g2));
@@ -217,12 +210,12 @@ describe("Noten API - Notenfreigabe", () => {
// Das Datum eines Termins gehört der Person, die ihn eingetragen hat. Die Freigabe macht die
// Note verbindlich und verschiebt kein Datum.
it("keeps the date of an exam that already exists", () => {
it("behält das Datum einer bestehenden Prüfung", () => {
const student = ctx.students[2];
const pruefungsdatum = attemptDate(ctx, 2);
resetNotenState(ctx);
seedBaseline(ctx, student.uid, {
seedBaseline(ctx, student, {
note: ctx.notes.negativ,
freigegeben: false,
erstantritt: false,
@@ -235,23 +228,21 @@ describe("Noten API - Notenfreigabe", () => {
.saveStudentenNoten(freigabePassword(), [notenPayload(student)], ctx.lvId, ctx.semKurzbz)
.then((response) => expectNotenSuccess(response, "saveStudentenNoten"));
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const attempts = attemptsOfStudent(data, student.uid);
expect(attempts, "der Termin bleibt der einzige").to.have.length(1);
expect(String(attempts[0].datum).slice(0, 10), "mit seinem eigenen Datum").to.eq(
pruefungsdatum,
);
expect(String(attempts[0].datum).slice(0, 10), "mit seinem eigenen Datum").to.eq(pruefungsdatum);
});
});
it("only touches rows whose benotungsdatum is newer than their freigabedatum", () => {
it("ändert nur Zeilen mit einem benotungsdatum nach dem 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, {
seedBaseline(ctx, changed, {
note: ctx.gradeNotes[0],
freigegeben: true,
freigabedatum: `${ctx.semKurzbz.slice(2, 6)}-01-05 08:00:00`,
@@ -259,7 +250,7 @@ describe("Noten API - Notenfreigabe", () => {
});
// already freigegeben and untouched since -> must be left alone
seedBaseline(ctx, alreadyReleased.uid, {
seedBaseline(ctx, alreadyReleased, {
note: ctx.gradeNotes[0],
freigegeben: true,
freigabedatum: `${ctx.semKurzbz.slice(2, 6)}-01-10 08:00:00`,
@@ -284,7 +275,7 @@ describe("Noten API - Notenfreigabe", () => {
).to.not.include(alreadyReleased.uid);
});
readLvGesamtnote(ctx, alreadyReleased.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, alreadyReleased.uid).then((row) => {
expect(
new Date(row.freigabedatum).toISOString().slice(0, 10),
"the untouched row keeps its original freigabedatum",
@@ -318,7 +309,7 @@ describe("Noten API - Notenfreigabe", () => {
const student = ctx.students[3];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ, freigegeben: false });
seedBaseline(ctx, student, { note: ctx.notes.negativ, freigegeben: false });
notenApi
.saveStudentenNoten(freigabePassword(), [notenPayload(student)], ctx.lvId, ctx.semKurzbz)
@@ -327,7 +318,7 @@ describe("Noten API - Notenfreigabe", () => {
expect(entry, `${student.uid} ist freigegeben`).to.exist;
});
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((row) => {
expect(row.freigabedatum, "freigabedatum").to.not.be.null;
});
});
@@ -353,7 +344,7 @@ describe("Noten API - Notenfreigabe", () => {
.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, andereNote())
.then((response) => expectNotenError(response, "freigabeEndgueltig"));
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((row) => {
expect(String(row.note), "die LV-Note bleibt").to.eq(String(ctx.notes.negativ));
});
});
@@ -367,7 +358,7 @@ describe("Noten API - Notenfreigabe", () => {
expectNotenError(response, "freigabeEndgueltig"),
);
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
expect(attemptsOfStudent(data, student.uid), "nur Antritt 1").to.have.length(1);
});
});
@@ -379,8 +370,8 @@ describe("Noten API - Notenfreigabe", () => {
const [freigegeben, offen] = ctx.students;
resetNotenState(ctx);
seedBaseline(ctx, freigegeben.uid, { freigegeben: true });
seedBaseline(ctx, offen.uid, { freigegeben: false });
seedBaseline(ctx, freigegeben, { freigegeben: true });
seedBaseline(ctx, offen, { freigegeben: false });
notenApi
.saveNotenvorschlagBulk(ctx.lvId, ctx.semKurzbz, [
@@ -6,15 +6,11 @@
* direkt nach der Zugriffsprüfung, ein abgelehnter Request schreibt also nichts. Die Meldung nennt die Frist
* und prüft damit ihre Ableitung aus NOTENEINTRAGUNGSFRIST_SS/WS.
*
* Seeder 019 legt das Sommersemester an, in dem der Testbenutzer nach der Frist unterrichtet.
* Die Seeder-Gruppe benotungstool_fixture_erweitert legt das Sommersemester an, in dem der Testbenutzer nach der Frist unterrichtet.
*/
import { notenApi } from "../../../../support/api/notenApi";
import {
expectNotenError,
expectNotenSuccess,
messageMatchesPhrase,
} from "../../../../support/helpers/notenErrors";
import { expectNotenError, expectNotenSuccess, messageMatchesPhrase } from "../../../../support/helpers/notenErrors";
import {
attemptDate,
expectedFristString,
@@ -54,7 +50,8 @@ describe("Noten API - Noteneintragungsfrist (Prüfungsordnung §1)", () => {
// ein fehlendes Semester ist ein Fixturefehler und kein übersprungener Test
const paarFuer = (typ) => {
expect(abgelaufen[typ], `ein ${typ} mit abgelaufener Frist, in dem der Testbenutzer unterrichtet`).to.not.be.null;
expect(abgelaufen[typ], `ein ${typ} mit abgelaufener Frist, in dem der Testbenutzer unterrichtet`).to.not.be
.null;
return abgelaufen[typ];
};
@@ -118,7 +115,8 @@ describe("Noten API - Noteneintragungsfrist (Prüfungsordnung §1)", () => {
sem_kurzbz: paar.semKurzbz,
pruefung_id: null,
}),
saveNotenvorschlag: (paar) => notenApi.saveNotenvorschlag(paar.lvId, paar.semKurzbz, s0().uid, ctx.gradeNotes[0]),
saveNotenvorschlag: (paar) =>
notenApi.saveNotenvorschlag(paar.lvId, paar.semKurzbz, s0().uid, ctx.gradeNotes[0]),
saveNotenvorschlagBulk: (paar) =>
notenApi.saveNotenvorschlagBulk(paar.lvId, paar.semKurzbz, [
{ uid: s0().uid, note: ctx.gradeNotes[0], punkte: null },
@@ -154,8 +152,10 @@ describe("Noten API - Noteneintragungsfrist (Prüfungsordnung §1)", () => {
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_FRIST_AUSNAHME_GILT", false);
const paar = paarFuer(typ);
expect(fristHasPassed(paar.semKurzbz, fristSS(), fristWS()), `die Frist von ${paar.semKurzbz} ist vorbei`)
.to.be.true;
expect(
fristHasPassed(paar.semKurzbz, fristSS(), fristWS()),
`die Frist von ${paar.semKurzbz} ist vorbei`,
).to.be.true;
aufruf(paar).then((response) => {
expectNotenError(response, "noteneintragungsfristVorbei");
@@ -7,7 +7,7 @@
import { notenApi } from "../../../../support/api/notenApi";
import { expectNotenError, expectNotenSuccess } from "../../../../support/helpers/notenErrors";
import { requirePunkteModus } from "../../../../support/helpers/notenConfig";
import { requirePunkteModus, skipWenn } from "../../../../support/helpers/notenConfig";
import {
attemptDate,
loadNotenContext,
@@ -15,7 +15,7 @@ import {
resetNotenState,
seedBaseline,
} from "../../../../support/helpers/notenTestData";
import { attemptsOfStudent, givenBaseline, readState } from "../../../../support/helpers/notenScenario";
import { attemptsOfStudent, givenBaseline, readStateViaApi } from "../../../../support/helpers/notenScenario";
const MAX_PUNKTE = 100;
@@ -44,7 +44,7 @@ describe("Noten API - Notenschlüssel (getNoteByPunkte)", () => {
return cy.then(() => observed);
};
it("maps every point value through a monotonic, exact grade scale", () => {
it("bildet jeden Punktewert auf eine monotone und exakte Notenskala ab", () => {
sweepScale().then((observed) => {
const graded = observed.filter((entry) => entry.note !== null && entry.note !== undefined);
@@ -84,11 +84,7 @@ describe("Noten API - Notenschlüssel (getNoteByPunkte)", () => {
}
}
cy.log(
`Notenschlüssel of LV ${lvId}: ${boundaries
.map((b) => `>=${b.punkte} -> ${b.note}`)
.join(", ")}`,
);
cy.log(`Notenschlüssel of LV ${lvId}: ${boundaries.map((b) => `>=${b.punkte} -> ${b.note}`).join(", ")}`);
expect(
boundaries.length,
@@ -128,15 +124,12 @@ describe("Noten API - Notenschlüssel (getNoteByPunkte)", () => {
beforeEach(function () {
requirePunkteModus(this, ctx);
if (schwelleUnterNull) {
Cypress.log({ name: "skip", message: "Übersprungen: der Notenschlüssel hat eine Schwelle unter 0 Punkten." });
this.skip();
}
skipWenn(this, schwelleUnterNull, "Übersprungen: der Notenschlüssel hat eine Schwelle unter 0 Punkten.");
requireDbReset();
});
const expectNurAntritt1 = (students) =>
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
students.forEach((s) => {
expect(attemptsOfStudent(data, s.uid), `Prüfungen von ${s.uid}`).to.have.length(1);
});
@@ -167,7 +160,7 @@ describe("Noten API - Notenschlüssel (getNoteByPunkte)", () => {
const students = ctx.students.slice(0, 2);
resetNotenState(ctx);
students.forEach((s) => seedBaseline(ctx, s.uid));
students.forEach((s) => seedBaseline(ctx, s));
notenApi
.createPruefungen(
@@ -183,5 +176,4 @@ describe("Noten API - Notenschlüssel (getNoteByPunkte)", () => {
expectNurAntritt1(students);
});
});
});
@@ -14,7 +14,7 @@ import {
baselineBenotungsdatum,
baselineDate,
loadNotenContext,
readLvGesamtnote,
readLvGesamtnoteViaDb,
requireDbReset,
resetNotenState,
seedBaseline,
@@ -24,7 +24,7 @@ import {
addPruefung,
attemptsOfStudent,
lvNoteOf,
readState,
readStateViaApi,
verlaufOfStudent,
} from "../../../../support/helpers/notenScenario";
@@ -38,41 +38,38 @@ describe("Noten API - Notenvorschlag", () => {
});
});
const readRow = (uid) => readLvGesamtnote(ctx, uid);
const readRow = (uid) => readLvGesamtnoteViaDb(ctx, uid);
describe("saving a Notenvorschlag on a student without a grade", () => {
it("returns the stored note with a benotungsdatum and no freigabedatum", () => {
describe("ein Notenvorschlag für einen Studierenden ohne Note", () => {
it("gibt die gespeicherte Note mit benotungsdatum und ohne freigabedatum zurück", () => {
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;
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,
"",
]);
});
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,
"",
]);
});
});
// getStudentenNoten liest seit dem Wechsel auf den ungefilterten Getter auch noch nicht
// freigegebene Noten - sonst sind LV-Note und Freigabestatus nach einem Reload leer.
it("reports the offen note back through getStudentenNoten", () => {
it("meldet die offene Note über getStudentenNoten zurück", () => {
const student = ctx.students[0];
resetNotenState(ctx);
notenApi.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.gradeNotes[0]);
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const grades = lvNoteOf(data, student.uid);
expect(grades, `grades entry for ${student.uid}`).to.exist;
expect(
@@ -93,7 +90,7 @@ describe("Noten API - Notenvorschlag", () => {
describe("das gewählte Benotungsdatum", () => {
// Die LV-Note IST Antritt 1. Ohne diese Zeile bekäme die nächste Prüfung Termin2, und der
// Legacy-Typ der ganzen Kette verschiebt sich um eine Stelle.
it("writes attempt 1 with the chosen day", function () {
it("schreibt Antritt 1 mit dem gewählten Tag", function () {
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_ERSTANTRITT_BEI_UEBERNAHME", true);
const student = ctx.students[1];
@@ -109,7 +106,7 @@ describe("Noten API - Notenvorschlag", () => {
expect(row.verlauf.antrittCount, "die LV-Note ist Antritt 1").to.eq(1);
expect(row.verlauf.hatWiederholung, "Antritt 1 ist keine Wiederholung").to.be.false;
})
.then(() => readState(ctx))
.then(() => readStateViaApi(ctx))
.then((data) => {
const attempts = attemptsOfStudent(data, student.uid);
expect(attempts, "genau ein Antritt").to.have.length(1);
@@ -118,7 +115,7 @@ describe("Noten API - Notenvorschlag", () => {
});
});
it("makes the next exam attempt 2, not attempt 1 again", function () {
it("macht die nächste Prüfung zu Antritt 2 statt wieder zu Antritt 1", function () {
requireWiederholung(this, ctx);
const student = ctx.students[2];
@@ -126,19 +123,15 @@ describe("Noten API - Notenvorschlag", () => {
resetNotenState(ctx);
notenApi
.saveNotenvorschlag(
ctx.lvId, ctx.semKurzbz, student.uid, ctx.notes.negativ, null, baselineDate(ctx),
)
.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.notes.negativ, null, baselineDate(ctx))
.then((response) => expectNotenSuccess(response, "übernehmen"));
addPruefung(ctx, student, { note: ctx.gradeNotes[1], datum: attemptDate(ctx, 1) })
.then((response) => {
const [saved] = expectNotenSuccess(response, "Wiederholung");
expect(saved.pruefungstyp_kurzbz, "die Wiederholung ist nicht Antritt 1").to.not.eq(
"Termin1",
);
expect(saved.pruefungstyp_kurzbz, "die Wiederholung ist nicht Antritt 1").to.not.eq("Termin1");
})
.then(() => readState(ctx))
.then(() => readStateViaApi(ctx))
.then((data) => {
const verlauf = verlaufOfStudent(data, student.uid);
expect(verlauf.antrittCount, "die Übernahme und die Prüfung sind zwei Antritte").to.eq(2);
@@ -160,7 +153,7 @@ describe("Noten API - Notenvorschlag", () => {
expect(row.verlauf.antrittCount, "die LV-Note zählt als Antritt 1").to.eq(1);
});
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
expect(attemptsOfStudent(data, student.uid), "kein Termin").to.have.length(0);
});
});
@@ -182,15 +175,17 @@ describe("Noten API - Notenvorschlag", () => {
expectNotenSuccess(response, "erster Termin"),
);
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const attempts = attemptsOfStudent(data, student.uid);
expect(attempts, "Antritt 1 und der neue Termin").to.have.length(2);
expect(String(attempts[0].note), "Antritt 1 trägt die LV-Note").to.eq(String(ctx.notes.negativ));
expect(attempts.map((p) => p.antritt_nr), "Antrittsnummern").to.deep.eq([1, 2]);
expect(
attempts.map((p) => p.antritt_nr),
"Antrittsnummern",
).to.deep.eq([1, 2]);
expect(verlaufOfStudent(data, student.uid).antrittCount, "zwei Antritte").to.eq(2);
});
});
});
// W5: Eine Abfrage liest alle LV-Noten der LV. Das Ergebnis je Studierendem bleibt gleich.
@@ -199,10 +194,10 @@ describe("Noten API - Notenvorschlag", () => {
const [freigegeben, offen, ohneNote] = ctx.students;
resetNotenState(ctx);
seedBaseline(ctx, freigegeben.uid, { freigegeben: true });
seedBaseline(ctx, offen.uid, { freigegeben: false });
seedBaseline(ctx, freigegeben, { freigegeben: true });
seedBaseline(ctx, offen, { freigegeben: false });
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const f = lvNoteOf(data, freigegeben.uid);
expect(f.note_lv, "LV-Note der freigegebenen Zeile").to.exist;
expect(f.freigabedatum, "freigabedatum der freigegebenen Zeile").to.exist;
@@ -224,18 +219,20 @@ describe("Noten API - Notenvorschlag", () => {
it("sperrt die Übernahme", () => {
const student = ctx.students[3];
const g2 =
ctx.notes.positiv ?? ctx.notes.bestnote ?? ctx.gradeNotes.find((n) => String(n) !== String(ctx.notes.negativ));
ctx.notes.positiv ??
ctx.notes.bestnote ??
ctx.gradeNotes.find((n) => String(n) !== String(ctx.notes.negativ));
const g3 = ctx.gradeNotes.find((n) => String(n) !== String(g2));
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { erstantritt: false });
seedBaseline(ctx, student, { erstantritt: false });
let wiederholungId;
seedPruefung(ctx, student, { note: g2, datum: attemptDate(ctx, 1), typ: "Termin2" }).then((seeded) => {
wiederholungId = seeded.pruefungId;
});
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
expect(verlaufOfStudent(data, student.uid).hatWiederholung, "hatWiederholung").to.be.true;
});
@@ -243,7 +240,7 @@ describe("Noten API - Notenvorschlag", () => {
.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, g3, null, baselineDate(ctx))
.then((response) => expectNotenError(response, "c4notenvorschlagGesperrt"));
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const pruefung = attemptsOfStudent(data, student.uid).find(
(p) => String(p.pruefung_id) === String(wiederholungId),
);
@@ -255,17 +252,17 @@ describe("Noten API - Notenvorschlag", () => {
});
});
describe("re-grading an already freigegebene note", () => {
describe("eine bereits freigegebene Note neu benoten", () => {
// eine endgültige Freigabe verbietet genau das, siehe noten.freigabe
beforeEach(function () {
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_FREIGABE_FINAL", false);
});
it("moves the state from freigegeben to changed", () => {
it("wechselt den Status von freigegeben auf geändert", () => {
const student = ctx.students[1];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, {
seedBaseline(ctx, student, {
note: ctx.gradeNotes[0],
freigegeben: true,
erstantritt: false,
@@ -273,7 +270,7 @@ describe("Noten API - Notenvorschlag", () => {
});
// baseline: benotungsdatum == freigabedatum -> freigegeben, not changed
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const grades = lvNoteOf(data, student.uid);
expect(grades.note_lv, "seeded freigegebene note is visible").to.exist;
expect(
@@ -282,18 +279,14 @@ describe("Noten API - Notenvorschlag", () => {
).to.be.false;
});
notenApi
.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.gradeNotes[1])
.then((response) => {
expectNotenSuccess(response, "re-grade after Freigabe");
});
notenApi.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.gradeNotes[1]).then((response) => {
expectNotenSuccess(response, "re-grade after Freigabe");
});
readState(ctx).then((data) => {
readStateViaApi(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(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),
@@ -302,11 +295,11 @@ describe("Noten API - Notenvorschlag", () => {
});
});
it("overwrites the grade rather than adding a second row", () => {
it("überschreibt die Note, statt eine zweite Zeile anzulegen", () => {
const student = ctx.students[1];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, {
seedBaseline(ctx, student, {
note: ctx.gradeNotes[0],
freigegeben: true,
erstantritt: false,
@@ -320,5 +313,4 @@ describe("Noten API - Notenvorschlag", () => {
});
});
});
});
@@ -14,6 +14,7 @@ import {
requireKommissionellerAntritt,
requireKonfiguration,
requireWiederholung,
skipWenn,
} from "../../../../support/helpers/notenConfig";
import {
attemptDate,
@@ -30,7 +31,7 @@ import {
countingAttemptsOfStudent,
editPruefung,
givenBaseline,
readState,
readStateViaApi,
verlaufOfStudent,
} from "../../../../support/helpers/notenScenario";
@@ -47,10 +48,7 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
`entschuldigt=${ctx.notes.entschuldigt}`,
);
expect(
ctx.maxAntritte,
"needs room for at least one retake beyond the first Antritt",
).to.be.greaterThan(1);
expect(ctx.maxAntritte, "needs room for at least one retake beyond the first Antritt").to.be.greaterThan(1);
});
});
@@ -58,11 +56,12 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
const studentFor = (index) => ctx.students[index % ctx.students.length];
/** Skip, wenn der letzte Antritt der Kette nicht der kommissionelle ist. */
const skipOhneKommissionLetzt = (test) => {
if (ctx.cisConfig.CIS_GESAMTNOTE_KOMMISSIONELL_AB_ANTRITT === ctx.maxAntritte) return;
Cypress.log({ name: "skip", message: "Übersprungen: der letzte Antritt ist nicht kommissionell." });
test.skip();
};
const skipOhneKommissionLetzt = (test) =>
skipWenn(
test,
ctx.cisConfig.CIS_GESAMTNOTE_KOMMISSIONELL_AB_ANTRITT !== ctx.maxAntritte,
"Übersprungen: der letzte Antritt ist nicht kommissionell.",
);
/** Adds counting attempts until the cap is reached. Baseline already provides Antritt 1. */
const fillToCap = (student, firstIndex = 1) => {
@@ -80,7 +79,7 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
// Die Konfiguration nennt die Sondernoten nur mit ihrer Bezeichnung. Die API antwortet mit dem
// aufgelösten Primärschlüssel, auf Demodaten ist entschuldigt die 14. Scheitert die Auflösung,
// greifen alle Regeln stillschweigend auf der falschen Note.
it("resolves the special notes from tbl_note by their Bezeichnung", () => {
it("löst die Sondernoten über ihre Bezeichnung aus tbl_note auf", () => {
notenApi.getCisConfig().then((response) => {
const config = expectNotenSuccess(response, "getCisConfig");
@@ -96,8 +95,8 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
});
});
describe("Rule A - maximum number of Prüfungsantritte", () => {
it("rejects an attempt once the configured maximum is reached", function () {
describe("Regel A - die Höchstzahl der Prüfungsantritte", () => {
it("lehnt einen Antritt ab, sobald das konfigurierte Maximum erreicht ist", function () {
// ohne Anlage der kommissionellen Prüfung endet die Kette früher, siehe den Test zu kommPruefNichtErlaubt
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF", true);
@@ -112,17 +111,19 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
});
// and nothing was written for the rejected attempt
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const dates = attemptsOfStudent(data, student.uid).map((p) => String(p.datum).slice(0, 10));
expect(dates, "no row may carry the rejected date").to.not.include(nextDate);
expect(countingAttemptsOfStudent(data, student.uid), "exactly the possible counting rows")
.to.have.length(ctx.maxAntritte);
expect(
countingAttemptsOfStudent(data, student.uid),
"exactly the possible counting rows",
).to.have.length(ctx.maxAntritte);
});
});
});
describe("Rule B - attempts are taken in chronological order", () => {
describe("Regel B - die Antritte folgen der Zeitordnung", () => {
beforeEach(function () {
requireWiederholung(this, ctx);
});
@@ -135,50 +136,46 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
});
};
it("rejects a new attempt dated before an existing one", () => {
it("lehnt einen neuen Antritt vor einem bestehenden ab", () => {
const student = studentFor(1);
givenOpenAttemptOn(student, attemptDate(ctx, 2));
addPruefung(ctx, student, { note: ctx.gradeNotes[0], datum: attemptDate(ctx, 1) }).then(
(response) => {
expectNotenError(response, "pruefungDatumBeforeExisting");
},
);
addPruefung(ctx, student, { note: ctx.gradeNotes[0], datum: attemptDate(ctx, 1) }).then((response) => {
expectNotenError(response, "pruefungDatumBeforeExisting");
});
});
it("accepts a new attempt dated after every existing one", () => {
it("nimmt einen neuen Antritt nach allen bestehenden an", () => {
const student = studentFor(1);
givenOpenAttemptOn(student, attemptDate(ctx, 2));
addPruefung(ctx, student, { note: ctx.gradeNotes[0], datum: attemptDate(ctx, 3) }).then(
(response) => {
expectNotenSuccess(response, "attempt strictly after the existing one");
},
);
addPruefung(ctx, student, { note: ctx.gradeNotes[0], datum: attemptDate(ctx, 3) }).then((response) => {
expectNotenSuccess(response, "attempt strictly after the existing one");
});
});
});
describe("Rule C - 'entschuldigt' may be assigned only once", () => {
describe("Regel C - 'entschuldigt' gilt nur einmal", () => {
beforeEach(function () {
requireWiederholung(this, ctx);
});
it("rejects a second entschuldigt attempt", () => {
it("lehnt einen zweiten entschuldigten Antritt ab", () => {
const student = studentFor(2);
givenBaseline(ctx, student);
addPruefung(ctx, student, { note: ctx.notes.entschuldigt, datum: attemptDate(ctx, 1) }).then(
(response) => expectNotenSuccess(response, "first entschuldigt attempt"),
addPruefung(ctx, student, { note: ctx.notes.entschuldigt, datum: attemptDate(ctx, 1) }).then((response) =>
expectNotenSuccess(response, "first entschuldigt attempt"),
);
addPruefung(ctx, student, { note: ctx.notes.entschuldigt, datum: attemptDate(ctx, 2) }).then(
(response) => expectNotenError(response, "noteOccuranceLimitReached"),
addPruefung(ctx, student, { note: ctx.notes.entschuldigt, datum: attemptDate(ctx, 2) }).then((response) =>
expectNotenError(response, "noteOccuranceLimitReached"),
);
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const excused = attemptsOfStudent(data, student.uid).filter(
(p) => String(p.note) === String(ctx.notes.entschuldigt),
);
@@ -187,30 +184,29 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
});
});
describe("entschuldigt does not consume an attempt", () => {
describe("'entschuldigt' verbraucht keinen Antritt", () => {
beforeEach(function () {
requireWiederholung(this, ctx);
});
it("still accepts a real grade after an excused attempt", () => {
it("nimmt nach einem entschuldigten Antritt weiter eine echte Note an", () => {
const student = studentFor(0);
givenBaseline(ctx, student);
addPruefung(ctx, student, { note: ctx.notes.entschuldigt, datum: attemptDate(ctx, 1) }).then(
(response) => expectNotenSuccess(response, "excused attempt"),
addPruefung(ctx, student, { note: ctx.notes.entschuldigt, datum: attemptDate(ctx, 1) }).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) }).then(
(response) =>
expectNotenSuccess(
response,
"a real grade after an excused attempt (excused must not count towards the cap)",
),
addPruefung(ctx, student, { note: ctx.gradeNotes[0], datum: attemptDate(ctx, 2) }).then((response) =>
expectNotenSuccess(
response,
"a real grade after an excused attempt (excused must not count towards the cap)",
),
);
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
expect(
verlaufOfStudent(data, student.uid).antrittCount,
"baseline + the real grade, the excused one not counted",
@@ -233,8 +229,8 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
// bis zum vorletzten Antritt auffüllen
for (let i = 1; i < ctx.maxAntritte - 1; i += 1) {
addPruefung(ctx, student, { note: ctx.notes.negativ, datum: attemptDate(ctx, i) }).then(
(response) => expectNotenSuccess(response, `Antritt ${i + 1}`),
addPruefung(ctx, student, { note: ctx.notes.negativ, datum: attemptDate(ctx, i) }).then((response) =>
expectNotenSuccess(response, `Antritt ${i + 1}`),
);
}
@@ -248,7 +244,7 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
);
});
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const verlauf = verlaufOfStudent(data, student.uid);
expect(verlauf.antrittCount, "die kommissionelle zählt als Antritt").to.eq(ctx.maxAntritte);
expect(verlauf.canAdd, "danach ist kein Antritt mehr möglich").to.be.false;
@@ -267,12 +263,12 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
// bis zum vorletzten Antritt auffüllen; danach wäre der kommissionelle fällig
for (let i = 1; i < ctx.maxAntritte - 1; i += 1) {
addPruefung(ctx, student, { note: ctx.notes.negativ, datum: attemptDate(ctx, i) }).then(
(response) => expectNotenSuccess(response, `Antritt ${i + 1}`),
addPruefung(ctx, student, { note: ctx.notes.negativ, datum: attemptDate(ctx, i) }).then((response) =>
expectNotenSuccess(response, `Antritt ${i + 1}`),
);
}
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const verlauf = verlaufOfStudent(data, student.uid);
expect(verlauf.kommPruefGesperrt, "der Verlauf nennt den Grund").to.be.true;
expect(verlauf.canAdd, "und lässt keinen Antritt mehr zu").to.be.false;
@@ -297,19 +293,19 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
typ: "kommPruef",
});
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const verlauf = verlaufOfStudent(data, student.uid);
expect(verlauf.terminal, "die Kette ist geschlossen").to.be.true;
expect(verlauf.canAdd, "kein weiterer Antritt möglich").to.be.false;
});
addPruefung(ctx, student, { note: ctx.gradeNotes[0], datum: attemptDate(ctx, 2) }).then(
(response) => expectNotenError(response, "maxAntritteReached"),
addPruefung(ctx, student, { note: ctx.gradeNotes[0], datum: attemptDate(ctx, 2) }).then((response) =>
expectNotenError(response, "maxAntritteReached"),
);
});
});
describe("'Noch nicht eingetragen' does not consume an attempt", () => {
describe("'Noch nicht eingetragen' verbraucht keinen Antritt", () => {
beforeEach(function () {
requireWiederholung(this, ctx);
});
@@ -322,7 +318,7 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
(response) => expectNotenSuccess(response, "offener Termin"),
);
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const offen = attemptsOfStudent(data, student.uid).find(
(p) => String(p.note) === String(ctx.notes.nochNichtEingetragen),
);
@@ -336,20 +332,17 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
});
});
describe("Anrechnung - no Prüfungen at all", () => {
it("refuses an attempt while the Zeugnisnote is angerechnet", function () {
describe("Anrechnung - gar keine Prüfungen", () => {
it("lehnt einen Antritt bei angerechneter Zeugnisnote ab", function () {
const angerechnet = (ctx.cisConfig.NOTEN_ANRECHNUNG || [])[0];
if (angerechnet === undefined) {
Cypress.log({ name: "skip", message: "Übersprungen: NOTEN_ANRECHNUNG löst keine Note auf." });
this.skip();
}
skipWenn(this, angerechnet === undefined, "Übersprungen: NOTEN_ANRECHNUNG löst keine Note auf.");
const student = studentFor(4);
givenBaseline(ctx, student);
seedZeugnisnote(ctx, student.uid, angerechnet);
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const verlauf = verlaufOfStudent(data, student.uid);
expect(verlauf.angerechnet, "the Verlauf marks the Anrechnung").to.be.true;
expect(verlauf.canAdd, "and blocks further attempts").to.be.false;
@@ -361,7 +354,7 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
});
});
describe("edit guards", () => {
describe("Schutz beim Bearbeiten", () => {
beforeEach(function () {
requireWiederholung(this, ctx);
});
@@ -373,7 +366,7 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
addPruefung(ctx, student, { note: ctx.notes.entschuldigt, datum: attemptDate(ctx, 1) });
addPruefung(ctx, student, { note: ctx.gradeNotes[0], datum: attemptDate(ctx, 2) });
return readState(ctx).then((data) => {
return readStateViaApi(ctx).then((data) => {
const excused = attemptsOfStudent(data, student.uid).find(
(p) => String(p.note) === String(ctx.notes.entschuldigt),
);
@@ -382,7 +375,7 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
});
};
it("rejects changing the grade once a later attempt exists", function () {
it("lehnt eine Notenänderung nach einem späteren Antritt ab", function () {
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_NOTE_SPERRE_BEI_SPAETEREM_TERMIN", true);
const student = studentFor(1);
@@ -396,7 +389,7 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
});
});
it("still allows a date-only correction between the neighbouring attempts", () => {
it("erlaubt weiter eine reine Datumskorrektur zwischen den Nachbarantritten", () => {
const student = studentFor(1);
givenThreeAttempts(student).then((excused) => {
@@ -407,7 +400,7 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
datum: shiftDate(attemptDate(ctx, 1), 3),
})
.then((response) => expectNotenSuccess(response, "date-only correction inside the bounds"))
.then(() => readState(ctx))
.then(() => readStateViaApi(ctx))
.then((data) => {
const moved = attemptsOfStudent(data, student.uid).find(
(p) => p.pruefung_id === excused.pruefung_id,
@@ -420,7 +413,7 @@ describe("Noten API - Prüfungsantritte (Prüfungsordnung §1)", () => {
});
});
it("rejects a date on or before the previous attempt", () => {
it("lehnt ein Datum am oder vor dem vorherigen Antritt ab", () => {
const student = studentFor(2);
givenThreeAttempts(student).then((excused) => {
@@ -12,7 +12,7 @@ import {
attemptDate,
baselineDate,
loadNotenContext,
readLvGesamtnote,
readLvGesamtnoteViaDb,
requireDbReset,
resetNotenState,
seedPruefung,
@@ -23,14 +23,14 @@ import {
attemptsOfStudent,
editPruefung,
givenBaseline,
readState,
readStateViaApi,
verlaufOfStudent,
} from "../../../../support/helpers/notenScenario";
import { notenApi, pruefungenOf } from "../../../../support/api/notenApi";
const dayOf = (value) => String(value).slice(0, 10);
describe("Noten API - Prüfungstermin (write path)", () => {
describe("Noten API - Prüfungstermin (Schreibpfad)", () => {
let ctx;
before(() => {
@@ -43,7 +43,7 @@ describe("Noten API - Prüfungstermin (write path)", () => {
const studentFor = (index) => ctx.students[index % ctx.students.length];
it("writes exactly one Prüfung per add - no snapshot alongside it", function () {
it("schreibt je Anlage genau eine Prüfung, ohne Kopie daneben", function () {
requireWiederholung(this, ctx);
const student = studentFor(0);
@@ -60,14 +60,14 @@ describe("Noten API - Prüfungstermin (write path)", () => {
expect(verlauf, "verlauf is returned").to.exist;
expect(verlauf.pruefungen, "verlauf carries the attempts").to.be.an("array");
})
.then(() => readState(ctx))
.then(() => readStateViaApi(ctx))
.then((data) => {
const attempts = attemptsOfStudent(data, student.uid);
expect(attempts, "baseline Antritt 1 plus exactly one added row").to.have.length(2);
});
});
it("derives position and Antrittsnummer server-side", function () {
it("leitet Position und Antrittsnummer auf dem Server ab", function () {
requireWiederholung(this, ctx);
const student = studentFor(1);
@@ -75,26 +75,33 @@ describe("Noten API - Prüfungstermin (write path)", () => {
givenBaseline(ctx, student);
addPruefung(ctx, student, { note: ctx.notes.negativ, datum: attemptDate(ctx, 1) })
.then(() => readState(ctx))
.then(() => readStateViaApi(ctx))
.then((data) => {
const attempts = attemptsOfStudent(data, student.uid);
expect(attempts.map((p) => p.position), "positions are 1..n in date order").to.deep.eq([1, 2]);
expect(attempts.map((p) => p.antritt_nr), "both count as attempts").to.deep.eq([1, 2]);
expect(
attempts.map((p) => p.position),
"positions are 1..n in date order",
).to.deep.eq([1, 2]);
expect(
attempts.map((p) => p.antritt_nr),
"both count as attempts",
).to.deep.eq([1, 2]);
attempts.forEach((p) => expect(p.zaehlt, `zaehlt of position ${p.position}`).to.be.true);
const verlauf = verlaufOfStudent(data, student.uid);
expect(verlauf.antrittCount, "both attempts counted").to.eq(2);
expect(verlauf.maxAntritte, "the cap comes from the server").to.eq(ctx.maxAntritte);
// der letzte Antritt ist kommissionell; darf das Tool ihn nicht anlegen, ist hier Schluss
const anlegbar = ctx.cisConfig.CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF !== false
? ctx.maxAntritte
: ctx.maxAntritte - 1;
const anlegbar =
ctx.cisConfig.CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF !== false
? ctx.maxAntritte
: ctx.maxAntritte - 1;
expect(verlauf.canAdd, `canAdd with 2 of ${anlegbar} possible`).to.eq(2 < anlegbar);
});
});
it("appends a new row per add instead of overwriting the previous one", function () {
it("hängt je Anlage eine neue Zeile an, statt die vorherige zu überschreiben", function () {
requireWiederholung(this, ctx);
// nur eine explizite pruefung_id aktualisiert, ein Add fügt immer ein
@@ -109,16 +116,19 @@ describe("Noten API - Prüfungstermin (write path)", () => {
return addPruefung(ctx, student, { note: ctx.gradeNotes[1], datum: attemptDate(ctx, 2) });
})
.then((response) => expectNotenSuccess(response, "second add"))
.then(() => readState(ctx))
.then(() => readStateViaApi(ctx))
.then((data) => {
const attempts = attemptsOfStudent(data, student.uid);
expect(attempts, "baseline + two added rows").to.have.length(3);
expect(attempts.map((p) => p.pruefung_id), "the first add still exists").to.include(firstId);
expect(
attempts.map((p) => p.pruefung_id),
"the first add still exists",
).to.include(firstId);
});
});
it("edits the pruefung identified by pruefung_id", () => {
it("bearbeitet die Prüfung mit der übergebenen pruefung_id", () => {
// An excused and a real attempt side by side - no endpoint builds this, hence the direct seed
const student = studentFor(2);
const excusedDate = attemptDate(ctx, 1);
@@ -142,7 +152,7 @@ describe("Noten API - Prüfungstermin (write path)", () => {
}),
)
.then((response) => expectNotenSuccess(response, "moving the excused attempt"))
.then(() => readState(ctx))
.then(() => readStateViaApi(ctx))
.then((data) => {
const rows = pruefungenOf(data, student.uid);
const excused = rows.find((p) => p.pruefung_id === excusedId);
@@ -150,16 +160,14 @@ describe("Noten API - Prüfungstermin (write path)", () => {
// the untargeted row first: silently rewriting a real grade is the worse outcome
expect(graded, "the other attempt still exists").to.exist;
expect(String(graded.note), "the untargeted attempt keeps its grade").to.eq(
String(ctx.gradeNotes[0]),
);
expect(String(graded.note), "the untargeted attempt keeps its grade").to.eq(String(ctx.gradeNotes[0]));
expect(excused, "the excused attempt still exists").to.exist;
expect(dayOf(excused.datum), "the row named by pruefung_id moved").to.eq(movedTo);
});
});
it("writes the LV note from the attempt's grade", function () {
it("schreibt die LV-Note aus der Note des Antritts", function () {
requireWiederholung(this, ctx);
const student = studentFor(3);
@@ -168,11 +176,9 @@ describe("Noten API - Prüfungstermin (write path)", () => {
addPruefung(ctx, student, { note: ctx.gradeNotes[1], datum: attemptDate(ctx, 1) })
.then((response) => expectNotenSuccess(response, "attempt with a grade"))
.then(() => readLvGesamtnote(ctx, student.uid))
.then(() => readLvGesamtnoteViaDb(ctx, student.uid))
.then((row) => {
expect(String(row.note), "the LV note follows the newest attempt").to.eq(
String(ctx.gradeNotes[1]),
);
expect(String(row.note), "the LV note follows the newest attempt").to.eq(String(ctx.gradeNotes[1]));
});
});
@@ -188,12 +194,15 @@ describe("Noten API - Prüfungstermin (write path)", () => {
expectNotenSuccess(response, "erster Termin neben der LV-Note"),
);
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const attempts = attemptsOfStudent(data, student.uid);
expect(attempts, "Antritt 1 und der neue Termin").to.have.length(2);
expect(String(attempts[0].note), "Antritt 1 trägt die LV-Note").to.eq(String(ctx.notes.negativ));
expect(dayOf(attempts[0].datum) < attemptDate(ctx, 1), "Antritt 1 liegt vor dem neuen Termin").to.be.true;
expect(attempts.map((p) => p.antritt_nr), "Antrittsnummern").to.deep.eq([1, 2]);
expect(
attempts.map((p) => p.antritt_nr),
"Antrittsnummern",
).to.deep.eq([1, 2]);
expect(verlaufOfStudent(data, student.uid).antrittCount, "zwei Antritte").to.eq(2);
});
});
@@ -203,16 +212,18 @@ describe("Noten API - Prüfungstermin (write path)", () => {
// Die LV-Note ist nie 'entschuldigt'.
describe("LV-Note nach einem Termin", () => {
const g2 = () =>
ctx.notes.positiv ?? ctx.notes.bestnote ?? ctx.gradeNotes.find((n) => String(n) !== String(ctx.notes.negativ));
ctx.notes.positiv ??
ctx.notes.bestnote ??
ctx.gradeNotes.find((n) => String(n) !== String(ctx.notes.negativ));
const expectLvNote = (student, note, message) =>
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((row) => {
expect(String(row.note), message).to.eq(String(note));
return row;
});
const expectAntritte = (student, anzahl) =>
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
expect(verlaufOfStudent(data, student.uid).antrittCount, "antrittCount").to.eq(anzahl);
});
@@ -226,7 +237,7 @@ describe("Noten API - Prüfungstermin (write path)", () => {
expectNotenSuccess(response, "Termin 2"),
);
readState(ctx)
readStateViaApi(ctx)
.then((data) => {
const t1 = attemptsOfStudent(data, student.uid)[0];
// die Note bleibt gleich, sonst meldet validateEdit pruefungNoteLocked
@@ -269,7 +280,7 @@ describe("Noten API - Prüfungstermin (write path)", () => {
expect(row.punkte, "ohne Punkte").to.be.null;
});
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const verlauf = verlaufOfStudent(data, student.uid);
expect(verlauf.antrittCount, "der entschuldigte Termin verbraucht keinen Antritt").to.eq(0);
expect(verlauf.canAdd, "ein weiterer Termin ist möglich").to.be.true;
@@ -279,7 +290,7 @@ describe("Noten API - Prüfungstermin (write path)", () => {
expectNotenSuccess(response, "erster zählender Termin"),
);
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const zaehlend = attemptsOfStudent(data, student.uid).find(
(p) => String(p.note) === String(ctx.notes.negativ),
);
@@ -292,7 +303,7 @@ describe("Noten API - Prüfungstermin (write path)", () => {
givenBaseline(ctx, student);
readState(ctx)
readStateViaApi(ctx)
.then((data) => {
const t1 = attemptsOfStudent(data, student.uid)[0];
return editPruefung(ctx, student, {
@@ -318,7 +329,7 @@ describe("Noten API - Prüfungstermin (write path)", () => {
expectNotenSuccess(response, "Termin 2"),
);
readState(ctx)
readStateViaApi(ctx)
.then((data) => {
const t2 = attemptsOfStudent(data, student.uid)[1];
return editPruefung(ctx, student, {
@@ -385,7 +396,7 @@ describe("Noten API - Prüfungstermin (write path)", () => {
addPruefung(ctx, student, { note: ctx.notes.negativ, datum: attemptDate(ctx, 1) }).then((response) =>
expectNotenSuccess(response, "Termin nach der Freigabe"),
);
return readLvGesamtnote(ctx, student.uid);
return readLvGesamtnoteViaDb(ctx, student.uid);
};
it("hebt die Freigabe mit einem neuen Termin auf", function () {
@@ -407,7 +418,7 @@ describe("Noten API - Prüfungstermin (write path)", () => {
});
});
it("creates the LV note when a student has none yet", () => {
it("legt die LV-Note an, wenn ein Studierender noch keine hat", () => {
// der Bulk-Pfad läuft durch denselben Kern und schreibt die LV-Note vor dem Termin
const student = studentFor(0);
@@ -425,13 +436,10 @@ describe("Noten API - Prüfungstermin (write path)", () => {
expect(data[student.uid], `row result for ${student.uid}`).to.be.an("object");
expect(data[student.uid].savedPruefung, "the Prüfung was written").to.exist;
})
.then(() => readLvGesamtnote(ctx, student.uid))
.then(() => readLvGesamtnoteViaDb(ctx, student.uid))
.then((row) => {
expect(row, "the LV note was created alongside").to.not.be.null;
expect(String(row.note), "carries the Prüfung's note").to.eq(
String(ctx.notes.nochNichtEingetragen),
);
expect(String(row.note), "carries the Prüfung's note").to.eq(String(ctx.notes.nochNichtEingetragen));
});
});
});
@@ -8,19 +8,29 @@
*/
import { expectBulkRowError, expectNotenError, expectNotenSuccess } from "../../../../support/helpers/notenErrors";
import { requireKonfiguration, requireWiederholung } from "../../../../support/helpers/notenConfig";
import {
requireKonfiguration,
requireNotenModus,
requireWiederholung,
skipWenn,
} from "../../../../support/helpers/notenConfig";
import {
attemptDate,
loadNotenContext,
readLvGesamtnote,
readLvGesamtnoteViaDb,
requireDbReset,
resetNotenState,
seedZeugnisnote,
} from "../../../../support/helpers/notenTestData";
import { addPruefung, attemptsOfStudent, givenBaseline, readState } from "../../../../support/helpers/notenScenario";
import {
addPruefung,
attemptsOfStudent,
givenBaseline,
readStateViaApi,
} from "../../../../support/helpers/notenScenario";
import { notenApi } from "../../../../support/api/notenApi";
describe("Noten API - Notenvorschlag overwrite rules", () => {
describe("Noten API - Regeln zum Überschreiben des Notenvorschlags", () => {
let ctx;
before(() => {
@@ -32,11 +42,10 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
const studentFor = (index) => ctx.students[index % ctx.students.length];
it("refuses a note the editor list never offers", function () {
it("lehnt eine Note ab, die die Auswahlliste nicht anbietet", function () {
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_VORSCHLAG_NUR_LEHRENOTEN", true);
if (ctx.notes.nichtLehre === null) {
this.skip(); // every active note is a lehre note on this instance
}
// auf dieser Instanz kann jede aktive Note eine Lehrenote sein
skipWenn(this, ctx.notes.nichtLehre === null, "Übersprungen: keine Verwaltungsnote in tbl_note.");
const student = studentFor(0);
@@ -46,7 +55,7 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
notenApi
.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.notes.nichtLehre)
.then((response) => expectNotenError(response, "c4noteNichtInLehre"))
.then(() => readLvGesamtnote(ctx, student.uid))
.then(() => readLvGesamtnoteViaDb(ctx, student.uid))
.then((row) => {
expect(String(row.note), "a non-lehre note must not become the LV note").to.eq(
String(ctx.notes.negativ),
@@ -54,7 +63,7 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
});
});
it("refuses to change the Notenvorschlag once a Prüfung exists", function () {
it("lehnt eine Änderung des Notenvorschlags nach einer Prüfung ab", function () {
requireWiederholung(this, ctx);
const student = studentFor(1);
@@ -69,18 +78,15 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
notenApi
.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.gradeNotes[0])
.then((response) => expectNotenError(response, "c4notenvorschlagGesperrt"))
.then(() => readLvGesamtnote(ctx, student.uid))
.then(() => readLvGesamtnoteViaDb(ctx, student.uid))
.then((row) => {
expect(String(row.note), "the attempt's grade must survive the call").to.eq(
String(ctx.gradeNotes[1]),
);
expect(String(row.note), "the attempt's grade must survive the call").to.eq(String(ctx.gradeNotes[1]));
});
});
it("refuses to overwrite a locked Zeugnisnote", function () {
if (ctx.notes.nichtUeberschreibbar === null) {
this.skip(); // every active note allows the teacher to overwrite it on this instance
}
it("lehnt das Überschreiben einer gesperrten Zeugnisnote ab", function () {
// auf dieser Instanz darf die Lehrperson jede aktive Note überschreiben
skipWenn(this, ctx.notes.nichtUeberschreibbar === null, "Übersprungen: keine gesperrte Note in tbl_note.");
const student = studentFor(4);
@@ -90,7 +96,7 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
notenApi
.saveNotenvorschlag(ctx.lvId, ctx.semKurzbz, student.uid, ctx.gradeNotes[1])
.then((response) => expectNotenError(response, "c4zeugnisnoteGesperrt"))
.then(() => readLvGesamtnote(ctx, student.uid))
.then(() => readLvGesamtnoteViaDb(ctx, student.uid))
.then((row) => {
expect(String(row.note), "the LV note must not change").to.eq(String(ctx.notes.negativ));
});
@@ -103,14 +109,13 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
requireKonfiguration(test, ctx, "CIS_GESAMTNOTE_VORSCHLAG_NUR_LEHRENOTEN", true);
const ohneAntritt = (ctx.cisConfig.NOTEN_OHNE_ANTRITT || []).map(String);
if (ctx.notes.nichtLehre !== null && !ohneAntritt.includes(String(ctx.notes.nichtLehre))) return;
const brauchbar = ctx.notes.nichtLehre !== null && !ohneAntritt.includes(String(ctx.notes.nichtLehre));
Cypress.log({ name: "skip", message: "Übersprungen: keine Verwaltungsnote, die einen Antritt verbraucht." });
test.skip();
skipWenn(test, !brauchbar, "Übersprungen: keine Verwaltungsnote, die einen Antritt verbraucht.");
};
const expectNurAntritt1 = (student) =>
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
expect(attemptsOfStudent(data, student.uid), "nur Antritt 1").to.have.length(1);
});
@@ -126,7 +131,7 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
);
expectNurAntritt1(student);
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((row) => {
expect(String(row.note), "die LV-Note bleibt").to.eq(String(ctx.notes.negativ));
});
});
@@ -139,10 +144,7 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
const gesperrt = ctx.notenOptions.find(
(n) => n.lkt_ueberschreibbar === false && !anrechnung.includes(String(n.note)),
);
if (!gesperrt) {
Cypress.log({ name: "skip", message: "Übersprungen: keine gesperrte Note ausser den Anrechnungsnoten." });
this.skip();
}
skipWenn(this, !gesperrt, "Übersprungen: keine gesperrte Note ausser den Anrechnungsnoten.");
const student = studentFor(1);
@@ -177,7 +179,7 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
});
students.forEach((s) =>
readLvGesamtnote(ctx, s.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, s.uid).then((row) => {
expect(row, `keine LV-Note für ${s.uid}`).to.be.null;
}),
);
@@ -188,25 +190,20 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
// antwortet 200 und meldet die abgelehnte Zeile in data[uid].
describe("saveNotenvorschlagBulk", () => {
beforeEach(function () {
if (ctx.cisConfig.CIS_GESAMTNOTE_PUNKTE) {
// im Punktemodus leitet der Import die Note aus den Punkten ab und verwirft eine
// Zeile ohne Punkte, bevor eine dieser Regeln greift
Cypress.log({ name: "skip", message: "Skipped: CIS_GESAMTNOTE_PUNKTE ist aktiv." });
this.skip();
}
// im Punktemodus leitet der Import die Note aus den Punkten ab und verwirft eine
// Zeile ohne Punkte, bevor eine dieser Regeln greift
requireNotenModus(this, ctx);
requireWiederholung(this, ctx);
});
it("refuses to change the Notenvorschlag once a Prüfung exists", () => {
it("lehnt je Zeile eine Änderung des Notenvorschlags nach einer Prüfung ab", () => {
const student = studentFor(3);
givenBaseline(ctx, student);
addPruefung(ctx, student, { note: ctx.gradeNotes[1], datum: attemptDate(ctx, 1) }).then(
(response) => {
expectNotenSuccess(response, "seed a Prüfung");
},
);
addPruefung(ctx, student, { note: ctx.gradeNotes[1], datum: attemptDate(ctx, 1) }).then((response) => {
expectNotenSuccess(response, "seed a Prüfung");
});
notenApi
.saveNotenvorschlagBulk(ctx.lvId, ctx.semKurzbz, [
@@ -216,7 +213,7 @@ describe("Noten API - Notenvorschlag overwrite rules", () => {
const data = expectNotenSuccess(response, "saveNotenvorschlagBulk");
expectBulkRowError(data, student.uid, "c4notenvorschlagGesperrt");
})
.then(() => readLvGesamtnote(ctx, student.uid))
.then(() => readLvGesamtnoteViaDb(ctx, student.uid))
.then((row) => {
expect(String(row.note), "the attempt's grade must survive the import").to.eq(
String(ctx.gradeNotes[1]),
@@ -3,7 +3,7 @@ import { requireKonfiguration } from "../../../../support/helpers/notenConfig";
import {
baselineDate,
loadNotenContext,
readLvGesamtnote,
readLvGesamtnoteViaDb,
requireDbReset,
resetNotenState,
seedBaseline,
@@ -37,7 +37,7 @@ context("Benotungstool UI - Notenfreigabe", () => {
const student = ctx.students[0];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.gradeNotes[0], freigegeben: false });
seedBaseline(ctx, student, { note: ctx.gradeNotes[0], freigegeben: false });
page.visitAndWaitForTable(ctx);
page.expectFreigabeState(student.uid, "changed");
@@ -53,7 +53,7 @@ context("Benotungstool UI - Notenfreigabe", () => {
const student = ctx.students[0];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.gradeNotes[0], freigegeben: false });
seedBaseline(ctx, student, { note: ctx.gradeNotes[0], freigegeben: false });
page.visitAndWaitForTable(ctx);
page.openFreigabeModal();
@@ -65,7 +65,7 @@ context("Benotungstool UI - Notenfreigabe", () => {
page.expectAbgelehnt("@saveStudentenNoten");
page.expectFreigabeState(student.uid, "changed");
readLvGesamtnote(ctx, student.uid).then((rowData) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((rowData) => {
expect(rowData.freigabedatum, "eine abgelehnte Freigabe stempelt nichts").to.be.null;
});
});
@@ -76,7 +76,7 @@ context("Benotungstool UI - Notenfreigabe", () => {
const student = ctx.students[0];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.gradeNotes[0], freigegeben: false });
seedBaseline(ctx, student, { note: ctx.gradeNotes[0], freigegeben: false });
page.visitAndWaitForTable(ctx);
page.expectFreigabeState(student.uid, "changed");
@@ -94,7 +94,7 @@ context("Benotungstool UI - Notenfreigabe", () => {
const student = ctx.students[1];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.gradeNotes[0], freigegeben: false });
seedBaseline(ctx, student, { note: ctx.gradeNotes[0], freigegeben: false });
page.visitAndWaitForTable(ctx);
page.openFreigabeModal();
@@ -1,5 +1,10 @@
import { benotungstoolPage as page } from "../../../../support/pages/benotungstool.po";
import { requireKonfiguration, requireWiederholung } from "../../../../support/helpers/notenConfig";
import {
requireKonfiguration,
requireNotenModus,
requireWiederholung,
skipWenn,
} from "../../../../support/helpers/notenConfig";
import {
attemptDate,
loadNotenContext,
@@ -30,11 +35,8 @@ context("Benotungstool UI - Import", () => {
});
beforeEach(function () {
if (ctx.cisConfig.CIS_GESAMTNOTE_PUNKTE) {
// im Punktemodus erwarten beide Importe Punkte statt einer Note
Cypress.log({ name: "skip", message: "Skipped: CIS_GESAMTNOTE_PUNKTE ist aktiv." });
this.skip();
}
// im Punktemodus erwarten beide Importe Punkte statt einer Note
requireNotenModus(this, ctx);
});
/**
@@ -45,7 +47,10 @@ context("Benotungstool UI - Import", () => {
const kuerzelNote = () => {
if (!ctx.cisConfig.CIS_GESAMTNOTE_IMPORT_NOTENKUERZEL) return null;
const kuerzelVon = (n) => String(n.anmerkung ?? "").trim().toLowerCase();
const kuerzelVon = (n) =>
String(n.anmerkung ?? "")
.trim()
.toLowerCase();
return (ctx.notenOptions ?? []).find((n) => {
const k = kuerzelVon(n);
@@ -60,10 +65,7 @@ context("Benotungstool UI - Import", () => {
describe("Notenimport", () => {
beforeEach(function () {
if (!ctx.cisConfig.CIS_GESAMTNOTE_NOTENIMPORT) {
Cypress.log({ name: "skip", message: "Skipped: CIS_GESAMTNOTE_NOTENIMPORT ist aus, der Button existiert nicht." });
this.skip();
}
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_NOTENIMPORT", true);
});
it("schreibt die LV-Note für jede Zeile", () => {
@@ -102,10 +104,7 @@ context("Benotungstool UI - Import", () => {
it("nimmt das Kürzel aus der Notenliste, sobald die Option das erlaubt", function () {
const kuerzel = kuerzelNote();
if (!kuerzel) {
Cypress.log({ name: "skip", message: "Skipped: keine Note mit brauchbarem Kürzel in tbl_note.anmerkung." });
this.skip();
}
skipWenn(this, !kuerzel, "Übersprungen: keine Note mit brauchbarem Kürzel in tbl_note.anmerkung.");
const student = ctx.students[1];
@@ -120,10 +119,7 @@ context("Benotungstool UI - Import", () => {
describe("Prüfungsimport", () => {
beforeEach(function () {
if (!ctx.cisConfig.CIS_GESAMTNOTE_PRUEFUNGSIMPORT) {
Cypress.log({ name: "skip", message: "Skipped: CIS_GESAMTNOTE_PRUEFUNGSIMPORT ist aus, der Button existiert nicht." });
this.skip();
}
requireKonfiguration(this, ctx, "CIS_GESAMTNOTE_PRUEFUNGSIMPORT", true);
});
it("legt je Zeile einen datierten Antritt an", function () {
@@ -134,8 +130,8 @@ context("Benotungstool UI - Import", () => {
const datum = page.importDatum(attemptDate(ctx, 1), ctx.cisConfig.CIS_GESAMTNOTE_IMPORT_DATUMSFORMAT);
resetNotenState(ctx);
seedBaseline(ctx, a.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, b.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, a, { note: ctx.notes.negativ });
seedBaseline(ctx, b, { note: ctx.notes.negativ });
page.visitAndWaitForTable(ctx);
@@ -157,11 +153,13 @@ context("Benotungstool UI - Import", () => {
resetNotenState(ctx);
page.visitAndWaitForTable(ctx);
page.importPruefungen([[
student.uid,
page.importDatum(attemptDate(ctx, 1), ctx.cisConfig.CIS_GESAMTNOTE_IMPORT_DATUMSFORMAT),
ctx.gradeNotes[0],
]]);
page.importPruefungen([
[
student.uid,
page.importDatum(attemptDate(ctx, 1), ctx.cisConfig.CIS_GESAMTNOTE_IMPORT_DATUMSFORMAT),
ctx.gradeNotes[0],
],
]);
page.expectPruefung(student.uid, "antritt_1", { note: ctx.gradeNotes[0], antritt: 1 });
page.expectLvNote(student.uid, bezeichnung(ctx.gradeNotes[0]));
@@ -185,8 +183,8 @@ context("Benotungstool UI - Import", () => {
expect(perMatrikelnr.matrikelnr, "Matrikelnummer des Studierenden").to.be.a("string").and.not.be.empty;
resetNotenState(ctx);
seedBaseline(ctx, perUid.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, perMatrikelnr.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, perUid, { note: ctx.notes.negativ });
seedBaseline(ctx, perMatrikelnr, { note: ctx.notes.negativ });
page.visitAndWaitForTable(ctx);
page.importPruefungen([
@@ -205,7 +203,10 @@ context("Benotungstool UI - Import", () => {
page.expectWarnungen(4);
page.gesendeteUids("@savePruefungenBulk", "pruefungen").then((uids) => {
expect(uids, "nur die gültigen Zeilen, die Matrikelnummer aufgelöst").to.deep.eq([perUid.uid, perMatrikelnr.uid]);
expect(uids, "nur die gültigen Zeilen, die Matrikelnummer aufgelöst").to.deep.eq([
perUid.uid,
perMatrikelnr.uid,
]);
});
});
@@ -1,10 +1,10 @@
import { benotungstoolPage as page } from "../../../../support/pages/benotungstool.po";
import { requireKonfiguration } from "../../../../support/helpers/notenConfig";
import { requireKonfiguration, requireNotenModus } from "../../../../support/helpers/notenConfig";
import {
attemptDate,
baselineDate,
loadNotenContext,
readLvGesamtnote,
readLvGesamtnoteViaDb,
requireDbReset,
resetNotenState,
seedBaseline,
@@ -31,11 +31,8 @@ context("Benotungstool UI - Notenvorschlag", () => {
});
beforeEach(function () {
if (ctx.cisConfig.CIS_GESAMTNOTE_PUNKTE) {
// im Punktemodus ist die Vorschlagsspalte gesperrt, die Note kommt aus den Punkten
Cypress.log({ name: "skip", message: "Skipped: CIS_GESAMTNOTE_PUNKTE ist aktiv." });
this.skip();
}
// im Punktemodus ist die Vorschlagsspalte gesperrt, die Note kommt aus den Punkten
requireNotenModus(this, ctx);
});
it("trägt einen Vorschlag über den Zelleneditor ein", () => {
@@ -84,9 +81,10 @@ context("Benotungstool UI - Notenvorschlag", () => {
page.expectLvNote(student.uid, bezeichnung(ctx.gradeNotes[0]));
page.getCell(student.uid, "antritt_1").should("contain.text", page.toDDMMYYYY(datum));
readLvGesamtnote(ctx, student.uid).then((rowData) => {
expect(String(rowData.benotungsdatum).slice(0, 10), "das benotungsdatum bleibt heute")
.to.eq(new Date().toISOString().slice(0, 10));
readLvGesamtnoteViaDb(ctx, student.uid).then((rowData) => {
expect(String(rowData.benotungsdatum).slice(0, 10), "das benotungsdatum bleibt heute").to.eq(
new Date().toISOString().slice(0, 10),
);
});
});
@@ -97,11 +95,13 @@ context("Benotungstool UI - Notenvorschlag", () => {
const kontrolle = ctx.students[4];
// Sehr Gut zuerst: setNotenvorschlag vergleicht mit contains, "Gut" träfe "Sehr Gut"
const vorschlag = ctx.gradeNotes[0];
const pruefungsnote = ctx.gradeNotes.find((n) => ![vorschlag, ctx.notes.negativ].map(String).includes(String(n)));
const pruefungsnote = ctx.gradeNotes.find(
(n) => ![vorschlag, ctx.notes.negativ].map(String).includes(String(n)),
);
resetNotenState(ctx);
seedBaseline(ctx, wiederholung.uid, { erstantritt: false });
seedBaseline(ctx, kontrolle.uid);
seedBaseline(ctx, wiederholung, { erstantritt: false });
seedBaseline(ctx, kontrolle);
seedPruefung(ctx, wiederholung, { note: pruefungsnote, datum: attemptDate(ctx, 1), typ: "Termin2" });
page.visitAndWaitForTable(ctx);
@@ -123,11 +123,13 @@ context("Benotungstool UI - Notenvorschlag", () => {
page.getCell(student.uid, "note_vorschlag").click();
// exakter Vergleich: "unentschuldigt" enthält "entschuldigt"
cy.get(".tabulator-edit-list-item").should("have.length.greaterThan", 0).then((items) => {
const labels = [...items].map((item) => item.innerText.trim());
expect(labels, "Optionen des Editors").to.include(bezeichnung(ctx.gradeNotes[0]));
expect(labels, "Optionen des Editors").to.not.include(bezeichnung(ctx.notes.entschuldigt));
});
cy.get(".tabulator-edit-list-item")
.should("have.length.greaterThan", 0)
.then((items) => {
const labels = [...items].map((item) => item.innerText.trim());
expect(labels, "Optionen des Editors").to.include(bezeichnung(ctx.gradeNotes[0]));
expect(labels, "Optionen des Editors").to.not.include(bezeichnung(ctx.notes.entschuldigt));
});
});
it("blendet den Übernehmen-Button aus, sobald Vorschlag und LV-Note übereinstimmen", () => {
@@ -141,5 +143,4 @@ context("Benotungstool UI - Notenvorschlag", () => {
page.getUebernehmenButton(student.uid).should("not.exist");
});
});
@@ -3,14 +3,16 @@ import { waitForOk } from "../../../../support/helpers/network";
import {
requireKommissionellerAntritt,
requireKonfiguration,
requireNotenModus,
requireWiederholung,
skipWenn,
} from "../../../../support/helpers/notenConfig";
import { expectNotenSuccess } from "../../../../support/helpers/notenErrors";
import { addPruefung } from "../../../../support/helpers/notenScenario";
import {
attemptDate,
loadNotenContext,
readLvGesamtnote,
readLvGesamtnoteViaDb,
requireDbReset,
resetNotenState,
seedBaseline,
@@ -39,11 +41,8 @@ context("Benotungstool UI - Prüfungen", () => {
});
beforeEach(function () {
if (ctx.cisConfig.CIS_GESAMTNOTE_PUNKTE) {
// im Punktemodus ist das Notenfeld gesperrt, die Note kommt aus dem Notenschlüssel
Cypress.log({ name: "skip", message: "Skipped: CIS_GESAMTNOTE_PUNKTE ist aktiv." });
this.skip();
}
// im Punktemodus ist das Notenfeld gesperrt, die Note kommt aus dem Notenschlüssel
requireNotenModus(this, ctx);
});
it("legt aus der Zelle eine Wiederholung an und zählt sie als Antritt 2", function () {
@@ -52,7 +51,7 @@ context("Benotungstool UI - Prüfungen", () => {
const student = ctx.students[0];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, student, { note: ctx.notes.negativ });
page.visitAndWaitForTable(ctx);
@@ -68,7 +67,7 @@ context("Benotungstool UI - Prüfungen", () => {
page.expectLvNote(student.uid, bezeichnung(ctx.gradeNotes[1]));
// die Zelle zeigt den Freigabestatus, den der Server geschrieben hat; die Regel prüft noten.pruefungstermin
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((row) => {
const geaendert = new Date(row.benotungsdatum) > new Date(row.freigabedatum);
page.expectFreigabeState(student.uid, geaendert ? "changed" : "ok");
});
@@ -99,7 +98,7 @@ context("Benotungstool UI - Prüfungen", () => {
const neuesDatum = attemptDate(ctx, 2);
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, student, { note: ctx.notes.negativ });
page.visitAndWaitForTable(ctx);
@@ -120,7 +119,7 @@ context("Benotungstool UI - Prüfungen", () => {
const student = ctx.students[0];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, student, { note: ctx.notes.negativ });
page.visitAndWaitForTable(ctx);
@@ -147,8 +146,8 @@ context("Benotungstool UI - Prüfungen", () => {
const [a, b] = ctx.students;
resetNotenState(ctx);
seedBaseline(ctx, a.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, b.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, a, { note: ctx.notes.negativ });
seedBaseline(ctx, b, { note: ctx.notes.negativ });
page.visitAndWaitForTable(ctx);
@@ -175,7 +174,7 @@ context("Benotungstool UI - Prüfungen", () => {
// 'entschuldigt' verbraucht keinen Antritt, deshalb bleibt ein weiterer Termin möglich
const mitTerminAm = (student, datum) => {
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, student, { note: ctx.notes.negativ });
addPruefung(ctx, student, { note: ctx.notes.entschuldigt, datum }).then((response) =>
expectNotenSuccess(response, "bestehender Termin"),
);
@@ -215,15 +214,16 @@ context("Benotungstool UI - Prüfungen", () => {
it("legt den letzten Antritt als kommissionelle Prüfung an", function () {
requireKommissionellerAntritt(this, ctx);
if (ctx.cisConfig.CIS_GESAMTNOTE_KOMMISSIONELL_AB_ANTRITT !== ctx.maxAntritte) {
Cypress.log({ name: "skip", message: "Übersprungen: der letzte Antritt ist nicht kommissionell." });
this.skip();
}
skipWenn(
this,
ctx.cisConfig.CIS_GESAMTNOTE_KOMMISSIONELL_AB_ANTRITT !== ctx.maxAntritte,
"Übersprungen: der letzte Antritt ist nicht kommissionell.",
);
const student = ctx.students[1];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ });
seedBaseline(ctx, student, { note: ctx.notes.negativ });
page.visitAndWaitForTable(ctx);
@@ -1,14 +1,10 @@
import { benotungstoolPage as page } from "../../../../support/pages/benotungstool.po";
import {
requireKonfiguration,
requirePunkteModus,
requireWiederholung,
} from "../../../../support/helpers/notenConfig";
import { attemptsOfStudent, readState } from "../../../../support/helpers/notenScenario";
import { requireKonfiguration, requirePunkteModus, requireWiederholung } from "../../../../support/helpers/notenConfig";
import { attemptsOfStudent, readStateViaApi } from "../../../../support/helpers/notenScenario";
import {
attemptDate,
loadNotenContext,
readLvGesamtnote,
readLvGesamtnoteViaDb,
requireDbReset,
resetNotenState,
seedBaseline,
@@ -82,7 +78,7 @@ context("Benotungstool UI - Punktemodus", () => {
page.expectLvNote(student.uid, page.bezeichnungOf(ctx, noteMitte));
page.expectFreigabeState(student.uid, "changed");
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((row) => {
expect(String(row.note), "abgeleitete Note").to.eq(String(noteMitte));
expect(Number(row.punkte), "die Punkte werden mitgeschrieben").to.eq(MITTE);
});
@@ -94,7 +90,7 @@ context("Benotungstool UI - Punktemodus", () => {
const student = ctx.students[1];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.gradeNotes[0], freigegeben: true });
seedBaseline(ctx, student, { note: ctx.gradeNotes[0], freigegeben: true });
seedPruefung(ctx, student, {
note: ctx.gradeNotes[0],
datum: attemptDate(ctx, 1),
@@ -113,7 +109,7 @@ context("Benotungstool UI - Punktemodus", () => {
const student = ctx.students[1];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ, freigegeben: true });
seedBaseline(ctx, student, { note: ctx.notes.negativ, freigegeben: true });
page.visitAndWaitForTable(ctx);
page.getPruefungAddButton(student.uid, "antritt_2").click();
@@ -127,7 +123,7 @@ context("Benotungstool UI - Punktemodus", () => {
const student = ctx.students[1];
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ, freigegeben: true });
seedBaseline(ctx, student, { note: ctx.notes.negativ, freigegeben: true });
page.visitAndWaitForTable(ctx);
page.getPruefungAddButton(student.uid, "antritt_2").click();
@@ -153,8 +149,8 @@ context("Benotungstool UI - Punktemodus", () => {
const [a, b] = [ctx.students[2], ctx.students[3]];
resetNotenState(ctx);
seedBaseline(ctx, a.uid, { note: ctx.notes.negativ, freigegeben: true });
seedBaseline(ctx, b.uid, { note: ctx.notes.negativ, freigegeben: true });
seedBaseline(ctx, a, { note: ctx.notes.negativ, freigegeben: true });
seedBaseline(ctx, b, { note: ctx.notes.negativ, freigegeben: true });
page.visitAndWaitForTable(ctx);
page.addPruefungBulk({ uids: [a.uid, b.uid], punkte: MITTE, datum: page.toDDMMYYYY(attemptDate(ctx, 1)) });
@@ -179,14 +175,14 @@ context("Benotungstool UI - Punktemodus", () => {
const datum = page.importDatum(attemptDate(ctx, 1), ctx.cisConfig.CIS_GESAMTNOTE_IMPORT_DATUMSFORMAT);
resetNotenState(ctx);
seedBaseline(ctx, student.uid, { note: ctx.notes.negativ, freigegeben: true });
seedBaseline(ctx, student, { note: ctx.notes.negativ, freigegeben: true });
page.visitAndWaitForTable(ctx);
page.importPruefungen([[student.uid, datum, eingabe]]);
cy.get("@savePruefungenBulk").its("request.body.pruefungen.0.punkte").should("eq", PUNKTE);
readState(ctx).then((data) => {
readStateViaApi(ctx).then((data) => {
const neu = attemptsOfStudent(data, student.uid)[1];
expect(neu, "der importierte Termin").to.exist;
expect(Number(neu.punkte), "die Punkte des Termins").to.eq(PUNKTE);
@@ -205,7 +201,7 @@ context("Benotungstool UI - Punktemodus", () => {
cy.get("@saveNotenvorschlagBulk").its("request.body.noten.0.punkte").should("eq", PUNKTE);
readLvGesamtnote(ctx, student.uid).then((row) => {
readLvGesamtnoteViaDb(ctx, student.uid).then((row) => {
expect(row, "die importierte LV-Note").to.not.be.null;
expect(Number(row.punkte), "die Punkte der LV-Note").to.eq(PUNKTE);
});
+87 -14
View File
@@ -39,18 +39,48 @@ const student = (props) => ({ pruefungen: [], note: null, hoechsterAntritt: 0, .
/** A row whose Verlauf the server has already delivered. */
const withVerlauf = (verlauf, props = {}) => student({ verlauf: { maxAntritte: 2, ...verlauf }, ...props });
describe("Benotungstool rules (client)", () => {
describe("Benotungstool-Regeln (Client)", () => {
describe("maxAntrittCount", () => {
it("liest die Zahl aus der Konfiguration", () => {
expect(maxAntrittCount(configWith(3))).to.eq(3);
expect(maxAntrittCount(configWith(1))).to.eq(1);
});
it("nimmt 1, solange die Konfiguration keine Zahl liefert", () => {
// getCisConfig liefert den Schlüssel erst nach dem Laden; bis dahin gilt ein Antritt
expect(maxAntrittCount({}), "Schlüssel fehlt").to.eq(1);
expect(maxAntrittCount({ CIS_GESAMTNOTE_MAX_ANTRITTE: null }), "Schlüssel ist null").to.eq(1);
expect(maxAntrittCount(undefined), "keine Konfiguration").to.eq(1);
expect(maxAntrittCount(null), "Konfiguration ist null").to.eq(1);
});
// Der Rückfall nutzt ??, nicht ||. Mit || würde eine konfigurierte 0 zu 1 werden und das
// Werkzeug liesse einen Antritt zu, den die Konfiguration verbietet.
it("behält eine konfigurierte 0", () => {
expect(maxAntrittCount(configWith(0))).to.eq(0);
});
// getMaxAntritte auf dem Server liefert ein int. Die Umwandlung hält den Wert auch dann eine
// Zahl, wenn er je als Text ankommt: die Aufrufer vergleichen ihn, die Tests streng.
it("gibt immer eine Zahl zurück", () => {
expect(maxAntrittCount(configWith("4")), "Text mit Zahl").to.eq(4);
expect(maxAntrittCount(configWith(3)), "bleibt eine Zahl").to.eq(3);
});
it("nimmt 1, wenn der Wert keine Zahl ist", () => {
expect(maxAntrittCount(configWith("abc")), "unbrauchbarer Text").to.eq(1);
expect(maxAntrittCount(configWith("")), "leerer Text").to.eq(1);
});
});
describe("antrittCountStudent", () => {
it("takes the count from the Verlauf when the server delivered one", () => {
it("nimmt die Anzahl aus dem Verlauf, wenn der Server einen liefert", () => {
const s = withVerlauf({ antrittCount: 2 }, { note: 1, pruefungen: [{ note: 5 }] });
expect(antrittCountStudent(s, ours, notenOptions), "server wins over the local fallback").to.eq(2);
});
describe("local fallback (Verlauf not loaded yet)", () => {
it("does not count entschuldigt, 'noch nicht eingetragen' or 'nicht beurteilt'", () => {
describe("lokaler Rückfall, solange der Verlauf fehlt", () => {
it("zählt entschuldigt, 'noch nicht eingetragen' und 'nicht beurteilt' nicht mit", () => {
// still just the original note: none of these consumed an attempt, which is what lets a
// second retake be entered
const s = student({
@@ -60,31 +90,34 @@ describe("Benotungstool rules (client)", () => {
expect(antrittCountStudent(s, ours, notenOptions)).to.eq(1);
});
it("counts the original LV note as the first Antritt", () => {
it("zählt die ursprüngliche LV-Note als ersten Antritt", () => {
expect(antrittCountStudent(student({ note: 1 }), ours, notenOptions)).to.eq(1);
});
// dieselbe Falle wie in brauchtNeueLvNote: die Note 0 ist eine Note, kein fehlender Wert
it("zählt auch die Note 0 als ersten Antritt", () => {
const mitNullNote = [...notenOptions, { note: 0, lehre: true }];
expect(antrittCountStudent(student({ note: 0 }), ours, mitNullNote)).to.eq(1);
});
});
});
describe("canAddPruefung", () => {
it("follows the Verlauf", () => {
it("folgt dem Verlauf", () => {
expect(canAddPruefung(withVerlauf({ canAdd: true, antrittCount: 1 }), ours)).to.be.true;
expect(canAddPruefung(withVerlauf({ canAdd: false, antrittCount: 2 }), ours)).to.be.false;
});
it("blocks an angerechnet row even though attempts are left", () => {
it("sperrt eine angerechnete Zeile trotz freier Antritte", () => {
// the server sets canAdd:false for an Anrechnung; the client must not second-guess it
const s = withVerlauf({ canAdd: false, antrittCount: 0, angerechnet: true });
expect(canAddPruefung(s, ours)).to.be.false;
});
it("falls back to the cap and the terminal attempt without a Verlauf", () => {
it("fällt ohne Verlauf auf das Maximum und den letzten Antritt zurück", () => {
expect(canAddPruefung(student({ note: 1 }), ours), "one attempt of two used").to.be.true;
expect(
canAddPruefung(student({ note: 1, pruefungen: [{ note: 5 }, { note: 4 }] }), ours),
"cap reached",
).to.be.false;
expect(canAddPruefung(student({ note: 1, pruefungen: [{ note: 5 }, { note: 4 }] }), ours), "cap reached").to
.be.false;
expect(
canAddPruefung(student({ note: 1, pruefungen: [{ note: 5, kommissionell: true }] }), ours),
"terminal attempt exists",
@@ -93,7 +126,7 @@ describe("Benotungstool rules (client)", () => {
// Bekannte Lücke, hier festgenagelt: die LV-Note zählt nur solange gar keine Prüfung
// existiert. Deshalb schreibt die Freigabe Antritt 1 als echte Zeile.
it("stops counting the LV note once a Prüfung exists", () => {
it("zählt die LV-Note nicht mehr mit, sobald eine Prüfung existiert", () => {
const s = student({ note: 1, pruefungen: [{ note: 5 }] });
expect(antrittCountStudent(s, ours, notenOptions), "the LV note is no longer added").to.eq(1);
expect(canAddPruefung(s, ours), "so another attempt still looks available").to.be.true;
@@ -101,10 +134,50 @@ describe("Benotungstool rules (client)", () => {
});
describe("brauchtNeueLvNote", () => {
/**
* Der Verlauf schlägt lv_note, und zwar genau deshalb: lv_note trägt nur FREIGEGEBENE Noten
* (Benotungstool.js setzt es aus getLvGesamtNoten), hatLvNote dagegen zählt auch die
* eingetragene, noch nicht freigegebene Note mit. Noten.php nennt das Feld
* "ungefiltert, also inklusive noch nicht freigegebener".
*/
it("meldet keinen Bedarf, wenn die LV-Note nur noch nicht freigegeben ist", () => {
const s = { verlauf: { hatLvNote: true }, lv_note: null };
expect(brauchtNeueLvNote(s), "die Note existiert, sie ist nur nicht freigegeben").to.be.false;
});
it("meldet Bedarf, wenn der Verlauf keine LV-Note kennt", () => {
// auch wenn lv_note gesetzt ist: der Verlauf entscheidet
expect(brauchtNeueLvNote({ verlauf: { hatLvNote: false }, lv_note: 1 })).to.be.true;
expect(brauchtNeueLvNote({ verlauf: { hatLvNote: false }, lv_note: null })).to.be.true;
});
// verlaufSummary hat für hatLvNote den Vorgabewert null. Ein Aufrufer, der ihn nicht
// übergibt, erzeugt damit den Hinweis "noch keine LV-Note".
it("meldet Bedarf, wenn der Verlauf das Feld nicht trägt", () => {
expect(brauchtNeueLvNote({ verlauf: {} })).to.be.true;
});
describe("lokaler Rückfall, solange der Verlauf fehlt", () => {
it("liest lv_note", () => {
expect(brauchtNeueLvNote({ lv_note: 1 }), "eine Note ist da").to.be.false;
expect(brauchtNeueLvNote({ lv_note: null }), "keine Note").to.be.true;
expect(brauchtNeueLvNote({}), "Feld fehlt").to.be.true;
});
// Die Note 0 ("Teilnote") ist eine Note. Ein truthy-Test hielte sie für "keine Note" und
// das Werkzeug verspräche eine neue LV-Note, obwohl eine existiert.
it("erkennt die Note 0 als Note", () => {
expect(brauchtNeueLvNote({ lv_note: 0 })).to.be.false;
});
it("hält einen leeren Text für keine Note", () => {
expect(brauchtNeueLvNote({ lv_note: "" })).to.be.true;
});
});
});
describe("checkFreigabe", () => {
it("derives the state from the two timestamps", () => {
it("leitet den Status aus den beiden Zeitstempeln ab", () => {
expect(checkFreigabe(null, null), "no grade at all").to.eq("offen");
expect(checkFreigabe(null, "2026-01-10 08:00:00"), "never released").to.eq("changed");
expect(checkFreigabe("2026-01-10 08:00:00", "2026-01-20 08:00:00"), "re-graded").to.eq("changed");
+2 -1
View File
@@ -106,7 +106,8 @@ module.exports = {
},
offen: {
beschreibung: "frühere Noten änderbar, künftiges Benotungsdatum, keine Datumsfrist, ein Termin hebt die Freigabe nicht auf",
beschreibung:
"frühere Noten änderbar, künftiges Benotungsdatum, keine Datumsfrist, ein Termin hebt die Freigabe nicht auf",
config: {
CIS_GESAMTNOTE_NOTE_SPERRE_BEI_SPAETEREM_TERMIN: false,
CIS_GESAMTNOTE_DATUM_ZUKUNFT: true,
+16 -14
View File
@@ -16,20 +16,22 @@ const parallelPost = ({ path, bodies }) => {
const base = String(process.env.BASE_URL).replace(/\/+$/, "");
const auth = "Basic " + Buffer.from(`${process.env.USER_NAME}:${process.env.USER_PASSWORD}`).toString("base64");
return Promise.all(bodies.map((body) =>
fetch(`${base}/index.ci.php/api/frontend/v1/Noten/${path}`, {
method: "POST",
headers: { Authorization: auth, "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then(async (r) => {
const text = await r.text();
try {
return { status: r.status, body: JSON.parse(text) };
} catch (e) {
return { status: r.status, body: null, text: text.slice(0, 500) };
}
}),
));
return Promise.all(
bodies.map((body) =>
fetch(`${base}/index.ci.php/api/frontend/v1/Noten/${path}`, {
method: "POST",
headers: { Authorization: auth, "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then(async (r) => {
const text = await r.text();
try {
return { status: r.status, body: JSON.parse(text) };
} catch (e) {
return { status: r.status, body: null, text: text.slice(0, 500) };
}
}),
),
);
};
module.exports = {
+23 -12
View File
@@ -26,11 +26,9 @@ export const notenApi = {
getNoten: () => apiGet("getNoten"),
getBenotungstoolContext: (sem_kurzbz, lv_id = null) =>
apiGet("getBenotungstoolContext", { sem_kurzbz, lv_id }),
getBenotungstoolContext: (sem_kurzbz, lv_id = null) => apiGet("getBenotungstoolContext", { sem_kurzbz, lv_id }),
getLvForStudiengang: (studiengang_kz, sem_kurzbz) =>
apiGet("getLvForStudiengang", { studiengang_kz, sem_kurzbz }),
getLvForStudiengang: (studiengang_kz, sem_kurzbz) => apiGet("getLvForStudiengang", { studiengang_kz, sem_kurzbz }),
getLehrendeFuerLehreinheit: (lehreinheit_id, lv_id, sem_kurzbz) =>
apiGet("getLehrendeFuerLehreinheit", { lehreinheit_id, lv_id, sem_kurzbz }),
@@ -50,10 +48,26 @@ export const notenApi = {
/** data -> [savedPruefung, lvgesamtnote, verlauf]. Kein `typ` auf der Leitung. Ohne mitarbeiter_uid fehlt das Feld. */
saveStudentPruefung: ({
student_uid, note, punkte = null, datum, lva_id, lehreinheit_id, sem_kurzbz, pruefung_id = null, mitarbeiter_uid,
student_uid,
note,
punkte = null,
datum,
lva_id,
lehreinheit_id,
sem_kurzbz,
pruefung_id = null,
mitarbeiter_uid,
}) =>
apiPost("saveStudentPruefung", {
student_uid, note, punkte, datum, lva_id, lehreinheit_id, sem_kurzbz, pruefung_id, mitarbeiter_uid,
student_uid,
note,
punkte,
datum,
lva_id,
lehreinheit_id,
sem_kurzbz,
pruefung_id,
mitarbeiter_uid,
}),
/**
@@ -66,8 +80,7 @@ export const notenApi = {
saveStudentenNoten: (password, noten, lv_id, sem_kurzbz) =>
apiGet("getCisConfig").then(() => apiPost("saveStudentenNoten", { password, noten, lv_id, sem_kurzbz })),
getNoteByPunkte: (punkte, lv_id, sem_kurzbz) =>
apiPost("getNoteByPunkte", { punkte, lv_id, sem_kurzbz }),
getNoteByPunkte: (punkte, lv_id, sem_kurzbz) => apiPost("getNoteByPunkte", { punkte, lv_id, sem_kurzbz }),
// Bulk-Pfade antworten 200 und melden Fehler je Zeile in data[uid]
saveNotenvorschlagBulk: (lv_id, sem_kurzbz, noten) =>
@@ -83,8 +96,7 @@ export const notenApi = {
// --- selectors over the getStudentenNoten payload ---
export const pruefungenOf = (data, uid) =>
(data[1] || []).filter((p) => p.student_uid === uid);
export const pruefungenOf = (data, uid) => (data[1] || []).filter((p) => p.student_uid === uid);
/** Antritte in Verlaufsreihenfolge. Specs prüfen position/zaehlt/antritt_nr/terminal, nie den Typ. */
export const attemptsOf = (data, uid) =>
@@ -94,8 +106,7 @@ export const attemptsOf = (data, uid) =>
export const countingAttemptsOf = (data, uid) => attemptsOf(data, uid).filter((p) => p.zaehlt);
/** Legacy projection written for old reports. Asserted in exactly one spec, never used as a rule. */
export const pruefungenOfTyp = (data, uid, typ) =>
pruefungenOf(data, uid).filter((p) => p.pruefungstyp_kurzbz === typ);
export const pruefungenOfTyp = (data, uid, typ) => pruefungenOf(data, uid).filter((p) => p.pruefungstyp_kurzbz === typ);
export const gradesOf = (data, uid) => (data[3] || {})[uid];
+27 -30
View File
@@ -2,26 +2,26 @@
/** Visits a page and fails if any request returned >= 400. */
Cypress.Commands.add("checkPageResources", (url) => {
const failedRequests = [];
const failedRequests = [];
cy.intercept("**", (req) => {
req.on("response", (res) => {
if (res.statusCode >= 400) {
failedRequests.push({ url: req.url, status: res.statusCode });
}
});
});
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.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}`);
}
});
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}`);
}
});
});
/**
@@ -30,22 +30,19 @@ Cypress.Commands.add("checkPageResources", (url) => {
* Ohne Argumente meldet sich der Suite-Benutzer an.
*/
Cypress.Commands.add("login", (username, password) => {
const { adminusername, adminpassword } = Cypress.env();
const auth = { username: username || adminusername, password: password || adminpassword };
const probe = () =>
cy
.request({ url: "/index.ci.php/api/frontend/v1/AuthInfo/getAuthUID", auth })
.its("status")
.should("eq", 200);
const { adminusername, adminpassword } = Cypress.env();
const auth = { username: username || adminusername, password: password || adminpassword };
const probe = () =>
cy.request({ url: "/index.ci.php/api/frontend/v1/AuthInfo/getAuthUID", auth }).its("status").should("eq", 200);
cy.session(["benotungstool-login", auth.username], probe, {
cacheAcrossSpecs: true,
validate: probe,
});
cy.session(["benotungstool-login", auth.username], probe, {
cacheAcrossSpecs: true,
validate: probe,
});
});
// Anwendungsfehler sollen den Test nicht abbrechen, aber sichtbar sein.
Cypress.on("uncaught:exception", (err) => {
Cypress.log({ name: "app error", message: err.message, consoleProps: () => ({ error: err }) });
return false;
Cypress.log({ name: "app error", message: err.message, consoleProps: () => ({ error: err }) });
return false;
});
+1 -2
View File
@@ -1,2 +1 @@
export const waitForOk = (alias) =>
cy.wait(alias).its("response.statusCode").should("eq", 200);
export const waitForOk = (alias) => cy.wait(alias).its("response.statusCode").should("eq", 200);
+26 -12
View File
@@ -7,6 +7,7 @@
*/
import { expectNotenSuccess } from "./notenErrors";
import { skipWenn } from "./notenConfig";
const NOTEN_API = "/index.ci.php/api/frontend/v1/Noten";
const SEMESTER_SUCHTIEFE = 6;
@@ -14,6 +15,14 @@ const SEMESTER_SUCHTIEFE = 6;
export const assistenzConfigured = () =>
Boolean(Cypress.env("NOTEN_ASSISTENZ_USER") && Cypress.env("NOTEN_ASSISTENZ_PASSWORD"));
/** Skip, wenn kein Konto der Assistenz konfiguriert ist. */
export const requireAssistenz = (testContext) =>
skipWenn(
testContext,
!assistenzConfigured(),
"Übersprungen: NOTEN_ASSISTENZ_USER / NOTEN_ASSISTENZ_PASSWORD fehlen.",
);
export const assistenzAuth = () => ({
username: Cypress.env("NOTEN_ASSISTENZ_USER"),
password: Cypress.env("NOTEN_ASSISTENZ_PASSWORD"),
@@ -48,9 +57,9 @@ export const assistenzKontext = (startSem) => {
i >= liste.length
? cy.wrap(null, { log: false })
: getAlsAssistenz("getBenotungstoolContext", { sem_kurzbz: liste[i] }).then((response) => {
const data = expectNotenSuccess(response, `Kontext der Assistenz in ${liste[i]}`);
return data.studiengaenge.length ? { sem: liste[i], data } : probiere(i + 1);
});
const data = expectNotenSuccess(response, `Kontext der Assistenz in ${liste[i]}`);
return data.studiengaenge.length ? { sem: liste[i], data } : probiere(i + 1);
});
return probiere(0);
};
@@ -67,19 +76,24 @@ export const assistenzLvMitLehreinheiten = (startSem) =>
j >= lvs.length
? cy.wrap(null, { log: false })
: cy
.task("noten:db:lehreinheitenDerLv", { lvId: lvs[j].lehrveranstaltung_id, semKurzbz: sem })
.then((ids) =>
ids.length
? { sem, lvId: lvs[j].lehrveranstaltung_id, lehreinheiten: ids.map(String).sort() }
: ersteLvMitLehreinheiten(lvs, j + 1),
);
.task("noten:db:lehreinheitenDerLv", { lvId: lvs[j].lehrveranstaltung_id, semKurzbz: sem })
.then((ids) =>
ids.length
? { sem, lvId: lvs[j].lehrveranstaltung_id, lehreinheiten: ids.map(String).sort() }
: ersteLvMitLehreinheiten(lvs, j + 1),
);
const probiereStudiengang = (i) =>
i >= studiengaenge.length
? cy.wrap(null, { log: false })
: getAlsAssistenz("getLvForStudiengang", { studiengang_kz: studiengaenge[i].studiengang_kz, sem_kurzbz: sem })
.then((response) => ersteLvMitLehreinheiten(expectNotenSuccess(response, "LVs des Studiengangs"), 0))
.then((ziel) => ziel || probiereStudiengang(i + 1));
: getAlsAssistenz("getLvForStudiengang", {
studiengang_kz: studiengaenge[i].studiengang_kz,
sem_kurzbz: sem,
})
.then((response) =>
ersteLvMitLehreinheiten(expectNotenSuccess(response, "LVs des Studiengangs"), 0),
)
.then((ziel) => ziel || probiereStudiengang(i + 1));
return probiereStudiengang(0);
});
+30 -27
View File
@@ -10,6 +10,19 @@
* vor - passt die Instanz nicht dazu, scheitert der Lauf sofort und laut.
*/
/**
* Der einzige Weg, einen Test zu überspringen. `bedingung` wahr -> überspringen, `grund` sagt warum.
* In it(function(){...}) / beforeEach(function(){...}) aufrufen, nie in einer Pfeilfunktion: eine
* Pfeilfunktion bindet kein `this` und erreicht test.skip() nicht.
*
* Die require*-Helfer unten nennen je eine fachliche Voraussetzung und rufen ihrerseits skipWenn.
*/
export const skipWenn = (testContext, bedingung, grund) => {
if (!bedingung) return;
Cypress.log({ name: "skip", message: grund });
testContext.skip();
};
/** Ist der Punktemodus auf dieser Instanz aktiv? */
export const punkteModus = (ctx) => Boolean(ctx.cisConfig.CIS_GESAMTNOTE_PUNKTE);
@@ -35,24 +48,19 @@ export const assertPunkteModus = (ctx) => {
};
/** Skip, wenn der Punktemodus aus ist. Als erste Zeile in beforeEach(function(){...}) aufrufen. */
export const requirePunkteModus = (testContext, ctx) => {
if (punkteModus(ctx)) return;
cy.log("Übersprungen: CIS_GESAMTNOTE_PUNKTE ist aus.");
testContext.skip();
};
export const requirePunkteModus = (testContext, ctx) =>
skipWenn(testContext, !punkteModus(ctx), "Übersprungen: CIS_GESAMTNOTE_PUNKTE ist aus.");
/**
* Skip, wenn der Schlüssel aus getCisConfig nicht den Zweig trägt, den der Test prüft. Ein Profil aus
* tests/cypress/profiles/noten.js schaltet den anderen Zweig ein.
*/
export const requireKonfiguration = (testContext, ctx, key, wert) => {
if (ctx.cisConfig[key] === wert) return;
Cypress.log({
name: "skip",
message: `Übersprungen: ${key} ist ${JSON.stringify(ctx.cisConfig[key])}, der Test braucht ${JSON.stringify(wert)}.`,
});
testContext.skip();
};
export const requireKonfiguration = (testContext, ctx, key, wert) =>
skipWenn(
testContext,
ctx.cisConfig[key] !== wert,
`Übersprungen: ${key} ist ${JSON.stringify(ctx.cisConfig[key])}, der Test braucht ${JSON.stringify(wert)}.`,
);
/**
* Skip, wenn das Werkzeug keinen zweiten Antritt anlegt. Ohne CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF
@@ -64,9 +72,7 @@ export const requireWiederholung = (testContext, ctx) => {
ctx.cisConfig.CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF === false && ab !== null
? Math.min(ctx.maxAntritte, ab - 1)
: ctx.maxAntritte;
if (anlegbar >= 2) return;
Cypress.log({ name: "skip", message: `Übersprungen: das Werkzeug legt nur ${anlegbar} Antritt an.` });
testContext.skip();
skipWenn(testContext, anlegbar < 2, `Übersprungen: das Werkzeug legt nur ${anlegbar} Antritt an.`);
};
/**
@@ -76,17 +82,14 @@ export const requireWiederholung = (testContext, ctx) => {
export const requireKommissionellerAntritt = (testContext, ctx) => {
const ab = ctx.cisConfig.CIS_GESAMTNOTE_KOMMISSIONELL_AB_ANTRITT ?? null;
const anlegbar = ctx.cisConfig.CIS_GESAMTNOTE_ALLOW_CREATE_KOMMPRUEF !== false;
if (anlegbar && ab !== null && ab >= 2 && ab <= ctx.maxAntritte) return;
Cypress.log({
name: "skip",
message: `Übersprungen: kein anlegbarer kommissioneller Antritt (ab ${JSON.stringify(ab)}, anlegen ${anlegbar}).`,
});
testContext.skip();
const moeglich = anlegbar && ab !== null && ab >= 2 && ab <= ctx.maxAntritte;
skipWenn(
testContext,
!moeglich,
`Übersprungen: kein anlegbarer kommissioneller Antritt (ab ${JSON.stringify(ab)}, anlegen ${anlegbar}).`,
);
};
/** Skip, wenn der Punktemodus an ist. */
export const requireNotenModus = (testContext, ctx) => {
if (!punkteModus(ctx)) return;
cy.log("Übersprungen: CIS_GESAMTNOTE_PUNKTE ist aktiv.");
testContext.skip();
};
export const requireNotenModus = (testContext, ctx) =>
skipWenn(testContext, punkteModus(ctx), "Übersprungen: CIS_GESAMTNOTE_PUNKTE ist aktiv.");
+8 -4
View File
@@ -116,7 +116,12 @@ 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]*?"));
new RegExp(
text
.split(/\{\d+\}/)
.map(escapeRegExp)
.join("[\\s\\S]*?"),
);
const patternsFor = (key) => {
const phrase = PHRASES[key];
@@ -141,7 +146,7 @@ export const expectNotenError = (response, key) => {
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(" | ");
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;
@@ -164,7 +169,6 @@ export const expectBulkRowError = (data, uid, key) => {
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));
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;
};
@@ -35,7 +35,11 @@ export const editPruefung = (context, student, { pruefungId, note, datum }) =>
pruefung_id: pruefungId,
});
export const readState = (context) =>
/**
* Liest den Zustand über die API (getStudentenNoten): was der Server MELDET.
* Das Gegenstück ist readLvGesamtnoteViaDb - es liest, was wirklich gespeichert ist.
*/
export const readStateViaApi = (context) =>
notenApi
.getStudentenNoten(context.lvId, context.semKurzbz)
.then((response) => expectNotenSuccess(response, "getStudentenNoten"));
@@ -53,4 +57,4 @@ 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));
resetNotenState(context).then(() => seedBaseline(context, student, options));
+31 -25
View File
@@ -10,8 +10,13 @@ import { expectNotenSuccess } from "./notenErrors";
// the client rule; it now only reads back what the server derived, so a mismatch is a config bug
import { maxAntrittCount as computeMaxAntritte } from "../../../../public/js/components/Cis/Benotungstool/notenRules.js";
import {
describeFailure, performRead, performReset, performSeed, performSeedPruefung,
performSeedZeugnisnote, resolveResetStrategy,
describeFailure,
performRead,
performReset,
performSeed,
performSeedPruefung,
performSeedZeugnisnote,
resolveResetStrategy,
} from "./notenReset";
import { assertPunkteModus } from "./notenConfig";
@@ -24,8 +29,7 @@ let cachedContext = null;
export const pad2 = (n) => String(n).padStart(2, "0");
export const toDateString = (date) =>
`${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
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);
@@ -35,11 +39,7 @@ export const shiftDate = (dateString, days) => {
};
/** 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 },
) => {
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;
@@ -60,7 +60,7 @@ export const fristHasPassed = (semKurzbz, ssConfig = { month: 11, day: 15 }, wsC
* A semester + course where the logged-in user actually teaches AND the grade deadline has passed.
*
* Both are needed: assertLvAccess runs BEFORE the deadline check, so a semester the user does not
* teach in fails there and never reaches the deadline. Seeder 019 provides the Sommersemester.
* teach in fails there and never reaches the deadline. Seeder group benotungstool_fixture_erweitert provides the Sommersemester.
*
* @param {string} type "SS" or "WS"
* @returns {Cypress.Chainable<{semKurzbz: string, lvId: number}|null>}
@@ -137,7 +137,6 @@ const resolveLehrveranstaltung = (semKurzbz) => {
});
};
/** -> { semKurzbz, lvId, cisConfig, maxAntritte, notes, gradeNotes, students, studentUids } */
export const loadNotenContext = () => {
if (cachedContext) return cy.wrap(cachedContext, { log: false });
@@ -183,7 +182,10 @@ export const loadNotenContext = () => {
context.gradeNotes =
ordinary.length > 1
? ordinary
: usable.map((n) => n.note).filter((n) => Number(n) !== 0).sort((a, b) => Number(a) - Number(b));
: 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);
@@ -244,8 +246,7 @@ export const loadNotenContext = () => {
// --- fixture state ---
export const resetNotenState = (context, studentUids) =>
performReset(context, studentUids || context.studentUids);
export const resetNotenState = (context, studentUids) => performReset(context, studentUids || context.studentUids);
/**
* Baseline für Antritt 1: freigegebene LV-Note PLUS die Prüfungszeile dazu - das ist, was die
@@ -254,18 +255,24 @@ export const resetNotenState = (context, studentUids) =>
*
* `erstantritt: false` (bzw. `freigegeben: false`) seedet die Altdaten-Form ohne diese Zeile.
*/
export const seedBaseline = (context, studentUid, options = {}) => {
export const seedBaseline = (context, student, options = {}) => {
expect(student, "seedBaseline braucht das Studierenden-Objekt aus context.students, nicht die uid").to.be.an(
"object",
);
// Defaults to a NEGATIVE grade: only after one may another attempt follow. Pass
// context.notes.bestnote explicitly to close the chain.
const note = options.note !== undefined ? options.note : context.notes.negativ;
// Mit CIS_GESAMTNOTE_FREIGABE_FINAL ist eine freigegebene Note endgültig. Ohne ausdrückliches
// freigegeben sät die Baseline dann eine offene Note, damit ein Test weitere Termine anlegen kann.
const freigegeben =
options.freigegeben !== undefined ? options.freigegeben : context.cisConfig.CIS_GESAMTNOTE_FREIGABE_FINAL !== true;
options.freigegeben !== undefined
? options.freigegeben
: context.cisConfig.CIS_GESAMTNOTE_FREIGABE_FINAL !== true;
// Antritt 1 fehlt nur bei ausdrücklichem erstantritt: false oder freigegeben: false
const erstantritt = options.erstantritt !== undefined ? options.erstantritt : options.freigegeben !== false;
return performSeed(context, studentUid, {
return performSeed(context, student.uid, {
note,
punkte: options.punkte !== undefined ? options.punkte : null,
benotungsdatum: options.benotungsdatum || baselineBenotungsdatum(context),
@@ -274,10 +281,7 @@ export const seedBaseline = (context, studentUid, options = {}) => {
}).then((seeded) => {
if (!erstantritt) return cy.wrap(seeded, { log: false });
const student = context.students.find((s) => s.uid === studentUid);
expect(student, `student ${studentUid} in the loaded LV`).to.exist;
return performSeedPruefung(context, studentUid, {
return performSeedPruefung(context, student.uid, {
lehreinheitId: student.lehreinheit_id,
note,
datum: baselineDate(context),
@@ -296,11 +300,13 @@ export const seedPruefung = (context, student, { note, datum, typ }) =>
});
/** Zeugnisnote setzen. Nur die Studierendenverwaltung schreibt sie, kein Endpunkt dieses Tools. */
export const seedZeugnisnote = (context, studentUid, note) =>
performSeedZeugnisnote(context, studentUid, { note });
export const seedZeugnisnote = (context, studentUid, note) => performSeedZeugnisnote(context, studentUid, { note });
/** Raw row, bypassing the freigabedatum filter of getLvGesamtNoten. */
export const readLvGesamtnote = (context, studentUid) => performRead(context, studentUid);
/**
* Liest die LV-Note direkt aus der DATENBANK: was wirklich gespeichert ist, ohne den
* freigabedatum-Filter von getLvGesamtNoten. Das Gegenstück ist readStateViaApi.
*/
export const readLvGesamtnoteViaDb = (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`;
+15 -21
View File
@@ -19,8 +19,7 @@ const API = "**/api/frontend/v1/Noten";
* Exakter Textvergleich für Optionslisten. Nötig, weil die Notenbezeichnungen einander enthalten:
* contains("Gut") trifft zuerst "Sehr Gut", contains("Genügend") zuerst "Nicht Genügend".
*/
const exactText = (text) =>
new RegExp(`^\\s*${String(text).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`);
const exactText = (text) => new RegExp(`^\\s*${String(text).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`);
class BenotungstoolPage {
selectors = {
@@ -73,10 +72,8 @@ class BenotungstoolPage {
getFreigabeState = (uid) => this.getCell(uid, "freigegeben").find("[data-cy='freigabe-state']");
getPruefungCell = (uid, spalte) => this.getCell(uid, spalte).find("[data-cy='pruefung-cell']");
getUebernehmenButton = (uid) => this.getCell(uid, "übernehmen").find("[data-cy='btn-uebernehmen']");
getPruefungAddButton = (uid, spalte) =>
this.getCell(uid, spalte).find("[data-cy='btn-pruefung-add']");
getPruefungEditButton = (uid, spalte) =>
this.getCell(uid, spalte).find("[data-cy='btn-pruefung-edit']");
getPruefungAddButton = (uid, spalte) => this.getCell(uid, spalte).find("[data-cy='btn-pruefung-add']");
getPruefungEditButton = (uid, spalte) => this.getCell(uid, spalte).find("[data-cy='btn-pruefung-edit']");
getPruefungModal = () => cy.get("[data-cy='modal-pruefung']");
getNeuePruefungModal = () => cy.get("[data-cy='modal-neue-pruefung']");
@@ -89,8 +86,7 @@ class BenotungstoolPage {
// --- Zustand prüfen --------------------------------------------------------------------------
/** offen | changed | ok */
expectFreigabeState = (uid, state) =>
this.getFreigabeState(uid).should("have.attr", "data-state", state);
expectFreigabeState = (uid, state) => this.getFreigabeState(uid).should("have.attr", "data-state", state);
expectLvNote = (uid, bezeichnung) => this.getCell(uid, "lv_note").should("contain.text", bezeichnung);
@@ -149,7 +145,11 @@ class BenotungstoolPage {
cy.get(".p-toast-message-warn", { timeout: TABLE_TIMEOUT }).should("have.length.at.least", anzahl);
/** Die uids des letzten Requests eines Sammelpfads. feld: "noten" oder "pruefungen" */
gesendeteUids = (alias, feld) => cy.get(alias).its(`request.body.${feld}`).then((zeilen) => zeilen.map((z) => z.uid));
gesendeteUids = (alias, feld) =>
cy
.get(alias)
.its(`request.body.${feld}`)
.then((zeilen) => zeilen.map((z) => z.uid));
// --- Notenvorschlag --------------------------------------------------------------------------
@@ -184,8 +184,7 @@ class BenotungstoolPage {
cy.contains(".p-dropdown-panel .p-dropdown-item", exactText(label)).click();
};
setDatum = (dataCy, ddmmyyyy) =>
cy.get(`[data-cy='${dataCy}'] input`).first().clear().type(`${ddmmyyyy}{enter}`);
setDatum = (dataCy, ddmmyyyy) => cy.get(`[data-cy='${dataCy}'] input`).first().clear().type(`${ddmmyyyy}{enter}`);
/**
* Nach jeder Speicherung zeigt das Tool einen Erfolgs-Toast ohne Ablaufzeit. Er liegt über dem Modal
@@ -279,8 +278,7 @@ class BenotungstoolPage {
.find("[data-cy='freigabe-row-released']")
.should("contain.text", releasedBezeichnung);
typeFreigabePasswort = (password) =>
cy.get("[data-cy='freigabe-passwort'] input").type(password, { log: false });
typeFreigabePasswort = (password) => cy.get("[data-cy='freigabe-passwort'] input").type(password, { log: false });
submitFreigabe = () => cy.get("[data-cy='freigabe-submit']").click();
@@ -349,8 +347,7 @@ class BenotungstoolPage {
waitForOk("@getNoteByPunkte");
};
expectPunkte = (uid, punkte) =>
this.getPunkteCell(uid).should("contain.text", String(punkte));
expectPunkte = (uid, punkte) => this.getPunkteCell(uid).should("contain.text", String(punkte));
/** Sobald ein Termin existiert, ist die Punktespalte gesperrt (editable-Guard der Spalte). */
expectPunkteZelleGesperrt = (uid) => {
@@ -373,14 +370,12 @@ class BenotungstoolPage {
};
/** Punktefeld der Sammelanlage; dort leitet erst der Server beim Speichern ab. */
setNeuePruefungPunkte = (punkte) =>
cy.get("[data-cy='neue-pruefung-punkte'] input").clear().type(String(punkte));
setNeuePruefungPunkte = (punkte) => cy.get("[data-cy='neue-pruefung-punkte'] input").clear().type(String(punkte));
// --- Hilfen ----------------------------------------------------------------------------------
/** Die Bezeichnung zu einer Noten-PK, wie sie in Dropdowns und Zellen steht. */
bezeichnungOf = (ctx, note) =>
(ctx.notenOptions ?? []).find((n) => String(n.note) === String(note))?.bezeichnung;
bezeichnungOf = (ctx, note) => (ctx.notenOptions ?? []).find((n) => String(n.note) === String(note))?.bezeichnung;
toDDMMYYYY = (isoDate) => {
const [y, m, d] = isoDate.split("-");
@@ -388,8 +383,7 @@ class BenotungstoolPage {
};
/** Date in the configured import format; kept apart from toDDMMYYYY, which the dialog uses. */
importDatum = (isoDate, format) =>
format === "yyyy-MM-dd" ? isoDate : this.toDDMMYYYY(isoDate);
importDatum = (isoDate, format) => (format === "yyyy-MM-dd" ? isoDate : this.toDDMMYYYY(isoDate));
}
export const benotungstoolPage = new BenotungstoolPage();
+21 -6
View File
@@ -24,7 +24,6 @@ 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
@@ -37,8 +36,16 @@ const getPool = () => {
user: env("USER"),
password: env("PASSWORD"),
max: 4,
// tells these connections apart from the web application in pg_stat_activity
application_name: "cypress",
connectionTimeoutMillis: 15000,
// the server ends a stuck statement or transaction before the 60 s task timeout, so no lock wait holds a connection
statement_timeout: 30000,
idle_in_transaction_session_timeout: 30000,
ssl: String(env("SSL")).toLowerCase() === "true" ? { rejectUnauthorized: false } : undefined,
});
// an idle client that loses its connection emits here; without a listener the config process crashes
pool.on("error", (error) => console.warn(`[db] idle connection lost: ${error.message}`));
return pool;
};
@@ -115,19 +122,27 @@ const checkAvailability = async ({ requireWrites = true } = {}) => {
} catch (error) {
// a failed pool is poisoned - drop it so a retry reconnects cleanly
if (pool) {
try { await pool.end(); } catch (e) { /* ignore */ }
const failed = pool;
pool = null;
try {
await failed.end();
} catch (e) {
/* ignore */
}
}
return { available: false, reason: explainFailure(error) };
}
};
const closeDb = async () => {
if (pool) {
await pool.end();
pool = null;
// drop the reference first: a later task must not get a pool that is ending
const open = pool;
pool = null;
try {
if (open) await open.end();
} finally {
await closeTunnel();
}
await closeTunnel();
return null;
};
+24 -9
View File
@@ -9,9 +9,7 @@
* No unscoped DELETE in this file.
*/
const {
assertWritable, dbConfigured, withClient, inTransaction, checkAvailability, closeDb,
} = require("./db");
const { assertWritable, dbConfigured, withClient, inTransaction, checkAvailability, closeDb } = require("./db");
const REQUIRED_SCOPE = ["lvId", "semKurzbz", "studentUids"];
@@ -83,8 +81,15 @@ const seedLvGesamtnote = async (scope) => {
assertScope({ ...scope, studentUids: [scope.studentUid] });
const {
lvId, semKurzbz, studentUid, note, punkte = null, mitarbeiterUid,
benotungsdatum, freigegeben = false, freigabedatum = null,
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");
@@ -107,8 +112,15 @@ const seedLvGesamtnote = async (scope) => {
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,
studentUid,
lvId,
semKurzbz,
note,
punkte,
mitarbeiterUid,
benotungsdatum,
resolvedFreigabe,
resolvedFreigabe ? mitarbeiterUid : null,
],
);
@@ -140,7 +152,9 @@ const seedPruefung = async (scope) => {
[lehreinheitId, lvId, semKurzbz],
);
if (!owned.rowCount) {
throw new Error(`noten:db seedPruefung refused - lehreinheit ${lehreinheitId} is outside ${lvId}/${semKurzbz}`);
throw new Error(
`noten:db seedPruefung refused - lehreinheit ${lehreinheitId} is outside ${lvId}/${semKurzbz}`,
);
}
const res = await client.query(
@@ -336,7 +350,8 @@ const lehrendeDerLehreinheit = async ({ lehreinheitId } = {}) => {
/** The grading person of one exam row, or null. */
const pruefungMitarbeiter = async ({ pruefungId, studentUid } = {}) => {
assertReadable();
if (!pruefungId || !studentUid) throw new Error("noten:db pruefungMitarbeiter - pruefungId and studentUid are required");
if (!pruefungId || !studentUid)
throw new Error("noten:db pruefungMitarbeiter - pruefungId and studentUid are required");
const rows = await readRows(
"SELECT mitarbeiter_uid FROM lehre.tbl_pruefung WHERE pruefung_id = $1 AND student_uid = $2",
+27 -10
View File
@@ -46,12 +46,15 @@ const sshKonfiguriert = () => Boolean(envWert("SSH_HOST") && envWert("SSH_USER")
const verbinde = () =>
new Promise((resolve, reject) => {
// eslint-disable-next-line global-require
const { Client } = require("ssh2");
let auth;
try {
auth = resolveAuth({ keyPath: envWert("SSH_KEY"), passphrase: envWert("SSH_PASSPHRASE"), agent: envWert("SSH_AGENT") });
auth = resolveAuth({
keyPath: envWert("SSH_KEY"),
passphrase: envWert("SSH_PASSPHRASE"),
agent: envWert("SSH_AGENT"),
});
} catch (error) {
reject(error);
return;
@@ -60,7 +63,9 @@ const verbinde = () =>
const client = new Client();
client
.on("ready", () => client.sftp((err, sftp) => (err ? reject(err) : resolve({ client, sftp }))))
.on("error", (err) => reject(new Error(`SSH zu ${envWert("SSH_USER")}@${envWert("SSH_HOST")} gescheitert: ${err.message}`)))
.on("error", (err) =>
reject(new Error(`SSH zu ${envWert("SSH_USER")}@${envWert("SSH_HOST")} gescheitert: ${err.message}`)),
)
.connect({
host: envWert("SSH_HOST"),
port: Number(envWert("SSH_PORT") || 22),
@@ -133,7 +138,9 @@ const mitFlags = (original, flags) => {
Object.entries(flags).forEach(([flag, wert]) => {
const muster = new RegExp(`^([ \\t]*)define\\(\\s*['"]${flag}['"]\\s*,[^;]*;`, "m");
if (!muster.test(text)) {
throw new Error(`${DATEIEN.flags} definiert ${flag} nicht. Ein Profil ändert nur einen bestehenden Schalter.`);
throw new Error(
`${DATEIEN.flags} definiert ${flag} nicht. Ein Profil ändert nur einen bestehenden Schalter.`,
);
}
text = text.replace(muster, (treffer, einzug) => `${einzug}define('${flag}', ${phpWert(wert)});`);
});
@@ -198,7 +205,8 @@ const setze = async (sftp, zustand, schluessel, umbau) => {
}
original = await lies(sftp, sicherung);
} else {
if (eintrag) console.warn(`[profil] ${entfernt} ist seit dem letzten Profil neu ausgerollt und gilt als Original.`);
if (eintrag)
console.warn(`[profil] ${entfernt} ist seit dem letzten Profil neu ausgerollt und gilt als Original.`);
await schreibeRoh(sftp, sicherung, aktuell, 0o600);
}
@@ -232,7 +240,9 @@ const leseCisConfig = async (url, auth) => {
// unter 500 steht eine PHP-Meldung vor dem JSON: die Datei ist kaputt, Warten hilft nicht
if (antwort.status < 500) {
throw new Error(`getCisConfig antwortet nach dem Profilwechsel mit HTTP ${antwort.status}: ${text.slice(0, 300)}`);
throw new Error(
`getCisConfig antwortet nach dem Profilwechsel mit HTTP ${antwort.status}: ${text.slice(0, 300)}`,
);
}
return { ist: null, grund: `getCisConfig antwortet mit HTTP ${antwort.status}` };
};
@@ -259,7 +269,9 @@ const pruefeInstanz = async (daten) => {
const abweichend = pruefbar.filter((key) => !gleich(ist[key], erwartet[key]));
if (!abweichend.length) return;
const liste = abweichend.map((k) => ` ${k}: ${JSON.stringify(ist[k])} statt ${JSON.stringify(erwartet[k])}`);
const liste = abweichend.map(
(k) => ` ${k}: ${JSON.stringify(ist[k])} statt ${JSON.stringify(erwartet[k])}`,
);
offen = `die Instanz zeigt das Profil nicht:\n${liste.join("\n")}`;
}
@@ -295,7 +307,9 @@ const anwenden = async (name) => {
const daten = profilDaten(name);
const root = envWert("REMOTE_ROOT");
if (!sshKonfiguriert() || !root) {
throw new Error(`Profil "${name}" braucht ${PREFIX}_SSH_HOST, ${PREFIX}_SSH_USER und ${PREFIX}_REMOTE_ROOT in tests/cypress/.env.`);
throw new Error(
`Profil "${name}" braucht ${PREFIX}_SSH_HOST, ${PREFIX}_SSH_USER und ${PREFIX}_REMOTE_ROOT in tests/cypress/.env.`,
);
}
await mitSftp(async (sftp) => {
@@ -303,12 +317,15 @@ const anwenden = async (name) => {
const zustand = (await leseZustand(sftp)) || { dateien: {} };
if (zustand.root && zustand.root !== root && Object.keys(zustand.dateien).length) {
throw new Error(`Auf ${zustand.root} ist noch "${zustand.profil}" aktiv. Zuerst: npm run noten:profil -- wiederherstellen`);
throw new Error(
`Auf ${zustand.root} ist noch "${zustand.profil}" aktiv. Zuerst: npm run noten:profil -- wiederherstellen`,
);
}
zustand.root = root;
zustand.profil = name;
if (Object.keys(daten.config).length) await setze(sftp, zustand, "config", (o) => mitConfig(o, name, daten.config));
if (Object.keys(daten.config).length)
await setze(sftp, zustand, "config", (o) => mitConfig(o, name, daten.config));
else await stelleHer(sftp, zustand, "config");
if (Object.keys(daten.flags).length) await setze(sftp, zustand, "flags", (o) => mitFlags(o, daten.flags));
+11 -6
View File
@@ -53,7 +53,6 @@ const openTunnel = (cfg) =>
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;
@@ -68,6 +67,8 @@ const openTunnel = (cfg) =>
sshClient
.on("ready", () => {
server = net.createServer((socket) => {
// an error before the channel is up must not crash the process
socket.on("error", () => socket.destroy());
sshClient.forwardOut(
socket.remoteAddress || "127.0.0.1",
socket.remotePort || 0,
@@ -75,6 +76,10 @@ const openTunnel = (cfg) =>
cfg.dbPort,
(err, stream) => {
if (err) return socket.destroy();
if (socket.destroyed) return stream.destroy();
// pipe forwards only a clean end; an aborted side must close the other, or the channel keeps its Postgres connection
socket.on("close", () => stream.destroy());
stream.on("error", () => socket.destroy()).on("close", () => socket.destroy());
socket.pipe(stream).pipe(socket);
},
);
@@ -85,7 +90,6 @@ const openTunnel = (cfg) =>
// 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})`,
@@ -114,14 +118,15 @@ const openTunnel = (cfg) =>
});
const closeTunnel = async () => {
if (server) {
await new Promise((resolve) => server.close(resolve));
server = null;
}
// the SSH connection first: its closing channels close the local sockets, which server.close waits for
if (sshClient) {
sshClient.end();
sshClient = null;
}
if (server) {
await new Promise((resolve) => server.close(resolve));
server = null;
}
localPort = null;
return null;
};
+29 -17
View File
@@ -9,9 +9,18 @@
const path = require("path");
// tests/cypress/tools/checks -> repo root is four levels up
const seeder = (name) => path.join(__dirname, "..", "..", "..", "..", "system", "seeders", name);
const SEEDER = path.join(
__dirname,
"..",
"..",
"..",
"..",
"system",
"seeders",
"TST60873_000_GesamtnoteneingabeCis4.sql",
);
/** Test semester: the running one, else the last started. Same rule as the suite and seeder 016. */
/** Test semester: the running one, else the last started. Same rule as the suite and seeder group benotungstool_noten. */
const SEM = `(SELECT studiensemester_kurzbz FROM public.tbl_studiensemester
WHERE start <= now() ORDER BY (ende >= now()) DESC, start DESC LIMIT 1)`;
@@ -19,7 +28,10 @@ module.exports = {
title: "Gesamtnoteneingabe",
sqlFiles: {
seed: [seeder("016_benotungstool_noten.sql"), seeder("019_benotungstool_fixture_erweitert.sql")],
// the idempotent fixture groups; studiengang_5 inserts without ON CONFLICT and needs a rebuild
seed: ["benotungstool_noten", "benotungstool_berechtigungen", "benotungstool_fixture_erweitert"].map(
(group) => ({ file: SEEDER, group }),
),
},
checks: [
@@ -27,20 +39,20 @@ module.exports = {
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.",
hint: "No studiensemester with start <= now(). Apply the base dump + BS003_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.",
hint: "Apply BS002_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.",
hint: "Rebuild the database. Seeder group studiengang_5 creates them.",
},
{
label: "Lehreinheiten of demolektor1 in the test semester",
@@ -50,8 +62,8 @@ module.exports = {
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.",
"demolektor1 teaches nothing in the CURRENT test semester. Seeder group studiengang_5 pins its" +
"Lehreinheiten to the semester that was active when it ran. Apply seeder group benotungstool_noten.",
},
{
label: "Lehreinheitgruppe rows with a non-NULL gruppe_kurzbz",
@@ -60,7 +72,7 @@ module.exports = {
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.",
"(verband set, gruppe_kurzbz NULL) never match. Apply seeder group benotungstool_noten.",
},
{
label: "Group memberships carrying a studiensemester",
@@ -69,14 +81,14 @@ module.exports = {
ok: (r) => r.value > 0,
hint:
"tbl_benutzergruppe.studiensemester_kurzbz must equal tbl_lehreinheit.studiensemester_kurzbz. " +
"NULL never matches. Apply 016_benotungstool_noten.sql.",
"NULL never matches. Apply seeder group benotungstool_noten.",
},
{
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.",
hint: "The suite needs at least 3 enrolled students. Apply seeder group benotungstool_noten.",
},
{
label: "tbl_note 'entschuldigt' (resolved by Bezeichnung)",
@@ -100,7 +112,7 @@ module.exports = {
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.",
"off, but a Termin3 insert would hit a foreign key error. Seeder group benotungstool_noten adds it.",
},
{
label: "Notenschluessel assigned to LV 5221",
@@ -111,20 +123,20 @@ module.exports = {
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.",
"null. Apply seeder group benotungstool_noten.",
},
{
label: "Students in the second Lehreinheit of LV 5221",
sql: `SELECT COUNT(*)::int AS value FROM campus.vw_student_lehrveranstaltung
WHERE lehreinheit_id = 51103 AND studiensemester_kurzbz = ${SEM}`,
ok: (r) => r.value > 0,
hint: "The Lehreinheit check of the write paths needs it. Apply 019_benotungstool_fixture_erweitert.sql.",
hint: "The Lehreinheit check of the write paths needs it. Apply seeder group benotungstool_fixture_erweitert.",
},
{
label: "Lektoren of Lehreinheit 51103",
sql: "SELECT COUNT(*)::int AS value FROM lehre.tbl_lehreinheitmitarbeiter WHERE lehreinheit_id = 51103",
ok: (r) => r.value >= 2,
hint: "The grader rules need two Lektoren. Apply 019_benotungstool_fixture_erweitert.sql.",
hint: "The grader rules need two Lektoren. Apply seeder group benotungstool_fixture_erweitert.",
},
{
label: "Sommersemester after its deadline, taught by demolektor1",
@@ -136,7 +148,7 @@ module.exports = {
AND make_date(substring(le.studiensemester_kurzbz FROM 3 FOR 4)::int, 11, 15) < current_date
ORDER BY le.studiensemester_kurzbz DESC LIMIT 1), '') AS value`,
ok: (r) => r.value !== "",
hint: "The deadline tests need it. Apply 019_benotungstool_fixture_erweitert.sql.",
hint: "The deadline tests need it. Apply seeder group benotungstool_fixture_erweitert.",
},
{
label: "Texts of the Vorlagen Notenfreigabe and Sancho_Mail_Template",
@@ -144,7 +156,7 @@ module.exports = {
WHERE vorlage_kurzbz IN ('Notenfreigabe', 'Sancho_Mail_Template') AND aktiv
AND COALESCE(text, '') <> ''`,
ok: (r) => r.value === 2,
hint: "Without both texts every release mail has an empty body. Apply 019_benotungstool_fixture_erweitert.sql.",
hint: "Without both texts every release mail has an empty body. Apply seeder group benotungstool_fixture_erweitert.",
},
],
};
+29 -10
View File
@@ -7,7 +7,8 @@
* node tests/cypress/tools/dbCheck.js noten env print the .env block to use
*
* A suite contributes tools/checks/<suite>.js exporting { title, checks, sqlFiles }. Connection,
* tunnel and output are shared and live here.
* tunnel and output are shared and live here. A sqlFiles entry is a path or { file, group }; a
* "-- ==== <group> ====" line starts a group.
*
* `check` is read-only; `seed` needs <PREFIX>_DB_ALLOW_WRITES=true and an owner role.
*/
@@ -32,12 +33,14 @@ 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(", ")
? 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);
};
@@ -74,7 +77,18 @@ const runChecks = async (client, suite) => {
return 1;
};
const applySqlFile = async (client, file) => {
/** The SQL of a file, or of one group in it. A group ends at the next group header. */
const readSql = (file, group) => {
const sql = fs.readFileSync(file, "utf8");
if (!group) return sql;
const parts = sql.split(/^-- ==== (.+?) ====[ \t]*\r?$/m);
for (let i = 1; i < parts.length; i += 2) if (parts[i] === group) return parts[i + 1];
return null;
};
const applySqlFile = async (client, entry) => {
const { file, group } = typeof entry === "string" ? { file: entry } : entry;
const label = path.relative(process.cwd(), file) + (group ? ` [${group}]` : "");
if (!db.writesAllowed()) {
console.error(`\nRefusing to write: set ${db.PREFIX}_DB_ALLOW_WRITES=true first.\n`);
return 2;
@@ -84,13 +98,19 @@ const applySqlFile = async (client, file) => {
return 2;
}
console.log(`\nApplying ${path.relative(process.cwd(), file)} ...\n`);
const sql = readSql(file, group);
if (sql === null) {
console.error(`\nGroup "${group}" not found in ${file}\n`);
return 2;
}
console.log(`\nApplying ${label} ...\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"));
result = await client.query(sql);
} catch (error) {
// `check` gets by as the application role; seeding creates and grants objects.
if (/permission denied/i.test(error.message)) {
@@ -107,11 +127,10 @@ const applySqlFile = async (client, file) => {
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}`)));
last.rows.forEach((row) => Object.entries(row).forEach(([k, v]) => console.log(` ${k.padEnd(24)} ${v}`)));
}
console.log(`\nDone: ${path.basename(file)}\n`);
console.log(`\nDone: ${label}\n`);
return 0;
};
@@ -175,7 +194,7 @@ const main = async () => {
client.on("notice", (n) => console.log(` NOTICE: ${n.message}`));
if (mode !== "check") {
// a suite can name several files; they run in this order
// a suite can name several files or groups; they run in this order
for (const file of [].concat(suite.sqlFiles[mode])) {
const code = await applySqlFile(client, file);
if (code !== 0) return code;