]> git.phdru.name Git - bookmarks_db.git/blob - check_urls2.py
Initial revision
[bookmarks_db.git] / check_urls2.py
1 #! /usr/local/bin/python -O
2 """
3    For every URL in the FLAD database get info from the Net
4    and store info in check.db
5
6    Version 2.0
7    Written by BroytMann, Aug 1997 - Mar 1999. Copyright (C) 1997-1999 PhiloSoft Design
8 """
9
10
11 import sys, os, stat, string, time
12 from getopt import getopt
13
14 import urllib, tempfile
15 from copy import _copy_dict
16
17 import cPickle
18 pickle = cPickle
19
20 import fladm, fladc, shutil
21 from flog import makelog, openlog
22
23
24 os.environ["PATH"] = ".:" + os.environ["PATH"]
25 from subproc import Subprocess, RecordFile
26
27
28 def set_checkpoint(rec_no):
29    cpfile = open("check.dat", 'w')
30    cpfile.write("# chk_urls checkpoint file\n")
31    cpfile.write("Size: %d\n" % db_stat[stat.ST_SIZE])
32    cpfile.write("MTime: %d\n" % db_stat[stat.ST_MTIME])
33    cpfile.write("Record: %d" % rec_no)
34    cpfile.close()
35
36 def get_checkpoint():
37    try:
38       cpfile = fladc.load_file("check.dat")
39       if (string.atoi(cpfile["Size"]) <> db_stat[stat.ST_SIZE]) or \
40          (string.atoi(cpfile["MTime"]) <> db_stat[stat.ST_MTIME]):
41          return -3
42
43       return string.atoi(cpfile["Record"])
44
45    except IOError: # No such file
46       return -1
47
48    except KeyError: # No such key in checkpoint file
49       return -2
50
51    except string.atoi_error: # Wrong numeric format
52       return -2
53
54    return 0
55
56 def start(db_name, report_stats):
57    start_recno = get_checkpoint()
58    if start_recno < 0:
59       if start_recno == -1:
60          log = makelog("check.log")
61          log("chk_urls started")
62          if report_stats:
63             print "   chk_urls: normal start"
64
65       elif start_recno == -2:
66          log = openlog("check.log")
67          log("chk_urls started")
68          log("   invalid checkpoint file, checkpoint ignored")
69          if report_stats:
70             print "   chk_urls: invalid checkpoint file, checkpoint ignored"
71
72       elif start_recno == -3:
73          log = makelog("check.log")
74          log("chk_urls started")
75          log("   bookmarks.db changed, checkpoint ignored")
76          if report_stats:
77             print "   chk_urls: bookmarks.db changed, checkpoint ignored"
78
79       else:
80          raise RuntimeError, "wrong get_checkpoint() return: `%s'" % str(start_recno)
81
82       start_recno = 0
83
84    elif start_recno == 0:
85       raise RuntimeError, "wrong get_checkpoint() return: `%s'" % str(start_recno)
86
87    else: # start_recno > 0
88       if os.path.exists("check.db"):
89          if not os.path.exists("check.old"):
90             shutil.copy("check.db", "check.old")
91          db_name = "check.db"
92
93          log = openlog("check.log")
94          log("chk_urls started")
95          log("   found valid checkpoint file, continue")
96          if report_stats:
97             print "   chk_urls: found valid checkpoint file, continue"
98
99       else:
100          log = makelog("check.log")
101          log("chk_urls started")
102          log("   valid checkpoint, but no check.db file, restarting")
103          if report_stats:
104             print "   chk_urls: valid checkpoint, but no check.db file, restarting"
105          start_recno = 0
106
107    return start_recno, db_name, log
108
109
110 tempfname = "check_urls" + tempfile.gettempprefix() + ".tmp"
111
112
113 check_subp = None
114 subp_pipe = None
115
116 def restart_subp(log, report_stats):
117    global check_subp, subp_pipe
118    if check_subp:
119       log("   restarting hanging subprocess")
120       if report_stats:
121          print "   chk_urls: restarting hanging subprocess"
122       del check_subp
123    del subp_pipe
124
125    check_subp = Subprocess("check_url_sub.py")
126    subp_pipe = RecordFile(check_subp)
127
128
129 def check_url(record, log, report_stats):
130    try:
131       record["TEMPFILE"] = tempfname
132       subp_pipe.write_record(pickle.dumps(record))
133
134       if check_subp.waitForPendingChar(900): # wait 15 minutes
135          rec = pickle.loads(subp_pipe.read_record())
136          del record["TEMPFILE"]
137          for key in rec.keys():
138             record[key] = rec[key]
139       else:
140          restart_subp(log, report_stats)
141          del record["TEMPFILE"]
142          record["Error"] = "Subprocess connection timed out"
143
144    except KeyboardInterrupt:
145       return 0
146
147    return 1
148
149
150 def run():
151    optlist, args = getopt(sys.argv[1:], "ise")
152
153    show_pbar = 1
154    report_stats = 1
155    only_errors = 0
156    db_name = "bookmarks.db"
157
158    for _opt, _arg in optlist:
159       if _opt == '-i':
160          show_pbar = 0
161       if _opt == '-s':
162          report_stats = 0
163       if _opt == '-e':
164          only_errors = 1
165    try:
166       del _opt, _arg
167    except NameError:
168       pass
169
170    if report_stats:
171       print "BroytMann check_urls, Copyright (C) 1997-1999 PhiloSoft Design"
172
173    if args:
174       if len(args) > 1:
175          sys.stderr.write("chk_urls: too many arguments\n")
176          sys.exit(1)
177       else:
178          db_name = args[0]
179
180    if show_pbar:
181       show_pbar = sys.stderr.isatty()
182
183    if show_pbar:
184       try:
185          from tty_pbar import ttyProgressBar
186       except ImportError:
187          show_pbar = 0
188
189    global db_stat, log
190    db_stat = os.stat(db_name)
191
192    if only_errors:
193       start_recno = 0
194       db_name = "check.db"
195       log = openlog("check.log")
196       log("chk_urls restarted for errors")
197    else:
198       start_recno, db_name, log = start(db_name, report_stats)
199
200    if report_stats:
201       sys.stdout.write("Loading %s: " % db_name)
202       sys.stdout.flush()
203
204    bookmarks_db = fladm.load_from_file(db_name, fladm.check_record, ["Level"])
205    bookmarks_dbstore = bookmarks_db
206
207    if only_errors:
208       bookmarks_db = filter(lambda r: r.has_key("Error") and r["Error"][:5] <> "Moved", bookmarks_db)
209
210    if report_stats:
211       print "Ok"
212
213    db_len = len(bookmarks_db)
214    if db_len == 0:
215       print "Database empty"
216       sys.exit(0)
217
218    if start_recno >= db_len:
219       _s = "start_recno (%d) >= db_len (%d), restarting" % (start_recno, db_len)
220       log("   " + _s)
221       if report_stats:
222          print "   chk_urls: " + _s
223       del _s
224       start_recno = 0
225
226    if report_stats:
227       if only_errors:
228          s = "Rechecking errors: "
229       else:
230          s = "Checking: "
231       sys.stdout.write(s)
232       sys.stdout.flush()
233
234    if show_pbar:
235       save_stats = report_stats
236       report_stats = 0
237       pbar = ttyProgressBar(0, db_len)
238
239    urls_no = 0
240    record_count = 0
241    start_time = time.time()
242
243    rcode = 1
244    restart_subp(log, report_stats) # Not restart, just start afresh
245    checked_dict = {} # Dictionary of checked URLs, mapped to records number
246
247    for record_no in range(start_recno, db_len):
248       if show_pbar:
249          pbar.display(record_no+1)
250
251       record = bookmarks_db[record_no]
252       record_count = record_count + 1
253
254       if only_errors:
255          del record["Error"]
256
257       if record.has_key("URL"):
258          url = record["URL"]
259          if checked_dict.has_key(url):
260             log("Already checked %s" % url)
261             level = record["Level"]
262             comment = record["Comment"]
263             bookmarks_db[record_no] = _copy_dict(bookmarks_db[checked_dict[url]])
264             bookmarks_db[record_no]["Level"] = level
265             bookmarks_db[record_no]["Comment"] = comment
266          else:
267             log("Checking %s" % url)
268             rcode = check_url(record, log, report_stats)
269             if rcode:
270                current_time = time.time()
271                if current_time - start_time >= 300: # Save checkpoint and database every 5 min
272                   bookmarks_dbstore.store_to_file("check.db")
273                   set_checkpoint(record_no)
274                   log.flush()
275                   start_time = current_time
276                urls_no = urls_no + 1
277                checked_dict[url] = record_no
278             else:
279                log("Interrupted by user (^C)")
280                break
281
282    if show_pbar:
283       del pbar
284       report_stats = save_stats 
285
286    if report_stats:
287       print "Ok"
288       print record_count, "records checked"
289       print urls_no, "URLs checked"
290
291    bookmarks_dbstore.store_to_file("check.db")
292
293    if rcode:
294       log("chk_urls finished ok")
295    log.close()
296
297    urllib.urlcleanup()
298    if os.path.exists(tempfname):
299       os.unlink(tempfname)
300
301    if rcode:
302       if os.path.exists("check.dat"):
303          os.unlink("check.dat")
304    else:
305       set_checkpoint(record_no)
306       sys.exit(1)
307
308
309 if __name__ == '__main__':
310    run()