calendar-assistant 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +8 -0
- data/README.md +41 -61
- data/lib/calendar_assistant.rb +16 -26
- data/lib/calendar_assistant/authorizer.rb +3 -2
- data/lib/calendar_assistant/cli.rb +70 -29
- data/lib/calendar_assistant/cli_helpers.rb +18 -2
- data/lib/calendar_assistant/config.rb +1 -0
- data/lib/calendar_assistant/event.rb +98 -0
- data/lib/calendar_assistant/event_repository.rb +50 -0
- data/lib/calendar_assistant/extensions/event_date_time_extensions.rb +31 -0
- data/lib/calendar_assistant/extensions/event_extensions.rb +71 -0
- data/lib/calendar_assistant/{rainbow_extensions.rb → extensions/rainbow_extensions.rb} +0 -0
- data/lib/calendar_assistant/string_helpers.rb +1 -0
- data/lib/calendar_assistant/version.rb +1 -1
- metadata +25 -34
- data/lib/calendar_assistant/event_extensions.rb +0 -173
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57a695007c637201348e3d625d20ea076b33a08e5d00cf1a0c8dc2d7e09c68ec
|
4
|
+
data.tar.gz: 57a2575e0a149d1a9480bf7d3990a412cc8f23dc8409a3ccf33a38f9c044e81f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5340ebdb6a3dd34bf39315debc91b7f5c1ba05a4915e5991d43aeb7046c949f39b7fb9a7391c8f20e76412f56e1b5de219816f093d2d8ed470d06d9f2304d3a9
|
7
|
+
data.tar.gz: c0322c182c45fe87d94199219a434db96a9f8b13d702e2b2832b5a846914afb98c29cb669081b6ae4324229412102f48196ce6dde969f331941bd188d4897007
|
data/Gemfile
CHANGED
@@ -1,3 +1,11 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
|
3
3
|
gemspec
|
4
|
+
|
5
|
+
group :optional do
|
6
|
+
# These gems are not required for a functioning test suite, so not listing them in the gemspec.
|
7
|
+
# I personally find them useful in the developer role, though.
|
8
|
+
gem "autotest"
|
9
|
+
gem "rspec-autotest"
|
10
|
+
gem "test_notifier"
|
11
|
+
end
|
data/README.md
CHANGED
@@ -66,8 +66,6 @@ Options:
|
|
66
66
|
-h, -?, [--help], [--no-help]
|
67
67
|
-p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
|
68
68
|
[--debug], [--no-debug] # how dare you suggest there are bugs
|
69
|
-
|
70
|
-
|
71
69
|
</pre>
|
72
70
|
|
73
71
|
|
@@ -86,7 +84,6 @@ Options:
|
|
86
84
|
[--debug], [--no-debug] # how dare you suggest there are bugs
|
87
85
|
|
88
86
|
Dump your configuration parameters (merge of defaults and overrides from /home/flavorjones/.calendar-assistant)
|
89
|
-
|
90
87
|
</pre>
|
91
88
|
|
92
89
|
The output is TOML, which is suitable for dumping into `~/.calendar-assistant` and editing.
|
@@ -99,7 +96,6 @@ end-of-day = "6pm"
|
|
99
96
|
meeting-length = "30m"
|
100
97
|
profile = "work"
|
101
98
|
start-of-day = "9am"
|
102
|
-
|
103
99
|
</pre>
|
104
100
|
|
105
101
|
|
@@ -116,7 +112,7 @@ Options:
|
|
116
112
|
[--debug], [--no-debug] # how dare you suggest there are bugs
|
117
113
|
|
118
114
|
Description:
|
119
|
-
Create and authorize a named profile (e.g., "work", "home", "
|
115
|
+
Create and authorize a named profile (e.g., "work", "home", "me@example.com") to access your calendar.
|
120
116
|
|
121
117
|
When setting up a profile, you'll be asked to visit a URL to authenticate, grant authorization, and generate and persist an access token.
|
122
118
|
|
@@ -128,7 +124,6 @@ Description:
|
|
128
124
|
1. Turn on the Google API for your account
|
129
125
|
2. Create a new Google API Project
|
130
126
|
3. Download the configuration file for the Project, and name it as `credentials.json`
|
131
|
-
|
132
127
|
</pre>
|
133
128
|
|
134
129
|
This command will generate a URL which you should load in your browser while logged in as the Google account you wish to authorize. Generate a token, and paste the token back into `calendar-assistant`.
|
@@ -150,60 +145,55 @@ Options:
|
|
150
145
|
[--debug], [--no-debug] # how dare you suggest there are bugs
|
151
146
|
|
152
147
|
Show your events for a date or range of dates (default 'today')
|
153
|
-
|
154
148
|
</pre>
|
155
149
|
|
156
150
|
For example: display all events scheduled for tomorrow:
|
157
151
|
|
158
152
|
<pre>
|
159
153
|
<b>$</b> calendar-assistant show --profile=work 2018-10-01
|
160
|
-
<i>
|
154
|
+
<i>me@example.com (all times in America/New_York)
|
161
155
|
</i>
|
162
|
-
2018-10-01 <b> | 🗺
|
163
|
-
<strike>2018-10-01 03:30 - 05:00 |
|
164
|
-
<strike>2018-10-01 07:30 - 08:30 |
|
165
|
-
<strike>2018-10-01 07:30 - 08:30 |
|
166
|
-
2018-10-01 08:00 - 09:00<b> |
|
167
|
-
2018-10-01 09:00 - 10:30<b> |
|
168
|
-
2018-10-01 10:30 - 10:55<b> |
|
169
|
-
2018-10-01 11:00 - 11:30<b> |
|
170
|
-
2018-10-01 11:30 - 12:00<b> |
|
171
|
-
<strike>2018-10-01 11:50 - 12:00 |
|
172
|
-
2018-10-01 12:00 - 12:30<b> |
|
173
|
-
<strike>2018-10-01 12:15 - 12:30 |
|
174
|
-
<strike>2018-10-01 12:30 - 13:30 |
|
175
|
-
2018-10-01 12:30 - 13:30<b> |
|
176
|
-
2018-10-01 13:30 - 14:50<b> |
|
177
|
-
<strike>2018-10-01 13:30 - 14:30 |
|
178
|
-
2018-10-01 15:00 - 15:30<b> |
|
179
|
-
2018-10-01 16:00 - 17:00<b> |
|
180
|
-
2018-10-01 16:45 - 17:00<b> |
|
181
|
-
2018-10-01 17:00 - 17:30<b> |
|
182
|
-
2018-10-01 17:30 - 17:55<b> |
|
183
|
-
<strike>2018-10-01 18:00 - 20:30 |
|
184
|
-
<strike>2018-10-01 18:30 - 19:00 |
|
185
|
-
<strike>2018-10-01 19:00 - 19:30 |
|
186
|
-
|
187
|
-
|
156
|
+
2018-10-01 <b> | 🗺 Mines of Moria </b><i> (not-busy, self)</i>
|
157
|
+
<strike>2018-10-01 03:30 - 05:00 | Generate enterprise portals </strike>
|
158
|
+
<strike>2018-10-01 07:30 - 08:30 | Incentivize wireless functionalities </strike>
|
159
|
+
<strike>2018-10-01 07:30 - 08:30 | Brand end-to-end action-items </strike>
|
160
|
+
2018-10-01 08:00 - 09:00<b> | Grow web-enabled synergies </b><i> (recurring, self)</i>
|
161
|
+
2018-10-01 09:00 - 10:30<b> | Disintermediate integrated web services </b><i> (self)</i>
|
162
|
+
2018-10-01 10:30 - 10:55<b> | Architect sticky synergies </b><i> (1:1, recurring)</i>
|
163
|
+
2018-10-01 11:00 - 11:30<b> | Leverage out-of-the-box supply-chains </b><i> (recurring)</i>
|
164
|
+
2018-10-01 11:30 - 12:00<b> | Architect dot-com portals </b><i> (1:1, recurring)</i>
|
165
|
+
<strike>2018-10-01 11:50 - 12:00 | Grow out-of-the-box convergence </strike>
|
166
|
+
2018-10-01 12:00 - 12:30<b> | Morph leading-edge experiences </b><i> (self)</i>
|
167
|
+
<strike>2018-10-01 12:15 - 12:30 | Revolutionize innovative communities </strike>
|
168
|
+
<strike>2018-10-01 12:30 - 13:30 | Evolve mission-critical e-services </strike>
|
169
|
+
2018-10-01 12:30 - 13:30<b> | Embrace world-class e-tailers </b><i> (recurring)</i>
|
170
|
+
2018-10-01 13:30 - 14:50<b> | Deliver out-of-the-box infomediaries </b><i> (self)</i>
|
171
|
+
<strike>2018-10-01 13:30 - 14:30 | Transform user-centric e-tailers </strike>
|
172
|
+
2018-10-01 15:00 - 15:30<b> | Aggregate magnetic e-business </b><i> (1:1)</i>
|
173
|
+
2018-10-01 16:00 - 17:00<b> | Leverage turn-key e-tailers </b><i> (1:1, recurring)</i>
|
174
|
+
2018-10-01 16:45 - 17:00<b> | Monetize real-time interfaces </b><i> (recurring)</i>
|
175
|
+
2018-10-01 17:00 - 17:30<b> | Generate out-of-the-box technologies </b><i> (recurring)</i>
|
176
|
+
2018-10-01 17:30 - 17:55<b> | Strategize enterprise communities </b><i> (1:1, recurring)</i>
|
177
|
+
<strike>2018-10-01 18:00 - 20:30 | Redefine 24/365 infrastructures </strike>
|
178
|
+
<strike>2018-10-01 18:30 - 19:00 | Innovate vertical e-business </strike>
|
179
|
+
<strike>2018-10-01 19:00 - 19:30 | Repurpose seamless deliverables </strike>
|
188
180
|
</pre>
|
189
181
|
|
190
182
|
Display _only_ the commitments I have to other people using the `-c` option:
|
191
183
|
|
192
184
|
<pre>
|
193
185
|
<b>$</b> calendar-assistant show -c 2018-10-01
|
194
|
-
<i>
|
186
|
+
<i>me@example.com (all times in America/New_York)
|
195
187
|
</i>
|
196
|
-
2018-10-01 10:30 - 10:55<b> |
|
197
|
-
2018-10-01 11:00 - 11:30<b> |
|
198
|
-
2018-10-01 11:30 - 12:00<b> |
|
199
|
-
2018-10-01 12:30 - 13:30<b> |
|
200
|
-
2018-10-01 15:00 - 15:30<b> |
|
201
|
-
2018-10-01 16:00 - 17:00<b> |
|
202
|
-
2018-10-01 16:45 - 17:00<b> |
|
203
|
-
2018-10-01 17:00 - 17:30<b> |
|
204
|
-
2018-10-01 17:30 - 17:55<b> |
|
205
|
-
|
206
|
-
|
188
|
+
2018-10-01 10:30 - 10:55<b> | Exploit b2b synergies </b><i> (1:1, recurring)</i>
|
189
|
+
2018-10-01 11:00 - 11:30<b> | Reinvent customized systems </b><i> (recurring)</i>
|
190
|
+
2018-10-01 11:30 - 12:00<b> | Incentivize visionary users </b><i> (1:1, recurring)</i>
|
191
|
+
2018-10-01 12:30 - 13:30<b> | Engineer robust roi </b><i> (recurring)</i>
|
192
|
+
2018-10-01 15:00 - 15:30<b> | Exploit next-generation content </b><i> (1:1)</i>
|
193
|
+
2018-10-01 16:00 - 17:00<b> | Integrate sticky platforms </b><i> (1:1, recurring)</i>
|
194
|
+
2018-10-01 16:45 - 17:00<b> | Enhance intuitive portals </b><i> (recurring)</i>
|
195
|
+
2018-10-01 17:00 - 17:30<b> | Reinvent viral platforms </b><i> (recurring)</i>
|
196
|
+
2018-10-01 17:30 - 17:55<b> | Incubate magnetic interfaces </b><i> (1:1, recurring)</i>
|
207
197
|
</pre>
|
208
198
|
|
209
199
|
|
@@ -222,7 +212,6 @@ Options:
|
|
222
212
|
[--debug], [--no-debug] # how dare you suggest there are bugs
|
223
213
|
|
224
214
|
Open the URL for a video call attached to your meeting at time TIME (default 'now')
|
225
|
-
|
226
215
|
</pre>
|
227
216
|
|
228
217
|
Some examples:
|
@@ -258,7 +247,6 @@ Options:
|
|
258
247
|
[--debug], [--no-debug] # how dare you suggest there are bugs
|
259
248
|
|
260
249
|
Show your availability for a date or range of dates (default 'today')
|
261
|
-
|
262
250
|
</pre>
|
263
251
|
|
264
252
|
|
@@ -266,7 +254,7 @@ For example: show me my available time over a chunk of time:
|
|
266
254
|
|
267
255
|
<pre>
|
268
256
|
<b>$</b> calendar-assistant avail 2018-10-02..2018-10-04
|
269
|
-
<i>
|
257
|
+
<i>me@example.com
|
270
258
|
- all times in America/New_York
|
271
259
|
- looking for blocks at least 30 mins long
|
272
260
|
</i>
|
@@ -286,8 +274,6 @@ For example: show me my available time over a chunk of time:
|
|
286
274
|
<b>Availability on Thursday, October 4:
|
287
275
|
</b>
|
288
276
|
• 10:55am - 1:00pm
|
289
|
-
|
290
|
-
|
291
277
|
</pre>
|
292
278
|
|
293
279
|
|
@@ -295,7 +281,7 @@ You can also set start and end times for the search, which is useful when lookin
|
|
295
281
|
|
296
282
|
<pre>
|
297
283
|
<b>$</b> calendar-assistant avail 2018-10-02..2018-10-04 -s 12pm -e 7pm
|
298
|
-
<i>
|
284
|
+
<i>me@example.com
|
299
285
|
- all times in America/New_York
|
300
286
|
- looking for blocks at least 30 mins long
|
301
287
|
</i>
|
@@ -317,8 +303,6 @@ You can also set start and end times for the search, which is useful when lookin
|
|
317
303
|
</b>
|
318
304
|
• 10:55am - 1:00pm
|
319
305
|
• 6:00pm - 7:00pm
|
320
|
-
|
321
|
-
|
322
306
|
</pre>
|
323
307
|
|
324
308
|
|
@@ -337,7 +321,6 @@ Options:
|
|
337
321
|
[--debug], [--no-debug] # how dare you suggest there are bugs
|
338
322
|
|
339
323
|
Set your location to LOCATION for a date or range of dates (default 'today')
|
340
|
-
|
341
324
|
</pre>
|
342
325
|
|
343
326
|
**Note** that you can only be in one place at a time, so existing location events may be modified or deleted when new overlapping events are created.
|
@@ -380,19 +363,16 @@ Options:
|
|
380
363
|
[--debug], [--no-debug] # how dare you suggest there are bugs
|
381
364
|
|
382
365
|
Show your location for a date or range of dates (default 'today')
|
383
|
-
|
384
366
|
</pre>
|
385
367
|
|
386
368
|
For example:
|
387
369
|
|
388
370
|
<pre>
|
389
371
|
<b>$</b> calendar-assistant location "2018-09-24...2018-09-28"
|
390
|
-
<i>
|
372
|
+
<i>me@example.com (all times in America/New_York)
|
391
373
|
</i>
|
392
|
-
2018-09-24 - 2018-09-27 <b> | 🗺
|
393
|
-
2018-09-28 <b> | 🗺
|
394
|
-
|
395
|
-
|
374
|
+
2018-09-24 - 2018-09-27 <b> | 🗺 Grey Mountains </b><i> (not-busy, self)</i>
|
375
|
+
2018-09-28 <b> | 🗺 Dorwinion </b><i> (not-busy, self)</i>
|
396
376
|
</pre>
|
397
377
|
|
398
378
|
|
data/lib/calendar_assistant.rb
CHANGED
@@ -30,24 +30,15 @@ class CalendarAssistant
|
|
30
30
|
time_range.first.to_date..(time_range.last + 1.day).to_date
|
31
31
|
end
|
32
32
|
|
33
|
-
def initialize config=CalendarAssistant::Config.new
|
33
|
+
def initialize config=CalendarAssistant::Config.new, event_repository: nil
|
34
34
|
@config = config
|
35
35
|
@service = Authorizer.new(config.profile_name, config.token_store).service
|
36
36
|
@calendar = service.get_calendar DEFAULT_CALENDAR_ID
|
37
|
+
@event_repository = event_repository || EventRepository.new(@service, DEFAULT_CALENDAR_ID)
|
37
38
|
end
|
38
39
|
|
39
40
|
def find_events time_range
|
40
|
-
|
41
|
-
time_min: time_range.first.iso8601,
|
42
|
-
time_max: time_range.last.iso8601,
|
43
|
-
order_by: "startTime",
|
44
|
-
single_events: true,
|
45
|
-
max_results: 2000,
|
46
|
-
)
|
47
|
-
if events.nil? || events.items.nil?
|
48
|
-
return []
|
49
|
-
end
|
50
|
-
events.items
|
41
|
+
@event_repository.find(time_range)
|
51
42
|
end
|
52
43
|
|
53
44
|
def availability time_range
|
@@ -100,7 +91,7 @@ class CalendarAssistant
|
|
100
91
|
end
|
101
92
|
|
102
93
|
def find_location_events time_range
|
103
|
-
|
94
|
+
@event_repository.find(time_range).select { |e| e.location_event? }
|
104
95
|
end
|
105
96
|
|
106
97
|
def create_location_event time_range, location
|
@@ -113,24 +104,17 @@ class CalendarAssistant
|
|
113
104
|
deleted_events = []
|
114
105
|
modified_events = []
|
115
106
|
|
116
|
-
event = GCal::Event
|
117
|
-
end: GCal::EventDateTime.new(date: range.last.iso8601),
|
118
|
-
summary: "#{EMOJI_WORLDMAP} #{location}",
|
119
|
-
transparency: GCal::Event::Transparency::TRANSPARENT
|
120
|
-
|
121
|
-
event = service.insert_event DEFAULT_CALENDAR_ID, event
|
107
|
+
event = @event_repository.create(transparency: GCal::Event::Transparency::TRANSPARENT, start: range.first, end: range.last , summary: "#{EMOJI_WORLDMAP} #{location}")
|
122
108
|
|
123
109
|
existing_events.each do |existing_event|
|
124
110
|
if existing_event.start.date >= event.start.date && existing_event.end.date <= event.end.date
|
125
|
-
|
111
|
+
@event_repository.delete existing_event
|
126
112
|
deleted_events << existing_event
|
127
113
|
elsif existing_event.start.date <= event.end.date && existing_event.end.date > event.end.date
|
128
|
-
|
129
|
-
service.update_event DEFAULT_CALENDAR_ID, existing_event.id, existing_event
|
114
|
+
@event_repository.update existing_event, start: range.last
|
130
115
|
modified_events << existing_event
|
131
116
|
elsif existing_event.start.date < event.start.date && existing_event.end.date >= event.start.date
|
132
|
-
|
133
|
-
service.update_event DEFAULT_CALENDAR_ID, existing_event.id, existing_event
|
117
|
+
@event_repository.update existing_event, end: range.first
|
134
118
|
modified_events << existing_event
|
135
119
|
end
|
136
120
|
end
|
@@ -158,6 +142,9 @@ class CalendarAssistant
|
|
158
142
|
attributes << "self" if event.human_attendees.nil? && event.visibility != "private"
|
159
143
|
attributes << "1:1" if event.one_on_one?
|
160
144
|
end
|
145
|
+
|
146
|
+
attributes << event.visibility if event.explicit_visibility?
|
147
|
+
|
161
148
|
s += Rainbow(sprintf(" (%s)", attributes.to_a.sort.join(", "))).italic unless attributes.empty?
|
162
149
|
|
163
150
|
s = Rainbow(Rainbow.uncolor(s)).faint.strike if event.declined?
|
@@ -198,5 +185,8 @@ require "calendar_assistant/config"
|
|
198
185
|
require "calendar_assistant/authorizer"
|
199
186
|
require "calendar_assistant/cli"
|
200
187
|
require "calendar_assistant/string_helpers"
|
201
|
-
require "calendar_assistant/
|
202
|
-
require "calendar_assistant/
|
188
|
+
require "calendar_assistant/extensions/event_date_time_extensions"
|
189
|
+
require "calendar_assistant/extensions/event_extensions"
|
190
|
+
require "calendar_assistant/event"
|
191
|
+
require "calendar_assistant/event_repository"
|
192
|
+
require "calendar_assistant/extensions/rainbow_extensions"
|
@@ -27,7 +27,7 @@ class CalendarAssistant
|
|
27
27
|
|
28
28
|
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'.freeze
|
29
29
|
APPLICATION_NAME = "Flavorjones Calendar Assistant".freeze
|
30
|
-
CREDENTIALS_PATH =
|
30
|
+
CREDENTIALS_PATH = File.join ENV["HOME"], ".calendar-assistant.client"
|
31
31
|
SCOPE = Google::Apis::CalendarV3::AUTH_CALENDAR
|
32
32
|
|
33
33
|
attr_reader :profile_name, :config_token_store
|
@@ -77,9 +77,10 @@ class CalendarAssistant
|
|
77
77
|
def authorizer
|
78
78
|
@authorizer ||= begin
|
79
79
|
if ! File.exists?(CREDENTIALS_PATH)
|
80
|
-
raise NoCredentials, "No credentials found. Please run `calendar-assistant help
|
80
|
+
raise NoCredentials, "No credentials found. Please run `calendar-assistant help setup` for instructions"
|
81
81
|
end
|
82
82
|
|
83
|
+
FileUtils.chmod 0600, CREDENTIALS_PATH
|
83
84
|
client_id = Google::Auth::ClientId.from_file(CREDENTIALS_PATH)
|
84
85
|
Google::Auth::UserAuthorizer.new(client_id, SCOPE, config_token_store)
|
85
86
|
end
|
@@ -7,23 +7,78 @@ require "calendar_assistant/cli_helpers"
|
|
7
7
|
|
8
8
|
class CalendarAssistant
|
9
9
|
class CLI < Thor
|
10
|
+
def self.supports_profile_option
|
11
|
+
option :profile,
|
12
|
+
type: :string,
|
13
|
+
desc: "the profile you'd like to use (if different from default)",
|
14
|
+
aliases: ["-p"]
|
15
|
+
end
|
16
|
+
|
10
17
|
default_config = CalendarAssistant::Config.new options: options # used in option descriptions
|
11
18
|
|
12
|
-
# it's unfortunate that thor does not support this usage of help args
|
13
19
|
class_option :help,
|
14
20
|
type: :boolean,
|
15
21
|
aliases: ["-h", "-?"]
|
16
|
-
|
17
|
-
# note that these options are passed straight through to CLIHelpers.print_events
|
18
|
-
class_option :profile,
|
19
|
-
type: :string,
|
20
|
-
desc: "the profile you'd like to use (if different from default)",
|
21
|
-
aliases: ["-p"]
|
22
22
|
class_option :debug,
|
23
23
|
type: :boolean,
|
24
24
|
desc: "how dare you suggest there are bugs"
|
25
25
|
|
26
26
|
|
27
|
+
desc "config",
|
28
|
+
"Dump your configuration parameters (merge of defaults and overrides from #{CalendarAssistant::Config::CONFIG_FILE_PATH})"
|
29
|
+
def config
|
30
|
+
return if handle_help_args
|
31
|
+
config = CalendarAssistant::Config.new
|
32
|
+
settings = {}
|
33
|
+
setting_names = CalendarAssistant::Config::Keys::Settings.constants.map { |k| CalendarAssistant::Config::Keys::Settings.const_get k }
|
34
|
+
setting_names.each do |key|
|
35
|
+
settings[key] = config.setting key
|
36
|
+
end
|
37
|
+
puts TOML::Generator.new({CalendarAssistant::Config::Keys::SETTINGS => settings}).body
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
desc "setup",
|
42
|
+
"Link your local calendar-assistant installation to a Google API Client"
|
43
|
+
long_desc <<~EOD
|
44
|
+
This command will walk you through setting up a Google Cloud
|
45
|
+
Project, enabling the Google Calendar API, and saving the
|
46
|
+
credentials necessary to access the API on behalf of users.
|
47
|
+
|
48
|
+
If you already have downloaded client credentials, you don't
|
49
|
+
need to run this command. Instead, rename the downloaded JSON
|
50
|
+
file to `#{CalendarAssistant::Authorizer::CREDENTIALS_PATH}`
|
51
|
+
EOD
|
52
|
+
def setup
|
53
|
+
out = CLIHelpers::Out.new
|
54
|
+
if File.exist? CalendarAssistant::Authorizer::CREDENTIALS_PATH
|
55
|
+
out.puts sprintf("Credentials already exist in %s",
|
56
|
+
CalendarAssistant::Authorizer::CREDENTIALS_PATH)
|
57
|
+
exit 0
|
58
|
+
end
|
59
|
+
|
60
|
+
out.launch "https://developers.google.com/calendar/quickstart/ruby"
|
61
|
+
sleep 1
|
62
|
+
out.puts <<~EOT
|
63
|
+
Please click on "ENABLE THE GOOGLE CALENDAR API" and either create a new project or select an existing project.
|
64
|
+
|
65
|
+
(If you create a new project, name it something like "yourname-calendar-assistant" so you remember why it exists.)
|
66
|
+
|
67
|
+
Then click "DOWNLOAD CLIENT CONFIGURATION" to download the credentials to local disk.
|
68
|
+
|
69
|
+
Finally, paste the contents of the downloaded file here (it should be a complete JSON object):
|
70
|
+
EOT
|
71
|
+
|
72
|
+
json = out.prompt "Paste JSON here"
|
73
|
+
File.open(CalendarAssistant::Authorizer::CREDENTIALS_PATH, "w") do |f|
|
74
|
+
f.write json
|
75
|
+
end
|
76
|
+
FileUtils.chmod 0600, CalendarAssistant::Authorizer::CREDENTIALS_PATH
|
77
|
+
|
78
|
+
out.puts "\nOK! Your next step is to run `calendar-assistant authorize`."
|
79
|
+
end
|
80
|
+
|
81
|
+
|
27
82
|
desc "authorize PROFILE_NAME",
|
28
83
|
"create (or validate) a profile named NAME with calendar access"
|
29
84
|
long_desc <<~EOD
|
@@ -34,15 +89,8 @@ class CalendarAssistant
|
|
34
89
|
authenticate, grant authorization, and generate and persist an
|
35
90
|
access token.
|
36
91
|
|
37
|
-
In order for this to work, you'll need to
|
38
|
-
|
39
|
-
|
40
|
-
> https://developers.google.com/calendar/quickstart/ruby
|
41
|
-
|
42
|
-
Namely, the prerequisites are:
|
43
|
-
\x5 1. Turn on the Google API for your account
|
44
|
-
\x5 2. Create a new Google API Project
|
45
|
-
\x5 3. Download the configuration file for the Project, and name it as `credentials.json`
|
92
|
+
In order for this to work, you'll need to have set up your API client
|
93
|
+
credentials. Run `calendar-assistant help setup` for instructions.
|
46
94
|
EOD
|
47
95
|
def authorize profile_name
|
48
96
|
return if handle_help_args
|
@@ -57,6 +105,7 @@ class CalendarAssistant
|
|
57
105
|
type: :boolean,
|
58
106
|
desc: "only show events that you've accepted with another person",
|
59
107
|
aliases: ["-c"]
|
108
|
+
supports_profile_option
|
60
109
|
def show datespec="today"
|
61
110
|
return if handle_help_args
|
62
111
|
config = CalendarAssistant::Config.new options: options
|
@@ -71,6 +120,7 @@ class CalendarAssistant
|
|
71
120
|
option :join,
|
72
121
|
type: :boolean, default: true,
|
73
122
|
desc: "launch a browser to join the video call URL"
|
123
|
+
supports_profile_option
|
74
124
|
def join timespec="now"
|
75
125
|
return if handle_help_args
|
76
126
|
config = CalendarAssistant::Config.new options: options
|
@@ -90,6 +140,7 @@ class CalendarAssistant
|
|
90
140
|
|
91
141
|
desc "location [DATE | DATERANGE]",
|
92
142
|
"Show your location for a date or range of dates (default 'today')"
|
143
|
+
supports_profile_option
|
93
144
|
def location datespec="today"
|
94
145
|
return if handle_help_args
|
95
146
|
config = CalendarAssistant::Config.new options: options
|
@@ -101,6 +152,7 @@ class CalendarAssistant
|
|
101
152
|
|
102
153
|
desc "location-set LOCATION [DATE | DATERANGE]",
|
103
154
|
"Set your location to LOCATION for a date or range of dates (default 'today')"
|
155
|
+
supports_profile_option
|
104
156
|
def location_set location, datespec="today"
|
105
157
|
return if handle_help_args
|
106
158
|
config = CalendarAssistant::Config.new options: options
|
@@ -109,6 +161,7 @@ class CalendarAssistant
|
|
109
161
|
CLIHelpers::Out.new.print_events ca, events, options
|
110
162
|
end
|
111
163
|
|
164
|
+
|
112
165
|
desc "availability [DATE | DATERANGE | TIMERANGE]",
|
113
166
|
"Show your availability for a date or range of dates (default 'today')"
|
114
167
|
option CalendarAssistant::Config::Keys::Settings::MEETING_LENGTH,
|
@@ -129,6 +182,7 @@ class CalendarAssistant
|
|
129
182
|
desc: sprintf("[default %s] find chunks of available time before TIME (which is a Chronic string like '9am' or '14:30')",
|
130
183
|
default_config.setting(CalendarAssistant::Config::Keys::Settings::END_OF_DAY)),
|
131
184
|
aliases: ["-e"]
|
185
|
+
supports_profile_option
|
132
186
|
def availability datespec="today"
|
133
187
|
return if handle_help_args
|
134
188
|
config = CalendarAssistant::Config.new options: options
|
@@ -137,19 +191,6 @@ class CalendarAssistant
|
|
137
191
|
CLIHelpers::Out.new.print_available_blocks ca, events, options
|
138
192
|
end
|
139
193
|
|
140
|
-
desc "config",
|
141
|
-
"Dump your configuration parameters (merge of defaults and overrides from #{CalendarAssistant::Config::CONFIG_FILE_PATH})"
|
142
|
-
def config
|
143
|
-
return if handle_help_args
|
144
|
-
config = CalendarAssistant::Config.new
|
145
|
-
settings = {}
|
146
|
-
setting_names = CalendarAssistant::Config::Keys::Settings.constants.map { |k| CalendarAssistant::Config::Keys::Settings.const_get k }
|
147
|
-
setting_names.each do |key|
|
148
|
-
settings[key] = config.setting key
|
149
|
-
end
|
150
|
-
puts TOML::Generator.new({CalendarAssistant::Config::Keys::SETTINGS => settings}).body
|
151
|
-
end
|
152
|
-
|
153
194
|
private
|
154
195
|
|
155
196
|
def handle_help_args
|
@@ -20,9 +20,9 @@ class CalendarAssistant
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.now
|
23
|
-
GCal::Event.new start: GCal::EventDateTime.new(date_time: Time.now),
|
23
|
+
CalendarAssistant::Event.new(GCal::Event.new start: GCal::EventDateTime.new(date_time: Time.now),
|
24
24
|
end: GCal::EventDateTime.new(date_time: Time.now),
|
25
|
-
summary: Rainbow(" now ").inverse.faint
|
25
|
+
summary: Rainbow(" now ").inverse.faint)
|
26
26
|
end
|
27
27
|
|
28
28
|
def self.find_av_uri ca, timespec
|
@@ -59,6 +59,22 @@ class CalendarAssistant
|
|
59
59
|
io.puts(*args)
|
60
60
|
end
|
61
61
|
|
62
|
+
def prompt query, default=nil
|
63
|
+
loop do
|
64
|
+
message = query
|
65
|
+
message += " [#{default}]" if default
|
66
|
+
message += ": "
|
67
|
+
print Rainbow(message).bold
|
68
|
+
answer = STDIN.gets.chomp.strip
|
69
|
+
if answer.empty?
|
70
|
+
return default if default
|
71
|
+
puts Rainbow("Please provide an answer.").red
|
72
|
+
else
|
73
|
+
return answer
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
62
78
|
def print_now! ca, event, printed_now
|
63
79
|
return true if printed_now
|
64
80
|
return false if event.start_date != Date.today
|
@@ -0,0 +1,98 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
class Event < SimpleDelegator
|
3
|
+
|
4
|
+
LOCATION_EVENT_REGEX = /^#{CalendarAssistant::EMOJI_WORLDMAP}/
|
5
|
+
|
6
|
+
def update **args
|
7
|
+
super
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def location_event?
|
12
|
+
!! (summary =~ LOCATION_EVENT_REGEX)
|
13
|
+
end
|
14
|
+
|
15
|
+
def all_day?
|
16
|
+
!! start.to_date
|
17
|
+
end
|
18
|
+
|
19
|
+
def past?
|
20
|
+
if all_day?
|
21
|
+
Date.today >= self.end.to_date
|
22
|
+
else
|
23
|
+
Time.now >= self.end.date_time
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def current?
|
28
|
+
! (past? || future?)
|
29
|
+
end
|
30
|
+
|
31
|
+
def future?
|
32
|
+
if all_day?
|
33
|
+
self.start.to_date > Date.today
|
34
|
+
else
|
35
|
+
self.start.date_time > Time.now
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def accepted?
|
40
|
+
response_status == GCal::Event::Response::ACCEPTED
|
41
|
+
end
|
42
|
+
|
43
|
+
def declined?
|
44
|
+
response_status == GCal::Event::Response::DECLINED
|
45
|
+
end
|
46
|
+
|
47
|
+
def one_on_one?
|
48
|
+
return false if attendees.nil?
|
49
|
+
return false unless attendees.any? { |a| a.self }
|
50
|
+
return false if human_attendees.length != 2
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
def busy?
|
55
|
+
transparency != GCal::Event::Transparency::TRANSPARENT
|
56
|
+
end
|
57
|
+
|
58
|
+
def commitment?
|
59
|
+
return false if human_attendees.nil? || human_attendees.length < 2
|
60
|
+
return false if declined?
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
def private?
|
65
|
+
visibility == GCal::Event::Visibility::PRIVATE
|
66
|
+
end
|
67
|
+
|
68
|
+
def public?
|
69
|
+
visibility == GCal::Event::Visibility::PUBLIC
|
70
|
+
end
|
71
|
+
|
72
|
+
def explicit_visibility?
|
73
|
+
private? || public?
|
74
|
+
end
|
75
|
+
|
76
|
+
def start_time
|
77
|
+
if all_day?
|
78
|
+
start.to_date.beginning_of_day
|
79
|
+
else
|
80
|
+
start.date_time
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def start_date
|
85
|
+
if all_day?
|
86
|
+
start.to_date
|
87
|
+
else
|
88
|
+
start.date_time.to_date
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def view_summary
|
93
|
+
return "(private)" if private? && (summary.nil? || summary.blank?)
|
94
|
+
return "(no title)" if summary.nil? || summary.blank?
|
95
|
+
summary
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
class EventRepository
|
3
|
+
def initialize(service, calendar_id)
|
4
|
+
@service = service
|
5
|
+
@calendar_id = calendar_id
|
6
|
+
end
|
7
|
+
|
8
|
+
def find time_range
|
9
|
+
events = @service.list_events(@calendar_id,
|
10
|
+
time_min: time_range.first.iso8601,
|
11
|
+
time_max: time_range.last.iso8601,
|
12
|
+
order_by: "startTime",
|
13
|
+
single_events: true,
|
14
|
+
max_results: 2000,
|
15
|
+
)
|
16
|
+
if events.nil? || events.items.nil?
|
17
|
+
return []
|
18
|
+
end
|
19
|
+
events.items.map { |e| CalendarAssistant::Event.new(e) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def create event_attributes
|
23
|
+
event = GCal::Event.new cast_dates(event_attributes)
|
24
|
+
@service.insert_event @calendar_id, event
|
25
|
+
CalendarAssistant::Event.new(event)
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete event
|
29
|
+
@service.delete_event @calendar_id, event.id
|
30
|
+
end
|
31
|
+
|
32
|
+
def update(event, attributes)
|
33
|
+
event.update! cast_dates(attributes)
|
34
|
+
@service.update_event @calendar_id, event.id, event
|
35
|
+
CalendarAssistant::Event.new(event)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def cast_dates attributes
|
41
|
+
attributes.each_with_object({}) do |(key, value), object|
|
42
|
+
if value.is_a?(Date)
|
43
|
+
object[key] = GCal::EventDateTime.new(date: value.iso8601)
|
44
|
+
else
|
45
|
+
object[key] = value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# this file extends the Google::EventDateTime class found in the "google_calendar" rubygem
|
3
|
+
#
|
4
|
+
|
5
|
+
require "google/apis/calendar_v3"
|
6
|
+
require "time"
|
7
|
+
|
8
|
+
class Google::Apis::CalendarV3::EventDateTime
|
9
|
+
def to_date
|
10
|
+
return nil if @date.nil?
|
11
|
+
return Date.parse(@date) if @date.is_a?(String)
|
12
|
+
@date
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_date!
|
16
|
+
return @date_time.to_date if @date.nil?
|
17
|
+
to_date
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
return @date.to_s if @date
|
22
|
+
@date_time.strftime "%Y-%m-%d %H:%M"
|
23
|
+
end
|
24
|
+
|
25
|
+
def == lhs
|
26
|
+
if @date
|
27
|
+
return to_date == lhs.to_date
|
28
|
+
end
|
29
|
+
date_time == lhs.date_time
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#
|
2
|
+
# this file extends the Google::Event class found in the "google_calendar" rubygem
|
3
|
+
#
|
4
|
+
|
5
|
+
require "google/apis/calendar_v3"
|
6
|
+
require "time"
|
7
|
+
require "calendar_assistant/extensions/event_date_time_extensions"
|
8
|
+
|
9
|
+
class Google::Apis::CalendarV3::Event
|
10
|
+
module RealResponse
|
11
|
+
DECLINED = "declined"
|
12
|
+
ACCEPTED = "accepted"
|
13
|
+
NEEDS_ACTION = "needsAction"
|
14
|
+
TENTATIVE = "tentative"
|
15
|
+
end
|
16
|
+
|
17
|
+
module Response
|
18
|
+
include RealResponse
|
19
|
+
SELF = "self" # not part of Google's API, but useful to represent meetings-for-myself
|
20
|
+
end
|
21
|
+
|
22
|
+
module Transparency
|
23
|
+
TRANSPARENT = "transparent"
|
24
|
+
OPAQUE = "opaque"
|
25
|
+
end
|
26
|
+
|
27
|
+
module Visibility
|
28
|
+
DEFAULT = "default"
|
29
|
+
PUBLIC = "public"
|
30
|
+
PRIVATE = "private"
|
31
|
+
end
|
32
|
+
|
33
|
+
def update **args
|
34
|
+
# this should be in the google API classes, IMHO
|
35
|
+
update!(**args)
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def human_attendees
|
40
|
+
return nil if attendees.nil?
|
41
|
+
attendees.select { |a| ! a.resource }
|
42
|
+
end
|
43
|
+
|
44
|
+
def attendee id
|
45
|
+
return nil if attendees.nil?
|
46
|
+
attendees.find do |attendee|
|
47
|
+
attendee.email == id
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def response_status
|
52
|
+
return Response::SELF if attendees.nil?
|
53
|
+
attendees.each do |attendee|
|
54
|
+
return attendee.response_status if attendee.self
|
55
|
+
end
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def av_uri
|
60
|
+
@av_uri ||= begin
|
61
|
+
description_link = CalendarAssistant::StringHelpers.find_uri_for_domain(description, "zoom.us")
|
62
|
+
return description_link if description_link
|
63
|
+
|
64
|
+
location_link = CalendarAssistant::StringHelpers.find_uri_for_domain(location, "zoom.us")
|
65
|
+
return location_link if location_link
|
66
|
+
|
67
|
+
return hangout_link if hangout_link
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
File without changes
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: calendar-assistant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Dalessio
|
8
|
+
- Mik Freedman
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2018-10-
|
12
|
+
date: 2018-10-31 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: google-api-client
|
@@ -165,78 +166,65 @@ dependencies:
|
|
165
166
|
- !ruby/object:Gem::Version
|
166
167
|
version: '10.0'
|
167
168
|
- !ruby/object:Gem::Dependency
|
168
|
-
name:
|
169
|
+
name: faker
|
169
170
|
requirement: !ruby/object:Gem::Requirement
|
170
171
|
requirements:
|
171
172
|
- - "~>"
|
172
173
|
- !ruby/object:Gem::Version
|
173
|
-
version:
|
174
|
+
version: 1.9.1
|
174
175
|
type: :development
|
175
176
|
prerelease: false
|
176
177
|
version_requirements: !ruby/object:Gem::Requirement
|
177
178
|
requirements:
|
178
179
|
- - "~>"
|
179
180
|
- !ruby/object:Gem::Version
|
180
|
-
version:
|
181
|
+
version: 1.9.1
|
181
182
|
- !ruby/object:Gem::Dependency
|
182
|
-
name:
|
183
|
+
name: rspec
|
183
184
|
requirement: !ruby/object:Gem::Requirement
|
184
185
|
requirements:
|
185
186
|
- - "~>"
|
186
187
|
- !ruby/object:Gem::Version
|
187
|
-
version:
|
188
|
+
version: '3.0'
|
188
189
|
type: :development
|
189
190
|
prerelease: false
|
190
191
|
version_requirements: !ruby/object:Gem::Requirement
|
191
192
|
requirements:
|
192
193
|
- - "~>"
|
193
194
|
- !ruby/object:Gem::Version
|
194
|
-
version:
|
195
|
-
- !ruby/object:Gem::Dependency
|
196
|
-
name: autotest
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
198
|
-
requirements:
|
199
|
-
- - ">="
|
200
|
-
- !ruby/object:Gem::Version
|
201
|
-
version: '0'
|
202
|
-
type: :development
|
203
|
-
prerelease: false
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
205
|
-
requirements:
|
206
|
-
- - ">="
|
207
|
-
- !ruby/object:Gem::Version
|
208
|
-
version: '0'
|
195
|
+
version: '3.0'
|
209
196
|
- !ruby/object:Gem::Dependency
|
210
|
-
name:
|
197
|
+
name: timecop
|
211
198
|
requirement: !ruby/object:Gem::Requirement
|
212
199
|
requirements:
|
213
|
-
- - "
|
200
|
+
- - "~>"
|
214
201
|
- !ruby/object:Gem::Version
|
215
|
-
version:
|
202
|
+
version: 0.9.0
|
216
203
|
type: :development
|
217
204
|
prerelease: false
|
218
205
|
version_requirements: !ruby/object:Gem::Requirement
|
219
206
|
requirements:
|
220
|
-
- - "
|
207
|
+
- - "~>"
|
221
208
|
- !ruby/object:Gem::Version
|
222
|
-
version:
|
209
|
+
version: 0.9.0
|
223
210
|
- !ruby/object:Gem::Dependency
|
224
|
-
name:
|
211
|
+
name: license_finder
|
225
212
|
requirement: !ruby/object:Gem::Requirement
|
226
213
|
requirements:
|
227
|
-
- - "
|
214
|
+
- - "~>"
|
228
215
|
- !ruby/object:Gem::Version
|
229
|
-
version:
|
216
|
+
version: 5.5.0
|
230
217
|
type: :development
|
231
218
|
prerelease: false
|
232
219
|
version_requirements: !ruby/object:Gem::Requirement
|
233
220
|
requirements:
|
234
|
-
- - "
|
221
|
+
- - "~>"
|
235
222
|
- !ruby/object:Gem::Version
|
236
|
-
version:
|
223
|
+
version: 5.5.0
|
237
224
|
description: A command-line tool to help manage your Google Calendar.
|
238
225
|
email:
|
239
226
|
- mike.dalessio@gmail.com
|
227
|
+
- github@michael-freedman.com
|
240
228
|
executables:
|
241
229
|
- calendar-assistant
|
242
230
|
extensions: []
|
@@ -254,8 +242,11 @@ files:
|
|
254
242
|
- lib/calendar_assistant/cli_helpers.rb
|
255
243
|
- lib/calendar_assistant/config.rb
|
256
244
|
- lib/calendar_assistant/config/token_store.rb
|
257
|
-
- lib/calendar_assistant/
|
258
|
-
- lib/calendar_assistant/
|
245
|
+
- lib/calendar_assistant/event.rb
|
246
|
+
- lib/calendar_assistant/event_repository.rb
|
247
|
+
- lib/calendar_assistant/extensions/event_date_time_extensions.rb
|
248
|
+
- lib/calendar_assistant/extensions/event_extensions.rb
|
249
|
+
- lib/calendar_assistant/extensions/rainbow_extensions.rb
|
259
250
|
- lib/calendar_assistant/string_helpers.rb
|
260
251
|
- lib/calendar_assistant/version.rb
|
261
252
|
homepage: https://github.com/flavorjones/calendar-assistant
|
@@ -1,173 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# this file extends the Google::Event class found in the "google_calendar" rubygem
|
3
|
-
#
|
4
|
-
|
5
|
-
require "google/apis/calendar_v3"
|
6
|
-
require "time"
|
7
|
-
|
8
|
-
class Google::Apis::CalendarV3::Event
|
9
|
-
module RealResponse
|
10
|
-
DECLINED = "declined"
|
11
|
-
ACCEPTED = "accepted"
|
12
|
-
NEEDS_ACTION = "needsAction"
|
13
|
-
TENTATIVE = "tentative"
|
14
|
-
end
|
15
|
-
|
16
|
-
module Response
|
17
|
-
include RealResponse
|
18
|
-
SELF = "self" # not part of Google's API, but useful to represent meetings-for-myself
|
19
|
-
end
|
20
|
-
|
21
|
-
module Transparency
|
22
|
-
TRANSPARENT = "transparent"
|
23
|
-
OPAQUE = "opaque"
|
24
|
-
end
|
25
|
-
|
26
|
-
module Visibility
|
27
|
-
DEFAULT = "default"
|
28
|
-
PUBLIC = "public"
|
29
|
-
PRIVATE = "private"
|
30
|
-
end
|
31
|
-
|
32
|
-
LOCATION_EVENT_REGEX = /^#{CalendarAssistant::EMOJI_WORLDMAP}/
|
33
|
-
|
34
|
-
def update **args
|
35
|
-
# this should be in the google API classes, IMHO
|
36
|
-
update!(**args)
|
37
|
-
self
|
38
|
-
end
|
39
|
-
|
40
|
-
def location_event?
|
41
|
-
!! (summary =~ LOCATION_EVENT_REGEX)
|
42
|
-
end
|
43
|
-
|
44
|
-
def all_day?
|
45
|
-
!! @start.to_date
|
46
|
-
end
|
47
|
-
|
48
|
-
def past?
|
49
|
-
if all_day?
|
50
|
-
Date.today >= self.end.to_date
|
51
|
-
else
|
52
|
-
Time.now >= self.end.date_time
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def current?
|
57
|
-
! (past? || future?)
|
58
|
-
end
|
59
|
-
|
60
|
-
def future?
|
61
|
-
if all_day?
|
62
|
-
self.start.to_date > Date.today
|
63
|
-
else
|
64
|
-
self.start.date_time > Time.now
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def accepted?
|
69
|
-
response_status == Response::ACCEPTED
|
70
|
-
end
|
71
|
-
|
72
|
-
def declined?
|
73
|
-
response_status == Response::DECLINED
|
74
|
-
end
|
75
|
-
|
76
|
-
def one_on_one?
|
77
|
-
return false if attendees.nil?
|
78
|
-
return false unless attendees.any? { |a| a.self }
|
79
|
-
return false if human_attendees.length != 2
|
80
|
-
true
|
81
|
-
end
|
82
|
-
|
83
|
-
def busy?
|
84
|
-
transparency != Transparency::TRANSPARENT
|
85
|
-
end
|
86
|
-
|
87
|
-
def commitment?
|
88
|
-
return false if human_attendees.nil? || human_attendees.length < 2
|
89
|
-
return false if declined?
|
90
|
-
true
|
91
|
-
end
|
92
|
-
|
93
|
-
def private?
|
94
|
-
visibility == Visibility::PRIVATE
|
95
|
-
end
|
96
|
-
|
97
|
-
def start_time
|
98
|
-
if all_day?
|
99
|
-
self.start.to_date.beginning_of_day
|
100
|
-
else
|
101
|
-
self.start.date_time
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def start_date
|
106
|
-
if all_day?
|
107
|
-
self.start.to_date
|
108
|
-
else
|
109
|
-
self.start.date_time.to_date
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def human_attendees
|
114
|
-
return nil if attendees.nil?
|
115
|
-
attendees.select { |a| ! a.resource }
|
116
|
-
end
|
117
|
-
|
118
|
-
def attendee id
|
119
|
-
return nil if attendees.nil?
|
120
|
-
attendees.find do |attendee|
|
121
|
-
attendee.email == id
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def response_status
|
126
|
-
return Response::SELF if attendees.nil?
|
127
|
-
attendees.each do |attendee|
|
128
|
-
return attendee.response_status if attendee.self
|
129
|
-
end
|
130
|
-
nil
|
131
|
-
end
|
132
|
-
|
133
|
-
def av_uri
|
134
|
-
@av_uri ||= begin
|
135
|
-
zoom = CalendarAssistant::StringHelpers.find_uri_for_domain(description, "zoom.us")
|
136
|
-
return zoom if zoom
|
137
|
-
|
138
|
-
return hangout_link if hangout_link
|
139
|
-
nil
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
def view_summary
|
144
|
-
return "(private)" if private? && (summary.nil? || summary.blank?)
|
145
|
-
return "(no title)" if summary.nil? || summary.blank?
|
146
|
-
summary
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
class Google::Apis::CalendarV3::EventDateTime
|
151
|
-
def to_date
|
152
|
-
return nil if @date.nil?
|
153
|
-
return Date.parse(@date) if @date.is_a?(String)
|
154
|
-
@date
|
155
|
-
end
|
156
|
-
|
157
|
-
def to_date!
|
158
|
-
return @date_time.to_date if @date.nil?
|
159
|
-
to_date
|
160
|
-
end
|
161
|
-
|
162
|
-
def to_s
|
163
|
-
return @date.to_s if @date
|
164
|
-
@date_time.strftime "%Y-%m-%d %H:%M"
|
165
|
-
end
|
166
|
-
|
167
|
-
def == lhs
|
168
|
-
if @date
|
169
|
-
return to_date == lhs.to_date
|
170
|
-
end
|
171
|
-
date_time == lhs.date_time
|
172
|
-
end
|
173
|
-
end
|