No description
  • Python 34.8%
  • JavaScript 28.1%
  • CSS 27.4%
  • HTML 8.5%
  • Dockerfile 1.2%
Find a file
Freydis Mende 1ca03f13b8
All checks were successful
CI / Lint (push) Successful in 8s
CI / Docker build & push (push) Successful in 3s
CI / Deploy (push) Successful in 0s
Make fleamarkets.ics generation deterministic
DTSTAMP was derived from wall-clock time, so the whole file changed on
every regeneration even with no new data -- the scheduled scrape
workflow would have committed a no-op update every day. Derive it from
each event's own date instead, so output is a pure function of
fleamarkets.json.
2026-08-16 11:27:49 +02:00
.forgejo/workflows Add iCalendar export and scheduled scraping 2026-08-16 10:30:36 +02:00
.github add ci magic 2026-06-01 17:03:00 +02:00
.gitignore Make fleamarkets.ics generation deterministic 2026-08-16 11:27:49 +02:00
.pre-commit-config.yaml add ci magic 2026-06-01 17:03:00 +02:00
default.conf Add iCalendar export and scheduled scraping 2026-08-16 10:30:36 +02:00
docker-compose.yml Add iCalendar export and scheduled scraping 2026-08-16 10:30:36 +02:00
Dockerfile Add iCalendar export and scheduled scraping 2026-08-16 10:30:36 +02:00
fleamarkets.ics Make fleamarkets.ics generation deterministic 2026-08-16 11:27:49 +02:00
fleamarkets.json fix scraper encoding 2026-06-07 18:24:06 +02:00
ics_export.py Make fleamarkets.ics generation deterministic 2026-08-16 11:27:49 +02:00
index.html Add iCalendar export and scheduled scraping 2026-08-16 10:30:36 +02:00
pyproject.toml Add iCalendar export and scheduled scraping 2026-08-16 10:30:36 +02:00
README.md Add iCalendar export and scheduled scraping 2026-08-16 10:30:36 +02:00
scrape.py Add iCalendar export and scheduled scraping 2026-08-16 10:30:36 +02:00
script.js add mobile agenda list view 2026-07-07 20:05:21 +02:00
style.css Add iCalendar export and scheduled scraping 2026-08-16 10:30:36 +02:00
uv.lock fix lint 2026-06-01 18:00:35 +02:00

Flohmarkt-Kalender

A flea-market calendar for the Flensburg area, live at flohmarkt.freyd.is.

Architecture

Two independent parts:

  • Scraper (scrape.py) — fetches dates from flohmarkt-in-flensburg.de and marktcom.de and writes them to fleamarkets.json. Run manually or on a schedule.
  • Frontend (index.html, style.css, script.js) — vanilla HTML/CSS/JS calendar that reads fleamarkets.json at runtime. Served by nginx via Docker. No build step, no framework.
  • iCalendar export (ics_export.py) — converts fleamarkets.json into fleamarkets.ics, an RFC 5545 feed served alongside the site so it can be subscribed to from calendar apps. Regenerated automatically at the end of every scrape run; run uv run build-ics to regenerate it standalone (e.g. after a manual edit to fleamarkets.json).

fleamarkets.json is the contract between scraper and frontend: an array of { "date": "YYYY-MM-DD", "location": string } objects.

Subscribing on iPhone / Apple Calendar

The calendar is published as a live-updating subscription feed at https://flohmarkt.freyd.is/fleamarkets.ics.

  • Tap the "📅 Im Kalender abonnieren" link on the site — Safari hands the webcal:// link straight to the Calendar app.
  • Or manually: Settings → Calendar → Accounts → Add Account → Other → Add Subscribed Calendar, and paste the URL above.

iOS refreshes subscribed calendars periodically (not instantly) — new/changed dates from fleamarkets.json show up automatically after the next scrape + refresh cycle.

Development

Requires Python 3.11+ and uv.

uv sync --group dev   # install runtime + dev deps
pre-commit install    # set up git hooks
uv run scrape         # run the scraper

Deployment

The Docker image (nginx:alpine) is built and pushed in CI, then deployed via Portainer webhook.

docker compose up     # run locally

Keeping dates up to date

A scheduled Forgejo Actions workflow (.forgejo/workflows/scrape.yaml) runs the scraper once a day, and commits + pushes fleamarkets.json/fleamarkets.ics if anything changed. That push triggers the normal CI pipeline (lint → Docker build/push → Portainer webhook deploy), so no manual steps are needed to keep the live calendar current. Trigger it manually anytime via the "Run workflow" button (workflow_dispatch) in the Actions tab.

Requires the repo's Actions token to have contents: write permission (checked under Settings → Actions in this Forgejo instance) — if the scheduled run fails to push, that's the first thing to check.