Skip to content

email

academic_doc_generator.core.email

Centralized email generation with templates.

ColloquiumRegistrationEmail

Template for colloquium registration emails.

Source code in src/academic_doc_generator/core/email.py
class ColloquiumRegistrationEmail:
    """Template for colloquium registration emails."""

    def render(
        self,
        student: EmailRecipient,
        examiner: str,
        date: str,
        time: str,
        location_text: str,
    ) -> str:
        """Render the registration email text.

        Args:
            student: The student being registered.
            examiner: Name of the first examiner.
            date: Date of the colloquium (DD.MM.YYYY).
            time: Time of the colloquium (HH:MM).
            location_text: Formatted location description.

        Returns:
            The rendered email body as a string.
        """
        weekday = weekday_from_string(date)

        examiner_str = examiner.title() if examiner else "Unbekannt"
        return f"""Lieber Prüfungsservice,
hiermit möchte ich {student.full_name_with_id} zum Kolloquium anmelden. Dieses findet statt am:
{weekday}, {date}, um {time},
{location_text}.
{student.title_last_name}: Bitte bereiten Sie eine max. 15-minütige Präsentation zu Ihrer Arbeit vor (wenn möglich inkl. Demo).
Viele Grüße,
{examiner_str}"""

render(student, examiner, date, time, location_text)

Render the registration email text.

Parameters:

Name Type Description Default
student EmailRecipient

The student being registered.

required
examiner str

Name of the first examiner.

required
date str

Date of the colloquium (DD.MM.YYYY).

required
time str

Time of the colloquium (HH:MM).

required
location_text str

Formatted location description.

required

Returns:

Type Description
str

The rendered email body as a string.

Source code in src/academic_doc_generator/core/email.py
    def render(
        self,
        student: EmailRecipient,
        examiner: str,
        date: str,
        time: str,
        location_text: str,
    ) -> str:
        """Render the registration email text.

        Args:
            student: The student being registered.
            examiner: Name of the first examiner.
            date: Date of the colloquium (DD.MM.YYYY).
            time: Time of the colloquium (HH:MM).
            location_text: Formatted location description.

        Returns:
            The rendered email body as a string.
        """
        weekday = weekday_from_string(date)

        examiner_str = examiner.title() if examiner else "Unbekannt"
        return f"""Lieber Prüfungsservice,
hiermit möchte ich {student.full_name_with_id} zum Kolloquium anmelden. Dieses findet statt am:
{weekday}, {date}, um {time},
{location_text}.
{student.title_last_name}: Bitte bereiten Sie eine max. 15-minütige Präsentation zu Ihrer Arbeit vor (wenn möglich inkl. Demo).
Viele Grüße,
{examiner_str}"""

EmailRecipient dataclass

Represents an email recipient with formal German addressing.

Source code in src/academic_doc_generator/core/email.py
@dataclass
class EmailRecipient:
    """Represents an email recipient with formal German addressing."""

    first_name: str
    last_name: str
    gender: str  # "Herr", "Frau", or "Herr/Frau"
    identifier: str = ""  # Matriculation number or other ID

    @property
    def formal_salutation(self) -> str:
        """German formal greeting."""
        if self.gender == "Herr/Frau" or not self.gender:
            return "Guten Tag"
        return f"Guten Tag {self.gender} {self.last_name}"

    @property
    def full_name_with_title(self) -> str:
        """E.g., 'Herr Max Mustermann'"""
        if self.gender == "Herr/Frau" or not self.gender:
            return f"{self.first_name} {self.last_name}"
        return f"{self.gender} {self.first_name} {self.last_name}"

    @property
    def full_name_with_id(self) -> str:
        """E.g., 'Herr Max Mustermann (123456)'"""
        name = self.full_name_with_title
        if self.identifier:
            return f"{name} ({self.identifier})"
        return name

    @property
    def title_last_name(self) -> str:
        """E.g., 'Herr Mustermann'"""
        if self.gender == "Herr/Frau" or not self.gender:
            return self.last_name
        return f"{self.gender} {self.last_name}"

formal_salutation property

German formal greeting.

full_name_with_id property

E.g., 'Herr Max Mustermann (123456)'

full_name_with_title property

E.g., 'Herr Max Mustermann'

title_last_name property

E.g., 'Herr Mustermann'

EmailTemplate

Bases: Protocol

Protocol for email templates.

Source code in src/academic_doc_generator/core/email.py
class EmailTemplate(Protocol):
    """Protocol for email templates."""

    def render(self, **kwargs) -> str: ...

FinalGradeEmail

Template for submitting final marks to the examination service.

Source code in src/academic_doc_generator/core/email.py
class FinalGradeEmail:
    """Template for submitting final marks to the examination service."""

    def render(
        self,
        student: EmailRecipient,
        examiner: str,
    ) -> str:
        examiner_str = examiner.title() if examiner else "Unbekannt"
        return f"""Lieber Prüfungsservice,
hiermit möchte ich die Bewertung für {student.full_name_with_id} einreichen (s. Anhang).
Viele Grüße,
{examiner_str}"""

StudentFeedbackEmail

Template for providing feedback directly to the student.

Source code in src/academic_doc_generator/core/email.py
class StudentFeedbackEmail:
    """Template for providing feedback directly to the student."""

    def render(
        self,
        student: EmailRecipient,
        mark: str,
        feedback_bulletpoints: str,
        examiner: str,
    ) -> str:
        examiner_str = examiner.title() if examiner else "Unbekannt"
        return f"""{student.formal_salutation},

ich habe Ihre Arbeit mit einer {mark} bewertet. Hier ist mein Feedback zu Ihrer Arbeit:

{feedback_bulletpoints}

Viele Grüße,
{examiner_str}"""

weekday_from_string(date_str, lang='de')

Get the weekday name from a date string (DD.MM.YYYY).

Parameters:

Name Type Description Default
date_str str

Date in format DD.MM.YYYY.

required
lang str

Language for the weekday name ("de" or "en"). Defaults to "de".

'de'

Returns:

Type Description
str

Name of the weekday in the specified language.

Raises:

Type Description
ValueError

If the date format is invalid or language is unsupported.

Source code in src/academic_doc_generator/core/email.py
def weekday_from_string(date_str: str, lang: str = "de") -> str:
    """Get the weekday name from a date string (DD.MM.YYYY).

    Args:
        date_str: Date in format DD.MM.YYYY.
        lang: Language for the weekday name ("de" or "en"). Defaults to "de".

    Returns:
        Name of the weekday in the specified language.

    Raises:
        ValueError: If the date format is invalid or language is unsupported.
    """
    date = datetime.strptime(date_str, "%d.%m.%Y")
    weekday_idx = date.weekday()  # Montag = 0

    weekdays = {
        "de": [
            "Montag",
            "Dienstag",
            "Mittwoch",
            "Donnerstag",
            "Freitag",
            "Samstag",
            "Sonntag",
        ],
        "en": [
            "Monday",
            "Tuesday",
            "Wednesday",
            "Thursday",
            "Friday",
            "Saturday",
            "Sunday",
        ],
    }

    if lang not in weekdays:
        raise ValueError(f"Unsupported language: {lang}")

    return weekdays[lang][weekday_idx]