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: 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_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' options = default_options(action_name).merge(options) body_node = Ragoon::XML.create_node(action_name) parameter_node = Ragoon::XML.create_node('parameters') body_node.add_child(parameter_node) schedule_event_node = Ragoon::XML.create_node( 'schedule_event', { xmlns: '', id: 'dummy', version: 'dummy', event_type: 'normal', plan: options[:plan], detail: options[:title], description: options[:description], public_type: (options[:private] ? 'private' : 'public') } ) parameter_node.add_child(schedule_event_node) 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: XML_NS) date_node = if options[:allday] Ragoon::XML.create_node( 'date', start: to_date_str(options[:start_at]), end: to_date_str(options[:end_at]) ) else Ragoon::XML.create_node( 'datetime', 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) client.request(action_name, body_node) events = client.result_set.xpath('//schedule_event'). find_all { |ev| ev[:event_type] != 'banner' }.map { |event| parse_event(event) }.first end 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], end_at: period[:end_at], 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: 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: 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, } end 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' 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, 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