calendar-assistant 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|