opening_hours_converter 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3d01e4f3c1f4306964ee63ade1bb6e018722446a
4
+ data.tar.gz: 5671d033a5182245b46ff12838c2ccb11de8f7e5
5
+ SHA512:
6
+ metadata.gz: 99ec2ff1a1232f5ff42a6f0c015c4211fdf0a0a801b98c42d448e3b0828dfe0275e5e3246bd39403c716def6c3cbb241b35ad13a4a2114db592c12a0f6b38cb7
7
+ data.tar.gz: 4438647b1676e08b941159403631eefbc873e90970e24176006c30cb10406822b65a978e93d930fea8486c6b3c4b9f86ca40af42d5910484a633890a70ca9a4c
@@ -0,0 +1,22 @@
1
+ module OpeningHoursConverter
2
+ module Constants
3
+ DAYS = {
4
+ LUNDI: 0,
5
+ MARDI: 1,
6
+ MERCREDI: 2,
7
+ JEUDI: 3,
8
+ VENDREDI: 4,
9
+ SAMEDI: 5,
10
+ DIMANCHE: 6
11
+ }
12
+ OSM_DAYS = [ "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su" ]
13
+ IRL_DAYS = [ "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" ]
14
+ OSM_MONTHS = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
15
+ IRL_MONTHS = [ "Janvier", "Fevrier", "Mars", "Avril", "Mai", "Juin", "Juillet", "Aout", "Septembre",
16
+ "Octobre", "Novembre", "Décembre" ]
17
+ MONTH_END_DAY = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
18
+ MINUTES_MAX = 1440
19
+ DAYS_MAX = 6
20
+ YEAR_DAYS_MAX = 365
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ module OpeningHoursConverter
2
+ class DateRange
3
+ attr_accessor :wide_interval, :typical
4
+
5
+ def initialize(w=nil)
6
+ @wide_interval = nil
7
+ @typical = nil
8
+ update_range(w)
9
+ end
10
+
11
+ def defines_typical_day?
12
+ @typical.instance_of?(OpeningHoursConverter::Day)
13
+ end
14
+
15
+ def defines_typical_week?
16
+ @typical.instance_of?(OpeningHoursConverter::Week)
17
+ end
18
+
19
+ def update_range(wide)
20
+ @wide_interval = !wide.nil? ? wide : OpeningHoursConverter::WideInterval.new.always
21
+
22
+ if @typical.nil?
23
+ case @wide_interval.type
24
+ when "day"
25
+ if @wide_interval.end.nil?
26
+ @typical = OpeningHoursConverter::Day.new
27
+ else
28
+ @typical = OpeningHoursConverter::Week.new
29
+ end
30
+ when "week"
31
+ @typical = OpeningHoursConverter::Week.new
32
+ when "month"
33
+ @typical = OpeningHoursConverter::Week.new
34
+ when "always"
35
+ @typical = OpeningHoursConverter::Week.new
36
+ end
37
+ end
38
+ end
39
+
40
+ def has_same_typical?(date_range)
41
+ defines_typical_day? == date_range.defines_typical_day? && @typical.same_as?(date_range.typical)
42
+ end
43
+
44
+ def is_general_for?(date_range)
45
+ defines_typical_day? == date_range.defines_typical_day? && @wide_interval.contains?(date_range.wide_interval)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,100 @@
1
+ require 'opening_hours_converter/constants'
2
+
3
+ module OpeningHoursConverter
4
+ class Day
5
+ include Constants
6
+ attr_accessor :intervals
7
+
8
+ def initialize
9
+ @intervals = []
10
+ end
11
+
12
+ def get_as_minute_array
13
+ minute_array = Array.new(MINUTES_MAX + 1, false)
14
+
15
+ @intervals.each do |interval|
16
+ if !interval.nil?
17
+ start_minute = nil
18
+ end_minute = nil
19
+
20
+ if interval.day_start == interval.day_end || interval.day_end == DAYS_MAX && interval.end == MINUTES_MAX
21
+ start_minute = interval.start
22
+ end_minute = interval.end
23
+ end
24
+
25
+ unless start_minute.nil? && end_minute.nil?
26
+ for minute in start_minute..end_minute
27
+ minute_array[minute] = true
28
+ end
29
+ else
30
+ raise "Invalid interval #{interval.inspect}"
31
+ end
32
+ end
33
+ end
34
+
35
+ minute_array
36
+ end
37
+
38
+ def get_intervals(clean=false)
39
+ if clean
40
+ minute_array = get_as_minute_array
41
+ intervals = []
42
+ minute_start = -1
43
+ minute_end = nil
44
+ minute_array.each_with_index do |minute, i|
45
+ if i == 0
46
+ if minute
47
+ minute_start = i
48
+ end
49
+ elsif i == minute_array.length - 1
50
+ if minute
51
+ intervals << OpeningHoursConverter::Interval.new(0, minute_start, 0, i - 1)
52
+ minute_start = -1
53
+ end
54
+ else
55
+ if minute && minute_start < 0
56
+ minute_start = i
57
+ elsif !minute && minute_start >= 0
58
+ intervals << OpeningHoursConverter::Interval.new(0, minute_start, 0, i - 1)
59
+ minute_start = -1
60
+ end
61
+ end
62
+ end
63
+ intervals
64
+ else
65
+ @intervals
66
+ end
67
+ end
68
+
69
+ def add_interval(interval)
70
+ @intervals << interval
71
+ return @intervals.length - 1
72
+ end
73
+
74
+ def edit_interval(id, interval)
75
+ @intervals[id] = interval
76
+ end
77
+
78
+ def remove_interval(id)
79
+ @intervals[id] = nil
80
+ end
81
+
82
+ def clear_intervals
83
+ @intervals = []
84
+ end
85
+
86
+ def copy_intervals(intervals)
87
+ @intervals = []
88
+ intervals.each do |interval|
89
+ if !interval.nil? && interval.day_start == 0 && interval.day_end == 0
90
+ @intervals << interval.dup
91
+ end
92
+ end
93
+ @intervals = get_intervals(true)
94
+ end
95
+
96
+ def same_as?(day)
97
+ day.get_as_minute_array == get_as_minute_array
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,20 @@
1
+ require 'opening_hours_converter/constants'
2
+
3
+ module OpeningHoursConverter
4
+ class Interval
5
+ include Constants
6
+ attr_accessor :day_start, :day_end, :start, :end
7
+
8
+ def initialize(day_start, min_start, day_end=0, min_end=0)
9
+ @day_start = day_start
10
+ @day_end = day_end
11
+ @start = min_start
12
+ @end = min_end
13
+
14
+ if @day_end == 0 && @end == 0
15
+ @day_end = DAYS_MAX
16
+ @end = MINUTES_MAX
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,368 @@
1
+ require 'opening_hours_converter/constants'
2
+
3
+ module OpeningHoursConverter
4
+ class OpeningHoursBuilder
5
+ include Constants
6
+ def build(date_ranges)
7
+ rules = []
8
+
9
+ oh_rules = nil
10
+ oh_rule_added = nil
11
+ range_general = nil
12
+ range_general_for = nil
13
+
14
+ date_ranges.each_with_index do |date_range, date_range_index|
15
+ if !date_range.nil?
16
+ range_general = nil
17
+ range_general_for = nil
18
+ range_general_id = date_range_index - 1
19
+ while range_general_id >= 0 && range_general.nil?
20
+ if !date_range.nil?
21
+ general_for = date_ranges[range_general_id].is_general_for?(date_range)
22
+ if date_ranges[range_general_id].has_same_typical?(date_range) && (date_ranges[range_general_id].wide_interval.equals(date_range.wide_interval) || general_for)
23
+ range_general = range_general_id
24
+ elsif general_for && date_ranges[range_general_id].defines_typical_week? && date_range.defines_typical_week?
25
+ range_general_for = range_general_id
26
+ end
27
+ end
28
+ range_general_id -= 1
29
+ end
30
+
31
+ if date_range_index == 0 || range_general.nil?
32
+ if date_range.defines_typical_week?
33
+ if !range_general_for.nil?
34
+ oh_rules = build_week_diff(date_range, date_ranges[range_general_for])
35
+ else
36
+ oh_rules = build_week(date_range)
37
+ end
38
+ else
39
+ oh_rules = build_day(date_range)
40
+ end
41
+ end
42
+
43
+ oh_rule_index = 0
44
+
45
+ while oh_rule_index < oh_rules.length
46
+ oh_rule = oh_rules[oh_rule_index]
47
+ oh_rule_added = false
48
+ rule_index = 0
49
+
50
+ while !oh_rule_added && rule_index < rules.length
51
+ if rules[rule_index].same_time?(oh_rule)
52
+ begin
53
+ for date_id in 0...oh_rule.date.length
54
+ rules[rule_index].add_date(oh_rule.date[date_id])
55
+ end
56
+ oh_rule_added = true
57
+ rescue Exception => e
58
+ puts e
59
+ # if(
60
+ # ohrule.getDate()[0].getWideType() == "holiday"
61
+ # && ohrule.getDate()[0].getWideValue() == "PH"
62
+ # && rules[ruleId].getDate()[0].getWideType() == "always"
63
+ # ) {
64
+ # rules[ruleId].addPhOpeningHoursConverter::Weekday();
65
+ # ohruleAdded = true;
66
+ # }
67
+ # else if(
68
+ # rules[ruleId].getDate()[0].getWideType() == "holiday"
69
+ # && rules[ruleId].getDate()[0].getWideValue() == "PH"
70
+ # && ohrule.getDate()[0].getWideType() == "always"
71
+ # ) {
72
+ # ohrule.addPhOpeningHoursConverter::Weekday();
73
+ # rules[ruleId] = ohrule;
74
+ # ohruleAdded = true;
75
+ # }
76
+ # else {
77
+ # ruleId++;
78
+ # }
79
+ rule_index += 1
80
+ end
81
+ else
82
+ rule_index+=1
83
+ end
84
+
85
+ end
86
+
87
+ if !oh_rule_added
88
+ rules << oh_rule
89
+ end
90
+
91
+ if oh_rule_index == oh_rules.length - 1 && oh_rule.has_overwritten_weekday?
92
+ oh_rule_over = OpeningHoursConverter::OpeningHoursRule.new
93
+
94
+ oh_rule.date.each do |date|
95
+ oh_rule_over.add_date(OpeningHoursConverter::OpeningHoursDate.new(date.wide, date.wide_type, date.weekdays_over))
96
+ end
97
+ oh_rule_over.add_time(OpeningHoursConverter::OpeningHoursTime.new)
98
+ oh_rules << oh_rule_over
99
+ oh_rule_index += 1
100
+ else
101
+ oh_rule_index += 1
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+
108
+ result = ""
109
+ rules.each_with_index do |rule, rule_index|
110
+ if rule_index > 0
111
+ result += "; "
112
+ end
113
+ result += rule.get
114
+ end
115
+
116
+ return result
117
+ end
118
+
119
+ def build_day(date_range)
120
+ intervals = date_range.typical.get_intervals(true)
121
+
122
+ rule = OpeningHoursConverter::OpeningHoursRule.new
123
+ date = OpeningHoursConverter::OpeningHoursDate.new(date_range.wide_interval.get_time_selector, date_range.wide_interval.type, [-1])
124
+ rule.add_date(date)
125
+
126
+ intervals.each do |interval|
127
+ if !interval.nil?
128
+ rule.add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, interval.end))
129
+ end
130
+ end
131
+
132
+ return [ rule ]
133
+ end
134
+
135
+ def build_week(date_range)
136
+ result = []
137
+ intervals = date_range.typical.get_intervals(true)
138
+ time_intervals = create_time_intervals(date_range.wide_interval.get_time_selector, date_range.wide_interval.type, intervals)
139
+
140
+ monday0 = time_intervals[0]
141
+ sunday24 = time_intervals[1]
142
+ days = time_intervals[2]
143
+
144
+ days = night_monday_sunday(days, monday0, sunday24)
145
+
146
+
147
+ days_status = Array.new(OSM_DAYS.length, 0)
148
+
149
+ days.each_with_index do |day, index|
150
+ if day.is_off? && days_status[index] == 0
151
+ days_status[index] = 8
152
+ elsif day.is_off? && days_status[index] < 0 && days_status[index] > -8
153
+ days_status[index] = -8
154
+ merged = false
155
+ md_off = 0
156
+ while !merged && md_off < index
157
+ if days[md_off].is_off?
158
+ days[md_off].add_weekday(index)
159
+ merged = true
160
+ else
161
+ md_off += 1
162
+ end
163
+ if !merged
164
+ result << days[index]
165
+ end
166
+ end
167
+ elsif days_status[index] <= 0 && days_status[index] > -8
168
+ days_status[index] = index + 1
169
+ last_same_day = index
170
+ same_day_count = 1
171
+
172
+ for j in (index+1)...days.length do
173
+ if day.same_time?(days[j])
174
+ days_status[j] = index + 1
175
+ day.add_weekday(j)
176
+ last_same_day = j
177
+ same_day_count += 1
178
+ end
179
+ end
180
+ if same_day_count == 1
181
+ result << day
182
+ elsif same_day_count == 2
183
+ day.add_weekday(last_same_day)
184
+ result << day
185
+ elsif same_day_count > 2
186
+ for j in (index+1)...last_same_day do
187
+ if days_status[j] == 0
188
+ days_status[j] = -index -1
189
+ day.add_overwritten_weekday(j)
190
+ end
191
+ end
192
+ day.add_weekday(last_same_day)
193
+ result << day
194
+ end
195
+ end
196
+ end
197
+ result = merge_days(result)
198
+
199
+ return result
200
+ end
201
+
202
+ def build_week_diff(date_range, general_date_range)
203
+ intervals = date_range.typical.get_intervals_diff(general_date_range.typical)
204
+
205
+ time_intervals = create_time_intervals(
206
+ date_range.wide_interval.get_time_selector,
207
+ date_range.wide_interval.type,
208
+ intervals[:open])
209
+ monday0 = time_intervals[0]
210
+ sunday24 = time_intervals[1]
211
+ days = time_intervals[2]
212
+ intervals[:closed].each do |interval|
213
+ for i in interval.day_start..interval.day_end do
214
+ days[i].add_time(OpeningHoursConverter::OpeningHoursTime.new)
215
+ end
216
+ end
217
+
218
+ days = night_monday_sunday(days, monday0, sunday24)
219
+
220
+ days_status = Array.new(OSM_DAYS.length, 0)
221
+ result = []
222
+
223
+ days.each_with_index do |day, index|
224
+ if day.is_off? && day.time.length == 1
225
+ days_status[index] = -8
226
+ merged = false
227
+ md_off = 0
228
+
229
+ while !merged && md_off < index
230
+ if days[md_off].is_off? && days[md_off].time.length == 1
231
+ days[md_off].add_weekday(index)
232
+ merged = true
233
+ else
234
+ md_off += 1
235
+ end
236
+ end
237
+
238
+ if !merged
239
+ result << day
240
+ end
241
+ elsif day.is_off? && day.time.length == 0
242
+ days_status[index] = 8
243
+ elsif days_status[index] <= 0 && days_status[index] > -8
244
+ days_status[index] = index + 1
245
+ same_day_count = 1
246
+ last_same_day = 1
247
+ result << day
248
+
249
+ for j in (index + 1)...days.length do
250
+ if day.same_time?(days[j])
251
+ days_status[j] = index + 1
252
+ day.add_weekday(j)
253
+ last_same_day = j
254
+ same_day_count += 1
255
+ end
256
+ end
257
+
258
+ if same_day_count == 1
259
+ result << day
260
+ elsif same_day_count == 2
261
+ day.add_weekday(last_same_day)
262
+ result << day
263
+ elsif same_day_count > 2
264
+ for j in (index + 1)...last_same_day do
265
+ if days_status[j] == 0
266
+ days_status[j] = -index - 1
267
+ if days[j].time.length > 0
268
+ day.add_overwritten_weekday(j)
269
+ end
270
+ end
271
+ end
272
+ day.add_weekday(last_same_day)
273
+ result << day
274
+ end
275
+
276
+ end
277
+ end
278
+ result = merge_days(result)
279
+ return result
280
+ end
281
+
282
+ def merge_days(rules)
283
+ return rules if rules.length == 0
284
+ result = []
285
+ result << rules[0]
286
+ dm = 0
287
+
288
+ for d in 1...rules.length do
289
+ date_merged = false
290
+ dm = 0
291
+ while !date_merged && dm < d
292
+ if rules[dm].same_time?(rules[d])
293
+ wds = rules[d].date[0].weekdays
294
+ wds.each do |wd|
295
+ rules[dm].add_weekday(wd)
296
+ end
297
+ date_merged = true
298
+ end
299
+ dm += 1
300
+ end
301
+ if !date_merged
302
+ result << rules[d]
303
+ end
304
+ end
305
+
306
+ return result
307
+ end
308
+
309
+ def create_time_intervals(time_selector, type, intervals)
310
+ monday0 = -1
311
+ sunday24 = -1
312
+
313
+ days = []
314
+ for i in 0...7 do
315
+ days << OpeningHoursConverter::OpeningHoursRule.new
316
+ days[i].add_date(OpeningHoursConverter::OpeningHoursDate.new(time_selector, type, [ i ]))
317
+ end
318
+
319
+ intervals.each do |interval|
320
+ if !interval.nil?
321
+ if interval.day_start == DAYS_MAX && interval.day_end == DAYS_MAX && interval.end == MINUTES_MAX
322
+ sunday24 = interval.start
323
+ end
324
+ if interval.day_start == 0 && interval.day_end == 0 && interval.start == 0
325
+ monday0 = interval.end
326
+ end
327
+ begin
328
+ if interval.day_start == interval.day_end
329
+ days[interval.day_start].add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, interval.end))
330
+ elsif interval.day_end - interval.day_start == 1
331
+ if interval.start > interval.end
332
+ days[interval.day_start].add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, interval.end))
333
+ else
334
+ days[interval.day_start].add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, MINUTES_MAX))
335
+ days[interval.day_end].add_time(OpeningHoursConverter::OpeningHoursTime.new(0, interval.end))
336
+ end
337
+ else
338
+ for j in interval.day_start..interval.day_end do
339
+ if j == interval.day_start
340
+ days[j].add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, MINUTES_MAX))
341
+ elsif j == interval.day_end
342
+ days[j].add_time(OpeningHoursConverter::OpeningHoursTime.new(0, interval.end))
343
+ else
344
+ days[j].add_time(OpeningHoursConverter::OpeningHoursTime.new(0, MINUTES_MAX))
345
+ end
346
+ end
347
+ end
348
+ rescue Exception => e
349
+ puts e
350
+ end
351
+ end
352
+ end
353
+
354
+ return [ monday0, sunday24, days ]
355
+ end
356
+
357
+ def night_monday_sunday(days, monday0, sunday24)
358
+ if monday0 >= 0 && sunday24 >= 0 && monday0 < sunday24
359
+ days[0].time.sort! { |a, b| a.start <=> b.start }
360
+ days[6].time.sort! { |a, b| a.start <=> b.start }
361
+
362
+ days[6].time[days[6].time.length - 1] = OpeningHoursConverter::OpeningHoursTime.new(sunday24, monday0)
363
+ days[0].time.shift
364
+ end
365
+ return days
366
+ end
367
+ end
368
+ end
@@ -0,0 +1,126 @@
1
+ require 'opening_hours_converter/constants'
2
+
3
+ module OpeningHoursConverter
4
+ class OpeningHoursDate
5
+ include Constants
6
+ attr_accessor :wide_type, :wide, :weekdays, :weekdays_over
7
+
8
+ def initialize(wide, wide_type, weekdays)
9
+ if wide.nil? || wide_type.nil? || weekdays.nil?
10
+ raise ArgumentError
11
+ end
12
+
13
+ @wide = wide
14
+ @wide_type = wide_type
15
+ @weekdays = weekdays.sort
16
+ @weekdays_over = []
17
+ end
18
+
19
+ def get_weekdays
20
+
21
+ result = ""
22
+ wd = @weekdays.concat(@weekdays_over).sort.uniq
23
+
24
+ if wd.length > 0 && wd.include?(6) && wd.include?(0) && (wd.include?(5) || wd.include?(1))
25
+ start_we = 6
26
+ i = wd.length - 2
27
+ stop_looking = false
28
+
29
+ while !stop_looking && i >= 0
30
+ if wd[i] == wd[i+1] - 1
31
+ start_we = wd[i]
32
+ i -= 1
33
+ else
34
+ stop_looking = true
35
+ end
36
+ end
37
+
38
+ i = 1
39
+ stop_looking = false
40
+ end_we = 0
41
+
42
+ while !stop_looking && i < wd.length
43
+ if wd[i-1] == wd[i] - 1
44
+ end_we = wd[i]
45
+ i += 1
46
+ else
47
+ stop_looking = true
48
+ end
49
+ end
50
+
51
+ length = 7 - start_we + end_we + 1
52
+
53
+ if length >= 3 && start_we > end_we
54
+ if result.length > 0
55
+ result += ","
56
+ end
57
+ result += "#{OSM_DAYS[start_we]}-#{OSM_DAYS[end_we]}"
58
+
59
+ j=0
60
+ while j < wd.length
61
+ if wd[j] <= end_we || wd[j] >= start_we
62
+ wd.slice!(j, 1)
63
+ else
64
+ j+=1
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ if wd.length > 1 || (wd.length == 1 && wd[0] != -1)
71
+ result += result.length > 0 ? ",#{OSM_DAYS[wd[0]]}" : OSM_DAYS[wd[0]]
72
+ first_in_row = wd[0]
73
+ for i in 1...wd.length
74
+ if wd[i-1] != wd[i] - 1
75
+ if first_in_row != wd[i-1]
76
+ if wd[i-1] - first_in_row == 1
77
+ result += ",#{OSM_DAYS[wd[i-1]]}"
78
+ else
79
+ result += "-#{OSM_DAYS[wd[i-1]]}"
80
+ end
81
+ end
82
+ result += ",#{OSM_DAYS[wd[i]]}"
83
+ first_in_row = wd[i]
84
+ elsif i == wd.length - 1
85
+ if wd[i] - first_in_row == 1
86
+ result += ",#{OSM_DAYS[wd[i]]}"
87
+ else
88
+ result += "-#{OSM_DAYS[wd[i]]}"
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ if result == "Mo-Su"
95
+ result = ""
96
+ end
97
+ return result
98
+ end
99
+
100
+ def add_weekday(weekday)
101
+ if !@weekdays.include?(weekday) && !@weekdays_over.include?(weekday)
102
+ @weekdays << weekday
103
+ @weekdays.sort!
104
+ end
105
+ end
106
+
107
+ def add_overwritten_weekday(weekday)
108
+ unless @weekdays_over.include?(weekday) && @weekdays_over.include?(weekday)
109
+ @weekdays_over << weekday
110
+ @weekdays_over.sort!
111
+ end
112
+ end
113
+
114
+ def same_kind_as?(date)
115
+ @wide_type == date.wide_type && date.same_weekdays?(@weekdays)
116
+ end
117
+
118
+ def same_weekdays?(weekdays)
119
+ weekdays == @weekdays
120
+ end
121
+
122
+ def equals(o)
123
+ o.instance_of?(OpeningHoursConverter::OpeningHoursDate) && @wide_type == o.wide_type && @wide == o.wide && o.same_weekdays?(@weekdays)
124
+ end
125
+ end
126
+ end