calendar-assistant 0.7.0 → 0.8.0
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/README.md +176 -102
- data/Rakefile +35 -7
- data/lib/calendar_assistant.rb +1 -0
- data/lib/calendar_assistant/calendar_assistant.rb +10 -7
- data/lib/calendar_assistant/cli.rb +1 -0
- data/lib/calendar_assistant/cli/command_service.rb +37 -0
- data/lib/calendar_assistant/cli/commands.rb +52 -52
- data/lib/calendar_assistant/config.rb +43 -11
- data/lib/calendar_assistant/event.rb +50 -7
- data/lib/calendar_assistant/event_repository.rb +15 -6
- data/lib/calendar_assistant/predicate_collection.rb +21 -0
- data/lib/calendar_assistant/scheduler.rb +2 -2
- data/lib/calendar_assistant/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d3530bae8b1e8aac4260f546dd2fb2c3673b09c57d1c98e5f9e91bf91bdf5ed
|
4
|
+
data.tar.gz: e7c80eca297a7aa6ce37abfae434507368c54764c1af4d4175a346e7a4cbf9f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7dcd5d6d01422c2ee042f47f0fba9fad1886fa3b2d2d2b65c411289007d5b20d3c262b888d325036c1f4bb79bf4c4d405e47c206f467adcf1a02de77be26afc
|
7
|
+
data.tar.gz: 34a1c905b4c45e5562639001721eacc977a5c3511f8527b986b9de294249e590a0e8ec0cebe689d22d2ffb0a6066f4565532d729a22491240a25d6ee6c4425a6
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
|
1
|
+
<img src="icons/calendar-assistant.png" align="right" />
|
2
2
|
|
3
|
-
|
3
|
+
# Calendar Assistant
|
4
|
+
>A command-line tool to help you manage your Google Calendar.
|
4
5
|
|
5
6
|
- easily join the videoconference for your current meeting
|
6
7
|
- see yours and others' "availability" suitable for an email response
|
@@ -20,6 +21,7 @@ A command-line tool to help you manage your Google Calendar.
|
|
20
21
|
* [Pretty Display in Your Terminal](#pretty-display-in-your-terminal)
|
21
22
|
* [Human-Friendly Date and Time Specification](#human-friendly-date-and-time-specification)
|
22
23
|
* [Human-Friendly Duration Specification](#human-friendly-duration-specification)
|
24
|
+
* [Filter Events by property](#filter-events-by-property)
|
23
25
|
* [Preferences](#preferences)
|
24
26
|
- [Setup](#setup)
|
25
27
|
* [Installation](#installation)
|
@@ -66,6 +68,43 @@ Also note that every command will adopt an intelligent default, which is general
|
|
66
68
|
|
67
69
|
Some duration-related preferences are interpreted by [ChronicDuration](https://github.com/henrypoydar/chronic_duration) and so can be terms like "10m", "30 minutes", "four hours", etc.
|
68
70
|
|
71
|
+
### Filter Events by property
|
72
|
+
|
73
|
+
Commands that return events like `show`, `join`, `availability`, `location` or `lint` can be filtered using the option `--must-be` or `--must-not-be`. These options expect a comma separated list of one of the following properties.
|
74
|
+
|
75
|
+
* **Response**
|
76
|
+
* `accepted`
|
77
|
+
* `declined`
|
78
|
+
* `awaiting`
|
79
|
+
* `tentative`
|
80
|
+
* **Temporal**
|
81
|
+
* `all_day`
|
82
|
+
* `past`
|
83
|
+
* `current`
|
84
|
+
* `future`
|
85
|
+
* **Visibility**
|
86
|
+
* `private`
|
87
|
+
* `public`
|
88
|
+
* `explicitly_visible`
|
89
|
+
* `visible_guestlist`
|
90
|
+
* **Attributes**
|
91
|
+
* `location_event`
|
92
|
+
* `self`
|
93
|
+
* `one_on_one`
|
94
|
+
* `busy`
|
95
|
+
* `commitment`
|
96
|
+
* `recurring`
|
97
|
+
* `abandoned`
|
98
|
+
* `anyone_can_add_self`
|
99
|
+
* `attendees_omitted`
|
100
|
+
* `end_time_unspecified`
|
101
|
+
* `guests_can_invite_others`
|
102
|
+
* `guests_can_modify`
|
103
|
+
* `guests_can_see_other_guests`
|
104
|
+
* `private_copy`
|
105
|
+
* `locked`
|
106
|
+
* `needs_action`
|
107
|
+
|
69
108
|
|
70
109
|
### Preferences
|
71
110
|
|
@@ -73,10 +112,32 @@ All tokens and preferences will be stored in `~/.calendar-assistant` which is in
|
|
73
112
|
|
74
113
|
The location of `.calendar-assistant` defaults to the user's home directory. This location may be overridden by setting the environment variable `CA_HOME`
|
75
114
|
|
115
|
+
|
116
|
+
#### Nickname
|
117
|
+
|
118
|
+
Some commands, like `location-set`, will refer to you by nickname if you configure it (the alternative might be to not specify your name, or use your email id).
|
119
|
+
|
120
|
+
Set `nickname` to a string that would uniquely and briefly identify you to others, like "Mike D" or "JK".
|
121
|
+
|
122
|
+
|
76
123
|
#### Location Emoji
|
77
124
|
|
78
125
|
There is a `[settings]` key called `location-icons` that may be set to an array denoting all emoji that indicate a location event. The first icon in the array will be used as the default. The default emoji included are `["🗺 ", "🌎"]` with the map icon first
|
79
126
|
|
127
|
+
|
128
|
+
#### Command-Specific Preferences
|
129
|
+
|
130
|
+
If there are user preferences you'd like to set for just a single command (e.g., making public location events created via `location-set`), you can create a nested section in your TOML settings file, like so:
|
131
|
+
|
132
|
+
```toml
|
133
|
+
[settings]
|
134
|
+
visibility = default
|
135
|
+
|
136
|
+
[settings.location_set]
|
137
|
+
visibility = public
|
138
|
+
```
|
139
|
+
|
140
|
+
|
80
141
|
## Setup
|
81
142
|
|
82
143
|
### Installation
|
@@ -91,10 +152,10 @@ Usage:
|
|
91
152
|
calendar-assistant setup
|
92
153
|
|
93
154
|
Options:
|
94
|
-
-h, -?, [--help], [--no-help]
|
95
|
-
[--debug], [--no-debug]
|
96
|
-
|
97
|
-
|
155
|
+
-h, -?, [--help], [--no-help]
|
156
|
+
[--debug], [--no-debug] # how dare you suggest there are bugs
|
157
|
+
-f, [--formatting], [--no-formatting] # Enable Text Formatting
|
158
|
+
# Default: true
|
98
159
|
|
99
160
|
Description:
|
100
161
|
This command will walk you through setting up a Google Cloud Project, enabling the Google Calendar
|
@@ -112,10 +173,10 @@ Usage:
|
|
112
173
|
calendar-assistant authorize PROFILE_NAME
|
113
174
|
|
114
175
|
Options:
|
115
|
-
-h, -?, [--help], [--no-help]
|
116
|
-
[--debug], [--no-debug]
|
117
|
-
|
118
|
-
|
176
|
+
-h, -?, [--help], [--no-help]
|
177
|
+
[--debug], [--no-debug] # how dare you suggest there are bugs
|
178
|
+
-f, [--formatting], [--no-formatting] # Enable Text Formatting
|
179
|
+
# Default: true
|
119
180
|
|
120
181
|
Description:
|
121
182
|
Create and authorize a named profile (e.g., "work", "home", "flastname@company.tld") to access your
|
@@ -146,10 +207,10 @@ Commands:
|
|
146
207
|
calendar-assistant version # Display the version of calend...
|
147
208
|
|
148
209
|
Options:
|
149
|
-
-h, -?, [--help], [--no-help]
|
150
|
-
[--debug], [--no-debug]
|
151
|
-
|
152
|
-
|
210
|
+
-h, -?, [--help], [--no-help]
|
211
|
+
[--debug], [--no-debug] # how dare you suggest there are bugs
|
212
|
+
-f, [--formatting], [--no-formatting] # Enable Text Formatting
|
213
|
+
# Default: true
|
153
214
|
</pre>
|
154
215
|
|
155
216
|
|
@@ -160,14 +221,16 @@ Usage:
|
|
160
221
|
calendar-assistant join [TIME]
|
161
222
|
|
162
223
|
Options:
|
163
|
-
[--join], [--no-join]
|
164
|
-
|
165
|
-
-p, [--profile=PROFILE]
|
166
|
-
|
167
|
-
-
|
168
|
-
|
169
|
-
|
170
|
-
|
224
|
+
[--join], [--no-join] # launch a browser to join the video call URL
|
225
|
+
# Default: true
|
226
|
+
-p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
|
227
|
+
-l, [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
|
228
|
+
-b, [--must-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be true (see README)
|
229
|
+
-n, [--must-not-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be false (see README)
|
230
|
+
-h, -?, [--help], [--no-help]
|
231
|
+
[--debug], [--no-debug] # how dare you suggest there are bugs
|
232
|
+
-f, [--formatting], [--no-formatting] # Enable Text Formatting
|
233
|
+
# Default: true
|
171
234
|
|
172
235
|
Open the URL for a video call attached to your meeting at time TIME (default 'now')
|
173
236
|
</pre>
|
@@ -201,16 +264,18 @@ Usage:
|
|
201
264
|
calendar-assistant availability [DATE | DATERANGE | TIMERANGE]
|
202
265
|
|
203
266
|
Options:
|
204
|
-
-l, [--meeting-length=LENGTH]
|
205
|
-
-s, [--start-of-day=TIME]
|
206
|
-
-e, [--end-of-day=TIME]
|
207
|
-
-a, [--attendees=ATTENDEE1[,ATTENDEE2[,...]]]
|
208
|
-
-p, [--profile=PROFILE]
|
209
|
-
|
210
|
-
-
|
211
|
-
|
212
|
-
|
213
|
-
|
267
|
+
-l, [--meeting-length=LENGTH] # [default 30m] find chunks of available time at least as long as LENGTH (which is a ChronicDuration string like '30m' or '2h')
|
268
|
+
-s, [--start-of-day=TIME] # [default 9am] find chunks of available time after TIME (which is a BusinessTime string like '9am' or '14:30')
|
269
|
+
-e, [--end-of-day=TIME] # [default 6pm] find chunks of available time before TIME (which is a BusinessTime string like '9am' or '14:30')
|
270
|
+
-a, [--attendees=ATTENDEE1[,ATTENDEE2[,...]]] # [default 'me'] people (email IDs) to whom this command will be applied
|
271
|
+
-p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
|
272
|
+
-l, [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
|
273
|
+
-b, [--must-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be true (see README)
|
274
|
+
-n, [--must-not-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be false (see README)
|
275
|
+
-h, -?, [--help], [--no-help]
|
276
|
+
[--debug], [--no-debug] # how dare you suggest there are bugs
|
277
|
+
-f, [--formatting], [--no-formatting] # Enable Text Formatting
|
278
|
+
# Default: true
|
214
279
|
|
215
280
|
Show your availability for a date or range of dates (default 'today')
|
216
281
|
</pre>
|
@@ -302,12 +367,15 @@ Usage:
|
|
302
367
|
calendar-assistant location-set LOCATION [DATE | DATERANGE]
|
303
368
|
|
304
369
|
Options:
|
305
|
-
|
306
|
-
|
307
|
-
-
|
308
|
-
|
309
|
-
|
310
|
-
|
370
|
+
[--visibility=VISIBILITY] # [default is 'default'] Set the visbility of the event. Values are 'public', 'private', 'default'.
|
371
|
+
-p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
|
372
|
+
-l, [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
|
373
|
+
-b, [--must-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be true (see README)
|
374
|
+
-n, [--must-not-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be false (see README)
|
375
|
+
-h, -?, [--help], [--no-help]
|
376
|
+
[--debug], [--no-debug] # how dare you suggest there are bugs
|
377
|
+
-f, [--formatting], [--no-formatting] # Enable Text Formatting
|
378
|
+
# Default: true
|
311
379
|
|
312
380
|
Set your location to LOCATION for a date or range of dates (default 'today')
|
313
381
|
</pre>
|
@@ -346,12 +414,14 @@ Usage:
|
|
346
414
|
calendar-assistant location [DATE | DATERANGE]
|
347
415
|
|
348
416
|
Options:
|
349
|
-
-p, [--profile=PROFILE]
|
350
|
-
|
351
|
-
-
|
352
|
-
|
353
|
-
|
354
|
-
|
417
|
+
-p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
|
418
|
+
-l, [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
|
419
|
+
-b, [--must-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be true (see README)
|
420
|
+
-n, [--must-not-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be false (see README)
|
421
|
+
-h, -?, [--help], [--no-help]
|
422
|
+
[--debug], [--no-debug] # how dare you suggest there are bugs
|
423
|
+
-f, [--formatting], [--no-formatting] # Enable Text Formatting
|
424
|
+
# Default: true
|
355
425
|
|
356
426
|
Show your location for a date or range of dates (default 'today')
|
357
427
|
</pre>
|
@@ -362,8 +432,8 @@ For example:
|
|
362
432
|
<b>$</b> calendar-assistant location "2018-09-24...2018-09-28"
|
363
433
|
<i>me@example.com (all times in America/New_York)
|
364
434
|
</i>
|
365
|
-
2018-09-24 - 2018-09-28 <b> |
|
366
|
-
2018-09-28 <b> |
|
435
|
+
2018-09-24 - 2018-09-28 <b> | 🌎 Iron Hills</b><i> (not-busy)</i>
|
436
|
+
2018-09-28 <b> | 🌎 Country Round</b><i> (not-busy)</i>
|
367
437
|
</pre>
|
368
438
|
|
369
439
|
|
@@ -374,14 +444,16 @@ Usage:
|
|
374
444
|
calendar-assistant show [DATE | DATERANGE | TIMERANGE]
|
375
445
|
|
376
446
|
Options:
|
377
|
-
-c, [--commitments], [--no-commitments]
|
378
|
-
-p, [--profile=PROFILE]
|
379
|
-
|
380
|
-
-a, [--attendees=ATTENDEE1[,ATTENDEE2[,...]]]
|
381
|
-
-
|
382
|
-
|
383
|
-
|
384
|
-
|
447
|
+
-c, [--commitments], [--no-commitments] # only show events that you've accepted with another person
|
448
|
+
-p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
|
449
|
+
-l, [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
|
450
|
+
-a, [--attendees=ATTENDEE1[,ATTENDEE2[,...]]] # [default 'me'] people (email IDs) to whom this command will be applied
|
451
|
+
-b, [--must-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be true (see README)
|
452
|
+
-n, [--must-not-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be false (see README)
|
453
|
+
-h, -?, [--help], [--no-help]
|
454
|
+
[--debug], [--no-debug] # how dare you suggest there are bugs
|
455
|
+
-f, [--formatting], [--no-formatting] # Enable Text Formatting
|
456
|
+
# Default: true
|
385
457
|
|
386
458
|
Show your events for a date or range of dates (default 'today')
|
387
459
|
</pre>
|
@@ -392,28 +464,28 @@ For example: display all events scheduled for tomorrow:
|
|
392
464
|
<b>$</b> calendar-assistant show 2018-10-01
|
393
465
|
<i>me@example.com (all times in America/New_York)
|
394
466
|
</i>
|
395
|
-
<strike>2018-10-01 03:30 - 05:00 |
|
396
|
-
<strike>2018-10-01 07:30 - 08:30 |
|
397
|
-
<strike>2018-10-01 07:30 - 08:30 |
|
398
|
-
2018-10-01 10:30 - 10:55<b> |
|
399
|
-
2018-10-01 11:00 - 11:30<b> |
|
400
|
-
2018-10-01 11:30 - 12:00<b> |
|
401
|
-
2018-10-01 11:50 - 12:00<b> |
|
402
|
-
2018-10-01 12:00 - 12:30<b> |
|
403
|
-
<strike>2018-10-01 12:15 - 12:30 |
|
404
|
-
<strike>2018-10-01 12:30 - 13:30 |
|
405
|
-
2018-10-01 12:30 - 13:30<b> |
|
406
|
-
2018-10-01 13:30 - 14:50<b> |
|
407
|
-
<strike>2018-10-01 13:30 - 14:30 | Enable
|
408
|
-
2018-10-01 15:00 - 15:30<b> |
|
409
|
-
2018-10-01 16:00 - 17:00<b> |
|
410
|
-
2018-10-01 16:45 - 17:00<b> |
|
411
|
-
2018-10-01 17:00 - 17:30<b> |
|
412
|
-
2018-10-01 17:30 - 17:55<b> |
|
413
|
-
<strike>2018-10-01 18:00 - 20:30 |
|
414
|
-
<strike>2018-10-01 18:30 - 19:00 |
|
415
|
-
2018-10-01 19:00 - 19:30<b> |
|
416
|
-
2018-10-01 <b> |
|
467
|
+
<strike>2018-10-01 03:30 - 05:00 | Facilitate 24/365 web services</strike>
|
468
|
+
<strike>2018-10-01 07:30 - 08:30 | Reintermediate magnetic communities</strike>
|
469
|
+
<strike>2018-10-01 07:30 - 08:30 | Revolutionize bricks-and-clicks synergies</strike>
|
470
|
+
2018-10-01 10:30 - 10:55<b> | Whiteboard clicks-and-mortar metrics</b><i> (1:1, recurring)</i>
|
471
|
+
2018-10-01 11:00 - 11:30<b> | Architect holistic models</b><i> (recurring)</i>
|
472
|
+
2018-10-01 11:30 - 12:00<b> | Reinvent bleeding-edge e-services</b><i> (1:1, recurring)</i>
|
473
|
+
2018-10-01 11:50 - 12:00<b> | Synergize e-business systems</b><i> (awaiting)</i>
|
474
|
+
2018-10-01 12:00 - 12:30<b> | Reinvent e-business e-tailers</b><i> (self)</i>
|
475
|
+
<strike>2018-10-01 12:15 - 12:30 | Maximize clicks-and-mortar convergence</strike>
|
476
|
+
<strike>2018-10-01 12:30 - 13:30 | Disintermediate end-to-end bandwidth</strike>
|
477
|
+
2018-10-01 12:30 - 13:30<b> | Iterate innovative networks</b><i> (awaiting, recurring)</i>
|
478
|
+
2018-10-01 13:30 - 14:50<b> | Engage holistic users</b><i> (self)</i>
|
479
|
+
<strike>2018-10-01 13:30 - 14:30 | Enable e-business e-services</strike>
|
480
|
+
2018-10-01 15:00 - 15:30<b> | Innovate impactful technologies</b><i> (1:1)</i>
|
481
|
+
2018-10-01 16:00 - 17:00<b> | Iterate dynamic networks</b><i> (1:1, recurring)</i>
|
482
|
+
2018-10-01 16:45 - 17:00<b> | Deploy robust content</b><i> (recurring)</i>
|
483
|
+
2018-10-01 17:00 - 17:30<b> | Synthesize viral methodologies</b><i> (recurring)</i>
|
484
|
+
2018-10-01 17:30 - 17:55<b> | Scale collaborative methodologies</b><i> (1:1, recurring)</i>
|
485
|
+
<strike>2018-10-01 18:00 - 20:30 | Seize rich experiences</strike>
|
486
|
+
<strike>2018-10-01 18:30 - 19:00 | Engineer one-to-one e-business</strike>
|
487
|
+
2018-10-01 19:00 - 19:30<b> | Recontextualize collaborative content</b><i> (awaiting)</i>
|
488
|
+
2018-10-01 <b> | 🌎 Grey Mountains</b><i> (not-busy)</i>
|
417
489
|
</pre>
|
418
490
|
|
419
491
|
Display _only_ the commitments I have to other people using the `-c` option:
|
@@ -422,17 +494,17 @@ Display _only_ the commitments I have to other people using the `-c` option:
|
|
422
494
|
<b>$</b> calendar-assistant show -c 2018-10-01
|
423
495
|
<i>me@example.com (all times in America/New_York)
|
424
496
|
</i>
|
425
|
-
2018-10-01 10:30 - 10:55<b> |
|
426
|
-
2018-10-01 11:00 - 11:30<b> |
|
427
|
-
2018-10-01 11:30 - 12:00<b> |
|
428
|
-
2018-10-01 11:50 - 12:00<b> |
|
429
|
-
2018-10-01 12:30 - 13:30<b> |
|
430
|
-
2018-10-01 15:00 - 15:30<b> |
|
431
|
-
2018-10-01 16:00 - 17:00<b> |
|
432
|
-
2018-10-01 16:45 - 17:00<b> |
|
433
|
-
2018-10-01 17:00 - 17:30<b> |
|
434
|
-
2018-10-01 17:30 - 17:55<b> |
|
435
|
-
2018-10-01 19:00 - 19:30<b> |
|
497
|
+
2018-10-01 10:30 - 10:55<b> | Whiteboard clicks-and-mortar metrics</b><i> (1:1, recurring)</i>
|
498
|
+
2018-10-01 11:00 - 11:30<b> | Architect holistic models</b><i> (recurring)</i>
|
499
|
+
2018-10-01 11:30 - 12:00<b> | Reinvent bleeding-edge e-services</b><i> (1:1, recurring)</i>
|
500
|
+
2018-10-01 11:50 - 12:00<b> | Synergize e-business systems</b><i> (awaiting)</i>
|
501
|
+
2018-10-01 12:30 - 13:30<b> | Iterate innovative networks</b><i> (awaiting, recurring)</i>
|
502
|
+
2018-10-01 15:00 - 15:30<b> | Innovate impactful technologies</b><i> (1:1)</i>
|
503
|
+
2018-10-01 16:00 - 17:00<b> | Iterate dynamic networks</b><i> (1:1, recurring)</i>
|
504
|
+
2018-10-01 16:45 - 17:00<b> | Deploy robust content</b><i> (recurring)</i>
|
505
|
+
2018-10-01 17:00 - 17:30<b> | Synthesize viral methodologies</b><i> (recurring)</i>
|
506
|
+
2018-10-01 17:30 - 17:55<b> | Scale collaborative methodologies</b><i> (1:1, recurring)</i>
|
507
|
+
2018-10-01 19:00 - 19:30<b> | Recontextualize collaborative content</b><i> (awaiting)</i>
|
436
508
|
</pre>
|
437
509
|
|
438
510
|
|
@@ -443,13 +515,15 @@ Usage:
|
|
443
515
|
calendar-assistant lint [DATE | DATERANGE | TIMERANGE]
|
444
516
|
|
445
517
|
Options:
|
446
|
-
-p, [--profile=PROFILE]
|
447
|
-
|
448
|
-
-a, [--attendees=ATTENDEE1[,ATTENDEE2[,...]]]
|
449
|
-
-
|
450
|
-
|
451
|
-
|
452
|
-
|
518
|
+
-p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
|
519
|
+
-l, [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
|
520
|
+
-a, [--attendees=ATTENDEE1[,ATTENDEE2[,...]]] # [default 'me'] people (email IDs) to whom this command will be applied
|
521
|
+
-b, [--must-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be true (see README)
|
522
|
+
-n, [--must-not-be=PROPERTY1[,PROPERTY2[,...]]] # Event properties that must be false (see README)
|
523
|
+
-h, -?, [--help], [--no-help]
|
524
|
+
[--debug], [--no-debug] # how dare you suggest there are bugs
|
525
|
+
-f, [--formatting], [--no-formatting] # Enable Text Formatting
|
526
|
+
# Default: true
|
453
527
|
|
454
528
|
Lint your events for a date or range of dates (default 'today')
|
455
529
|
</pre>
|
@@ -462,11 +536,11 @@ me@example.com
|
|
462
536
|
- looking for events that need attention
|
463
537
|
- all times in America/New_York
|
464
538
|
|
465
|
-
2018-10-01 11:50 - 12:00<b> |
|
539
|
+
2018-10-01 11:50 - 12:00<b> | Synergize e-business systems</b><i> (awaiting)</i>
|
466
540
|
attendees: 👍 three@example.com, 🤷 four@example.com
|
467
|
-
2018-10-01 12:30 - 13:30<b> |
|
541
|
+
2018-10-01 12:30 - 13:30<b> | Iterate innovative networks</b><i> (awaiting, recurring)</i>
|
468
542
|
attendees: 👍 three@example.com, 🤷 four@example.com
|
469
|
-
2018-10-01 19:00 - 19:30<b> |
|
543
|
+
2018-10-01 19:00 - 19:30<b> | Recontextualize collaborative content</b><i> (awaiting)</i>
|
470
544
|
attendees: 👍 three@example.com, 🤷 four@example.com
|
471
545
|
</pre>
|
472
546
|
|
@@ -480,10 +554,10 @@ Usage:
|
|
480
554
|
calendar-assistant config
|
481
555
|
|
482
556
|
Options:
|
483
|
-
-h, -?, [--help], [--no-help]
|
484
|
-
[--debug], [--no-debug]
|
485
|
-
|
486
|
-
|
557
|
+
-h, -?, [--help], [--no-help]
|
558
|
+
[--debug], [--no-debug] # how dare you suggest there are bugs
|
559
|
+
-f, [--formatting], [--no-formatting] # Enable Text Formatting
|
560
|
+
# Default: true
|
487
561
|
|
488
562
|
Dump your configuration parameters (merge of defaults and overrides from /home/user/.calendar-assistant)
|
489
563
|
</pre>
|
@@ -495,7 +569,7 @@ The output is TOML, which is suitable for dumping into `~/.calendar-assistant` a
|
|
495
569
|
|
496
570
|
[settings]
|
497
571
|
end-of-day = "6pm"
|
498
|
-
location-
|
572
|
+
location-icon = "🌎"
|
499
573
|
meeting-length = "30m"
|
500
574
|
profile = "work"
|
501
575
|
start-of-day = "9am"
|
data/Rakefile
CHANGED
@@ -3,25 +3,53 @@ require "rspec/core/rake_task"
|
|
3
3
|
require "concourse"
|
4
4
|
require "license_finder"
|
5
5
|
|
6
|
-
|
6
|
+
#
|
7
|
+
# concourse
|
8
|
+
#
|
9
|
+
Concourse.new("calendar-assistant", fly_target: "calendar-assistants").create_tasks!
|
7
10
|
|
8
|
-
RSpec::Core::RakeTask.new(:test)
|
9
11
|
|
10
|
-
|
12
|
+
#
|
13
|
+
# spec tasks
|
14
|
+
#
|
15
|
+
desc "Run unit and feature specs"
|
16
|
+
RSpec::Core::RakeTask.new("spec")
|
17
|
+
task "test" => "spec" # undocumented alias for 'spec'
|
18
|
+
|
19
|
+
desc "Run unit specs"
|
20
|
+
RSpec::Core::RakeTask.new("spec:unit") do |t|
|
11
21
|
t.rspec_opts ||= []
|
12
22
|
t.rspec_opts << " --tag=~type:aruba"
|
13
23
|
end
|
14
24
|
|
15
|
-
|
25
|
+
desc "Run feature specs"
|
26
|
+
RSpec::Core::RakeTask.new("spec:feature") do |t|
|
16
27
|
t.rspec_opts ||= []
|
17
28
|
t.rspec_opts << " --tag=type:aruba"
|
18
29
|
end
|
19
30
|
|
20
31
|
desc "Ensure all dependencies meet license requirements."
|
21
|
-
task
|
32
|
+
task "license_finder" do
|
22
33
|
LicenseFinder::CLI::Main.start(["report"])
|
23
34
|
LicenseFinder::CLI::Main.start([])
|
24
35
|
end
|
25
36
|
|
26
|
-
desc "Run specs,
|
27
|
-
task
|
37
|
+
desc "Run unit specs, feature specs, and license finder"
|
38
|
+
task "default" => ["spec", "license_finder"]
|
39
|
+
|
40
|
+
|
41
|
+
#
|
42
|
+
# docker docker docker
|
43
|
+
#
|
44
|
+
desc "Build a docker image for testing"
|
45
|
+
task "docker:build" do
|
46
|
+
sh "docker build -t flavorjones/calendar-assistant-test -f concourse/images/Dockerfile ."
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Push a docker image for testing"
|
50
|
+
task "docker:push" do
|
51
|
+
sh "docker push flavorjones/calendar-assistant-test"
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Build and push a docker image for testing"
|
55
|
+
task "docker" => ["docker:build", "docker:push"]
|
data/lib/calendar_assistant.rb
CHANGED
@@ -35,6 +35,7 @@ class CalendarAssistant
|
|
35
35
|
autoload :EventSet, "calendar_assistant/event_set"
|
36
36
|
autoload :Scheduler, "calendar_assistant/scheduler"
|
37
37
|
autoload :LocalService, "calendar_assistant/local_service"
|
38
|
+
autoload :PredicateCollection, "calendar_assistant/predicate_collection"
|
38
39
|
end
|
39
40
|
|
40
41
|
require "calendar_assistant/cli"
|
@@ -37,8 +37,7 @@ class CalendarAssistant
|
|
37
37
|
@calendar = service.get_calendar Config::DEFAULT_CALENDAR_ID
|
38
38
|
@event_repository_factory = event_repository_factory
|
39
39
|
@event_repositories = {} # calendar_id → event_repository
|
40
|
-
|
41
|
-
@emoji_worldmap = Array(@config.setting(CalendarAssistant::Config::Keys::Settings::LOCATION_ICONS)).first
|
40
|
+
@event_predicates = PredicateCollection.build(config.must_be, config.must_not_be)
|
42
41
|
end
|
43
42
|
|
44
43
|
def in_env &block
|
@@ -62,7 +61,7 @@ class CalendarAssistant
|
|
62
61
|
raise BaseException, "CalendarAssistant#lint_events only supports one person (for now)"
|
63
62
|
end
|
64
63
|
|
65
|
-
event_repository(calendar_ids.first).find(time_range, predicates: {
|
64
|
+
event_repository(calendar_ids.first).find(time_range, predicates: @event_predicates.merge({needs_action?: true}))
|
66
65
|
end
|
67
66
|
|
68
67
|
def find_events time_range
|
@@ -70,7 +69,7 @@ class CalendarAssistant
|
|
70
69
|
if calendar_ids.length > 1
|
71
70
|
raise BaseException, "CalendarAssistant#find_events only supports one person (for now)"
|
72
71
|
end
|
73
|
-
event_repository(calendar_ids.first).find(time_range)
|
72
|
+
event_repository(calendar_ids.first).find(time_range, predicates: @event_predicates)
|
74
73
|
end
|
75
74
|
|
76
75
|
def availability time_range
|
@@ -78,11 +77,11 @@ class CalendarAssistant
|
|
78
77
|
ers = calendar_ids.map do |calendar_id|
|
79
78
|
event_repository calendar_id
|
80
79
|
end
|
81
|
-
Scheduler.new(self, ers).available_blocks(time_range)
|
80
|
+
Scheduler.new(self, ers).available_blocks(time_range, predicates: @event_predicates)
|
82
81
|
end
|
83
82
|
|
84
83
|
def find_location_events time_range
|
85
|
-
event_set = event_repository.find(time_range)
|
84
|
+
event_set = event_repository.find(time_range, predicates: @event_predicates)
|
86
85
|
event_set.new event_set.events.select { |e| e.location_event? }
|
87
86
|
end
|
88
87
|
|
@@ -96,7 +95,11 @@ class CalendarAssistant
|
|
96
95
|
deleted_events = []
|
97
96
|
modified_events = []
|
98
97
|
|
99
|
-
event = event_repository.create(
|
98
|
+
event = event_repository.create(
|
99
|
+
transparency: CalendarAssistant::Event::Transparency::TRANSPARENT,
|
100
|
+
start: range.first, end: range.last,
|
101
|
+
summary: "#{Event.location_event_prefix(@config)}#{location}"
|
102
|
+
)
|
100
103
|
|
101
104
|
existing_event_set.events.each do |existing_event|
|
102
105
|
if existing_event.start_date >= event.start_date && existing_event.end_date <= event.end_date
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
module CLI
|
3
|
+
class CommandService
|
4
|
+
attr_reader :options, :config, :out
|
5
|
+
|
6
|
+
def initialize(context:, options: {})
|
7
|
+
@options = options.dup
|
8
|
+
@options[CalendarAssistant::Config::Keys::Options::CONTEXT] ||= context.to_s
|
9
|
+
|
10
|
+
@config = CalendarAssistant::CLI::Config.new(options: @options)
|
11
|
+
@authorizer = {}
|
12
|
+
@out = CalendarAssistant::CLI::Printer.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def calendar_assistant(datespec)
|
16
|
+
ca = CalendarAssistant.new(config, service: service)
|
17
|
+
ca.in_env do
|
18
|
+
yield(ca, CalendarAssistant::CLI::Helpers.parse_datespec(datespec), out)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def authorizer(profile_name: config.profile_name, token_store: config.token_store)
|
23
|
+
@authorizer[profile_name] ||= Authorizer.new(profile_name, token_store)
|
24
|
+
end
|
25
|
+
|
26
|
+
def service
|
27
|
+
@service ||= begin
|
28
|
+
if filename = config.setting(Config::Keys::Options::LOCAL_STORE)
|
29
|
+
CalendarAssistant::LocalService.new(file: filename)
|
30
|
+
else
|
31
|
+
authorizer.service
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require "calendar_assistant/cli/helpers"
|
2
|
-
|
3
1
|
class CalendarAssistant
|
4
2
|
module CLI
|
5
3
|
class Commands < Thor
|
@@ -12,7 +10,21 @@ class CalendarAssistant
|
|
12
10
|
option CalendarAssistant::Config::Keys::Options::LOCAL_STORE,
|
13
11
|
type: :string,
|
14
12
|
banner: "FILENAME",
|
15
|
-
desc: "Load events from a local file instead of Google Calendar"
|
13
|
+
desc: "Load events from a local file instead of Google Calendar",
|
14
|
+
aliases: [ "-l" ]
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.has_events
|
18
|
+
option CalendarAssistant::Config::Keys::Options::MUST_BE,
|
19
|
+
type: :string,
|
20
|
+
desc: "Event properties that must be true (see README)",
|
21
|
+
banner: "PROPERTY1[,PROPERTY2[,...]]",
|
22
|
+
aliases: [ "-b" ]
|
23
|
+
option CalendarAssistant::Config::Keys::Options::MUST_NOT_BE,
|
24
|
+
type: :string,
|
25
|
+
desc: "Event properties that must be false (see README)",
|
26
|
+
banner: "PROPERTY1[,PROPERTY2[,...]]",
|
27
|
+
aliases: [ "-n" ]
|
16
28
|
end
|
17
29
|
|
18
30
|
def self.has_attendees
|
@@ -35,14 +47,16 @@ class CalendarAssistant
|
|
35
47
|
class_option CalendarAssistant::Config::Keys::Options::FORMATTING,
|
36
48
|
type: :boolean,
|
37
49
|
desc: "Enable Text Formatting",
|
38
|
-
default: CalendarAssistant::Config::DEFAULT_SETTINGS[CalendarAssistant::Config::Keys::Options::FORMATTING]
|
50
|
+
default: CalendarAssistant::Config::DEFAULT_SETTINGS[CalendarAssistant::Config::Keys::Options::FORMATTING],
|
51
|
+
aliases: "-f"
|
52
|
+
|
39
53
|
|
40
54
|
desc "version",
|
41
55
|
"Display the version of calendar-assistant"
|
42
56
|
|
43
57
|
def version
|
44
58
|
return if handle_help_args
|
45
|
-
out.puts CalendarAssistant::VERSION
|
59
|
+
command_service.out.puts CalendarAssistant::VERSION
|
46
60
|
end
|
47
61
|
|
48
62
|
|
@@ -52,7 +66,7 @@ class CalendarAssistant
|
|
52
66
|
def config
|
53
67
|
return if handle_help_args
|
54
68
|
settings = CalendarAssistant::CLI::Config.new.settings
|
55
|
-
out.puts TOML::Generator.new({CalendarAssistant::Config::Keys::SETTINGS => settings}).body
|
69
|
+
command_service.out.puts TOML::Generator.new({CalendarAssistant::Config::Keys::SETTINGS => settings}).body
|
56
70
|
end
|
57
71
|
|
58
72
|
|
@@ -72,14 +86,14 @@ class CalendarAssistant
|
|
72
86
|
# TODO ugh see #34 for advice on how to clean this up
|
73
87
|
return if handle_help_args
|
74
88
|
if File.exist? CalendarAssistant::CLI::Authorizer::CREDENTIALS_PATH
|
75
|
-
out.puts sprintf("Credentials already exist in %s",
|
76
|
-
|
89
|
+
command_service.out.puts sprintf("Credentials already exist in %s",
|
90
|
+
CalendarAssistant::CLI::Authorizer::CREDENTIALS_PATH)
|
77
91
|
return
|
78
92
|
end
|
79
93
|
|
80
|
-
out.launch "https://developers.google.com/calendar/quickstart/ruby"
|
94
|
+
command_service.out.launch "https://developers.google.com/calendar/quickstart/ruby"
|
81
95
|
sleep 1
|
82
|
-
out.puts <<~EOT
|
96
|
+
command_service.out.puts <<~EOT
|
83
97
|
Please click on "ENABLE THE GOOGLE CALENDAR API" and either create a new project or select an existing project.
|
84
98
|
|
85
99
|
(If you create a new project, name it something like "yourname-calendar-assistant" so you remember why it exists.)
|
@@ -89,13 +103,13 @@ class CalendarAssistant
|
|
89
103
|
Finally, paste the contents of the downloaded file here (it should be a complete JSON object):
|
90
104
|
EOT
|
91
105
|
|
92
|
-
json = out.prompt "Paste JSON here"
|
106
|
+
json = command_service.out.prompt "Paste JSON here"
|
93
107
|
File.open(CalendarAssistant::CLI::Authorizer::CREDENTIALS_PATH, "w") do |f|
|
94
108
|
f.write json
|
95
109
|
end
|
96
110
|
FileUtils.chmod 0600, CalendarAssistant::CLI::Authorizer::CREDENTIALS_PATH
|
97
111
|
|
98
|
-
out.puts "\nOK! Your next step is to run `calendar-assistant authorize`."
|
112
|
+
command_service.out.puts "\nOK! Your next step is to run `calendar-assistant authorize`."
|
99
113
|
end
|
100
114
|
|
101
115
|
|
@@ -117,7 +131,7 @@ class CalendarAssistant
|
|
117
131
|
return if handle_help_args
|
118
132
|
return help! if profile_name.nil?
|
119
133
|
|
120
|
-
|
134
|
+
command_service.authorizer(profile_name: profile_name).authorize
|
121
135
|
|
122
136
|
puts "\nYou're authorized!\n\n"
|
123
137
|
end
|
@@ -127,13 +141,14 @@ class CalendarAssistant
|
|
127
141
|
will_create_a_service
|
128
142
|
has_attendees
|
129
143
|
|
144
|
+
has_events
|
130
145
|
def lint datespec = "today"
|
131
|
-
calendar_assistant(datespec) do |ca, date|
|
146
|
+
calendar_assistant(datespec) do |ca, date, out|
|
132
147
|
event_set = ca.lint_events date
|
133
148
|
out.print_events ca, event_set, presenter_class: CalendarAssistant::CLI::LinterEventSetPresenter
|
134
149
|
end
|
135
150
|
end
|
136
|
-
|
151
|
+
|
137
152
|
desc "show [DATE | DATERANGE | TIMERANGE]",
|
138
153
|
"Show your events for a date or range of dates (default 'today')"
|
139
154
|
option CalendarAssistant::Config::Keys::Options::COMMITMENTS,
|
@@ -143,8 +158,9 @@ class CalendarAssistant
|
|
143
158
|
will_create_a_service
|
144
159
|
has_attendees
|
145
160
|
|
161
|
+
has_events
|
146
162
|
def show datespec = "today"
|
147
|
-
calendar_assistant(datespec) do |ca, date|
|
163
|
+
calendar_assistant(datespec) do |ca, date, out|
|
148
164
|
event_set = ca.find_events date
|
149
165
|
out.print_events ca, event_set
|
150
166
|
end
|
@@ -158,18 +174,19 @@ class CalendarAssistant
|
|
158
174
|
desc: "launch a browser to join the video call URL"
|
159
175
|
will_create_a_service
|
160
176
|
|
177
|
+
has_events
|
161
178
|
def join timespec = "now"
|
162
179
|
return if handle_help_args
|
163
180
|
set_formatting
|
164
|
-
ca = CalendarAssistant.new
|
181
|
+
ca = CalendarAssistant.new command_service.config, service: command_service.service
|
165
182
|
ca.in_env do
|
166
183
|
event_set, url = CalendarAssistant::CLI::Helpers.find_av_uri ca, timespec
|
167
184
|
if !event_set.empty?
|
168
|
-
out.print_events ca, event_set
|
169
|
-
out.puts url
|
170
|
-
out.launch url if options[CalendarAssistant::Config::Keys::Options::JOIN]
|
185
|
+
command_service.out.print_events ca, event_set
|
186
|
+
command_service.out.puts url
|
187
|
+
command_service.out.launch url if options[CalendarAssistant::Config::Keys::Options::JOIN]
|
171
188
|
else
|
172
|
-
out.puts "Could not find a meeting '#{timespec}' with a video call to join."
|
189
|
+
command_service.out.puts "Could not find a meeting '#{timespec}' with a video call to join."
|
173
190
|
end
|
174
191
|
end
|
175
192
|
end
|
@@ -179,8 +196,9 @@ class CalendarAssistant
|
|
179
196
|
"Show your location for a date or range of dates (default 'today')"
|
180
197
|
will_create_a_service
|
181
198
|
|
199
|
+
has_events
|
182
200
|
def location datespec = "today"
|
183
|
-
calendar_assistant(datespec) do |ca, date|
|
201
|
+
calendar_assistant(datespec) do |ca, date, out|
|
184
202
|
event_set = ca.find_location_events date
|
185
203
|
out.print_events ca, event_set
|
186
204
|
end
|
@@ -189,12 +207,16 @@ class CalendarAssistant
|
|
189
207
|
|
190
208
|
desc "location-set LOCATION [DATE | DATERANGE]",
|
191
209
|
"Set your location to LOCATION for a date or range of dates (default 'today')"
|
210
|
+
option CalendarAssistant::Config::Keys::Settings::VISIBILITY,
|
211
|
+
type: :string,
|
212
|
+
banner: "VISIBILITY",
|
213
|
+
desc: "[default is 'default'] Set the visbility of the event. Values are 'public', 'private', 'default'."
|
192
214
|
will_create_a_service
|
193
|
-
|
215
|
+
has_events
|
194
216
|
def location_set location = nil, datespec = "today"
|
195
217
|
return help! if location.nil?
|
196
218
|
|
197
|
-
calendar_assistant(datespec) do |ca, date|
|
219
|
+
calendar_assistant(datespec) do |ca, date, out|
|
198
220
|
event_set = ca.create_location_event date, location
|
199
221
|
out.print_events ca, event_set
|
200
222
|
end
|
@@ -223,9 +245,9 @@ class CalendarAssistant
|
|
223
245
|
aliases: ["-e"]
|
224
246
|
has_attendees
|
225
247
|
will_create_a_service
|
226
|
-
|
248
|
+
has_events
|
227
249
|
def availability datespec = "today"
|
228
|
-
calendar_assistant(datespec) do |ca, date|
|
250
|
+
calendar_assistant(datespec) do |ca, date, out|
|
229
251
|
event_set = ca.availability date
|
230
252
|
out.print_available_blocks ca, event_set
|
231
253
|
end
|
@@ -237,36 +259,14 @@ class CalendarAssistant
|
|
237
259
|
Rainbow.enabled = !!options[:formatting]
|
238
260
|
end
|
239
261
|
|
240
|
-
def
|
241
|
-
@
|
242
|
-
if filename = get_config.setting(Config::Keys::Options::LOCAL_STORE)
|
243
|
-
CalendarAssistant::LocalService.new(file: filename)
|
244
|
-
else
|
245
|
-
get_authorizer.service
|
246
|
-
end
|
247
|
-
end
|
262
|
+
def command_service
|
263
|
+
@command_service ||= CommandService.new(context: current_command_chain.first, options: options)
|
248
264
|
end
|
249
265
|
|
250
|
-
def
|
251
|
-
@authorizer ||= {}
|
252
|
-
@authorizer[profile_name] ||= Authorizer.new(profile_name, token_store)
|
253
|
-
end
|
254
|
-
|
255
|
-
def calendar_assistant datespec = "today"
|
266
|
+
def calendar_assistant datespec = "today", &block
|
256
267
|
return if handle_help_args
|
257
268
|
set_formatting
|
258
|
-
|
259
|
-
ca.in_env do
|
260
|
-
yield(ca, CalendarAssistant::CLI::Helpers.parse_datespec(datespec))
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
def get_config
|
265
|
-
@config ||= CalendarAssistant::CLI::Config.new(options: options)
|
266
|
-
end
|
267
|
-
|
268
|
-
def out
|
269
|
-
@out ||= CalendarAssistant::CLI::Printer.new
|
269
|
+
command_service.calendar_assistant(datespec, &block)
|
270
270
|
end
|
271
271
|
|
272
272
|
def help!
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
class CalendarAssistant
|
2
3
|
class Config
|
3
4
|
autoload :TokenStore, "calendar_assistant/config/token_store"
|
@@ -16,11 +17,13 @@ class CalendarAssistant
|
|
16
17
|
# and which can be overridden by entries in the user config file
|
17
18
|
#
|
18
19
|
module Settings
|
19
|
-
PROFILE = "profile"
|
20
|
-
MEETING_LENGTH = "meeting-length"
|
21
|
-
START_OF_DAY = "start-of-day"
|
22
|
-
END_OF_DAY = "end-of-day"
|
23
|
-
|
20
|
+
PROFILE = "profile" # string
|
21
|
+
MEETING_LENGTH = "meeting-length" # ChronicDuration
|
22
|
+
START_OF_DAY = "start-of-day" # BusinessTime
|
23
|
+
END_OF_DAY = "end-of-day" # BusinessTime
|
24
|
+
LOCATION_ICON = "location-icon" # string emoji
|
25
|
+
NICKNAME = "nickname" # string
|
26
|
+
VISIBILITY = "visibility" # Event Visibility
|
24
27
|
end
|
25
28
|
|
26
29
|
#
|
@@ -34,13 +37,16 @@ class CalendarAssistant
|
|
34
37
|
LOCAL_STORE = "local-store" # filename
|
35
38
|
DEBUG = "debug" # bool
|
36
39
|
FORMATTING = "formatting" # Rainbow
|
40
|
+
MUST_BE = "must-be" # array of event predicates (comma-delimited)
|
41
|
+
MUST_NOT_BE = "must-not-be" # array of event predicates (comma-delimited)
|
42
|
+
CONTEXT = "context" # symbol referring to command context
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
46
|
DEFAULT_CALENDAR_ID = "primary"
|
41
47
|
|
42
48
|
DEFAULT_SETTINGS = {
|
43
|
-
Keys::Settings::
|
49
|
+
Keys::Settings::LOCATION_ICON => "🌎", # string emoji
|
44
50
|
Keys::Settings::MEETING_LENGTH => "30m", # ChronicDuration
|
45
51
|
Keys::Settings::START_OF_DAY => "9am", # BusinessTime
|
46
52
|
Keys::Settings::END_OF_DAY => "6pm", # BusinessTime
|
@@ -110,7 +116,9 @@ class CalendarAssistant
|
|
110
116
|
# and settings
|
111
117
|
#
|
112
118
|
def setting setting_name
|
119
|
+
context = Config.find_in_hash(options, Keys::Options::CONTEXT)
|
113
120
|
Config.find_in_hash(options, setting_name) ||
|
121
|
+
Config.find_in_hash(user_config, [Keys::SETTINGS, context, setting_name]) ||
|
114
122
|
Config.find_in_hash(user_config, [Keys::SETTINGS, setting_name]) ||
|
115
123
|
Config.find_in_hash(defaults, setting_name)
|
116
124
|
end
|
@@ -140,11 +148,15 @@ class CalendarAssistant
|
|
140
148
|
# helper method for Keys::Options::ATTENDEES
|
141
149
|
#
|
142
150
|
def attendees
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
151
|
+
split_if_array(Keys::Options::ATTENDEES)
|
152
|
+
end
|
153
|
+
|
154
|
+
def must_be
|
155
|
+
split_if_array(Keys::Options::MUST_BE)
|
156
|
+
end
|
157
|
+
|
158
|
+
def must_not_be
|
159
|
+
split_if_array(Keys::Options::MUST_NOT_BE)
|
148
160
|
end
|
149
161
|
|
150
162
|
def debug?
|
@@ -155,8 +167,28 @@ class CalendarAssistant
|
|
155
167
|
#noop
|
156
168
|
end
|
157
169
|
|
170
|
+
def event_visibility
|
171
|
+
value = setting(Keys::Settings::VISIBILITY)
|
172
|
+
case value
|
173
|
+
when CalendarAssistant::Event::Visibility::PUBLIC
|
174
|
+
CalendarAssistant::Event::Visibility::PUBLIC
|
175
|
+
when CalendarAssistant::Event::Visibility::PRIVATE
|
176
|
+
CalendarAssistant::Event::Visibility::PRIVATE
|
177
|
+
else
|
178
|
+
CalendarAssistant::Event::Visibility::DEFAULT
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
158
182
|
private
|
159
183
|
|
184
|
+
def split_if_array(key)
|
185
|
+
a = setting(key)
|
186
|
+
if a.is_a?(String)
|
187
|
+
a = a.split(",")
|
188
|
+
end
|
189
|
+
a
|
190
|
+
end
|
191
|
+
|
160
192
|
def self.find_in_hash hash, keypath
|
161
193
|
current_val = hash
|
162
194
|
keypath = keypath.split(".") unless keypath.is_a?(Array)
|
@@ -27,6 +27,45 @@ class CalendarAssistant
|
|
27
27
|
PRIVATE = "private"
|
28
28
|
end
|
29
29
|
|
30
|
+
PREDICATES = {
|
31
|
+
"response": %I[
|
32
|
+
accepted?
|
33
|
+
declined?
|
34
|
+
awaiting?
|
35
|
+
tentative?
|
36
|
+
],
|
37
|
+
"temporal": %I[
|
38
|
+
all_day?
|
39
|
+
past?
|
40
|
+
current?
|
41
|
+
future?
|
42
|
+
],
|
43
|
+
"visibility": %I[
|
44
|
+
private?
|
45
|
+
public?
|
46
|
+
explicitly_visible?
|
47
|
+
visible_guestlist?
|
48
|
+
],
|
49
|
+
"attributes": %I[
|
50
|
+
location_event?
|
51
|
+
self?
|
52
|
+
one_on_one?
|
53
|
+
busy?
|
54
|
+
commitment?
|
55
|
+
recurring?
|
56
|
+
abandoned?
|
57
|
+
anyone_can_add_self?
|
58
|
+
attendees_omitted?
|
59
|
+
end_time_unspecified?
|
60
|
+
guests_can_invite_others?
|
61
|
+
guests_can_modify?
|
62
|
+
guests_can_see_other_guests?
|
63
|
+
private_copy?
|
64
|
+
locked?
|
65
|
+
needs_action?
|
66
|
+
]
|
67
|
+
}
|
68
|
+
|
30
69
|
#
|
31
70
|
# class methods
|
32
71
|
#
|
@@ -34,17 +73,20 @@ class CalendarAssistant
|
|
34
73
|
(end_time.to_datetime - start_time.to_datetime).days.to_i
|
35
74
|
end
|
36
75
|
|
76
|
+
def self.location_event_prefix config
|
77
|
+
icon = config[CalendarAssistant::Config::Keys::Settings::LOCATION_ICON]
|
78
|
+
if nickname = config[CalendarAssistant::Config::Keys::Settings::NICKNAME]
|
79
|
+
return "#{icon} #{nickname} @ "
|
80
|
+
end
|
81
|
+
"#{icon} "
|
82
|
+
end
|
83
|
+
|
37
84
|
#
|
38
85
|
# instance methods
|
39
86
|
#
|
40
|
-
attr_reader :location_event_regex
|
41
|
-
|
42
87
|
def initialize(obj, config: CalendarAssistant::Config.new)
|
43
|
-
|
44
88
|
super(obj)
|
45
|
-
@
|
46
|
-
|
47
|
-
@location_event_regex = /^#{@location_icons.join("|")}/
|
89
|
+
@config = config
|
48
90
|
end
|
49
91
|
|
50
92
|
def update **args
|
@@ -53,7 +95,7 @@ class CalendarAssistant
|
|
53
95
|
end
|
54
96
|
|
55
97
|
def location_event?
|
56
|
-
!! (
|
98
|
+
!! summary.try(:starts_with?, Event.location_event_prefix(@config))
|
57
99
|
end
|
58
100
|
|
59
101
|
def all_day?
|
@@ -240,5 +282,6 @@ class CalendarAssistant
|
|
240
282
|
def contains? time
|
241
283
|
start_time <= time && time < end_time
|
242
284
|
end
|
285
|
+
|
243
286
|
end
|
244
287
|
end
|
@@ -28,17 +28,13 @@ class CalendarAssistant
|
|
28
28
|
)
|
29
29
|
events = events.items.map { |e| CalendarAssistant::Event.new(e, config: config) }
|
30
30
|
|
31
|
-
unless predicates.empty?
|
32
|
-
events = events.select do |event|
|
33
|
-
predicates.all? { |predicate, value| event.public_send(predicate) == value }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
31
|
+
events = filter_by_predicates(events, predicates) unless predicates.empty?
|
37
32
|
CalendarAssistant::EventSet.new self, events
|
38
33
|
end
|
39
34
|
|
40
35
|
def new event_attributes
|
41
36
|
event = Google::Apis::CalendarV3::Event.new DateHelpers.cast_dates(event_attributes)
|
37
|
+
event.visibility ||= config.event_visibility
|
42
38
|
CalendarAssistant::Event.new(event, config: config)
|
43
39
|
end
|
44
40
|
|
@@ -66,5 +62,18 @@ class CalendarAssistant
|
|
66
62
|
)
|
67
63
|
CalendarAssistant::Event.new e, config: config
|
68
64
|
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def filter_by_predicates(events, predicates)
|
69
|
+
valid_predicates = CalendarAssistant::Event::PREDICATES.values.flatten
|
70
|
+
|
71
|
+
events.select do |event|
|
72
|
+
predicates.all? do |predicate, value|
|
73
|
+
raise BaseException, "Invalid event predicate" unless valid_predicates.include?(predicate)
|
74
|
+
event.public_send(predicate) == value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
69
78
|
end
|
70
79
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
module PredicateCollection
|
3
|
+
|
4
|
+
def self.build(must_be, must_not_be)
|
5
|
+
predicates = {}
|
6
|
+
Array(must_be).each do |predicate|
|
7
|
+
predicates[predicate_symbol(predicate)] = true
|
8
|
+
end
|
9
|
+
|
10
|
+
Array(must_not_be).each do |predicate|
|
11
|
+
predicates[predicate_symbol(predicate)] = false
|
12
|
+
end
|
13
|
+
|
14
|
+
predicates
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.predicate_symbol(str)
|
18
|
+
str.to_s.gsub(/(.*[^?])$/, "\\1?").to_sym
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -26,10 +26,10 @@ class CalendarAssistant
|
|
26
26
|
@ers = Array(event_repositories)
|
27
27
|
end
|
28
28
|
|
29
|
-
def available_blocks time_range
|
29
|
+
def available_blocks time_range, predicates: {}
|
30
30
|
avail = nil
|
31
31
|
ers.each do |er|
|
32
|
-
event_set = er.find time_range # array
|
32
|
+
event_set = er.find time_range, predicates: predicates # array
|
33
33
|
event_set = Scheduler.select_busy_events event_set # hash
|
34
34
|
event_set.ensure_keys time_range.first.to_date .. time_range.last.to_date, only: true
|
35
35
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: calendar-assistant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Dalessio
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-01-
|
12
|
+
date: 2019-01-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -76,7 +76,7 @@ dependencies:
|
|
76
76
|
version: '0.24'
|
77
77
|
- - "<"
|
78
78
|
- !ruby/object:Gem::Version
|
79
|
-
version: '0.
|
79
|
+
version: '0.29'
|
80
80
|
type: :runtime
|
81
81
|
prerelease: false
|
82
82
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -86,7 +86,7 @@ dependencies:
|
|
86
86
|
version: '0.24'
|
87
87
|
- - "<"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0.
|
89
|
+
version: '0.29'
|
90
90
|
- !ruby/object:Gem::Dependency
|
91
91
|
name: launchy
|
92
92
|
requirement: !ruby/object:Gem::Requirement
|
@@ -288,6 +288,7 @@ files:
|
|
288
288
|
- lib/calendar_assistant/calendar_assistant.rb
|
289
289
|
- lib/calendar_assistant/cli.rb
|
290
290
|
- lib/calendar_assistant/cli/authorizer.rb
|
291
|
+
- lib/calendar_assistant/cli/command_service.rb
|
291
292
|
- lib/calendar_assistant/cli/commands.rb
|
292
293
|
- lib/calendar_assistant/cli/config.rb
|
293
294
|
- lib/calendar_assistant/cli/event_presenter.rb
|
@@ -306,6 +307,7 @@ files:
|
|
306
307
|
- lib/calendar_assistant/extensions/google_apis_extensions.rb
|
307
308
|
- lib/calendar_assistant/extensions/rainbow_extensions.rb
|
308
309
|
- lib/calendar_assistant/local_service.rb
|
310
|
+
- lib/calendar_assistant/predicate_collection.rb
|
309
311
|
- lib/calendar_assistant/scheduler.rb
|
310
312
|
- lib/calendar_assistant/string_helpers.rb
|
311
313
|
- lib/calendar_assistant/version.rb
|