Am 17.05.19 um 01:01 schrieb Martin Blais:
> Alright now I see what you want to do.
> You want to rewrite your payees, but in the source file itself.
> That's a nice idea.

Thanks!

> However, I don't think you'll be able to put together a nice solution with 
> rewriting after processing.
> I would work off the source text itself.
> Or even better: as a combination of both.
> Here's what you could do: parse the entire thing, filter just the 
> transactions.
> For each transaction  you have the filename and line number.
> Do whatever remapping / processing / cleaning you want to do on the payee 
> names in your script.
> Then, process each file, using a regexp to replace the first string that 
> occurs on the lines where you have transactions with renamed payees.
> 
> This is better than working purely from the source file because you won't 
> have to write a full alternative parser to make your replacements; all you 
> need to ace is replacement of the first string on those transaction lines and 
> leave all the other lines untouched. Should be pretty easy and robust enough 
> (tip: make sure you safeguard your files in a git/hg repo and diff just in 
> case). The benefit is your source files will keep all the other formatting 
> and comments and spacing and and ordering and whatever else.
> 
> This is how I'd go about this.
> I think it would even be possible to template this and provide helper 
> functions.

Ok, I understand what you're suggesting, but I am not really sure if that is 
the way to go. For an easy case, such as replacing payees it is ok, but I think 
for more complex tasks, like adding new meta data fields, changing accounts, or 
even splitting transactions between accounts a search-and-replace approach will 
evolve into just rewriting the entire transactions in the source file from the 
Transaction object.

Right now, I think reading in the beancount file into a string, parse them 
using bc.parser.parser.parse_many and perform the transformations is the best 
way for me. Then, rewrite the entire file using bc.parser.printer.print_entries.

You wrote:

> Still, when you write entries out, they won't look precisely the same as the 
> input. Numbers will have been filled in, cost bases will show up, etc. I 
> don't see the point.

Given my very simple transactions, e.g.,

2018-05-20 * "KREDITKARTENABRECHNUNG" "18.05.18 1234"
  buchungsart: "Lastschr.Kreditkarten"
  empfaenger: " / "
  hash: "e4a580e7002e606a4314f864f64f30a12fb8673f"
  Assets:Giro       -115.00 EUR
  Expenses:Unknown             

Rewriting the ledger file, as I mentioned above, does not change an entry like 
that.

As I just use simple beancount syntax, but potentially want to use more, do you 
consider that kind of rewriting a problem?

In the long run, I think a rewriting protocol would make a beneficial addition 
to beancount, as Stefano also suggested.

Maybe something like the importer protocol:

class MyRewriter:

  def rewriteTransaction(self, txn):
    return txn

  def rewriteBAlance(self, bal):
    return bal

or alike, one function for all types. Then you invoke bean-rewrite on a file or 
a set of transactions. Just a first idea...  ;-)

Best Regards,
Florian


> On Wed, May 15, 2019 at 4:12 AM Florian Lindner <mailingli...@xgm.de 
> <mailto:mailingli...@xgm.de>> wrote:
> 
>     Hi,
> 
>     Am 15.05.19 um 02:57 schrieb Martin Blais:> But why are you trying to do 
> this? What's your purpose?
>     My importer applies a set of rules to convert payee names and assign 
> certain kind of transactions to accounts:
> 
>     # List of tuples (regular expression, replacement)
>     payee_replacements = [
>         ("^AMAZON", "Amazon"),
>     ]
> 
>     # List of tuples (python expression to match, second account to set)
>     accounts_assignments = [
>         ("desc == 'Miete PSW 1'", "Expenses:Miete"),
>         ("payee in ['REWE', 'Kaufland', 'ALDI']", "Expenses:Groceries"),
>         ("True", "Expenses:Unknown")
>     ]
> 
> 
>     def transform_txn(txn):
>         payee = txn.payee
> 
>         for pattern, substitute in payee_replacements:
>             if re.match(pattern, payee):
>                 payee = substitute
>                 break
> 
>         txn = txn._replace(payee = payee)
> 
>         local_vars = {"payee" : txn.payee, "desc" : txn.narration, 
> "buchungsart" : txn.meta["buchungsart"]}   
>         if txn.postings[1].account == "Expenses:Unknown":
>             for expr, acc in accounts_assignments:
>                 if eval(expr, local_vars):
>                     account = acc
>                     break
> 
>             txn.postings[1] = txn.postings[1]._replace(account = account)
>            
>         return txn
> 
> 
>     These two rulesets are applied on import.
> 
>     I want to also apply them on existing ledgers.
> 
>     Usecase: I identify a recurring transaction pattern, such as "buchungsart 
> == 'GAA,Spk.Netz'. All matching transaction to imported as well as existing 
> ones should have the account "Assets:Bargeld" assigned. For that, I need a 
> method to read in all transactions, transform them and write them to a 
> beancount file.
> 
>     This is my solution to this question: 
> https://groups.google.com/forum/#!topic/beancount/e93VI4s4YCQ
> 
>     An alternative approach are plugins. So far I understand plugins they 
> only apply live transformations, i.e., they transform data as it is loaded 
> from a file, but do not write back the data to the file.
> 
>     >> A workaround I see, is to read in main.beancount and write out the 
> entries to different files based on entries[6].meta["filename"]. Basically 
> rewriting the entire ledger.
>     >
>     > I was going to suggest this.
>     > Still, when you write entries out, they won't look precisely the same 
> as the input. Numbers will have been filled in, cost bases will show up, etc. 
> I don't see the point.
>     Yes, I have noticed that, but that seems ok to me.
> 
>     I hope I was able to explain my use case. I am open to any thoughts and 
> ideas to achieve that differently.
> 
>     Best Regards,
>     Florian
> 
>     >
>     >
>     >
>     > On Mon, May 13, 2019 at 10:36 AM Florian Lindner <mailingli...@xgm.de 
> <mailto:mailingli...@xgm.de> <mailto:mailingli...@xgm.de 
> <mailto:mailingli...@xgm.de>>> wrote:
>     >
>     >         I see.
>     >         Well FWIW, entries which have errors are not guaranteed to show 
> up in the output stream at all.
>     >         It's unclear to me whether this is always the best outcome, but 
> a long while ago I decided to do this for transactions and for some other 
> directives.
>     >         
> https://bitbucket.org/blais/beancount/src/d1b2cbf2841669e988f6692ec1d39db3708730cc/beancount/ops/balance.py#lines-119
>     >
>     >         I don't have a solution for you. This is an unusual case.
>     >
>     >
>     >     I tried to apply the workaround I mentioned:
>     >
>     >         entries, error, option_map = bc.loader.load_file(args.inputfile)
>     >         sorted_entries = {} # file -> list of entries
>     >
>     >         for e in entries:
>     >             entry = transform_txn(e) if type(e) == data.Transaction 
> else e
>     >             name = entry.meta["filename"]
>     >             sorted_entries[name] = sorted_entries.get(name, []) + 
> [entry]
>     >      
>     >         for filename in sorted_entries:
>     >             with open(filename, "w") as f:
>     >                 
> bc.parser.printer.print_entries(sorted_entries[filename], file = f)
>     >
>     >     A problem that shows up, is that in main.beancount I have some 
> options set (e.g. operation_currency). They don't show up in entries, but in 
> option_map. However, I don't know how to write them to file.
>     >
>     >     Another idea: At a first try, it seems that reading the entire file 
> into a string and use |beancount.parser.parser.||parse_many|would work and 
> also parser the balances:
>     >
>     >         with open(args.inputfile, "r") as f:
>     >             instr = f.read()
>     >       
>     >         entries = bc.parser.parser.parse_many(instr)
>     >
>     >     Seems to work fine so far. What do you think?
> 
>     -- 
>     You received this message because you are subscribed to the Google Groups 
> "Beancount" group.
>     To unsubscribe from this group and stop receiving emails from it, send an 
> email to beancount+unsubscr...@googlegroups.com 
> <mailto:beancount+unsubscr...@googlegroups.com>.
>     To post to this group, send email to beancount@googlegroups.com 
> <mailto:beancount@googlegroups.com>.
>     To view this discussion on the web visit 
> https://groups.google.com/d/msgid/beancount/a404f3ac-e9db-4470-b15c-4ee2bc611525%40googlegroups.com
>  
> <https://groups.google.com/d/msgid/beancount/a404f3ac-e9db-4470-b15c-4ee2bc611525%40googlegroups.com?utm_medium=email&utm_source=footer>.
>     For more options, visit https://groups.google.com/d/optout.
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "Beancount" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to beancount+unsubscr...@googlegroups.com 
> <mailto:beancount+unsubscr...@googlegroups.com>.
> To post to this group, send email to beancount@googlegroups.com 
> <mailto:beancount@googlegroups.com>.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/beancount/CAK21%2BhMvPQ7_8hY98oi1mFXvcJ4X0yishrUNiSVeV76Cq1d_kA%40mail.gmail.com
>  
> <https://groups.google.com/d/msgid/beancount/CAK21%2BhMvPQ7_8hY98oi1mFXvcJ4X0yishrUNiSVeV76Cq1d_kA%40mail.gmail.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"Beancount" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to beancount+unsubscr...@googlegroups.com.
To post to this group, send email to beancount@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/beancount/525d347f-6608-4690-8f60-3ca4a5fa9bc4%40xgm.de.
For more options, visit https://groups.google.com/d/optout.

Reply via email to