8 def to_s; sprintf '%.2f', self; end
14 sprintf "%d:%02d:%02d", i / 3600, (i / 60) % 60, i % 60
26 Loads messages into the Sup index, adding sources as needed to the
30 sup-import [options] <source>*
31 where <source>* is zero or more source descriptions (e.g., mbox
32 filenames on disk, or imap/imaps URIs).
34 If the sources listed are not already in the Sup source list,
35 they will be added to it, as parameterized by the following options:
36 --archive: messages from these sources will not appear in the inbox
37 --unusual: these sources will not be polled when the flag --the-usual
40 Regardless of whether the sources are new or not, they will be polled,
41 and any new messages will be added to the index, as parameterized by
42 the following options:
43 --force-archive: regardless of the source "archive" flag, any new
44 messages found will not appear in the inbox.
45 --force-read: any messages found will not be marked as new.
47 The following options can also be specified:
48 --the-usual: import new messages from all usual sources
49 --rebuild: rebuild the index for the specified sources rather than
50 just adding new messages. Useful if the sources
51 have changed in any way *other* than new messages
53 --force-rebuild: force a rebuild of all messages in the inbox, not just
54 ones that have changed. You probably won't need this
55 unless William changes the index format.
56 --optimize: optimize the index after adding any new messages.
57 --help: don't do anything, just show this message.
62 educate_user if ARGV.member? '--help'
64 archive = ARGV.delete "--archive"
65 unusual = ARGV.delete "--unusual"
66 force_archive = ARGV.delete "--force-archive"
67 force_read = ARGV.delete "--force-read"
68 the_usual = ARGV.delete "--the-usual"
69 rebuild = ARGV.delete "--rebuild"
70 force_rebuild = ARGV.delete "--force-rebuild"
71 optimize = ARGV.delete "--optimize"
72 start_at = # ok really need to use optparse or something now
73 if(i = ARGV.index("--start-at"))
74 raise "start-at requires a numeric argument: #{ARGV[i + 1].inspect}" unless ARGV.length > (i + 1) && ARGV[i + 1] =~ /\d/
76 ARGV.delete_at(i).to_i # whoa!
79 if(o = ARGV.find { |x| x =~ /^--/ })
80 $stderr.puts "error: unknown option #{o}"
86 index = Redwood::Index.new
91 sources = ARGV.map do |fn|
92 fn = "mbox://#{fn}" unless fn =~ %r!://!
93 source = index.source_for fn
97 when %r!^mbox\+ssh://!
98 username = h.ask("Username for #{fn}: ");
99 password = h.ask("Password for #{fn}: ") { |q| q.echo = false }
101 Redwood::MBox::SSHLoader.new(fn, username, password, nil, !unusual, !!archive)
103 username = h.ask("Username for #{fn}: ");
104 password = h.ask("Password for #{fn}: ") { |q| q.echo = false }
106 Redwood::IMAP.new(fn, username, password, nil, !unusual, !!archive)
108 Redwood::MBox::Loader.new(fn, nil, !unusual, !!archive)
110 index.add_source source
115 sources = (sources + index.usual_sources).uniq if the_usual
116 if rebuild || force_rebuild
118 sources.each { |s| s.seek_to! start_at }
120 sources.each { |s| s.reset! }
127 sources.each do |source|
129 puts "error loading messages from #{source}: #{source.broken_msg}"
133 puts "loading from #{source}... "
136 source.each do |offset, labels|
137 start_offset ||= offset
138 labels -= [:inbox] if force_archive
139 labels -= [:unread] if force_read
141 m = Redwood::Message.new :source => source, :source_info => offset, :labels => labels
143 puts "skipping duplicate message #{m.id}"
148 m.remove_label :unread if m.status == "RO" unless force_read
149 puts "# message at #{offset}, labels: #{labels * ', '}" unless rebuild || force_rebuild
150 if (rebuild || force_rebuild) &&
151 (docid, entry = index.load_entry_for_id(m.id)) && entry
152 if force_rebuild || entry[:source_info].to_i != offset
153 puts "replacing message #{m.id} labels #{entry[:label].inspect} (offset #{entry[:source_info]} => #{offset})"
154 m.labels = entry[:label].split.map { |l| l.intern }
155 num += 1 if index.update_message m, source, offset
158 num += 1 if index.add_message m
160 rescue Redwood::MessageFormatError, Redwood::SourceError => e
161 $stderr.puts "ignoring erroneous message at #{source}##{offset}: #{e.message}"
163 if num % 1000 == 0 && num > 0
164 elapsed = Time.now - start
165 pctdone = (offset.to_f - start_offset) / (source.total.to_f - start_offset)
166 remaining = (source.total.to_f - offset.to_f) * (elapsed.to_f / (offset.to_f - start_offset))
167 puts "## #{num} (#{(pctdone * 100.0)}% done) read; #{elapsed.to_time_s} elapsed; est. #{remaining.to_time_s} remaining"
170 puts "loaded #{num} messages" unless num == 0
177 if rebuild || force_rebuild
178 puts "deleting missing messages from the index..."
180 sources.each do |source|
181 raise "no source id for #{source}" unless source.id
182 q = "+source_id:#{source.id}"
183 q += " +source_info: >= #{start_at}" if start_at
185 num += index.index.search_each(q, :limit => :all) do |docid, score|
186 mid = index.index[docid][:message_id]
188 puts "deleting #{mid}"
189 index.index.delete docid
194 puts "deleted #{numdel} / #{num} messages"
198 puts "optimizing index..."
199 optt = time { index.index.optimize }
200 puts "optimized index of size #{index.size} in #{optt}s."