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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e7456770368242baeb04b1645ee98ccf2b4d0c1061df5a2d935903c59c3cbe4
4
- data.tar.gz: 58c65019e44c91fbe216ff0dd151073cc1113dc73597995553f44f9a5b6bddf6
3
+ metadata.gz: 0d3530bae8b1e8aac4260f546dd2fb2c3673b09c57d1c98e5f9e91bf91bdf5ed
4
+ data.tar.gz: e7c80eca297a7aa6ce37abfae434507368c54764c1af4d4175a346e7a4cbf9f4
5
5
  SHA512:
6
- metadata.gz: 93b5438be7365f06fc0b2cf79258ec491d29dd751d48cbea218a56f505441cf937e1406c7bb8418c6d195ead78ccd0e9453580664a4ebbe4d4a8573ac7bf3bc5
7
- data.tar.gz: c490f565590ffdebfb3119833bd8b907a8b457644f8c93d2fc9d9902c7869b325c70dbdd8cc9d84deabb459915cb966bd5261c7a9fb674fdfee14b436d124b80
6
+ metadata.gz: b7dcd5d6d01422c2ee042f47f0fba9fad1886fa3b2d2d2b65c411289007d5b20d3c262b888d325036c1f4bb79bf4c4d405e47c206f467adcf1a02de77be26afc
7
+ data.tar.gz: 34a1c905b4c45e5562639001721eacc977a5c3511f8527b986b9de294249e590a0e8ec0cebe689d22d2ffb0a6066f4565532d729a22491240a25d6ee6c4425a6
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
- # calendar assistant
1
+ <img src="icons/calendar-assistant.png" align="right" />
2
2
 
