Class: Calendar
- Inherits:
-
DynamicContent
- Object
- DynamicContent
- Calendar
- Defined in:
- app/models/calendar.rb
Defined Under Namespace
Classes: CalendarResults
Constant Summary collapse
- DISPLAY_FORMATS =
{ "List (Multiple)" => "headlines", "Detailed (Single)" => "detailed" }
- CALENDAR_SOURCES =
exclude RSS and ATOM since cant get individual fields
{ # exclude RSS and ATOM since cant get individual fields "Google" => "google", "iCal" => "ical", # "Bedework JSON" => "bedeworkjson" }
Class Method Summary collapse
-
.form_attributes ⇒ Object
calendar api parameters and preferred view (output_format).
Instance Method Summary collapse
- #build_content ⇒ Object
- #fetch_calendar ⇒ Object
- #item_to_html(item, day_format, time_format) ⇒ Object
-
#items_to_html(items, day_format, time_format) ⇒ Object
display date (only when it changes) / times with title…
- #set_defaults ⇒ Object
- #validate_config ⇒ Object
-
#validate_request ⇒ Object
make sure the request is valid by fetching a result back.
Class Method Details
.form_attributes ⇒ Object
calendar api parameters and preferred view (output_format)
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'app/models/calendar.rb', line 221 def self.form_attributes attributes = super() attributes.concat([:config => [ :calendar_source, # google or ical (or bedework JSON eventually) :api_key, # google :calendar_id, # google :calendar_url, # iCal url (specify parms in url manually) :max_results, :days_ahead, :start_date, :end_date, :output_format, # all cals :day_format, # all cals :time_format # all cals ]]) end |
Instance Method Details
#build_content ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'app/models/calendar.rb', line 55 def build_content contents = [] result = fetch_calendar if result.error? raise result. else day_format = self.config['day_format'] time_format = self.config['time_format'] case self.config['output_format'] when 'headlines' # 5 items per entry, titles only result.items.each_slice(5).with_index do |items, index| htmltext = HtmlText.new() htmltext.name = "#{result.name} (#{index+1})" htmltext.data = "<h1>#{result.name}</h1>#{items_to_html(items, day_format, time_format)}" contents << htmltext end when 'detailed' # each item is a separate entry, title and description result.items.each_with_index do |item, index| htmltext = HtmlText.new() htmltext.name = "#{result.name} (#{index+1})" htmltext.data = item_to_html(item, day_format, time_format) contents << htmltext end else raise ArgumentError, 'Unexpected output format for Calendar feed.' end end return contents end |
#fetch_calendar ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'app/models/calendar.rb', line 88 def fetch_calendar result = CalendarResults.new client_key = self.config['api_key'] calendar_id = self.config['calendar_id'] calendar_source = self.config['calendar_source'] start_date = self.config['start_date'].strip.empty? ? Clock.time.beginning_of_day.iso8601 : self.config['start_date'].to_time.beginning_of_day.iso8601 # end_date is not used by the google api, as the resulting behavior is unexpected end_date = self.config['end_date'].strip.empty? ? (start_date.to_time.beginning_of_day + self.config['days_ahead'].to_i.days).end_of_day.iso8601 : self.config['end_date'].to_time.beginning_of_day.iso8601 case calendar_source when 'google' if !client_key.empty? # ---------------------------------- google calendar api v3 via client api require 'google/apis/calendar_v3' client = Google::Apis::CalendarV3::CalendarService.new client.key = client_key begin tmp = client.list_events(calendar_id, max_results: self.config['max_results'], single_events: true, order_by: 'startTime', time_min: start_date) # convert to common data structure #result.error_message = tmp.error_message if tmp.error? rescue => e result. = e. end if !result.error? result.name = tmp.summary tmp.items.each do |item| # All-day events aren't parsed as DateTime. Make it so. starttime = item.start.date_time || Date.parse(item.start.date) endtime = item.end.date_time || Date.parse(item.end.date) result.add_item(item.summary, item.description, item.location, starttime, endtime) end end end when 'ical' # ---------------------------------- iCal calendar # need to filter manually below because the url may not accommodate filtering # so respect self.config[max_results] and start_date and end_date (which incorporates the days ahead) require 'open-uri' require 'icalendar' begin url = self.config['calendar_url'] calendars = nil open(URI.parse(url)) do |cal| calendars = Icalendar.parse(cal) end max_results = self.config['max_results'].to_i result.name = self.name # iCal doesn't provide a calendar name, so use the user's provided name calendars.first.events.each do |item| title = item.summary description = item.description location = item.location # behave a little differently depending on whether or not our iCal has a timezone defined # this behavior is somewhat lazy/sloppy in that we're assuming the existence of a defined timezone means # that times are local ('correct'), whereas the absence of a timezone means the data is # likely in UTC/Zulu time if calendars.first.has_timezone? item_start_time = item.dtstart.to_datetime unless item.dtstart.nil? item_end_time = item.dtend.to_datetime unless item.dtend.nil? else item_start_time = Time.zone.parse(item.dtstart.to_s).to_datetime unless item.dtstart.nil? item_end_time = Time.zone.parse(item.dtend.to_s).to_datetime unless item.dtend.nil? end item_end_time = nil if item_end_time == item_start_time # make sure the item's start date is within the specified range if item_start_time >= start_date && item_start_time < end_date result.add_item(title, description, location, item_start_time, item_end_time) end end result.items.sort! { |a, b| a.start_time <=> b.start_time } result.items = result.items[0..(max_results -1)] rescue => e result. = e. end else result. = "unsupported calendar source #{calendar_source}" end return result end |
#item_to_html(item, day_format, time_format) ⇒ Object
178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'app/models/calendar.rb', line 178 def item_to_html(item, day_format, time_format) start_time = item.start_time.strftime(time_format) end_time = item.end_time.strftime(time_format) unless item.end_time.nil? html = [] html << "<h1>#{item.name}</h1>" html << "<h2>#{item.start_time.strftime(day_format)}</h2>" html << (end_time.nil? || start_time == end_time ? "<div class=\"cal-time\">#{start_time}</div>" : "<div class=\"cal-time\">#{start_time} - #{end_time}</div>") html << "<div class=\"cal-location\">#{item.location}</div>" html << "<p>#{item.description}</p>" return html.join("") end |
#items_to_html(items, day_format, time_format) ⇒ Object
display date (only when it changes) / times with title…
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'app/models/calendar.rb', line 192 def items_to_html(items, day_format, time_format) html = [] last_date = nil items.each do |item| # see if we need a date header if last_date != item.start_time.to_date if last_date.nil? # dont need to close list else html << "</dl>" end html << "<h2>#{item.start_time.strftime(day_format)}</h2>" html << "<dl>" end # todo: end time should include date if different start_time = item.start_time.strftime(time_format) end_time = item.end_time.strftime(time_format) unless item.end_time.nil? html << (end_time.nil? || start_time == end_time ? "<dt>#{start_time}</dt>" : "<dt>#{start_time} - #{end_time}</dt>") # if the item we're evaluating isn't a DateTime, it's a full-day event html << (item.start_time.is_a?(DateTime) ? "" : "<dt>Time N/A</dt>") html << "<dd>#{item.name}</dd>" last_date = item.start_time.to_date end html << "</dl>" if !last_date.nil? return html.join("") end |
#set_defaults ⇒ Object
47 48 49 50 51 52 53 |
# File 'app/models/calendar.rb', line 47 def set_defaults self.config['calendar_source'] ||= 'ical' self.config['day_format'] ||= '%A %b %e' self.config['time_format'] ||= '%l:%M %P' self.config['max_results'] ||= 10 self.config['days_ahead'] ||= 7 end |
#validate_config ⇒ Object
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'app/models/calendar.rb', line 238 def validate_config # if self.config['api_key'].blank? # errors.add(:config_api_key, "can't be blank") # end prerequisites_met = true if self.config['calendar_id'].blank? && self.config['calendar_source'] == "google" errors.add(:config_calendar_id, "can't be blank") prerequisites_met = false end if self.config['calendar_url'].blank? && self.config['calendar_source'] != "google" errors.add(:config_calendar_url, "can't be blank") prerequisites_met = false end if self.config['max_results'].blank? errors.add(:config_max_results, "can't be blank") end # if self.config['days_ahead'].blank? && self.config['end_date'].blank? # errors.add(:config_days_ahead, "days ahead or end_date must be specified") # end if !self.config['start_date'].blank? && !self.config['end_date'].blank? start_date = self.config['start_date'].to_date end_date = self.config['end_date'].to_date if start_date > end_date errors.add(:config_start_date, "must precede end date") end end if !CALENDAR_SOURCES.values.include?(self.config['calendar_source']) errors.add(:config_calendar_source, "must be #{CALENDAR_SOURCES.keys.join(' or ')}") end if !DISPLAY_FORMATS.values.include?(self.config['output_format']) errors.add(:config_output_format, "must be #{DISPLAY_FORMATS.keys.join(' or ')}") end # todo: validate strftime components in day_format and time_format? begin validate_request #if !self.config['api_key'].blank? rescue => e errors.add(:base, "Could not fetch calendar - #{e.}") end if prerequisites_met end |
#validate_request ⇒ Object
make sure the request is valid by fetching a result back
281 282 283 284 285 286 287 |
# File 'app/models/calendar.rb', line 281 def validate_request result = fetch_calendar if result.error? raise result. end end |