lib/ragoon/services/schedule.rb in ragoon-0.7.0 vs lib/ragoon/services/schedule.rb in ragoon-0.8.0
- old
+ new
@@ -1,27 +1,39 @@
class Ragoon::Services::Schedule < Ragoon::Services
+ XML_NS = 'http://schemas.cybozu.co.jp/schedule/2008'
+
def schedule_get_events(options = {})
action_name = 'ScheduleGetEvents'
options = default_options(action_name).merge(options)
body_node = Ragoon::XML.create_node(action_name)
parameter_node = Ragoon::XML.create_node(
'parameters',
- start: options[:start].strftime('%FT%T'),
- end: options[:end].strftime('%FT%T'),
- start_for_daily: options[:start].localtime.strftime('%F'),
- end_for_daily: options[:end].localtime.strftime('%F'),
+ start: to_datetime_str(options[:start]),
+ end: to_datetime_str(options[:end]),
+ start_for_daily: to_date_str(options[:start]),
+ end_for_daily: to_date_str(options[:end]),
)
body_node.add_child(parameter_node)
client.request(action_name, body_node)
- events = client.result_set.xpath('//schedule_event').
- find_all { |ev| ev[:event_type] != 'banner' }.map do |event|
- parse_event(event)
- end
+ events_by_type = client.result_set.xpath('//schedule_event')
+ .group_by {|ev| ev[:event_type] }
+
+ # events_by_type['banner'] does not include results
+ [
+ (events_by_type['normal'] || []).map {|event|
+ parse_event(event)
+ },
+ (events_by_type['repeat'] || []).map {|event|
+ expand_repeat_event_periods(event, options[:start], options[:end]).map {|period|
+ parse_event(event, period)
+ }
+ }
+ ].flatten.sort_by { |e| e[:start_at] }
end
def schedule_add_event(options = {})
action_name = 'ScheduleAddEvents'
@@ -44,40 +56,34 @@
public_type: (options[:private] ? 'private' : 'public')
}
)
parameter_node.add_child(schedule_event_node)
- members_node = Ragoon::XML.create_node(
- 'members',
- xmlns: 'http://schemas.cybozu.co.jp/schedule/2008'
- )
+ members_node = Ragoon::XML.create_node('members', xmlns: XML_NS)
schedule_event_node.add_child(members_node)
if options[:users]
options[:users].each do |user|
member_node = Ragoon::XML.create_node('member')
user_node = Ragoon::XML.create_node('user', id: user.to_i)
member_node.add_child(user_node)
members_node.add_child(member_node)
end
end
- when_node = Ragoon::XML.create_node(
- 'when',
- xmlns: 'http://schemas.cybozu.co.jp/schedule/2008'
- )
+ when_node = Ragoon::XML.create_node('when', xmlns: XML_NS)
date_node =
if options[:allday]
Ragoon::XML.create_node(
'date',
- start: options[:start_at].strftime('%F'),
- end: options[:end_at].strftime('%F')
+ start: to_date_str(options[:start_at]),
+ end: to_date_str(options[:end_at])
)
else
Ragoon::XML.create_node(
'datetime',
- start: options[:start_at].utc.strftime('%FT%T'),
- end: options[:end_at].utc.strftime('%FT%T')
+ start: to_datetime_str(options[:start_at]),
+ end: to_datetime_str(options[:end_at])
)
end
when_node.add_child(date_node)
schedule_event_node.add_child(when_node)
@@ -87,12 +93,13 @@
find_all { |ev| ev[:event_type] != 'banner' }.map { |event|
parse_event(event)
}.first
end
- def parse_event(event)
- period = start_and_end(event)
+ def parse_event(event, period = nil)
+ period = start_and_end(event) if period.nil?
+
{
id: event[:id],
url: event_url(event[:id]),
title: event[:detail],
start_at: period[:start_at],
@@ -100,26 +107,27 @@
plan: event[:plan],
facilities: facility_names(event),
users: users_info(event),
private: !(event[:public_type] == 'public'),
allday: event[:allday] == 'true',
+ event_type: event[:event_type],
}
end
def event_url(id)
"#{base_endpoint}/schedule/view?event=#{id}"
end
def facility_names(event)
- event.xpath('ev:members', ev: "http://schemas.cybozu.co.jp/schedule/2008").
- children.map { |c| c.xpath('ev:facility', ev: "http://schemas.cybozu.co.jp/schedule/2008").first }.
+ event.xpath('ev:members', ev: XML_NS).
+ children.map { |c| c.xpath('ev:facility', ev: XML_NS).first }.
compact.map { |n| n[:name] }
end
def users_info(event)
- event.xpath('ev:members', ev: "http://schemas.cybozu.co.jp/schedule/2008").
- children.map { |c| c.xpath('ev:user', ev: "http://schemas.cybozu.co.jp/schedule/2008").first }.
+ event.xpath('ev:members', ev: XML_NS).
+ children.map { |c| c.xpath('ev:user', ev: XML_NS).first }.
compact.map do |n|
{
id: n[:id].to_i,
name: n[:name].to_s,
}
@@ -127,49 +135,118 @@
end
def start_and_end(event)
start_at = nil
end_at = nil
+ timezone = event[:timezone]
+ end_timezone = event[:end_timezone] || event[:timezone]
unless event[:allday] == 'true'
- case event[:event_type]
- when 'normal'
- period = event.children.xpath('ev:datetime', ev: "http://schemas.cybozu.co.jp/schedule/2008").first
- unless period.nil?
- start_at = parse_event_time(period[:start])
- unless event[:start_only] == 'true'
- end_at = parse_event_time(period[:end])
- end
- end
- when 'repeat'
- repeat_info = event.xpath('ev:repeat_info', ev: 'http://schemas.cybozu.co.jp/schedule/2008').first
- unless repeat_info.nil?
- period = repeat_info.xpath('ev:condition', ev: 'http://schemas.cybozu.co.jp/schedule/2008').first
- unless period.nil?
- start_at = parse_event_time(period[:start_time])
- unless event[:start_only] == 'true'
- end_at = parse_event_time(period[:end_time])
- end
- end
- end
+ period = event.xpath('ev:when/ev:datetime', ev: XML_NS).first
+ unless period.nil?
+ start_at = parse_event_time(period[:start], timezone)
+ end_at = parse_event_time(period[:end], end_timezone)
end
+
+ else
+ period = event.xpath('ev:when/ev:date', ev: XML_NS).first
+ unless period.nil?
+ start_at = parse_event_time(period[:start], timezone)
+ end_at = parse_event_time(period[:end] + " 23:59:59", end_timezone)
+ end
end
{
start_at: start_at,
end_at: end_at,
}
end
- def parse_event_time(time)
- Time.parse(time).localtime.to_s
+ def parse_event_time(time, timezone = ENV['TZ'], now = Time.now)
+ return nil if time.nil?
+
+ begin
+ old_timezone = ENV['TZ']
+ ENV['TZ'] = timezone
+ Time.parse(time, now).localtime.to_s
+ ensure
+ ENV['TZ'] = old_timezone
+ end
end
def default_options(action_name)
case action_name
when 'ScheduleGetEvents'
Ragoon::Services.start_and_end
else
{}
end
+ end
+
+ private
+
+ def expand_repeat_event_periods(event, start_at, end_at)
+ timezone = event[:timezone]
+ end_timezone = event[:end_timezone] || event[:timezone]
+
+ periods = []
+
+ event.xpath('ev:repeat_info/ev:condition', ev: XML_NS).each do |cond|
+ exclusive_dates = event.xpath('ev:repeat_info/ev:exclusive_datetimes/ev:exclusive_datetime',
+ ev: XML_NS).map do |exclusive_date|
+ Date.parse(exclusive_date[:start])
+ end
+
+ dates = expand_dates(start_at.to_date, end_at.to_date, cond, exclusive_dates)
+
+ periods = dates.map do |date|
+ {
+ start_at: parse_event_time(cond[:start_time], timezone, date.to_time),
+ end_at: parse_event_time(cond[:end_time], end_timezone, date.to_time)
+ }
+ end
+ end
+
+ periods
+ end
+
+ def expand_dates(start_date, end_date, cond, exclusive_dates)
+ (start_date..end_date).select {|date|
+ next false if exclusive_dates.include?(date)
+
+ case cond[:type]
+ when 'day' # everyday
+ next true
+
+ when 'week' # everyweek
+ next date.wday == cond[:week].to_i
+
+ when 'weekday'
+ next (1..5).include?(date.wday)
+
+ when '1stweek'
+ next nth_weekday_of_month(date) == 1 && date.wday == cond[:week].to_i
+
+ when '2ndweek'
+ next nth_weekday_of_month(date) == 2 && date.wday == cond[:week].to_i
+
+ when '3rdweek'
+ next nth_weekday_of_month(date) == 3 && date.wday == cond[:week].to_i
+
+ when '4thweek'
+ next nth_weekday_of_month(date) == 4 && date.wday == cond[:week].to_i
+
+ when 'lastweek'
+ next date.month != date.next_day(7).month &&
+ date.wday == cond[:week].to_i
+
+ when 'month'
+ next date.day == cond[:day].to_i
+
+ end
+ }
+ end
+
+ def nth_weekday_of_month(date)
+ (date.day + 6) / 7
end
end