Skip to content

Conversation

yasulab
Copy link
Member

@yasulab yasulab commented Aug 8, 2025

📊 道場統計の年次フィルタリング機能とCSV/JSONダウンロード対応(完成版)

🎯 概要

CoderDojo一覧ページ(/dojos)に年次フィルタリング機能を追加し、特定年末時点でアクティブだった道場の一覧をHTML表示・CSV・JSON形式でダウンロードできる機能を実装しました。/statsページのグラフとの完全統合により、統計分析と詳細データ確認がシームレスに行えます。

✅ 実装完了機能

🔍 年次フィルタリング機能

  • 対象期間セレクトボックス: 2012年〜現在年までの年を選択可能
  • 自動遷移: セレクトボックス変更時に自動的にページ遷移(表示ボタン不要)
  • 内部リンク: #table アンカーで自動的にテーブル位置へスクロール
  • 全期間表示: デフォルトで全道場(アクティブ+非アクティブ)を表示

📊 統計データのエクスポート

  • CSV形式: 日本語ヘッダー付きCSVファイルのダウンロード
    • ヘッダー: ID, 道場名, 道場数, 都道府県, URL, 設立日, 状態
    • 合計行に道場数の総計(counter値の合計)を表示
  • JSON形式: 既存のAPIフォーマットで年次フィルタリング対応
  • HTML表示: テーブル形式での一覧表示(道場数カラムは非表示)

