1
- # frozen_string_literal: true
2
-
3
1
require 'active_support/core_ext/integer/time'
4
2
require 'active_support/core_ext/numeric/time'
5
3
6
4
module Jekyll
7
5
module TimeTableGenerator
8
- # タイムテーブル表を事前に計算してグリッド形式に変換
6
+ # タイムテーブル表を事前に計算してイベント表形式に変換
9
7
# これにより、Liquid テンプレートは単純な表示のみを担当
10
8
class Generator < Jekyll ::Generator
11
9
safe true
@@ -17,112 +15,89 @@ class Generator < Jekyll::Generator
17
15
DEFAULT_DAY_END_HOUR = 16 # 16:00
18
16
19
17
def generate ( site )
20
- return unless site . data [ 'time_table' ]
21
-
22
18
tt = site . data [ 'time_table' ]
19
+ return unless tt
23
20
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' , { } )
30
28
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 )
33
31
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
36
35
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 )
40
45
end
41
46
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
+ }
46
53
end
47
54
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
58
68
}
59
69
end
60
70
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 )
64
72
room_index = rooms . index ( event [ 'room' ] )
65
73
return unless room_index
66
74
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' ] )
77
78
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
88
83
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
97
85
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
106
89
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'
112
94
end
113
95
end
114
96
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
126
101
end
127
102
end
128
103
end
0 commit comments