icalPal 3.10.0 → 3.10.2
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/bin/icalPal +3 -3
- data/ext/extconf.rb +1 -1
- data/lib/ToICalPal.rb +1 -1
- data/lib/event.rb +2 -2
- data/lib/options.rb +2 -8
- data/lib/rdt.rb +16 -10
- data/lib/reminder.rb +4 -11
- data/lib/version.rb +1 -1
- metadata +2 -3
- data/bin/icalpal +0 -363
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a19cb126e559cf9684d82d4d47eebfefe1a058dadc63ca642693d1ac488a97b0
|
|
4
|
+
data.tar.gz: 562f98a62d93eda9a03de31a5d1a09a7ddf9841d97abe0ea7d335a0976625dc7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 659550c0e455ff95a565dbc7d2af162872e14126b35f50f7c8cbaee43c154252d2ef45748421540ab2bc7bde89e50aba0135280a093d086a37e3df970adefd89
|
|
7
|
+
data.tar.gz: 941c218eef0998cc5356dac3e23cd91ea00046f6463b01e0bf6ebb651f008ce00c615d3c787945ec707a9fda35cdb25e733f10058464234ccb93d092213d727e
|
data/bin/icalPal
CHANGED
|
@@ -206,10 +206,10 @@ $rows.each do |row|
|
|
|
206
206
|
next if $opts[:ed] && item['completed'].zero?
|
|
207
207
|
next unless $opts[:id] || item['completed'].zero?
|
|
208
208
|
|
|
209
|
-
next if $opts[:dated] ==
|
|
210
|
-
next if $opts[:dated] ==
|
|
209
|
+
next if $opts[:dated] == 'undatedTasks' && item['due_date'] && item['due_date'].positive?
|
|
210
|
+
next if $opts[:dated] == 'datedTasks' && (!item['due_date'] || item['due_date'].zero?)
|
|
211
211
|
|
|
212
|
-
if $opts[:dated] ==
|
|
212
|
+
if $opts[:dated] == 'tasksDueBefore'
|
|
213
213
|
next unless item['due_date']
|
|
214
214
|
next unless item['due_date'].between?($opts[:from].to_i, $opts[:to].to_i)
|
|
215
215
|
end
|
data/ext/extconf.rb
CHANGED
|
@@ -7,7 +7,7 @@ begin
|
|
|
7
7
|
Gem.path.each { |p| gemdir = p if File.writable? p }
|
|
8
8
|
|
|
9
9
|
# Dependencies common to all environments
|
|
10
|
-
dependencies = %w[ plist
|
|
10
|
+
dependencies = %w[ plist ]
|
|
11
11
|
|
|
12
12
|
if RUBY_VERSION >= '3.4'
|
|
13
13
|
# bigdecimal is not part of the default gems starting from Ruby 3.4.0.
|
data/lib/ToICalPal.rb
CHANGED
data/lib/event.rb
CHANGED
|
@@ -95,7 +95,7 @@ module ICalPal
|
|
|
95
95
|
|
|
96
96
|
# Save as seconds, Time, RDT
|
|
97
97
|
ctime = obj[k] + ITIME
|
|
98
|
-
ctime -= Time.at(ctime).utc_offset if obj["#{k}_tz"] == '_float' && !zone
|
|
98
|
+
ctime -= Time.at(ctime).utc_offset if obj["#{k.split('_')[0]}_tz"] == '_float' && !zone
|
|
99
99
|
ttime = Time.at(ctime, in: zone)
|
|
100
100
|
|
|
101
101
|
@self["#{k[0]}seconds"] = ctime
|
|
@@ -265,7 +265,7 @@ module ICalPal
|
|
|
265
265
|
c['edate'] = ICalPal.nth(nth, day, nedate)
|
|
266
266
|
else
|
|
267
267
|
%w[ sdate edate ].each do |d|
|
|
268
|
-
diff = day - c[
|
|
268
|
+
diff = day - c[d].wday
|
|
269
269
|
diff += 7 if diff.negative?
|
|
270
270
|
|
|
271
271
|
t1 = Time.at(c[d].to_time)
|
data/lib/options.rb
CHANGED
|
@@ -275,15 +275,9 @@ module ICalPal
|
|
|
275
275
|
|
|
276
276
|
# Handle tasks command variants
|
|
277
277
|
if cli[:cmd] =~ /tasks/i
|
|
278
|
-
|
|
279
|
-
when 'undatedTasks'
|
|
280
|
-
cli[:dated] = 1
|
|
278
|
+
cli[:dated] = cli[:cmd]
|
|
281
279
|
|
|
282
|
-
|
|
283
|
-
cli[:dated] = 2
|
|
284
|
-
|
|
285
|
-
when 'tasksDueBefore'
|
|
286
|
-
cli[:dated] = 3
|
|
280
|
+
if cli[:dated] == 'tasksDueBefore'
|
|
287
281
|
cli.delete(:days) unless cli[:days]
|
|
288
282
|
cli[:from] = RDT.from_epoch(0) unless cli[:from]
|
|
289
283
|
cli[:to] = $today unless cli[:to]
|
data/lib/rdt.rb
CHANGED
|
@@ -25,15 +25,25 @@ module ICalPal
|
|
|
25
25
|
# @return [RDT] a new RDT
|
|
26
26
|
def self.conv(str)
|
|
27
27
|
case str
|
|
28
|
-
when 'yesterday' then $today
|
|
28
|
+
when 'yesterday' then $today.add(-1)
|
|
29
29
|
when 'today' then $today
|
|
30
|
-
when 'tomorrow' then $today
|
|
30
|
+
when 'tomorrow' then $today.add(1)
|
|
31
31
|
when /^\+([0-9]+)/ then $today + Regexp.last_match(1).to_i
|
|
32
32
|
when /^-([0-9]+)/ then $today - Regexp.last_match(1).to_i
|
|
33
33
|
else parse(str)
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# Add a number of days accounting for daylight saving time changes
|
|
38
|
+
#
|
|
39
|
+
# @param days [Integer] Number of days to add
|
|
40
|
+
# @return [RDT] A new RDT
|
|
41
|
+
def add(days)
|
|
42
|
+
n = self + days
|
|
43
|
+
t = Time.parse("#{n.year}-#{n.month}-#{n.day} #{n.hour}:#{n.min}:#{n.sec}")
|
|
44
|
+
RDT.from_time(t)
|
|
45
|
+
end
|
|
46
|
+
|
|
37
47
|
# Values can be +day before yesterday+, +yesterday+,
|
|
38
48
|
# +today+, +tomorrow+, +day after tomorrow+, or the result from
|
|
39
49
|
# strftime
|
|
@@ -41,10 +51,10 @@ module ICalPal
|
|
|
41
51
|
# @return [String] A string representation of self relative to
|
|
42
52
|
# today.
|
|
43
53
|
def to_s
|
|
44
|
-
return strftime($opts[:df]) if $opts && $opts[:
|
|
54
|
+
return strftime($opts[:df]) if $opts && $opts[:df] && $opts[:nrd]
|
|
45
55
|
return super unless $today && $opts
|
|
46
56
|
|
|
47
|
-
case
|
|
57
|
+
case (self - $today).round
|
|
48
58
|
when -2 then 'day before yesterday'
|
|
49
59
|
when -1 then 'yesterday'
|
|
50
60
|
when 0 then 'today'
|
|
@@ -68,13 +78,13 @@ module ICalPal
|
|
|
68
78
|
to_time.to_i
|
|
69
79
|
end
|
|
70
80
|
|
|
71
|
-
# @param [Integer] Optional UTC offset
|
|
81
|
+
# @param z [Integer] Optional UTC offset
|
|
72
82
|
# @return [RDT] Self at 00:00:00
|
|
73
83
|
def day_start(z = zone)
|
|
74
84
|
RDT.new(year, month, day, 0, 0, 0, z)
|
|
75
85
|
end
|
|
76
86
|
|
|
77
|
-
# @param [Integer] Optional UTC offset
|
|
87
|
+
# @param z [Integer] Optional UTC offset
|
|
78
88
|
# @return [RDT] Self at 23:59:59
|
|
79
89
|
def day_end(z = zone)
|
|
80
90
|
RDT.new(year, month, day, 23, 59, 59, z)
|
|
@@ -90,9 +100,5 @@ module ICalPal
|
|
|
90
100
|
[ hour, min, sec ]
|
|
91
101
|
end
|
|
92
102
|
|
|
93
|
-
# @return [Array] Only the year, month and day of self
|
|
94
|
-
def ymd
|
|
95
|
-
[ year, month, day ]
|
|
96
|
-
end
|
|
97
103
|
end
|
|
98
104
|
end
|
data/lib/reminder.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
r 'tzinfo'
|
|
2
|
-
|
|
3
1
|
module ICalPal
|
|
4
2
|
# Class representing items from the <tt>Reminders</tt> database
|
|
5
3
|
class Reminder
|
|
@@ -103,14 +101,9 @@ module ICalPal
|
|
|
103
101
|
|
|
104
102
|
# Due date
|
|
105
103
|
if @self['due_date']
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
rescue TZInfo::InvalidTimezoneIdentifier
|
|
110
|
-
zone = '+00:00'
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
@self['due'] = RDT.from_time(Time.at(@self['due_date'], in: zone))
|
|
104
|
+
@self['due_date'] += ITIME
|
|
105
|
+
@self['due_date'] -= obj['utc_offset'] if obj['utc_offset']
|
|
106
|
+
@self['due'] = RDT.from_time(Time.at(@self['due_date']))
|
|
114
107
|
end
|
|
115
108
|
|
|
116
109
|
# Notes
|
|
@@ -196,9 +189,9 @@ r1.zNotes as notes,
|
|
|
196
189
|
r1.zPriority as priority,
|
|
197
190
|
r1.zContactHandles as messaging,
|
|
198
191
|
r1.zDueDateDeltaAlertsData as alert,
|
|
199
|
-
r1.zTimezone as timezone,
|
|
200
192
|
r1.zckIdentifier as id,
|
|
201
193
|
r1.zCompleted as completed,
|
|
194
|
+
r1.zDisplayDateUpdatedForSecondsFromGMT as utc_offset,
|
|
202
195
|
|
|
203
196
|
bl1.zBadgeEmblem as badge,
|
|
204
197
|
bl1.zColor as color,
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: icalPal
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.10.
|
|
4
|
+
version: 3.10.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Rosen
|
|
@@ -22,7 +22,6 @@ extra_rdoc_files:
|
|
|
22
22
|
files:
|
|
23
23
|
- README.md
|
|
24
24
|
- bin/icalPal
|
|
25
|
-
- bin/icalpal
|
|
26
25
|
- ext/extconf.rb
|
|
27
26
|
- icalPal.gemspec
|
|
28
27
|
- lib/EventKit.rb
|
|
@@ -62,7 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
62
61
|
- !ruby/object:Gem::Version
|
|
63
62
|
version: '0'
|
|
64
63
|
requirements: []
|
|
65
|
-
rubygems_version: 3.
|
|
64
|
+
rubygems_version: 3.6.9
|
|
66
65
|
specification_version: 4
|
|
67
66
|
summary: Command-line tool to query the macOS Calendar and Reminders
|
|
68
67
|
test_files: []
|
data/bin/icalpal
DELETED
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
# rubocop: disable Style/RedundantBegin
|
|
4
|
-
|
|
5
|
-
# require a gem
|
|
6
|
-
#
|
|
7
|
-
# @param gem [String] The gem
|
|
8
|
-
def r(gem)
|
|
9
|
-
begin
|
|
10
|
-
# puts "require \"#{gem}\""
|
|
11
|
-
require gem
|
|
12
|
-
rescue LoadError => e
|
|
13
|
-
$stderr.puts "FATAL: icalPal is missing a dependency: #{gem}"
|
|
14
|
-
$stderr.puts e
|
|
15
|
-
$stderr.puts
|
|
16
|
-
abort "Try installing with 'gem install #{gem}'"
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# require_relative a library
|
|
21
|
-
#
|
|
22
|
-
# @param library [String] The library
|
|
23
|
-
def rr(library)
|
|
24
|
-
begin
|
|
25
|
-
# puts "require_relative \"../lib/#{library}\""
|
|
26
|
-
require_relative "../lib/#{library}"
|
|
27
|
-
rescue LoadError => e
|
|
28
|
-
$stderr.puts "FATAL: Could not load library: #{library}"
|
|
29
|
-
$stderr.puts
|
|
30
|
-
abort e.message
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# rubocop: enable Style/RedundantBegin
|
|
35
|
-
|
|
36
|
-
%w[ logger csv json rdoc sqlite3 yaml ].each { |g| r g }
|
|
37
|
-
%w[ icalPal defaults options utils ].each { |l| rr l }
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
##################################################
|
|
41
|
-
# Load options
|
|
42
|
-
|
|
43
|
-
# All kids love log!
|
|
44
|
-
$log = Logger.new(STDERR, { level: $defaults[:common][:debug] })
|
|
45
|
-
$log.formatter = proc do |s, t, _p, m| # Severity, time, progname, msg
|
|
46
|
-
format("[%-5<sev>s] %<time>s [%<file>s:%<line>5s] - %<message>s\n",
|
|
47
|
-
{
|
|
48
|
-
sev: s,
|
|
49
|
-
time: t.strftime('%H:%M:%S.%L'),
|
|
50
|
-
file: caller(4, 1)[0].split('/')[-1].split(':')[0],
|
|
51
|
-
line: caller(4, 1)[0].split('/')[-1].split(':')[1],
|
|
52
|
-
message: m
|
|
53
|
-
})
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
$opts = ICalPal::Options.new.parse_options
|
|
57
|
-
|
|
58
|
-
$rows = [] # Rows from the database
|
|
59
|
-
$sections = [] # Calendar list sections
|
|
60
|
-
$items = [] # Items to be printed
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
##################################################
|
|
64
|
-
# All kids love log!
|
|
65
|
-
|
|
66
|
-
$log.info("Options:\n#{$opts.to_json}")
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
##################################################
|
|
70
|
-
# Add an item to the list
|
|
71
|
-
#
|
|
72
|
-
# @param item[Object]
|
|
73
|
-
|
|
74
|
-
def add(item)
|
|
75
|
-
$log.debug("Adding #{item.dump} #{item['UUID']} (#{item['title']})") if item['UUID']
|
|
76
|
-
|
|
77
|
-
$items.push(item)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
##################################################
|
|
82
|
-
# Load the data
|
|
83
|
-
|
|
84
|
-
# What are we getting?
|
|
85
|
-
klass = ICalPal.call($opts[:cmd])
|
|
86
|
-
success = false
|
|
87
|
-
|
|
88
|
-
# Get it
|
|
89
|
-
$opts[:db].each do |db|
|
|
90
|
-
$log.debug("Trying #{db}")
|
|
91
|
-
|
|
92
|
-
if klass == ICalPal::Reminder
|
|
93
|
-
# Load all .sqlite files
|
|
94
|
-
$log.debug("Loading *.sqlite in #{db}")
|
|
95
|
-
Dir.glob("#{db}/*.sqlite").each do |d|
|
|
96
|
-
success = true
|
|
97
|
-
|
|
98
|
-
rows = klass.load_data(d, klass::QUERY)
|
|
99
|
-
$rows += rows
|
|
100
|
-
|
|
101
|
-
sections = klass.load_data(d, klass::SECTIONS_QUERY)
|
|
102
|
-
$sections += sections
|
|
103
|
-
|
|
104
|
-
$log.info("Loaded #{rows.length} rows and #{sections.length} sections from #{d}")
|
|
105
|
-
end
|
|
106
|
-
else
|
|
107
|
-
# Load database
|
|
108
|
-
rows = ICalPal.load_data(db, klass::QUERY)
|
|
109
|
-
$rows += rows
|
|
110
|
-
|
|
111
|
-
success = true
|
|
112
|
-
|
|
113
|
-
$log.info("Loaded #{rows.length} rows from #{db}")
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
rescue Errno::EPERM
|
|
117
|
-
# Probably need Full Disk Access
|
|
118
|
-
|
|
119
|
-
rescue SQLite3::CantOpenException
|
|
120
|
-
# Non-fatal exception, try the next one
|
|
121
|
-
|
|
122
|
-
rescue StandardError => e
|
|
123
|
-
# Log the error and (try to) continue
|
|
124
|
-
$log.error("#{db}: #{e.message}")
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Make sure we opened at least one database
|
|
128
|
-
unless success
|
|
129
|
-
$log.fatal('Could not open database')
|
|
130
|
-
|
|
131
|
-
# SQLite3 does not return useful error messages. If any databases
|
|
132
|
-
# failed because of EPERM (operation not permitted), our parent
|
|
133
|
-
# process might need Full Disk Access, and we should suggest that.
|
|
134
|
-
eperm = 0
|
|
135
|
-
|
|
136
|
-
$opts[:db].each do |db|
|
|
137
|
-
# Use a real open to get a useful error
|
|
138
|
-
File.open(db).close
|
|
139
|
-
rescue Exception => e
|
|
140
|
-
$log.fatal("#{e.class}: #{db}")
|
|
141
|
-
|
|
142
|
-
eperm = 1 if e.instance_of?(Errno::EPERM)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
if eperm.positive?
|
|
146
|
-
$stderr.puts
|
|
147
|
-
$stderr.puts "Does #{ancestor} have Full Disk Access in System Settings?"
|
|
148
|
-
$stderr.puts
|
|
149
|
-
$stderr.puts "Try running: open 'x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles'"
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
abort
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
$log.debug("Loaded #{$rows.length} #{klass} items")
|
|
156
|
-
$log.info("Window is #{$opts[:from]} to #{$opts[:to]}") if $opts[:from]
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
##################################################
|
|
160
|
-
# Process the data
|
|
161
|
-
|
|
162
|
-
# Add rows
|
|
163
|
-
$rows.each do |row|
|
|
164
|
-
# --es/--is
|
|
165
|
-
next if $opts[:es].any? row['account']
|
|
166
|
-
next unless $opts[:is].empty? || ($opts[:is].any? row['account'])
|
|
167
|
-
|
|
168
|
-
# --ec/--ic
|
|
169
|
-
unless klass == ICalPal::Store || !row['calendar']
|
|
170
|
-
next if $opts[:ec].any? row['calendar']
|
|
171
|
-
next unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
# Instantiate an item
|
|
175
|
-
item = klass.new(row)
|
|
176
|
-
|
|
177
|
-
# --et/--it
|
|
178
|
-
next if $opts[:et].any? item['type']
|
|
179
|
-
next unless $opts[:it].empty? || ($opts[:it].any? item['type'])
|
|
180
|
-
|
|
181
|
-
# --el/--il
|
|
182
|
-
next if $opts[:el].any? item['list_name']
|
|
183
|
-
next unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
|
|
184
|
-
|
|
185
|
-
# --match
|
|
186
|
-
if $opts[:match]
|
|
187
|
-
r = $opts[:match].split('=')
|
|
188
|
-
|
|
189
|
-
if item[r[0]].to_s.respond_to?(:match)
|
|
190
|
-
next unless item[r[0]].to_s =~ Regexp.new(r[1], Regexp::IGNORECASE)
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
if item.is_a?(ICalPal::Event)
|
|
195
|
-
# Check for all-day and cancelled events
|
|
196
|
-
next if $opts[:ea] && item['all_day'].positive?
|
|
197
|
-
next if $opts[:ia] && !item['all_day'].positive?
|
|
198
|
-
next if item['status'] == :canceled
|
|
199
|
-
|
|
200
|
-
(item['has_recurrences'].positive?)?
|
|
201
|
-
item.recurring.each { |j| add(j) } :
|
|
202
|
-
item.non_recurring.each { |j| add(j) }
|
|
203
|
-
else
|
|
204
|
-
# Check for completed or dated reminders
|
|
205
|
-
if item.is_a?(ICalPal::Reminder)
|
|
206
|
-
next if $opts[:ed] && item['completed'].zero?
|
|
207
|
-
next unless $opts[:id] || item['completed'].zero?
|
|
208
|
-
|
|
209
|
-
next if $opts[:dated] == 1 && item['due_date'] && item['due_date'].positive?
|
|
210
|
-
next if $opts[:dated] == 2 && (!item['due_date'] || item['due_date'].zero?)
|
|
211
|
-
|
|
212
|
-
if $opts[:dated] == 3
|
|
213
|
-
next unless item['due_date']
|
|
214
|
-
next unless item['due_date'].between?($opts[:from].to_i, $opts[:to].to_i)
|
|
215
|
-
end
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
add(item)
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
# Add placeholders for empty days
|
|
223
|
-
if $opts[:sed] && $opts[:sd] && klass == ICalPal::Event
|
|
224
|
-
days = $items.collect { |i| i['sday'] }.uniq.sort
|
|
225
|
-
|
|
226
|
-
$opts[:days].times do |n|
|
|
227
|
-
day = $opts[:from] + n
|
|
228
|
-
$items.push(klass.new(day)) unless days.any? { |i| i.to_s == day.to_s }
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
# Sort the rows
|
|
233
|
-
begin
|
|
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
|
-
|
|
239
|
-
$log.info("Sorting #{$items.count} items by #{$sort_attrs}, reverse #{$opts[:reverse].inspect}")
|
|
240
|
-
|
|
241
|
-
$items.sort!
|
|
242
|
-
$items.reverse! if $opts[:reverse]
|
|
243
|
-
rescue Exception => e
|
|
244
|
-
$log.info("Sorting failed: #{e}\n")
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
$log.debug("#{$items.count} items remain")
|
|
248
|
-
|
|
249
|
-
# Configure formatting
|
|
250
|
-
mu = case $opts[:output]
|
|
251
|
-
when 'ansi' then RDoc::Markup::ToAnsi.new
|
|
252
|
-
when 'default' then RDoc::Markup::ToICalPal.new($opts)
|
|
253
|
-
when 'html'
|
|
254
|
-
rdoc = RDoc::Options.new
|
|
255
|
-
rdoc.pipe = true
|
|
256
|
-
rdoc.output_decoration = false
|
|
257
|
-
RDoc::Markup::ToHtml.new(rdoc)
|
|
258
|
-
when 'md' then RDoc::Markup::ToMarkdown.new
|
|
259
|
-
when 'rdoc' then RDoc::Markup::ToRdoc.new
|
|
260
|
-
when 'toc' then RDoc::Markup::ToTableOfContents.new
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
##################################################
|
|
265
|
-
# Print the data
|
|
266
|
-
|
|
267
|
-
items = $items[0..($opts[:li] - 1)]
|
|
268
|
-
|
|
269
|
-
unless mu
|
|
270
|
-
$log.debug("Output in #{$opts[:output]} format")
|
|
271
|
-
|
|
272
|
-
puts case $opts[:output]
|
|
273
|
-
when 'csv'
|
|
274
|
-
# Get all headers
|
|
275
|
-
headers = []
|
|
276
|
-
items.each { |i| headers += i.keys }
|
|
277
|
-
headers.uniq!
|
|
278
|
-
|
|
279
|
-
# Populate a CSV::Table
|
|
280
|
-
table = CSV::Table.new([], headers: headers)
|
|
281
|
-
items.each { |i| table << i.to_csv(headers) }
|
|
282
|
-
|
|
283
|
-
table
|
|
284
|
-
when 'hash' then items.map { |i| i.self }
|
|
285
|
-
when 'json' then items.map { |i| i.self }.to_json
|
|
286
|
-
when 'xml'
|
|
287
|
-
xml = items.map { |i| "<#{$opts[:cmd].chomp('s')}>#{i.to_xml}</#{$opts[:cmd].chomp('s')}>" }
|
|
288
|
-
"<#{$opts[:cmd]}>\n#{xml.join}</#{$opts[:cmd]}>"
|
|
289
|
-
when 'yaml' then items.map { |i| i.self }.to_yaml
|
|
290
|
-
when 'remind' then items.map { |i|
|
|
291
|
-
"REM #{i['sdate'].strftime('%F AT %R')} " +
|
|
292
|
-
"DURATION #{((i['edate'] - i['sdate']).to_f * 1440).to_i} " +
|
|
293
|
-
"MSG #{i['title'].gsub(/([[:cntrl:]])/) { |c| c.dump[1..-2] }}"
|
|
294
|
-
}.join("\n")
|
|
295
|
-
else abort "No formatter for #{$opts[:output]}"
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
exit
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
$log.debug("Formatting with #{mu.inspect}")
|
|
302
|
-
|
|
303
|
-
doc = RDoc::Markup::Document.new
|
|
304
|
-
section = nil
|
|
305
|
-
|
|
306
|
-
items.each_with_index do |i, j|
|
|
307
|
-
$log.debug("Print #{j}: #{i.inspect}")
|
|
308
|
-
|
|
309
|
-
# --li
|
|
310
|
-
break if $opts[:li].positive? && j >= $opts[:li]
|
|
311
|
-
|
|
312
|
-
# Use RDoc::Markup::Verbatim to save the item
|
|
313
|
-
v = RDoc::Markup::Verbatim.new
|
|
314
|
-
v.format = i
|
|
315
|
-
doc << v
|
|
316
|
-
|
|
317
|
-
# Sections
|
|
318
|
-
if $opts[:sep] && section != i[$opts[:sep]]
|
|
319
|
-
$log.debug("New section '#{$opts[:sep]}': #{i[$opts[:sep]]}")
|
|
320
|
-
|
|
321
|
-
doc << RDoc::Markup::Raw.new($opts[:sep])
|
|
322
|
-
|
|
323
|
-
doc << RDoc::Markup::BlankLine.new if j.positive?
|
|
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
|
|
328
|
-
|
|
329
|
-
section = i[$opts[:sep]]
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
# Item
|
|
333
|
-
props = RDoc::Markup::List.new(:BULLET)
|
|
334
|
-
|
|
335
|
-
# Properties
|
|
336
|
-
$opts[:props].each_with_index do |prop, k|
|
|
337
|
-
value = i[prop]
|
|
338
|
-
|
|
339
|
-
next unless value
|
|
340
|
-
next if value.is_a?(Array) && !value[0]
|
|
341
|
-
next if value.is_a?(String) && value.empty?
|
|
342
|
-
|
|
343
|
-
$log.debug("#{prop}: #{value}")
|
|
344
|
-
|
|
345
|
-
# Use Raw to save the property
|
|
346
|
-
props << RDoc::Markup::Raw.new(prop)
|
|
347
|
-
|
|
348
|
-
if k.positive?
|
|
349
|
-
props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
|
|
350
|
-
props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless (i['placeholder'])
|
|
351
|
-
else
|
|
352
|
-
# First property, value only
|
|
353
|
-
props << RDoc::Markup::Heading.new(2, value.to_s)
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
# Print it
|
|
358
|
-
props << RDoc::Markup::BlankLine.new unless props.empty?
|
|
359
|
-
|
|
360
|
-
doc << props
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
print doc.accept(mu)
|