1. Novice Level Project - simple, fun and useful

Greetings All,

After a few years in and out of the hospital, I decided I had better get a better handle on my finances. So I wrote a very simple little program I call "Budget Buddy" using the Euphoria Database System (EDS).

I created has two main tables - register, and cash. I record my income as deposits, expenditures and cash withdrawals as debits in/from the register table. The register table has only three fields: a datetime date is the key for each for each entry, The second field is a transaction note, and the last field is the transaction amount. Debits are entered and maintained internally as negative numbers, but in table listings they are reported without a sign and color coded red. Deposits are color coded blue.

Cash debits from the register table are "deposited" into the cash table which has the same structure and operation as the register table.

Transaction notes which exceed the allotted space are truncated with an asterisk color coded yellow.

In the table listings the current balance is calculated on the fly.

                                   register                                    
 
#     Day   Transaction                          Amount           BALANCE 
----------------------------------------------------------------------------------- 
44    8-17  Debit cash                           $300.00          $4,141.40 
---- 
74    9-21  Accordia PA Amanda referring me to ~ $60.00           $1,391.60 
75    9-21  Walmart groceries                    $93.87           $1,297.73 
----------------------------------------------------------------------------------- 
A menu selection calls a routine which displays the full record:
Display full note of record #: 74 
 
74    9-21    Accordia PA Amanda referring me to Cardiologist.    
              Suggested a Urologist in Dublin. Next appointment   
              12-21-20 walk in.                                   $60.00 

                                     CASH                                      
 
#     Day   Transaction                          Amount           BALANCE 
----------------------------------------------------------------------------------- 
1     7-6   Cash on hand                         $233.21          $233.21 
2     7-6   groceries                            $40.00           $193.21 
3     7-13  ALLCARE ADDERALL MEDICAL             $14.40           $178.81 
 

Reports of expenditures can be generated using search terms from the transaction notes:

                        <keyword: GROCERIES> Year: 2020 Month: 8                         
-----------------------------------------------------------------------------------                                                     *****register*****                                       
 
33    8-3     groceries Walmart                                   $98.82 
34    8-3     groceries meat Thriftway                            $266.19 
 
                                                            total $365.01 
-----------------------------------------------------------------------------------                                       *****CASH*****                                         
 
19    8-17    groceries Walmart                                   $40.00 
20    8-17    groceries Thriftway                                 $22.00 
 
                                                            total $62.00 
----------------------------------------------------------------------------------- 
 
                                                            total $427.01 
I wrote several menu selected routines to edit the record data:
#:79    DATE:   2020-9-26-4-10-13 
 
        1   Transaction Note:   autolog DEPOSIT interest paid 
        2    $  2.48 
 
        Enter # of data element to edit, Enter 0 to abort: 1 
 
        Transaction Note:  autolog DEPOSIT interest paid  
 
    1. Append  Keyword to transaction note 
    2. Replace Keyword in transaction note 
    3. Remove  Keyword in transaction note 
    4. Replace entire transaction note  

I also added a budget table which contains my income and budget category data. Each time the program is executed a file called AUTOLOG.DAT which contains the month number of the last budget autolog. If the AUTOLOG.DAT month is not the current month, the database is backed up and then the budget table data is automatically logged into the register table. The AUTOLOG.DAT file is then updated. I added a prompt with an opt-out option for good measure. The budget data can also be autologged from a menu selection.

Regards, Ken

new topic     » topic index » view message » categorize

2. Re: Novice Level Project - simple, fun and useful

Sorry to hear about the hospitalizations. Hope you're better now!

That seems like a useful program, care to share?

new topic     » goto parent     » topic index » view message » categorize

3. Re: Novice Level Project - simple, fun and useful

Hi

Are you recording values as whole numbers, or as decimals? It won't matter so much when you are only doing addition and subtraction, but if it ever does tax or percentage calculations, then it can lead to rounding errors - I made this mistake in my veterinary practice management program, and had to a lot of little 'kludges' later ro get the right tax calculations.

Nice project.

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

4. Re: Novice Level Project - simple, fun and useful

irv said...

Sorry to hear about the hospitalizations. Hope you're better now!

That seems like a useful program, care to share?

Thanks, Irv - I'm doing fairly well health wise.

I will try to clean-up my code a bit and post something today or tomorrow.

Regards, Ken

new topic     » goto parent     » topic index » view message » categorize

5. Re: Novice Level Project - simple, fun and useful

ChrisB said...

Are you recording values as whole numbers, or as decimals? It won't matter so much when you are only doing addition and subtraction, but if it ever does tax or percentage calculations, then it can lead to rounding errors - I made this mistake in my veterinary practice management program, and had to a lot of little 'kludges' later ro get the right tax calculations.

Is tracking money values as integers of pennies usually the best approach then?

