Skip to content

Commit d7788cb

Browse files
committed
Version 3.8.0
Major update. See CHANGELOG for details.
1 parent a3d6273 commit d7788cb

File tree

14 files changed

+466
-96
lines changed

14 files changed

+466
-96
lines changed

.rubocop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ Style/GlobalStdStream:
118118
Enabled: false
119119

120120
Style/GlobalVars:
121-
AllowedVariables: [ $defaults, $items, $log, $now, $opts, $today, $rows ]
121+
AllowedVariables: [ $defaults, $items, $log, $now, $opts, $today, $rows, $sections ]
122122

123123
Style/LineEndConcatenation:
124124
Enabled: false

CHANGELOG.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,68 @@
1+
icalPal-3.8.0
2+
==================
3+
4+
* Add properties for tasks commands
5+
* id
6+
* group
7+
* section
8+
* tags
9+
* assignee
10+
* timezone
11+
* Notifications
12+
* due (due_date formatted with --df and --tf options)
13+
* alert (Early Reminder)
14+
* location, proximity (arriving or leaving), radius (in meters)
15+
* messaging (email addresses and phone numbers from "When Messaging")
16+
17+
* Limit properties for accounts command
18+
* account
19+
* notes
20+
* owner
21+
* type
22+
* delegations
23+
24+
* Limit properties for calendar command
25+
* account
26+
* UUID
27+
* shared\_owner_name, shared\_owner_address
28+
* self\_identity_email, owner\_identity_email
29+
* subcal\_account_id, subcal_url
30+
* published_URL
31+
* notes
32+
* locale
33+
34+
* New command aliases
35+
* reminders for tasks commands
36+
37+
* Property aliases
38+
* name or title for account in accounts command
39+
* name or title for calendar in calendars command
40+
* name or event for title in events commands
41+
* name, reminder, or task for title in tasks commands
42+
43+
* Bug fixes
44+
* Add additional properties from --iep/--aep with --iep/--aep all
45+
* Do not include properties twice
46+
* Adjust indentation of task properties to match icalBuddy
47+
* Sorting tasks
48+
* Handle Errno::EPERM (needs Full Disk Access) for Reminders database
49+
50+
icalPal-3.7.2
51+
==================
52+
53+
* atp, etp, and itp options were not aliases for aep, eep, and iep per the documentation
54+
55+
icalPal-3.7.1
56+
==================
57+
58+
* Fix stores command (#36)
59+
60+
61+
icalPal-3.7.0
62+
==================
63+
64+
* Add sseconds/eseconds fields for easier post-processing
65+
166
icalPal-3.6.1 / 2025-05-18
267
==================
368

README.md

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@ on any system with [Ruby](https://www.ruby-lang.org/) and access to a
1010
Calendar or Reminders database.
1111

1212
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
13-
1413
**Table of Contents**
1514

1615
- [Installation](#installation)
1716
- [Features](#features)
18-
- [Compatability with icalBuddy](#compatability-with-icalbuddy)
1917
- [Additional commands](#additional-commands)
2018
- [Additional options](#additional-options)
19+
- [Additional properties](#additional-properties)
2120
- [Usage](#usage)
2221
- [Output formats](#output-formats)
2322
- [History](#history)
@@ -67,24 +66,61 @@ as *Stores*; you can run ```icalPal stores``` instead.
6766

6867
Shows only reminders that have a due date.
6968

69+
```icalPal reminders```
70+
71+
*reminders*, *datedReminders*, and *undatedReminders* can be used instead of *tasks*
72+
7073
### Additional options
7174

7275
* 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```.
7376
* The ```-c``` part is optional, but you cannot abbreviate the command if you leave it off.
7477
* Use ```-o``` to print the output in different formats. CSV or JSON are intertesting choices.
75-
* Copy your Calendar database file and use ```--db``` on it.
78+
* Copy your Calendar or Reminders database file and use ```--db``` on it.
7679
* ```--it``` and ```--et``` will filter by Calendar *type*. Types are **Local**, **Exchange**, **CalDAV**, **MobileMe**, **Subscribed**, **Birthdays**, and **Reminders**
7780
* ```--il``` and ```-el``` will filter by Reminder list
7881
* ```--ia``` includes *only* all-day events (opposite of ```--ea```)
7982
* ```--aep``` is like ```--iep```, but *adds* to the default property list instead of replacing it.
8083
* ```--sep``` to separate by any property, not just calendar (```--sc```) or date (```--sd```)
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/).
84+
* ```--color``` uses a wider color palette. Colors are what you have chosen in the Calendar and Reminders apps, including custom colors
8285
* ```--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
8386

8487
Because icalPal is written in Ruby, and not a native Mac application,
8588
you can run it just about anywhere. It's been tested with the
86-
versions of Ruby included with macOS Sequoia (2.6.10) and
87-
[Homebrew](https://brew.sh/) (3.4.4).
89+
versions of Ruby included with macOS Sequoia and Tahoe (2.6.10) and
90+
[Homebrew](https://brew.sh/) (3.4.x).
91+
92+
### Additional properties
93+
94+
Several additional properties are available for each command.
95+
96+
* Accounts
97+
* account
98+
* notes
99+
* owner
100+
* type
101+
* delegations
102+
103+
* Calendar
104+
* account
105+
* shared\_owner_name, shared\_owner_address
106+
* self\_identity_email, owner\_identity_email
107+
* subcal_account_id, subcal_url
108+
* published_URL
109+
* notes
110+
* locale
111+
112+
* Tasks
113+
* id
114+
* group
115+
* section
116+
* tags
117+
* assignee
118+
* timezone
119+
* Notifications
120+
* due (due_date formatted with --df and --tf options)
121+
* alert (Early Reminder)
122+
* location, proximity (arriving or leaving), radius (in meters)
123+
* messaging (email addresses and phone numbers from "When Messaging")
88124

89125
## Usage
90126

@@ -109,16 +145,16 @@ Global options:
109145
```
110146
-c, --cmd=COMMAND Command to run
111147
--db=DB Use DB file instead of Calendar
112-
(default: ["/Users/user/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb", "/Users/user/Library/Calendars/Calendar.sqlitedb"]
148+
(default: ["$HOME/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb", "/Users/user/Library/Calendars/Calendar.sqlitedb"]
113149
For the tasks commands this should be a directory containing .sqlite files
114-
(default: /Users/user/Library/Group Containers/group.com.apple.reminders/Container_v1/Stores)
115-
--cf=FILE Set config file path (default: /Users/user/.icalpal)
150+
(default: $HOME/Library/Group Containers/group.com.apple.reminders/Container_v1/Stores)
151+
--cf=FILE Set config file path (default: $HOME/.icalpal)
116152
--norc Ignore ICALPAL and ICALPAL_CONFIG environment variables
117153
-o, --output=FORMAT Print as FORMAT (default: default)
118154
[ansi, csv, default, hash, html, json, md, rdoc, remind, toc, xml, yaml]
119155
```
120156

121-
Including/excluding calendars and reminders:
157+
Including/excluding accounts, calendars, reminders and items:
122158
```
123159
--is=ACCOUNTS List of accounts to include
124160
--es=ACCOUNTS List of accounts to exclude
@@ -133,8 +169,7 @@ Including/excluding calendars and reminders:
133169
--il=LISTS List of reminder lists to include
134170
--el=LISTS List of reminder lists to exclude
135171
136-
--match=FIELD=REGEXP
137-
Include only items whose FIELD matches REGEXP (ignoring case)
172+
--match=FIELD=REGEX Include only items whose FIELD matches REGEXP (ignoring case)
138173
```
139174

140175
Choosing dates:

bin/icalpal

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ end
5656
$opts = ICalPal::Options.new.parse_options
5757

5858
$rows = [] # Rows from the database
59+
$sections = [] # Calendar list sections
5960
$items = [] # Items to be printed
6061

6162

@@ -93,19 +94,31 @@ $opts[:db].each do |db|
9394
# Load all .sqlite files
9495
$log.debug("Loading *.sqlite in #{db}")
9596
Dir.glob("#{db}/*.sqlite").each do |d|
96-
$rows += ICalPal.load_data(d, klass::QUERY)
9797
success = true
9898

99+
rows = klass.load_data(d, klass::QUERY)
100+
$rows += rows
101+
102+
sections = klass.load_data(d, klass::SECTIONS_QUERY)
103+
$sections += sections
104+
105+
$log.info("Loaded #{rows.length} rows and #{sections.length} sections from #{d}")
99106
rescue SQLite3::CantOpenException
100107
# Non-fatal exception, try the next one
101108
end
109+
110+
rescue Errno::EPERM
111+
# Probably need Full Disk Access
102112
end
103113
else
104114
# Load database
105115
begin
106-
$rows += ICalPal.load_data(db, klass::QUERY)
116+
rows = ICalPal.load_data(db, klass::QUERY)
117+
$rows += rows
118+
107119
success = true
108120

121+
$log.info("Loaded #{rows.length} rows from #{db}")
109122
rescue SQLite3::CantOpenException
110123
# Non-fatal exception, try the next one
111124
end
@@ -140,8 +153,7 @@ unless success
140153
abort
141154
end
142155

143-
$log.info("Loaded #{$rows.count} #{klass} rows")
144-
$log.info("Window is #{$opts[:from]} to #{$opts[:to]}")
156+
$log.info("Window is #{$opts[:from]} to #{$opts[:to]}") if $opts[:from]
145157

146158

147159
##################################################
@@ -191,8 +203,8 @@ $rows.each do |row|
191203
else
192204
# Check for dated reminders
193205
if ICalPal::Reminder === item
194-
next if $opts[:dated] == 1 && item['due_date'].positive?
195-
next if $opts[:dated] == 2 && item['due_date'].zero?
206+
next if $opts[:dated] == 1 && item['due_date'] && item['due_date'].positive?
207+
next if $opts[:dated] == 2 && (!item['due_date'] || item['due_date'].zero?)
196208
end
197209

198210
add(item)
@@ -211,9 +223,14 @@ end
211223

212224
# Sort the rows
213225
begin
214-
$log.info("Sorting #{$items.count} items by #{[ $opts[:sep], $opts[:sort], 'sdate' ]}, reverse #{$opts[:reverse].inspect}")
226+
sort = []
227+
sort.push $opts[:sep] if $opts[:sep]
228+
sort.push $opts[:sort] if $opts[:sort]
229+
sort.push 'sdate'
230+
231+
$log.info("Sorting #{$items.count} items by #{sort}, reverse #{$opts[:reverse].inspect}")
215232

216-
$items.sort_by! { |i| [ i[$opts[:sep]], i[$opts[:sort]], i['sdate'] ] }
233+
$items.sort_by! { |i| sort }
217234
$items.reverse! if $opts[:reverse]
218235
rescue Exception => e
219236
$log.info("Sorting failed: #{e}\n")

lib/EventKit.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,29 @@ class EventKit
3737
'low', # 9
3838
].freeze
3939

40+
EKReminderDueDateDeltaUnit = %w[
41+
minutes
42+
hours
43+
days
44+
weeks
45+
months
46+
].freeze
47+
48+
EKReminderProximity = [
49+
nil,
50+
'arriving',
51+
'leaving'
52+
].freeze
53+
54+
EKReminderAccessLevel = [
55+
{ value: 2, level: 'can add' }
56+
].freeze
57+
58+
EKReminderSharingStatus = [
59+
{ value: 1, status: 'accepted' },
60+
{ value: 5, status: 'invited' }
61+
].freeze
62+
4063
# EKSourceType (with color)
4164
EKSourceType = [
4265
{ name: 'Local', color: '#FFFFFF' }, # White

lib/calendar.rb

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,41 @@ module ICalPal
33
class Calendar
44
include ICalPal
55

6+
def [](k)
7+
case k
8+
when 'name', 'title' # Aliases
9+
@self['calendar']
10+
11+
else @self[k]
12+
end
13+
end
14+
615
QUERY = <<~SQL.freeze
716
SELECT DISTINCT
817
9-
Store.name AS account,
10-
Calendar.title AS calendar,
11-
*
18+
s1.name AS account,
19+
20+
c1.UUID,
21+
c1.title AS calendar,
22+
23+
c1.shared_owner_name,
24+
c1.shared_owner_address,
25+
26+
c1.published_URL,
27+
c1.self_identity_email,
28+
c1.owner_identity_email,
29+
c1.notes,
30+
c1.subcal_account_id,
31+
c1.subcal_url,
32+
c1.locale
1233
13-
FROM #{self.name.split('::').last}
34+
FROM #{self.name.split('::').last} c1
1435
15-
JOIN Store ON store_id = Store.rowid
36+
JOIN Store s1 ON c1.store_id = s1.rowid
1637
17-
WHERE Store.disabled IS NOT 1
18-
AND Store.display_order IS NOT -1
19-
AND Calendar.flags IS NOT 519
38+
WHERE s1.disabled IS NOT 1
39+
AND s1.display_order IS NOT -1
40+
AND c1.flags IS NOT 519
2041
SQL
2142

2243
end

lib/defaults.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,27 @@
4444
dated: 0,
4545
db: [ ICalPal::Reminder::DB_PATH ],
4646
iep: %w[ title notes due priority ],
47+
ps: [ "\n " ],
4748
sort: 'prio',
4849
},
4950

5051
undatedTasks: {
5152
dated: 1,
5253
db: [ ICalPal::Reminder::DB_PATH ],
5354
iep: %w[ title notes due priority ],
55+
ps: [ "\n " ],
5456
sort: 'prio',
5557
},
5658

5759
datedTasks: {
5860
dated: 2,
5961
db: [ ICalPal::Reminder::DB_PATH ],
6062
iep: %w[ title notes due priority ],
63+
ps: [ "\n " ],
6164
sort: 'prio',
6265
},
6366

64-
stores: {
67+
accounts: {
6568
iep: %w[ account type ],
6669
sort: 'account',
6770
},

0 commit comments

Comments
 (0)