icalPal 1.2.1 → 2.0.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 +53 -20
- data/bin/icalPal +64 -48
- data/icalPal.gemspec +4 -1
- data/lib/EventKit.rb +7 -0
- data/lib/ToICalPal.rb +43 -17
- data/lib/defaults.rb +21 -5
- data/lib/icalPal.rb +62 -3
- data/lib/options.rb +40 -25
- data/lib/reminder.rb +110 -0
- data/lib/version.rb +3 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f91434d02dd357bcd02342b2d79418b91e23ec789a6a069a01f2f508685d8ca
|
4
|
+
data.tar.gz: 51cfa58c767650d0c7e62213ad4a58dbe9a2d5fc42f17d5ce18c8b65e6a178be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20b4a10406535baf33986bd6bdf859376a6dfd7055815b2f263d6971d6e8255d5d60fa019c49c4de54ec3a4178e3752136478aafa5937176c80adfdfc36cc8aa
|
7
|
+
data.tar.gz: 8a5fbde979186c3347ed6c8c52f3b39b81fc6b303680e1923c0b011583b1c63651dc65b33e4e34a3794b5d01430cb7bd4dd66b2f12510374f13626e438aef079
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
icalPal is a command-line tool to query a macOS Calendar database for
|
8
8
|
accounts, calendars, and events. It can be run on any system with
|
9
9
|
[Ruby](https://www.ruby-lang.org/) and access to a Calendar database
|
10
|
-
file.
|
10
|
+
file, or a Reminders database.
|
11
11
|
|
12
12
|
## Installation
|
13
13
|
|
@@ -25,7 +25,9 @@ and for output. There are a few differences to be aware of.
|
|
25
25
|
|
26
26
|
* Options require two hyphens, except for single-letter options that require one hyphen
|
27
27
|
* *eventsFrom* is not supported. Instead there is *--from*, *--to*, and *--days*
|
28
|
-
*
|
28
|
+
* *uncompletedTasks* is simply *tasks*
|
29
|
+
* *undatedUncompletedTasks* is simply *undatedTasks*
|
30
|
+
* *tasksDueBefore:DATE* is not yet supported
|
29
31
|
* The command can go anywhere; it doesn't have to be the last argument
|
30
32
|
* Property separators are comma-delimited
|
31
33
|
|
@@ -35,6 +37,10 @@ and for output. There are a few differences to be aware of.
|
|
35
37
|
|
36
38
|
Shows a list of enabled Calendar accounts. Internally they are known as *Stores*; you can run ```icalPal stores``` instead.
|
37
39
|
|
40
|
+
```icalPal datedTasks```
|
41
|
+
|
42
|
+
Shows only reminders that have a due date.
|
43
|
+
|
38
44
|
### Additional options
|
39
45
|
|
40
46
|
* Options can be abbreviated, so long as they are unique. Eg., ```icalPal -c ev --da 3``` is the same as ```icalPal -c events --days 3```.
|
@@ -42,37 +48,45 @@ Shows a list of enabled Calendar accounts. Internally they are known as *Stores
|
|
42
48
|
* Use ```-o``` to print the output in different formats. CSV or JSON are intertesting choices.
|
43
49
|
* Copy your Calendar database file and use ```--db``` on it.
|
44
50
|
* ```--it``` and ```--et``` will filter by Calendar *type*. Types are **Local**, **Exchange**, **CalDAV**, **MobileMe**, **Subscribed**, and **Birthdays**
|
51
|
+
* ```--il``` and ```-el``` will filter by Reminder list
|
45
52
|
* ```--ia``` includes *only* all-day events (opposite of ```--ea```)
|
46
53
|
* ```--aep``` is like ```--iep```, but *adds* to the default property list instead of replacing it.
|
47
54
|
* ```--sep``` to separate by any property, not just calendar (```--sc```) or date (```--sd```)
|
48
55
|
* ```--color``` uses a wider color palette. Calendar colors are what you have chosen in the Calendar app. Not supported in all terminals, but looks great in [iTerm2](https://iterm2.com/).
|
49
56
|
|
50
|
-
Because icalPal is written in Ruby, and not a native Mac application, you can run it just about anywhere. It's been tested with version of Ruby (2.6.10) included with macOS
|
57
|
+
Because icalPal is written in Ruby, and not a native Mac application, you can run it just about anywhere. It's been tested with the version of Ruby (2.6.10) included with macOS.
|
51
58
|
|
52
59
|
## Usage
|
53
60
|
|
54
61
|
icalPal: Usage: icalPal [options] [-c] COMMAND
|
55
62
|
|
56
63
|
COMMAND must be one of the following:
|
57
|
-
|
64
|
+
```
|
58
65
|
events Print events
|
66
|
+
tasks Print tasks
|
59
67
|
calendars Print calendars
|
60
68
|
accounts Print accounts
|
61
69
|
|
62
70
|
eventsToday Print events occurring today
|
63
71
|
eventsToday+NUM Print events occurring between today and NUM days into the future
|
64
72
|
eventsNow Print events occurring at present time
|
73
|
+
datedTasks Print tasks with a due date
|
74
|
+
undatedTasks Print tasks with no due date
|
75
|
+
```
|
65
76
|
|
66
77
|
Global options:
|
67
|
-
|
78
|
+
```
|
68
79
|
-c, --cmd=COMMAND Command to run
|
69
|
-
--db=DB Use DB file instead of Calendar
|
70
|
-
|
80
|
+
--db=DB Use DB file instead of Calendar (default: /Users/ajr/Library/Calendars/Calendar.sqlitedb)
|
81
|
+
For the tasks commands this should be a directory containing .sqlite files
|
82
|
+
(default: /Users/ajr/Library/Group Containers/group.com.apple.reminders/Container_v1/Stores)
|
83
|
+
--cf=FILE Set config file path (default: /Users/ajr/.icalPal)
|
71
84
|
-o, --output=FORMAT Print as FORMAT (default: default)
|
72
|
-
[ansi, csv, default, hash, html, json, md, rdoc, toc,
|
73
|
-
|
74
|
-
Including/excluding calendars:
|
85
|
+
[ansi, csv, default, hash, html, json, md, rdoc, remind, toc, xml, yaml]
|
86
|
+
```
|
75
87
|
|
88
|
+
Including/excluding calendars and reminders:
|
89
|
+
```
|
76
90
|
--is=ACCOUNTS List of accounts to include
|
77
91
|
--es=ACCOUNTS List of accounts to exclude
|
78
92
|
|
@@ -83,8 +97,12 @@ Including/excluding calendars:
|
|
83
97
|
--ic=CALENDARS List of calendars to include
|
84
98
|
--ec=CALENDARS List of calendars to exclude
|
85
99
|
|
86
|
-
|
100
|
+
--il=LISTS List of reminder lists to include
|
101
|
+
--el=LISTS List of reminder lists to exclude
|
102
|
+
```
|
87
103
|
|
104
|
+
Choosing dates:
|
105
|
+
```
|
88
106
|
--from=DATE List events starting on or after DATE
|
89
107
|
--to=DATE List events starting on or before DATE
|
90
108
|
DATE can be yesterday, today, tomorrow, +N, -N, or anything accepted by DateTime.parse()
|
@@ -95,13 +113,19 @@ Choosing dates:
|
|
95
113
|
--sed Show empty dates with --sd
|
96
114
|
--ia Include only all-day events
|
97
115
|
--ea Exclude all-day events
|
116
|
+
```
|
98
117
|
|
99
118
|
Choose properties to include in the output:
|
100
|
-
|
119
|
+
```
|
101
120
|
--iep=PROPERTIES List of properties to include
|
102
121
|
--eep=PROPERTIES List of properties to exclude
|
103
122
|
--aep=PROPERTIES List of properties to include in addition to the default list
|
104
123
|
|
124
|
+
--itp=PROPERTIES List of task properties to include
|
125
|
+
--etp=PROPERTIES List of task properties to exclude
|
126
|
+
--atp=PROPERTIES List of task properties to include in addition to the default list
|
127
|
+
Included for backwards compatability, these are aliases for --iep, --eep, and --aep
|
128
|
+
|
105
129
|
--uid Show event UIDs
|
106
130
|
--eed Exclude end datetimes
|
107
131
|
|
@@ -113,16 +137,20 @@ Choose properties to include in the output:
|
|
113
137
|
|
114
138
|
Use 'all' for PROPERTIES to include all available properties (except any listed in --eep)
|
115
139
|
Use 'list' for PROPERTIES to list all available properties and exit
|
140
|
+
```
|
116
141
|
|
117
142
|
Formatting the output:
|
118
|
-
|
143
|
+
```
|
119
144
|
--li=N Show at most N items (default: 0 for no limit)
|
120
145
|
|
121
146
|
--sc Separate by calendar
|
122
147
|
--sd Separate by date
|
148
|
+
--sp Separate by priority
|
123
149
|
--sep=PROPERTY Separate by PROPERTY
|
124
150
|
|
125
151
|
--sort=PROPERTY Sort by PROPERTY
|
152
|
+
--std Sort tasks by due date (same as --sort=due_date)
|
153
|
+
--stda Sort tasks by due date (ascending) (same as --sort=due_date -r)
|
126
154
|
-r, --reverse Sort in reverse
|
127
155
|
|
128
156
|
--ps=SEPARATORS List of property separators
|
@@ -133,24 +161,28 @@ Formatting the output:
|
|
133
161
|
See https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-i-strftime for details
|
134
162
|
|
135
163
|
-b, --bullet=STRING Use STRING for bullets
|
164
|
+
--ab=STRING Use STRING for alert bullets
|
165
|
+
--nb Do not use bullets
|
136
166
|
--nnr=SEPARATOR Set replacement for newlines within notes
|
137
167
|
|
138
168
|
-f Format output using standard ANSI colors
|
139
169
|
--color Format output using a larger color palette
|
170
|
+
```
|
140
171
|
|
141
172
|
Help:
|
142
|
-
|
173
|
+
```
|
143
174
|
-h, --help Show this message
|
144
|
-
-V, -v, --version Show version and exit (
|
175
|
+
-V, -v, --version Show version and exit (2.0.0)
|
145
176
|
-d, --debug=LEVEL Set the logging level (default: warn)
|
146
177
|
[debug, info, warn, error, fatal]
|
178
|
+
```
|
147
179
|
|
148
180
|
Environment variables:
|
149
|
-
|
181
|
+
```
|
150
182
|
ICALPAL Additional arguments
|
151
183
|
ICALPAL_CONFIG Additional arguments from a file
|
152
|
-
(default:
|
153
|
-
|
184
|
+
(default: /Users/ajr/.icalPal)
|
185
|
+
```
|
154
186
|
|
155
187
|
## History
|
156
188
|
|
@@ -178,8 +210,9 @@ directly from the Calendar database file instead of an API, you *can*.
|
|
178
210
|
icalPal supports several output formats. The **default** format tries
|
179
211
|
to mimic icalBuddy as much as possible.
|
180
212
|
|
181
|
-
CSV, Hash, JSON, and YAML print all fields for all items in their
|
182
|
-
respective formats. From that you can analyze the results any way you
|
213
|
+
CSV, Hash, JSON, XML, and YAML print all fields for all items in their
|
214
|
+
respective formats. From that you can analyze the results any way you
|
215
|
+
like.
|
183
216
|
|
184
217
|
[Remind](https://dianne.skoll.ca/projects/remind/) format uses a minimal implementation built in icalPal.
|
185
218
|
|
data/bin/icalPal
CHANGED
@@ -44,7 +44,7 @@ $log.info("Options: #{$opts}")
|
|
44
44
|
def add(item)
|
45
45
|
$log.info("Adding #{item.inspect} #{item['UUID']} (#{item['title']})") if item['UUID']
|
46
46
|
|
47
|
-
item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if item['sdate']
|
47
|
+
item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if item === ICalPal::Event && item['sdate']
|
48
48
|
$items.push(item)
|
49
49
|
end
|
50
50
|
|
@@ -54,32 +54,17 @@ end
|
|
54
54
|
|
55
55
|
# What are we getting?
|
56
56
|
klass = ICalPal::($opts[:cmd])
|
57
|
-
q = klass::QUERY
|
58
|
-
|
59
|
-
$log.debug(q.gsub(/\n/, ' '))
|
60
57
|
|
61
58
|
# Get it
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
stmt.execute.each_with_index { |i, j| $rows[j] = i }
|
69
|
-
stmt.close
|
70
|
-
|
71
|
-
# Close the database
|
72
|
-
$db.close
|
73
|
-
$log.debug("Closed #{$opts[:db]}")
|
74
|
-
|
75
|
-
rescue SQLite3::BusyException => e
|
76
|
-
$log.error("Non-fatal error closing database #{$db.filename}")
|
77
|
-
|
78
|
-
rescue SQLite3::SQLException => e
|
79
|
-
abort(e.message)
|
59
|
+
if klass == ICalPal::Reminder then
|
60
|
+
# Load all .sqlite files
|
61
|
+
Dir.glob("#{$opts[:db]}/*.sqlite").each { |db| $rows += ICalPal.load_data(db, klass::QUERY) }
|
62
|
+
else
|
63
|
+
# Load database
|
64
|
+
$rows += ICalPal.load_data($opts[:db], klass::QUERY)
|
80
65
|
end
|
81
66
|
|
82
|
-
$log.info("Loaded #{$rows.count}
|
67
|
+
$log.info("Loaded #{$rows.count} #{klass} rows")
|
83
68
|
|
84
69
|
|
85
70
|
##################################################
|
@@ -111,6 +96,7 @@ $rows.each_with_index do |row, i|
|
|
111
96
|
next
|
112
97
|
end
|
113
98
|
|
99
|
+
# Instantiate an item
|
114
100
|
item = klass.new(row)
|
115
101
|
|
116
102
|
# --et/--it
|
@@ -124,11 +110,18 @@ $rows.each_with_index do |row, i|
|
|
124
110
|
next
|
125
111
|
end
|
126
112
|
|
127
|
-
|
128
|
-
|
129
|
-
$log.debug("
|
130
|
-
|
131
|
-
|
113
|
+
# --el/--il
|
114
|
+
if $opts[:el].any? item['list_name'] then
|
115
|
+
$log.debug(":el")
|
116
|
+
next
|
117
|
+
end
|
118
|
+
|
119
|
+
unless $opts[:il].empty? or $opts[:il].any? item['list_name']
|
120
|
+
$log.debug(":il")
|
121
|
+
next
|
122
|
+
end
|
123
|
+
|
124
|
+
if ICalPal::Event === item
|
132
125
|
# Check for all-day and cancelled events
|
133
126
|
if $opts[:ea] && item['all_day'].positive? then
|
134
127
|
$log.debug(":ea")
|
@@ -148,6 +141,21 @@ $rows.each_with_index do |row, i|
|
|
148
141
|
(item['has_recurrences'].positive?)?
|
149
142
|
item.recurring.each { |i| add(i) } :
|
150
143
|
item.non_recurring.each { |i| add(i) }
|
144
|
+
else
|
145
|
+
# Check for dated reminders
|
146
|
+
if ICalPal::Reminder === item then
|
147
|
+
if $opts[:dated] == 1 and item['due_date'] > 0 then
|
148
|
+
$log.debug(":undated")
|
149
|
+
next
|
150
|
+
end
|
151
|
+
|
152
|
+
if $opts[:dated] == 2 and item['due_date'] == 0 then
|
153
|
+
$log.debug(":dated")
|
154
|
+
next
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
add(item)
|
151
159
|
end
|
152
160
|
end
|
153
161
|
|
@@ -163,13 +171,13 @@ end
|
|
163
171
|
|
164
172
|
# Sort the rows
|
165
173
|
begin
|
166
|
-
$log.
|
174
|
+
$log.info("Sorting/uniqing #{$items.count} items by #{[ $opts[:sep], $opts[:sort], 'sdate' ]}, reverse #{$opts[:reverse].inspect}")
|
167
175
|
|
168
176
|
$items.sort_by! { |i| [ i[$opts[:sep]], i[$opts[:sort]], i['sdate'] ] }
|
169
177
|
$items.reverse! if $opts[:reverse]
|
170
178
|
$items.uniq!
|
171
|
-
rescue
|
172
|
-
$log.
|
179
|
+
rescue Exception => e
|
180
|
+
$log.info("Sorting failed: #{e}\n")
|
173
181
|
end
|
174
182
|
|
175
183
|
$log.debug("#{$items.count} items remain")
|
@@ -211,6 +219,9 @@ unless mu
|
|
211
219
|
table
|
212
220
|
when 'hash' then items.map { |i| i.self }
|
213
221
|
when 'json' then items.map { |i| i.self }.to_json
|
222
|
+
when 'xml' then
|
223
|
+
xml = items.map { |i| "<#{$opts[:cmd].chomp("s")}>#{i.to_xml}</#{$opts[:cmd].chomp("s")}>" }
|
224
|
+
"<#{$opts[:cmd]}>\n#{xml.join("")}</#{$opts[:cmd]}>"
|
214
225
|
when 'yaml' then items.map { |i| i.self }.to_yaml
|
215
226
|
when 'remind' then items.map { |i|
|
216
227
|
"REM #{i['sdate'].strftime('%F AT %R')} " +
|
@@ -225,7 +236,8 @@ end
|
|
225
236
|
|
226
237
|
$log.debug("Formatting with #{mu.inspect}")
|
227
238
|
|
228
|
-
|
239
|
+
doc = RDoc::Markup::Document.new
|
240
|
+
section = nil
|
229
241
|
|
230
242
|
items.each_with_index do |i, j|
|
231
243
|
$log.debug("Print #{j}: #{i.inspect}")
|
@@ -233,15 +245,16 @@ items.each_with_index do |i, j|
|
|
233
245
|
# --li
|
234
246
|
break if $opts[:li].positive? && j >= $opts[:li]
|
235
247
|
|
236
|
-
|
248
|
+
# Use RDoc::Markup::Verbatim to save the item
|
249
|
+
v = RDoc::Markup::Verbatim.new
|
250
|
+
v.format = i
|
251
|
+
doc << v
|
237
252
|
|
238
253
|
# Sections
|
239
254
|
if $opts[:sep] && section != i[$opts[:sep]]
|
240
255
|
$log.debug("New section '#{$opts[:sep]}': #{i[$opts[:sep]]}")
|
241
256
|
|
242
|
-
|
243
|
-
v.format = { item: i, prop: $opts[:sep] }
|
244
|
-
doc << v
|
257
|
+
doc << RDoc::Markup::Raw.new($opts[:sep])
|
245
258
|
|
246
259
|
doc << RDoc::Markup::BlankLine.new if j.positive?
|
247
260
|
doc << RDoc::Markup::Heading.new(1, i[$opts[:sep]].to_s)
|
@@ -255,27 +268,30 @@ items.each_with_index do |i, j|
|
|
255
268
|
|
256
269
|
# Properties
|
257
270
|
$opts[:props].each_with_index do |prop, k|
|
258
|
-
|
259
|
-
|
271
|
+
value = i[prop]
|
272
|
+
|
273
|
+
next unless value
|
274
|
+
next if Array === value && !value[0]
|
275
|
+
next if String === value && value.length == 0
|
260
276
|
|
261
|
-
$log.debug("#{prop}: #{
|
277
|
+
$log.debug("#{prop}: #{value}")
|
262
278
|
|
263
|
-
|
264
|
-
|
265
|
-
props << v
|
279
|
+
# Use Raw to save the property
|
280
|
+
props << RDoc::Markup::Raw.new(prop)
|
266
281
|
|
267
282
|
unless k.positive?
|
268
283
|
# First property, value only
|
269
|
-
props << RDoc::Markup::Heading.new(2,
|
284
|
+
props << RDoc::Markup::Heading.new(2, value.to_s)
|
270
285
|
else
|
271
286
|
props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
|
272
|
-
props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(
|
287
|
+
props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless(i['placeholder'])
|
273
288
|
end
|
274
289
|
end
|
275
290
|
|
276
291
|
# Print it
|
277
|
-
unless props.empty?
|
278
|
-
|
279
|
-
|
280
|
-
end
|
292
|
+
props << RDoc::Markup::BlankLine.new unless props.empty?
|
293
|
+
|
294
|
+
doc << props
|
281
295
|
end
|
296
|
+
|
297
|
+
print doc.accept(mu)
|
data/icalPal.gemspec
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
require './lib/version'
|
2
|
+
|
1
3
|
Gem::Specification.new do |s|
|
2
4
|
s.name = "icalPal"
|
3
|
-
s.version =
|
5
|
+
s.version = ICalPal::VERSION
|
4
6
|
s.summary = "Command-line tool to query the macOS Calendar"
|
5
7
|
s.description = <<-EOF
|
6
8
|
Inspired by icalBuddy and maintains close compatability. Includes
|
@@ -17,6 +19,7 @@ EOF
|
|
17
19
|
s.extra_rdoc_files = [ "README.md" ]
|
18
20
|
|
19
21
|
s.add_runtime_dependency "sqlite3", "~> 1"
|
22
|
+
s.add_runtime_dependency "nokogiri-plist", "~> 0.5.0"
|
20
23
|
|
21
24
|
s.bindir = 'bin'
|
22
25
|
s.required_ruby_version = '>= 2.6.0'
|
data/lib/EventKit.rb
CHANGED
@@ -27,6 +27,13 @@ class EventKit
|
|
27
27
|
'yearly',
|
28
28
|
]
|
29
29
|
|
30
|
+
EKReminderProperty = [
|
31
|
+
'none', # 0
|
32
|
+
'high', nil, nil, nil, # 1
|
33
|
+
'medium', nil, nil, nil, # 5
|
34
|
+
'low', # 9
|
35
|
+
]
|
36
|
+
|
30
37
|
# EKSourceType (with color)
|
31
38
|
EKSourceType = [
|
32
39
|
{ name: 'Local', color: '#FFFFFF' }, # White
|
data/lib/ToICalPal.rb
CHANGED
@@ -7,15 +7,25 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
7
7
|
# ANSI[https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.416-199303-I!!PDF-E&type=items]
|
8
8
|
# colors
|
9
9
|
ANSI = {
|
10
|
-
'black':
|
11
|
-
'red':
|
12
|
-
'green':
|
13
|
-
'yellow':
|
14
|
-
'blue':
|
15
|
-
'magenta': 35,
|
16
|
-
'cyan':
|
17
|
-
'white':
|
18
|
-
'default': 39,
|
10
|
+
'black': 30, '#000000': '38;5;0',
|
11
|
+
'red': 31, '#ff0000': '38;5;1',
|
12
|
+
'green': 32, '#00ff00': '38;5;2',
|
13
|
+
'yellow': 33, '#ffff00': '38;5;3',
|
14
|
+
'blue': 34, '#0000ff': '38;5;4',
|
15
|
+
'magenta': 35, '#ff00ff': '38;5;5',
|
16
|
+
'cyan': 36, '#00ffff': '38;5;6',
|
17
|
+
'white': 37, '#ffffff': '38;5;255',
|
18
|
+
'default': 39, 'custom': nil,
|
19
|
+
|
20
|
+
# Reminders custom colors
|
21
|
+
'brown': '38;2;162;132;94',
|
22
|
+
'gray': '38;2;91;98;106',
|
23
|
+
'indigo': '38;2;88;86;214',
|
24
|
+
'lightblue': '38;2;90;200;250',
|
25
|
+
'orange': '38;2;255;149;0',
|
26
|
+
'pink': '38;2;255;45;85',
|
27
|
+
'purple': '38;2;204;115;225',
|
28
|
+
'rose': '38;2;217;166;159',
|
19
29
|
}
|
20
30
|
|
21
31
|
# Increased intensity
|
@@ -38,6 +48,8 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
38
48
|
|
39
49
|
# @param opts [Hash] Used for conditional formatting
|
40
50
|
# @option opts [String] :bullet Bullet
|
51
|
+
# @option opts [String] :ab Alert bullet
|
52
|
+
# @option opts [Boolean] :nb No bullet
|
41
53
|
# @option opts [Boolean] :nc No calendar names
|
42
54
|
# @option opts [Boolean] :npn No property names
|
43
55
|
# @option opts [Integer] :palette (nil) 8 for \-f, 24 for \--color
|
@@ -67,6 +79,14 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
67
79
|
rescue
|
68
80
|
end
|
69
81
|
|
82
|
+
begin
|
83
|
+
if (@item['due_date'] + ICalPal::ITIME).between?(ICalPal::ITIME + 1, $now.to_i) then
|
84
|
+
@res << "#{@opts[:ab]} " unless @opts[:nb]
|
85
|
+
return
|
86
|
+
end
|
87
|
+
rescue
|
88
|
+
end
|
89
|
+
|
70
90
|
@res << "#{@opts[:bullet]} " unless @opts[:nb]
|
71
91
|
end
|
72
92
|
|
@@ -126,15 +146,21 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
126
146
|
accept_blank_line
|
127
147
|
end
|
128
148
|
|
129
|
-
# Don't add anything to the document, just save the item
|
130
|
-
#
|
149
|
+
# Don't add anything to the document, just save the item for later
|
150
|
+
#
|
151
|
+
# @param arg [RDoc::Markup::Verbatim]
|
152
|
+
# @option arg [Object] :format The item
|
153
|
+
def accept_verbatim(arg)
|
154
|
+
@item = arg.format
|
155
|
+
end
|
156
|
+
|
157
|
+
# Don't add anything to the document, just save the property name
|
158
|
+
# for later
|
131
159
|
#
|
132
|
-
# @param
|
133
|
-
# @option
|
134
|
-
|
135
|
-
|
136
|
-
@item = h.format[:item]
|
137
|
-
@prop = h.format[:prop]
|
160
|
+
# @param arg [RDoc::Markup::Raw]
|
161
|
+
# @option arg [Object] :parts The property
|
162
|
+
def accept_raw(arg)
|
163
|
+
@prop = arg.parts[0]
|
138
164
|
end
|
139
165
|
|
140
166
|
# @param str [String]
|
data/lib/defaults.rb
CHANGED
@@ -5,17 +5,21 @@ $today = ICalPal::RDT.new(*$now.to_a[0..2] + [0, 0, 0, $now.zone])
|
|
5
5
|
# Defaults
|
6
6
|
$defaults = {
|
7
7
|
common: {
|
8
|
+
ab: '!',
|
8
9
|
aep: [],
|
9
10
|
bullet: '•',
|
10
11
|
cf: "#{ENV['HOME']}/.icalPal",
|
11
12
|
color: false,
|
12
13
|
db: "#{ENV['HOME']}/Library/Calendars/Calendar.sqlitedb",
|
13
14
|
debug: Logger::WARN,
|
15
|
+
df: '%b %-d, %Y',
|
14
16
|
ec: [],
|
15
17
|
eep: [],
|
18
|
+
el: [],
|
16
19
|
es: [],
|
17
20
|
et: [],
|
18
21
|
ic: [],
|
22
|
+
il: [],
|
19
23
|
is: [],
|
20
24
|
it: [],
|
21
25
|
li: 0,
|
@@ -27,11 +31,25 @@ $defaults = {
|
|
27
31
|
sep: false,
|
28
32
|
sort: nil,
|
29
33
|
sp: false,
|
34
|
+
tf: '%-I:%M %p',
|
30
35
|
},
|
31
36
|
tasks: {
|
32
|
-
|
33
|
-
|
34
|
-
|
37
|
+
dated: 0,
|
38
|
+
db: ICalPal::Reminder::DB_PATH,
|
39
|
+
iep: [ 'title', 'notes', 'due', 'priority' ],
|
40
|
+
sort: 'prio',
|
41
|
+
},
|
42
|
+
undatedTasks: {
|
43
|
+
dated: 1,
|
44
|
+
db: ICalPal::Reminder::DB_PATH,
|
45
|
+
iep: [ 'title', 'notes', 'due', 'priority' ],
|
46
|
+
sort: 'prio',
|
47
|
+
},
|
48
|
+
datedTasks: {
|
49
|
+
dated: 2,
|
50
|
+
db: ICalPal::Reminder::DB_PATH,
|
51
|
+
iep: [ 'title', 'notes', 'due', 'priority' ],
|
52
|
+
sort: 'prio',
|
35
53
|
},
|
36
54
|
stores: {
|
37
55
|
iep: [ 'account', 'type' ],
|
@@ -43,7 +61,6 @@ $defaults = {
|
|
43
61
|
},
|
44
62
|
events: {
|
45
63
|
days: nil,
|
46
|
-
df: '%b %-d, %Y',
|
47
64
|
ea: false,
|
48
65
|
eed: false,
|
49
66
|
eep: [],
|
@@ -57,7 +74,6 @@ $defaults = {
|
|
57
74
|
sed: false,
|
58
75
|
sort: 'sdate',
|
59
76
|
ss: "\n------------------------",
|
60
|
-
tf: '%-I:%M %p',
|
61
77
|
to: nil,
|
62
78
|
uid: false,
|
63
79
|
}
|
data/lib/icalPal.rb
CHANGED
@@ -3,10 +3,12 @@ require_relative 'ToICalPal'
|
|
3
3
|
require_relative 'calendar'
|
4
4
|
require_relative 'event'
|
5
5
|
require_relative 'rdt'
|
6
|
+
require_relative 'reminder'
|
6
7
|
require_relative 'store'
|
7
8
|
|
8
9
|
# Encapsulate the _Store_ (accounts), _Calendar_ and _CalendarItem_
|
9
|
-
# tables of a Calendar database
|
10
|
+
# tables of a Calendar database, and the _Reminder_ table of a
|
11
|
+
# Reminders database
|
10
12
|
|
11
13
|
module ICalPal
|
12
14
|
attr_reader :self
|
@@ -14,7 +16,7 @@ module ICalPal
|
|
14
16
|
# Dynamic instantiation of our classes based on the command being
|
15
17
|
# run
|
16
18
|
#
|
17
|
-
# @param klass [String] One of +accounts+, +stores+, +calendars+, or +
|
19
|
+
# @param klass [String] One of +accounts+, +stores+, +calendars+, +events+, or +tasks+
|
18
20
|
# @return [Class] The subclass of ICalPal
|
19
21
|
def self.call(klass)
|
20
22
|
case klass
|
@@ -22,13 +24,48 @@ module ICalPal
|
|
22
24
|
when 'stores' then Store
|
23
25
|
when 'calendars' then Calendar
|
24
26
|
when 'events' then Event
|
25
|
-
when 'tasks' then
|
27
|
+
when 'tasks' then Reminder
|
26
28
|
else
|
27
29
|
$log.fatal("Unknown class: #{klass}")
|
28
30
|
exit
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
34
|
+
# Load data
|
35
|
+
def self.load_data(db_file, q)
|
36
|
+
$log.debug(q.gsub(/\n/, ' '))
|
37
|
+
|
38
|
+
rows = []
|
39
|
+
|
40
|
+
begin
|
41
|
+
# Open the database
|
42
|
+
$log.debug("Opening database: #{db_file}")
|
43
|
+
db = SQLite3::Database.new(db_file, { readonly: true, results_as_hash: true })
|
44
|
+
|
45
|
+
# Prepare the query
|
46
|
+
stmt = db.prepare(q)
|
47
|
+
abort(stmt.columns.sort.join(' ')) if $opts[:props].any? 'list'
|
48
|
+
$opts[:props] = stmt.columns - $opts[:eep] if $opts[:props].any? 'all'
|
49
|
+
|
50
|
+
# Iterate the SQLite3::ResultSet once
|
51
|
+
stmt.execute.each_with_index { |i, j| rows[j] = i }
|
52
|
+
stmt.close
|
53
|
+
|
54
|
+
# Close the database
|
55
|
+
db.close
|
56
|
+
$log.debug("Closed #{db_file}")
|
57
|
+
|
58
|
+
rescue SQLite3::BusyException => e
|
59
|
+
$log.error("Non-fatal error closing database #{db.filename}")
|
60
|
+
|
61
|
+
rescue SQLite3::Exception => e
|
62
|
+
abort("#{db_file}: #{e}")
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
return(rows)
|
67
|
+
end
|
68
|
+
|
32
69
|
# @param obj [ICalPal] A +Store+ or +Calendar+
|
33
70
|
def initialize(obj)
|
34
71
|
obj['type'] = EventKit::EKSourceType.find_index { |i| i[:name] == 'Subscribed' } if obj['subcal_url']
|
@@ -55,6 +92,28 @@ module ICalPal
|
|
55
92
|
CSV::Row::new(headers, values)
|
56
93
|
end
|
57
94
|
|
95
|
+
# @return [String] All fields in a simple XML format: <field>value</field>.
|
96
|
+
# Fields with empty values return <field/>.
|
97
|
+
def to_xml
|
98
|
+
retval = ""
|
99
|
+
|
100
|
+
@self.keys.each do |k|
|
101
|
+
v = @self[k]
|
102
|
+
|
103
|
+
if v.respond_to?(:length) then
|
104
|
+
if v.length == 0 or v[0] == nil then
|
105
|
+
retval += "<#{k}/>"
|
106
|
+
else
|
107
|
+
# Keep non-blank and whitespace, except form feeds and vertical whitespace
|
108
|
+
v = v.gsub(/[^[[:print:]][[:space:]]]/, '.').gsub(/[\f\v]/, '.')
|
109
|
+
retval += "<#{k}>#{v}</#{k}>"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
retval
|
115
|
+
end
|
116
|
+
|
58
117
|
# Get the +n+'th +dow+ in month +m+
|
59
118
|
#
|
60
119
|
# @param n [Integer] Integer between -4 and +4
|
data/lib/options.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
|
3
3
|
require_relative 'defaults'
|
4
|
+
require_relative 'version'
|
4
5
|
|
5
6
|
module ICalPal
|
6
7
|
# Handle program options from all sources:
|
@@ -23,7 +24,7 @@ module ICalPal
|
|
23
24
|
@op = OptionParser.new
|
24
25
|
@op.summary_width = 23
|
25
26
|
@op.banner += " [-c] COMMAND"
|
26
|
-
@op.version =
|
27
|
+
@op.version = ICalPal::VERSION
|
27
28
|
|
28
29
|
@op.accept(ICalPal::RDT) { |s| ICalPal::RDT.conv(s) }
|
29
30
|
|
@@ -31,6 +32,7 @@ module ICalPal
|
|
31
32
|
@op.on("\nCOMMAND must be one of the following:\n\n")
|
32
33
|
|
33
34
|
@op.on("%s%s %sPrint events" % pad('events'))
|
35
|
+
@op.on("%s%s %sPrint tasks" % pad('tasks'))
|
34
36
|
@op.on("%s%s %sPrint calendars" % pad('calendars'))
|
35
37
|
@op.on("%s%s %sPrint accounts" % pad('accounts'))
|
36
38
|
|
@@ -38,16 +40,20 @@ module ICalPal
|
|
38
40
|
@op.on("%s%s %sPrint events occurring today" % pad('eventsToday'))
|
39
41
|
@op.on("%s%s %sPrint events occurring between today and NUM days into the future" % pad('eventsToday+NUM'))
|
40
42
|
@op.on("%s%s %sPrint events occurring at present time" % pad('eventsNow'))
|
43
|
+
@op.on("%s%s %sPrint tasks with a due date" % pad('datedTasks'))
|
44
|
+
@op.on("%s%s %sPrint tasks with no due date" % pad('undatedTasks'))
|
41
45
|
|
42
46
|
# global
|
43
47
|
@op.separator("\nGlobal options:\n\n")
|
44
48
|
|
45
49
|
@op.on('-c=COMMAND', '--cmd=COMMAND', COMMANDS, 'Command to run')
|
46
|
-
@op.on('--db=DB',
|
50
|
+
@op.on('--db=DB', "Use DB file instead of Calendar (default: #{$defaults[:common][:db]})",
|
51
|
+
'For the tasks commands this should be a directory containing .sqlite files',
|
52
|
+
"(default: #{$defaults[:tasks][:db]})")
|
47
53
|
@op.on('--cf=FILE', "Set config file path (default: #{$defaults[:common][:cf]})")
|
48
54
|
@op.on('-o', '--output=FORMAT', OUTFORMATS,
|
49
55
|
"Print as FORMAT (default: #{$defaults[:common][:output]})", "[#{OUTFORMATS.join(', ')}]")
|
50
|
-
|
56
|
+
|
51
57
|
# include/exclude
|
52
58
|
@op.separator("\nIncluding/excluding calendars:\n\n")
|
53
59
|
|
@@ -63,6 +69,10 @@ module ICalPal
|
|
63
69
|
@op.on('--ic=CALENDARS', Array, 'List of calendars to include')
|
64
70
|
@op.on('--ec=CALENDARS', Array, 'List of calendars to exclude')
|
65
71
|
|
72
|
+
@op.separator('')
|
73
|
+
@op.on('--il=LISTS', Array, 'List of reminder lists to include')
|
74
|
+
@op.on('--el=LISTS', Array, 'List of reminder lists to exclude')
|
75
|
+
|
66
76
|
# dates
|
67
77
|
@op.separator("\nChoosing dates:\n\n")
|
68
78
|
|
@@ -85,10 +95,11 @@ module ICalPal
|
|
85
95
|
@op.on('--eep=PROPERTIES', Array, 'List of properties to exclude')
|
86
96
|
@op.on('--aep=PROPERTIES', Array, 'List of properties to include in addition to the default list')
|
87
97
|
@op.separator('')
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
98
|
+
@op.on('--itp=PROPERTIES', Array, 'List of task properties to include')
|
99
|
+
@op.on('--etp=PROPERTIES', Array, 'List of task properties to exclude')
|
100
|
+
@op.on('--atp=PROPERTIES', Array, 'List of task properties to include in addition to the default list',
|
101
|
+
'Included for backwards compatability, these are aliases for --iep, --eep, and --aep')
|
102
|
+
@op.separator('')
|
92
103
|
|
93
104
|
@op.on('--uid', 'Show event UIDs')
|
94
105
|
@op.on('--eed', 'Exclude end datetimes')
|
@@ -114,13 +125,12 @@ module ICalPal
|
|
114
125
|
@op.separator('')
|
115
126
|
@op.on('--sc', 'Separate by calendar')
|
116
127
|
@op.on('--sd', 'Separate by date')
|
117
|
-
|
118
|
-
# @op.on('--sta', 'Sort tasks by due date (ascending)')
|
119
|
-
# @op.on('--std', 'Sort tasks by due date (descending)')
|
120
|
-
# @op.separator('')
|
128
|
+
@op.on('--sp', 'Separate by priority')
|
121
129
|
@op.on('--sep=PROPERTY', 'Separate by PROPERTY')
|
122
130
|
@op.separator('')
|
123
131
|
@op.on('--sort=PROPERTY', 'Sort by PROPERTY')
|
132
|
+
@op.on('--std', 'Sort tasks by due date (same as --sort=due_date)')
|
133
|
+
@op.on('--stda', 'Sort tasks by due date (ascending) (same as --sort=due_date -r)')
|
124
134
|
@op.on('-r', '--reverse', 'Sort in reverse')
|
125
135
|
|
126
136
|
@op.separator('')
|
@@ -134,6 +144,7 @@ module ICalPal
|
|
134
144
|
|
135
145
|
@op.separator('')
|
136
146
|
@op.on('-b', '--bullet=STRING', String, 'Use STRING for bullets')
|
147
|
+
@op.on('--ab=STRING', String, 'Use STRING for alert bullets')
|
137
148
|
@op.on('--nb', 'Do not use bullets')
|
138
149
|
@op.on('--nnr=SEPARATOR', String, 'Set replacement for newlines within notes')
|
139
150
|
|
@@ -202,10 +213,21 @@ module ICalPal
|
|
202
213
|
.merge(env)
|
203
214
|
.merge(cli)
|
204
215
|
|
216
|
+
# datedTasks and undatedTasks
|
217
|
+
opts[:cmd] = "tasks" if opts[:cmd] == "datedTasks"
|
218
|
+
opts[:cmd] = "tasks" if opts[:cmd] == "undatedTasks"
|
219
|
+
|
205
220
|
# All kids love log!
|
206
221
|
$log.level = opts[:debug]
|
207
222
|
|
223
|
+
# For posterity
|
224
|
+
opts[:ruby] = RUBY_VERSION
|
225
|
+
opts[:version] = @op.version
|
226
|
+
|
208
227
|
# From the Department of Redundancy Department
|
228
|
+
opts[:iep] += opts[:itp] if opts[:itp]
|
229
|
+
opts[:eep] += opts[:etp] if opts[:etp]
|
230
|
+
opts[:aep] += opts[:atp] if opts[:atp]
|
209
231
|
opts[:props] = (opts[:iep] + opts[:aep] - opts[:eep]).uniq
|
210
232
|
|
211
233
|
# From, to, days
|
@@ -217,6 +239,10 @@ module ICalPal
|
|
217
239
|
opts[:from] = $now if opts[:n]
|
218
240
|
end
|
219
241
|
|
242
|
+
# Sorting
|
243
|
+
opts[:sort] = 'due_date' if opts[:std] or opts[:stda]
|
244
|
+
opts[:reverse] = true if opts[:std]
|
245
|
+
|
220
246
|
# Colors
|
221
247
|
opts[:palette] = 8 if opts[:f]
|
222
248
|
opts[:palette] = 24 if opts[:color]
|
@@ -225,7 +251,7 @@ module ICalPal
|
|
225
251
|
unless opts[:sep]
|
226
252
|
opts[:sep] = 'calendar' if opts[:sc]
|
227
253
|
opts[:sep] = 'sday' if opts[:sd]
|
228
|
-
opts[:sep] = '
|
254
|
+
opts[:sep] = 'long_priority' if opts[:sp]
|
229
255
|
end
|
230
256
|
opts[:nc] = true if opts[:sc]
|
231
257
|
|
@@ -234,17 +260,6 @@ module ICalPal
|
|
234
260
|
raise(OptionParser::InvalidOption, 'Start date must be before end date') if opts[:from] && opts[:from] > opts[:to]
|
235
261
|
raise(OptionParser::MissingArgument, 'No properties to display') if opts[:props].empty?
|
236
262
|
|
237
|
-
# Open the database here so we can catch errors and print the help message
|
238
|
-
$log.debug("Opening database: #{opts[:db]}")
|
239
|
-
$db = SQLite3::Database.new(opts[:db], { readonly: true, results_as_hash: true })
|
240
|
-
$db.prepare('SELECT 1 FROM Calendar LIMIT 1').close
|
241
|
-
|
242
|
-
rescue SQLite3::SQLException => e
|
243
|
-
@op.abort("#{opts[:db]} is not a Calendar database")
|
244
|
-
|
245
|
-
rescue SQLite3::Exception => e
|
246
|
-
@op.abort("#{opts[:db]}: #{e}")
|
247
|
-
|
248
263
|
rescue StandardError => e
|
249
264
|
@op.abort("#{e}\n\n#{@op.help}\n#{e}")
|
250
265
|
end
|
@@ -253,10 +268,10 @@ module ICalPal
|
|
253
268
|
end
|
254
269
|
|
255
270
|
# Commands that can be run
|
256
|
-
COMMANDS = %w{events eventsToday eventsNow calendars accounts stores}
|
271
|
+
COMMANDS = %w{events eventsToday eventsNow tasks datedTasks undatedTasks calendars accounts stores}
|
257
272
|
|
258
273
|
# Supported output formats
|
259
|
-
OUTFORMATS = %w{ansi csv default hash html json md rdoc toc yaml
|
274
|
+
OUTFORMATS = %w{ansi csv default hash html json md rdoc remind toc xml yaml}
|
260
275
|
|
261
276
|
private
|
262
277
|
|
data/lib/reminder.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'nokogiri-plist'
|
3
|
+
|
4
|
+
module ICalPal
|
5
|
+
# Class representing items from the <tt>Reminders</tt> database
|
6
|
+
class Reminder
|
7
|
+
include ICalPal
|
8
|
+
|
9
|
+
def [](k)
|
10
|
+
case k
|
11
|
+
when 'notes' then # Skip empty notes
|
12
|
+
@self['notes'].length > 0? @self['notes'] : nil
|
13
|
+
|
14
|
+
when 'priority' then # Integer -> String
|
15
|
+
EventKit::EKReminderProperty[@self['priority']] if @self['priority'] > 0
|
16
|
+
|
17
|
+
when 'sdate' then # For sorting
|
18
|
+
@self['title']
|
19
|
+
|
20
|
+
else @self[k]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(obj)
|
25
|
+
@self = {}
|
26
|
+
obj.keys.each { |k| @self[k] = obj[k] }
|
27
|
+
|
28
|
+
# Priority
|
29
|
+
@self['prio'] = 0 if @self['priority'] == 1 # high
|
30
|
+
@self['prio'] = 1 if @self['priority'] == 5 # medium
|
31
|
+
@self['prio'] = 2 if @self['priority'] == 9 # low
|
32
|
+
@self['prio'] = 3 if @self['priority'] == 0 # none
|
33
|
+
|
34
|
+
@self['long_priority'] = LONG_PRIORITY[@self['prio']]
|
35
|
+
|
36
|
+
# For sorting
|
37
|
+
@self['sdate'] = (@self['title'])? @self['title'] : ""
|
38
|
+
|
39
|
+
# Due date
|
40
|
+
@self['due'] = RDT.new(*Time.at(@self['due_date'] + ITIME).to_a.reverse[4..]) if @self['due_date']
|
41
|
+
@self['due_date'] = 0 unless @self['due_date']
|
42
|
+
|
43
|
+
# Notes
|
44
|
+
@self['notes'] = "" unless @self['notes']
|
45
|
+
|
46
|
+
# Color
|
47
|
+
@self['color'] = nil unless $opts[:palette]
|
48
|
+
|
49
|
+
if @self['color'] then
|
50
|
+
# Run command
|
51
|
+
stdin, stdout, stderr, e = Open3.popen3(PL_CONVERT)
|
52
|
+
|
53
|
+
# Send color bplist
|
54
|
+
stdin.write(@self['color'])
|
55
|
+
stdin.close
|
56
|
+
|
57
|
+
# Read output
|
58
|
+
plist = Nokogiri::PList(stdout.read)['$objects']
|
59
|
+
|
60
|
+
@self['color'] = plist[3]
|
61
|
+
@self['symbolic_color_name'] = (plist[2] == 'custom')? plist[4] : plist[2]
|
62
|
+
else
|
63
|
+
@self['color'] = DEFAULT_COLOR
|
64
|
+
@self['symbolic_color_name'] = DEFAULT_SYMBOLIC_COLOR
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
DEFAULT_COLOR = '#1BADF8'
|
71
|
+
DEFAULT_SYMBOLIC_COLOR = 'blue'
|
72
|
+
|
73
|
+
LONG_PRIORITY = [
|
74
|
+
"High priority",
|
75
|
+
"Medium priority",
|
76
|
+
"Low priority",
|
77
|
+
"No priority",
|
78
|
+
]
|
79
|
+
|
80
|
+
PL_CONVERT = '/usr/bin/plutil -convert xml1 -o - -'
|
81
|
+
|
82
|
+
DB_PATH = "#{Dir::home}/Library/Group Containers/group.com.apple.reminders/Container_v1/Stores"
|
83
|
+
|
84
|
+
QUERY = <<~SQL
|
85
|
+
SELECT DISTINCT
|
86
|
+
|
87
|
+
zremcdReminder.zAllday as all_day,
|
88
|
+
zremcdReminder.zDuedate as due_date,
|
89
|
+
zremcdReminder.zFlagged as flagged,
|
90
|
+
zremcdReminder.zNotes as notes,
|
91
|
+
zremcdReminder.zPriority as priority,
|
92
|
+
zremcdReminder.zTitle as title,
|
93
|
+
|
94
|
+
zremcdBaseList.zBadgeEmblem as badge,
|
95
|
+
zremcdBaseList.zColor as color,
|
96
|
+
zremcdBaseList.zName as list_name,
|
97
|
+
zremcdBaseList.zParentList as parent,
|
98
|
+
zremcdBaseList.zSharingStatus as shared
|
99
|
+
|
100
|
+
FROM zremcdReminder
|
101
|
+
|
102
|
+
JOIN zremcdBaseList ON zremcdReminder.zList = zremcdBaseList.z_pk
|
103
|
+
|
104
|
+
WHERE zremcdReminder.zCompleted = 0
|
105
|
+
AND zremcdReminder.zMarkedForDeletion = 0
|
106
|
+
|
107
|
+
SQL
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
data/lib/version.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: icalPal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Rosen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-04-
|
11
|
+
date: 2024-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sqlite3
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri-plist
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.5.0
|
27
41
|
description: |
|
28
42
|
Inspired by icalBuddy and maintains close compatability. Includes
|
29
43
|
many additional features for querying, filtering, and formatting.
|
@@ -45,7 +59,9 @@ files:
|
|
45
59
|
- lib/icalPal.rb
|
46
60
|
- lib/options.rb
|
47
61
|
- lib/rdt.rb
|
62
|
+
- lib/reminder.rb
|
48
63
|
- lib/store.rb
|
64
|
+
- lib/version.rb
|
49
65
|
homepage: https://github.com/ajrosen/icalPal
|
50
66
|
licenses:
|
51
67
|
- GPL-3.0-or-later
|