icalPal 3.9.3 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +45 -73
- data/bin/icalPal +10 -8
- data/bin/icalpal +10 -8
- data/lib/calendar.rb +12 -0
- data/lib/defaults.rb +0 -25
- data/lib/event.rb +32 -23
- data/lib/icalPal.rb +19 -0
- data/lib/options.rb +17 -5
- data/lib/rdt.rb +16 -4
- data/lib/reminder.rb +37 -5
- data/lib/store.rb +0 -2
- data/lib/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 014f8b5179cd56cb757404df75a3d23145907d2f40b4186031ed5028ad5e92af
|
|
4
|
+
data.tar.gz: 8abe6a5c80c695fa61b0000ae20c52fcbd1fab29ab326fcb89fe1a4d469fae82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 24bafd17756e9e4ccce28dc0b23ea421d334f3d9c58f3fe62dd07cf26777b47c56587a52e61c5995a730b823a3ad2f0ebc8bef460205ebcde981df3e0bd35108
|
|
7
|
+
data.tar.gz: a21caa7650b168542625a176909e05e0f87825c452015e7292f5297aa8b9d96863dc22c1f921a197a97769c9b3fb5db0a593adf1b22784b1874e03ecb05a8b34
|
data/README.md
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+

|
|
2
|
+
[](https://badge.fury.io/rb/icalPal.svg?icon=si%3Arubygems&icon_color=%23ff0000)
|
|
3
|
+
[](https://badge.fury.io/rb/icalPal.svg?icon=si%3Arubygems&icon_color=%23ff0000)
|
|
2
4
|
|
|
3
5
|
# icalPal
|
|
4
6
|
|
|
5
7
|
## Description
|
|
6
8
|
|
|
7
|
-
icalPal is a command-line tool to query macOS Calendar and
|
|
8
|
-
databases for accounts, calendars, events, and tasks. It
|
|
9
|
-
on any system with [Ruby](https://www.ruby-lang.org/) and
|
|
10
|
-
Calendar or Reminders database.
|
|
9
|
+
icalPal is a command-line tool to query macOS *Calendar* and
|
|
10
|
+
*Reminders* databases for accounts, calendars, events, and tasks. It
|
|
11
|
+
can be run on any system with [Ruby](https://www.ruby-lang.org/) and
|
|
12
|
+
access to a Calendar or Reminders database.
|
|
11
13
|
|
|
12
14
|
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
|
|
15
|
+
|
|
13
16
|
**Table of Contents**
|
|
14
17
|
|
|
15
18
|
- [Installation](#installation)
|
|
@@ -26,13 +29,13 @@ Calendar or Reminders database.
|
|
|
26
29
|
|
|
27
30
|
## Installation
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
Install icalPal for all users:
|
|
30
33
|
|
|
31
34
|
```
|
|
32
35
|
gem install icalPal
|
|
33
36
|
```
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
For only you:
|
|
36
39
|
|
|
37
40
|
```
|
|
38
41
|
gem install --user-install icalPal
|
|
@@ -64,6 +67,8 @@ differences to be aware of.
|
|
|
64
67
|
|
|
65
68
|
### Additional commands
|
|
66
69
|
|
|
70
|
+
icalPal also supports additional commands.
|
|
71
|
+
|
|
67
72
|
```icalPal accounts```
|
|
68
73
|
|
|
69
74
|
Shows a list of enabled Calendar accounts. Internally they are known
|
|
@@ -79,7 +84,7 @@ Shows only reminders that have a due date.
|
|
|
79
84
|
*remindersDueBefore* can be used instead of *tasks*
|
|
80
85
|
|
|
81
86
|
Reminders can also be viewed in the *Scheduled Reminders* calendar,
|
|
82
|
-
using the *
|
|
87
|
+
using the *events* commands. Repeating reminders are treated the same
|
|
83
88
|
as repeating events.
|
|
84
89
|
|
|
85
90
|
### Additional options
|
|
@@ -98,39 +103,35 @@ as repeating events.
|
|
|
98
103
|
* ```--color``` uses a wider color palette. Colors are what you have chosen in the Calendar and Reminders apps, including custom colors
|
|
99
104
|
* ```--match``` lets you filter the results of any command to items where a *FIELD* matches a regular expression. Eg., ```--match notes=zoom.us``` to show only Zoom meeetings
|
|
100
105
|
|
|
101
|
-
Because icalPal is written in Ruby, and not a native Mac application,
|
|
102
|
-
you can run it just about anywhere. It's been tested with the
|
|
103
|
-
versions of Ruby included with macOS Sequoia and Tahoe (2.6.10) and
|
|
104
|
-
[Homebrew](https://brew.sh/) (3.4.x).
|
|
105
|
-
|
|
106
106
|
### Additional properties
|
|
107
107
|
|
|
108
108
|
Several additional properties are available for each command.
|
|
109
109
|
|
|
110
110
|
* Accounts
|
|
111
111
|
* account
|
|
112
|
+
* delegations
|
|
112
113
|
* notes
|
|
113
114
|
* owner
|
|
114
115
|
* type
|
|
115
|
-
* delegations
|
|
116
116
|
|
|
117
117
|
* Calendar
|
|
118
118
|
* account
|
|
119
|
-
*
|
|
119
|
+
* locale
|
|
120
|
+
* notes
|
|
121
|
+
* published_URL
|
|
120
122
|
* self\_identity_email, owner\_identity_email
|
|
123
|
+
* shared\_owner_name, shared\_owner_address
|
|
124
|
+
* sharees
|
|
121
125
|
* subcal_account_id, subcal_url
|
|
122
|
-
* published_URL
|
|
123
|
-
* notes
|
|
124
|
-
* locale
|
|
125
126
|
|
|
126
127
|
* Tasks
|
|
127
|
-
*
|
|
128
|
-
* grocery
|
|
128
|
+
* assignee
|
|
129
129
|
* completed
|
|
130
|
+
* grocery
|
|
130
131
|
* group
|
|
132
|
+
* id
|
|
131
133
|
* section
|
|
132
134
|
* tags
|
|
133
|
-
* assignee
|
|
134
135
|
* timezone
|
|
135
136
|
* Notifications
|
|
136
137
|
* due (due_date formatted with --df and --tf options)
|
|
@@ -143,7 +144,7 @@ Several additional properties are available for each command.
|
|
|
143
144
|
icalPal: Usage: icalPal [options] [-c] COMMAND
|
|
144
145
|
|
|
145
146
|
COMMAND must be one of the following:
|
|
146
|
-
|
|
147
|
+
|
|
147
148
|
events Print events
|
|
148
149
|
tasks Print tasks
|
|
149
150
|
calendars Print calendars
|
|
@@ -159,29 +160,27 @@ COMMAND must be one of the following:
|
|
|
159
160
|
|
|
160
161
|
stores can be used instead of accounts
|
|
161
162
|
reminders can be used instead of tasks
|
|
162
|
-
```
|
|
163
163
|
|
|
164
164
|
Global options:
|
|
165
|
-
|
|
165
|
+
|
|
166
166
|
-c, --cmd=COMMAND Command to run
|
|
167
167
|
--db=DB Use DB file instead of Calendar
|
|
168
|
-
(default: ["$HOME/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb", $HOME/Library/Calendars/Calendar.sqlitedb]
|
|
168
|
+
(default: ["$HOME/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb", "$HOME/Library/Calendars/Calendar.sqlitedb"]
|
|
169
169
|
For the tasks commands this should be a directory containing .sqlite files
|
|
170
|
-
(default: "$HOME/Library/Group Containers/group.com.apple.reminders/Container_v1/Stores")
|
|
170
|
+
(default: ["$HOME/Library/Group Containers/group.com.apple.reminders/Container_v1/Stores"])
|
|
171
171
|
--cf=FILE Set config file path (default: $HOME/.icalpal)
|
|
172
172
|
--norc Ignore ICALPAL and ICALPAL_CONFIG environment variables
|
|
173
173
|
-o, --output=FORMAT Print as FORMAT (default: default)
|
|
174
174
|
[ansi, csv, default, hash, html, json, md, rdoc, remind, toc, xml, yaml]
|
|
175
|
-
```
|
|
176
175
|
|
|
177
|
-
Including/excluding accounts, calendars,
|
|
178
|
-
|
|
176
|
+
Including/excluding accounts, calendars, items:
|
|
177
|
+
|
|
179
178
|
--is=ACCOUNTS List of accounts to include
|
|
180
179
|
--es=ACCOUNTS List of accounts to exclude
|
|
181
180
|
|
|
182
181
|
--it=TYPES List of calendar types to include
|
|
183
182
|
--et=TYPES List of calendar types to exclude
|
|
184
|
-
[Local, Exchange, CalDAV, MobileMe, Subscribed, Birthdays]
|
|
183
|
+
[Local, Exchange, CalDAV, MobileMe, Subscribed, Birthdays, Reminders]
|
|
185
184
|
|
|
186
185
|
--ic=CALENDARS List of calendars to include
|
|
187
186
|
--ec=CALENDARS List of calendars to exclude
|
|
@@ -192,11 +191,10 @@ Including/excluding accounts, calendars, reminders and items:
|
|
|
192
191
|
--id Include completed reminders
|
|
193
192
|
--ed Exclude uncompleted reminders
|
|
194
193
|
|
|
195
|
-
--match=FIELD=REGEX Include only items whose FIELD matches
|
|
196
|
-
```
|
|
194
|
+
--match=FIELD=REGEX Include only items whose FIELD matches REGEX (ignoring case)
|
|
197
195
|
|
|
198
196
|
Choosing dates:
|
|
199
|
-
|
|
197
|
+
|
|
200
198
|
--from=DATE List events starting on or after DATE
|
|
201
199
|
--to=DATE List events starting on or before DATE
|
|
202
200
|
DATE can be yesterday, today, tomorrow, +N, -N, or anything accepted by DateTime.parse()
|
|
@@ -207,10 +205,9 @@ Choosing dates:
|
|
|
207
205
|
--sed Show empty dates with --sd
|
|
208
206
|
--ia Include only all-day events
|
|
209
207
|
--ea Exclude all-day events
|
|
210
|
-
```
|
|
211
208
|
|
|
212
209
|
Choose properties to include in the output:
|
|
213
|
-
|
|
210
|
+
|
|
214
211
|
--iep=PROPERTIES List of properties to include
|
|
215
212
|
--eep=PROPERTIES List of properties to exclude
|
|
216
213
|
--aep=PROPERTIES List of properties to include in addition to the default list
|
|
@@ -231,10 +228,9 @@ Choose properties to include in the output:
|
|
|
231
228
|
|
|
232
229
|
Use 'all' for PROPERTIES to include all available properties (except any listed in --eep)
|
|
233
230
|
Use 'list' for PROPERTIES to list all available properties and exit
|
|
234
|
-
```
|
|
235
231
|
|
|
236
232
|
Formatting the output:
|
|
237
|
-
|
|
233
|
+
|
|
238
234
|
--li=N Show at most N items (default: 0 for no limit)
|
|
239
235
|
|
|
240
236
|
--sc Separate by calendar
|
|
@@ -261,24 +257,21 @@ Formatting the output:
|
|
|
261
257
|
|
|
262
258
|
-f Format output using standard ANSI colors
|
|
263
259
|
--color Format output using a larger color palette
|
|
264
|
-
```
|
|
265
260
|
|
|
266
261
|
Help:
|
|
267
|
-
|
|
262
|
+
|
|
268
263
|
-h, --help Show this message
|
|
269
|
-
-V, -v, --version Show version and exit (3.
|
|
264
|
+
-V, -v, --version Show version and exit (3.10.0)
|
|
270
265
|
-d, --debug=LEVEL Set the logging level (default: warn)
|
|
271
266
|
[debug, info, warn, error, fatal]
|
|
272
|
-
```
|
|
273
267
|
|
|
274
268
|
Environment variables:
|
|
275
|
-
|
|
269
|
+
|
|
276
270
|
ICALPAL Additional arguments
|
|
277
271
|
ICALPAL_CONFIG Additional arguments from a file
|
|
278
272
|
(default: $HOME/.icalpal)
|
|
279
273
|
|
|
280
274
|
Do not quote or escape values. Options set in ICALPAL override ICALPAL_CONFIG. Options on the command line override ICALPAL.
|
|
281
|
-
```
|
|
282
275
|
|
|
283
276
|
## Output formats
|
|
284
277
|
|
|
@@ -295,35 +288,9 @@ properly formatted.
|
|
|
295
288
|
|
|
296
289
|
Other formats such as ANSI, HTML, Markdown, RDoc, and TOC, use Ruby's
|
|
297
290
|
[RDoc::Markup](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup.html)
|
|
298
|
-
framework to build and render the items.
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
[RDoc::Markup::Document](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Document.html).
|
|
302
|
-
|
|
303
|
-
When using one of the _separate by_ options, a section header is added
|
|
304
|
-
first. The section contains:
|
|
305
|
-
|
|
306
|
-
* [RDoc::Markup::BlankLine](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/BlankLine.html)
|
|
307
|
-
(unless this is the first section)
|
|
308
|
-
* RDoc::Markup::Heading (level 1)
|
|
309
|
-
* [RDoc::Markup::Rule](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Rule.html)
|
|
310
|
-
|
|
311
|
-
The rest of the document is a series of
|
|
312
|
-
[RDoc::Markup::List](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/List.html)
|
|
313
|
-
objects, one for each of the item's properties:
|
|
314
|
-
|
|
315
|
-
* [RDoc::Markup::List](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/List.html)
|
|
316
|
-
* RDoc::Markup::Heading (level 2)
|
|
317
|
-
* [RDoc::Markup::BlankLine](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/BlankLine.html)
|
|
318
|
-
* [RDoc::Markup::ListItem](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/ListItem.html)
|
|
319
|
-
* [RDoc::Markup::Paragraph](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Paragraph.html)
|
|
320
|
-
|
|
321
|
-
The document will also include a number of
|
|
322
|
-
[RDoc::Markup::Verbatim](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Verbatim.html)
|
|
323
|
-
and
|
|
324
|
-
[RDoc::Markup::Raw](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Raw.html)
|
|
325
|
-
items. They are not included in the output, but are used to pass
|
|
326
|
-
information about the item and property to the default formatter.
|
|
291
|
+
framework to build and render the items. See
|
|
292
|
+
[RDoc](https://github.com/ajrosen/icalPal/blob/main/RDoc.md) for a
|
|
293
|
+
breakdown of how icalPal uses RDoc::Markup.
|
|
327
294
|
|
|
328
295
|
## History
|
|
329
296
|
|
|
@@ -341,9 +308,14 @@ Lawton](https://github.com/jimlawton) that it even compiles anymore.
|
|
|
341
308
|
Instead of trying to understand and extend the existing code, I chose
|
|
342
309
|
to start anew using my language of choice:
|
|
343
310
|
[Ruby](https://www.ruby-lang.org). Using Ruby meant there is *much*
|
|
344
|
-
less code; a little over 2,
|
|
311
|
+
less code; a little over 2,200 lines vs. 7,000. It also means icalPal
|
|
345
312
|
is multi-platform.
|
|
346
313
|
|
|
314
|
+
Because icalPal is written in Ruby, and not a native Mac application,
|
|
315
|
+
you can run it just about anywhere. It's been tested with the
|
|
316
|
+
versions of Ruby included with macOS Sequoia and Tahoe (2.6.10) and
|
|
317
|
+
[Homebrew](https://brew.sh/) (3.4.x).
|
|
318
|
+
|
|
347
319
|
I won't pretend to understand **why** you would want to run this on
|
|
348
320
|
Linux or Windows. But since icalPal is written in Ruby and gets its
|
|
349
321
|
data directly from the Calendar and Reminders database files instead
|
data/bin/icalPal
CHANGED
|
@@ -231,14 +231,14 @@ end
|
|
|
231
231
|
|
|
232
232
|
# Sort the rows
|
|
233
233
|
begin
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
234
|
+
$sort_attrs = []
|
|
235
|
+
$sort_attrs.push $opts[:sep] if $opts[:sep]
|
|
236
|
+
$sort_attrs.push $opts[:sort] if $opts[:sort]
|
|
237
|
+
$sort_attrs.push 'sdate'
|
|
238
238
|
|
|
239
|
-
$log.info("Sorting #{$items.count} items by #{
|
|
239
|
+
$log.info("Sorting #{$items.count} items by #{$sort_attrs}, reverse #{$opts[:reverse].inspect}")
|
|
240
240
|
|
|
241
|
-
$items.
|
|
241
|
+
$items.sort!
|
|
242
242
|
$items.reverse! if $opts[:reverse]
|
|
243
243
|
rescue Exception => e
|
|
244
244
|
$log.info("Sorting failed: #{e}\n")
|
|
@@ -321,8 +321,10 @@ items.each_with_index do |i, j|
|
|
|
321
321
|
doc << RDoc::Markup::Raw.new($opts[:sep])
|
|
322
322
|
|
|
323
323
|
doc << RDoc::Markup::BlankLine.new if j.positive?
|
|
324
|
-
|
|
325
|
-
|
|
324
|
+
if i[$opts[:sep]]
|
|
325
|
+
doc << RDoc::Markup::Heading.new(1, i[$opts[:sep]].to_s)
|
|
326
|
+
doc << RDoc::Markup::Rule.new(0)
|
|
327
|
+
end
|
|
326
328
|
|
|
327
329
|
section = i[$opts[:sep]]
|
|
328
330
|
end
|
data/bin/icalpal
CHANGED
|
@@ -231,14 +231,14 @@ end
|
|
|
231
231
|
|
|
232
232
|
# Sort the rows
|
|
233
233
|
begin
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
234
|
+
$sort_attrs = []
|
|
235
|
+
$sort_attrs.push $opts[:sep] if $opts[:sep]
|
|
236
|
+
$sort_attrs.push $opts[:sort] if $opts[:sort]
|
|
237
|
+
$sort_attrs.push 'sdate'
|
|
238
238
|
|
|
239
|
-
$log.info("Sorting #{$items.count} items by #{
|
|
239
|
+
$log.info("Sorting #{$items.count} items by #{$sort_attrs}, reverse #{$opts[:reverse].inspect}")
|
|
240
240
|
|
|
241
|
-
$items.
|
|
241
|
+
$items.sort!
|
|
242
242
|
$items.reverse! if $opts[:reverse]
|
|
243
243
|
rescue Exception => e
|
|
244
244
|
$log.info("Sorting failed: #{e}\n")
|
|
@@ -321,8 +321,10 @@ items.each_with_index do |i, j|
|
|
|
321
321
|
doc << RDoc::Markup::Raw.new($opts[:sep])
|
|
322
322
|
|
|
323
323
|
doc << RDoc::Markup::BlankLine.new if j.positive?
|
|
324
|
-
|
|
325
|
-
|
|
324
|
+
if i[$opts[:sep]]
|
|
325
|
+
doc << RDoc::Markup::Heading.new(1, i[$opts[:sep]].to_s)
|
|
326
|
+
doc << RDoc::Markup::Rule.new(0)
|
|
327
|
+
end
|
|
326
328
|
|
|
327
329
|
section = i[$opts[:sep]]
|
|
328
330
|
end
|
data/lib/calendar.rb
CHANGED
|
@@ -12,6 +12,12 @@ module ICalPal
|
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
def initialize(obj)
|
|
16
|
+
super
|
|
17
|
+
|
|
18
|
+
@self['sharees'] = JSON.parse(obj['sharees'])
|
|
19
|
+
end
|
|
20
|
+
|
|
15
21
|
QUERY = <<~SQL.freeze
|
|
16
22
|
SELECT DISTINCT
|
|
17
23
|
|
|
@@ -23,6 +29,8 @@ c1.title AS calendar,
|
|
|
23
29
|
c1.shared_owner_name,
|
|
24
30
|
c1.shared_owner_address,
|
|
25
31
|
|
|
32
|
+
json_group_array(i1.display_name) AS sharees,
|
|
33
|
+
|
|
26
34
|
c1.published_URL,
|
|
27
35
|
c1.self_identity_email,
|
|
28
36
|
c1.owner_identity_email,
|
|
@@ -34,10 +42,14 @@ c1.locale
|
|
|
34
42
|
FROM #{self.name.split('::').last} c1
|
|
35
43
|
|
|
36
44
|
JOIN Store s1 ON c1.store_id = s1.rowid
|
|
45
|
+
LEFT OUTER JOIN Sharee s2 ON c1.rowid = s2.owner_id
|
|
46
|
+
LEFT OUTER JOIN Identity i1 ON s2.identity_id = i1.rowid
|
|
37
47
|
|
|
38
48
|
WHERE s1.disabled IS NOT 1
|
|
39
49
|
AND s1.display_order IS NOT -1
|
|
40
50
|
AND c1.flags IS NOT 519
|
|
51
|
+
|
|
52
|
+
GROUP BY c1.title
|
|
41
53
|
SQL
|
|
42
54
|
|
|
43
55
|
end
|
data/lib/defaults.rb
CHANGED
|
@@ -49,31 +49,6 @@ $defaults = {
|
|
|
49
49
|
sort: 'prio',
|
|
50
50
|
},
|
|
51
51
|
|
|
52
|
-
undatedTasks: {
|
|
53
|
-
dated: 1,
|
|
54
|
-
db: [ ICalPal::Reminder::DB_PATH ],
|
|
55
|
-
iep: %w[ title notes due priority ],
|
|
56
|
-
ps: [ "\n " ],
|
|
57
|
-
sort: 'prio',
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
datedTasks: {
|
|
61
|
-
dated: 2,
|
|
62
|
-
db: [ ICalPal::Reminder::DB_PATH ],
|
|
63
|
-
iep: %w[ title notes due priority ],
|
|
64
|
-
ps: [ "\n " ],
|
|
65
|
-
sort: 'prio',
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
tasksDueBefore: {
|
|
69
|
-
dated: 3,
|
|
70
|
-
db: [ ICalPal::Reminder::DB_PATH ],
|
|
71
|
-
iep: %w[ title notes due priority ],
|
|
72
|
-
ps: [ "\n " ],
|
|
73
|
-
sort: 'prio',
|
|
74
|
-
to: $today,
|
|
75
|
-
},
|
|
76
|
-
|
|
77
52
|
accounts: {
|
|
78
53
|
iep: %w[ account type ],
|
|
79
54
|
sort: 'account',
|
data/lib/event.rb
CHANGED
|
@@ -16,14 +16,14 @@ module ICalPal
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# Standard accessor with special handling for +age+,
|
|
19
|
-
# +availability+, +datetime+, +location+, +notes+, +
|
|
20
|
-
# +
|
|
19
|
+
# +availability+, +datetime+, +location+, +notes+, +sday+,
|
|
20
|
+
# +status+, +uid+, and +event+/+name+/+title+
|
|
21
21
|
#
|
|
22
22
|
# @param k [String] Key/property name
|
|
23
23
|
def [](k)
|
|
24
24
|
case k
|
|
25
25
|
when 'age' # pseudo-property
|
|
26
|
-
@self['sdate'].year - @self['
|
|
26
|
+
@self['sdate'].year - Time.at(@self['start_date'] + ITIME).year
|
|
27
27
|
|
|
28
28
|
when 'availability' # Integer -> String
|
|
29
29
|
EventKit::EKEventAvailability.select { |_k, v| v == @self['availability'] }.keys
|
|
@@ -48,7 +48,7 @@ module ICalPal
|
|
|
48
48
|
(@self['notes'])? @self['notes'].strip.gsub("\n", $opts[:nnr]) : nil
|
|
49
49
|
|
|
50
50
|
when 'sday' # pseudo-property
|
|
51
|
-
@self['sdate'].day_start
|
|
51
|
+
@self['sdate'].day_start(0)
|
|
52
52
|
|
|
53
53
|
when 'status' # Integer -> String
|
|
54
54
|
EventKit::EKEventStatus.select { |_k, v| v == @self['status'] }.keys[0]
|
|
@@ -76,7 +76,7 @@ module ICalPal
|
|
|
76
76
|
'sdate' => obj,
|
|
77
77
|
'placeholder' => true,
|
|
78
78
|
'title' => 'Nothing.',
|
|
79
|
-
} if obj.is_a?(DateTime)
|
|
79
|
+
} if $opts[:sed] && obj.is_a?(DateTime)
|
|
80
80
|
|
|
81
81
|
super
|
|
82
82
|
|
|
@@ -90,10 +90,13 @@ module ICalPal
|
|
|
90
90
|
obj.keys.select { |i| i.end_with? '_date' }.each do |k|
|
|
91
91
|
next unless obj[k]
|
|
92
92
|
|
|
93
|
+
zone = nil
|
|
94
|
+
zone = '+00:00' if obj['all_day'].positive?
|
|
95
|
+
|
|
93
96
|
# Save as seconds, Time, RDT
|
|
94
97
|
ctime = obj[k] + ITIME
|
|
95
|
-
ctime -=
|
|
96
|
-
ttime = Time.at(ctime)
|
|
98
|
+
ctime -= Time.at(ctime).utc_offset if obj["#{k}_tz"] == '_float' && !zone
|
|
99
|
+
ttime = Time.at(ctime, in: zone)
|
|
97
100
|
|
|
98
101
|
@self["#{k[0]}seconds"] = ctime
|
|
99
102
|
@self["#{k[0]}ctime"] = ttime
|
|
@@ -173,7 +176,7 @@ module ICalPal
|
|
|
173
176
|
skip = true if codate == odate
|
|
174
177
|
end
|
|
175
178
|
|
|
176
|
-
events.push(clone(occurrence)) if in_window?(occurrence['sdate'], occurrence['edate'])
|
|
179
|
+
events.push(clone(occurrence)) if !skip && in_window?(occurrence['sdate'], occurrence['edate'])
|
|
177
180
|
end
|
|
178
181
|
|
|
179
182
|
# Handle frequency and interval
|
|
@@ -261,11 +264,16 @@ module ICalPal
|
|
|
261
264
|
c['sdate'] = ICalPal.nth(nth, day, nsdate)
|
|
262
265
|
c['edate'] = ICalPal.nth(nth, day, nedate)
|
|
263
266
|
else
|
|
264
|
-
|
|
265
|
-
|
|
267
|
+
%w[ sdate edate ].each do |d|
|
|
268
|
+
diff = day - c['sdate'].wday
|
|
269
|
+
diff += 7 if diff.negative?
|
|
270
|
+
|
|
271
|
+
t1 = Time.at(c[d].to_time)
|
|
272
|
+
t2 = Time.at(t1.to_i + (diff * 86_400))
|
|
273
|
+
t2 += (t1.gmt_offset - t2.gmt_offset)
|
|
266
274
|
|
|
267
|
-
|
|
268
|
-
|
|
275
|
+
c[d] = RDT.from_time(t2)
|
|
276
|
+
end
|
|
269
277
|
end
|
|
270
278
|
|
|
271
279
|
o.push(clone(c)) if in_window?(c['sdate'], c['edate'])
|
|
@@ -277,18 +285,18 @@ module ICalPal
|
|
|
277
285
|
|
|
278
286
|
# Apply frequency and interval
|
|
279
287
|
def apply_frequency!
|
|
280
|
-
|
|
281
|
-
dates = [ 'sdate' ]
|
|
282
|
-
dates << 'edate' unless self['calendar'].include?('Birthday')
|
|
283
|
-
|
|
284
|
-
dates.each do |d|
|
|
288
|
+
%w[ sdate edate ].each do |d|
|
|
285
289
|
case EventKit::EKRecurrenceFrequency[self['frequency'] - 1]
|
|
286
|
-
when 'daily' then self[d]
|
|
287
|
-
when 'weekly' then self[d]
|
|
288
|
-
when 'monthly' then self[d]
|
|
289
|
-
when 'yearly' then self[d]
|
|
290
|
+
when 'daily' then nd = self[d] + self['interval']
|
|
291
|
+
when 'weekly' then nd = self[d] + (self['interval'] * 7)
|
|
292
|
+
when 'monthly' then nd = self[d] >> self['interval']
|
|
293
|
+
when 'yearly' then nd = self[d] >> (self['interval'] * 12)
|
|
290
294
|
else $log.error("Unknown frequency: #{self['frequency']}")
|
|
291
295
|
end
|
|
296
|
+
|
|
297
|
+
# Create a new Time object in case we crossed a daylight saving change
|
|
298
|
+
t = Time.parse("#{nd.year}-#{nd.month}-#{nd.day} #{nd.hour}:#{nd.min}:#{nd.sec}")
|
|
299
|
+
self[d] = RDT.from_time(t)
|
|
292
300
|
end
|
|
293
301
|
end
|
|
294
302
|
|
|
@@ -307,8 +315,8 @@ module ICalPal
|
|
|
307
315
|
$log.debug("not now: #{s} to #{e} vs. #{$now}")
|
|
308
316
|
false
|
|
309
317
|
end
|
|
310
|
-
elsif ([ s, e ].max >= $opts[:from]
|
|
311
|
-
$log.debug("in window: #{s} to #{e} vs. #{$opts[:from]} to #{$opts[:to]}")
|
|
318
|
+
elsif (s < $opts[:to] && [ s, e ].max >= $opts[:from])
|
|
319
|
+
$log.debug("#{@self['title']} in window: #{s} to #{e} vs. #{$opts[:from]} to #{$opts[:to]}")
|
|
312
320
|
true
|
|
313
321
|
else
|
|
314
322
|
$log.debug("not in window: #{s} to #{e} vs. #{$opts[:from]} to #{$opts[:to]}")
|
|
@@ -335,6 +343,7 @@ CalendarItem.all_day,
|
|
|
335
343
|
CalendarItem.availability,
|
|
336
344
|
CalendarItem.conference_url_detected,
|
|
337
345
|
CalendarItem.description AS notes,
|
|
346
|
+
CalendarItem.end_tz,
|
|
338
347
|
CalendarItem.has_recurrences,
|
|
339
348
|
CalendarItem.invitation_status,
|
|
340
349
|
CalendarItem.orig_item_id,
|
data/lib/icalPal.rb
CHANGED
|
@@ -149,6 +149,25 @@ module ICalPal
|
|
|
149
149
|
@self.values
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
+
# @see Array.<=>
|
|
153
|
+
#
|
|
154
|
+
# If either self or other is nil, but not both, the nil object is
|
|
155
|
+
# always less than
|
|
156
|
+
def <=>(other)
|
|
157
|
+
$sort_attrs.each do |s|
|
|
158
|
+
next if self[s] == other[s]
|
|
159
|
+
|
|
160
|
+
# nil is always less than
|
|
161
|
+
return -1 if other[s].nil?
|
|
162
|
+
return 1 if self[s].nil?
|
|
163
|
+
|
|
164
|
+
return -1 if self[s] < other[s]
|
|
165
|
+
return 1 if self[s] > other[s]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
0
|
|
169
|
+
end
|
|
170
|
+
|
|
152
171
|
# Like inspect, but easier for humans to read
|
|
153
172
|
#
|
|
154
173
|
# @return [Array<String>] @self as a key=value array, sorted by key
|
data/lib/options.rb
CHANGED
|
@@ -268,17 +268,29 @@ module ICalPal
|
|
|
268
268
|
|
|
269
269
|
when 'Remaining'
|
|
270
270
|
cli[:from] = RDT.from_time($now)
|
|
271
|
-
cli[:to] = $today.day_end
|
|
271
|
+
cli[:to] = $today.day_end(0)
|
|
272
272
|
cli[:days] = 1
|
|
273
273
|
end
|
|
274
274
|
end
|
|
275
275
|
|
|
276
276
|
# Handle tasks command variants
|
|
277
|
-
if cli[:cmd]
|
|
278
|
-
|
|
279
|
-
|
|
277
|
+
if cli[:cmd] =~ /tasks/i
|
|
278
|
+
case cli[:cmd]
|
|
279
|
+
when 'undatedTasks'
|
|
280
|
+
cli[:dated] = 1
|
|
281
|
+
|
|
282
|
+
when 'datedTasks'
|
|
283
|
+
cli[:dated] = 2
|
|
284
|
+
|
|
285
|
+
when 'tasksDueBefore'
|
|
286
|
+
cli[:dated] = 3
|
|
287
|
+
cli.delete(:days) unless cli[:days]
|
|
288
|
+
cli[:from] = RDT.from_epoch(0) unless cli[:from]
|
|
289
|
+
cli[:to] = $today unless cli[:to]
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
cli[:cmd] = 'tasks'
|
|
280
293
|
end
|
|
281
|
-
cli[:cmd] = 'tasks' if %w[ datedTasks undatedTasks tasksDueBefore ].include? cli[:cmd]
|
|
282
294
|
|
|
283
295
|
# Must have a valid command
|
|
284
296
|
raise(OptionParser::InvalidArgument, "Unknown COMMAND #{cli[:cmd]}") unless (COMMANDS.any? cli[:cmd])
|
data/lib/rdt.rb
CHANGED
|
@@ -68,14 +68,26 @@ module ICalPal
|
|
|
68
68
|
to_time.to_i
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
+
# @param [Integer] Optional UTC offset
|
|
71
72
|
# @return [RDT] Self at 00:00:00
|
|
72
|
-
def day_start
|
|
73
|
-
RDT.new(year, month, day, 0, 0, 0,
|
|
73
|
+
def day_start(z = zone)
|
|
74
|
+
RDT.new(year, month, day, 0, 0, 0, z)
|
|
74
75
|
end
|
|
75
76
|
|
|
77
|
+
# @param [Integer] Optional UTC offset
|
|
76
78
|
# @return [RDT] Self at 23:59:59
|
|
77
|
-
def day_end
|
|
78
|
-
RDT.new(year, month, day, 23, 59, 59,
|
|
79
|
+
def day_end(z = zone)
|
|
80
|
+
RDT.new(year, month, day, 23, 59, 59, z)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @return [Array] Only the year, month and day of self
|
|
84
|
+
def ymd
|
|
85
|
+
[ year, month, day ]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @return [Array] Only the hour, min and sec of self
|
|
89
|
+
def hms
|
|
90
|
+
[ hour, min, sec ]
|
|
79
91
|
end
|
|
80
92
|
|
|
81
93
|
# @return [Array] Only the year, month and day of self
|
data/lib/reminder.rb
CHANGED
|
@@ -56,6 +56,12 @@ module ICalPal
|
|
|
56
56
|
when 'sdate' # For sorting
|
|
57
57
|
@self['due_date']
|
|
58
58
|
|
|
59
|
+
when 'sday'
|
|
60
|
+
@self['due'].day_start(0) if @self['due']
|
|
61
|
+
|
|
62
|
+
when 'section'
|
|
63
|
+
(@self['section'])? @self['section'] : 'Others'
|
|
64
|
+
|
|
59
65
|
when 'name', 'reminder', 'task' # Aliases
|
|
60
66
|
@self['title']
|
|
61
67
|
|
|
@@ -67,11 +73,13 @@ module ICalPal
|
|
|
67
73
|
super
|
|
68
74
|
|
|
69
75
|
# Convert JSON arrays to Arrays
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
%w[ assignee tags ].each do |a|
|
|
77
|
+
@self[a] = JSON.parse(obj[a]) if obj[a]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
%w[ location proximity radius ].each do |a|
|
|
81
|
+
@self[a] = JSON.parse(obj[a]).compact.uniq[0] if obj[a]
|
|
82
|
+
end
|
|
75
83
|
|
|
76
84
|
# Section
|
|
77
85
|
if @self['members']
|
|
@@ -141,6 +149,30 @@ module ICalPal
|
|
|
141
149
|
@self['messaging'] = messaging
|
|
142
150
|
end
|
|
143
151
|
|
|
152
|
+
# @see ICalPal.<=>
|
|
153
|
+
#
|
|
154
|
+
# When comparing sections, "Others" always goes last
|
|
155
|
+
def <=>(other)
|
|
156
|
+
$sort_attrs.each do |s|
|
|
157
|
+
next if self[s] == other[s]
|
|
158
|
+
|
|
159
|
+
# nil is always less than
|
|
160
|
+
return -1 if other[s].nil?
|
|
161
|
+
return 1 if self[s].nil?
|
|
162
|
+
|
|
163
|
+
if s == 'section'
|
|
164
|
+
# Section "Others" always goes last
|
|
165
|
+
return -1 if other[s] == 'Others'
|
|
166
|
+
return 1 if self[s] == 'Others'
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
return -1 if self[s] < other[s]
|
|
170
|
+
return 1 if self[s] > other[s]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
0
|
|
174
|
+
end
|
|
175
|
+
|
|
144
176
|
DEFAULT_COLOR = '#1BADF8'.freeze
|
|
145
177
|
DEFAULT_SYMBOLIC_COLOR = 'blue'.freeze
|
|
146
178
|
|
data/lib/store.rb
CHANGED
data/lib/version.rb
CHANGED