calendar-assistant 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +280 -202
- data/Rakefile +9 -1
- data/lib/calendar_assistant.rb +35 -187
- data/lib/calendar_assistant/authorizer.rb +0 -3
- data/lib/calendar_assistant/calendar_assistant.rb +122 -0
- data/lib/calendar_assistant/cli.rb +84 -54
- data/lib/calendar_assistant/cli_helpers.rb +101 -38
- data/lib/calendar_assistant/config.rb +71 -12
- data/lib/calendar_assistant/date_helpers.rb +16 -0
- data/lib/calendar_assistant/event.rb +124 -14
- data/lib/calendar_assistant/event_repository.rb +30 -19
- data/lib/calendar_assistant/event_repository_factory.rb +7 -0
- data/lib/calendar_assistant/event_set.rb +124 -0
- data/lib/calendar_assistant/extensions/{event_date_time_extensions.rb → google_apis_extensions.rb} +5 -6
- data/lib/calendar_assistant/local_service.rb +82 -0
- data/lib/calendar_assistant/scheduler.rb +45 -0
- data/lib/calendar_assistant/string_helpers.rb +0 -2
- data/lib/calendar_assistant/version.rb +1 -1
- metadata +9 -4
- data/lib/calendar_assistant/extensions/event_extensions.rb +0 -71
@@ -20,28 +20,30 @@ class CalendarAssistant
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.now
|
23
|
-
CalendarAssistant::Event.new(
|
24
|
-
|
25
|
-
|
23
|
+
CalendarAssistant::Event.new(
|
24
|
+
Google::Apis::CalendarV3::Event.new(start: Google::Apis::CalendarV3::EventDateTime.new(date_time: Time.now),
|
25
|
+
end: Google::Apis::CalendarV3::EventDateTime.new(date_time: Time.now),
|
26
|
+
summary: Rainbow(" now ").inverse.faint)
|
27
|
+
)
|
26
28
|
end
|
27
29
|
|
28
30
|
def self.find_av_uri ca, timespec
|
29
31
|
time = Chronic.parse timespec
|
30
32
|
range = time..(time+5.minutes)
|
31
|
-
|
33
|
+
event_set = ca.find_events range
|
32
34
|
|
33
|
-
[
|
34
|
-
|
35
|
-
|
35
|
+
[CalendarAssistant::Event::Response::ACCEPTED,
|
36
|
+
CalendarAssistant::Event::Response::TENTATIVE,
|
37
|
+
CalendarAssistant::Event::Response::NEEDS_ACTION,
|
36
38
|
].each do |response|
|
37
|
-
events.reverse.select do |event|
|
39
|
+
event_set.events.reverse.select do |event|
|
38
40
|
event.response_status == response
|
39
41
|
end.each do |event|
|
40
|
-
return [event, event.av_uri] if event.av_uri
|
42
|
+
return [event_set.new(event), event.av_uri] if event.av_uri
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
44
|
-
nil
|
46
|
+
event_set.new(nil)
|
45
47
|
end
|
46
48
|
|
47
49
|
class Out
|
@@ -80,78 +82,139 @@ class CalendarAssistant
|
|
80
82
|
return false if event.start_date != Date.today
|
81
83
|
|
82
84
|
if event.start_time > Time.now
|
83
|
-
puts
|
85
|
+
puts event_description(CLIHelpers.now)
|
84
86
|
return true
|
85
87
|
end
|
86
88
|
|
87
89
|
false
|
88
90
|
end
|
89
91
|
|
90
|
-
def print_events ca,
|
91
|
-
unless
|
92
|
-
|
93
|
-
|
92
|
+
def print_events ca, event_set, omit_title: false
|
93
|
+
unless omit_title
|
94
|
+
er = event_set.event_repository
|
95
|
+
puts Rainbow("#{er.calendar.id} (all times in #{er.calendar.time_zone})\n").italic
|
94
96
|
end
|
95
97
|
|
96
|
-
if
|
97
|
-
events.each do |key, value|
|
98
|
+
if event_set.is_a?(EventSet::Hash)
|
99
|
+
event_set.events.each do |key, value|
|
98
100
|
puts Rainbow(key.to_s.capitalize + ":").bold.italic
|
99
|
-
print_events ca, value,
|
101
|
+
print_events ca, event_set.new(value), omit_title: true
|
100
102
|
end
|
101
103
|
return
|
102
104
|
end
|
103
105
|
|
104
|
-
events = Array(events)
|
106
|
+
events = Array(event_set.events)
|
105
107
|
if events.empty?
|
106
108
|
puts "No events in this time range."
|
107
109
|
return
|
108
110
|
end
|
109
111
|
|
110
112
|
display_events = events.select do |event|
|
111
|
-
!
|
113
|
+
! ca.config.setting(CalendarAssistant::Config::Keys::Options::COMMITMENTS) || event.commitment?
|
112
114
|
end
|
113
115
|
|
114
116
|
printed_now = false
|
115
117
|
display_events.each do |event|
|
116
118
|
printed_now = print_now! ca, event, printed_now
|
117
|
-
puts
|
118
|
-
pp event if
|
119
|
+
puts event_description(event)
|
120
|
+
pp event if ca.config.debug?
|
119
121
|
end
|
120
122
|
|
121
123
|
puts
|
122
124
|
end
|
123
125
|
|
124
|
-
def print_available_blocks ca,
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
126
|
+
def print_available_blocks ca, event_set, omit_title: false
|
127
|
+
ers = ca.config.attendees.map { |calendar_id| ca.event_repository calendar_id }
|
128
|
+
time_zones = ers.map { |er| er.calendar.time_zone }.uniq
|
129
|
+
|
130
|
+
unless omit_title
|
131
|
+
puts Rainbow(ers.map { |er| er.calendar.id }.join(", ")).italic
|
132
|
+
puts Rainbow(sprintf("- looking for blocks at least %s long",
|
133
|
+
ChronicDuration.output(
|
134
|
+
ChronicDuration.parse(
|
135
|
+
ca.config.setting(Config::Keys::Settings::MEETING_LENGTH))))).italic
|
136
|
+
time_zones.each do |time_zone|
|
137
|
+
puts Rainbow(sprintf("- between %s and %s in %s",
|
138
|
+
ca.config.setting(Config::Keys::Settings::START_OF_DAY),
|
139
|
+
ca.config.setting(Config::Keys::Settings::END_OF_DAY),
|
140
|
+
time_zone,
|
141
|
+
)).italic
|
142
|
+
end
|
143
|
+
puts
|
132
144
|
end
|
133
145
|
|
134
|
-
if
|
135
|
-
events.each do |key, value|
|
146
|
+
if event_set.is_a?(EventSet::Hash)
|
147
|
+
event_set.events.each do |key, value|
|
136
148
|
puts(sprintf(Rainbow("Availability on %s:\n").bold,
|
137
149
|
key.strftime("%A, %B %-d")))
|
138
|
-
print_available_blocks ca, value,
|
150
|
+
print_available_blocks ca, event_set.new(value), omit_title: true
|
139
151
|
puts
|
140
152
|
end
|
141
153
|
return
|
142
154
|
end
|
143
155
|
|
144
|
-
events = Array(events)
|
156
|
+
events = Array(event_set.events)
|
145
157
|
if events.empty?
|
146
158
|
puts " (No available blocks in this time range.)"
|
147
159
|
return
|
148
160
|
end
|
149
161
|
|
150
162
|
events.each do |event|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
163
|
+
line = []
|
164
|
+
time_zones.each do |time_zone|
|
165
|
+
line << sprintf("%s - %s",
|
166
|
+
event.start_time.in_time_zone(time_zone).strftime("%l:%M%P"),
|
167
|
+
event.end_time.in_time_zone(time_zone).strftime("%l:%M%P %Z"))
|
168
|
+
end
|
169
|
+
line.uniq!
|
170
|
+
puts " • " + line.join(" / ") + Rainbow(" (" + event.duration + ")").italic
|
171
|
+
pp event if ca.config.debug?
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def event_description event
|
176
|
+
s = sprintf("%-25.25s", event_date_description(event))
|
177
|
+
|
178
|
+
date_ansi_codes = []
|
179
|
+
date_ansi_codes << :bright if event.current?
|
180
|
+
date_ansi_codes << :faint if event.past?
|
181
|
+
s = date_ansi_codes.inject(Rainbow(s)) { |text, ansi| text.send ansi }
|
182
|
+
|
183
|
+
s += Rainbow(sprintf(" | %s", event.view_summary)).bold
|
184
|
+
|
185
|
+
attributes = []
|
186
|
+
unless event.private?
|
187
|
+
attributes << "recurring" if event.recurring?
|
188
|
+
attributes << "not-busy" unless event.busy?
|
189
|
+
attributes << "self" if event.self? && !event.private?
|
190
|
+
attributes << "1:1" if event.one_on_one?
|
191
|
+
attributes << "awaiting" if event.awaiting?
|
192
|
+
end
|
193
|
+
|
194
|
+
attributes << event.visibility if event.explicit_visibility?
|
195
|
+
|
196
|
+
s += Rainbow(sprintf(" (%s)", attributes.to_a.sort.join(", "))).italic unless attributes.empty?
|
197
|
+
|
198
|
+
s = Rainbow(Rainbow.uncolor(s)).faint.strike if event.declined?
|
199
|
+
|
200
|
+
s
|
201
|
+
end
|
202
|
+
|
203
|
+
def event_date_description event
|
204
|
+
if event.all_day?
|
205
|
+
start_date = event.start_date
|
206
|
+
end_date = event.end_date
|
207
|
+
if (end_date - start_date) <= 1
|
208
|
+
event.start.to_s
|
209
|
+
else
|
210
|
+
sprintf("%s - %s", start_date, end_date - 1.day)
|
211
|
+
end
|
212
|
+
else
|
213
|
+
if event.start_date == event.end_date
|
214
|
+
sprintf("%s - %s", event.start.date_time.strftime("%Y-%m-%d %H:%M"), event.end.date_time.strftime("%H:%M"))
|
215
|
+
else
|
216
|
+
sprintf("%s - %s", event.start.date_time.strftime("%Y-%m-%d %H:%M"), event.end.date_time.strftime("%Y-%m-%d %H:%M"))
|
217
|
+
end
|
155
218
|
end
|
156
219
|
end
|
157
220
|
end
|
@@ -1,30 +1,48 @@
|
|
1
|
-
require "toml"
|
2
|
-
|
3
1
|
class CalendarAssistant
|
4
2
|
class Config
|
3
|
+
autoload :TokenStore, "calendar_assistant/config/token_store"
|
4
|
+
|
5
5
|
class TomlParseFailure < CalendarAssistant::BaseException ; end
|
6
6
|
class NoConfigFileToPersist < CalendarAssistant::BaseException ; end
|
7
7
|
class NoTokensAuthorized < CalendarAssistant::BaseException ; end
|
8
8
|
class AccessingHashAsScalar < CalendarAssistant::BaseException ; end
|
9
9
|
|
10
10
|
CONFIG_FILE_PATH = File.join ENV["HOME"], ".calendar-assistant"
|
11
|
+
DEFAULT_CALENDAR_ID = "primary"
|
11
12
|
|
12
13
|
module Keys
|
13
14
|
TOKENS = "tokens"
|
14
15
|
SETTINGS = "settings"
|
15
16
|
|
17
|
+
#
|
18
|
+
# Settings are values that have a value in DEFAULT_SETTINGS below,
|
19
|
+
# and which can be overridden by entries in the user config file
|
20
|
+
#
|
16
21
|
module Settings
|
17
|
-
PROFILE = "profile"
|
18
|
-
MEETING_LENGTH = "meeting-length"
|
19
|
-
START_OF_DAY = "start-of-day"
|
20
|
-
END_OF_DAY = "end-of-day"
|
22
|
+
PROFILE = "profile" # string
|
23
|
+
MEETING_LENGTH = "meeting-length" # ChronicDuration
|
24
|
+
START_OF_DAY = "start-of-day" # BusinessTime
|
25
|
+
END_OF_DAY = "end-of-day" # BusinessTime
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Options are ephemeral command-line flag settings which _may_
|
30
|
+
# have a value in DEFAULT_SETTINGS below
|
31
|
+
#
|
32
|
+
module Options
|
33
|
+
COMMITMENTS = "commitments" # bool
|
34
|
+
JOIN = "join" # bool
|
35
|
+
ATTENDEES = "attendees" # array of calendar ids (comma-delimited)
|
36
|
+
LOCAL_STORE = "local-store" # filename
|
37
|
+
DEBUG = "debug" # bool
|
21
38
|
end
|
22
39
|
end
|
23
40
|
|
24
41
|
DEFAULT_SETTINGS = {
|
25
|
-
Keys::Settings::MEETING_LENGTH => "30m",
|
26
|
-
Keys::Settings::START_OF_DAY => "9am",
|
27
|
-
Keys::Settings::END_OF_DAY => "6pm",
|
42
|
+
Keys::Settings::MEETING_LENGTH => "30m", # ChronicDuration
|
43
|
+
Keys::Settings::START_OF_DAY => "9am", # BusinessTime
|
44
|
+
Keys::Settings::END_OF_DAY => "6pm", # BusinessTime
|
45
|
+
Keys::Options::ATTENDEES => [DEFAULT_CALENDAR_ID], # array of calendar ids
|
28
46
|
}
|
29
47
|
|
30
48
|
attr_reader :config_file_path, :user_config, :options, :defaults
|
@@ -58,9 +76,23 @@ class CalendarAssistant
|
|
58
76
|
@options = options
|
59
77
|
end
|
60
78
|
|
79
|
+
def in_env &block
|
80
|
+
# this is totally not thread-safe
|
81
|
+
orig_b_o_d = BusinessTime::Config.beginning_of_workday
|
82
|
+
orig_e_o_d = BusinessTime::Config.end_of_workday
|
83
|
+
begin
|
84
|
+
BusinessTime::Config.beginning_of_workday = setting(Config::Keys::Settings::START_OF_DAY)
|
85
|
+
BusinessTime::Config.end_of_workday = setting(Config::Keys::Settings::END_OF_DAY)
|
86
|
+
yield
|
87
|
+
ensure
|
88
|
+
BusinessTime::Config.beginning_of_workday = orig_b_o_d
|
89
|
+
BusinessTime::Config.end_of_workday = orig_e_o_d
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
61
93
|
def profile_name
|
62
94
|
# CLI option takes precedence
|
63
|
-
return options[
|
95
|
+
return options[Keys::Settings::PROFILE] if options[Keys::Settings::PROFILE]
|
64
96
|
|
65
97
|
# then a configured preference takes precedence
|
66
98
|
default = get([Keys::SETTINGS, Keys::Settings::PROFILE])
|
@@ -91,12 +123,26 @@ class CalendarAssistant
|
|
91
123
|
Config.set_in_hash user_config, keypath, value
|
92
124
|
end
|
93
125
|
|
126
|
+
#
|
127
|
+
# note that, despite the name, this method returns both options
|
128
|
+
# and settings
|
129
|
+
#
|
94
130
|
def setting setting_name
|
95
131
|
Config.find_in_hash(options, setting_name) ||
|
96
132
|
Config.find_in_hash(user_config, [Keys::SETTINGS, setting_name]) ||
|
97
133
|
Config.find_in_hash(defaults, setting_name)
|
98
134
|
end
|
99
135
|
|
136
|
+
def settings
|
137
|
+
setting_names = CalendarAssistant::Config::Keys::Settings.constants.map do |k|
|
138
|
+
CalendarAssistant::Config::Keys::Settings.const_get k
|
139
|
+
end
|
140
|
+
setting_names.inject({}) do |settings, key|
|
141
|
+
settings[key] = setting key
|
142
|
+
settings
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
100
146
|
def tokens
|
101
147
|
Config.find_in_hash(user_config, Keys::TOKENS) ||
|
102
148
|
Config.set_in_hash(user_config, Keys::TOKENS, {})
|
@@ -118,6 +164,21 @@ class CalendarAssistant
|
|
118
164
|
end
|
119
165
|
end
|
120
166
|
|
167
|
+
#
|
168
|
+
# helper method for Keys::Options::ATTENDEES
|
169
|
+
#
|
170
|
+
def attendees
|
171
|
+
a = setting(Keys::Options::ATTENDEES)
|
172
|
+
if a.is_a?(String)
|
173
|
+
a = a.split(",")
|
174
|
+
end
|
175
|
+
a
|
176
|
+
end
|
177
|
+
|
178
|
+
def debug?
|
179
|
+
setting(Keys::Options::DEBUG)
|
180
|
+
end
|
181
|
+
|
121
182
|
private
|
122
183
|
|
123
184
|
def self.find_in_hash hash, keypath
|
@@ -150,5 +211,3 @@ class CalendarAssistant
|
|
150
211
|
end
|
151
212
|
end
|
152
213
|
end
|
153
|
-
|
154
|
-
require "calendar_assistant/config/token_store"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
module DateHelpers
|
3
|
+
def self.cast_dates attributes
|
4
|
+
attributes.each_with_object({}) do |(key, value), object|
|
5
|
+
if value.is_a?(Time) || value.is_a?(DateTime)
|
6
|
+
object[key] = Google::Apis::CalendarV3::EventDateTime.new(date_time: value)
|
7
|
+
elsif value.is_a?(Date)
|
8
|
+
object[key] = Google::Apis::CalendarV3::EventDateTime.new(date: value.to_s)
|
9
|
+
else
|
10
|
+
object[key] = value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -1,10 +1,42 @@
|
|
1
1
|
class CalendarAssistant
|
2
2
|
class Event < SimpleDelegator
|
3
|
+
#
|
4
|
+
# constants describing enumerated attribute values
|
5
|
+
# see https://developers.google.com/calendar/v3/reference/events
|
6
|
+
#
|
7
|
+
module RealResponse
|
8
|
+
DECLINED = "declined"
|
9
|
+
ACCEPTED = "accepted"
|
10
|
+
NEEDS_ACTION = "needsAction"
|
11
|
+
TENTATIVE = "tentative"
|
12
|
+
end
|
13
|
+
|
14
|
+
module Response
|
15
|
+
include RealResponse
|
16
|
+
SELF = "self" # not part of Google's API, but useful to represent meetings-for-myself
|
17
|
+
end
|
3
18
|
|
19
|
+
module Transparency
|
20
|
+
TRANSPARENT = "transparent"
|
21
|
+
OPAQUE = "opaque"
|
22
|
+
end
|
23
|
+
|
24
|
+
module Visibility
|
25
|
+
DEFAULT = "default"
|
26
|
+
PUBLIC = "public"
|
27
|
+
PRIVATE = "private"
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# constants describing behavior
|
32
|
+
#
|
4
33
|
LOCATION_EVENT_REGEX = /^#{CalendarAssistant::EMOJI_WORLDMAP}/
|
5
34
|
|
35
|
+
#
|
36
|
+
# methods
|
37
|
+
#
|
6
38
|
def update **args
|
7
|
-
|
39
|
+
update!(**args)
|
8
40
|
self
|
9
41
|
end
|
10
42
|
|
@@ -13,14 +45,14 @@ class CalendarAssistant
|
|
13
45
|
end
|
14
46
|
|
15
47
|
def all_day?
|
16
|
-
|
48
|
+
start.try(:date) || self.end.try(:date)
|
17
49
|
end
|
18
50
|
|
19
51
|
def past?
|
20
52
|
if all_day?
|
21
|
-
Date.today >=
|
53
|
+
Date.today >= end_date
|
22
54
|
else
|
23
|
-
Time.now >=
|
55
|
+
Time.now >= end_time
|
24
56
|
end
|
25
57
|
end
|
26
58
|
|
@@ -30,18 +62,26 @@ class CalendarAssistant
|
|
30
62
|
|
31
63
|
def future?
|
32
64
|
if all_day?
|
33
|
-
|
65
|
+
start_date > Date.today
|
34
66
|
else
|
35
|
-
|
67
|
+
start_time > Time.now
|
36
68
|
end
|
37
69
|
end
|
38
70
|
|
39
71
|
def accepted?
|
40
|
-
response_status ==
|
72
|
+
response_status == CalendarAssistant::Event::Response::ACCEPTED
|
41
73
|
end
|
42
74
|
|
43
75
|
def declined?
|
44
|
-
response_status ==
|
76
|
+
response_status == CalendarAssistant::Event::Response::DECLINED
|
77
|
+
end
|
78
|
+
|
79
|
+
def awaiting?
|
80
|
+
response_status == CalendarAssistant::Event::Response::NEEDS_ACTION
|
81
|
+
end
|
82
|
+
|
83
|
+
def self?
|
84
|
+
response_status == CalendarAssistant::Event::Response::SELF
|
45
85
|
end
|
46
86
|
|
47
87
|
def one_on_one?
|
@@ -52,7 +92,7 @@ class CalendarAssistant
|
|
52
92
|
end
|
53
93
|
|
54
94
|
def busy?
|
55
|
-
transparency !=
|
95
|
+
transparency != CalendarAssistant::Event::Transparency::TRANSPARENT
|
56
96
|
end
|
57
97
|
|
58
98
|
def commitment?
|
@@ -62,20 +102,24 @@ class CalendarAssistant
|
|
62
102
|
end
|
63
103
|
|
64
104
|
def private?
|
65
|
-
visibility ==
|
105
|
+
visibility == CalendarAssistant::Event::Visibility::PRIVATE
|
66
106
|
end
|
67
107
|
|
68
108
|
def public?
|
69
|
-
visibility ==
|
109
|
+
visibility == CalendarAssistant::Event::Visibility::PUBLIC
|
70
110
|
end
|
71
111
|
|
72
112
|
def explicit_visibility?
|
73
113
|
private? || public?
|
74
114
|
end
|
75
115
|
|
116
|
+
def recurring?
|
117
|
+
!!recurring_event_id
|
118
|
+
end
|
119
|
+
|
76
120
|
def start_time
|
77
121
|
if all_day?
|
78
|
-
|
122
|
+
start_date.beginning_of_day
|
79
123
|
else
|
80
124
|
start.date_time
|
81
125
|
end
|
@@ -85,7 +129,23 @@ class CalendarAssistant
|
|
85
129
|
if all_day?
|
86
130
|
start.to_date
|
87
131
|
else
|
88
|
-
|
132
|
+
start_time.to_date
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def end_time
|
137
|
+
if all_day?
|
138
|
+
end_date.beginning_of_day
|
139
|
+
else
|
140
|
+
self.end.date_time
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def end_date
|
145
|
+
if all_day?
|
146
|
+
self.end.to_date
|
147
|
+
else
|
148
|
+
end_time.to_date
|
89
149
|
end
|
90
150
|
end
|
91
151
|
|
@@ -94,5 +154,55 @@ class CalendarAssistant
|
|
94
154
|
return "(no title)" if summary.nil? || summary.blank?
|
95
155
|
summary
|
96
156
|
end
|
157
|
+
|
158
|
+
def duration
|
159
|
+
if all_day?
|
160
|
+
days = (end_date - start_date).to_i
|
161
|
+
return "#{days}d"
|
162
|
+
end
|
163
|
+
|
164
|
+
p = ActiveSupport::Duration.build(end_time - start_time).parts
|
165
|
+
s = []
|
166
|
+
s << "#{p[:hours]}h" if p.has_key?(:hours)
|
167
|
+
s << "#{p[:minutes]}m" if p.has_key?(:minutes)
|
168
|
+
s.join(" ")
|
169
|
+
end
|
170
|
+
|
171
|
+
def human_attendees
|
172
|
+
return nil if attendees.nil?
|
173
|
+
attendees.select { |a| ! a.resource }
|
174
|
+
end
|
175
|
+
|
176
|
+
def attendee id
|
177
|
+
return nil if attendees.nil?
|
178
|
+
attendees.find do |attendee|
|
179
|
+
attendee.email == id
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def response_status
|
184
|
+
return CalendarAssistant::Event::Response::SELF if attendees.nil?
|
185
|
+
attendees.each do |attendee|
|
186
|
+
return attendee.response_status if attendee.self
|
187
|
+
end
|
188
|
+
nil
|
189
|
+
end
|
190
|
+
|
191
|
+
def av_uri
|
192
|
+
@av_uri ||= begin
|
193
|
+
description_link = CalendarAssistant::StringHelpers.find_uri_for_domain(description, "zoom.us")
|
194
|
+
return description_link if description_link
|
195
|
+
|
196
|
+
location_link = CalendarAssistant::StringHelpers.find_uri_for_domain(location, "zoom.us")
|
197
|
+
return location_link if location_link
|
198
|
+
|
199
|
+
return hangout_link if hangout_link
|
200
|
+
nil
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def contains? time
|
205
|
+
start_time <= time && time < end_time
|
206
|
+
end
|
97
207
|
end
|
98
|
-
end
|
208
|
+
end
|