So here's a simple script. There are two ways to run it. The first is to feed it with data on stdin while the second is to provide a command which the script will run periodically to get the data it needs.
The script then tries to read a number from the input, watches that number, and predicts when it will hit some target. The timing and the target can be adjusted.
Please note that this isn't intended to be good code, or an example of my typical coding style. It's simply a utility that's grown over time, and I thought others might find it useful. Unlikely, but possible. So here it is. If there's more demand, or much response, I'll actually spend some time knocking into better shape and putting it on github. Or something.
Here are some more examples:
As a final example, suppose the time is now 22:09. Run this command:
#!/usr/bin/python from sys import argv, stdin, stdout, exit from os import popen from re import search from time import time, ctime, sleep from select import select print 'Initialising' target, interval, epsilon, argc, ETA = 0.0, 10, 0.01, len(argv), False def vector_sum(l0,l1): return [ a+b for (a,b) in zip(l0,l1) ] def extract_datum(line): try: return float(search('([-0-9.]+)',line).groups()[0]) except: return None def get_value_from_stdin(command,timeout): return select([stdin], [], [], timeout)[0] and extract_datum(stdin.readline()) def get_value_from_command(command,timeout): sleep(timeout) return extract_datum(popen(command).readline()) def print_usage_and_exit(s): exit( stdout.write(file('ETA_Usage.txt').read() % s) or 1 ) class Predictor: def __init__(self,target=0.0): self.vec, self.start, self.target = 5*[0], time(), target def update(self,y): if y: x = time() - self.start self.vec = vector_sum( self.vec , [1,x,x*x,y,y*x] ) else: y=None ETA,msg,e,rate = self.predict() return ETA,msg,rate, y and ('%8.2f ' % y) or e def predict(self): S,Sx,Sxx,Sy,Syx = self.vec denom = S*Sxx - Sx**2 if abs(denom) < epsilon: return None, 'No ETA', ' --.-- ', 0.0 m,c = (S *Syx - Sx*Sy ) / denom , (Sxx*Sy - Sx*Syx) / denom if abs(m) < epsilon: return None, 'No ETA', ' XX.XX ',m ETA = self.start+(self.target-c)/m return ETA, ctime(ETA), '%8.2f(e)' % ((time()-self.start)*m+c), m try: if argc<=1 or argc>4: raise Exception if argc>1: command = argv[1] if argc>2: target = float(argv[2]) if argc>3: interval = float(argv[3]) except: print_usage_and_exit(argv[0]) get_value = (command=='-') and get_value_from_stdin or get_value_from_command predictor = Predictor(target) if command!='-': predictor.update( get_value(command,0) ) while not (ETA and ETA < time()): ETA,msg,rate,value = predictor.update( get_value(command,interval) ) print '@ %s ETA = %s : V=%s @ Rate=%8.2f/sec' % (ctime(), msg, value, abs(rate) ) if ETA and ETA-time()<interval: interval = max(ETA-time()-0.5,1)
You also need the "usage" file:
Usage: %s[target] [interval] Execute every seconds (default 10) and predict when the result will be (default 0). Using "-" as the command will result in data being read from stdin. Results will be printed on every input line, or after seconds, whichever is less.