Merge branch 'next'
26
README-1ST
|
|
@ -33,9 +33,21 @@ available for testing and more useful bug reports.
|
|||
|
||||
===============================================================================
|
||||
|
||||
F.A.Q.
|
||||
IF YOU HAVE CONFIGURE OR BUILD PROBLEMS
|
||||
|
||||
----------------------------------------------------------------------
|
||||
The first order of business if acprep update doesn't work is to find out where
|
||||
things went wrong. So follow these steps to produce a bug report I can track
|
||||
down easily:
|
||||
|
||||
$ ./acprep --debug update # shows what acprep was thinking
|
||||
$ <edit config.log> # shows what configure was thinking
|
||||
|
||||
With the contents of config.log, and the output from acprep --debug update,
|
||||
it's usually fairly obvious where things have gone astray.
|
||||
|
||||
===============================================================================
|
||||
|
||||
F.A.Q.
|
||||
|
||||
- Q: The build fails saying it can't find utf8.h
|
||||
|
||||
|
|
@ -65,6 +77,14 @@ available for testing and more useful bug reports.
|
|||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
- Q: I'm seeing a segfault deep inside the boost_regex code!
|
||||
|
||||
A: Actually, the real segfault is in libstdc++'s facet code. It's being
|
||||
caused by using a debug Boost with a non-debug build of Ledger, or
|
||||
vice-versa.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
- Q: Something else fails, or Ledger crashes on startup
|
||||
|
||||
A: This, I am most interested in hearing about. Please e-mail me a copy of
|
||||
|
|
@ -98,5 +118,3 @@ available for testing and more useful bug reports.
|
|||
|
||||
A: This can happen for the same reason as above. It can also happen if you
|
||||
have ICU support enabled. This is a bug I'm still trying to track down.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
|
|
|||
BIN
python/res/asc.gif
Normal file
|
After Width: | Height: | Size: 54 B |
BIN
python/res/bg.gif
Normal file
|
After Width: | Height: | Size: 64 B |
BIN
python/res/desc.gif
Normal file
|
After Width: | Height: | Size: 54 B |
BIN
python/res/icons/first.png
Normal file
|
After Width: | Height: | Size: 720 B |
BIN
python/res/icons/last.png
Normal file
|
After Width: | Height: | Size: 737 B |
BIN
python/res/icons/next.png
Normal file
|
After Width: | Height: | Size: 736 B |
BIN
python/res/icons/prev.png
Normal file
|
After Width: | Height: | Size: 745 B |
32
python/res/jquery-latest.js
vendored
Normal file
12
python/res/jquery.dimensions.min.js
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/* Copyright (c) 2007 Paul Bakaus (paul.bakaus@googlemail.com) and Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
|
||||
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
|
||||
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
|
||||
*
|
||||
* $LastChangedDate: 2007-12-20 08:43:48 -0600 (Thu, 20 Dec 2007) $
|
||||
* $Rev: 4257 $
|
||||
*
|
||||
* Version: 1.2
|
||||
*
|
||||
* Requires: jQuery 1.2+
|
||||
*/
|
||||
(function($){$.dimensions={version:'1.2'};$.each(['Height','Width'],function(i,name){$.fn['inner'+name]=function(){if(!this[0])return;var torl=name=='Height'?'Top':'Left',borr=name=='Height'?'Bottom':'Right';return this.is(':visible')?this[0]['client'+name]:num(this,name.toLowerCase())+num(this,'padding'+torl)+num(this,'padding'+borr);};$.fn['outer'+name]=function(options){if(!this[0])return;var torl=name=='Height'?'Top':'Left',borr=name=='Height'?'Bottom':'Right';options=$.extend({margin:false},options||{});var val=this.is(':visible')?this[0]['offset'+name]:num(this,name.toLowerCase())+num(this,'border'+torl+'Width')+num(this,'border'+borr+'Width')+num(this,'padding'+torl)+num(this,'padding'+borr);return val+(options.margin?(num(this,'margin'+torl)+num(this,'margin'+borr)):0);};});$.each(['Left','Top'],function(i,name){$.fn['scroll'+name]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(name=='Left'?val:$(window)['scrollLeft'](),name=='Top'?val:$(window)['scrollTop']()):this['scroll'+name]=val;}):this[0]==window||this[0]==document?self[(name=='Left'?'pageXOffset':'pageYOffset')]||$.boxModel&&document.documentElement['scroll'+name]||document.body['scroll'+name]:this[0]['scroll'+name];};});$.fn.extend({position:function(){var left=0,top=0,elem=this[0],offset,parentOffset,offsetParent,results;if(elem){offsetParent=this.offsetParent();offset=this.offset();parentOffset=offsetParent.offset();offset.top-=num(elem,'marginTop');offset.left-=num(elem,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&$.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return $(offsetParent);}});function num(el,prop){return parseInt($.curCSS(el.jquery?el[0]:el,prop,true))||0;};})(jQuery);
|
||||
122
python/res/jquery.metadata.js
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Metadata - jQuery plugin for parsing metadata from elements
|
||||
*
|
||||
* Copyright (c) 2006 John Resig, Yehuda Katz, J<EFBFBD>örn Zaefferer, Paul McLanahan
|
||||
*
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
* Revision: $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets the type of metadata to use. Metadata is encoded in JSON, and each property
|
||||
* in the JSON will become a property of the element itself.
|
||||
*
|
||||
* There are three supported types of metadata storage:
|
||||
*
|
||||
* attr: Inside an attribute. The name parameter indicates *which* attribute.
|
||||
*
|
||||
* class: Inside the class attribute, wrapped in curly braces: { }
|
||||
*
|
||||
* elem: Inside a child element (e.g. a script tag). The
|
||||
* name parameter indicates *which* element.
|
||||
*
|
||||
* The metadata for an element is loaded the first time the element is accessed via jQuery.
|
||||
*
|
||||
* As a result, you can define the metadata type, use $(expr) to load the metadata into the elements
|
||||
* matched by expr, then redefine the metadata type and run another $(expr) for other elements.
|
||||
*
|
||||
* @name $.metadata.setType
|
||||
*
|
||||
* @example <p id="one" class="some_class {item_id: 1, item_label: 'Label'}">This is a p</p>
|
||||
* @before $.metadata.setType("class")
|
||||
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
|
||||
* @desc Reads metadata from the class attribute
|
||||
*
|
||||
* @example <p id="one" class="some_class" data="{item_id: 1, item_label: 'Label'}">This is a p</p>
|
||||
* @before $.metadata.setType("attr", "data")
|
||||
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
|
||||
* @desc Reads metadata from a "data" attribute
|
||||
*
|
||||
* @example <p id="one" class="some_class"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>
|
||||
* @before $.metadata.setType("elem", "script")
|
||||
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
|
||||
* @desc Reads metadata from a nested script element
|
||||
*
|
||||
* @param String type The encoding type
|
||||
* @param String name The name of the attribute to be used to get metadata (optional)
|
||||
* @cat Plugins/Metadata
|
||||
* @descr Sets the type of encoding to be used when loading metadata for the first time
|
||||
* @type undefined
|
||||
* @see metadata()
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
$.extend({
|
||||
metadata : {
|
||||
defaults : {
|
||||
type: 'class',
|
||||
name: 'metadata',
|
||||
cre: /({.*})/,
|
||||
single: 'metadata'
|
||||
},
|
||||
setType: function( type, name ){
|
||||
this.defaults.type = type;
|
||||
this.defaults.name = name;
|
||||
},
|
||||
get: function( elem, opts ){
|
||||
var settings = $.extend({},this.defaults,opts);
|
||||
// check for empty string in single property
|
||||
if ( !settings.single.length ) settings.single = 'metadata';
|
||||
|
||||
var data = $.data(elem, settings.single);
|
||||
// returned cached data if it already exists
|
||||
if ( data ) return data;
|
||||
|
||||
data = "{}";
|
||||
|
||||
if ( settings.type == "class" ) {
|
||||
var m = settings.cre.exec( elem.className );
|
||||
if ( m )
|
||||
data = m[1];
|
||||
} else if ( settings.type == "elem" ) {
|
||||
if( !elem.getElementsByTagName )
|
||||
return undefined;
|
||||
var e = elem.getElementsByTagName(settings.name);
|
||||
if ( e.length )
|
||||
data = $.trim(e[0].innerHTML);
|
||||
} else if ( elem.getAttribute != undefined ) {
|
||||
var attr = elem.getAttribute( settings.name );
|
||||
if ( attr )
|
||||
data = attr;
|
||||
}
|
||||
|
||||
if ( data.indexOf( '{' ) <0 )
|
||||
data = "{" + data + "}";
|
||||
|
||||
data = eval("(" + data + ")");
|
||||
|
||||
$.data( elem, settings.single, data );
|
||||
return data;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the metadata object for the first member of the jQuery object.
|
||||
*
|
||||
* @name metadata
|
||||
* @descr Returns element's metadata object
|
||||
* @param Object opts An object contianing settings to override the defaults
|
||||
* @type jQuery
|
||||
* @cat Plugins/Metadata
|
||||
*/
|
||||
$.fn.metadata = function( opts ){
|
||||
return $.metadata.get( this[0], opts );
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
2
python/res/jquery.tablesorter.min.js
vendored
Normal file
25
python/res/jquery.tablesorter.pager.css
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
div.tablesorterPager {
|
||||
padding: 10px 0 10px 0;
|
||||
background-color: #D6D2C2;
|
||||
text-align: center;
|
||||
}
|
||||
div.tablesorterPager span {
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
div.tablesorterPager input.prev {
|
||||
width: auto;
|
||||
margin-right: 10px;
|
||||
}
|
||||
div.tablesorterPager input.next {
|
||||
width: auto;
|
||||
margin-left: 10px;
|
||||
}
|
||||
div.tablesorterPager input {
|
||||
font-size: 8px;
|
||||
width: 50px;
|
||||
border: 1px solid #330000;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
184
python/res/jquery.tablesorter.pager.js
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
(function($) {
|
||||
$.extend({
|
||||
tablesorterPager: new function() {
|
||||
|
||||
function updatePageDisplay(c) {
|
||||
var s = $(c.cssPageDisplay,c.container).val((c.page+1) + c.seperator + c.totalPages);
|
||||
}
|
||||
|
||||
function setPageSize(table,size) {
|
||||
var c = table.config;
|
||||
c.size = size;
|
||||
c.totalPages = Math.ceil(c.totalRows / c.size);
|
||||
c.pagerPositionSet = false;
|
||||
moveToPage(table);
|
||||
fixPosition(table);
|
||||
}
|
||||
|
||||
function fixPosition(table) {
|
||||
var c = table.config;
|
||||
if(!c.pagerPositionSet && c.positionFixed) {
|
||||
var c = table.config, o = $(table);
|
||||
if(o.offset) {
|
||||
c.container.css({
|
||||
top: o.offset().top + o.height() + 'px',
|
||||
position: 'absolute'
|
||||
});
|
||||
}
|
||||
c.pagerPositionSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
function moveToFirstPage(table) {
|
||||
var c = table.config;
|
||||
c.page = 0;
|
||||
moveToPage(table);
|
||||
}
|
||||
|
||||
function moveToLastPage(table) {
|
||||
var c = table.config;
|
||||
c.page = (c.totalPages-1);
|
||||
moveToPage(table);
|
||||
}
|
||||
|
||||
function moveToNextPage(table) {
|
||||
var c = table.config;
|
||||
c.page++;
|
||||
if(c.page >= (c.totalPages-1)) {
|
||||
c.page = (c.totalPages-1);
|
||||
}
|
||||
moveToPage(table);
|
||||
}
|
||||
|
||||
function moveToPrevPage(table) {
|
||||
var c = table.config;
|
||||
c.page--;
|
||||
if(c.page <= 0) {
|
||||
c.page = 0;
|
||||
}
|
||||
moveToPage(table);
|
||||
}
|
||||
|
||||
|
||||
function moveToPage(table) {
|
||||
var c = table.config;
|
||||
if(c.page < 0 || c.page > (c.totalPages-1)) {
|
||||
c.page = 0;
|
||||
}
|
||||
|
||||
renderTable(table,c.rowsCopy);
|
||||
}
|
||||
|
||||
function renderTable(table,rows) {
|
||||
|
||||
var c = table.config;
|
||||
var l = rows.length;
|
||||
var s = (c.page * c.size);
|
||||
var e = (s + c.size);
|
||||
if(e > rows.length ) {
|
||||
e = rows.length;
|
||||
}
|
||||
|
||||
|
||||
var tableBody = $(table.tBodies[0]);
|
||||
|
||||
// clear the table body
|
||||
|
||||
$.tablesorter.clearTableBody(table);
|
||||
|
||||
for(var i = s; i < e; i++) {
|
||||
|
||||
//tableBody.append(rows[i]);
|
||||
|
||||
var o = rows[i];
|
||||
var l = o.length;
|
||||
for(var j=0; j < l; j++) {
|
||||
|
||||
tableBody[0].appendChild(o[j]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fixPosition(table,tableBody);
|
||||
|
||||
$(table).trigger("applyWidgets");
|
||||
|
||||
if( c.page >= c.totalPages ) {
|
||||
moveToLastPage(table);
|
||||
}
|
||||
|
||||
updatePageDisplay(c);
|
||||
}
|
||||
|
||||
this.appender = function(table,rows) {
|
||||
|
||||
var c = table.config;
|
||||
|
||||
c.rowsCopy = rows;
|
||||
c.totalRows = rows.length;
|
||||
c.totalPages = Math.ceil(c.totalRows / c.size);
|
||||
|
||||
renderTable(table,rows);
|
||||
};
|
||||
|
||||
this.defaults = {
|
||||
size: 10,
|
||||
offset: 0,
|
||||
page: 0,
|
||||
totalRows: 0,
|
||||
totalPages: 0,
|
||||
container: null,
|
||||
cssNext: '.next',
|
||||
cssPrev: '.prev',
|
||||
cssFirst: '.first',
|
||||
cssLast: '.last',
|
||||
cssPageDisplay: '.pagedisplay',
|
||||
cssPageSize: '.pagesize',
|
||||
seperator: "/",
|
||||
positionFixed: true,
|
||||
appender: this.appender
|
||||
};
|
||||
|
||||
this.construct = function(settings) {
|
||||
|
||||
return this.each(function() {
|
||||
|
||||
config = $.extend(this.config, $.tablesorterPager.defaults, settings);
|
||||
|
||||
var table = this, pager = config.container;
|
||||
|
||||
$(this).trigger("appendCache");
|
||||
|
||||
config.size = parseInt($(".pagesize",pager).val());
|
||||
|
||||
$(config.cssFirst,pager).click(function() {
|
||||
moveToFirstPage(table);
|
||||
return false;
|
||||
});
|
||||
$(config.cssNext,pager).click(function() {
|
||||
moveToNextPage(table);
|
||||
return false;
|
||||
});
|
||||
$(config.cssPrev,pager).click(function() {
|
||||
moveToPrevPage(table);
|
||||
return false;
|
||||
});
|
||||
$(config.cssLast,pager).click(function() {
|
||||
moveToLastPage(table);
|
||||
return false;
|
||||
});
|
||||
$(config.cssPageSize,pager).change(function() {
|
||||
setPageSize(table,parseInt($(this).val()));
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
});
|
||||
// extend plugin scope
|
||||
$.fn.extend({
|
||||
tablesorterPager: $.tablesorterPager.construct
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
39
python/res/style.css
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* tables */
|
||||
table.tablesorter {
|
||||
font-family:arial;
|
||||
background-color: #CDCDCD;
|
||||
margin:10px 0pt 15px;
|
||||
font-size: 8pt;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
table.tablesorter thead tr th, table.tablesorter tfoot tr th {
|
||||
background-color: #e6EEEE;
|
||||
border: 1px solid #FFF;
|
||||
font-size: 8pt;
|
||||
padding: 4px;
|
||||
}
|
||||
table.tablesorter thead tr .header {
|
||||
background-image: url(bg.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center right;
|
||||
cursor: pointer;
|
||||
}
|
||||
table.tablesorter tbody td {
|
||||
color: #3D3D3D;
|
||||
padding: 4px;
|
||||
background-color: #FFF;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.tablesorter tbody tr.odd td {
|
||||
background-color:#F0F0F6;
|
||||
}
|
||||
table.tablesorter thead tr .headerSortUp {
|
||||
background-image: url(asc.gif);
|
||||
}
|
||||
table.tablesorter thead tr .headerSortDown {
|
||||
background-image: url(desc.gif);
|
||||
}
|
||||
table.tablesorter thead tr .headerSortDown, table.tablesorter thead tr .headerSortUp {
|
||||
background-color: #8dbdd8;
|
||||
}
|
||||
166
python/server.py
|
|
@ -1,13 +1,132 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import ledger
|
||||
import cgi
|
||||
import sys
|
||||
import types
|
||||
import posixpath
|
||||
import urllib
|
||||
import shutil
|
||||
import os
|
||||
import re
|
||||
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
from os.path import exists, join, isfile
|
||||
|
||||
from Cheetah.Template import Template
|
||||
from Cheetah.Filters import Filter, WebSafe
|
||||
|
||||
webroot = join(os.getcwd(), 'python', 'res')
|
||||
|
||||
class UnicodeFilter(Filter):
|
||||
def filter(self, s, **kargs):
|
||||
return Filter.filter(self, s, str=unicode, **kargs)
|
||||
|
||||
def strip(value):
|
||||
#return re.sub('\n', '<br />', value.strip_annotations().to_string())
|
||||
return value.strip_annotations().to_string()
|
||||
|
||||
templateDef = '''#encoding utf-8
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>$title</title>
|
||||
<link rel="stylesheet" href="/style.css" type="text/css" media="print, projection, screen" />
|
||||
<script type="text/javascript" src="/jquery-latest.js"></script>
|
||||
<script type="text/javascript" src="/jquery.tablesorter.min.js"></script>
|
||||
<script type="text/javascript" src="/jquery.tablesorter.pager.js"></script>
|
||||
<script type="text/javascript" src="/jquery.dimensions.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
\$(function() {
|
||||
\$("table")
|
||||
.tablesorter({textExtraction: 'complex',
|
||||
widthFixed: true,
|
||||
widgets: ['zebra']})
|
||||
.tablesorterPager({size: 100,
|
||||
container: \$("\#pager")});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
<h1>Register report</h1>
|
||||
<table id="register" cellspacing="1" class="tablesorter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Payee</th>
|
||||
<th>Account</th>
|
||||
<th>Amount</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Payee</th>
|
||||
<th>Account</th>
|
||||
<th>Amount</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
#for $post in $posts
|
||||
#set $total = $total + $post.amount
|
||||
<tr>
|
||||
<!--<td>${$post.xact.date if $post.xact is not $last_xact else $empty}</td>
|
||||
<td>${$post.xact.payee if $post.xact is not $last_xact else $empty}</td>-->
|
||||
<td>$post.xact.date</td>
|
||||
<td>$post.xact.payee</td>
|
||||
<td>$post.account</td>
|
||||
<td>${strip($post.amount)}</td>
|
||||
<td>${strip($total)}</td>
|
||||
</tr>
|
||||
#set $last_xact = $post.xact
|
||||
#end for
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="pager" class="pager">
|
||||
<form>
|
||||
<img src="/icons/first.png" class="first"/>
|
||||
<img src="/icons/prev.png" class="prev"/>
|
||||
<input type="text" class="pagedisplay"/>
|
||||
<img src="/icons/next.png" class="next"/>
|
||||
<img src="/icons/last.png" class="last"/>
|
||||
<select class="pagesize">
|
||||
<option selected="selected" value="40">40</option>
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
<option value="300">300</option>
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
class LedgerHandler(BaseHTTPRequestHandler):
|
||||
def __init__(self, *args):
|
||||
self.journal = ledger.Journal(sys.argv[1])
|
||||
BaseHTTPRequestHandler.__init__(self, *args)
|
||||
|
||||
def do_GET(self):
|
||||
print "Saw a GET request!"
|
||||
sys.exit(0)
|
||||
path = self.translate_path(self.path)
|
||||
|
||||
if path and exists(path) and isfile(path):
|
||||
self.copyfile(open(path), self.wfile)
|
||||
else:
|
||||
tmpl = Template(templateDef, filter=UnicodeFilter)
|
||||
|
||||
tmpl.title = 'Ledger Journal'
|
||||
tmpl.posts = self.journal.collect(sys.argv[2])
|
||||
tmpl.total = ledger.Value(0)
|
||||
tmpl.strip = strip
|
||||
tmpl.last_xact = None
|
||||
tmpl.empty = ""
|
||||
|
||||
html = unicode(tmpl)
|
||||
html = html.encode('utf-8')
|
||||
self.wfile.write(html)
|
||||
|
||||
def do_POST(self):
|
||||
print "Saw a POST request!"
|
||||
|
|
@ -20,6 +139,45 @@ class LedgerHandler(BaseHTTPRequestHandler):
|
|||
except Exception:
|
||||
print "Saw exception in POST handler"
|
||||
|
||||
# This code is straight from SimpleHTTPServer.py
|
||||
def copyfile(self, source, outputfile):
|
||||
"""Copy all data between two file objects.
|
||||
|
||||
The SOURCE argument is a file object open for reading
|
||||
(or anything with a read() method) and the DESTINATION
|
||||
argument is a file object open for writing (or
|
||||
anything with a write() method).
|
||||
|
||||
The only reason for overriding this would be to change
|
||||
the block size or perhaps to replace newlines by CRLF
|
||||
-- note however that this the default server uses this
|
||||
to copy binary data as well.
|
||||
|
||||
"""
|
||||
shutil.copyfileobj(source, outputfile)
|
||||
|
||||
def translate_path(self, path):
|
||||
"""Translate a /-separated PATH to the local filename syntax.
|
||||
|
||||
Components that mean special things to the local file system
|
||||
(e.g. drive or directory names) are ignored. (XXX They should
|
||||
probably be diagnosed.)
|
||||
|
||||
"""
|
||||
# abandon query parameters
|
||||
path = path.split('?',1)[0]
|
||||
path = path.split('#',1)[0]
|
||||
path = posixpath.normpath(urllib.unquote(path))
|
||||
words = path.split('/')
|
||||
words = filter(None, words)
|
||||
path = webroot
|
||||
for word in words:
|
||||
drive, word = os.path.splitdrive(word)
|
||||
head, word = os.path.split(word)
|
||||
if word in (os.curdir, os.pardir): continue
|
||||
path = os.path.join(path, word)
|
||||
return path
|
||||
|
||||
def main(*args):
|
||||
try:
|
||||
port = 9000
|
||||
|
|
@ -31,6 +189,8 @@ def main(*args):
|
|||
print "Shutting down server"
|
||||
server.socket.close()
|
||||
|
||||
print __name__
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 3:
|
||||
print "usage: server.py <DATA-FILE> <REPORT-QUERY>"
|
||||
sys.exit(1)
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -496,6 +496,9 @@ public:
|
|||
long to_long() const;
|
||||
bool fits_in_long() const;
|
||||
|
||||
operator string() const {
|
||||
return to_string();
|
||||
}
|
||||
string to_string() const;
|
||||
string to_fullstring() const;
|
||||
string quantity_string() const;
|
||||
|
|
|
|||
|
|
@ -461,6 +461,15 @@ public:
|
|||
* Conversion methods. A balance can be converted to an amount, but
|
||||
* only if contains a single component amount.
|
||||
*/
|
||||
operator string() const {
|
||||
return to_string();
|
||||
}
|
||||
string to_string() const {
|
||||
std::ostringstream buf;
|
||||
print(buf);
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
amount_t to_amount() const {
|
||||
if (is_empty())
|
||||
throw_(balance_error, _("Cannot convert an empty balance to an amount"));
|
||||
|
|
@ -532,8 +541,8 @@ public:
|
|||
void print(std::ostream& out,
|
||||
const int first_width = -1,
|
||||
const int latter_width = -1,
|
||||
const bool right_justify = true,
|
||||
const bool colorize = true) const;
|
||||
const bool right_justify = false,
|
||||
const bool colorize = false) const;
|
||||
|
||||
/**
|
||||
* Debugging methods. There are two methods defined to help with
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ public:
|
|||
if (! args.empty())
|
||||
parse_args(args);
|
||||
}
|
||||
~draft_t() {
|
||||
virtual ~draft_t() {
|
||||
TRACE_DTOR(draft_t);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public:
|
|||
parse(in, flags);
|
||||
}
|
||||
|
||||
~expr_t() throw() {
|
||||
virtual ~expr_t() {
|
||||
TRACE_DTOR(expr_t);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -92,8 +92,7 @@ public:
|
|||
{
|
||||
TRACE_CTOR(expr_base_t, "scope_t *");
|
||||
}
|
||||
|
||||
~expr_base_t() throw() {
|
||||
virtual ~expr_base_t() {
|
||||
TRACE_DTOR(expr_base_t);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ public:
|
|||
if (! _str.empty())
|
||||
parse_format(_str);
|
||||
}
|
||||
~format_t() {
|
||||
virtual ~format_t() {
|
||||
TRACE_DTOR(format_t);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public:
|
|||
: expr_t(in, flags), what_to_keep(_what_to_keep) {
|
||||
TRACE_CTOR(predicate_t, "std::istream&, keep_details_t, parse_flags_t");
|
||||
}
|
||||
~predicate_t() throw() {
|
||||
virtual ~predicate_t() {
|
||||
TRACE_DTOR(predicate_t);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <system.hh>
|
||||
|
||||
#include "pyinterp.h"
|
||||
#include "pyutils.h"
|
||||
#include "account.h"
|
||||
#include "post.h"
|
||||
|
||||
|
|
@ -90,6 +91,10 @@ namespace {
|
|||
return account.xdata();
|
||||
}
|
||||
|
||||
PyObject * py_account_unicode(account_t& account) {
|
||||
return str_to_py_unicode(account.fullname());
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
void export_account()
|
||||
|
|
@ -180,7 +185,8 @@ void export_account()
|
|||
.def_readwrite("note", &account_t::note)
|
||||
.def_readonly("depth", &account_t::depth)
|
||||
|
||||
.def(self_ns::str(self))
|
||||
.def("__str__", &account_t::fullname)
|
||||
.def("__unicode__", py_account_unicode)
|
||||
|
||||
.def("fullname", &account_t::fullname)
|
||||
.def("partial_name", &account_t::partial_name)
|
||||
|
|
|
|||
|
|
@ -98,6 +98,10 @@ namespace {
|
|||
return amount.strip_annotations(keep);
|
||||
}
|
||||
|
||||
PyObject * py_amount_unicode(amount_t& amount) {
|
||||
return str_to_py_unicode(amount.to_string());
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
#define EXC_TRANSLATOR(type) \
|
||||
|
|
@ -248,8 +252,9 @@ internal precision."))
|
|||
.def("__int__", &amount_t::to_long)
|
||||
.def("fits_in_long", &amount_t::fits_in_long)
|
||||
|
||||
.def("to_string", &amount_t::to_string)
|
||||
.def("__str__", &amount_t::to_string)
|
||||
.def("to_string", &amount_t::to_string)
|
||||
.def("__unicode__", py_amount_unicode)
|
||||
.def("to_fullstring", &amount_t::to_fullstring)
|
||||
.def("__repr__", &amount_t::to_fullstring)
|
||||
.def("quantity_string", &amount_t::quantity_string)
|
||||
|
|
|
|||
|
|
@ -105,6 +105,10 @@ namespace {
|
|||
return balance.strip_annotations(keep);
|
||||
}
|
||||
|
||||
PyObject * py_balance_unicode(balance_t& balance) {
|
||||
return str_to_py_unicode(balance.to_string());
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
#define EXC_TRANSLATOR(type) \
|
||||
|
|
@ -152,7 +156,9 @@ void export_balance()
|
|||
.def(self != long())
|
||||
.def(! self)
|
||||
|
||||
.def(self_ns::str(self))
|
||||
.def("__str__", &balance_t::to_string)
|
||||
.def("to_string", &balance_t::to_string)
|
||||
.def("__unicode__", py_balance_unicode)
|
||||
|
||||
.def("negated", &balance_t::negated)
|
||||
.def("in_place_negate", &balance_t::in_place_negate,
|
||||
|
|
|
|||
|
|
@ -232,6 +232,10 @@ namespace {
|
|||
return ann.price = price;
|
||||
}
|
||||
|
||||
PyObject * py_commodity_unicode(commodity_t& commodity) {
|
||||
return str_to_py_unicode(commodity.symbol());
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
void export_commodity()
|
||||
|
|
@ -331,6 +335,8 @@ void export_commodity()
|
|||
make_getter(&commodity_t::european_by_default),
|
||||
make_setter(&commodity_t::european_by_default))
|
||||
|
||||
.def("__str__", &commodity_t::symbol)
|
||||
.def("__unicode__", py_commodity_unicode)
|
||||
.def("__nonzero__", &commodity_t::operator bool)
|
||||
|
||||
.def(self == self)
|
||||
|
|
|
|||
|
|
@ -79,15 +79,8 @@ struct string_to_python
|
|||
{
|
||||
static PyObject* convert(const string& str)
|
||||
{
|
||||
#if 1
|
||||
// Return a Unicode object
|
||||
PyObject * pstr = PyString_FromString(str.c_str());
|
||||
PyObject * uni = PyUnicode_FromEncodedObject(pstr, "UTF-8", NULL);
|
||||
return object(handle<>(borrowed(uni))).ptr();
|
||||
#else
|
||||
// Return a 7-bit ASCII string
|
||||
// Return bytes, not characters; see __unicode__ methods for that
|
||||
return incref(object(static_cast<const std::string&>(str)).ptr());
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,11 @@ namespace {
|
|||
value_t py_strip_annotations_1(value_t& value, const keep_details_t& keep) {
|
||||
return value.strip_annotations(keep);
|
||||
}
|
||||
|
||||
PyObject * py_value_unicode(value_t& value) {
|
||||
return str_to_py_unicode(value.to_string());
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
#define EXC_TRANSLATOR(type) \
|
||||
|
|
@ -115,16 +120,16 @@ EXC_TRANSLATOR(value_error)
|
|||
void export_value()
|
||||
{
|
||||
enum_< value_t::type_t >("ValueType")
|
||||
.value("VOID", value_t::VOID)
|
||||
.value("BOOLEAN", value_t::BOOLEAN)
|
||||
.value("DATETIME", value_t::DATETIME)
|
||||
.value("DATE", value_t::DATE)
|
||||
.value("INTEGER", value_t::INTEGER)
|
||||
.value("AMOUNT", value_t::AMOUNT)
|
||||
.value("BALANCE", value_t::BALANCE)
|
||||
.value("STRING", value_t::STRING)
|
||||
.value("SEQUENCE", value_t::SEQUENCE)
|
||||
.value("SCOPE", value_t::SCOPE)
|
||||
.value("Void", value_t::VOID)
|
||||
.value("Boolean", value_t::BOOLEAN)
|
||||
.value("DateTime", value_t::DATETIME)
|
||||
.value("Date", value_t::DATE)
|
||||
.value("Integer", value_t::INTEGER)
|
||||
.value("Amount", value_t::AMOUNT)
|
||||
.value("Balance", value_t::BALANCE)
|
||||
.value("String", value_t::STRING)
|
||||
.value("Sequence", value_t::SEQUENCE)
|
||||
.value("Scope", value_t::SCOPE)
|
||||
;
|
||||
|
||||
class_< value_t > ("Value")
|
||||
|
|
@ -309,11 +314,13 @@ void export_value()
|
|||
.def("to_date", &value_t::to_date)
|
||||
.def("to_amount", &value_t::to_amount)
|
||||
.def("to_balance", &value_t::to_balance)
|
||||
.def("__str__", &value_t::to_string)
|
||||
.def("__unicode__", py_value_unicode)
|
||||
.def("to_string", &value_t::to_string)
|
||||
.def("to_mask", &value_t::to_mask)
|
||||
.def("to_sequence", &value_t::to_sequence)
|
||||
|
||||
.def("__str__", py_dump_relaxed)
|
||||
.def("__unicode__", py_dump_relaxed)
|
||||
.def("__repr__", py_dump)
|
||||
|
||||
.def("casted", &value_t::casted)
|
||||
|
|
|
|||
|
|
@ -126,6 +126,15 @@ struct map_value_type_converter
|
|||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
PyObject * str_to_py_unicode(const T& str)
|
||||
{
|
||||
using namespace boost::python;
|
||||
PyObject * pstr = PyString_FromString(str.c_str());
|
||||
PyObject * uni = PyUnicode_FromEncodedObject(pstr, "UTF-8", NULL);
|
||||
return object(handle<>(borrowed(uni))).ptr();
|
||||
}
|
||||
|
||||
namespace boost { namespace python {
|
||||
|
||||
// Use expr to create the PyObject corresponding to x
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ public:
|
|||
if (! args.empty())
|
||||
parse_args(args);
|
||||
}
|
||||
~query_t() throw() {
|
||||
virtual ~query_t() {
|
||||
TRACE_DTOR(query_t);
|
||||
}
|
||||
|
||||
|
|
|
|||
17
src/value.cc
|
|
@ -1114,18 +1114,18 @@ void value_t::in_place_cast(type_t cast_type)
|
|||
break;
|
||||
}
|
||||
|
||||
case BALANCE:
|
||||
case BALANCE: {
|
||||
const balance_t& bal(as_balance());
|
||||
switch (cast_type) {
|
||||
case AMOUNT: {
|
||||
const balance_t& temp(as_balance());
|
||||
if (temp.amounts.size() == 1) {
|
||||
if (bal.amounts.size() == 1) {
|
||||
// Because we are changing the current balance value to an amount
|
||||
// value, and because set_amount takes a reference (and that memory is
|
||||
// about to be repurposed), we must pass in a copy.
|
||||
set_amount(amount_t((*temp.amounts.begin()).second));
|
||||
set_amount(amount_t((*bal.amounts.begin()).second));
|
||||
return;
|
||||
}
|
||||
else if (temp.amounts.size() == 0) {
|
||||
else if (bal.amounts.size() == 0) {
|
||||
set_amount(0L);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1135,10 +1135,17 @@ void value_t::in_place_cast(type_t cast_type)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case STRING:
|
||||
if (bal.is_empty())
|
||||
set_string("");
|
||||
else
|
||||
set_string(as_balance().to_string());
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STRING:
|
||||
switch (cast_type) {
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@
|
|||
>>>2
|
||||
While parsing file "$sourcepath/src/amount.h", line 67:
|
||||
Error: No quantity specified for amount
|
||||
While parsing file "$sourcepath/src/amount.h", line 717:
|
||||
While parsing file "$sourcepath/src/amount.h", line 720:
|
||||
Error: Invalid date/time: line amount_t amoun
|
||||
While parsing file "$sourcepath/src/amount.h", line 723:
|
||||
While parsing file "$sourcepath/src/amount.h", line 726:
|
||||
Error: Invalid date/time: line string amount_
|
||||
While parsing file "$sourcepath/src/amount.h", line 729:
|
||||
While parsing file "$sourcepath/src/amount.h", line 732:
|
||||
Error: Invalid date/time: line string amount_
|
||||
While parsing file "$sourcepath/src/amount.h", line 735:
|
||||
While parsing file "$sourcepath/src/amount.h", line 738:
|
||||
Error: Invalid date/time: line string amount_
|
||||
While parsing file "$sourcepath/src/amount.h", line 741:
|
||||
While parsing file "$sourcepath/src/amount.h", line 744:
|
||||
Error: Invalid date/time: line std::ostream&
|
||||
While parsing file "$sourcepath/src/amount.h", line 748:
|
||||
While parsing file "$sourcepath/src/amount.h", line 751:
|
||||
Error: Invalid date/time: line std::istream&
|
||||
=== 7
|
||||
|
|
|
|||