-Greg

new topic     » goto parent     » topic index » view message » categorize

6. Re: Novice Level Project - simple, fun and useful

ChrisB said...

Hi

Are you recording values as whole numbers, or as decimals? It won't matter so much when you are only doing addition and subtraction, but if it ever does tax or percentage calculations, then it can lead to rounding errors - I made this mistake in my veterinary practice management program, and had to a lot of little 'kludges' later ro get the right tax calculations.

Nice project.

Cheers

Chris

Thanks.

Values are entered/recorded/stored as atoms. Negative values,debits, are initially entered as positive numbers then multiplied by -1 to produce a negative number which is then stored. When viewing a negative value which is to be edited, it is displayed with its sign and the new negative value is entered including the sign.

I enter values with two decimal places c.f. 25.00. I have noticed when editing such a value is displayed as 25 with no decimal point; however, 25.25 displays for editing as 25.25.

Any suggestions for improvement will be appreciated.

Thanks, Ken

new topic     » goto parent     » topic index » view message » categorize

7. Re: Novice Level Project - simple, fun and useful

Senator said...

Any suggestions for improvement will be appreciated.

Thanks, Ken

String math. I believe there's libs in the archives, for multiple numeric bases too. Solves the whole human-decimal to computer-binary issue.

Kat

new topic     » goto parent     » topic index » view message » categorize

8. Re: Novice Level Project - simple, fun and useful

ghaberek said...
ChrisB said...

Are you recording values as whole numbers, or as decimals? It won't matter so much when you are only doing addition and subtraction, but if it ever does tax or percentage calculations, then it can lead to rounding errors - I made this mistake in my veterinary practice management program, and had to a lot of little 'kludges' later ro get the right tax calculations.

Is tracking money values as integers of pennies usually the best approach then?

-Greg

This is what I do for currency values in my office programs. Conversion is only done for displaying the value or saving to file. OTOH for currency rates I use doubles.

Some discussion here

Spock

new topic     » goto parent     » topic index » view message » categorize

9. Re: Novice Level Project - simple, fun and useful

Hi

Yes, exactly. You'll be ok mostly for addition and subtraction, but once you start doing splits and percentages, then you'll start to see 'not quite right' errors creeping in.

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

10. Re: Novice Level Project - simple, fun and useful

Thanks for all the feedback.

I was just about to post my code... but first let me see if I have this right...

I obtain the value as an atom/decimal number say, 100.36 then
multiply it by 100, converting it to 10036 an integer, and store
that value in the data base.

When I display that value I simply divide it by 100
and everything is copasetic, right?

I already have 81 records in the database now.
It shouldn't be hard to loop through those records converting the stored value to integers/pennies.

Regards, Ken

new topic     » goto parent     » topic index » view message » categorize

11. Re: Novice Level Project - simple, fun and useful

You may find it helpful to display the numbers using something like:

display("$ [:8,,.2]",n/100) 

This will allow columns to line up neatly, and add commas, e.g: $ 1,234.56

new topic     » goto parent     » topic index » view message » categorize

12. Re: Novice Level Project - simple, fun and useful

irv said...
display("$ [:8,,.2]",n/100) 

Ugh, display(), ugh. Sorry, but there is no limit to the amount of scorn and vitriol I could pour on that sad excuse of a routine and it's illegitimate sibling, text_format(), pttth.
No doubt it has it's fans here, but if you don't believe me, just look for yourself at the docs or better yet the ridiculous and revolting source code, especially the latter, like some kind of weird regex on acid.
It is not and I have no plans to ever make it compatible with Phix, however the following dose of sanity will give exactly the same results (in Phix, but sadly not Eu):

printf(1,"$ %,8.2f\n",n/100) 

In fairness, for comparison, the latter is not exactly pretty either, but at least there is only one of it and it is far less than 1/4 the size, plus the above actually entirely depends upon a non-amendable (C) version of that anyway.
(The only thing Eu does not have is the comma handling, which is only like six lines of code anyway.)

PS OK, technically, display() runs fine (if that's a valid word here) on Phix, but I certainly ain't supporting or fixing or documenting it!

new topic     » goto parent     » topic index » view message » categorize

13. Re: Novice Level Project - simple, fun and useful

petelomax said...

Ugh, display(), ugh. Sorry, but there is no limit to the amount of scorn and vitriol I could pour on that sad excuse of a routine and it's illegitimate sibling, text_format(), pttth.

Agreed. I took one look at the documentation for display() and then immediately ignored it. No. Thank. You.

I don't really like Python's format() either. I guess .NET's String.Format() is alright.

Unfortunately this is a hard problem to solve, and I think we dropped the ball on that one.

-Greg

new topic     » goto parent     » topic index » view message » categorize

14. Re: Novice Level Project - simple, fun and useful

If it works, I use it.

Display is part of a library file (std/console.e) so you can post your neater, cleaner version any time!

new topic     » goto parent     » topic index » view message » categorize

15. Re: Novice Level Project - simple, fun and useful

irv said...

Display is part of a library file (std/console.e) so you can post your neater, cleaner version any time!

I thought I just did, it's called printf()!

I can post the phix comma-insertion code if you like, and/or a phix-compatible version of sprintf() in javascript that I recently wrote.

new topic     » goto parent     » topic index » view message » categorize

16. Re: Novice Level Project - simple, fun and useful

Coincidentally (while on a completely unrelated code-fragment-formatting-issue-mini-trawl) I just ran into the bankers rounding section of the phix docs, and was obviously reminded of this thread, and ended up completely rewriting both the previously frankly rubbish code and the previously rather dour and depressing documentation:

integer pennies = bankers_rounding(atom pence, integer precison=1)

In the standard round() function, exact .5s are rounded up (matching Euphoria) whereas banker's rounding yields the nearest even number, eg:

constant x1 = {-4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5} 
?apply(x1,round) 
?apply(x1,bankers_rounding) 
function precision100(atom xi) return bankers_rounding(xi*100,100) end function 
?apply(x1,precision100) 
-- output: 
-- {-4,-3,-2,-1,0,1,2,3,4,5} 
-- {-4,-4,-2,-2,0,0,2,2,4,4} 
-- {-400,-400,-200,-200,0,0,200,200,400,400} 

The reason you might want to use banker's rounding is so that the average of the rounded numbers is as close as possible to the average of the original numbers (ditto total), whereas always rounding up obviously introduces a systemic bias.
Above, the standard rounding average is out by 0.1, whereas the average x1 and the average bankers are both exactly zero (not that it would be difficult to construct a deliberately biased x1 such that the opposite were true; you only get better outputs for evenly-dispersed/unbiased inputs).

Note that this is only possible when working in whole (integer) pennies/cents (and above), not pounds/dollars to 2dp, and the precision is not inverted (see Technicalia). Should you want to store things in 2dp format, that's fine as long as you round(*100) to get exact whole integer penny/cent values back out when you retrieve them, that is prior to attempting any subsequent calculations that are likely to end up requiring any further banker's rounding, but of course you don't need to do that if all you're going to do is print or add and subtract them. [It should make no difference whatsoever if you use round() or bankers_rounding() to retrieve the whole integer penny/cent values.]

Technicalia:
Since eg 0.045 cannot be held exactly in an IEEE 754 floating point, it would be rather difficult to coerce it to (almost) 0.04 rather than (almost) 0.05 - it would instead naturally fall depending on whether the nearest admissible representation was out by +/-epsilon. An abs(frac-0.5)<1e-13 or similar rather than frac=0.5 (see pmaths.e below) might work, but could introduce another (small) bias of the very same kind that we are trying to eliminate. Best to just stick with integers. There is of course nothing to stop you taking a copy of bankers_rounding() and creating a (renamed) application specific version of it, tweaked however you like.

Likewise if bankers_rounding() had an inverted precision, inaccuracies in the representation of(eg) 0.01 could totally spanner it, hence it has an non-inverted (and integer) precision.
The integer return type of bankers_rounding is quite, quite, deliberate.

And the (new) code is just:

global function bankers_rounding(atom pence, integer precision=1) 
    integer pennies, -- (or nearest 100, etc, but never nearest < 1 ) 
            s = sign(pence), whole 
    pence = abs(pence)/precision 
    whole = floor(pence) 
    atom fract = pence-whole 
    if fract=0.5 then 
        pennies = whole+and_bits(whole,1) 
    else 
        pennies = floor(0.5+pence) 
    end if 
    pennies *= s*precision 
    return pennies 
end function 
new topic     » goto parent     » topic index » view message » categorize

17. Re: Novice Level Project - simple, fun and useful

Thanks for all the suggestions. I have tried to convert to integer storage and display of floating point data using Pete's bankers_rounding function.

Unfortunately I discovered a problem with corrupted line warnings escalating to a crash in display of my main table, register, data. I do not think the problem is related to my converting all my monetary data storage to integers. I just didn't notice the debug.log until I started gathering the program files to post here.

And things were going so well. Here is some excerpts from debug.logs and db_dumps. Any suggestions will be appreciated.

Running program from WEE editor: 
 
listing of the main table register appears normal up to the final record: 
97    10-31 autolog SC Balance Requirement Fee     (bal not shown) 
 
At which point the program crashed:  
 
Writing profile results to ex.pro ...  ex.pro is an empty file 
 
Fatal run-time error: 
A machine-level exception occurred during execution of this statement (signal -17 
 
/tmp/wee_run_196511.sh: line 3: 24388 Segmentation fault      (core dumped) /home/ken/euphoria-4.1.0-Linux-x64/bin/eui -batch /home/ken/euprogs/Budget_Builder/register.ex 
Press any key... 
 
debug.log list numerous line corruption errors  
 
smaller table, cash, does not crash - displays as normal but a debug.log is generated 
with numerous line corrupted errors 
 
 
Running interpreter from terminal - just displaying the menu,  
generates a debug.log... 
 
/home/ken/euprogs/Budget_Builder/debug.log:1 
<0117>:: Not expecting to see ''('' here 
line corrupted (26), s is 23, col is 0 
 
The table listing and menu displays as normal,  
but selecting the cash table 
again generates more debug.log data 
**starting with record # 23** which is 
the same rec # where similar debug.log 
warnings started being reported for the 
register table. 
 
captured from screen 
23    9-5   Tabatha Kitchens household           $55.00           $66.00 
24    9-8   McDonalds breakfast fast-food        $8.00            $58.00 
25    9-30  Lowes Wasp Hornet Spray              $13.00           $45.00 
 
cash table listing debug.log: 
line corrupted (26), s is 23, col is 0 
line corrupted (26), s is 9-5, col is 6 
line corrupted (26), s is Tabatha Kitchens household, col i 
line corrupted (26), s is $55.00, col is 49 
line corrupted (26), s is $66.00, col is 66 
line corrupted (26), s is , col is 72 
line corrupted (27), s is 24, col is 0 
line corrupted (27), s is 9-8, col is 6 
line corrupted (27), s is McDonalds breakfast fast-food, co 
line corrupted (27), s is $8.00, col is 49 
line corrupted (27), s is $58.00, col is 66 
line corrupted (27), s is , col is 72 
line corrupted (28), s is 25, col is 0 
line corrupted (28), s is 9-30, col is 6 
line corrupted (28), s is Lowes Wasp Hornet Spray, col is 1 
line corrupted (28), s is $13.00, col is 49 
line corrupted (28), s is $45.00, col is 66 
 
               ^ 
I cannot tell from db_dump that register.edb actual data 
has been corrupted. Example from cash table: 
 
key: {2020,9,5,10,48,42} 
  data: { 
          "Tabatha Kitchens household", 
          -5485 
        } 
 
  key: {2020,9,8,10,54,2} 
  data: { 
          "McDonalds breakfast fast-food", 
          -800 
        } 
 
  key: {2020,9,30,18,7,18} 
  data: { 
          "Lowes Wasp Hornet Spray", 
          -1300 
        } 
 

Any suggestions will be appreciated.

Regards, Ken


Forked into: Budget Builder

new topic     » goto parent     » topic index » view message » categorize

18. Re: Novice Level Project - simple, fun and useful

Senator said...

Thanks for all the suggestions. I have tried to convert to integer storage and display of floating point data using Pete's bankers_rounding function.

Unfortunately I discovered a problem with corrupted line warnings escalating to a crash in display of my main table, register, data. I do not think the problem is related to my converting all my monetary data storage to integers. I just didn't notice the debug.log until I started gathering the program files to post here.

And things were going so well. Here is some excerpts from debug.logs and db_dumps. Any suggestions will be appreciated.

... ...

Regards, Ken

As much as possible I use CSV files for storing data. Each time the file is loaded the program verifies the syntax (occasionally some semantics too) of each field in each line. The file itself is human-readable so corrupt data can't really hide anywhere.

Spock

new topic     » goto parent     » topic index » view message » categorize

19. Re: Novice Level Project - simple, fun and useful

Senator said...

captured from screen 
23    9-5   Tabatha Kitchens household           $55.00           $66.00 
 
cash table listing debug.log: 
line corrupted (26), s is Tabatha Kitchens household, col i 
line corrupted (26), s is $55.00, col is 49 
line corrupted (26), s is $66.00, col is 66 
 
I cannot tell from db_dump that register.edb actual data 
has been corrupted. Example from cash table: 
 
key: {2020,9,5,10,48,42} 
  data: { 
          "Tabatha Kitchens household", 
          -5485 
        } 

That tells me you're mixing up dbdump output from different register.edb.
It's either $55.00 or $54.85, not both.

I suspect your register.edb is corrupt and needs to be deleted/rebuilt.
You may be able to recover some records from it, try db_compress or doing it record-by-record by hand.
Sadly, .edb files are not very reliable. I used to have terrible trouble with edita.edb,
but now I only have to trash it every 3 or 4 years (which is down to not crashing and therefore
always calling db_close() which flushes any and all updates from memory, but when inevitably it does
crash, by which I mean for something completely unrelated to database handling, I'll probably start
experiencing corruption issues a week or month later, at a guess).

Pete

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu