From 985d70c3ff00640da8884dff30872abb7590b4da Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Wed, 4 Apr 2012 13:47:26 +0200 Subject: add first stocks script --- stocks.py | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100755 stocks.py (limited to 'stocks.py') diff --git a/stocks.py b/stocks.py new file mode 100755 index 0000000..2b7475c --- /dev/null +++ b/stocks.py @@ -0,0 +1,218 @@ +#!/usr/bin/python +"""Handle stocks on the web.""" +import cgi +import cgitb +cgitb.enable () + +from Cheetah.Template import Template +import fcntl + +DEFAULT_DATABASE = 'stocks.txt' + +class StocksError (Exception): + pass + +class StocksParseError (StocksError): + + def __init__ (self, message, filename, line_index): + StocksError.__init__ (self, message, filename, line_index) + self.filename = filename + self.line = line_index + 1 + self.message = message + + def __str__ (self): + return '%s:%d: %s' % (self.filename, self.line, self.message) + +class StocksItem: + """A specific stock item.""" + + def __init__ (self, code, cmd, qty, desc = None): + self.code = code + self.cmd = cmd + self.qty = { 'main': qty } + self.desc = desc + + def itersortedqty (self, withmain = False): + for k in sorted (self.qty.keys ()): + if withmain or k != 'main': + yield k, self.qty[k] + + def __str__ (self): + s = [ ] + s.append ('%s\t%s\t%d' % (self.code, self.cmd, self.qty['main'])) + if self.desc: + s.extend (('\t', self.desc)) + for place, qty in self.itersortedqty (): + s.append ('\n\t%s\t%d' % (place, qty)) + return ''.join (s) + +class Stocks: + """Stocks, i.e. stock items list.""" + + def __init__ (self): + self.database = None + + def __delete__ (self): + self.close () + + def load (self, filename): + """Open an lock database.""" + # Open and lock. + self.close () + self.database = open (filename, 'r+') + fcntl.flock (self.database, fcntl.LOCK_EX) + self.database.seek (0) + # Load. + self.head = [ ] + self.tail = [ ] + self.items = { } + headtail = self.head + last_item = None + for n, line in enumerate (self.database): + if '\t' not in line: + headtail.append (line) + elif line.startswith ('Code\t'): + headtail.append (line) + headtail = self.tail + elif not line.startswith ('\t'): + cols = line.strip ().split ('\t') + if len (cols) not in (3, 4): + raise StocksParseError ("bad input line", filename, n) + code, cmd, qty = cols[0:3] + desc = cols[3] if len (cols) == 4 else None + try: + qty = int (qty) + except ValueError: + raise StocksParseError ("bad quantity", filename, n) + if code in self.items: + raise StocksParseError ("duplicated code", filename, n) + item = StocksItem (code, cmd, qty, desc) + self.items[item.code] = item + last_item = item + else: + cols = line.strip ().split ('\t') + if len (cols) != 2 or last_item is None: + raise StocksParseError ("bad input line", filename, n) + place, qty = cols + try: + qty = int (qty) + except ValueError: + raise StocksParseError ("bad quantity", filename, n) + if place in last_item.qty: + raise StocksParseError ("duplicated place", filename, n) + last_item.qty[place] = qty + + def dump (self): + """Dump stocks to database.""" + self.database.seek (0) + self.database.truncate () + self.database.write (''.join (self.head)) + for ik in sorted (self.items.keys ()): + self.database.write (str (self.items[ik]) + '\n') + self.database.write (''.join (self.tail)) + + def close (self): + """Close and unlock database.""" + if self.database is not None: + self.database.close () + + def itersorted (self): + """Return sorted iterable list of items.""" + for ik in sorted (self.items.keys ()): + yield self.items[ik] + +class AppCGI: + + def __init__ (self): + pass + + def run (self): + stocks = Stocks () + stocks.load (DEFAULT_DATABASE) + stocks.dump () + t = Template (AppCGI.template) + t.stylesheet = AppCGI.stylesheet + t.stocks = stocks + print t + +AppCGI.stylesheet = """\ +table { + border-collapse: collapse; + width: 100%; +} +th { + background: #d0d0d0; +} +td { + background: #e0e0e0; + border-top: 1px solid white; + border-left: 1px solid white; +} +tr:hover td { + background: #e8e8e8; +} +td.qty { + text-align: right; +} +""" + +AppCGI.template = """\ +Content-Type: text/html; charset=UTF-8 + + + +Stocks + + + +

Stocks

+ +#def cmdlink($cmd) + #if $cmd.isdigit() + $cmd + #else + #filter WebSafe + $cmd + #end filter + #end if +#end def + +#filter WebSafe + + + + + + + + + #for $i in $stocks.itersorted + + + #filter None + + #end filter + + + + #for $place, $qty in $i.itersortedqty + + + + + + #end for + #end for +
CodeCommandDescriptionQty
$i.code$cmdlink($i.cmd)$i.desc$i.qty.main
$place$qty
+ +#end filter + + + +""" + +if __name__ == '__main__': + a = AppCGI () + a.run () -- cgit v1.2.3