User Stories
Studierende erklärt Prüfungsrücktritt aufgrund von Krankheit
Eine Studierende kann aufgrund einer akuten Erkrankung nicht an einer Prüfung teilnehmen und möchte daher entsprechend §10 Abs. 6b und 7 APSO den Rücktritt von der Prüfung erklären. Sie hat ein ärztliches Attest als PDF/JPG/PNG-Datei entsprechend den Anforderungen.
Studierende beantragt Ruhen der Bearbeitungszeit für die Abschlussarbeit während Krankheit
Eine Studierende kann aufgrund einer akuten Erkrankung die Arbeit an ihrer Abschlussarbeit nicht fortsetzen und möchte daher nach §18 Abs. 6 Satz 3 APSO erreichen, dass die Bearbeitungsdauer ruht. Sie hat ein ärztliches Attest oder eine Arbeitsunfähigkeitsbescheinigung als PDF/JPG/PNG-Datei.
Studierende beantragt Verlängerung der Bearbeitungszeit für die Abschlussarbeit
Eine Studierende kann aufgrund eines Wasserschadens im Labor eine Woche lang keine Messungen durchführen und beantragt daher nach §18 Abs. 6 Satz 2 APSO die Verlängerung der Bearbeitungszeit. Sie hat die Zustimmung ihrer Themenstellerin eingeholt.
Studierende erklärt Prüfungsrücktritt aufgrund streikbedingter Behinderung
Eine Studierende kann aufgrund eines großflächigen Warnstreiks den Ort der Prüfung nicht oder nicht zumutbar erreichen und möchte daher entsprechend §10 Abs. 6b und 7 APSO den Rücktritt von der Prüfung erklären. Sie kann darlegen, dass alternative Verkehrsmittel von ihrem Wohnort zum Prüfungsort nicht verfügbar waren.
Studierende beantragt die Abmeldung von Modulprüfungen der Grundlagen- und Orientierungsprüfung (GOP)
Eine Studierende ist längere Zeit während des Semesters krank und kann daher nicht an Lehrveranstaltungen teilnehmen und sich angemessen auf die Prüfung vorbereiten. Sie beantragt daher, dass die Meldefiktion nach §45 Abs. 2 FPSO ausgesetzt und eine Verlängerung der Frist nach §38 Abs. 2 FPSO zur Erbringung der Credits aus der GOP gewährt wird.
Im Studienbüro geht eine Erklärung eines Prüfungsrücktritts z.B. auf Papier ein
Das Studienbüro möchte ein per Post eingereichtes Attest als Scan ablegen und zur weiteren Beurteilung an die Schriftführung weitergeben. Nach Suche über die eigenen Studierenden kann die Maske wie durch Studierende ausgefüllt werden.
Schriftführung bearbeitet Erklärungen/Anträge
Die für den Studiengang zuständige Schriftführung möchte die eingegangene Erklärungen/Anträge einsehen.
Bei einem einzelnen Antrag werden die jeweils betroffenen Prüfungstermine und die Zahl der vergangenen Rücktrittserklärungen (insbesondere abgelehnte und trotz unzureichender Nachweise anerkannte) angezeigt. Zur weiteren Bearbeitung können folgende Attribute gesetzt werden:
der Rücktritt betrifft GOP-Modulprüfungen. (sollte grundsätzlich automatisiert gekennzeichnet werden)
der Rücktritt wurde digital erklärt. (ergibt sich aus dem vorhergehenden Prozess)
- die vom Rücktritt betroffene(n) Prüfung(en) ist/sind zu wiederholen
- am zweiten Prüfungstermin desselben Semesters
- im nächsten Semester
- im nächsten Jahr
es besteht der Verdacht, dass es sich um längerfristige Beeinträchtigung handelt.
das Attest beschreibt langfristige medizinische Einschränkungen.
das Attest erfüllt die Anforderungen nicht vollständig, wird aber ausnahmsweise akzeptiert.
- ergänzende Hinweise (Standard-Texte oder Freitext)
- z.B. "Anträge auf Rücktritt von einer Prüfung sind unverzüglich zu stellen. Eine Einreichung erst erheblich nach dem Prüfungstermin ist grundsätzlich nicht ausreichend und bedarf mindestens einer Begründung. Wir akzeptieren die verspätete Einreichung ausnahmsweise."
- interne Anmerkung für Attestverbuchung in TUMonline
Bei Anerkennung des Prüfungsrücktritts bzw. Gewährung der Fristverlängerung durch den Prüfungsausschuss, verbucht die Schriftführung dies. Es erfolgt ein Eintrag der Attestverbuchung in TUMonline und eine Benachrichtigung der Studierenden über den anerkannten Rücktritt mit Hinweis auf die Wiederholung.
Bei Ablehnung des Prüfungsrücktritts ist eine Begründung anzugeben, wobei Standard-Texte zur Auswahl stehen, z.B.
- Aus dem vorgelegten Nachweis geht nicht hervor, weshalb Sie nicht an der Prüfung teilnehmen können.
- Die Ankündigung eines Warnstreiks allein ist kein Grund für den Rücktritt von einer zukünftigen Prüfung. Sie müssen durch die Maßnahme tatsächlich daran gehindert worden sein, die Prüfung abzulegen.
- Das vorgelegte Attest macht keine ausreichenden Angaben zu Beginn und Ende der krankheitsbedingten Prüfungsunfähigkeit.
- Die Gründe wurden nicht unverzüglich vorgebracht.
Die Ablehnung wird i.d.R. zunächst nur vorläufig verbucht, was eine E-Mail-Benachrichtigung der Studierenden unter Angabe der Gründe hat und die Studierenden dann eine Woche Zeit gegeben wird, den Nachweis/die Begründung nachzubessern.
Eine endgültige Ablehnung wird an das für den Studiengang zuständige CST/GO-Team zur Verbescheidung weitergegeben.
Von einem Rücktritt sind potentiell Prüfungen eines anderen Studiengangs (Doppel-Immatrikulation) betroffen
Die die Rücktrittserklärung bearbeitende Schriftführung möchte darauf hingewiesen werden, dass Prüfungsnameldungen betroffen sind, die nicht in dem Studiengang erfolgt sind, in dem der Rücktritt erklärt wird. Anschließend soll die zuständige Schriftführung für den anderen Studiengang um Stellungnahme gebeten werden.
Angaben
- Stammdaten (Name, Matrikel)
- liegen aufgrund des Logins vor bzw. Suchmaske für Studienbüro
- Studiengang für den der Prüfungsrücktritt erklärt wird
- Auswahl aus Immatrikulationen
- Art des Rücktritts
- Rücktritt mit ärztlichem Attest (z.B. akute Erkrankung, ...)
- Rücktritt aus anderen Gründen (z.B. gerichtliche Vorladung, ...)
- Aufhebung Meldefiktion/Abmeldung (z.B. lange Krankheit während der Vorlesungszeit, ...)
- Verlängerung der Bearbeitungszeit einer Abschlussarbeit mit ärztlichem Attest (z.B. akute Erkrankung, ...)
- Verlängerung der Bearbeitungszeit einer Abschlussarbeit aus anderen Gründen (z.B. Defekt an unverzichtbarem Großgerät, ...)
- Beginn und Ende der Prüfungsunfähigkeit
- Anzeige der angemeldeten und damit betroffenen Prüfungen
- abhängig von 3. kann direkt eine Prüfung erfolgen: a und b können regelmäßig frühestens kurz vor der Prüfung und spätestens wenige Tage nach der Prüfung erklärt werden, während c regelmäßig deutlich vor dem Prüfungstermin zu erklären ist (Unverzüglichkeit)
- Nachweis
- Upload ärztliches Attest (verpflichtend bei 3.a.)
- Freitext-Begründung/ergänzende Angaben zum Attest (verpflichtend, falls kein Upload)
Rückfragen an andere Prüfungsausschüsse
Es ist möglich zu einzelnen Rücktrittserklärungen Rückfragen an andere Prüfungsausschüsse zu stellen – diese werden per E-Mail benachrichigt und können direkt im System antworten. Auf die gleiche Weise erfolgt die Bestätigung eines Antrags auf Verlängerung der Abgabefrist einer Abschlussarbeit aus anderen Gründen bei den Themenstellenden.
Wenn die Studierende in dem Zeitraum, für Prüfungen in einem anderen Studiengang, für den eine Zuständigkeit konfiguriert ist, angemeldet ist, wird automatisch eine entsprechende Rückfrage vorgeschlagen.
Automatisierung
Studierendendaten werden momentan aus DSSNAT bezogen, welches diese über generische Exporte und GUI-Automatisierung aus TUMonline bezieht.
Nach Eingang des Antrags wird anhand der betroffenen Prüfungstermine über die "Exam Registrations Service"-API ermittelt, ob GOP-Modulprüfungen betroffen sind und eine entsprechende Kennzeichnung des Antrags vorgenommen.
Außerdem kann aus den angekündigten Prüfungsterminen, welche in DSSNAT aus "Exam Events Service" vorliegen, automatisch ermittelt werden, ob die Prüfung am zweiten Prüfungstermin desselben Semesters wiederholt werden muss, und eine entsprechende Kennzeichnung vorgenommen werden.
Anerkannte Rücktritte werden mit Hilfe von GUI-Automatisierung in TUMonline eingetragen.
studakt_add(
st_person_nr, # numerische ID der Studierenden
description, # ID des Attests in DSSNAT
document_type=15, # Attestverbuchung (ATTESTVB)
data=[
('GRUND', 'Attest'), # alternativ "Rücktritt mit anerkanntem Grund"
('BEGIN', ...), # Beginn der Prüfungsunfähigkeit
('END', ...), # Ende der Prüfungsunfähigkeit
('ANMERKUNG', ...), # optional interne Anmerkung der Schriftführung
],
)
E-Mail-Benachrichtigung der Schriftführung erfolgt, wenn Studierende oder das Studienbüro (ohne Schriftführer-Berechtigung) eine Erklärung/einen Antrag stellen. Je SPO-Version kann hierfür ein Team (wahlweise werden individuell die Team-Mitglieder mit ihren persönlichen Adressen benachrichtigt oder eine Team-Adresse) gesetzt werden.
Das zuständige CST/GO-Team ergibt sich aus der Zuordnung im APV-Tool.
Datenbank-Modell
class StudentAttestType(Base):
'''StudentAttestType model representing a type of certificate for exam exceptions'''
__tablename__ = 'students_attesttypes'
attest_type: Mapped[str] = mapped_column(String, primary_key=True, doc='ID of the attest type')
attest_type_group: Mapped[str | None] = mapped_column(String, nullable=False, doc='Attest types with the same attest_type_group may not conflict each other')
sorting: Mapped[int] = mapped_column(Integer, nullable=False)
attesttype_de: Mapped[str] = mapped_column(String, nullable=False, doc='Name of the attest type in German')
attesttype_en: Mapped[str] = mapped_column(String, nullable=False, doc='Name of the attest type in English')
attesttype_description_de: Mapped[str | None] = mapped_column(String, doc='Description of the attest type for student ui in German')
attesttype_description_en: Mapped[str | None] = mapped_column(String, doc='Description of the attest type for student ui in English')
document_type_id_tumonline: Mapped[int | None] = mapped_column(Integer, nullable=True, doc='ID of document type in TUMonline')
attest_type_tumonline: Mapped[str | None] = mapped_column(String, nullable=True, doc='Reason of attest for document type ID')
student_request: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=true(), doc='Whether students may create this request themselves')
upload_required: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=true(), doc='Whether students need to upload a file')
full_exemption: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=false(), doc='Whether this attest type corresponds to full exemption')
consult_examiner: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=false(), doc='Whether consultation of examiner/supervisor is required by default')
allow_future: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=false(), doc='Whether request for future dates is allowed')
semester_aligned: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=true(), doc='Whether request refres to a semester only')
class StudentAttestConsultationType(Base):
'''StudentAttestConsultationType model representing a type of consultation request to a certificate for exam exceptions'''
__tablename__ = 'students_attest_consultationtypes'
consultationtype_id: Mapped[str] = mapped_column(String, primary_key=True, doc='ID of the consultation type')
consultationtype_de: Mapped[str] = mapped_column(String, nullable=False, doc='Name of the consultation typein German')
consultationtype_en: Mapped[str] = mapped_column(String, nullable=False, doc='Name of the consultation typein English')
class StudentAttest(Versioned, Base):
'''StudentAttest model representing a certificate for exam exceptions'''
__tablename__ = 'students_attests'
attest_id: Mapped[UUID] = mapped_column(postgresql.UUID(as_uuid=True), primary_key=True, server_default=text('gen_random_uuid()'), doc='ID of the attest.')
matrikel: Mapped[str] = mapped_column(String(length=8), nullable=False, doc=Student.matrikel.__doc__)
study_id: Mapped[str] = mapped_column(String, nullable=False, doc=Program.study_id.__doc__)
semester_key: Mapped[SemesterKey] = mapped_column(String(length=5), ForeignKey(Semester.semester_key), nullable=False, doc='Semester of the exception.')
attest_type: Mapped[str] = mapped_column(String, ForeignKey(StudentAttestType.attest_type), nullable=False, server_default='withdrawal_attest', doc='Type of attest record')
valid_from: Mapped[datetime.date] = mapped_column(postgresql.DATE, server_default=func.current_date(), nullable=False, doc='This exception is valid from.')
valid_through: Mapped[datetime.date | None] = mapped_column(postgresql.DATE, doc='This exception is valid through.')
request_date: Mapped[datetime.datetime] = mapped_column(postgresql.TIMESTAMP(timezone=True), server_default=func.now(), doc='Timestamp of request by the student.', nullable=False)
deny_date: Mapped[datetime.datetime | None] = mapped_column(postgresql.TIMESTAMP(timezone=True), doc='Timestamp of the denial of the request.')
denyfinal_date: Mapped[datetime.datetime | None] = mapped_column(postgresql.TIMESTAMP(timezone=True), doc='Timestamp of the final denial of the request.')
accept_date: Mapped[datetime.datetime | None] = mapped_column(postgresql.TIMESTAMP(timezone=True), doc='Timestamp of the acceptance of the request.')
apv_date: Mapped[datetime.datetime | None] = mapped_column(postgresql.TIMESTAMP(timezone=True), doc='Timestamp of notification of CST/GO.')
tumonline_document_id: Mapped[int | None] = mapped_column(Integer, doc='ID of the document in TUMonline')
full_exemption: Mapped[bool] = mapped_column(Boolean, nullable=False, doc='Student is fully exempt from any duty to take exams.')
extension: Mapped[Decimal | None] = mapped_column(Numeric, doc='Student should have exam time extended by this fraction.')
remark_student: Mapped[str | None] = mapped_column(String, doc='Remark the student gave with their request.')
remark_internal: Mapped[str | None] = mapped_column(String, doc='Internal remark for the student office.')
remark_examiner: Mapped[str | None] = mapped_column(String, doc='Note for the examiner(s) in German.')
remark_examiner_en: Mapped[str | None] = mapped_column(String, doc='Note for the examiner(s) in English.')
deny_reason: Mapped[str | None] = mapped_column(String, doc='Reason for denial by student office in German.')
deny_reason_en: Mapped[str | None] = mapped_column(String, doc='Reason for denial by student office in English.')
gop: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=false(), doc='The certificate concerns GOP exams.')
digital_copy: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=false(), doc='The student handed in the documents digitally.')
specialneeds: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=false(), doc='The student might have a longterm condition.')
longterm: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=false(), doc='The attest refers to a longterm condition.')
accept_deficient: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=false(), doc='The attest does not fulfill the requirements but is accepted as an exception.')
repeat_mode: Mapped[str | None] = mapped_column(String, doc='When the exams need to be repeated')
additional_note: Mapped[str | None] = mapped_column(String, doc='Additional note to the student in German')
additional_note_en: Mapped[str | None] = mapped_column(String, doc='Additional note to the student in English')
modified_username: Mapped[TUMKennung | None] = mapped_column(String(7), ForeignKey(PersonLocal.username, onupdate='CASCADE'), doc='Username of the last modification.')
modified: Mapped[datetime.datetime] = mapped_column(postgresql.TIMESTAMP(timezone=True), server_default=func.now(), doc='Timestamp of last modification of the database entry.', nullable=False)
program: Mapped[ProgramCombined] = relationship(ProgramCombined, foreign_keys=[study_id], primaryjoin=study_id==ProgramCombined.study_id, uselist=False, viewonly=True)
semester: Mapped[Semester] = relationship(Semester)
attesttype: Mapped[StudentAttestType] = relationship(StudentAttestType)
modified_user: Mapped[Person | None] = relationship(Person, foreign_keys=[modified_username], viewonly=True)
theses: Mapped[list[Thesis]] = relationship(Thesis,
foreign_keys=[matrikel, study_id, valid_from, valid_through],
secondary=StatisticsStudent.__table__,
secondaryjoin=(StatisticsStudent.__table__.c.matrikel==matrikel)&(Thesis.student_username==StatisticsStudent.__table__.c.username),
primaryjoin=(StatisticsStudent.__table__.c.matrikel==matrikel)&(Thesis.student_username==StatisticsStudent.__table__.c.username)&(Thesis.study_id==study_id)&(Thesis.start_date<=valid_through)&(func.coalesce(Thesis.cancel_date, Thesis.end_date, Thesis.due_date)>=valid_from),
backref=backref('attests', order_by='StudentAttest.valid_from'),
order_by=Thesis.start_date,
viewonly=True, uselist=True)
consultations: Mapped[list[StudentAttestConsultation]] = relationship('StudentAttestConsultation', back_populates='attest')
@hybrid_property
def open_consultation_count(self) -> int:
return sum(1 for e in self.consultations if e.answer_date is None)
@open_consultation_count.inplace.expression
@classmethod
def _open_consultation_count_expression(cls) -> ColumnElement[int]:
return select(func.count()).where((StudentAttestConsultation.attest_id==cls.attest_id)&(StudentAttestConsultation.answer_date==None)).correlate_except(StudentAttestConsultation).scalar_subquery()
class StudentAttestConsultation(Base):
'''StudentAttestConsultation model representing a consultation request to a certificate for exam exceptions'''
__tablename__ = 'students_attest_consultations'
attest_id: Mapped[UUID] = mapped_column(postgresql.UUID(as_uuid=True), ForeignKey(StudentAttest.attest_id), nullable=False, doc=StudentAttest.attest_id.__doc__)
consultationtype_id: Mapped[str] = mapped_column(String, ForeignKey(StudentAttestConsultationType.consultationtype_id), nullable=False, doc=StudentAttestConsultationType.consultationtype_id.__doc__)
consultation_id: Mapped[UUID] = mapped_column(postgresql.UUID(as_uuid=True), primary_key=True, server_default=text('gen_random_uuid()'), doc='Sequence number of the consultation')
consulted_username: Mapped[TUMKennung | None] = mapped_column(String(7), ForeignKey(PersonLocal.username, onupdate='CASCADE'), doc='Username of the consulted.')
consulted_team_id: Mapped[str | None] = mapped_column(String, ForeignKey(ProgramTeam.team_id, onupdate='CASCADE'), doc='ID of the consulted team.')
request_de: Mapped[str | None] = mapped_column(String, doc='Additional note in the consultation request in German')
request_en: Mapped[str | None] = mapped_column(String, doc='Additional note in the consultation request in English')
request_date: Mapped[datetime.datetime] = mapped_column(postgresql.TIMESTAMP(timezone=True), server_default=func.now(), doc='Date of the request')
answer_de: Mapped[str | None] = mapped_column(String, doc='Additional note in the answer in German')
answer_en: Mapped[str | None] = mapped_column(String, doc='Additional note in the answer in English')
answer_date: Mapped[datetime.datetime | None] = mapped_column(postgresql.TIMESTAMP(timezone=True), doc='Date of the answer')
approved: Mapped[bool | None] = mapped_column(Boolean, doc='Whether the request was approved')
consultationtype: Mapped[StudentAttestConsultationType] = relationship(StudentAttestConsultationType, foreign_keys=[consultationtype_id])
modified_username: Mapped[TUMKennung | None] = mapped_column(String(7), ForeignKey(PersonLocal.username, onupdate='CASCADE'), doc='Username of the last modification.')
modified: Mapped[datetime.datetime] = mapped_column(postgresql.TIMESTAMP(timezone=True), server_default=func.now(), doc='Timestamp of last modification of the database entry.', nullable=False)
__table_args__ = (CheckConstraint((consulted_username != None)|(consulted_team_id != None), name='consulted_username_or_consulted_team_id'), {})
consulted_user: Mapped[Person | None] = relationship(Person, foreign_keys=[consulted_username], viewonly=True)
consulted_team: Mapped[ProgramTeam | None] = relationship(ProgramTeam, foreign_keys=[consulted_team_id], viewonly=True)
modified_user: Mapped[Person | None] = relationship(Person, foreign_keys=[modified_username], viewonly=True)
attest: Mapped[StudentAttest] = relationship(StudentAttest, back_populates='consultations')
Das Datenbank-Modell kann bereits für Nachteilsausgleiche genutzt werden (Spalte full_exemption=FALSE und dann extension, remark_examiner, remark_examiner_en).
valid_from, valid_through werden aktuell nur als Datum gespeichert. Bei der Übertragung nach TUMonline wird valid_from mit Uhrzeit 0:00 und valid_through mit Uhrzeit 23:59 kombiniert.
Die Tabelle ist historisiert, d.h. bei jeder Änderung wird die geänderte Zeile in eine Tabelle students_attests_history kopiert.
Berechtigungen
Studierende können für sich selbst Erklärungen abgeben/Anträge stellen, wenn für ihren Studiengang eine Zuständigkeit mit einem Team "Prüfungsrücktritt" hinterlegt ist.
Personen mit Berechtigung std.clerk (aktuell gemappt auf PRV/PV_PRUEFUNG_PARAMETER) dürfen Erklärungen/Anträge für eigene Studierende anlegen und bearbeiten, aber nicht anerkennen oder ablehnen.
Personen mit Berechtigung std.admin dürfen Erklärungen/Anträge für eigene Studierende anlegen, bearbeiten, anerkennen und ablehnen.
Screenshots
Übersicht Studierende
Neue Rücktrittserklärung Studierende
Rücktrittserklärung bearbeiten (Schriftführung)
Das Studienbüro (Berechtigung std.clerk) hat im Wesentlichen dieselbe Sicht außer den Feldern zum Anerkennen oder Ablehnen der Erklärung/des Antrags.
E-Mail-Benachrichtigung Studierende Anerkennung
Die E-Mail-Vorlage kann je SPO-Version angepasst werden, was insbesondere wegen des Hinweises auf die Beratung bei Verdacht auf längerfristige oder wiederholte gesundheitliche Probleme sinnvoll ist.
E-Mail-Benachrichtigung Studierende vorläufige Ablehnung

