]> git.notmuchmail.org Git - sup/blob - bin/sup-import
moved evertying to devel
[sup] / bin / sup-import
1 #!/bin/env ruby
2
3 require "sup"
4
5 class Float
6   def to_s; sprintf '%.2f', self; end
7 end
8
9 class Numeric
10   def to_time_s
11     i = to_i
12     sprintf "%d:%02d:%02d", i / 3600, (i / 60) % 60, i % 60
13   end
14 end
15
16 def time
17   startt = Time.now
18   yield
19   Time.now - startt
20 end
21
22 def educate_user
23   $stderr.puts <<EOS
24 Loads messages into the Sup index, adding sources as needed to the
25 source list.
26
27 Usage:
28   sup-import [options] <source>*
29 where <source>* is zero or more source descriptions (e.g., mbox
30 filenames on disk). 
31
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
36              is called
37
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.
44
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
50                    being added.
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.
56 EOS
57 #' stupid ruby-mode
58   exit
59 end
60
61 educate_user if ARGV.member? '--help'
62
63 archive = ARGV.delete "--archive"
64 unusual = ARGV.delete "--unusual"
65 force_archive = ARGV.delete "--force-archive"
66 force_read = ARGV.delete "--force-read"
67 the_usual = ARGV.delete "--the-usual"
68 rebuild = ARGV.delete "--rebuild"
69 force_rebuild = ARGV.delete "--force-rebuild"
70 optimize = ARGV.delete "--optimize"
71
72 if(o = ARGV.find { |x| x =~ /^--/ })
73   $stderr.puts "error: unknown option #{o}"
74   educate_user
75 end
76
77 puts "loading index..."
78 index = Redwood::Index.new
79 index.load
80 pre_nm = index.size
81 puts "loaded index of #{index.size} messages"
82
83 sources = ARGV.map do |fn|
84   source = index.source_for fn
85   unless source
86     source = Redwood::MBox::Loader.new(fn, 0, !unusual, !!archive)
87     index.add_source source
88   end
89   source
90 end
91 sources = (sources + index.usual_sources).uniq if the_usual
92 sources.each { |s| s.reset! } if rebuild || force_rebuild
93
94 found = {}
95 start = Time.now
96 begin
97   sources.each do |source|
98     next if source.done?
99     puts "loading from #{source}... "
100     num = 0
101     start_offset = nil
102     source.each do |offset, labels|
103       start_offset ||= offset
104       labels -= [:inbox] if force_archive
105       labels -= [:unread] if force_read
106       begin
107         m = Redwood::Message.new source, offset, labels
108         if found[m.id]
109           puts "skipping duplicate message #{m.id}"
110           next
111         else
112           found[m.id] = true
113         end
114
115         m.remove_label :unread if m.mbox_status == "RO" unless force_read
116         if (rebuild || force_rebuild) && 
117             (docid, entry = index.load_entry_for_id(m.id)) && entry
118           if force_rebuild || entry[:source_info].to_i != offset
119             puts "replacing message #{m.id} labels #{entry[:label].inspect} (offset #{entry[:source_info]} => #{offset})"
120             m.labels = entry[:label].split.map { |l| l.intern }
121             num += 1 if index.update_message m, source, offset
122           end
123         else
124           num += 1 if index.add_message m
125         end
126       rescue Redwood::MessageFormatError => e
127         $stderr.puts "ignoring erroneous message at #{source}##{offset}: #{e.message}"
128       end
129       if num % 1000 == 0 && num > 0
130         elapsed = Time.now - start
131         pctdone = (offset.to_f - start_offset) / (source.total.to_f - start_offset)
132         remaining = (source.total.to_f - offset.to_f) * (elapsed.to_f / (offset.to_f - start_offset))
133         puts "## #{num} (#{(pctdone * 100.0)}% done) read; #{elapsed.to_time_s} elapsed; est. #{remaining.to_time_s} remaining"
134       end
135     end
136     puts "loaded #{num} messages" unless num == 0
137   end
138 ensure
139   index.save
140 end
141
142 if rebuild || force_rebuild
143   puts "deleting missing messages from the index..."
144   numdel = 0
145   sources.each do |source|
146     raise "no source id for #{source}" unless source.id
147     index.index.search_each("source_id:#{source.id}", :limit => :all) do |docid, score|
148       mid = index.index[docid][:message_id]
149       next if found[mid]
150       puts "deleting #{mid}"
151       index.index.delete docid
152       numdel += 1
153     end
154   end
155   puts "deleted #{numdel} messages"
156 end
157
158 if optimize
159   puts "optimizing index..."
160   optt = time { index.index.optimize }
161   puts "optimized index of size #{index.size} in #{optt}s."
162 end