|
GnuCash 2.4.99
|
00001 #!/usr/bin/env python 00002 ## @file @brief Add __str__ and __unicode__ methods to financial objects so that @code print object @endcode leads to human readable results 00003 """ @package str_methods.py -- Add __str__ and __unicode__ methods to financial objects 00004 00005 Import this module and str(Object) and unicode(Object) where Object is Transaction, Split,Invoice 00006 or Entry leads to human readable results. That is handy when using @code print object @endcode 00007 00008 I chose to put these functions/methods in a seperate file to develop them like this and maybe if 00009 they prove to be useful they can be put in gnucash_core.py. 00010 00011 I am searching to find the best way to serialize these complex objects. Ideally this serialization 00012 would be configurable. 00013 00014 If someone has suggestions how to beautify, purify or improve this code in any way please feel 00015 free to do so. 00016 00017 This is written as a first approach to a shell-environment using ipython to interactively manipulate 00018 GnuCashs Data.""" 00019 00020 # @author Christoph Holtermann, c.holtermann@gmx.de 00021 # @ingroup python_bindings_examples 00022 # @date May 2011 00023 # 00024 # ToDo : 00025 # 00026 # * Testing for SWIGtypes 00027 # * dealing the cutting format in a bit more elegant way 00028 # * having setflag as a classmethod makes it probably impossible to have flags on instance level. Would changing that be useful ? 00029 # * It seems useful to have an object for each modification. That is because there is some Initialisation to be done. 00030 # 00031 00032 import gnucash, function_class 00033 00034 # Default values for encoding of strings in GnuCashs Database 00035 DEFAULT_ENCODING = "UTF-8" 00036 DEFAULT_ERROR = "ignore" 00037 00038 def setflag(self, name, value): 00039 if not(name in self.OPTIONFLAGS_BY_NAME): 00040 self.register_optionflag(name) 00041 if value == True: 00042 self.optionflags |= self.OPTIONFLAGS_BY_NAME[name] 00043 else: 00044 self.optionflags &= ~self.OPTIONFLAGS_BY_NAME[name] 00045 00046 def getflag(self, name): 00047 if not(name in self.OPTIONFLAGS_BY_NAME): 00048 raise KeyError(str(name)+" is not a registered key.") 00049 return ((self.optionflags & self.OPTIONFLAGS_BY_NAME[name]) != 0) 00050 00051 def register_optionflag(self,name): 00052 """Taken from doctest.py""" 00053 # Create a new flag unless `name` is already known. 00054 return self.OPTIONFLAGS_BY_NAME.setdefault(name, 1 << len(self.OPTIONFLAGS_BY_NAME)) 00055 00056 def ya_add_method(_class, function, method_name=None, clsmethod=False, noinstance=False): 00057 """Calls add_method from function_methods.py but makes it 00058 possible to use functions in this module. Also keeps the 00059 docstring""" 00060 00061 if method_name == None: 00062 method_name = function.__name__ 00063 00064 setattr(gnucash.gnucash_core_c,function.__name__,function) 00065 if clsmethod: 00066 mf=_class.ya_add_classmethod(function.__name__,method_name) 00067 elif noinstance: 00068 mf=_class.add_method(function.__name__,method_name) 00069 else: 00070 mf=_class.ya_add_method(function.__name__,method_name) 00071 if function.__doc__ != None: 00072 setattr(mf, "__doc__", function.__doc__) 00073 00074 def infect(_class, function, method_name): 00075 if not getattr(_class, "OPTIONFLAGS_BY_NAME", None): 00076 _class.OPTIONFLAGS_BY_NAME={} 00077 _class.optionflags=0 00078 ya_add_method(_class,register_optionflag,clsmethod=True) 00079 ya_add_method(_class,setflag,clsmethod=True) 00080 ya_add_method(_class,getflag,clsmethod=True) 00081 ya_add_method(_class, function, method_name) 00082 00083 class ClassWithCutting__format__(): 00084 """This class provides a __format__ method which cuts values to a certain width. 00085 00086 If width is too big '...' will be put at the end of the resulting string.""" 00087 00088 def __init__(self,value): 00089 self.value = value 00090 00091 def __format__(self, fmt): 00092 def get_width(fmt_spec): 00093 """Parse fmt_spec to obtain width""" 00094 00095 def remove_alignment(fmt_spec): 00096 if fmt_spec[1] in ["<","^",">"]: 00097 fmt_spec=fmt_spec[2:len(fmt_spec)] 00098 return fmt_spec 00099 00100 def remove_sign(fmt_spec): 00101 if fmt_spec[0] in ["-","+"," "]: 00102 fmt_spec=fmt_spec[1:len(fmt_spec)] 00103 return fmt_spec 00104 00105 def remove_cross(fmt_spec): 00106 if fmt_spec[0] in ["#"]: 00107 fmt_spec=fmt_spec[1:len(fmt_spec)] 00108 return fmt_spec 00109 00110 def do_width(fmt_spec): 00111 n="" 00112 00113 while len(fmt_spec)>0: 00114 if fmt_spec[0].isdigit(): 00115 n+=fmt_spec[0] 00116 fmt_spec=fmt_spec[1:len(fmt_spec)] 00117 else: 00118 break 00119 if n: 00120 return int(n) 00121 else: 00122 return None 00123 00124 if len(fmt_spec)>=2: 00125 fmt_spec=remove_alignment(fmt_spec) 00126 if len(fmt_spec)>=1: 00127 fmt_spec=remove_sign(fmt_spec) 00128 if len(fmt_spec)>=1: 00129 fmt_spec=remove_cross(fmt_spec) 00130 width=do_width(fmt_spec) 00131 # Stop parsing here for we only need width 00132 00133 return width 00134 00135 def cut(s, width, replace_string="..."): 00136 """Cuts s to width and puts replace_string at it's end.""" 00137 00138 #s=s.decode('UTF-8', "replace") 00139 00140 if len(s)>width: 00141 if len(replace_string)>width: 00142 replace_string=replace_string[0:width] 00143 s=s[0:width-len(replace_string)] 00144 s=s+replace_string 00145 00146 return s 00147 00148 value=self.value 00149 00150 # Replace Tabs and linebreaks 00151 import types 00152 if type(value) in [types.StringType, types.UnicodeType]: 00153 value=value.replace("\t","|") 00154 value=value.replace("\n","|") 00155 00156 # Do regular formatting of object 00157 value=value.__format__(fmt) 00158 00159 # Cut resulting value if longer than specified by width 00160 width=get_width(fmt) 00161 if width: 00162 value=cut(value, width, "...") 00163 00164 return value 00165 00166 def all_as_classwithcutting__format__(*args): 00167 """Converts every argument to instance of ClassWithCutting__format__""" 00168 00169 import types 00170 l=[] 00171 for a in args: 00172 if type(a) in [types.StringType, types.UnicodeType]: 00173 a=a.decode("UTF-8") 00174 l.append(ClassWithCutting__format__(a)) 00175 00176 return l 00177 00178 def all_as_classwithcutting__format__keys(encoding=None, error=None, **keys): 00179 """Converts every argument to instance of ClassWithCutting__format__""" 00180 00181 import types 00182 d={} 00183 if encoding==None: 00184 encoding=DEFAULT_ENCODING 00185 if error==None: 00186 error=DEFAULT_ERROR 00187 for a in keys: 00188 if type(keys[a]) in [types.StringType, types.UnicodeType]: 00189 keys[a]=keys[a].decode(encoding,error) 00190 d[a]=ClassWithCutting__format__(keys[a]) 00191 00192 return d 00193 00194 00195 00196 # Split 00197 def __split__unicode__(self, encoding=None, error=None): 00198 """__unicode__(self, encoding=None, error=None) -> object 00199 00200 Serialize the Split object and return as a new Unicode object. 00201 00202 Keyword arguments: 00203 encoding -- defaults to str_methods.default_encoding 00204 error -- defaults to str_methods.default_error 00205 See help(unicode) for more details or http://docs.python.org/howto/unicode.html. 00206 00207 """ 00208 00209 from gnucash import Split 00210 import time 00211 #self=Split(instance=self) 00212 00213 lot=self.GetLot() 00214 if lot: 00215 if type(lot).__name__ == 'SwigPyObject': 00216 lot=gnucash.GncLot(instance=lot) 00217 lot_str=lot.get_title() 00218 else: 00219 lot_str='---' 00220 00221 transaction=self.GetParent() 00222 00223 # This dict and the return statement can be changed according to individual needs 00224 fmt_dict={ 00225 "account":self.GetAccount().name, 00226 "value":self.GetValue(), 00227 "memo":self.GetMemo(), 00228 "lot":lot_str} 00229 00230 fmt_str= (u"Account: {account:20} "+ 00231 u"Value: {value:>10} "+ 00232 u"Memo: {memo:30} ") 00233 00234 if self.optionflags & self.OPTIONFLAGS_BY_NAME["PRINT_TRANSACTION"]: 00235 fmt_t_dict={ 00236 "transaction_time":time.ctime(transaction.GetDate()), 00237 "transaction2":transaction.GetDescription()} 00238 fmt_t_str=( 00239 u"Transaction: {transaction_time:30} "+ 00240 u"- {transaction2:30} "+ 00241 u"Lot: {lot:10}") 00242 fmt_dict.update(fmt_t_dict) 00243 fmt_str += fmt_t_str 00244 00245 return fmt_str.format(**all_as_classwithcutting__format__keys(encoding,error,**fmt_dict)) 00246 00247 def __split__str__(self): 00248 """Returns a bytestring representation of self.__unicode__""" 00249 00250 from gnucash import Split 00251 #self=Split(instance=self) 00252 00253 return unicode(self).encode('utf-8') 00254 00255 # This could be something like an __init__. Maybe we could call it virus because it infects the Split object which 00256 # thereafter mutates to have better capabilities. 00257 infect(gnucash.Split,__split__str__,"__str__") 00258 infect(gnucash.Split,__split__unicode__,"__unicode__") 00259 gnucash.Split.register_optionflag("PRINT_TRANSACTION") 00260 gnucash.Split.setflag("PRINT_TRANSACTION",True) 00261 00262 def __transaction__unicode__(self): 00263 """__unicode__ method for Transaction class""" 00264 from gnucash import Transaction 00265 import time 00266 self=Transaction(instance=self) 00267 00268 fmt_tuple=('Date:',time.ctime(self.GetDate()), 00269 'Description:',self.GetDescription(), 00270 'Notes:',self.GetNotes()) 00271 00272 transaction_str = u"{0:6}{1:25} {2:14}{3:40} {4:7}{5:40}".format( 00273 *all_as_classwithcutting__format__(*fmt_tuple)) 00274 transaction_str += "\n" 00275 00276 splits_str="" 00277 for n,split in enumerate(self.GetSplitList()): 00278 if not (type(split)==gnucash.Split): 00279 split=gnucash.Split(instance=split) 00280 00281 transaction_flag = split.getflag("PRINT_TRANSACTION") 00282 split.setflag("PRINT_TRANSACTION",False) 00283 splits_str += u"[{0:>2}] ".format(unicode(n)) 00284 splits_str += unicode(split) 00285 splits_str += "\n" 00286 split.setflag("PRINT_TRANSACTION",transaction_flag) 00287 00288 return transaction_str + splits_str 00289 00290 def __transaction__str__(self): 00291 """__str__ method for Transaction class""" 00292 from gnucash import Transaction 00293 00294 self=Transaction(instance=self) 00295 return unicode(self).encode('utf-8') 00296 00297 # These lines add transaction_str as method __str__ to Transaction object 00298 gnucash.gnucash_core_c.__transaction__str__=__transaction__str__ 00299 gnucash.Transaction.add_method("__transaction__str__","__str__") 00300 00301 gnucash.gnucash_core_c.__transaction__unicode__=__transaction__unicode__ 00302 gnucash.Transaction.add_method("__transaction__unicode__","__unicode__") 00303 00304 def __invoice__unicode__(self): 00305 """__unicode__ method for Invoice""" 00306 00307 from gnucash.gnucash_business import Invoice 00308 self=Invoice(instance=self) 00309 00310 00311 # This dict and the return statement can be changed according to individual needs 00312 fmt_dict={ 00313 "id_name":"ID:", 00314 "id_value":self.GetID(), 00315 "notes_name":"Notes:", 00316 "notes_value":self.GetNotes(), 00317 "active_name":"Active:", 00318 "active_value":str(self.GetActive()), 00319 "owner_name":"Owner Name:", 00320 "owner_value":self.GetOwner().GetName(), 00321 "total_name":"Total:", 00322 "total_value":str(self.GetTotal()), 00323 "currency_mnemonic":self.GetCurrency().get_mnemonic()} 00324 00325 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}"+ 00326 u"{total_name:8}{total_value:10}{currency_mnemonic:3}").\ 00327 format(**all_as_classwithcutting__format__keys(**fmt_dict)) 00328 00329 ret_entries=u"" 00330 entry_list = self.GetEntries() 00331 for entry in entry_list: # Type of entry has to be checked 00332 if not(type(entry)==Entry): 00333 entry=Entry(instance=entry) 00334 ret_entries += " "+unicode(entry)+"\n" 00335 00336 return ret_invoice+"\n"+ret_entries 00337 00338 def __invoice__str__(self): 00339 """__str__ method for invoice class""" 00340 00341 from gnucash.gnucash_business import Invoice 00342 self=Invoice(instance=self) 00343 00344 return unicode(self).encode('utf-8') 00345 00346 from gnucash.gnucash_business import Invoice 00347 00348 gnucash.gnucash_core_c.__invoice__str__=__invoice__str__ 00349 gnucash.gnucash_business.Invoice.add_method("__invoice__str__","__str__") 00350 00351 gnucash.gnucash_core_c.__invoice__unicode__=__invoice__unicode__ 00352 gnucash.gnucash_business.Invoice.add_method("__invoice__unicode__","__unicode__") 00353 00354 def __entry__unicode__(self): 00355 """__unicode__ method for Entry""" 00356 00357 from gnucash.gnucash_business import Entry 00358 self=Entry(instance=self) 00359 00360 # This dict and the return statement can be changed according to individual needs 00361 fmt_dict={ 00362 "date_name":"Date:", 00363 "date_value":unicode(self.GetDate()), 00364 "description_name":"Description:", 00365 "description_value":self.GetDescription(), 00366 "notes_name":"Notes:", 00367 "notes_value":self.GetNotes(), 00368 "quant_name":"Quantity:", 00369 "quant_value":unicode(gnucash.GncNumeric(instance=self.GetQuantity())), 00370 "invprice_name":"InvPrice:", 00371 "invprice_value":unicode(gnucash.GncNumeric(instance=self.GetInvPrice()))} 00372 00373 return (u"{date_name:6}{date_value:15} {description_name:13}{description_value:20} {notes_name:7}{notes_value:20}"+ 00374 u"{quant_name:12}{quant_value:7} {invprice_name:10}{invprice_value:7}").\ 00375 format(**all_as_classwithcutting__format__keys(**fmt_dict)) 00376 00377 def __entry__str__(self): 00378 """__str__ method for Entry class""" 00379 00380 from gnucash.gnucash_business import Entry 00381 self=Entry(instance=self) 00382 00383 return unicode(self).encode('utf-8') 00384 00385 from gnucash.gnucash_business import Entry 00386 00387 gnucash.gnucash_core_c.__entry__str__=__entry__str__ 00388 gnucash.gnucash_business.Entry.add_method("__entry__str__","__str__") 00389 00390 gnucash.gnucash_core_c.__entry__unicode__=__entry__unicode__ 00391 gnucash.gnucash_business.Entry.add_method("__entry__unicode__","__unicode__") 00392 00393
1.7.4