Skip to main content

hm-semester

A Python package for generating ICS calendar files for Hochschule München (HM) semesters. Create calendars with semester dates, breaks, holidays, and weekly/biweekly lecture schedules.

Features

  • Semester Calendar: Generate calendars with semester start/end dates and breaks (Christmas, Easter, Pentecost)
  • Lecture Agenda: Create individual lecture events with holiday-aware scheduling
  • Biweekly Support: Handle biweekly lectures that maintain alternating patterns even when holidays interrupt
  • Update Tracking: Deterministic UIDs and SEQUENCE numbers for calendar updates
  • Multi-language: Support for German and English labels

Usage

Semester Calendar (Holidays & Breaks)

Generate a calendar with semester dates and break periods.

CLI

python -m hm_semester --year 2025 --semester winter --lang en

This creates winter_semester_2025_en.ics with:

  • Semester start and end dates
  • Christmas break (Winter semester)
  • Easter and Pentecost breaks (Summer semester)

Python API

from hm_semester.semester import generate_calendar

cal = generate_calendar(2025, "winter", "en")
with open("semester.ics", "wb") as f:
    f.write(cal.to_ical())

Lecture Agenda

Generate individual lecture events with holiday-aware scheduling.

Basic Example

from datetime import time
from hm_semester.agenda import WeeklyEvent, create_agenda

events = [
    WeeklyEvent(
        summary="Algorithms",
        course_id="CS101",
        weekday=0,  # Monday
        start_time=time(9, 0),
        end_time=time(11, 0),
        location="Room 101",
    ),
    WeeklyEvent(
        summary="Database Systems",
        course_id="CS202",
        weekday=2,  # Wednesday
        start_time=time(14, 0),
        end_time=time(16, 0),
        location="Lab 305",
        biweekly=True,  # Every 2 weeks
        start_week=1,
    ),
]

cal = create_agenda(events, 2026, "en", "summer")
with open("lectures.ics", "wb") as f:
    f.write(cal.to_ical())

WeeklyEvent Parameters

  • summary (str): Event title
  • course_id (str): Course identifier for deterministic UID generation
  • weekday (int): Day of week (0=Monday, 6=Sunday)
  • start_time (time): Lecture start time
  • end_time (time): Lecture end time
  • location (str, optional): Room or building
  • biweekly (bool, optional): If True, event occurs every 2 weeks
  • start_week (int, optional): For biweekly, which week to start (1, 2, 3, ...)
  • timezone (str, optional): Timezone name (default: "Europe/Berlin")
  • sequence (int, optional): Version number for updates (default: 0)

Holiday-Aware Scheduling

Lectures automatically skip semester breaks:

  • Individual events are generated (no RRULE)
  • Holidays are excluded from the schedule
  • Biweekly lectures maintain alternating pattern even when holidays interrupt
    • Example: If a biweekly lecture would be in week 7 (holiday), it shifts to week 8

Biweekly Patterns

Use start_week to stagger multiple biweekly groups:

# Four groups meeting every 2 weeks, staggered by 1 week
groups = [
    WeeklyEvent("Group A", "grpA", 0, time(10, 0), time(12, 0), 
                biweekly=True, start_week=1),
    WeeklyEvent("Group B", "grpB", 0, time(10, 0), time(12, 0), 
                biweekly=True, start_week=2),
    WeeklyEvent("Group C", "grpC", 0, time(10, 0), time(12, 0), 
                biweekly=True, start_week=3),
    WeeklyEvent("Group D", "grpD", 0, time(10, 0), time(12, 0), 
                biweekly=True, start_week=4),
]

Updating Calendars

When room locations or times change, increment the sequence parameter:

# Initial calendar
events = [
    WeeklyEvent("Algorithms", "CS101", 0, time(9, 0), time(11, 0), 
                location="Room 101", sequence=0),
]
cal = create_agenda(events, 2026, "en", "summer")

# Later: room changed
events[0].location = "Room 999"
events[0].sequence = 1
updated_cal = create_agenda(events, 2026, "en", "summer")

For Thunderbird/local calendar apps: Delete the old calendar and import the new one.

For CalDAV/subscribed calendars: Events with the same UID and higher SEQUENCE are automatically updated.

Examples

See examples/create_agenda_example.py for a complete example.

Output Format

  • Each lecture gets a unique event with format: "Course Name (1)", "Course Name (2)", etc.
  • UIDs are deterministic: {course_id}-{year}-{semester}-lesson-{number}@hm.edu
  • VTIMEZONE components are included for proper timezone handling
  • SEQUENCE field tracks version numbers for updates

Installation

From PyPI

pip install hm-semester

From source

git clone https://github.com/DavidMStraub/hm-semester.git
cd hm-semester
pip install .

Development

Run tests:

pytest tests/

Release Process

This package uses GitHub Actions for automated PyPI releases:

  1. Update version in pyproject.toml
  2. Commit and push changes
  3. Create a new release on GitHub
  4. The workflow automatically builds and publishes to PyPI and TestPyPI

The workflow uses Trusted Publishing (no API tokens needed). Configure trusted publishers:

Metadata

Release files for hm-semester 26.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for hm-semester 26.1.0
File Size Uploaded
hm_semester-26.1.0.tar.gz 12.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for hm-semester 26.1.0
File Interpreter ABI Platform
hm_semester-26.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 21.2 kB

Release files / hm_semester-26.1.0.tar.gz

Download URL hm_semester-26.1.0.tar.gz
Size 12.0 kB
Tags Source
SHA-256 checksum
How to use checksums
5132e6c99dd7d8d1e09fcbd964c131dfef54b167f6aaa4c60b3020be4e3cfc01
BLAKE2b-256 checksum
How to use checksums
193abdc859d6de5a2b877f4ed15d3944d378352647d44d5cfe623bb939381cec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 30, 2026.

Transparency log

Release files / hm_semester-26.1.0-py3-none-any.whl

Download URL hm_semester-26.1.0-py3-none-any.whl
Size 9.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0e989ec0f96231af8eaf41bd985d8e38f72b1418dd8fdea63c4dcf6e057ecb17
BLAKE2b-256 checksum
How to use checksums
e90b06d117ffecf0a10bbbf4754f892e7a46c9cce9a90193087e20a83f778f7b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

26.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page