Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Build and Deploy Jekyll site to Pages

on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python for preprocessing
uses: actions/setup-python@v4
with:
python-version: "3.11"

- name: Generate NEXT_MEETINGS in README
run: |
python scripts/render_next_meetings.py

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.1" # Not needed with a .ruby-version file
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
cache-version: 0 # Increment this number if you need to re-download cached gems

- name: Setup Pages
id: pages
uses: actions/configure-pages@v5

- name: Build with Jekyll
# Outputs to the './_site' directory by default
run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
env:
JEKYLL_ENV: production

- name: Upload artifact
# Automatically uploads an artifact from the './_site' directory by default
uses: actions/upload-pages-artifact@v3

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
31 changes: 3 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,17 @@ Außerdem soll mit den Vorträgen das Wissen von und um C++ erweitert werden kö

# Termine

Das nächste Treffen findet am **Mittwoch, den 3. Dezember 2025** statt.
Wir treffen uns ab **18.30 Uhr** bei [**Utimaco**](https://utimaco.com/), die Vorträge beginnen ab 19.00 Uhr.
Wir streben einen (grob) zwei-monatigen Rhythmus an. Weitere Informationen über [Meetup](https://www.meetup.com/de-DE/C-User-Gruppe-Aachen).

Geplante Vorträge:

* **Safer integer handling with modern C++** (John Franklin Rickard)
* **ASIO & Coro - Networking Programming with C++20 Coroutines** (Peter Eisenlohr)

Ort der Veranstaltung:

[**Utimaco**](https://utimaco.com/)
Germanusstraße 4
52080 Aachen

Hinweis: Bitte den Eingang auf der Alt-Haarener Straße 36 benutzen.

Weitere Informationen über [Meetup](https://www.meetup.com/de-DE/C-User-Gruppe-Aachen).

## Nächste Termine

Wir streben einen (grob) zwei-monatigen Rhythmus an, vorläufige Termine sind:

* 4\. Februar 2026 (ModuleWorks)
* 1\. April 2026 (SLB)
* 3\. Juni 2026 (Magma)
* 5\. August 2026 (Utimaco)
* 7\. Oktober 2026 (ModuleWorks)
* 2\. Dezember 2026 (SLB)
{{ NEXT_MEETINGS }}

## Elektronischer Kalender

Der jeweils aktuellste Stand der Planung befindet sich unter<br>[`https://github.com/cpp-aachen/cpp-aachen.github.io/releases/download/latest/cpp-aachen.ics`](https://github.com/cpp-aachen/cpp-aachen.github.io/releases/download/latest/cpp-aachen.ics).

Diesen Kalender kann man sich durch Angabe der URL in der eigenen Kalender-App auf dem Mobiltelefon oder dem entsprechenden Programm (Outlook, Thunderbird) abonnieren und bleibt so immer auf dem neuesten Stand.

# Vorträge
## Vorträge

Die Treffen leben von *euren* Vorträgen, meldet euch gerne mit euren Vorschlägen und Ideen.
Wir möchten auch ausdrücklich darauf hinweisen, dass nicht jeder Vortrag über Template Metaprogrammierung gehen muss, auch "einfachere" Themen sind sehr willkommen und wir hatten bereits viele Vorträge die sehr gut angekommen sind.
Expand Down
117 changes: 117 additions & 0 deletions scripts/render_next_meetings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/env python3
import json
import sys
from datetime import datetime
from zoneinfo import ZoneInfo
from pathlib import Path

CAL_PATH = Path("calendar/cpp-aachen.jsoncal")
README_SRC = Path("README.md")
README_OUT = Path("README.md") # überschreibt im Arbeitsverzeichnis (nur im Build!)
PLACEHOLDER = "{{ NEXT_MEETINGS }}"
TZ = ZoneInfo("Europe/Berlin")

def load_events():
with CAL_PATH.open("r", encoding="utf-8") as f:
data = json.load(f)
events = data.get("events", [])
# parse start as aware datetime in specified timezone (if provided), otherwise naive->Berlin
norm_events = []
for e in events:
start_str = e.get("start")
if not start_str:
continue
tzname = e.get("timezone")
tz = ZoneInfo(tzname) if tzname else TZ
try:
# ISO 8601 string -> naive dt; assign tz for proper comparison and formatting
dt = datetime.fromisoformat(start_str)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=tz)
else:
# normalize to event tz if provided
if tzname:
dt = dt.astimezone(tz)
except Exception:
continue
norm_events.append({
"summary": e.get("summary", "C++ User Group Aachen"),
"location": e.get("location", ""),
"description": e.get("description", ""),
"url": e.get("url", ""),
"start": dt,
"tz": tz
})
return norm_events

def filter_future(events):
now = datetime.now(TZ)
return [e for e in events if e["start"] >= now]

def format_markdown(events, limit=None):
if not events:
return "_Aktuell sind keine zukünftigen Termine geplant._"
# optional: limit anzeigen (z.B. Top 6)
if limit:
events = events[:limit]
lines = []
# Markdown-Format: - Mi, 3. Dezember 2025, 18:30 — Utimaco, Aachen — Kurzbeschreibung
# inklusive Link, wenn vorhanden
for e in events:
dt = e["start"]
date_str = dt.strftime("%A, %-d. %B %Y, %H:%M")
# Einige Systeme unterstützen %-d nicht (Mac). Fallback:
if "%-d" in "%-d":
date_str = dt.strftime("%A, %d. %B %Y, %H:%M")
# führende Null optional entfernen:
if date_str[9] == "0":
date_str = date_str[:9] + date_str[10:]

date_str = date_str.replace("Monday","Montag")\
.replace("Tuesday","Dienstag")\
.replace("Wednesday","Mittwoch")\
.replace("Thursday","Donnerstag")\
.replace("Friday","Freitag")\
.replace("Saturday","Samstag")\
.replace("Sunday","Sonntag")


parts = ['- **' + f"{date_str}" + 'Uhr**']

if e["location"]:

location_text = ", [" \
+ e["location"] \
+ "](https://www.google.com/maps/search/?api=1&query=" \
+ e["location"].replace(" ", "+") \
+ ")\n"

parts.append(location_text)

desc = e["description"].strip() if e["description"] else ""

if (desc != ""):
parts.append(" - ")
parts.append(desc.replace("\n", "\n - "))

parts.append("\n")

lines.append("".join(parts))


return "".join(lines)

def main():
events = load_events()
events = sorted(events, key=lambda e: e["start"])
future = filter_future(events)
md = format_markdown(future, limit=10)

text = README_SRC.read_text(encoding="utf-8")
if PLACEHOLDER not in text:
print(f"Warnung: Platzhalter {PLACEHOLDER} nicht in README gefunden.", file=sys.stderr)
text = text.replace(PLACEHOLDER, md)
README_OUT.write_text(text, encoding="utf-8")

if __name__ == "__main__":
main()