icalPal 3.3.0 → 3.5.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 -25
- data/bin/icalPal +30 -89
- data/bin/icalpal +30 -89
- data/icalPal.gemspec +11 -9
- data/lib/ToICalPal.rb +24 -27
- data/lib/defaults.rb +14 -6
- data/lib/event.rb +99 -78
- data/lib/icalPal.rb +18 -11
- data/lib/options.rb +58 -13
- data/lib/rdt.rb +22 -4
- data/lib/utils.rb +0 -7
- data/lib/version.rb +1 -1
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2549d35908833fd3fbb48d98187e3ee5618fb3c610dcb03659e8f41b67ac91d
|
4
|
+
data.tar.gz: b5999da17435b5eefbc1f19fe8822c45c5ffec0741f5cb58d7f64c313d700ca2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64f542aa97dd5b66c28001b0f70ef289a447bc89b5272ed3a4916930a048ea2c2baed4c75b7fe71310611827b0e6e8b0a0dd13665807dba91816e72a6647b0d9
|
7
|
+
data.tar.gz: e229b5a9a3261c1f145f1692d4abeb26e653e2e8c7d086d1d0f3a900fe1dfe13a8c68477ddbb22538262d27d1f7fd9cdb7ad302693815e4cb721955c5c379e30
|
data/README.md
CHANGED
@@ -4,11 +4,27 @@
|
|
4
4
|
|
5
5
|
## Description
|
6
6
|
|
7
|
-
icalPal is a command-line tool to query
|
7
|
+
icalPal is a command-line tool to query macOS Calendar and Reminders
|
8
8
|
databases for accounts, calendars, events, and tasks. It can be run
|
9
9
|
on any system with [Ruby](https://www.ruby-lang.org/) and access to a
|
10
10
|
Calendar or Reminders database.
|
11
11
|
|
12
|
+
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
|
13
|
+
|
14
|
+
**Table of Contents**
|
15
|
+
|
16
|
+
- [Installation](#installation)
|
17
|
+
- [Features](#features)
|
18
|
+
- [Compatability with icalBuddy](#compatability-with-icalbuddy)
|
19
|
+
- [Additional commands](#additional-commands)
|
20
|
+
- [Additional options](#additional-options)
|
21
|
+
- [Usage](#usage)
|
22
|
+
- [Output formats](#output-formats)
|
23
|
+
- [History](#history)
|
24
|
+
|
25
|
+
<!-- markdown-toc end -->
|
26
|
+
|
27
|
+
|
12
28
|
## Installation
|
13
29
|
|
14
30
|
As a system-wide Ruby gem:
|
@@ -23,20 +39,14 @@ or in your home diretory:
|
|
23
39
|
gem install --user-install icalPal
|
24
40
|
```
|
25
41
|
|
26
|
-
As a Homebrew formula:
|
27
|
-
|
28
|
-
```
|
29
|
-
brew tap ajrosen/icalPal
|
30
|
-
brew install icalPal
|
31
|
-
```
|
32
|
-
|
33
42
|
## Features
|
34
43
|
|
35
|
-
### Compatability with
|
44
|
+
### Compatability with icalBuddy
|
36
45
|
|
37
|
-
icalPal tries to be compatible with
|
38
|
-
|
39
|
-
|
46
|
+
icalPal tries to be compatible with
|
47
|
+
[icalBuddy](https://github.com/ali-rantakari/icalBuddy) for
|
48
|
+
command-line options and for output. There are a some important
|
49
|
+
differences to be aware of.
|
40
50
|
|
41
51
|
* Options require two hyphens, except for single-letter options that require one hyphen
|
42
52
|
* *eventsFrom* is not supported. Instead there is *--from*, *--to*, and *--days*
|
@@ -50,7 +60,8 @@ of.
|
|
50
60
|
|
51
61
|
```icalPal accounts```
|
52
62
|
|
53
|
-
Shows a list of enabled Calendar accounts. Internally they are known
|
63
|
+
Shows a list of enabled Calendar accounts. Internally they are known
|
64
|
+
as *Stores*; you can run ```icalPal stores``` instead.
|
54
65
|
|
55
66
|
```icalPal datedTasks```
|
56
67
|
|
@@ -62,7 +73,7 @@ Shows only reminders that have a due date.
|
|
62
73
|
* The ```-c``` part is optional, but you cannot abbreviate the command if you leave it off.
|
63
74
|
* Use ```-o``` to print the output in different formats. CSV or JSON are intertesting choices.
|
64
75
|
* Copy your Calendar database file and use ```--db``` on it.
|
65
|
-
* ```--it``` and ```--et``` will filter by Calendar *type*. Types are **Local**, **Exchange**, **CalDAV**, **MobileMe**, **Subscribed**, and **
|
76
|
+
* ```--it``` and ```--et``` will filter by Calendar *type*. Types are **Local**, **Exchange**, **CalDAV**, **MobileMe**, **Subscribed**, **Birthdays**, and **Reminders**
|
66
77
|
* ```--il``` and ```-el``` will filter by Reminder list
|
67
78
|
* ```--ia``` includes *only* all-day events (opposite of ```--ea```)
|
68
79
|
* ```--aep``` is like ```--iep```, but *adds* to the default property list instead of replacing it.
|
@@ -70,7 +81,9 @@ Shows only reminders that have a due date.
|
|
70
81
|
* ```--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/).
|
71
82
|
* ```--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
|
72
83
|
|
73
|
-
Because icalPal is written in Ruby, and not a native Mac application,
|
84
|
+
Because icalPal is written in Ruby, and not a native Mac application,
|
85
|
+
you can run it just about anywhere. It's been tested with the version
|
86
|
+
of Ruby (2.6.10) included with macOS.
|
74
87
|
|
75
88
|
## Usage
|
76
89
|
|
@@ -93,10 +106,12 @@ COMMAND must be one of the following:
|
|
93
106
|
Global options:
|
94
107
|
```
|
95
108
|
-c, --cmd=COMMAND Command to run
|
96
|
-
--db=DB Use DB file instead of Calendar
|
109
|
+
--db=DB Use DB file instead of Calendar
|
110
|
+
(default: ["/Users/user/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb", "/Users/user/Library/Calendars/Calendar.sqlitedb"]
|
97
111
|
For the tasks commands this should be a directory containing .sqlite files
|
98
|
-
(default: /Users/
|
99
|
-
--cf=FILE Set config file path (default: /Users/
|
112
|
+
(default: /Users/user/Library/Group Containers/group.com.apple.reminders/Container_v1/Stores)
|
113
|
+
--cf=FILE Set config file path (default: /Users/user/.icalpal)
|
114
|
+
--norc Ignore ICALPAL and ICALPAL_CONFIG environment variables
|
100
115
|
-o, --output=FORMAT Print as FORMAT (default: default)
|
101
116
|
[ansi, csv, default, hash, html, json, md, rdoc, remind, toc, xml, yaml]
|
102
117
|
```
|
@@ -200,7 +215,9 @@ Environment variables:
|
|
200
215
|
```
|
201
216
|
ICALPAL Additional arguments
|
202
217
|
ICALPAL_CONFIG Additional arguments from a file
|
203
|
-
(default: /Users/
|
218
|
+
(default: /Users/user/.icalpal)
|
219
|
+
|
220
|
+
Do not quote or escape values. Options set in ICALPAL override ICALPAL_CONFIG. Options on the command line override ICALPAL.
|
204
221
|
```
|
205
222
|
|
206
223
|
## Output formats
|
@@ -212,7 +229,8 @@ CSV, Hash, JSON, XML, and YAML print all fields for all items in their
|
|
212
229
|
respective formats. From that you can analyze the results any way you
|
213
230
|
like.
|
214
231
|
|
215
|
-
[Remind](https://dianne.skoll.ca/projects/remind/) format uses a
|
232
|
+
[Remind](https://dianne.skoll.ca/projects/remind/) format uses a
|
233
|
+
minimal implementation built into icalPal.
|
216
234
|
|
217
235
|
Other formats such as ANSI, HTML, Markdown, RDoc, and TOC, use Ruby's
|
218
236
|
[RDoc::Markup](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup.html)
|
@@ -221,7 +239,8 @@ framework to build and render the items.
|
|
221
239
|
Each item to be printed is a new
|
222
240
|
[RDoc::Markup::Document](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Document.html).
|
223
241
|
|
224
|
-
When using one of the <em>separate by</em> options, a section header
|
242
|
+
When using one of the <em>separate by</em> options, a section header
|
243
|
+
is added first. The section contains:
|
225
244
|
|
226
245
|
* [RDoc::Markup::BlankLine](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/BlankLine.html)
|
227
246
|
(unless this is the first section)
|
@@ -248,9 +267,10 @@ information about the item and property to the default formatter.
|
|
248
267
|
## History
|
249
268
|
|
250
269
|
I used icalBuddy for many years. It's great for scripting,
|
251
|
-
automation, and as a
|
252
|
-
[
|
253
|
-
[
|
270
|
+
automation, and as a widget for apps like
|
271
|
+
[Übersicht](https://tracesof.net/uebersicht/),
|
272
|
+
[GeekTool](https://www.tynsoe.org/geektool/), and
|
273
|
+
[SketchyBar](https://felixkratz.github.io/SketchyBar/).
|
254
274
|
|
255
275
|
As with many applications, I started to run into some limitations in
|
256
276
|
icalBuddy. The biggest being that active development ended in 2014.
|
@@ -259,7 +279,7 @@ Lawton](https://github.com/jimlawton) that it even compiles anymore.
|
|
259
279
|
|
260
280
|
Instead of trying to understand and extend the existing code, I chose
|
261
281
|
to start anew using my language of choice: Ruby. Using Ruby meant
|
262
|
-
there is *much* less code;
|
282
|
+
there is *much* less code; less than 2,000 lines vs. 7,000. It also means
|
263
283
|
icalPal is multi-platform.
|
264
284
|
|
265
285
|
I won't pretend to understand **why** you would want to run this on
|
data/bin/icalPal
CHANGED
@@ -10,6 +10,7 @@ begin
|
|
10
10
|
require 'yaml'
|
11
11
|
|
12
12
|
require_relative '../lib/icalpal'
|
13
|
+
require_relative '../lib/defaults'
|
13
14
|
require_relative '../lib/options'
|
14
15
|
require_relative '../lib/utils'
|
15
16
|
rescue LoadError => e
|
@@ -29,14 +30,14 @@ end
|
|
29
30
|
# All kids love log!
|
30
31
|
$log = Logger.new(STDERR, { level: $defaults[:common][:debug] })
|
31
32
|
$log.formatter = proc do |s, t, _p, m| # Severity, time, progname, msg
|
32
|
-
(
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
format("[%-5<sev>s] %<time>s [%<file>s:%<line>5s] - %<message>s\n",
|
34
|
+
{
|
35
|
+
sev: s,
|
36
|
+
time: t.strftime('%H:%M:%S.%L'),
|
37
|
+
file: caller(4, 1)[0].split('/')[-1].split(':')[0],
|
38
|
+
line: caller(4, 1)[0].split('/')[-1].split(':')[1],
|
39
|
+
message: m
|
40
|
+
})
|
40
41
|
end
|
41
42
|
|
42
43
|
$opts = ICalPal::Options.new.parse_options
|
@@ -57,9 +58,8 @@ $log.info("Options: #{$opts}")
|
|
57
58
|
# @param item[Object]
|
58
59
|
|
59
60
|
def add(item)
|
60
|
-
$log.
|
61
|
+
$log.debug("Adding #{item.dump} #{item['UUID']} (#{item['title']})") if item['UUID']
|
61
62
|
|
62
|
-
item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if ICalPal::Event === item && item['sdate']
|
63
63
|
$items.push(item)
|
64
64
|
end
|
65
65
|
|
@@ -128,108 +128,58 @@ unless success
|
|
128
128
|
end
|
129
129
|
|
130
130
|
$log.info("Loaded #{$rows.count} #{klass} rows")
|
131
|
+
$log.info("Window is #{$opts[:from]} to #{$opts[:to]}")
|
131
132
|
|
132
133
|
|
133
134
|
##################################################
|
134
135
|
# Process the data
|
135
136
|
|
136
137
|
# Add rows
|
137
|
-
$rows.
|
138
|
-
$log.debug("Row #{i}: #{row['ROWID']}:#{row['UUID']} - #{row['account']}/#{row['calendar']}/#{row['title']}")
|
139
|
-
|
138
|
+
$rows.each do |row|
|
140
139
|
# --es/--is
|
141
|
-
if $opts[:es].any? row['account']
|
142
|
-
|
143
|
-
next
|
144
|
-
end
|
145
|
-
|
146
|
-
unless $opts[:is].empty? || ($opts[:is].any? row['account'])
|
147
|
-
$log.debug(':is')
|
148
|
-
next
|
149
|
-
end
|
140
|
+
next if $opts[:es].any? row['account']
|
141
|
+
next unless $opts[:is].empty? || ($opts[:is].any? row['account'])
|
150
142
|
|
151
143
|
# --ec/--ic
|
152
144
|
unless klass == ICalPal::Store || !row['calendar']
|
153
|
-
if $opts[:ec].any? row['calendar']
|
154
|
-
|
155
|
-
next
|
156
|
-
end
|
157
|
-
|
158
|
-
unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
|
159
|
-
$log.debug(':ic')
|
160
|
-
next
|
161
|
-
end
|
145
|
+
next if $opts[:ec].any? row['calendar']
|
146
|
+
next unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
|
162
147
|
end
|
163
148
|
|
164
149
|
# Instantiate an item
|
165
150
|
item = klass.new(row)
|
166
151
|
|
167
152
|
# --et/--it
|
168
|
-
if $opts[:et].any? item['type']
|
169
|
-
|
170
|
-
next
|
171
|
-
end
|
172
|
-
|
173
|
-
unless $opts[:it].empty? || ($opts[:it].any? item['type'])
|
174
|
-
$log.debug(':it')
|
175
|
-
next
|
176
|
-
end
|
153
|
+
next if $opts[:et].any? item['type']
|
154
|
+
next unless $opts[:it].empty? || ($opts[:it].any? item['type'])
|
177
155
|
|
178
156
|
# --el/--il
|
179
|
-
if $opts[:el].any? item['list_name']
|
180
|
-
|
181
|
-
next
|
182
|
-
end
|
183
|
-
|
184
|
-
unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
|
185
|
-
$log.debug(':il')
|
186
|
-
next
|
187
|
-
end
|
157
|
+
next if $opts[:el].any? item['list_name']
|
158
|
+
next unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
|
188
159
|
|
189
160
|
# --match
|
190
161
|
if $opts[:match]
|
191
162
|
r = $opts[:match].split('=')
|
192
163
|
|
193
164
|
if item[r[0]].to_s.respond_to?(:match)
|
194
|
-
unless item[r[0]].to_s
|
195
|
-
$log.debug(':match')
|
196
|
-
next
|
197
|
-
end
|
165
|
+
next unless item[r[0]].to_s =~ Regexp.new(r[1], Regexp::IGNORECASE)
|
198
166
|
end
|
199
167
|
end
|
200
168
|
|
201
169
|
if ICalPal::Event === item
|
202
170
|
# Check for all-day and cancelled events
|
203
|
-
if $opts[:ea] && item['all_day'].positive?
|
204
|
-
|
205
|
-
|
206
|
-
end
|
207
|
-
|
208
|
-
if $opts[:ia] && !item['all_day'].positive?
|
209
|
-
$log.debug(':ia')
|
210
|
-
next
|
211
|
-
end
|
212
|
-
|
213
|
-
if item['status'] == :canceled
|
214
|
-
$log.debug(':canceled')
|
215
|
-
next
|
216
|
-
end
|
171
|
+
next if $opts[:ea] && item['all_day'].positive?
|
172
|
+
next if $opts[:ia] && !item['all_day'].positive?
|
173
|
+
next if item['status'] == :canceled
|
217
174
|
|
218
175
|
(item['has_recurrences'].positive?)?
|
219
|
-
item.recurring.each { |
|
220
|
-
item.non_recurring.each { |
|
176
|
+
item.recurring.each { |j| add(j) } :
|
177
|
+
item.non_recurring.each { |j| add(j) }
|
221
178
|
else
|
222
179
|
# Check for dated reminders
|
223
180
|
if ICalPal::Reminder === item
|
224
|
-
if $opts[:dated] == 1 && item['due_date'].positive?
|
225
|
-
|
226
|
-
next
|
227
|
-
end
|
228
|
-
|
229
|
-
if $opts[:dated] == 2 && item['due_date'].zero?
|
230
|
-
$log.debug(':dated')
|
231
|
-
next
|
232
|
-
end
|
181
|
+
next if $opts[:dated] == 1 && item['due_date'].positive?
|
182
|
+
next if $opts[:dated] == 2 && item['due_date'].zero?
|
233
183
|
end
|
234
184
|
|
235
185
|
add(item)
|
@@ -248,11 +198,10 @@ end
|
|
248
198
|
|
249
199
|
# Sort the rows
|
250
200
|
begin
|
251
|
-
$log.info("Sorting
|
201
|
+
$log.info("Sorting #{$items.count} items by #{[ $opts[:sep], $opts[:sort], 'sdate' ]}, reverse #{$opts[:reverse].inspect}")
|
252
202
|
|
253
203
|
$items.sort_by! { |i| [ i[$opts[:sep]], i[$opts[:sort]], i['sdate'] ] }
|
254
204
|
$items.reverse! if $opts[:reverse]
|
255
|
-
$items.uniq!
|
256
205
|
rescue Exception => e
|
257
206
|
$log.info("Sorting failed: #{e}\n")
|
258
207
|
end
|
@@ -298,7 +247,7 @@ unless mu
|
|
298
247
|
when 'json' then items.map { |i| i.self }.to_json
|
299
248
|
when 'xml'
|
300
249
|
xml = items.map { |i| "<#{$opts[:cmd].chomp('s')}>#{i.to_xml}</#{$opts[:cmd].chomp('s')}>" }
|
301
|
-
"<#{$opts[:cmd]}>\n#{xml.join
|
250
|
+
"<#{$opts[:cmd]}>\n#{xml.join}</#{$opts[:cmd]}>"
|
302
251
|
when 'yaml' then items.map { |i| i.self }.to_yaml
|
303
252
|
when 'remind' then items.map { |i|
|
304
253
|
"REM #{i['sdate'].strftime('%F AT %R')} " +
|
@@ -363,14 +312,6 @@ items.each_with_index do |i, j|
|
|
363
312
|
# First property, value only
|
364
313
|
props << RDoc::Markup::Heading.new(2, value.to_s)
|
365
314
|
end
|
366
|
-
|
367
|
-
# unless k.positive?
|
368
|
-
# # First property, value only
|
369
|
-
# props << RDoc::Markup::Heading.new(2, value.to_s)
|
370
|
-
# else
|
371
|
-
# props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
|
372
|
-
# props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless (i['placeholder'])
|
373
|
-
# end
|
374
315
|
end
|
375
316
|
|
376
317
|
# Print it
|
data/bin/icalpal
CHANGED
@@ -10,6 +10,7 @@ begin
|
|
10
10
|
require 'yaml'
|
11
11
|
|
12
12
|
require_relative '../lib/icalpal'
|
13
|
+
require_relative '../lib/defaults'
|
13
14
|
require_relative '../lib/options'
|
14
15
|
require_relative '../lib/utils'
|
15
16
|
rescue LoadError => e
|
@@ -29,14 +30,14 @@ end
|
|
29
30
|
# All kids love log!
|
30
31
|
$log = Logger.new(STDERR, { level: $defaults[:common][:debug] })
|
31
32
|
$log.formatter = proc do |s, t, _p, m| # Severity, time, progname, msg
|
32
|
-
(
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
format("[%-5<sev>s] %<time>s [%<file>s:%<line>5s] - %<message>s\n",
|
34
|
+
{
|
35
|
+
sev: s,
|
36
|
+
time: t.strftime('%H:%M:%S.%L'),
|
37
|
+
file: caller(4, 1)[0].split('/')[-1].split(':')[0],
|
38
|
+
line: caller(4, 1)[0].split('/')[-1].split(':')[1],
|
39
|
+
message: m
|
40
|
+
})
|
40
41
|
end
|
41
42
|
|
42
43
|
$opts = ICalPal::Options.new.parse_options
|
@@ -57,9 +58,8 @@ $log.info("Options: #{$opts}")
|
|
57
58
|
# @param item[Object]
|
58
59
|
|
59
60
|
def add(item)
|
60
|
-
$log.
|
61
|
+
$log.debug("Adding #{item.dump} #{item['UUID']} (#{item['title']})") if item['UUID']
|
61
62
|
|
62
|
-
item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if ICalPal::Event === item && item['sdate']
|
63
63
|
$items.push(item)
|
64
64
|
end
|
65
65
|
|
@@ -128,108 +128,58 @@ unless success
|
|
128
128
|
end
|
129
129
|
|
130
130
|
$log.info("Loaded #{$rows.count} #{klass} rows")
|
131
|
+
$log.info("Window is #{$opts[:from]} to #{$opts[:to]}")
|
131
132
|
|
132
133
|
|
133
134
|
##################################################
|
134
135
|
# Process the data
|
135
136
|
|
136
137
|
# Add rows
|
137
|
-
$rows.
|
138
|
-
$log.debug("Row #{i}: #{row['ROWID']}:#{row['UUID']} - #{row['account']}/#{row['calendar']}/#{row['title']}")
|
139
|
-
|
138
|
+
$rows.each do |row|
|
140
139
|
# --es/--is
|
141
|
-
if $opts[:es].any? row['account']
|
142
|
-
|
143
|
-
next
|
144
|
-
end
|
145
|
-
|
146
|
-
unless $opts[:is].empty? || ($opts[:is].any? row['account'])
|
147
|
-
$log.debug(':is')
|
148
|
-
next
|
149
|
-
end
|
140
|
+
next if $opts[:es].any? row['account']
|
141
|
+
next unless $opts[:is].empty? || ($opts[:is].any? row['account'])
|
150
142
|
|
151
143
|
# --ec/--ic
|
152
144
|
unless klass == ICalPal::Store || !row['calendar']
|
153
|
-
if $opts[:ec].any? row['calendar']
|
154
|
-
|
155
|
-
next
|
156
|
-
end
|
157
|
-
|
158
|
-
unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
|
159
|
-
$log.debug(':ic')
|
160
|
-
next
|
161
|
-
end
|
145
|
+
next if $opts[:ec].any? row['calendar']
|
146
|
+
next unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
|
162
147
|
end
|
163
148
|
|
164
149
|
# Instantiate an item
|
165
150
|
item = klass.new(row)
|
166
151
|
|
167
152
|
# --et/--it
|
168
|
-
if $opts[:et].any? item['type']
|
169
|
-
|
170
|
-
next
|
171
|
-
end
|
172
|
-
|
173
|
-
unless $opts[:it].empty? || ($opts[:it].any? item['type'])
|
174
|
-
$log.debug(':it')
|
175
|
-
next
|
176
|
-
end
|
153
|
+
next if $opts[:et].any? item['type']
|
154
|
+
next unless $opts[:it].empty? || ($opts[:it].any? item['type'])
|
177
155
|
|
178
156
|
# --el/--il
|
179
|
-
if $opts[:el].any? item['list_name']
|
180
|
-
|
181
|
-
next
|
182
|
-
end
|
183
|
-
|
184
|
-
unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
|
185
|
-
$log.debug(':il')
|
186
|
-
next
|
187
|
-
end
|
157
|
+
next if $opts[:el].any? item['list_name']
|
158
|
+
next unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
|
188
159
|
|
189
160
|
# --match
|
190
161
|
if $opts[:match]
|
191
162
|
r = $opts[:match].split('=')
|
192
163
|
|
193
164
|
if item[r[0]].to_s.respond_to?(:match)
|
194
|
-
unless item[r[0]].to_s
|
195
|
-
$log.debug(':match')
|
196
|
-
next
|
197
|
-
end
|
165
|
+
next unless item[r[0]].to_s =~ Regexp.new(r[1], Regexp::IGNORECASE)
|
198
166
|
end
|
199
167
|
end
|
200
168
|
|
201
169
|
if ICalPal::Event === item
|
202
170
|
# Check for all-day and cancelled events
|
203
|
-
if $opts[:ea] && item['all_day'].positive?
|
204
|
-
|
205
|
-
|
206
|
-
end
|
207
|
-
|
208
|
-
if $opts[:ia] && !item['all_day'].positive?
|
209
|
-
$log.debug(':ia')
|
210
|
-
next
|
211
|
-
end
|
212
|
-
|
213
|
-
if item['status'] == :canceled
|
214
|
-
$log.debug(':canceled')
|
215
|
-
next
|
216
|
-
end
|
171
|
+
next if $opts[:ea] && item['all_day'].positive?
|
172
|
+
next if $opts[:ia] && !item['all_day'].positive?
|
173
|
+
next if item['status'] == :canceled
|
217
174
|
|
218
175
|
(item['has_recurrences'].positive?)?
|
219
|
-
item.recurring.each { |
|
220
|
-
item.non_recurring.each { |
|
176
|
+
item.recurring.each { |j| add(j) } :
|
177
|
+
item.non_recurring.each { |j| add(j) }
|
221
178
|
else
|
222
179
|
# Check for dated reminders
|
223
180
|
if ICalPal::Reminder === item
|
224
|
-
if $opts[:dated] == 1 && item['due_date'].positive?
|
225
|
-
|
226
|
-
next
|
227
|
-
end
|
228
|
-
|
229
|
-
if $opts[:dated] == 2 && item['due_date'].zero?
|
230
|
-
$log.debug(':dated')
|
231
|
-
next
|
232
|
-
end
|
181
|
+
next if $opts[:dated] == 1 && item['due_date'].positive?
|
182
|
+
next if $opts[:dated] == 2 && item['due_date'].zero?
|
233
183
|
end
|
234
184
|
|
235
185
|
add(item)
|
@@ -248,11 +198,10 @@ end
|
|
248
198
|
|
249
199
|
# Sort the rows
|
250
200
|
begin
|
251
|
-
$log.info("Sorting
|
201
|
+
$log.info("Sorting #{$items.count} items by #{[ $opts[:sep], $opts[:sort], 'sdate' ]}, reverse #{$opts[:reverse].inspect}")
|
252
202
|
|
253
203
|
$items.sort_by! { |i| [ i[$opts[:sep]], i[$opts[:sort]], i['sdate'] ] }
|
254
204
|
$items.reverse! if $opts[:reverse]
|
255
|
-
$items.uniq!
|
256
205
|
rescue Exception => e
|
257
206
|
$log.info("Sorting failed: #{e}\n")
|
258
207
|
end
|
@@ -298,7 +247,7 @@ unless mu
|
|
298
247
|
when 'json' then items.map { |i| i.self }.to_json
|
299
248
|
when 'xml'
|
300
249
|
xml = items.map { |i| "<#{$opts[:cmd].chomp('s')}>#{i.to_xml}</#{$opts[:cmd].chomp('s')}>" }
|
301
|
-
"<#{$opts[:cmd]}>\n#{xml.join
|
250
|
+
"<#{$opts[:cmd]}>\n#{xml.join}</#{$opts[:cmd]}>"
|
302
251
|
when 'yaml' then items.map { |i| i.self }.to_yaml
|
303
252
|
when 'remind' then items.map { |i|
|
304
253
|
"REM #{i['sdate'].strftime('%F AT %R')} " +
|
@@ -363,14 +312,6 @@ items.each_with_index do |i, j|
|
|
363
312
|
# First property, value only
|
364
313
|
props << RDoc::Markup::Heading.new(2, value.to_s)
|
365
314
|
end
|
366
|
-
|
367
|
-
# unless k.positive?
|
368
|
-
# # First property, value only
|
369
|
-
# props << RDoc::Markup::Heading.new(2, value.to_s)
|
370
|
-
# else
|
371
|
-
# props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
|
372
|
-
# props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless (i['placeholder'])
|
373
|
-
# end
|
374
315
|
end
|
375
316
|
|
376
317
|
# Print it
|
data/icalPal.gemspec
CHANGED
@@ -16,33 +16,35 @@ EOF
|
|
16
16
|
s.licenses = [ 'GPL-3.0-or-later' ]
|
17
17
|
|
18
18
|
s.metadata = {
|
19
|
-
'bug_tracker_uri' => "https://github.com/ajrosen/#{s.name}/issues"
|
19
|
+
'bug_tracker_uri' => "https://github.com/ajrosen/#{s.name}/issues",
|
20
|
+
'rubygems_mfa_required' => 'true'
|
20
21
|
}
|
21
22
|
|
22
23
|
s.files = Dir["#{s.name}.gemspec", 'bin/*', 'lib/*.rb']
|
23
24
|
s.executables = [ "#{s.name}" ]
|
24
25
|
s.extra_rdoc_files = [ 'README.md' ]
|
25
26
|
|
26
|
-
s.add_dependency 'nokogiri-plist', '~> 0.5.0'
|
27
|
-
s.add_dependency 'sqlite3', '~> 2.6.0' unless s.rubygems_version == `/usr/bin/gem --version`.strip
|
28
|
-
|
29
27
|
# The macOS and Homebrew versions of rubygems have incompatible
|
30
28
|
# requirements for sqlite3.
|
31
|
-
#
|
29
|
+
#
|
32
30
|
# macOS comes with version 1.3.13, so it does not need to be added
|
33
31
|
# as a dependency, but it cannot install anything newer:
|
34
|
-
#
|
32
|
+
#
|
35
33
|
# requires Ruby version >= 3.0, < 3.4.dev. The current ruby version is 2.6.10.
|
36
|
-
#
|
34
|
+
#
|
37
35
|
# Homebrew's Ruby formula does not come with sqlite3, so it does
|
38
36
|
# need to be added as a dependency, but it cannot install version
|
39
37
|
# 1.3.13:
|
40
|
-
#
|
38
|
+
#
|
41
39
|
# error: call to undeclared function
|
42
|
-
#
|
40
|
+
#
|
43
41
|
# So we must call add_dependency, but iff we are not building with
|
44
42
|
# macOS' Ruby installation.
|
45
43
|
|
44
|
+
s.add_dependency 'nokogiri-plist', '~> 0.5.0'
|
45
|
+
s.add_dependency 'sqlite3', '~> 2.6.0' unless s.rubygems_version == `/usr/bin/gem --version`.strip
|
46
|
+
s.add_dependency 'timezone', '>= 0.99', '~> 1.3.0'
|
47
|
+
|
46
48
|
s.bindir = 'bin'
|
47
49
|
s.required_ruby_version = '>= 2.6.0'
|
48
50
|
|