mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Improve python bindings: str_methods.py improved.
Patch by Christoph Holtermann: str_methods.py - added a flagging system to change the output of the __str__ methods - added unicode and str-methods to invoice and entry function_class.py - added modified add_method and add_classmethods to implement the previous gnucash_business.py - add methods_return_instance_lists for method Invoice.GetEntries git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@20695 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
@@ -1,26 +1,90 @@
|
||||
#!/usr/bin/env python
|
||||
## @file @brief Add __str__ and __unicode__ methods to financial objects so that @code print object @endcode leads to human readable results
|
||||
""" @package str_methods.py -- Add __str__ and __unicode__ methods to financial objects
|
||||
|
||||
Import this module and str(Object) and unicode(Object) where Object is Transaction, Split,Invoice
|
||||
or Entry leads to human readable results. That is handy when using @code print object @endcode
|
||||
|
||||
I chose to put these functions/methods in a seperate file to develop them like this and maybe if
|
||||
they prove to be useful they can be put in gnucash_core.py.
|
||||
|
||||
I am searching to find the best way to serialize these complex objects. Ideally this serialization
|
||||
would be configurable.
|
||||
|
||||
If someone has suggestions how to beautify, purify or improve this code in any way please feel
|
||||
free to do so.
|
||||
|
||||
This is written as a first approach to a shell-environment using ipython to interactively manipulate
|
||||
GnuCashs Data."""
|
||||
|
||||
# str_methods.py -- Add __str__ and __unicode__ methods to financial objects
|
||||
#
|
||||
|
||||
## @file
|
||||
# @brief Add __str__ and __unicode__ methods to financial objects so that @code print object @endcode leads to human readable results
|
||||
# @author Christoph Holtermann, c.holtermann@gmx.de
|
||||
# @ingroup python_bindings_examples
|
||||
# @date May 2011
|
||||
#
|
||||
# Import this module and str(Object) and unicode(Object) where Object is Transaction or Split leads
|
||||
# to human readable results. That is handy when using @code print object @endcode
|
||||
# ToDo :
|
||||
#
|
||||
# I chose to put these functions/methods in a seperate file to develop them like this and maybe if
|
||||
# they prove to be useful they can be put in gnucash_core.py
|
||||
#
|
||||
# This is written as a first approach to a shell-environment using ipython to interactively manipulate
|
||||
# GnuCashs Data.
|
||||
# * Testing for SWIGtypes
|
||||
# * dealing the cutting format in a bit more elegant way
|
||||
# * having setflag as a classmethod makes it probably impossible to have flags on instance level. Would changing that be useful ?
|
||||
# * It seems useful to have an object for each modification. That is because there is some Initialisation to be done.
|
||||
#
|
||||
|
||||
import gnucash
|
||||
import gnucash, function_class
|
||||
|
||||
# Default values for encoding of strings in GnuCashs Database
|
||||
DEFAULT_ENCODING = "UTF-8"
|
||||
DEFAULT_ERROR = "ignore"
|
||||
|
||||
def setflag(self, name, value):
|
||||
if not(name in self.OPTIONFLAGS_BY_NAME):
|
||||
self.register_optionflag(name)
|
||||
if value == True:
|
||||
self.optionflags |= self.OPTIONFLAGS_BY_NAME[name]
|
||||
else:
|
||||
self.optionflags &= ~self.OPTIONFLAGS_BY_NAME[name]
|
||||
|
||||
def getflag(self, name):
|
||||
if not(name in self.OPTIONFLAGS_BY_NAME):
|
||||
raise KeyError(str(name)+" is not a registered key.")
|
||||
return ((self.optionflags & self.OPTIONFLAGS_BY_NAME[name]) != 0)
|
||||
|
||||
def register_optionflag(self,name):
|
||||
"""Taken from doctest.py"""
|
||||
# Create a new flag unless `name` is already known.
|
||||
return self.OPTIONFLAGS_BY_NAME.setdefault(name, 1 << len(self.OPTIONFLAGS_BY_NAME))
|
||||
|
||||
def ya_add_method(_class, function, method_name=None, clsmethod=False, noinstance=False):
|
||||
"""Calls add_method from function_methods.py but makes it
|
||||
possible to use functions in this module. Also keeps the
|
||||
docstring"""
|
||||
|
||||
if method_name == None:
|
||||
method_name = function.__name__
|
||||
|
||||
setattr(gnucash.gnucash_core_c,function.__name__,function)
|
||||
if clsmethod:
|
||||
mf=_class.ya_add_classmethod(function.__name__,method_name)
|
||||
elif noinstance:
|
||||
mf=_class.add_method(function.__name__,method_name)
|
||||
else:
|
||||
mf=_class.ya_add_method(function.__name__,method_name)
|
||||
if function.__doc__ != None:
|
||||
setattr(mf, "__doc__", function.__doc__)
|
||||
|
||||
def infect(_class, function, method_name):
|
||||
if not getattr(_class, "OPTIONFLAGS_BY_NAME", None):
|
||||
_class.OPTIONFLAGS_BY_NAME={}
|
||||
_class.optionflags=0
|
||||
ya_add_method(_class,register_optionflag,clsmethod=True)
|
||||
ya_add_method(_class,setflag,clsmethod=True)
|
||||
ya_add_method(_class,getflag,clsmethod=True)
|
||||
ya_add_method(_class, function, method_name)
|
||||
|
||||
class ClassWithCutting__format__():
|
||||
"""This class provides a __format__ method which cuts values to a certain width.
|
||||
|
||||
If width is too big '...' will be put at the end of the resulting string."""
|
||||
|
||||
def __init__(self,value):
|
||||
self.value = value
|
||||
|
||||
@@ -99,7 +163,7 @@ class ClassWithCutting__format__():
|
||||
|
||||
return value
|
||||
|
||||
def all_as_ClassWithCutting__format__(*args):
|
||||
def all_as_classwithcutting__format__(*args):
|
||||
"""Converts every argument to instance of ClassWithCutting__format__"""
|
||||
|
||||
import types
|
||||
@@ -111,27 +175,45 @@ def all_as_ClassWithCutting__format__(*args):
|
||||
|
||||
return l
|
||||
|
||||
def all_as_ClassWithCutting__format__keys(**keys):
|
||||
def all_as_classwithcutting__format__keys(encoding=None, error=None, **keys):
|
||||
"""Converts every argument to instance of ClassWithCutting__format__"""
|
||||
|
||||
import types
|
||||
d={}
|
||||
if encoding==None:
|
||||
encoding=DEFAULT_ENCODING
|
||||
if error==None:
|
||||
error=DEFAULT_ERROR
|
||||
for a in keys:
|
||||
if type(keys[a]) in [types.StringType, types.UnicodeType]:
|
||||
keys[a]=keys[a].decode("UTF-8")
|
||||
keys[a]=keys[a].decode(encoding,error)
|
||||
d[a]=ClassWithCutting__format__(keys[a])
|
||||
|
||||
return d
|
||||
|
||||
def __split__unicode__(self):
|
||||
"""__unicode__ method for Split"""
|
||||
|
||||
|
||||
# Split
|
||||
def __split__unicode__(self, encoding=None, error=None):
|
||||
"""__unicode__(self, encoding=None, error=None) -> object
|
||||
|
||||
Serialize the Split object and return as a new Unicode object.
|
||||
|
||||
Keyword arguments:
|
||||
encoding -- defaults to str_methods.default_encoding
|
||||
error -- defaults to str_methods.default_error
|
||||
See help(unicode) for more details or http://docs.python.org/howto/unicode.html.
|
||||
|
||||
"""
|
||||
|
||||
from gnucash import Split
|
||||
import time
|
||||
self=Split(instance=self)
|
||||
#self=Split(instance=self)
|
||||
|
||||
lot=gnucash.GncLot(instance=self.GetLot())
|
||||
lot=self.GetLot()
|
||||
if lot:
|
||||
if type(lot).__name__ == 'SwigPyObject':
|
||||
lot=gnucash.GncLot(instance=lot)
|
||||
lot_str=lot.get_title()
|
||||
else:
|
||||
lot_str='---'
|
||||
@@ -140,40 +222,42 @@ def __split__unicode__(self):
|
||||
|
||||
# This dict and the return statement can be changed according to individual needs
|
||||
fmt_dict={
|
||||
"account_name":'Account:',
|
||||
"account_value":self.GetAccount().name,
|
||||
"value_name":'Value:',
|
||||
"value_value":self.GetValue(),
|
||||
"memo_name":'Memo:',
|
||||
"memo_value":self.GetMemo(),
|
||||
"transaction_name1":'Transaction:',
|
||||
"transaction_value1":time.ctime(transaction.GetDate()),
|
||||
"transaction_name2":u'-',
|
||||
"transaction_value2":transaction.GetDescription(),
|
||||
"lot_name":'Lot: ',
|
||||
"lot_value":lot_str}
|
||||
|
||||
return (u"{account_name:8}{account_value:20} "+
|
||||
u"{value_name:7}{value_value:>10} "+
|
||||
u"{memo_name:7}{memo_value:30} "+
|
||||
u"{transaction_name1:12}{transaction_value1:15} "+
|
||||
u"{transaction_name2:1}{transaction_value2:30} "+
|
||||
u"{lot_name:5}{lot_value:10}").\
|
||||
format(**all_as_ClassWithCutting__format__keys(**fmt_dict))
|
||||
"account":self.GetAccount().name,
|
||||
"value":self.GetValue(),
|
||||
"memo":self.GetMemo(),
|
||||
"lot":lot_str}
|
||||
|
||||
fmt_str= (u"Account: {account:20} "+
|
||||
u"Value: {value:>10} "+
|
||||
u"Memo: {memo:30} ")
|
||||
|
||||
if self.optionflags & self.OPTIONFLAGS_BY_NAME["PRINT_TRANSACTION"]:
|
||||
fmt_t_dict={
|
||||
"transaction_time":time.ctime(transaction.GetDate()),
|
||||
"transaction2":transaction.GetDescription()}
|
||||
fmt_t_str=(
|
||||
u"Transaction: {transaction_time:30} "+
|
||||
u"- {transaction2:30} "+
|
||||
u"Lot: {lot:10}")
|
||||
fmt_dict.update(fmt_t_dict)
|
||||
fmt_str += fmt_t_str
|
||||
|
||||
return fmt_str.format(**all_as_classwithcutting__format__keys(encoding,error,**fmt_dict))
|
||||
|
||||
def __split__str__(self):
|
||||
"""__str__ method for split class"""
|
||||
"""Returns a bytestring representation of self.__unicode__"""
|
||||
|
||||
from gnucash import Split
|
||||
self=Split(instance=self)
|
||||
#self=Split(instance=self)
|
||||
|
||||
return unicode(self).encode('utf-8')
|
||||
|
||||
gnucash.gnucash_core_c.__split__str__=__split__str__
|
||||
gnucash.Split.add_method("__split__str__","__str__")
|
||||
|
||||
gnucash.gnucash_core_c.__split__unicode__=__split__unicode__
|
||||
gnucash.Split.add_method("__split__unicode__","__unicode__")
|
||||
# This could be something like an __init__. Maybe we could call it virus because it infects the Split object which
|
||||
# thereafter mutates to have better capabilities.
|
||||
infect(gnucash.Split,__split__str__,"__str__")
|
||||
infect(gnucash.Split,__split__unicode__,"__unicode__")
|
||||
gnucash.Split.register_optionflag("PRINT_TRANSACTION")
|
||||
gnucash.Split.setflag("PRINT_TRANSACTION",True)
|
||||
|
||||
def __transaction__unicode__(self):
|
||||
"""__unicode__ method for Transaction class"""
|
||||
@@ -181,19 +265,25 @@ def __transaction__unicode__(self):
|
||||
import time
|
||||
self=Transaction(instance=self)
|
||||
|
||||
fmt_tuple=('Date:',str(time.ctime(self.GetDate())),
|
||||
'Description:',str(self.GetDescription()),
|
||||
'Notes:',str(self.GetNotes()))
|
||||
fmt_tuple=('Date:',time.ctime(self.GetDate()),
|
||||
'Description:',self.GetDescription(),
|
||||
'Notes:',self.GetNotes())
|
||||
|
||||
transaction_str = u"{0:6}{1:25} {2:14}{3:40} {4:7}{5:40}".format(
|
||||
*all_as_ClassWithCutting__format__(*fmt_tuple))
|
||||
*all_as_classwithcutting__format__(*fmt_tuple))
|
||||
transaction_str += "\n"
|
||||
|
||||
splits_str=""
|
||||
for n,split in enumerate(self.GetSplitList()):
|
||||
if not type(split)==gnucash.Split:
|
||||
if not (type(split)==gnucash.Split):
|
||||
split=gnucash.Split(instance=split)
|
||||
splits_str += u"[{0:>2}] ".format(str(n))
|
||||
|
||||
transaction_flag = split.getflag("PRINT_TRANSACTION")
|
||||
split.setflag("PRINT_TRANSACTION",False)
|
||||
splits_str += u"[{0:>2}] ".format(unicode(n))
|
||||
splits_str += unicode(split)
|
||||
splits_str += "\n"
|
||||
split.setflag("PRINT_TRANSACTION",transaction_flag)
|
||||
|
||||
return transaction_str + splits_str
|
||||
|
||||
@@ -210,3 +300,94 @@ gnucash.Transaction.add_method("__transaction__str__","__str__")
|
||||
|
||||
gnucash.gnucash_core_c.__transaction__unicode__=__transaction__unicode__
|
||||
gnucash.Transaction.add_method("__transaction__unicode__","__unicode__")
|
||||
|
||||
def __invoice__unicode__(self):
|
||||
"""__unicode__ method for Invoice"""
|
||||
|
||||
from gnucash.gnucash_business import Invoice
|
||||
self=Invoice(instance=self)
|
||||
|
||||
|
||||
# This dict and the return statement can be changed according to individual needs
|
||||
fmt_dict={
|
||||
"id_name":"ID:",
|
||||
"id_value":self.GetID(),
|
||||
"notes_name":"Notes:",
|
||||
"notes_value":self.GetNotes(),
|
||||
"active_name":"Active:",
|
||||
"active_value":str(self.GetActive()),
|
||||
"owner_name":"Owner Name:",
|
||||
"owner_value":self.GetOwner().GetName(),
|
||||
"total_name":"Total:",
|
||||
"total_value":str(self.GetTotal()),
|
||||
"currency_mnemonic":self.GetCurrency().get_mnemonic()}
|
||||
|
||||
ret_invoice= (u"{id_name:4}{id_value:10} {notes_name:7}{notes_value:20} {active_name:8}{active_value:7} {owner_name:12}{owner_value:20}"+
|
||||
u"{total_name:8}{total_value:10}{currency_mnemonic:3}").\
|
||||
format(**all_as_classwithcutting__format__keys(**fmt_dict))
|
||||
|
||||
ret_entries=u""
|
||||
entry_list = self.GetEntries()
|
||||
for entry in entry_list: # Type of entry has to be checked
|
||||
if not(type(entry)==Entry):
|
||||
entry=Entry(instance=entry)
|
||||
ret_entries += " "+unicode(entry)+"\n"
|
||||
|
||||
return ret_invoice+"\n"+ret_entries
|
||||
|
||||
def __invoice__str__(self):
|
||||
"""__str__ method for invoice class"""
|
||||
|
||||
from gnucash.gnucash_business import Invoice
|
||||
self=Invoice(instance=self)
|
||||
|
||||
return unicode(self).encode('utf-8')
|
||||
|
||||
from gnucash.gnucash_business import Invoice
|
||||
|
||||
gnucash.gnucash_core_c.__invoice__str__=__invoice__str__
|
||||
gnucash.gnucash_business.Invoice.add_method("__invoice__str__","__str__")
|
||||
|
||||
gnucash.gnucash_core_c.__invoice__unicode__=__invoice__unicode__
|
||||
gnucash.gnucash_business.Invoice.add_method("__invoice__unicode__","__unicode__")
|
||||
|
||||
def __entry__unicode__(self):
|
||||
"""__unicode__ method for Entry"""
|
||||
|
||||
from gnucash.gnucash_business import Entry
|
||||
self=Entry(instance=self)
|
||||
|
||||
# This dict and the return statement can be changed according to individual needs
|
||||
fmt_dict={
|
||||
"date_name":"Date:",
|
||||
"date_value":unicode(self.GetDate()),
|
||||
"description_name":"Description:",
|
||||
"description_value":self.GetDescription(),
|
||||
"notes_name":"Notes:",
|
||||
"notes_value":self.GetNotes(),
|
||||
"quant_name":"Quantity:",
|
||||
"quant_value":unicode(gnucash.GncNumeric(instance=self.GetQuantity())),
|
||||
"invprice_name":"InvPrice:",
|
||||
"invprice_value":unicode(gnucash.GncNumeric(instance=self.GetInvPrice()))}
|
||||
|
||||
return (u"{date_name:6}{date_value:15} {description_name:13}{description_value:20} {notes_name:7}{notes_value:20}"+
|
||||
u"{quant_name:12}{quant_value:7} {invprice_name:10}{invprice_value:7}").\
|
||||
format(**all_as_classwithcutting__format__keys(**fmt_dict))
|
||||
|
||||
def __entry__str__(self):
|
||||
"""__str__ method for Entry class"""
|
||||
|
||||
from gnucash.gnucash_business import Entry
|
||||
self=Entry(instance=self)
|
||||
|
||||
return unicode(self).encode('utf-8')
|
||||
|
||||
from gnucash.gnucash_business import Entry
|
||||
|
||||
gnucash.gnucash_core_c.__entry__str__=__entry__str__
|
||||
gnucash.gnucash_business.Entry.add_method("__entry__str__","__str__")
|
||||
|
||||
gnucash.gnucash_core_c.__entry__unicode__=__entry__unicode__
|
||||
gnucash.gnucash_business.Entry.add_method("__entry__unicode__","__unicode__")
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ INSTANCE_ARGUMENT = "instance"
|
||||
|
||||
class ClassFromFunctions(object):
|
||||
"""Inherit this class to give yourself a python class that wraps a set of
|
||||
functions that together consitute the methods of the class.
|
||||
functions that together constitute the methods of the class.
|
||||
|
||||
The method functions must all have as a first argument an object
|
||||
holding the instance data. There must also be a function that
|
||||
@@ -50,7 +50,7 @@ class ClassFromFunctions(object):
|
||||
def __new__(cls, *args, **kargs):
|
||||
# why reimpliment __new__? Because later on we're going to
|
||||
# use new to avoid creating new instances when existing instances
|
||||
# already exist with the same __instance value, or equivlent __instance
|
||||
# already exist with the same __instance value, or equivalent __instance
|
||||
# values, where this is desirable...
|
||||
return super(ClassFromFunctions, cls).__new__(cls)
|
||||
|
||||
@@ -93,7 +93,37 @@ class ClassFromFunctions(object):
|
||||
setattr(cls, method_name, method_function)
|
||||
setattr(method_function, "__name__", method_name)
|
||||
return method_function
|
||||
|
||||
|
||||
@classmethod
|
||||
def ya_add_classmethod(cls, function_name, method_name):
|
||||
"""Add the function, method_name to this class as a classmethod named name
|
||||
|
||||
Taken from function_class and slightly modified.
|
||||
"""
|
||||
def method_function(self, *meth_func_args):
|
||||
return getattr(self._module, function_name)(
|
||||
self,
|
||||
*process_list_convert_to_instance(meth_func_args) )
|
||||
|
||||
setattr(cls, method_name, classmethod(method_function))
|
||||
setattr(method_function, "__name__", method_name)
|
||||
return method_function
|
||||
|
||||
@classmethod
|
||||
def ya_add_method(cls, function_name, method_name):
|
||||
"""Add the function, method_name to this class as a method named name
|
||||
|
||||
Taken from function_class and slightly modified.
|
||||
"""
|
||||
def method_function(self, *meth_func_args):
|
||||
return getattr(self._module, function_name)(
|
||||
self,
|
||||
*process_list_convert_to_instance(meth_func_args) )
|
||||
|
||||
setattr(cls, method_name, method_function)
|
||||
setattr(method_function, "__name__", method_name)
|
||||
return method_function
|
||||
|
||||
@classmethod
|
||||
def add_methods_with_prefix(cls, prefix):
|
||||
"""Add a group of functions with the same prefix
|
||||
|
||||
@@ -31,7 +31,7 @@ import gnucash_core_c
|
||||
from function_class import \
|
||||
ClassFromFunctions, extract_attributes_with_prefix, \
|
||||
default_arguments_decorator, method_function_returns_instance, \
|
||||
methods_return_instance
|
||||
methods_return_instance, methods_return_instance_lists
|
||||
|
||||
from gnucash_core import \
|
||||
GnuCashCoreClass, GncNumeric, GncCommodity, Transaction, \
|
||||
@@ -309,6 +309,8 @@ taxtableentry_dict = {
|
||||
|
||||
# Invoice
|
||||
Invoice.add_constructor_and_methods_with_prefix('gncInvoice', 'Create')
|
||||
methods_return_instance_lists(
|
||||
Invoice, { 'GetEntries': Entry })
|
||||
|
||||
# Bill
|
||||
Bill.add_methods_with_prefix('gncBill')
|
||||
|
||||
Reference in New Issue
Block a user