Hamdy Tabsissi
繁體中文

EventMap 正式上線 個人專案

今晚一個好去處,而不是 200 個選項

一張免費地圖,一眼看出今晚巴黎有哪些活動,並依文化與類別分類。目的是幫人輕鬆做出選擇,而不是被選項淹沒。

需求

忙了一整週,大家都想出門走走,但要滑過幾百個活動實在很累。選項太多,最後往往乾脆待在家。

構想

真正的困擾不是選項太少,而是太多。所以這個應用程式不會一打開就丟出一整份清單,而是從一個簡單的提問出發:今晚,在附近可以做什麼?

我打造了什麼

  • 一個網站,一打開就在地圖上顯示今晚的活動,並以手機使用為優先設計。
  • 自動收集程式,彙整巴黎市政府的開放資料、駐巴黎各國文化機構(FICEP)及其他公開來源的活動,各來源依自己的週期更新。
  • 可依文化來源與活動類別瀏覽,隨當下心情挑選想去的地方。
  • 安全機制:某個資料來源中斷時,網站會繼續提供最後一份正常的資料,狀態頁面則顯示「降級運作」,而不是整個當掉。

成果

  • 27,623資料庫中的活動數(2026 年 10 月 6 日測量)
  • 0在地圖上沒有位置的活動
  • 4自動整合的開放資料與場館來源(2026 年 10 月 6 日測量)

自 2026 年 8 月 23 日起在 eventmap.hamdy-tabsissi.com 上線。資料庫中的每一個活動都標示在地圖上,資料也會自動更新。

畫面一覽

EventMap 首頁:依文化來源分組的巴黎活動,以及標示今晚去處的地圖
EventMap 首頁:依文化來源分組的巴黎活動,以及標示今晚去處的地圖

我學到了什麼

先驗證資料,再設計產品:快速調查資料來源後,發現 Nanterre 的活動是 0 筆、Montreuil 只有 14 筆,所以我把範圍縮小到巴黎,而不是去維護容易壞掉的爬蟲程式。另外,無聲無息的錯誤比直接當機代價更高:日期被當成文字來比較,一直悄悄把部分活動藏起來,直到測試時才被抓出來。

技術細節 技術堆疊、架構與決策——給技術背景的讀者

技術堆疊

  • Python (FastAPI, asyncio)
  • SQLite
  • Leaflet + OpenStreetMap
  • nginx
  • systemd
  • Ubuntu 22.04

架構一覽

  1. 訪客的手機或瀏覽器
  2. nginx — 反向代理、安全標頭
  3. FastAPI 應用程式(uvicorn) — 只綁定 127.0.0.1 的 systemd 服務,並設有 CPU 與記憶體配額
  4. SQLite — 活動、資料來源、去除重複
  5. 每 6 小時執行的收集程式 ← 巴黎市政府開放資料 + FICEP(OpenAgenda)

運作方式

  • 以 asyncio 非同步匯入多個公開來源,包括 Que faire à Paris(巴黎市政府,ODbL 授權)、FICEP 與 OpenAgenda(.ics),去除重複後寫入 SQLite。現行來源清單可在 /health 查看。
  • 依來源逐一進行具容錯能力的更新:失敗的來源絕不會清除既有資料;/health 會回報各來源的狀態,並切換為「degraded」。
  • 稽核 13 個近郊網站後,發現沒有任何可用的資料來源(沒有 .ics,也沒有 JSON-LD Event),因此將範圍從 Nanterre–巴黎–Montreuil 縮小到巴黎。多來源架構仍保持就緒。
  • 隔離的服務:不具 shell 的專用系統帳號、systemd 的 CPU 與記憶體配額,應用程式只綁定 127.0.0.1,並置於 nginx 之後。
  • 測試中發現的程式錯誤:日期被當成字串比較,導致正式環境中的部分活動被無聲排除;已修正並寫成文件。

事件與修正

  • 2026 年 9 月 24 日至 10 月 5 日,伺服器無法連上巴黎市政府的資料來源(DNS 錯誤):EventMap 持續提供最後一份正常的資料,/health 也標明故障來源並回報「degraded」。資料來源恢復後,一個資料庫約束(指向待刪除資料列的重複項目)讓每次更新都悄悄失敗,/health 卻誤報「ok」。我在本機重現問題、完成修正、以兩個測試涵蓋,並於當天重新部署:16,160 筆活動已更新。

仍待補強

  • 僅限巴黎
  • 取決於公開開放資料的品質