icalPal 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/icalPal +86 -27
- data/icalPal.gemspec +1 -1
- data/lib/event.rb +40 -10
- data/lib/icalPal.rb +12 -0
- data/lib/options.rb +1 -1
- data/lib/rdt.rb +7 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c08ab4aaf0558f9648fbefbb9eaea1aac9c50de2770e1897699b619ba92ef6b6
|
4
|
+
data.tar.gz: '087d14b6e855bdbf2681e526941baec600fed607a6e6760b09f4361bb33735d8'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70934664b432d1b99e0f200bd1e78a2b142f9b1b9b405c8763913aa9d414a83171f4876e0145ff741854abd1b643fdb4a72031b564af14a2a4f1764cc494a13f
|
7
|
+
data.tar.gz: '0580e0d9556919516b1c2d4a4ea11306a2a8786ea80ee5ccc5821c97f899d7bc95c3499c7f5d9dc05ac091dba53867b01a7b676e0bd20881563d7529e16a1a31'
|
data/bin/icalPal
CHANGED
@@ -59,45 +59,91 @@ q = klass::QUERY
|
|
59
59
|
$log.debug(q.gsub(/\n/, ' '))
|
60
60
|
|
61
61
|
# Get it
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
begin
|
63
|
+
stmt = $db.prepare(q)
|
64
|
+
abort(stmt.columns.sort.join(' ')) if $opts[:props].any? 'list'
|
65
|
+
$opts[:props] = stmt.columns - $opts[:eep] if $opts[:props].any? 'all'
|
66
|
+
|
67
|
+
# Iterate the SQLite3::ResultSet once
|
68
|
+
stmt.execute.each_with_index { |i, j| $rows[j] = i }
|
69
|
+
stmt.close
|
65
70
|
|
66
|
-
#
|
67
|
-
|
68
|
-
|
71
|
+
# Close the database
|
72
|
+
$db.close
|
73
|
+
$log.debug("Closed #{$opts[:db]}")
|
69
74
|
|
70
|
-
|
71
|
-
$db.
|
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)
|
80
|
+
end
|
81
|
+
|
82
|
+
$log.info("Loaded #{$rows.count} rows from #{$opts[:db]}")
|
72
83
|
|
73
84
|
|
74
85
|
##################################################
|
75
86
|
# Process the data
|
76
87
|
|
77
88
|
# Add rows
|
78
|
-
$rows.
|
89
|
+
$rows.each_with_index do |row, i|
|
90
|
+
$log.debug("Row #{i}: #{row['ROWID']}:#{row['UUID']} - #{row['account']}/#{row['calendar']}/#{row['title']}")
|
91
|
+
|
79
92
|
# --es/--is
|
80
|
-
|
81
|
-
|
93
|
+
if $opts[:es].any? row['account'] then
|
94
|
+
$log.debug(":es")
|
95
|
+
next
|
96
|
+
end
|
97
|
+
|
98
|
+
unless $opts[:is].empty? or $opts[:is].any? row['account']
|
99
|
+
$log.debug(":is");
|
100
|
+
next
|
101
|
+
end
|
82
102
|
|
83
103
|
# --ec/--ic
|
84
|
-
|
85
|
-
|
104
|
+
if $opts[:ec].any? row['calendar'] then
|
105
|
+
$log.debug(":ec")
|
106
|
+
next
|
107
|
+
end
|
108
|
+
|
109
|
+
unless $opts[:ic].empty? or $opts[:ic].any? row['calendar']
|
110
|
+
$log.debug(":ic")
|
111
|
+
next
|
112
|
+
end
|
86
113
|
|
87
114
|
item = klass.new(row)
|
88
115
|
|
89
116
|
# --et/--it
|
90
|
-
|
91
|
-
|
117
|
+
if $opts[:et].any? item['type'] then
|
118
|
+
$log.debug(":et")
|
119
|
+
next
|
120
|
+
end
|
121
|
+
|
122
|
+
unless $opts[:it].empty? or $opts[:it].any? item['type']
|
123
|
+
$log.debug(":it")
|
124
|
+
next
|
125
|
+
end
|
92
126
|
|
93
127
|
unless ICalPal::Event === item
|
94
128
|
# Always add non-event items
|
129
|
+
$log.debug("Adding non-event #{item}")
|
95
130
|
add(item)
|
96
131
|
else
|
97
132
|
# Check for all-day and cancelled events
|
98
|
-
|
99
|
-
|
100
|
-
|
133
|
+
if $opts[:ea] && item['all_day'].positive? then
|
134
|
+
$log.debug(":ea")
|
135
|
+
next
|
136
|
+
end
|
137
|
+
|
138
|
+
if $opts[:ia] && !item['all_day'].positive? then
|
139
|
+
$log.debug(":ia")
|
140
|
+
next
|
141
|
+
end
|
142
|
+
|
143
|
+
if item['status'] == :canceled then
|
144
|
+
$log.debug(":canceled")
|
145
|
+
next
|
146
|
+
end
|
101
147
|
|
102
148
|
(item['has_recurrences'].positive?)?
|
103
149
|
item.recurring.each { |i| add(i) } :
|
@@ -111,12 +157,14 @@ if $opts[:sed] && $opts[:sd] && klass == ICalPal::Event
|
|
111
157
|
|
112
158
|
$opts[:days].times do |n|
|
113
159
|
day = $opts[:from] + n
|
114
|
-
$items.push(klass.new(day)) unless days.any? { |i| i == day }
|
160
|
+
$items.push(klass.new(day)) unless days.any? { |i| i.to_s == day.to_s }
|
115
161
|
end
|
116
162
|
end
|
117
163
|
|
118
164
|
# Sort the rows
|
119
165
|
begin
|
166
|
+
$log.debug("Sorting/uniqing #{$items.count} items by #{[ $opts[:sep], $opts[:sort], 'sdate' ]}, reverse #{$opts[:reverse].inspect}")
|
167
|
+
|
120
168
|
$items.sort_by! { |i| [ i[$opts[:sep]], i[$opts[:sort]], i['sdate'] ] }
|
121
169
|
$items.reverse! if $opts[:reverse]
|
122
170
|
$items.uniq!
|
@@ -124,6 +172,8 @@ rescue ArgumentError => e
|
|
124
172
|
$log.warn("Sorting failed, results may be unexpected\n")
|
125
173
|
end
|
126
174
|
|
175
|
+
$log.debug("#{$items.count} items remain")
|
176
|
+
|
127
177
|
# Configure formatting
|
128
178
|
mu = case $opts[:output]
|
129
179
|
when 'ansi' then RDoc::Markup::ToAnsi.new
|
@@ -145,15 +195,20 @@ mu = case $opts[:output]
|
|
145
195
|
items = $items[0..$opts[:li] - 1]
|
146
196
|
|
147
197
|
unless mu
|
198
|
+
$log.debug("Output in #{$opts[:output]} format")
|
199
|
+
|
148
200
|
puts case $opts[:output]
|
149
201
|
when 'csv' then
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
}
|
202
|
+
# Get all headers
|
203
|
+
headers = []
|
204
|
+
items.each { |i| headers += i.keys }
|
205
|
+
headers.uniq!
|
155
206
|
|
156
|
-
|
207
|
+
# Populate a CSV::Table
|
208
|
+
table = CSV::Table.new([], headers: headers)
|
209
|
+
items.each { |i| table << i.to_csv(headers) }
|
210
|
+
|
211
|
+
table
|
157
212
|
when 'hash' then items.map { |i| i.self }
|
158
213
|
when 'json' then items.map { |i| i.self }.to_json
|
159
214
|
when 'yaml' then items.map { |i| i.self }.to_yaml
|
@@ -168,9 +223,13 @@ unless mu
|
|
168
223
|
exit
|
169
224
|
end
|
170
225
|
|
226
|
+
$log.debug("Formatting with #{mu.inspect}")
|
227
|
+
|
171
228
|
section = nil if $opts[:sep]
|
172
229
|
|
173
230
|
items.each_with_index do |i, j|
|
231
|
+
$log.debug("Print #{j}: #{i.inspect}")
|
232
|
+
|
174
233
|
# --li
|
175
234
|
break if $opts[:li].positive? && j >= $opts[:li]
|
176
235
|
|
@@ -178,7 +237,7 @@ items.each_with_index do |i, j|
|
|
178
237
|
|
179
238
|
# Sections
|
180
239
|
if $opts[:sep] && section != i[$opts[:sep]]
|
181
|
-
$log.debug("
|
240
|
+
$log.debug("New section '#{$opts[:sep]}': #{i[$opts[:sep]]}")
|
182
241
|
|
183
242
|
v = RDoc::Markup::Verbatim.new
|
184
243
|
v.format = { item: i, prop: $opts[:sep] }
|
@@ -210,7 +269,7 @@ items.each_with_index do |i, j|
|
|
210
269
|
props << RDoc::Markup::Heading.new(2, i[prop].to_s)
|
211
270
|
else
|
212
271
|
props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
|
213
|
-
props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(i[prop]))
|
272
|
+
props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(i[prop])) unless(i['placeholder'])
|
214
273
|
end
|
215
274
|
end
|
216
275
|
|
data/icalPal.gemspec
CHANGED
data/lib/event.rb
CHANGED
@@ -3,6 +3,11 @@ module ICalPal
|
|
3
3
|
class Event
|
4
4
|
include ICalPal
|
5
5
|
|
6
|
+
# Standard accessor with special handling for +sdate+. Setting
|
7
|
+
# +sdate+ will also set +sday+.
|
8
|
+
#
|
9
|
+
# @param k [String] Key/property name
|
10
|
+
# @param v [Object] Key/property value
|
6
11
|
def []=(k, v)
|
7
12
|
@self[k] = v
|
8
13
|
@self['sday'] = ICalPal::RDT.new(*self['sdate'].to_a[0..2]) if k == 'sdate'
|
@@ -27,7 +32,7 @@ module ICalPal
|
|
27
32
|
t += ' at ' unless @self['all_day'].positive?
|
28
33
|
end
|
29
34
|
|
30
|
-
unless @self['all_day'] && @self['all_day'].positive?
|
35
|
+
unless @self['all_day'] && @self['all_day'].positive? || @self['placeholder']
|
31
36
|
t ||= ''
|
32
37
|
t += "#{@self['sdate'].strftime($opts[:tf])}" if @self['sdate']
|
33
38
|
t += " - #{@self['edate'].strftime($opts[:tf])}" unless $opts[:eed] || !@self['edate']
|
@@ -65,10 +70,11 @@ module ICalPal
|
|
65
70
|
def initialize(obj)
|
66
71
|
# Placeholder for days with no events
|
67
72
|
return @self = {
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
73
|
+
$opts[:sep] => obj,
|
74
|
+
'sdate' => obj,
|
75
|
+
'placeholder' => true,
|
76
|
+
'title' => 'Nothing.',
|
77
|
+
} if DateTime === obj
|
72
78
|
|
73
79
|
@self = {}
|
74
80
|
obj.keys.each { |k| @self[k] = obj[k] }
|
@@ -108,6 +114,7 @@ module ICalPal
|
|
108
114
|
|
109
115
|
# Repeat for multi-day events
|
110
116
|
((self['duration'] / 86400).to_i + 1).times do |i|
|
117
|
+
$log.debug("multi-day event #{i + 1}") if (i > 0)
|
111
118
|
self['daynum'] = i + 1
|
112
119
|
retval.push(clone) if in_window?(self['sdate'])
|
113
120
|
self['sdate'] += 1
|
@@ -126,14 +133,20 @@ module ICalPal
|
|
126
133
|
|
127
134
|
# See if event ends before we start
|
128
135
|
stop = [ $opts[:to], (self['rdate'] || $opts[:to]) ].min
|
129
|
-
|
136
|
+
if stop < $opts[:from] then
|
137
|
+
$log.debug("#{stop} < #{$opts[:from]}")
|
138
|
+
return(retval)
|
139
|
+
end
|
130
140
|
|
131
141
|
# Get changes to series
|
132
142
|
changes = $rows.select { |r| r['orig_item_id'] == self['ROWID'] }
|
133
143
|
|
134
144
|
i = 1
|
135
145
|
while self['sdate'] <= stop
|
136
|
-
|
146
|
+
if self['count'].positive? && i > self['count'] then
|
147
|
+
$log.debug("count exceeded: #{i} > #{self['count']}")
|
148
|
+
return(retval)
|
149
|
+
end
|
137
150
|
i += 1
|
138
151
|
|
139
152
|
unless @self['xdate'].any?(@self['sdate']) # Exceptions?
|
@@ -179,6 +192,9 @@ module ICalPal
|
|
179
192
|
when 'O' then ndate = RDT.new(ndate.year, j[1].to_i, ndate.day)
|
180
193
|
when 'S' then @self['specifier'].sub!(/D=0/, "D=+#{j[1].to_i}")
|
181
194
|
end
|
195
|
+
|
196
|
+
# No time travel!
|
197
|
+
ndate = self['sdate'] if ndate <= self['sdate']
|
182
198
|
end
|
183
199
|
|
184
200
|
# D=Day of the week
|
@@ -236,9 +252,23 @@ module ICalPal
|
|
236
252
|
# @param e [RDT] Event end
|
237
253
|
# @return [Boolean]
|
238
254
|
def in_window?(s, e = s)
|
239
|
-
$opts[:n]
|
240
|
-
($now >= s && $now < e)
|
241
|
-
|
255
|
+
if $opts[:n] then
|
256
|
+
if ($now >= s && $now < e) then
|
257
|
+
$log.debug("now: #{s} to #{e} vs. #{$now}")
|
258
|
+
return(true)
|
259
|
+
else
|
260
|
+
$log.debug("not now: #{s} to #{e} vs. #{$now}")
|
261
|
+
return(false)
|
262
|
+
end
|
263
|
+
else
|
264
|
+
if ([ s, e ].max >= $opts[:from] && s < $opts[:to]) then
|
265
|
+
$log.debug("in window: #{s} to #{e} vs. #{$opts[:from]} to #{$opts[:to]}")
|
266
|
+
return(true)
|
267
|
+
else
|
268
|
+
$log.debug("not in window: #{s} to #{e} vs. #{$opts[:from]} to #{$opts[:to]}")
|
269
|
+
return(false)
|
270
|
+
end
|
271
|
+
end
|
242
272
|
end
|
243
273
|
|
244
274
|
QUERY = <<~SQL
|
data/lib/icalPal.rb
CHANGED
@@ -43,6 +43,18 @@ module ICalPal
|
|
43
43
|
@self = obj
|
44
44
|
end
|
45
45
|
|
46
|
+
# Create a new CSV::Row with values from +self+. Newlines are
|
47
|
+
# replaced with '\n' to ensure each Row is a single line of text.
|
48
|
+
#
|
49
|
+
# @param headers [Array] Key names used as the header row in a CSV::Table
|
50
|
+
# @return [CSV::Row] The +Store+, +Calendar+, or +CalendarItem+ as a CSV::Row
|
51
|
+
def to_csv(headers)
|
52
|
+
values = []
|
53
|
+
headers.each { |h| values.push(@self[h].respond_to?(:gsub)? @self[h].gsub(/\n/, '\n') : @self[h]) }
|
54
|
+
|
55
|
+
CSV::Row::new(headers, values)
|
56
|
+
end
|
57
|
+
|
46
58
|
# Get the +n+'th +dow+ in month +m+
|
47
59
|
#
|
48
60
|
# @param n [Integer] Integer between -4 and +4
|
data/lib/options.rb
CHANGED
data/lib/rdt.rb
CHANGED
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: 1.2.
|
4
|
+
version: 1.2.1
|
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-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sqlite3
|