3
- A command-line tool to help you manage your Google Calendar.
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] # how dare you suggest there are bugs
96
- [--formatting], [--no-formatting] # Enable Text Formatting
97
- # Default: true
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] # how dare you suggest there are bugs
117
- [--formatting], [--no-formatting] # Enable Text Formatting
118
- # Default: true
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] # how dare you suggest there are bugs
151
- [--formatting], [--no-formatting] # Enable Text Formatting
152
- # Default: true
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] # launch a browser to join the video call URL
164
- # Default: true
165
- -p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
166
- [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
167
- -h, -?, [--help], [--no-help]
168
- [--debug], [--no-debug] # how dare you suggest there are bugs
169
- [--formatting], [--no-formatting] # Enable Text Formatting
170
- # Default: true
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] # [default 30m] find chunks of available time at least as long as LENGTH (which is a ChronicDuration string like '30m' or '2h')
205
- -s, [--start-of-day=TIME] # [default 9am] find chunks of available time after TIME (which is a BusinessTime string like '9am' or '14:30')
206
- -e, [--end-of-day=TIME] # [default 6pm] find chunks of available time before TIME (which is a BusinessTime string like '9am' or '14:30')
207
- -a, [--attendees=ATTENDEE1[,ATTENDEE2[,...]]] # [default 'me'] people (email IDs) to whom this command will be applied
208
- -p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
209
- [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
210
- -h, -?, [--help], [--no-help]
211
- [--debug], [--no-debug] # how dare you suggest there are bugs
212
- [--formatting], [--no-formatting] # Enable Text Formatting
213
- # Default: true
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
- -p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
306
- [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
307
- -h, -?, [--help], [--no-help]
308
- [--debug], [--no-debug] # how dare you suggest there are bugs
309
- [--formatting], [--no-formatting] # Enable Text Formatting
310
- # Default: true
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] # the profile you'd like to use (if different from default)
350
- [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
351
- -h, -?, [--help], [--no-help]
352
- [--debug], [--no-debug] # how dare you suggest there are bugs
353
- [--formatting], [--no-formatting] # Enable Text Formatting
354
- # Default: true
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> | 🗺 Gondolin</b><i> (not-busy)</i>
366
- 2018-09-28 <b> | 🗺 Rivendell</b><i> (not-busy)</i>
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] # only show events that you've accepted with another person
378
- -p, [--profile=PROFILE] # the profile you'd like to use (if different from default)
379
- [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
380
- -a, [--attendees=ATTENDEE1[,ATTENDEE2[,...]]] # [default 'me'] people (email IDs) to whom this command will be applied
381
- -h, -?, [--help], [--no-help]
382
- [--debug], [--no-debug] # how dare you suggest there are bugs
383
- [--formatting], [--no-formatting] # Enable Text Formatting
384
- # Default: true
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 | Repurpose end-to-end convergence</strike>
396
- <strike>2018-10-01 07:30 - 08:30 | Iterate clicks-and-mortar vortals</strike>
397
- <strike>2018-10-01 07:30 - 08:30 | Grow viral portals</strike>
398
- 2018-10-01 10:30 - 10:55<b> | Enable rich roi</b><i> (1:1, recurring)</i>
399
- 2018-10-01 11:00 - 11:30<b> | Leverage rich e-services</b><i> (recurring)</i>
400
- 2018-10-01 11:30 - 12:00<b> | Grow leading-edge communities</b><i> (1:1, recurring)</i>
401
- 2018-10-01 11:50 - 12:00<b> | Cultivate revolutionary experiences</b><i> (awaiting)</i>
402
- 2018-10-01 12:00 - 12:30<b> | Incentivize ubiquitous mindshare</b><i> (self)</i>
403
- <strike>2018-10-01 12:15 - 12:30 | Disintermediate next-generation niches</strike>
404
- <strike>2018-10-01 12:30 - 13:30 | Drive viral communities</strike>
405
- 2018-10-01 12:30 - 13:30<b> | Synergize integrated supply-chains</b><i> (awaiting, recurring)</i>
406
- 2018-10-01 13:30 - 14:50<b> | Transform revolutionary users</b><i> (self)</i>
407
- <strike>2018-10-01 13:30 - 14:30 | Enable interactive networks</strike>
408
- 2018-10-01 15:00 - 15:30<b> | Evolve global applications</b><i> (1:1)</i>
409
- 2018-10-01 16:00 - 17:00<b> | Implement b2c architectures</b><i> (1:1, recurring)</i>
410
- 2018-10-01 16:45 - 17:00<b> | Enable rich niches</b><i> (recurring)</i>
411
- 2018-10-01 17:00 - 17:30<b> | Implement frictionless e-markets</b><i> (recurring)</i>
412
- 2018-10-01 17:30 - 17:55<b> | Streamline world-class portals</b><i> (1:1, recurring)</i>
413
- <strike>2018-10-01 18:00 - 20:30 | Recontextualize rich paradigms</strike>
414
- <strike>2018-10-01 18:30 - 19:00 | Synergize dot-com functionalities</strike>
415
- 2018-10-01 19:00 - 19:30<b> | Revolutionize transparent e-markets</b><i> (awaiting)</i>
416
- 2018-10-01 <b> | 🗺 Country Round</b><i> (not-busy)</i>
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> | Enable rich roi</b><i> (1:1, recurring)</i>
426
- 2018-10-01 11:00 - 11:30<b> | Leverage rich e-services</b><i> (recurring)</i>
427
- 2018-10-01 11:30 - 12:00<b> | Grow leading-edge communities</b><i> (1:1, recurring)</i>
428
- 2018-10-01 11:50 - 12:00<b> | Cultivate revolutionary experiences</b><i> (awaiting)</i>
429
- 2018-10-01 12:30 - 13:30<b> | Synergize integrated supply-chains</b><i> (awaiting, recurring)</i>
430
- 2018-10-01 15:00 - 15:30<b> | Evolve global applications</b><i> (1:1)</i>
431
- 2018-10-01 16:00 - 17:00<b> | Implement b2c architectures</b><i> (1:1, recurring)</i>
432
- 2018-10-01 16:45 - 17:00<b> | Enable rich niches</b><i> (recurring)</i>
433
- 2018-10-01 17:00 - 17:30<b> | Implement frictionless e-markets</b><i> (recurring)</i>
434
- 2018-10-01 17:30 - 17:55<b> | Streamline world-class portals</b><i> (1:1, recurring)</i>
435
- 2018-10-01 19:00 - 19:30<b> | Revolutionize transparent e-markets</b><i> (awaiting)</i>
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] # the profile you'd like to use (if different from default)
447
- [--local-store=FILENAME] # Load events from a local file instead of Google Calendar
448
- -a, [--attendees=ATTENDEE1[,ATTENDEE2[,...]]] # [default 'me'] people (email IDs) to whom this command will be applied
449
- -h, -?, [--help], [--no-help]
450
- [--debug], [--no-debug] # how dare you suggest there are bugs
451
- [--formatting], [--no-formatting] # Enable Text Formatting
452
- # Default: true
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> | Cultivate revolutionary experiences</b><i> (awaiting)</i>
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> | Synergize integrated supply-chains</b><i> (awaiting, recurring)</i>
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> | Revolutionize transparent e-markets</b><i> (awaiting)</i>
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] # how dare you suggest there are bugs
485
- [--formatting], [--no-formatting] # Enable Text Formatting
486
- # Default: true
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-icons = ["🌎","🗺 "]
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
- Concourse.new("calendar-assistant").create_tasks!
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
- RSpec::Core::RakeTask.new(:spec) do |t|
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
- RSpec::Core::RakeTask.new(:features) do |t|
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 :license_finder do
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, features and license finder"
27
- task :default => [:test, :license_finder]
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"]
@@ -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: { needs_action?: true })
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(transparency: CalendarAssistant::Event::Transparency::TRANSPARENT, start: range.first, end: range.last , summary: "#{@emoji_worldmap} #{location}")
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
@@ -1,5 +1,6 @@
1
1
  require_relative 'cli/config'
2
2
  require_relative 'cli/helpers'
3
+ require_relative 'cli/command_service'
3
4
  require_relative 'cli/printer'
4
5
  require_relative 'cli/event_presenter'
5
6
  require_relative 'cli/event_set_presenter'
@@ -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
- CalendarAssistant::CLI::Authorizer::CREDENTIALS_PATH)
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
- get_authorizer(profile_name: profile_name).authorize
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 get_config, service: service
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 service
241
- @service ||= begin
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 get_authorizer(profile_name: get_config.profile_name, token_store: get_config.token_store)
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
- ca = CalendarAssistant.new(get_config, service: service)
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" # string
20
- MEETING_LENGTH = "meeting-length" # ChronicDuration
21
- START_OF_DAY = "start-of-day" # BusinessTime
22
- END_OF_DAY = "end-of-day" # BusinessTime
23
- LOCATION_ICONS = "location-icons" # Location Icons
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::LOCATION_ICONS => ["🗺 ", "🌎"], # Location Icons
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
- a = setting(Keys::Options::ATTENDEES)
144
- if a.is_a?(String)
145
- a = a.split(",")
146
- end
147
- a
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
- @location_icons = config[CalendarAssistant::Config::Keys::Settings::LOCATION_ICONS]
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
- !! (summary =~ location_event_regex)
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
 
@@ -1,3 +1,3 @@
1
1
  class CalendarAssistant
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
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.7.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-16 00:00:00.000000000 Z
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.28'
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.28'
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