Skip to content

Commit ba221ac

Browse files
authored
♻️ Clubs との突合を dojo2dojo.csv から global_club_id に切り替える (#42)
クラブ名を手で対応付ける dojo2dojo.csv (301 行) をやめ、coderdojo.jp の global_club_id で直接突き合わせる。CSV は削除した。 名前での突合には次の問題があった。 - 新しい Dojo を追加するたび CSV に 1 行足す運用が必要で、実際に漏れていた - Clubs 側で改名されると追従できない(那覇の登録名が変わっていた) - 同名クラブが二重登録されていると、先に現れた方を拾ってしまう(流山・古河) active な全エントリが global_club_id を持つことは coderdojo.jp 側の spec が 保証しているため、名前で救済する経路は置いていない。 ## 同じ入力データで新旧の出力を比較した結果 - 日本のマーカー 195 -> 202 件。地図から消えた Dojo は 0 件 - 増えた 7 件は CSV に行が無く漏れていたもの - 流山・古河は約 0.1km 移動。Clubs 側の同名二重登録のうち、coderdojo.jp が 指定した UUID のクラブを選ぶようになったため ## あわせて整理したもの - tmp/unmatched_dojos.json と Slack 通知を追加。CSV が無くなった今、Clubs 側の 削除・ID 変更を検知できるのはこれだけになる - deploy_to_pages.yml のデータ取得に Japan 側の件数・UUID 数チェックを追加 - 突合の情報源が Japan DB 自体になり到達不能となったガードを削除 (#43) - /dojo2dojo.json を /dojos.json に改名(dojo2dojo は CSV に由来する名前) - _plugins/build_hooks.rb が生成タスクの終了コードを見ていなかったため、 失敗しても古い地図が出荷されていた。exception: true で止める - PR でもビルドとテストを走らせる (deploy_to_pages.yml -> ci.yml)。 これまで rake test は main への push 後にしか走っていなかった
1 parent cbdae67 commit ba221ac

18 files changed

Lines changed: 547 additions & 483 deletions
Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
1-
name: Deploy to GitHub Pages
1+
name: CI
22
on:
33
push:
44
branches: ["main"]
55

6+
# PR でもビルドとテストを走らせる。デプロイはしない (最後の 🚀 の if を参照)。
7+
# これを足す前は rake test が main への push 後にしか走らず、テストが落ちるのは
8+
# マージした後だった。日本のマーカーが静かに消える回帰は、この rake test でしか
9+
# 検出できない (tests/dojo_matching_test.rb)。
10+
# cf. https://github.com/coderdojo-japan/map.coderdojo.jp/pull/42
11+
pull_request:
12+
613
# Allows you to run this workflow manually from the Actions tab
714
workflow_dispatch:
815

916
jobs:
1017
deploy:
18+
name: 🧪 Build, Test & Deploy
1119
runs-on: ubuntu-latest
1220
steps:
1321
- name: 📥 Download codes from GitHub
@@ -26,9 +34,9 @@ jobs:
2634
2735
# Jekyll のビルドは _data/*.json から GeoJSON を再生成するが、データの再取得は
2836
# しない。そのため取得なしでビルドすると、コミット済みの古いデータで地図が
29-
# 出荷される。新しい Dojo を dojo2dojo.csv に追加して push した直後は、その
30-
# Dojo が _data/dojos_japan.json に必ず未反映なので、地図に出ないまま配信
31-
# されてしまう (upsert_dojos_geojson.rb のガードによりスキップされるため)。
37+
# 出荷される。coderdojo.jp に新しい Dojo が追加された直後は、その Dojo が
38+
# _data/dojos_japan.json に未反映なので、地図に出ないまま配信されてしまう
39+
# (upsert_dojos_geojson.rb のガードによりスキップされるため)。
3240
#
3341
# ここで最新データを取れれば push 直後から正しく描画できる。ただし取得は外部
3442
# API に依存するため、失敗してもデプロイは止めない (continue-on-error)。
@@ -37,8 +45,12 @@ jobs:
3745
# 従来どおり (古いデータでのビルド) に落とす。
3846
#
3947
# データのコミットはしない。それは scheduler_daily.yml の責務。
48+
#
49+
# PR では取得しない。コミット済みのデータだけでビルドすることで、PR の結果が
50+
# 外部 API の状態で揺れなくなり、リポジトリの中身そのものを検証できる。
4051
- name: 🌐 Fetch latest data (best-effort)
4152
id: fetch
53+
if: github.event_name != 'pull_request'
4254
continue-on-error: true
4355
run: |
4456
bundle exec rake get_data_from_earth
@@ -49,6 +61,20 @@ jobs:
4961
fi
5062
bundle exec rake get_data_from_japan
5163
64+
# Japan 側も件数を確かめる。ここを素通りさせると、取得は成功したが中身が
65+
# 劣化していた場合に下の復元パスへ落ちず、後段の rake test でデプロイ全体が
66+
# 止まる。Geolonia 障害時の緊急デプロイを外部 API の状態に左右させないため、
67+
# ここで失敗させてコミット済みデータへ戻す。
68+
# 突合は global_club_id で行うので、件数だけでなく UUID の数も見る。
69+
# 下限は tests/dojo_matching_test.rb と揃えている。
70+
NUMBER_OF_DOJOS=$(ruby -rjson -e 'puts JSON.parse(File.read("_data/dojos_japan.json")).size')
71+
NUMBER_OF_UUIDS=$(ruby -rjson -e 'puts JSON.parse(File.read("_data/dojos_japan.json")).count { |d| d["global_club_id"] }')
72+
if [ "$NUMBER_OF_DOJOS" -lt "300" ] || [ "$NUMBER_OF_UUIDS" -lt "150" ] ; then
73+
echo "ERROR: NUMBER_OF_DOJOS=$NUMBER_OF_DOJOS NUMBER_OF_UUIDS=$NUMBER_OF_UUIDS"
74+
echo "These numbers should be 300+ / 150+. Something went wrong."
75+
exit 1
76+
fi
77+
5278
- name: ♻️ Restore committed data if the fetch failed
5379
if: steps.fetch.outcome == 'failure'
5480
run: |
@@ -65,7 +91,10 @@ jobs:
6591
# This is NOT for security reason but for others, especially who fork,
6692
# to easier find out which API key they need to replace with their own.
6793
# Check out the official tutorial for details: https://docs.geolonia.com/tutorial
94+
# PR では注入しない。デプロイしないので不要で、fork からの PR には
95+
# そもそも secrets が渡らない。
6896
- name: 🔑 Pass Geolonia YOUR-API-KEY from secrets
97+
if: github.event_name != 'pull_request'
6998
env:
7099
GEOLONIA_API_KEY: ${{ secrets.GEOLONIA_API_KEY }}
71100
run: |

.github/workflows/scheduler_daily.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ jobs:
5151
run: |
5252
bundle exec rake get_data_from_japan
5353
54+
# 日本のマーカーは global_club_id での突合に依存している。取得に失敗して
55+
# 空や欠損のデータが入ると、海外のマーカーだけが残った地図がデプロイされる。
56+
# Earth 側と同じく、入口で件数を確かめる
57+
NUMBER_OF_DOJOS=$(ruby -rjson -e 'puts JSON.parse(File.read("_data/dojos_japan.json")).size')
58+
if [ "$NUMBER_OF_DOJOS" -lt "300" ] ; then
59+
echo "ERROR: NUMBER_OF_DOJOS=$NUMBER_OF_DOJOS"
60+
echo "This number should be 300+. Something went wrong."
61+
exit 1
62+
fi
63+
5464
- name: 🔧 Build data to render DojoMap
5565
run: |
5666
# Jekyll plugin automatically runs upsert_dojos_geojson and compact_geojson
@@ -88,6 +98,52 @@ jobs:
8898
env:
8999
GITHUB_TOKEN:
90100

101+
# 地図に載らなかった active な Dojo を検知する。
102+
#
103+
# 意図的な設計:
104+
# - デプロイの「後」に置く。1 つの Dojo が Clubs 側で消えただけで日次更新
105+
# 全体を止めるのは割に合わない
106+
# - continue-on-error でジョブ自体は失敗させない
107+
# - 通知するのは uuid_not_in_clubs と no_uuid の 2 つ。
108+
# uuid_not_in_clubs は Japan 側が UUID を持っているのに Clubs 側にその
109+
# クラブが無い状態で、削除か UUID 変更が起きている。
110+
# no_uuid は coderdojo.jp 側の spec が防いでいるはずの状態なので、ここに
111+
# 出たら上流の保証が破れている。
112+
# - club_excluded_by_status_or_coordinates は Clubs 側の登録内容の問題で、
113+
# こちらからは直せないためログのみに留める
114+
- name: 🔎 Check unmatched dojos
115+
id: unmatched
116+
continue-on-error: true
117+
run: |
118+
cat tmp/unmatched_dojos.json
119+
ruby -rjson -e '
120+
critical = JSON.parse(File.read("tmp/unmatched_dojos.json"))
121+
.select { |d| %w[uuid_not_in_clubs no_uuid].include?(d["reason"]) }
122+
exit 0 if critical.empty?
123+
critical.each { |d| warn "#{d["name"]} (id=#{d["id"]}): #{d["reason"]} #{d["global_club_id"]}" }
124+
exit 1
125+
'
126+
127+
- name: 🔔 Notify unmatched dojos to Slack (if any)
128+
if: steps.unmatched.outcome == 'failure'
129+
uses: slackapi/slack-github-action@v3.0.5
130+
with:
131+
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
132+
webhook-type: incoming-webhook
133+
payload: |
134+
{
135+
"text": "<@yasulab> 地図に載らなかった Dojo があります。",
136+
"blocks": [
137+
{
138+
"type": "section",
139+
"text": {
140+
"type": "mrkdwn",
141+
"text": "<@yasulab> *地図に載らなかった Dojo があります。* coderdojo.jp 側の `global_club_id` が Clubs DB のクラブと結びついていません。提携先での削除・ID 変更か、`global_club_id` の未設定が考えられます。\n\n*対応:* `db/dojos.yml` の `global_club_id` を現在の値に更新してください。該当 Dojo 名と理由はログに出ています。\n\n<https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|» 詳細を見る (GitHub)>"
142+
}
143+
}
144+
]
145+
}
146+
91147
# This is NOT for security reason but for others, especially who fork,
92148
# to easier find out which API key they need to replace with their own.
93149
# Check out the official tutorial for details: https://docs.geolonia.com/tutorial

CLAUDE.md

Lines changed: 37 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ bundle exec rake test
7373

7474
**メリット**:
7575
- ✅ ローカル開発でも本番環境でも一貫した動作
76-
-`dojo2dojo.csv`更新後、`jekyll build`だけで自動反映
76+
-`_data/*.json` 更新後、`jekyll build`だけで自動反映
7777
- ✅ 手動でのRakeタスク実行が不要
7878

7979
**実行ログ例**:
@@ -100,8 +100,8 @@ GitHub Actionsで毎日自動更新(日本時間 5:59):
100100
# scheduler_daily.yml(データ更新+デプロイ)
101101
gh workflow run scheduler_daily.yml
102102

103-
# deploy_to_pages.yml(デプロイのみ
104-
gh workflow run deploy_to_pages.yml
103+
# ci.yml(ビルド・テスト・デプロイ。PR ではデプロイせずテストまで
104+
gh workflow run ci.yml
105105

106106
# 実行状況確認
107107
gh run list --workflow=scheduler_daily.yml --limit 3
@@ -118,8 +118,8 @@ gh run watch # リアルタイム監視
118118
- `_data/dojos_earth.json`: Clubs APIから取得した全世界のDojoデータ
119119
- `_data/dojos_japan.json`: CoderDojo Japan APIから取得した日本のDojoデータ
120120
- `_data/events_japan.json`: 日本のイベントデータ
121-
- `_data/dojo2dojo.json`: dojo2dojo.csvから生成されたマッピングデータ
122-
- `dojo2dojo.csv`: Japan APIとClubs APIの名前マッピング(編集可能
121+
- `_data/dojos.json`: 地図に載った日本のDojo一覧(生成物)。`/dojos.json` として配信し、掲載後の反映確認に使う
122+
- `tmp/unmatched_dojos.json`: 地図に載らなかったactiveなDojoと、その理由(生成物
123123
- `dojos.geojson`: 地図表示用の統合データ(GeoJSON形式、人間が読める形式)
124124
- `dojos.min.geojson`: 圧縮版GeoJSON(本番環境で使用、22.9%削減)
125125
- `_plugins/build_hooks.rb`: Jekyllビルド時の自動データ更新フック
@@ -208,8 +208,8 @@ mcp__o3__o3-search "GitHub Actions Pages deploy Jekyll JEKYLL_ENV production 202
208208
# 複数データソース統合時の名前マッチング問題
209209
mcp__o3__o3-search "GeoJSON data integration name mapping mismatch multiple sources 2025"
210210

211-
# CSVベースのマッピングファイル管理
212-
mcp__o3__o3-search "CSV mapping file data integration best practices version control 2025"
211+
# ID ベースでのデータ突合
212+
mcp__o3__o3-search "stable identifier vs name matching data integration best practices 2025"
213213

214214
# データ不整合のデバッグ手法
215215
mcp__o3__o3-search "multi-source data integration debugging missing records troubleshooting 2025"
@@ -226,59 +226,47 @@ o3の検索結果を使用する際は必ず:
226226

227227
### データ統合で特定のDojoが地図に表示されない場合
228228

229-
この問題は主にdojo2dojo.csvのマッピング不一致が原因です。以下の手順で調査・修正してください:
229+
Clubs DB と Japan DB は `global_club_id` (UUID) で突合している。表示されない場合、
230+
ほぼ全て「UUID が一致していない」ことが原因なので、まず生成物の診断結果を見る。
230231

231-
1. **データソースの確認**
232+
1. **地図に載らなかった Dojo と理由を確認する**
232233
```bash
233-
# Clubs APIのデータ確認
234-
grep "対象Dojo名" dojos_earth.json
235-
236-
# Japan APIのデータ確認
237-
grep "対象Dojo名" dojos_japan.json
234+
bundle exec rake upsert_dojos_geojson
235+
cat tmp/unmatched_dojos.json
238236
```
239237

240-
2. **マッピングファイルの確認**
241-
```bash
242-
# dojo2dojo.csvでのマッピング確認
243-
grep "対象Dojo名" dojo2dojo.csv
244-
```
238+
`reason` の意味は次のとおり。
245239

246-
3. **問題の特定と修正**
247-
- Clubs APIでの登録名と完全一致するようにdojo2dojo.csvを修正
248-
- 例:「Coderdojo Saga」vs「Saga」のような不一致を修正
240+
| reason | 意味 | 対応 |
241+
|---|---|---|
242+
| `uuid_not_in_clubs` | coderdojo.jp の `global_club_id` が指すクラブが Clubs DB に無い | Clubs 側で削除・ID 変更が起きている。`db/dojos.yml` の値を現在のものに更新する |
243+
| `club_excluded_by_status_or_coordinates` | クラブはあるが、座標が無いか活動中ではない | Clubs 側の登録内容を直す(提携先の管理画面) |
244+
| `no_uuid` | coderdojo.jp 側に `global_club_id` が無い | `db/dojos.yml` に設定する。通常は向こうの CI が防ぐ |
249245

250-
4. **マッピング追加とGeoJSON再生成**
246+
2. **Clubs DB 側の現在の UUID を調べる**
251247
```bash
252-
# dojo2dojo.csvに追加(例: すぎなみ)
253-
echo "すぎなみ Suginami" >> dojo2dojo.csv
254-
255-
# jekyll buildで自動的にGeoJSON再生成(推奨)
256-
bundle exec jekyll build
257-
258-
# または個別実行
259-
bundle exec rake upsert_dojos_geojson
260-
bundle exec rake compact_geojson
248+
ruby -rjson -e 'JSON.parse(File.read("_data/dojos_earth.json")).select { |c| c["countryCode"] == "JP" && c["name"].include?("対象名") }.each { |c| puts "#{c["id"]} #{c["name"]} #{c["status"]}" }'
261249
```
262250

263-
5. **結果の確認**
264-
```bash
265-
# GeoJSONに含まれているか確認
266-
grep "対象Dojo名" dojos.geojson
251+
3. **coderdojo.jp 側を直す**
267252

268-
# ローカルで地図表示確認
269-
bundle exec jekyll server
270-
# http://localhost:4000/ で確認
271-
```
253+
`db/dojos.yml``global_club_id` を更新し、PR を出す。地図側のリポジトリで
254+
修正することはない(名前で突合していた頃と違い、こちらに手動のマッピングは無い)。
272255

273-
6. **変更をコミット**
256+
4. **反映を確認する**
274257
```bash
275-
git add dojo2dojo.csv _data/dojo2dojo.json dojos.geojson
276-
git commit -m "CoderDojo XXXのマッピングを追加"
277-
git push origin main
258+
bundle exec rake get_data_from_japan # coderdojo.jp のデプロイ完了後に実行
259+
bundle exec rake upsert_dojos_geojson
260+
bundle exec rake test_matching
278261
```
279262

280-
### よくあるマッピング問題
281-
- **プレフィックスの不一致**: "Coderdojo XXX" vs "XXX"
282-
- **スペースの不一致**: "CoderDojo" vs "Coderdojo"
283-
- **記号の不一致**: "@" や "、" の有無
284-
- **日本語・英語の混在**: 漢字・ひらがな・カタカナ・ローマ字の不一致
263+
### 名前での突合をやめた理由
264+
265+
以前は `dojo2dojo.csv` で「Japan 登録名 → Clubs 登録名」を手で対応付けていたが、
266+
次の理由で UUID 突合に切り替えた。
267+
268+
- 新しい Dojo を追加するたび CSV に 1 行足す運用が必要で、実際に漏れていた
269+
- Clubs 側で改名されると追従できない(那覇の登録名が変わっていた)
270+
- 同名クラブが二重登録されていると、先に現れた方を拾ってしまう(流山・古河)
271+
- 表記ゆれ("Coderdojo XXX" vs "XXX"、"@" や "、" の有無、ローマ字と日本語)に
272+
そのつど対応する必要があった

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[![Deploy to GitHub Pages](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/deploy_to_pages.yml/badge.svg)](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/deploy_to_pages.yml) [![pages-build-deployment](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/pages/pages-build-deployment) [![Daily Update](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/scheduler_daily.yml/badge.svg)](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/scheduler_daily.yml)
1+
[![CI](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/ci.yml/badge.svg)](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/ci.yml) [![pages-build-deployment](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/pages/pages-build-deployment) [![Daily Update](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/scheduler_daily.yml/badge.svg)](https://github.com/coderdojo-japan/map.coderdojo.jp/actions/workflows/scheduler_daily.yml)
22

33
# :japan: DojoMap - 地図で CoderDojo を探す
44

Rakefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ task(:compact_geojson) { ruby '_tasks/compact_geojson.rb' }
1212
desc 'Run GeoJSON data and map-marker integrity tests'
1313
task(:test_markers) { ruby 'tests/markers_integrity_test.rb' }
1414

15+
# Clubs DB と Japan DB の突合が壊れていないかのテスト (minitest)
16+
# 日本のマーカーだけが静かに消える回帰を防ぐ。詳細は tests/dojo_matching_test.rb 参照。
17+
desc 'Run dojo matching (global_club_id) integrity tests'
18+
task(:test_matching) { ruby 'tests/dojo_matching_test.rb' }
19+
1520
# Geolonia スプライト サーバの健全性を手動確認するテスト (minitest)。
1621
# 外部サービス状態に CI を依存させないため、`test` には含めず手動実行する:
1722
# GEOLONIA_API_KEY=xxxx bundle exec rake test_sprite
@@ -21,7 +26,7 @@ task(:test_sprite) { ruby 'tests/sprite_status_test.rb' }
2126
# GitHub - gjtorikian/html-proofer
2227
# https://github.com/gjtorikian/html-proofer
2328
require 'html-proofer'
24-
task test: [:build, :test_markers] do
29+
task test: [:build, :test_markers, :test_matching] do
2530
#require './tests/custom_checks'
2631
options = {
2732
#checks: ['Links', 'Images', 'Scripts', 'OpenGraph', 'Favicon', 'CustomChecks'],

_config.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ exclude:
2626
- README.md
2727
- LICENSE.md
2828
- CODE_OF_CONDUCT.md
29-
- dojo2dojo.csv
3029

3130
# See the official README for more options:
3231
# https://github.com/digitalsparky/jekyll-minifier

0 commit comments

Comments
 (0)