6 def to_s; sprintf '%.2f', self; end
12 sprintf "%d:%02d:%02d", i / 3600, (i / 60) % 60, i % 60
24 Loads messages into the Sup index, adding sources as needed to the
28 sup-import [options] <source>*
29 where <source>* is zero or more source descriptions (e.g., mbox
32 If the sources listed are not already in the Sup source list,
33 they will be added to it, as parameterized by the following options:
34 --archive: messages from these sources will not appear in the inbox
35 --unusual: these sources will not be polled when the flag --the-usual
38 Regardless of whether the sources are new or not, they will be polled,
39 and any new messages will be added to the index, as parameterized by
40 the following options:
41 --force-archive: regardless of the source "archive" flag, any new
42 messages found will not appear in the inbox.
43 --force-read: any messages found will not be marked as new.
45 The following options can also be specified:
46 --the-usual: import new messages from all usual sources
47 --rebuild: rebuild the index for the specified sources rather than
48 just adding new messages. Useful if the sources
49 have changed in any way *other* than new messages
51 --force-rebuild: force a rebuild of all messages in the inbox, not just
52 ones that have changed. You probably won't need this
53 unless William changes the index format.
54 --optimize: optimize the index after adding any new messages.
55 --help: don't do anything, just show this message.
60 educate_user if ARGV.member? '--help'
62 archive = ARGV.delete "--archive"
63 unusual = ARGV.delete "--unusual"
64 force_archive = ARGV.delete "--force-archive"
65 force_read = ARGV.delete "--force-read"
66 the_usual = ARGV.delete "--the-usual"
67 rebuild = ARGV.delete "--rebuild"
68 force_rebuild = ARGV.delete "--force-rebuild"
69 optimize = ARGV.delete "--optimize"
70 start_at = # ok really need to use optparse or something now
71 if(i = ARGV.index("--start-at"))
72 raise "start-at requires a numeric argument: #{ARGV[i + 1].inspect}" unless ARGV.length > (i + 1) && ARGV[i + 1] =~ /\d/
74 ARGV.delete_at(i).to_i # whoa!
77 if(o = ARGV.find { |x| x =~ /^--/ })
78 $stderr.puts "error: unknown option #{o}"
82 puts "loading index..."
83 index = Redwood::Index.new
85 puts "loaded index of #{index.size} messages"
87 sources = ARGV.map do |fn|
88 source = index.source_for fn
93 print "Username for #{fn}: "
94 username = $stdin.gets.chomp
95 print "Password for #{fn} (warning: cleartext): "
96 password = $stdin.gets.chomp
97 Redwood::IMAP.new(fn, username, password, nil, !unusual, !!archive)
99 Redwood::MBox::Loader.new(fn, nil, !unusual, !!archive)
101 index.add_source source
106 sources = (sources + index.usual_sources).uniq if the_usual
107 if rebuild || force_rebuild
109 sources.each { |s| s.seek_to! start_at }
111 sources.each { |s| s.reset! }
118 sources.each do |source|
120 puts "loading from #{source}... "
123 source.each do |offset, labels|
124 start_offset ||= offset
125 labels -= [:inbox] if force_archive
126 labels -= [:unread] if force_read
128 m = Redwood::Message.new :source => source, :source_info => offset, :labels => labels
130 puts "skipping duplicate message #{m.id}"
135 m.remove_label :unread if m.status == "RO" unless force_read
136 puts "# message at #{offset} labels #{labels.inspect}" unless rebuild || force_rebuild
137 if (rebuild || force_rebuild) &&
138 (docid, entry = index.load_entry_for_id(m.id)) && entry
139 if force_rebuild || entry[:source_info].to_i != offset
140 puts "replacing message #{m.id} labels #{entry[:label].inspect} (offset #{entry[:source_info]} => #{offset})"
141 m.labels = entry[:label].split.map { |l| l.intern }
142 num += 1 if index.update_message m, source, offset
145 num += 1 if index.add_message m
147 rescue Redwood::MessageFormatError, Redwood::MBox::Error => e
148 $stderr.puts "ignoring erroneous message at #{source}##{offset}: #{e.message}"
150 if num % 1000 == 0 && num > 0
151 elapsed = Time.now - start
152 pctdone = (offset.to_f - start_offset) / (source.total.to_f - start_offset)
153 remaining = (source.total.to_f - offset.to_f) * (elapsed.to_f / (offset.to_f - start_offset))
154 puts "## #{num} (#{(pctdone * 100.0)}% done) read; #{elapsed.to_time_s} elapsed; est. #{remaining.to_time_s} remaining"
157 puts "loaded #{num} messages" unless num == 0
163 if rebuild || force_rebuild
164 puts "deleting missing messages from the index..."
166 sources.each do |source|
167 raise "no source id for #{source}" unless source.id
168 q = "+source_id:#{source.id}"
169 q += " +source_info: >= #{start_at}" if start_at
171 num += index.index.search_each(q, :limit => :all) do |docid, score|
172 mid = index.index[docid][:message_id]
174 puts "deleting #{mid}"
175 index.index.delete docid
180 puts "deleted #{numdel} / #{num} messages"
184 puts "optimizing index..."
185 optt = time { index.index.optimize }
186 puts "optimized index of size #{index.size} in #{optt}s."