Nb204 Anonymous Procedures in a TinyDB Monthly Expense Category Tracker

This thread will demonstrate the new nb204 anonymous procedure blocks in a sample expense tracker app. It will also show

  • how to store data with compound keys in TinyDB
  • how to offer custom filtering of data
  • how to offer custom grouping and summarization of data

Sample run:
Sample run

The source, for those who just want the app:

monthly_category_totals.aia (13.7 KB)

Details and code to come in subsequent posts in this thread ...

Data storage and entry:

All data is kept in TinyDB, one tag per monthly expense entry.

The top of the app display is for data entry:

The Designer components:

  • a List Picker (lpk) for choosing Categories of expenses, and optionally adding new categories
  • a Text Box (txb) for entry of the year
  • a List Picker (lpk) for entry of the Month (01-12)
  • a numbers-only Text Box (txb) for the expense amount to be added to the current amount in that category and time frame
  • an Enter Button (btn) to trigger the retrieval and update

Screen Initialization:

The Enter button is disabled until a category has been chosen.
Default current year and month are loaded, for convenience.
A procedure is called to refresh the data summaries (discussed later.)

The TinyDB tag structure:

I separate the parts of the TinyDB tag with '|' to make it easy to extract them later:




The Enter button builds a tag for TinyDB, and adds the amount to the existing amount:


The SelectionIndex > 0 check is necessary to prevent adding "Category?" as a category in the data base.

Choosing Categories:

For flexibility, category options are chosen from the data base, with the option of selecting "[+]" to add a new expense category.

initialize global CREATE to

We need a distinct function to extract only distinct values from a list:


(This is reusable across projects.)

We also need a procedure to return only the TinyDB tags that are entries in our little data base:

(In this app, we use only one Namespace, and no other compound TinyDB tags, so this should suffice to return only the TinyDB tags for expenses. If you are testing in the Companion, you may need to change the TinyDB NameSpace to avoid other apps' data.)

Adding new Categories during data entry:
I use the "[+]" selection for adding new data, through a special purpose Notifier:
initialize global CREATE to

When we receive a new category value, we add it to the list of distinct existing categories from our TinyDB tags, and set it as our selected category.

Selecting a new Month is simple, since they have been pre-loaded in the Designer:

Next post: Filtering data.

Filters are kept as a dictionary, with column names ("Category", "Year", "Month", "Year.Month") as keys. The value under each key is the list of acceptable filter values for that column. If the disctionary is empty, everything is acceptable. If there are multiple column filters, each tag must satisfy all filters.

Designer:

Filters are presented in a ListView lvwFilters.
They can be added and removed (one filter column and value at a time) using two List Pickers and a Button.

Sample filter choices:


Here we are asking to see only the heat expenses for the years 2024 and 2026. (I did not add any data for 2025, so it does not appear as a choice.)

The nb204 Anonymous Procedures, at work:
This is where the column names meet the code to extract their values from TinyDB tags:

This dictionary is where the descriptive text names of the columns meet the code to extract that column value from a tag.

This code would have had to been coded as an if/then/elseif ladder before nb204.
Now the keys of this dictionary are available to be loaded into List Picker Elements to help the user configure his filters.

This is the filter dictionary we will be loading up with filters:
initialize global filtersDictionary to

Using the new nb204 anonymous procedure blocks, we can look up and invoke the procedure to extract any requested column from a tag:


This procedure takes a column name ("Year",...) and a TinyDB tag and looks up the code to extract that column from the tag, then invokes it to return that column value in that tag.

After a filter column is chosen, we use that column choice to extract all distinct values of that column from our TinyDB tags, and present them in the adjacent List Picker:

When a filter column value is chosen, we stage it for the add/remove button:

The Button to add and remove filter values needs both List Picker Selections, and reverses what it finds for this combination of column and value in the filters dictionary.

When the last filter value for a column is removed, that column name is removed from the filter dictionary.

The filter dictionary by itself is not enough.
We need a procedure to walk the list of TinyDB tags and return only the ones that pass all filters in the dictionary:


This procedures examines each tag, and applies each test in the filterDictionary, applying an Is In List test for that tag's column values against acceptable filter values.

The reduce list block and the Make New Filtered List block are from nb191's advanced list blocks collection.

Here's the procedure to refresh the summaries so far:

Summary List Picker handling in next post:

For debugging and data correction, here are two List Pickers:



This List Picker is just to see our filter results in more detail.

Next post: grouping

The grouping setup controls are after the filter controls in the display:


The grouping is done from a list of column names.
The List Picker chooses a column name to add or remove from the list of column names.
The ListView shows the grouping order, initially empty.
An empty grouping order provides a detailed list in the Group Summaries ListView.

Sample run: filtering by year, and grouping by category:

Sample run: grouping by category and year combination:


Selecting a Column for grouping just toggles its entry in the Elements list of the ListView lvwGrouByColumns.

The group summarization is done in a dictionary, with each key derived from applying the group column names to each tag, and the key value is kept as the running sum of the matching tags' TinyDB values.

initialize global groupSummariesDict to

Assigning a group key to a tag:

The procedure to fill the group summaries:


This procedure uses the nb191 advanced list blocks mentioned in an earlier post.

That completes the blocks walkthru for this sample app.

Next post: Afterthoughts on Anonymous procedures

Afterthoughts:

@Taifun asks

This procedure

would have to be replaced with

I would still need a list of column names as in the keys of

to load some List Picker elements lists, but now I would need to make sure there are no typoes between the two collections of column names.

This is my first use of anonymous procedures.

I imagine them useful for apps like calculators and interpreters, where you have to map operation names '+', '-',... into code to apply those operations.

I open this thread for further discussion.

1 Like