Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6ab4a24
docs: 道場統計年次ダウンロード機能の実装計画を追加
yasulab Aug 8, 2025
cb64d0b
docs: 実装計画を改善 - respond_toと三項演算子を活用
yasulab Aug 8, 2025
dc06bb5
docs: データ取得範囲の仕様を正確に記述
yasulab Aug 8, 2025
0024d7c
docs: format.htmlを明示的に記述して可読性を向上
yasulab Aug 8, 2025
5194c45
docs: Phase 1とPhase 2の内容を正確に整理
yasulab Aug 8, 2025
c3a24db
docs: 重要なテストケースを追加 - 非アクティブ道場の扱い
yasulab Aug 8, 2025
658583f
docs: 実装計画を大幅に簡素化 - 既存動作の正しい理解に基づく
yasulab Aug 8, 2025
d58e5f4
feat: 道場統計の年次フィルタリングとCSVダウンロード機能を実装
yasulab Aug 8, 2025
5d57dd5
test: 年次フィルタリング機能のテストスクリプトを追加
yasulab Aug 8, 2025
aca927f
feat: 道場一覧にcounterカラムを追加して/statsとの数値検証を容易に
yasulab Aug 8, 2025
8a9a048
fix: 新八尾道場のcounter値を修正(重複カウント回避)
yasulab Aug 8, 2025
6f6409b
test: 道場年次フィルタリング機能のRSpecテストを追加
yasulab Aug 8, 2025
2f07933
fix: CoderDojo文脈に合わせた表記修正
yasulab Aug 8, 2025
3ef3301
improve: 年次フィルタリングUIのラベルを改善
yasulab Aug 8, 2025
277cef0
improve: HTMLビューから道場数カラムを削除してシンプルに
yasulab Aug 8, 2025
427c728
refactor: 非アクティブ項目のスタイルを共通CSSクラスに抽出
yasulab Aug 8, 2025
a0d34fb
feat: アクティブな道場を先に表示するようソート順を改善
yasulab Aug 8, 2025
a2d5106
improve: 長いURLを30文字で切り詰めて表示(Railsのtruncateヘルパー使用)
yasulab Aug 8, 2025
0b51b26
improve: 年次フィルタリング説明文を「対象期間」に合わせて更新
yasulab Aug 8, 2025
fde0c73
style: 説明文から「※」記号を削除
yasulab Aug 8, 2025
88937e7
improve: 対象期間セレクトボックスの変更で自動遷移(表示ボタン削除)
yasulab Aug 8, 2025
4f1a31a
feat: テーブルセクションへの内部リンクを追加(#table)
yasulab Aug 8, 2025
0d07eb4
remove: 「全道場を表示」リンクを削除(セレクトボックスで切り替え可能)
yasulab Aug 8, 2025
27928f4
fix: 全期間選択時のURLをシンプルに(/dojos#table)
yasulab Aug 8, 2025
5cda416
feat: エラーメッセージを年次フィルタリングセクションに表示
yasulab Aug 8, 2025
697e8af
security: エラーメッセージからユーザー入力値を除外(XSS対策)
yasulab Aug 8, 2025
4ed4e33
refactor: inline_プレフィックスパターンでflashメッセージの表示位置を制御
yasulab Aug 8, 2025
4043516
fix: 年フィルタリング時のアクティブ状態を選択年時点の状態に修正
yasulab Aug 8, 2025
806e706
improve: セクションタイトルを「年次データを取得する」に変更
yasulab Aug 8, 2025
79f260d
feat: 年次フィルタリング時に統計情報を表示
yasulab Aug 8, 2025
b2af6f2
fix: 統計表示とラベルを/statsページと完全一致させる修正
yasulab Aug 8, 2025
56e44ba
style: 日付表示を自然な日本語に改善
yasulab Aug 8, 2025
1ee8de9
feat: デフォルトページに「非アクティブ含む」情報を表示
yasulab Aug 8, 2025
fecea11
docs: ページ説明を新機能に合わせて更新
yasulab Aug 8, 2025
890abd7
feat: 統計ページと道場一覧ページの相互連携とCSV機能を強化
yasulab Aug 8, 2025
9b66204
fix: CSVファイルから合計行を削除してデータ一貫性を改善
yasulab Aug 8, 2025
a447999
test: CSV合計行削除に対応したテストの修正
yasulab Aug 8, 2025
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
7 changes: 7 additions & 0 deletions app/assets/stylesheets/custom.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1072,3 +1072,10 @@ section {
line-height: 1.7em;
color: #909090;
}

/* Stats and Dojos table styling */
.stats-table {
td.inactive-item {
background-color: gainsboro;
}
}
79 changes: 73 additions & 6 deletions app/controllers/dojos_controller.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,95 @@
class DojosController < ApplicationController

# GET /dojos[.json]
# GET /dojos[.html|.json|.csv]
def index
# yearパラメータがある場合の処理
if params[:year].present?
begin
year = params[:year].to_i
# 有効な年の範囲をチェック
unless year.between?(2012, Date.current.year)
flash[:inline_alert] = "指定された年は無効です。2012年から#{Date.current.year}年の間で指定してください。"
return redirect_to dojos_path(anchor: 'table')
end

@selected_year = year
year_end = Time.zone.local(@selected_year).end_of_year

# その年末時点でアクティブだった道場を取得
dojos_scope = Dojo.active_at(year_end)
@page_title = "#{@selected_year}年末時点のCoderDojo一覧"
rescue ArgumentError
flash[:inline_alert] = "無効な年が指定されました"
return redirect_to dojos_path(anchor: 'table')
end
else
# yearパラメータなしの場合(既存の実装そのまま)
dojos_scope = Dojo.all
end

@dojos = []
Dojo.includes(:prefecture).order(order: :asc).all.each do |dojo|
dojos_scope.includes(:prefecture).order(is_active: :desc, order: :asc).each do |dojo|
# 年が選択されている場合は、その年末時点でのアクティブ状態を判定
# 選択されていない場合は、現在の is_active を使用
is_active_at_selected_time = if @selected_year
# その年末時点でアクティブだったかを判定
# inactivated_at が nil(まだアクティブ)または選択年より後に非アクティブ化
dojo.inactivated_at.nil? || dojo.inactivated_at > Time.zone.local(@selected_year).end_of_year
else
dojo.is_active
end

@dojos << {
id: dojo.id,
url: dojo.url,
name: dojo.name,
logo: root_url + dojo.logo[1..],
order: dojo.order,
counter: dojo.counter,
is_active: dojo.is_active,
is_active: is_active_at_selected_time,
prefecture: dojo.prefecture.name,
created_at: dojo.created_at,
description: dojo.description,
inactivated_at: dojo.inactivated_at, # CSV用に追加
}
end

# counter合計を計算(/statsとの照合用)
@counter_sum = @dojos.sum { |d| d[:counter] }

# 情報メッセージを設定
if @selected_year
# /statsページと同じ計算方法を使用
# 開設数 = その年に新規開設されたDojoのcounter合計
year_begin = Time.zone.local(@selected_year).beginning_of_year
year_end = Time.zone.local(@selected_year).end_of_year
new_dojos_count = Dojo.where(created_at: year_begin..year_end).sum(:counter)

# 合計数 = その年末時点でアクティブだったDojoのcounter合計
total_dojos_count = Dojo.active_at(year_end).sum(:counter)

# 表示用の日付テキスト
display_date = "#{@selected_year}年末"
display_date = Date.current.strftime('%Y年%-m月%-d日') if @selected_year == Date.current.year

flash.now[:inline_info] = "#{display_date}時点のアクティブな道場を表示中<br>(開設数: #{new_dojos_count} / 合計数: #{total_dojos_count})".html_safe
else
# 全期間表示時の情報メッセージ
flash.now[:inline_info] = "全期間の道場を表示中(非アクティブ含む)"
end

respond_to do |format|
# No corresponding View for now.
# Only for API: GET /dojos.json
format.html # => app/views/dojos/index.html.erb
format.html { render :index } # => app/views/dojos/index.html.erb
format.json { render json: @dojos }
format.csv do
# ファイル名を年に応じて設定
filename = if @selected_year
"dojos_#{@selected_year}.csv"
else
"dojos_all.csv"
end
send_data render_to_string, type: :csv, filename: filename
end
end
end

Expand Down
9 changes: 9 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ def page_lang(lang)
lang.empty? ? 'ja' : lang
end

# 'inline_' プレフィックスがついたflashメッセージをビュー内で表示するヘルパー
# inline_alert → alert, inline_warning → warning のように変換してBootstrapのCSSクラスを適用
def render_inline_flash_messages
flash.select { |type, _| type.to_s.start_with?('inline_') }.map do |type, message|
css_class = type.to_s.gsub('inline_', '')
content_tag(:div, message, class: "alert alert-#{css_class}", style: "margin-bottom: 15px;")
end.join.html_safe
end

def kata_description
"道場で役立つ資料やコンテスト情報、立ち上げ方や各種支援をまとめています。"
end
Expand Down
42 changes: 42 additions & 0 deletions app/views/dojos/index.csv.ruby
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require 'csv'

csv_data = CSV.generate do |csv|
# ヘッダー行
# 選択年に応じて状態カラムのヘッダーを変更
status_header = if @selected_year
if @selected_year == Date.current.year
"状態 (#{Date.current.strftime('%Y年%-m月%-d日')}時点)"
else
"状態 (#{@selected_year}年末時点)"
end
else
'状態'
end

# 全期間の場合のみ閉鎖日カラムを追加
if @selected_year
csv << ['ID', '道場名', '道場数', '都道府県', 'URL', '設立日', status_header]
else
csv << ['ID', '道場名', '道場数', '都道府県', 'URL', '設立日', status_header, '閉鎖日']
end

# データ行
@dojos.each do |dojo|
row = [
dojo[:id],
dojo[:name],
dojo[:counter],
dojo[:prefecture],
dojo[:url],
dojo[:created_at].strftime("%F"),
dojo[:is_active] ? 'アクティブ' : '非アクティブ'
]

# 全期間の場合のみ閉鎖日を追加
if !@selected_year
row << (dojo[:inactivated_at] ? dojo[:inactivated_at].strftime("%F") : '')
end

csv << row
end
end
108 changes: 84 additions & 24 deletions app/views/dojos/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,49 @@
<br>
<div class='form__terms list'>
<ul style='list-style-type: "\2713\0020"; font-size: smaller;'>
<li>現在は活動停止中 (In-active) の道場も表示されています</li>
<li>道場名をクリックすると個別の統計データが確認できます</li>
<li>下記表は <code><%= link_to dojos_path(format: :json), dojos_path(format: :json) %></code> で JSON に変換できます</li>
<li>道場名をクリックすると、その道場の個別の統計データを確認できます</li>
<li>対象期間を選ぶと、その期間のアクティブな道場の一覧を表示できます</li>
<li>全期間の場合のみ、すべての道場(非アクティブ含む)を表示できます</li>
</ul>
</div>
<div style="text-align: center; margin-top: 10px; margin-bottom: 50px;">
<a href="/stats" style="color: #007bff; text-decoration: none;">
&raquo; 推移グラフで見る
</a>
</div>
</p>

<!-- 年次データを取得する -->
<div style="margin: 30px auto; padding: 20px; background: #f8f9fa; border-radius: 8px; max-width: 600px;">
<h3 id="table">
<a href="#table">📊</a>
年次データを取得する
</h3>

<%= render_inline_flash_messages %>

<div style="display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 15px;">
<%= label_tag :year, '対象期間:', style: 'font-weight: bold;' %>
<%= select_tag :year,
options_for_select(
(2012..Date.current.year).to_a.reverse.map { |y| [y.to_s + '年', y] },
params[:year]
),
include_blank: '全期間',
onchange: "window.location.href = this.value ? '#{dojos_path}?year=' + this.value + '#table' : '#{dojos_path}#table'",
style: 'padding: 5px; border: 1px solid #ced4da; border-radius: 4px; cursor: pointer;' %>

<%= link_to 'CSV', dojos_path(format: :csv, year: params[:year]),
style: 'padding: 5px 15px; background: #28a745; color: white; text-decoration: none; border-radius: 4px;' %>

<%= link_to 'JSON', dojos_path(format: :json, year: params[:year]),
style: 'padding: 5px 15px; background: #6c757d; color: white; text-decoration: none; border-radius: 4px;' %>
</div>

<p style="font-size: smaller; color: #6c757d; margin-top: 15px;">
対象期間を選択すると、その時点のアクティブな道場の一覧を表示・ダウンロードできます。
</p>
</div>

<style type="text/css">
/* URL 用のセルにクラスを付けておく想定 */
Expand All @@ -47,7 +84,7 @@
</style>

<div style="margin-top: 20px;" align="center">
<table border="1">
<table border="1" class="stats-table">
<tr>
<th>
<small>
Expand All @@ -73,26 +110,49 @@
</tr>
<% @dojos.each do |dojo| %>
<tr>
<td>
<small>
<%= link_to dojo_path(dojo[:id]) do %>
<%= dojo[:name] %><br>
<small>(ID: <%= dojo[:id] %>)</small>
<% end %>
</small>
</td>
<td>
<small><%= dojo[:created_at].strftime("%F") %></small>
</td>
<td class="url-cell">
<small>
<a href='<%= dojo[:url] %>'>
<span title="<%= dojo[:url] %>">
<%= CGI.unescape dojo[:url].gsub('https://', '').gsub('http://', '').gsub('www.', '').chomp('/') %>
</span>
</a>
</small>
</td>
<% if dojo[:is_active] %>
<td>
<small>
<%= link_to dojo_path(dojo[:id]) do %>
<%= dojo[:name] %><br>
<small>(ID: <%= dojo[:id] %>)</small>
<% end %>
</small>
</td>
<td>
<small><%= dojo[:created_at].strftime("%F") %></small>
</td>
<td class="url-cell">
<small>
<a href='<%= dojo[:url] %>'>
<span title="<%= dojo[:url] %>">
<%= truncate(CGI.unescape(dojo[:url].gsub('https://', '').gsub('http://', '').gsub('www.', '').chomp('/')), length: 30) %>
</span>
</a>
</small>
</td>
<% else %>
<td class="inactive-item">
<small>
<%= link_to dojo_path(dojo[:id]) do %>
<%= dojo[:name] %><br>
<small>(ID: <%= dojo[:id] %>)</small>
<% end %>
</small>
</td>
<td class="inactive-item">
<small><%= dojo[:created_at].strftime("%F") %></small>
</td>
<td class="url-cell inactive-item">
<small>
<a href='<%= dojo[:url] %>'>
<span title="<%= dojo[:url] %>">
<%= truncate(CGI.unescape(dojo[:url].gsub('https://', '').gsub('http://', '').gsub('www.', '').chomp('/')), length: 30) %>
</span>
</a>
</small>
</td>
<% end %>
</tr>
<% end %>
</table>
Expand Down
5 changes: 4 additions & 1 deletion app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,11 @@
<body>
<%= render 'shared/header' %>

<%# 'inline_' プレフィックスがついたflashメッセージは、ここでは表示せず、各ビュー内でカスタム表示する %>
<% flash.each do |message_type, message| %>
<div class="alert alert-<%= message_type %>"><%= message %></div>
<% unless message_type.to_s.start_with?('inline_') %>
<div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>
<% end %>

<%= yield %>
Expand Down
10 changes: 5 additions & 5 deletions app/views/stats/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
</div>
<div style="margin-top: 20px;">
<% if @lang == 'en' %>
<a href="/stats">&raquo; Switch to Japanese</a>
<a href="/stats">&raquo; Switch to Japanese</a> / <a href="/dojos">&raquo; View Annual Data</a>
<% else %>
<a href="/english/stats">&raquo; View in English</a>
<a href="/english/stats">&raquo; View in English</a> / <a href="/dojos">&raquo; 年次データを見る</a>
<% end %>
</div>

Expand Down Expand Up @@ -361,7 +361,7 @@
</div>
</h2>
<div style="margin-top: 20px;" align="center">
<table border="1">
<table border="1" class="stats-table">
<tr>
<th style="padding: 10px;">
<small><%= @lang == 'en' ? 'Prefecture' : '都道府県名' %></small>
Expand All @@ -373,10 +373,10 @@
<% @data_by_prefecture.each_with_index do |(prefecture, count), index| %>
<tr>
<% if count == 0 %>
<td style="background-color: gainsboro; padding: 0px;">
<td class="inactive-item" style="padding: 0px;">
<small><%= prefecture %></small>
</td>
<td style="background-color: gainsboro; padding: 0px;">
<td class="inactive-item" style="padding: 0px;">
<small><%= count %></small>
</td>
<% else %>
Expand Down
1 change: 0 additions & 1 deletion db/dojos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3084,7 +3084,6 @@
order: '272124'
created_at: '2022-11-18'
name: 八尾
counter: 2
prefecture_id: 27
logo: "/img/dojos/yao-yotteco.webp"
url: https://www.facebook.com/profile.php?id=100087493006122
Expand Down
Loading