calendar-assistant 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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