🎨 UX/UI改善

  • 統計情報の表示: /statsページのグラフとの比較検証が可能
    • 例: 2025年8月8日時点のアクティブな道場を表示中(開設数: 15 / 合計数: 199)
  • 非アクティブ道場のスタイリング:
    • gainsboro 背景色で視覚的に区別(/stats#prefectures と統一)
    • 共通CSSクラス .stats-table .inactive-item を作成
  • ソート順の改善: アクティブな道場を先に、非アクティブな道場を後に表示
  • URL表示の最適化: 30文字を超えるURLは truncate ヘルパーで省略表示
  • セクションタイトル: 「年次データを取得する」(動詞表現で訪問者が主語として行動しやすく)

💬 情報表示の改善

  • 現在年の表示: 「2025年8月8日時点」(自然な日本語表記)
  • 過去年の表示: 「2024年末時点」(確定済み時点)
  • デフォルト表示: 「全期間の道場を表示中(非アクティブ含む)」
  • ページ説明: 新機能に対応した説明文に更新

🛡️ Flashメッセージの表示位置制御(汎用パターン)

  • inline_プレフィックスパターン: inline_* プレフィックスでカスタム位置に表示
    • エラー時: flash[:inline_alert] で赤いアラート
    • 成功時: flash.now[:inline_info] で青い情報メッセージ
    • デフォルト位置(ページ上部)との二重表示を防止
  • ヘルパーメソッド: render_inline_flash_messages で再利用可能に
  • Bootstrap CSS自動適用: inline_alertalert-alert クラスに変換

⚡ パフォーマンス最適化

  • 効率的なクエリ: active_at スコープを活用した時点ベースのフィルタリング
  • 測定結果: 全年で8ms以下の高速応答
  • 変数命名: year_begin/year_end で統一(可読性向上)

🐛 重要なバグ修正

TDDアプローチによる年フィルタリング問題の修正

  • 問題: 年フィルタリング時に2024年非アクティブ化道場が2023年表示で灰色になっていた
  • 原因: 現在の is_active を使用していたため、選択年時点の状態と異なっていた
  • 解決: 選択年末時点での正しいアクティブ状態を計算するロジックを実装
  • 手法: 先にテストを書いて失敗を確認してから修正(TDD)

📈 統計精度の大幅改善

/statsページとの完全一致

  • 統計ラベル統一: 「開設道場数」→「開設数」、「合計道場数」→「合計数」
  • 計算ロジック統一: /statsページと同一の計算方法を採用
    • 開設数: その年に新規開設されたDojoの counter 合計
    • 合計数: その年末時点でアクティブなDojoの counter 合計
  • 検証例: 2023年の統計値が完全一致
    • 開設数: 20、合計数: 199(/statsページのグラフ値と同一)

データ整合性の確認

  • 124個の非アクティブ道場: inactivated_atデータとis_activeフラグが100%一致
  • 統計精度向上: 過去年の道場数が大幅に正確化
    • 2018年: 98 → 172道場(+75.5%の精度向上)
    • 2019年: 126 → 200道場(+58.7%の精度向上)

🧪 包括的テスト実装

  • 25個のRSpecテスト: 全テストが成功
    • 年パラメータのバリデーション
    • フィルタリング機能の動作確認
    • CSV/JSON形式の出力検証
    • UIコンポーネントのテスト
    • CSSクラス(inactive-item)の正しい適用テスト
    • 統計情報表示のテスト

🔒 セキュリティ対策

  • XSS対策: エラーメッセージからユーザー入力値を除外
  • パラメータ検証: 年パラメータは整数のみ受け付け(2012〜現在年)
  • HTMLエスケープ: Railsのデフォルト機能を活用

🎨 実装で得られた技術的知見

複雑度管理の重要性

  • デフォルト+条件更新パターン: if-elseのネストを避け、線形増加の設計を採用
  • コード品質向上: 指数的な複雑度増加を防ぐ設計原則の適用
  • 保守性: 将来の機能追加が容易な構造

段階的リファクタリングの価値

  • シンプル化: 複雑なif-elseブロックから2行のエレガントなコードへ
  • 可読性: year_begin/year_end の統一命名で関連性を明確化
  • 自然な日本語: 「2025年8月8日時点」(ゼロパディングなし)

📊 改善効果

ユーザーにとっての価値

  • 統計ページとの連動: グラフで見た数値を詳細データで確認可能
  • 年次推移の分析: 特定年の道場データを瞬時に抽出
  • 外部ツール連携: Excel等での詳細分析が可能
  • 情報の明確化: 何が表示されているかが一目瞭然

開発・運用面の改善

  • 保守しやすいコード: シンプルで理解しやすい実装
  • 完全なテストカバレッジ: 将来の変更に対する安全網
  • パフォーマンス: 高速な応答時間(< 10ms)
  • 拡張性: 新機能追加が容易な設計

🔍 パフォーマンス検証結果

総Dojo数: 323個
2020年フィルタリング: 8.04ms
2023年フィルタリング: 1.47ms  
2024年フィルタリング: 2.64ms

🧪 動作確認

  • ✅ 全期間表示: /dojos
  • ✅ 2024年フィルタリング: /dojos?year=2024
  • ✅ 現在年表示: /dojos?year=2025 (「2025年8月8日時点」と表示)
  • ✅ CSVダウンロード: /dojos.csv?year=2024
  • ✅ JSONダウンロード: /dojos.json?year=2024
  • ✅ 無効な年のエラー表示: /dojos?year=2026 (フィルタリングセクション内に表示)
  • ✅ 統計情報表示: 開設数と合計数が正確に表示

📋 実装タスク完了状況

✅ 完了済み

  • 年次フィルタリング機能の実装
  • CSV/JSON形式でのデータエクスポート
  • 統計情報表示(/statsページとの完全一致)
  • UI/UXの改善(スタイリング、メッセージ表示)
  • セキュリティ対策(XSS防止、パラメータ検証)
  • 包括的テストの実装(25個のテストケース)
  • パフォーマンス最適化と検証
  • 重要なバグ修正(TDDアプローチ)
  • ページ説明文の新機能対応
  • inline_プレフィックスパターンの実装

⏳ 将来のPRで対応予定

  1. is_activeカラムの削除

    • データ整合性確認済み(124個すべて一致)
    • inactivated_atカラムで代替可能
  2. 命名の統一(inactive → inactivated)

    • CSSクラス名: inactive-iteminactivated-item
    • 変数名・コメントの全体的な統一

🔗 関連情報


🚀 レビュー・マージ準備完了

この機能により、CoderDojo.jpの統計機能が大幅に向上し、ユーザーは詳細な年次データを効率的に分析できるようになります。全25個のテストが成功し、パフォーマンスも良好です。

Issue #1373 に関連した新機能の実装計画:
- /dojos エンドポイントに年次フィルタリング機能を追加
- CSV/JSON形式でのデータエクスポート対応
- yearパラメータによる特定年のデータ取得(整数値のみ)
- HTML表示でのデータプレビュー機能
- 全年次統計データと特定年データの両方をサポート
@yasulab yasulab changed the title [WIP] 道場統計年次ダウンロード機能の実装 [WIP] 📥 Enable to download Dojo stats as CSV for specific year. 📊 Aug 8, 2025
yasulab added 6 commits August 8, 2025 11:59
主な改善点:
- respond_toメソッドを使用してRailsの標準パターンに準拠
- 三項演算子でformat.json/csvのコードを簡潔に記述
- render_yearly_stats内の重複したパラメータチェックを削除
- より読みやすく保守しやすいコード構造に改善
重要な仕様の明確化:
- yearパラメータなし:
  - HTML: 現在アクティブな道場のみ(既存動作維持)
  - CSV/JSON: 全道場(アクティブ + 非アクティブ)
- yearパラメータあり(例: year=2024):
  - HTML/JSON/CSV すべて: その年末時点のアクティブ道場のみ

すべての形式(HTML/JSON/CSV)がyearパラメータを正しく処理
render :index を明示的に記述することで、
3つの形式すべてで何がレンダリングされるかを
コードリーディング時に一目で理解できるように改善
Phase 1(MVP):
- 特定年のアクティブ道場ダウンロードは既に含まれている
- yearパラメータによるフィルタリング機能を明記

Phase 2(将来):
- 重複していた項目を削除
- 都道府県別フィルタリングなど真の拡張機能を記載
yearパラメータ指定時:
- HTML/JSON/CSVすべてで非アクティブ道場を含まないことを確認

yearパラメータなし:
- CSV/JSONは全道場(非アクティブ含む)を確認
- HTMLはアクティブのみ(既存動作)を確認

これにより仕様が正しく実装されることを保証
重要な気づき:
- 既存の/dojosは既に全道場(非アクティブ含む)を返している
- CSV形式のサポートを追加するだけで良い
- render_yearly_statsなどの複雑なメソッドは不要
- yearパラメータでのフィルタリングのみ追加

これにより実装がシンプルになり、既存コードの変更を最小限に
主な変更内容:
- /dojos エンドポイントにCSV形式サポートを追加
- yearパラメータによる年末時点のアクティブ道場フィルタリング
- 年選択UIとダウンロードボタンをHTMLビューに追加

機能詳細:
- GET /dojos.csv → 全道場リスト(CSV形式)
- GET /dojos?year=2020 → 2020年末時点のアクティブ道場(HTML)
- GET /dojos.json?year=2020 → 同上(JSON形式)
- GET /dojos.csv?year=2020 → 同上(CSV形式)

エラーハンドリング:
- 無効な年が指定された場合はフラッシュメッセージでエラーを表示
- 2012年から現在年までの範囲チェック実装

Closes #1373
@yasulab yasulab changed the title [WIP] 📥 Enable to download Dojo stats as CSV for specific year. 📊 道場統計年次ダウンロード機能の実装 Aug 8, 2025
/statsと/dojosの数値の違いを検証するテストを実装:
- /stats: sum(:counter) - 支部数の合計
- /dojos: 道場の個数 - 各道場は1回のみカウント

テスト結果:
- 全年度でテスト成功
- 差分は複数支部を持つ道場(大田・邑南など)によるもの
- 無効な年のリダイレクトも正常動作

docs: 仕様の違いを明確に文書化
@yasulab yasulab changed the title 道場統計年次ダウンロード機能の実装 📥 Enable to download Dojo stats as CSV for specific year. 📊 Aug 8, 2025
yasulab added 2 commits August 8, 2025 13:10
- HTMLテーブルに支部数(counter)カラムを追加
- CSVファイルに支部数カラムと合計行を追加
- コントローラーで@counter_sumを計算して合計表示
- /statsの累積合計と視覚的に比較可能に

これにより、/statsページの支部数合計(sum(:counter))と
/dojosページの道場リスト(個別道場数)の違いが明確になる
新八尾道場(ID: 291)のcounter: 2を削除してデフォルト値1を使用。

背景:
- 東大阪道場(ID: 41)と旧八尾道場(ID: 101)は合同運営だった
- しかしYAMLには両道場が別々に記載されている
- 重複カウントを避けるため、それぞれcounter: 1として扱う
- 新八尾道場(ID: 291)は2022年11月開設の単独道場

現在の設定:
- 東大阪道場: counter省略(デフォルト1)
- 旧八尾道場: counter省略(デフォルト1)
- 新八尾道場: counter: 2 → 削除(デフォルト1)
@yasulab yasulab force-pushed the enable-to-donwload-dojo-stats-yearly branch from d4646d4 to 8a9a048 Compare August 8, 2025 04:27
@yasulab yasulab marked this pull request as ready for review August 8, 2025 04:30
yasulab added 13 commits August 8, 2025 13:32
script/test_yearly_dojos_count.rb の内容をRSpecに移行。
CIでテストDBを使って自動実行可能に。

追加したテスト:
- 年パラメータのバリデーション(2012-現在年)
- 年次フィルタリング機能(全道場 vs 特定年のアクティブ道場)
- counter(支部数)フィールドの表示と集計
- CSV形式の出力(ヘッダー、日付形式、合計行)
- HTML形式の年選択UI

また、Dojoファクトリーに必要な属性を追加。
- '🏢 支部数' → '☯️ 道場数' に変更(CoderDojoでは支部と呼ばない)
- 道場名カラムに '🗾' アイコンを追加
- HTMLとCSVの両方で統一
- テストも合わせて更新
- '年を選択' → '対象期間' に変更(より汎用的な表現)
- デフォルトラベル '全道場' → '全期間' に変更(道場ページなので冗長性を排除)
- テストも合わせて更新
- HTMLビューから道場数(counter)カラムを削除
- 道場名に☯️アイコンを復活
- 詳細な道場数はCSVで確認可能なため、HTMLはシンプルに保つ
- CSVには引き続き道場数カラムと合計を表示
- テストもHTML/CSVの役割分担に合わせて更新
- custom.scssに.stats-table .inactive-itemクラスを追加
- background-color: gainsboroスタイルを一元管理
- /dojosと/stats#prefecturesの両方で同じクラスを使用
- 将来的に他のテーブルでも再利用可能
- すべての形式(HTML/JSON/CSV)でアクティブな道場を先に表示
- order by is_active DESC, order ASC でソート
- アクティブな道場が見つけやすくなり、非アクティブな道場は最後にまとめて表示
- Railsのtruncateヘルパーメソッドを使用
- 30文字を超えるURLは「...」で省略表示
- title属性で完全なURLをツールチップ表示
- リンク先は完全なURLを維持
- '年を選択すると' → '対象期間を選択すると'
- 'その年末時点でアクティブだった道場のデータ' → 'その時点のアクティブな道場の一覧'
- より簡潔で分かりやすい表現に
- フォントサイズとグレー色で説明文であることが明確
- 「※」記号は冗長なので削除
- onchangeイベントで選択と同時にページ遷移
- 「表示」ボタンを削除してUIをシンプルに
- form_withタグも不要になったため削除
- より直感的で素早い操作が可能に
- h3タグにid='table'を追加し、📊アイコンを内部リンクに
- 対象期間セレクトボックスの自動遷移に#tableアンカーを追加
- '全道場を表示'リンクにも#tableアンカーを追加
- ページ遷移後に自動的にテーブル位置へスクロール
- セレクトボックスで簡単に切り替えられるため冗長
- UIをよりシンプルに
- 選択中の年のみを表示する簡潔な表示に
- 全期間選択時は ?year= パラメータを付けない
- /dojos?year=#table → /dojos#table に修正
- 三項演算子で値がある場合とない場合を判定
yasulab added 2 commits August 8, 2025 14:17
- flash[:alert]を同じ位置に赤い背景色で表示
- 内部リンク(#table)でジャンプしても見える位置に配置
- リダイレクト時も#tableアンカーを追加
- Bootstrap風のアラートスタイル(background: #f8d7da)を適用
- エラーメッセージに具体的な入力値を含めない
- '指定された年(値)は無効です' → '指定された年は無効です'
- 現在も.to_iとHTMLエスケープで安全だが、より防御的な実装に
- セキュリティのベストプラクティスに従う
@yasulab yasulab changed the title 📥 Enable to download Dojo stats as CSV for specific year. 📊 📥 Enable to download Dojo stats as CSV for specific year Aug 8, 2025
yasulab added 11 commits August 8, 2025 14:51
## 実装内容
- 'inline_' プレフィックスがついたflashメッセージは、デフォルト位置(ページ上部)では表示しない
- 各ビュー内でカスタム位置に表示できるようにヘルパーメソッドを追加
- /dojos ページの年フィルタリングのアラートとインフォメッセージに適用

## メリット
- flashメッセージの2重表示問題を解決
- inline_alert → alert-alert のようにBootstrapのCSSクラスを自動適用
- 将来的に inline_warning, inline_success なども同じパターンで使用可能
- コードがシンプルになり、条件分岐が不要に

## 変更箇所
- flash[:alert] → flash[:inline_alert] に変更(エラー時)
- flash.now[:inline_info] を追加(成功時)
- render_inline_flash_messages ヘルパーメソッドを追加
- テストを inline_alert に対応
## 問題
- /dojos?year=2023 で2024年に非アクティブ化された道場が灰色表示されていた
- 現在のis_activeを使用していたため、選択年時点の状態と異なっていた

## 解決策
- 選択年末時点でのアクティブ状態を計算するロジックを追加
- inactivated_atと選択年を比較して正しい状態を判定

## テスト
- inactive-item CSSクラスの存在をチェックするテストを追加
- TDDアプローチ:先にテストを書いて失敗を確認してから修正

## 今後の課題(このPRではやらない)
- inactive-item → inactivated-item へのCSS名変更
- is_activeカラムの削除(inactivated_atで代替可能)
- 変数名・コメントのinactive → inactivatedへの統一
訪問者が主語として行動しやすいよう、動詞を使った表現に変更
- 年次データのフィルタリング → 年次データを取得する
選択した年の「開設道場数」と「合計道場数」を表示することで、
訪問者が /stats ページのグラフと数値を比較検証できるようにする

表示例:
2020年末時点のアクティブな道場を表示中
(開設道場数: 3 / 合計道場数: 7)
- 統計ラベルを修正:「開設道場数」→「開設数」、「合計道場数」→「合計数」
- 統計計算ロジックを/statsページと同一に修正(counter合計を使用)
- 現在年の表示テキストを改善:「2025年末時点」→「2025年8月8日時点」
- 変数名を統一:start_of_year/end_of_year → year_begin/year_end
- if-elseブロックを削除してコードをシンプル化

この修正により、/dojos?year=XXXXの統計値が/statsページの
グラフ値と完全に一致し、訪問者が数値を正確に照合できるようになった。
現在年の表示を「2025年08月08日時点」から「2025年8月8日時点」に変更。
日本語として自然な表記(ゼロパディングなし)を採用。
全期間表示時に「全期間の道場を表示中(非アクティブ含む)」のメッセージを追加。
年別フィルタとの違いを明確化し、ユーザーの誤解を防止。

- デフォルト: 「全期間の道場を表示中(非アクティブ含む)」
- 年別フィルタ: 「YYYY年X月X日時点のアクティブな道場を表示中」

テストも追加し、25個すべてのテストが成功。
古い説明文を削除し、年次フィルタリング機能に対応した新しい説明に更新:

- 削除: 「現在は活動停止中の道場も表示」(誤解を招く表現)
- 削除: 直接JSON/CSVリンク(フィルタリング機能の説明に変更)
- 追加: 対象期間選択機能の説明
- 追加: 全期間と年別フィルタの違いの明確化

訪問者が新機能を理解しやすい説明に改善。
統計グラフと詳細データの行き来を容易にし、CSVダウンロード機能を改善

## 追加機能

### 相互ナビゲーション
- /dojosページに「» 推移グラフで見る」リンクを追加(/statsへ)
- /statsページに「» 年次データを見る」リンクを追加(/dojosへ)
- 英語版にも対応(View Annual Data)

### CSV機能の強化
1. 時点情報の明確化
   - 状態カラムヘッダーに時点を追加
   - 例:「状態 (2023年末時点)」「状態 (2025年8月8日時点)」

2. 動的ファイル名
   - 年別:dojos_2023.csv
   - 全期間:dojos_all.csv
   - 複数年のデータ管理が容易に

3. 閉鎖日カラムの追加(全期間CSVのみ)
   - アクティブな道場:空欄
   - 非アクティブな道場:閉鎖日を表示
   - 道場のライフサイクル全体を把握可能

## 改善効果
- ユーザーが統計グラフと詳細データをシームレスに分析可能
- CSVデータの時点が明確になり誤解を防止
- ダウンロードファイルの管理が容易に
CSVファイルの最後にあった合計行を削除し、プログラムでのパース時の
エラーを防止

## 削除理由
- 合計行のデータ型が各カラムと不一致(IDカラムに「合計」文字列など)
- CSVパーサーでのエラーや警告の原因になる
- データ分析ツール(Excel、pandas等)での処理を妨げる

## 改善効果
- CSVファイルが標準的なフォーマットに準拠
- プログラムでの自動処理が容易に
- データの一貫性が向上
CSVから合計行を削除したことに伴い、関連するテストを更新

## 変更内容
1. counter合計値のテストを個別道場のcounter値確認に変更
2. 合計行の存在確認テストをデータ一貫性確認テストに変更
3. 全期間CSVのヘッダーに閉鎖日カラムを追加

## テスト結果
- 全25個のテストが成功
- CSVデータの一貫性が保証される
@yasulab
Copy link
Member Author

yasulab commented Aug 8, 2025

だいたい仕上がったのでマージ&デプロイします!!🚀✨

@yasulab yasulab merged commit 92ac900 into main Aug 8, 2025
5 checks passed
@yasulab yasulab deleted the enable-to-donwload-dojo-stats-yearly branch August 8, 2025 07:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant