From: Ian Jackson Date: Wed, 27 May 2009 22:57:54 +0000 (+0100) Subject: Merge branch 'keyboard' X-Git-Tag: 1.0~8 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~yarrgweb/git?p=ypp-sc-tools.db-test.git;a=commitdiff_plain;h=bd2af4a72e41bdec3f416ac7cbcce93d66d74815;hp=-c Merge branch 'keyboard' --- bd2af4a72e41bdec3f416ac7cbcce93d66d74815 diff --combined yoweb-scrape index 40cdad5,d2d194c..d42f427 --- a/yoweb-scrape +++ b/yoweb-scrape @@@ -14,6 -14,7 +14,7 @@@ import sy import re as regexp import random import curses + import termios from optparse import OptionParser from BeautifulSoup import BeautifulSoup @@@ -27,8 -28,20 +28,20 @@@ puzzles = ('Swordfighting/Bilging/Saili '/Drinking/Spades/Hearts/Treasure Drop/Poker/Distilling'+ '/Alchemistry/Shipwrightery/Blacksmithing/Foraging').split('/') + core_duty_puzzles = [ + 'Gunning', + ['Sailing','Rigging'], + 'Bilging', + 'Carpentry', + ] + + duty_puzzles = ([ 'Navigating', 'Battle Navigation' ] + + core_duty_puzzles + + [ 'Treasure Haul' ]) + standingvals = ('Able/Distinguished/Respected/Master'+ '/Renowned/Grand-Master/Legendary/Ultimate').split('/') + standing_limit = len(standingvals) pirate_ref_re = regexp.compile('^/yoweb/pirate\\.wm') @@@ -41,9 -54,12 +54,12 @@@ def debug(m) if opts.debug > 0: print >>opts.debug_file, m - def sleep(seconds): + def debug_flush(): if opts.debug > 0: - opts.debug_file.flush() + opts.debug_file.flush() + + def sleep(seconds): + debug_flush() time.sleep(seconds) def format_time_interval(ti): @@@ -248,7 -264,7 +264,7 @@@ u'\\s*\\S*/([-A-Za-z]+)\\s*$|\\s*\\S*/\ skl.msg('puzzle "%s" no standing found' % puzzle) continue standing = sl[0] - for i in range(0, len(standingvals)): + for i in range(0, standing_limit): if standing == standingvals[i]: self.standings[puzzle] = i if not puzzle in self.standings: @@@ -332,37 -348,23 +348,30 @@@ class CrewInfo(SomethingSoupInfo) #---------- pretty-printer for tables of pirate puzzle standings ---------- class StandingsTable: - def __init__(self, use_puzzles=None, col_width=6): + def __init__(self, use_puzzles=None, col_width=6, gap_every=5): if use_puzzles is None: if opts.ship_duty: - use_puzzles=[ - 'Navigating','Battle Navigation', - 'Gunning', - ['Sailing','Rigging'], - 'Bilging', - 'Carpentry', - 'Treasure Haul' - ] + use_puzzles=duty_puzzles else: use_puzzles=puzzles self._puzzles = use_puzzles self.s = '' self._cw = col_width-1 + self._gap_every = gap_every + self._linecount = 0 def _pline(self, pirate, puzstrs, extra): + if (self._linecount > 0 + and self._gap_every is not None + and not (self._linecount % self._gap_every)): + self.s += '\n' self.s += ' %-*s' % (max(max_pirate_namelen, 14), pirate) for v in puzstrs: self.s += ' %-*.*s' % (self._cw,self._cw, v) if extra: self.s += ' ' + extra self.s += '\n' + self._linecount += 1 def _puzstr(self, pi, puzzle): if not isinstance(puzzle,list): puzzle = [puzzle] @@@ -389,12 -391,9 +398,12 @@@ spc = name.find(' ') if spc < 0: return name return name[0:min(4,spc)] + name[spc+1:] + self._linecount = -2 self._pline('', map(puzn_redact, self._puzzles), None) + self._linecount = 0 def literalline(self, line): self.s += line + '\n' + self._linecount = 0 def pirate_dummy(self, name, standingstring, extra=None): self._pline(name, standingstring * len(self._puzzles), extra) def pirate(self, pi, extra=None): @@@ -845,6 -844,8 +854,8 @@@ class ChatLogTracker # sorted by pirate name # you can pass this None and you'll get [] # or True for the current vessel (which is the default) + # the returned value is a fresh list of persistent + # PirateAboard objects if vesselname is True: v = self._v else: v = self._vl.get(vesselname.title()) if v is None: return [] @@@ -877,7 -878,6 +888,7 @@@ def do_standings_crew_of(args, bu) tab.headings() for (rank, members) in ci.crew: if not members: continue + tab.literalline('') tab.literalline('%s:' % rank) for p in members: pi = PirateInfo(p, random.randint(900,1800)) @@@ -1000,16 -1000,56 +1011,56 @@@ def do_ship_aid(args, bu) if opts.ship_duty is None: opts.ship_duty = True displayer = globals()['Display_'+opts.display]() - rotate_nya = '/-\\' (myself, track) = prep_chat_log(args, bu, progress=displayer) - def timeevent(t,e): - if t is None: return ' ' * 22 - return " %-4s %-16s" % (format_time_interval(now - t),e) - displayer.realstart() + if os.isatty(0): kr_create = KeystrokeReader + else: kr_create = DummyKeystrokeReader + + try: + kreader = kr_create(0, 10) + ship_aid_core(myself, track, displayer, kreader) + finally: + kreader.stop() + print '\n' + + class KeyBasedSorter: + def compar_key_pa(self, pa): + return self.compar_key(pa.pirate_info()) + def lsort_pa(self, l): + l.sort(key = self.compar_key_pa) + + class NameSorter(KeyBasedSorter): + def compar_key(self, pi): return pi.name + + class SkillSorter(NameSorter): + def __init__(self, relevant): + self._want = frozenset(relevant.split('/')) + self._avoid = set() + for p in core_duty_puzzles: + if isinstance(p,basestring): self._avoid.add(p) + else: self._avoid |= set(p) + self._avoid -= self._want + + def compar_key(self, pi): + best_want = max([ + pi.standings.get(puz,-1) + for puz in self._want + ]) + best_avoid = [ + -pi.standings.get(puz,standing_limit) + for puz in self._avoid + ] + best_avoid.sort() + def negate(x): return -x + debug('compar_key %s bw=%s ba=%s' % (pi.name, `best_want`, + `best_avoid`)) + return (-best_want, map(negate, best_avoid), pi.name) + + def ship_aid_core(myself, track, displayer, kreader): + def find_vessel(): vn = track.vesselname() if vn: return (vn, " on board the %s" % vn) @@@ -1017,21 -1057,33 +1068,33 @@@ if vn: return (vn, " ashore from the %s" % vn) return (None, " not on a vessel") + def timeevent(t,e): + if t is None: return ' ' * 22 + return " %-4s %-16s" % (format_time_interval(now - t),e) + displayer.show(track.myname() + find_vessel()[1] + '...') + rotate_nya = '/-\\' + + sort = NameSorter() + while True: track.catchup() now = time.time() (vn, s) = find_vessel() s = track.myname() + s - s += " at %s\n" % time.strftime("%Y-%m-%d %H:%M:%S") + s += " at %s" % time.strftime("%Y-%m-%d %H:%M:%S") + s += kreader.info() + s += '\n' tbl = StandingsTable() tbl.headings() aboard = track.aboard(vn) + sort.lsort_pa(aboard) + for pa in aboard: pi = pa.pirate_info() @@@ -1047,10 -1099,51 +1110,51 @@@ tbl.pirate(pi, xs) s += tbl.results() - displayer.show(s) - sleep(1) - rotate_nya = rotate_nya[1:3] + rotate_nya[0] + + k = kreader.getch() + if k is None: + rotate_nya = rotate_nya[1:3] + rotate_nya[0] + continue + + if k == 'q': break + elif k == 'g': sort = SkillSorter('Gunning') + elif k == 'c': sort = SkillSorter('Carpentry') + elif k == 's': sort = SkillSorter('Sailing/Rigging') + elif k == 'b': sort = SkillSorter('Bilging') + elif k == 'n': sort = SkillSorter('Navigating') + elif k == 'd': sort = SkillSorter('Battle Navigation') + elif k == 't': sort = SkillSorter('Treasure Haul') + elif k == 'a': sort = NameSorter() + else: pass # unknown key command + + #---------- individual keystroke input ---------- + + class DummyKeystrokeReader: + def __init__(self,fd,timeout_dummy): pass + def stop(self): pass + def getch(self): sleep(1); return None + def info(self): return ' [noninteractive]' + + class KeystrokeReader(DummyKeystrokeReader): + def __init__(self, fd, timeout_decisec=0): + self._fd = fd + self._saved = termios.tcgetattr(fd) + a = termios.tcgetattr(fd) + a[3] &= ~(termios.ECHO | termios.ECHONL | + termios.ICANON | termios.IEXTEN) + a[6][termios.VMIN] = 0 + a[6][termios.VTIME] = timeout_decisec + termios.tcsetattr(fd, termios.TCSANOW, a) + def stop(self): + termios.tcsetattr(self._fd, termios.TCSANOW, self._saved) + def getch(self): + debug_flush() + byte = os.read(self._fd, 1) + if not len(byte): return None + return byte + def info(self): + return '' #---------- main program ---------- @@@ -1091,10 -1184,6 +1195,10 @@@ display modes (for --display) apply to ao('--all-puzzles', action='store_false', dest='ship_duty', help='show all puzzles, not just ship duty stations') + ao('--min-cache-reuse', type='int', dest='min_max_age', + metavar='SECONDS', default=60, + help='always reuse cache yoweb data if no older than this') + (opts,args) = pa.parse_args() random.seed() @@@ -1112,8 -1201,8 +1216,8 @@@ except KeyError: pa.error('unknown mode "%s"' % mode) # fixed parameters - opts.min_max_age = 60 - opts.expire_age = 3600 + opts.expire_age = max(3600, opts.min_max_age) + opts.ship_reboard_clearout = 3600 if opts.cache_dir.startswith('~/'):