summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Schodet2012-04-04 13:47:26 +0200
committerNicolas Schodet2012-04-04 13:47:26 +0200
commit985d70c3ff00640da8884dff30872abb7590b4da (patch)
tree6d26c6cb29a5a9243a12e2d01a2e0bca7ea3fa4d
parentbbcd67b237be4144c48a282319871161b85107f7 (diff)
add first stocks script
-rwxr-xr-xstocks.py218
1 files changed, 218 insertions, 0 deletions
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
+
+<html>
+<head>
+<title>Stocks</title>
+<style type="text/css">
+$stylesheet
+</style>
+</head>
+<body>
+<h1>Stocks</h1>
+
+#def cmdlink($cmd)
+ #if $cmd.isdigit()
+ <a href="http://fr.farnell.com/$cmd">$cmd</a>
+ #else
+ #filter WebSafe
+ $cmd
+ #end filter
+ #end if
+#end def
+
+#filter WebSafe
+
+<table>
+ <tr>
+ <th>Code</th>
+ <th>Command</th>
+ <th>Description</th>
+ <th colspan="2">Qty</th>
+ </tr>
+ #for $i in $stocks.itersorted
+ <tr>
+ <td>$i.code</td>
+ #filter None
+ <td>$cmdlink($i.cmd)</td>
+ #end filter
+ <td>$i.desc</td>
+ <td colspan="2" class="qty">$i.qty.main</td>
+ </tr>
+ #for $place, $qty in $i.itersortedqty
+ <tr>
+ <td colspan="3"></td>
+ <td>$place</td>
+ <td class="qty">$qty</td>
+ </tr>
+ #end for
+ #end for
+</table>
+
+#end filter
+
+</body>
+</html>
+"""
+
+if __name__ == '__main__':
+ a = AppCGI ()
+ a.run ()