1- # frozen_string_literal: true
2-
31require 'active_support/core_ext/integer/time'
42require 'active_support/core_ext/numeric/time'
53
64module Jekyll
75 module TimeTableGenerator
8- # タイムテーブル表を事前に計算してグリッド形式に変換
6+ # タイムテーブル表を事前に計算してイベント表形式に変換
97 # これにより、Liquid テンプレートは単純な表示のみを担当
108 class Generator < Jekyll ::Generator
119 safe true
@@ -17,112 +15,89 @@ class Generator < Jekyll::Generator
1715 DEFAULT_DAY_END_HOUR = 16 # 16:00
1816
1917 def generate ( site )
20- return unless site . data [ 'time_table' ]
21-
2218 tt = site . data [ 'time_table' ]
19+ return unless tt
2320
24- # 設定値(Active Support の Duration を使用)
25- slot_duration = tt . fetch ( 'slot_minutes' , DEFAULT_SLOT_MINUTES ) . minutes
26- day_start = tt . fetch ( 'day_start_hour' , DEFAULT_DAY_START_HOUR ) . hours
27- day_end = tt . fetch ( 'day_end_hour' , DEFAULT_DAY_END_HOUR ) . hours
28- total_duration = day_end - day_start
29- total_slots = ( total_duration / slot_duration ) . to_i
21+ # 設定値を取得
22+ slot_minutes = tt . fetch ( 'slot_minutes' , DEFAULT_SLOT_MINUTES )
23+ day_start = tt . fetch ( 'day_start_hour' , DEFAULT_DAY_START_HOUR )
24+ day_end = tt . fetch ( 'day_end_hour' , DEFAULT_DAY_END_HOUR )
25+ rooms = tt . fetch ( 'rooms' , [ ] )
26+ events = tt . fetch ( 'events' , [ ] )
27+ room_styles = tt . fetch ( 'room_styles' , { } )
3028
31- rooms = tt . fetch ( 'rooms' , [ ] )
32- events = tt . fetch ( ' events' , [ ] )
29+ # イベント情報を表形式で生成
30+ time_table_events = create_event_table ( events , rooms , room_styles , slot_minutes , day_start , day_end )
3331
34- # イベントグリッドを作成(行 = 時間スロット、列 = 部屋)
35- event_grid = Array . new ( total_slots ) { Array . new ( rooms . size ) }
32+ # 生成したイベント表データを Liquid に提供
33+ site . data [ 'time_table_events' ] = time_table_events
34+ end
3635
37- # イベントグリッドに各イベントを配置
38- events . each do |event |
39- place_event_on_grid ( event , event_grid , rooms , day_start , day_end , slot_duration , total_slots )
36+ private
37+
38+ def create_event_table ( events , rooms , room_styles , slot_minutes , day_start , day_end )
39+ total_slots = ( ( day_end - day_start ) * 60 / slot_minutes ) . to_i
40+
41+ # 時間ラベルを生成
42+ time_labels = ( 0 ...total_slots ) . map do |slot |
43+ minutes = day_start * 60 + slot * slot_minutes
44+ "#{ minutes / 60 } :%02d" % ( minutes % 60 )
4045 end
4146
42- # 各スロットの時刻を事前計算
43- time_labels = ( 0 ...total_slots ) . map do |slot_index |
44- slot_time = day_start + ( slot_index * slot_duration )
45- format_time_label ( slot_time )
47+ # ルーム情報を生成(room.style でアクセス可能)
48+ rooms_data = rooms . map do |room_name |
49+ {
50+ 'name' => room_name ,
51+ 'style' => room_styles [ room_name ] || { }
52+ }
4653 end
4754
48- # 計算済みデータをsite.dataに追加(分単位に戻して保存)
49- site . data [ 'time_table_grid' ] = {
50- 'time_slots' => event_grid , # より明確な名前:時間スロットの配列
51- 'rooms' => rooms ,
52- 'room_styles' => tt . fetch ( 'room_styles' , { } ) ,
53- 'time_labels' => time_labels ,
54- 'slot_minutes' => slot_duration . in_minutes . to_i ,
55- 'day_start_min' => day_start . in_minutes . to_i ,
56- 'day_end_min' => day_end . in_minutes . to_i ,
57- 'total_slots' => total_slots
55+ # イベント表を生成(2次元配列)
56+ table = Array . new ( total_slots ) { Array . new ( rooms . size ) }
57+
58+ events . each do |event |
59+ place_event ( event , table , rooms , slot_minutes , day_start , total_slots )
60+ end
61+
62+ {
63+ 'events' => table ,
64+ 'rooms' => rooms_data ,
65+ 'time_labels' => time_labels ,
66+ 'total_slots' => total_slots - 1 , # Liquidの (0..n) は inclusive なので -1
67+ 'total_rooms' => rooms . size - 1 # Liquidの (0..n) は inclusive なので -1
5868 }
5969 end
6070
61- private
62-
63- def place_event_on_grid ( event , event_grid , rooms , day_start , day_end , slot_duration , total_slots )
71+ def place_event ( event , table , rooms , slot_minutes , day_start , total_slots )
6472 room_index = rooms . index ( event [ 'room' ] )
6573 return unless room_index
6674
67- # 開始・終了時間を Duration に変換
68- start_time = parse_time_to_duration ( event [ 'start' ] )
69- end_time = parse_time_to_duration ( event [ 'end' ] )
70-
71- # 表示範囲内に収める(クリッピング)
72- display_start = [ start_time , day_start ] . max
73- display_end = [ end_time , day_end ] . min
74-
75- # スロットインデックスを計算
76- start_slot , end_slot , span = calculate_slot_indices ( display_start , display_end , day_start , slot_duration )
75+ # 時間を分に変換
76+ start_minutes = time_to_minutes ( event [ 'start' ] )
77+ end_minutes = time_to_minutes ( event [ 'end' ] )
7778
78- # クリッピング済みなので start_slot は必ず有効範囲内
79- # display_start は day_start 以上、day_end 以下に制限されている
80- return if start_slot >= total_slots # 念のためのチェック(通常はテストで検知)
81-
82- # イベント開始セルを配置
83- event_grid [ start_slot ] [ room_index ] = create_event_cell ( event , span , start_slot , end_slot )
84-
85- # 継続スロットにマーカーを配置
86- mark_continued_slots ( event_grid , room_index , start_slot , end_slot , total_slots )
87- end
79+ # スロット計算
80+ start_slot = [ ( start_minutes - day_start * 60 ) / slot_minutes , 0 ] . max . to_i
81+ end_slot = [ ( end_minutes - day_start * 60 ) / slot_minutes , total_slots ] . min . to_i
82+ span = end_slot - start_slot
8883
89- def calculate_slot_indices ( display_start , display_end , day_start , slot_duration )
90- start_slot = ( ( display_start - day_start ) / slot_duration ) . to_i
91- # 終了時刻が正確にスロット境界上の場合、そのスロットを含めない
92- # 例: 10:30終了で15分スロットの場合、10:30-10:45のスロットは含めない
93- end_slot = ( ( display_end - day_start ) / slot_duration ) . ceil
94- span = end_slot - start_slot
95- [ start_slot , end_slot , span ]
96- end
84+ return if start_slot >= total_slots || span <= 0
9785
98- def create_event_cell ( event , span , start_slot , end_slot )
99- {
100- 'event' => event ,
101- 'span' => span ,
102- 'start_slot' => start_slot ,
103- 'end_slot' => end_slot
104- }
105- end
86+ # Eventにspan情報を追加
87+ enhanced_event = event . merge ( 'span' => span )
88+ table [ start_slot ] [ room_index ] = enhanced_event
10689
107- def mark_continued_slots ( event_grid , room_index , start_slot , end_slot , total_slots )
108- # end_slot を有効範囲内に制限してから反復処理
109- actual_end = [ end_slot , total_slots ] . min
110- ( start_slot + 1 ...actual_end ) . each do |slot |
111- event_grid [ slot ] [ room_index ] = { 'continued' => true }
90+ # 継続スロットをマーク
91+ ( start_slot + 1 ...end_slot ) . each do |slot |
92+ break if slot >= total_slots
93+ table [ slot ] [ room_index ] = 'continued'
11294 end
11395 end
11496
115- def parse_time_to_duration ( time_str )
116- return 0 . hours unless time_str
117- time = Time . parse ( time_str )
118- time . hour . hours + time . min . minutes
119- end
120-
121- def format_time_label ( duration )
122- total_minutes = duration . in_minutes . to_i
123- hours = total_minutes / 60
124- minutes = total_minutes % 60
125- format ( '%d:%02d' , hours , minutes )
97+ def time_to_minutes ( time_str )
98+ return 0 unless time_str
99+ hours , minutes = time_str . split ( ':' ) . map ( &:to_i )
100+ hours * 60 + minutes
126101 end
127102 end
128103 end
0 commit comments