Using Kaptain for accessing database

Introduction

In this example, I show you some tricks about using Kaptain for accessing a PostgreSQL database.

Kaptain is a universal graphical front-end for command line programs, so one can use it with other database managers which support queries and data manipulation through command line.

Creating database

Let's hope you want to make a database for your personal budget. It is useful at the end of the month, you can see to where your money has disappeared. You need only one table for this, which can be created by the following sql statement:

CREATE TABLE budget (
  trans_date  DATE DEFAULT DATE('now'),
  description TEXT,
  quantity  INTEGER NOT NULL,
  currency CHAR(3) DEFAULT 'EUR' NOT NULL,
  destination VARCHAR(10)
);

The date and the description of a money transaction is stored together with the amount of money and the currency. I use the field destination to remind me where the money went od where it came from.

Now inserting rows into this table can be done with simply in sql:

INSERT INTO budget VALUES ('2001-03-31', ... );

PostgreSQL allows us to execute queries from the commandline like this

 $psql -c "INSERT INTO budget VALUES ('2001-03-31', ... );"

Though it's quite unfriendly this way, it's very useful. We can write a Kaptain grammar for this.

Grammar for inserting

For a detailed rewiev of creating dialogs, consult Kaptain handbook. Here I only reflect some interesting details. Consider the grammar I created for inserting money transactions into the database. It looks like this:

Pressing the OK button generates the command

 psql -c INSERT INTO budget VALUES(date('now'),'Buy a car',-10000,'HUF','Other');

In fact, the whole sql statement must be only one argument, so it cannot be easily done with bash. (Putting it into quotes and escaping some chars, it would be ugly). The solution is to call psql directly, not through bash with the command @exec:

  buttons :horizontal -> @exec("psql","-c",query)="OK" @close="Cancel";

Look at the 'Other' date field. It is declared in the grammar with the following rule:

 specified_date :beside "Other" ->
  "'"
  @regexp("^[0-9]+-[0-9]+-[0-9]+$")=x@date +%D | sed -e 's!\(..\)/\(..\)/\(..\)!20\3-\1-\2!'@
  "'";

Here, @regexp guarantees that the text in the line input field is in the form of numbers-numbers-numbers. Note that you can't use strict patterns for a form YYYY-MM-DD here because if the user's editing the field, he or she must delete some chars before inserting the correct ones. Make sure that the regexp is satisfied initially. Here I've done it by calling the date command and editing it's output

  date +%D | sed -e 's!\(..\)/\(..\)/\(..\)!20\3-\1-\2!'

This converts the date in the standard hungarian form: YYYY-MM-DD from the original MM-DD-YY given by program date.

Querying from database

Looking at money transactions can be done by the query

SELECT * FROM budget ORDER BY trans_date

The grammar to generate this is quite simple.

The only trick is transforming the sql table into a well-formed multicolumn listview: This is done by regular expression based substitution (new feature in 0.6). Look at the output of the query command:

$psql -c "select * from budget"
 trans_date |          description           | quantity | currency | destination
------------+--------------------------------+----------+----------+-------------
 2001-02-23 | OTP számla állása              |   ****** | HUF      | BANK
 2001-03-20 | Pénz felvétel bútorhoz, bt-hez |   ****** | HUF      | BANK
 2001-02-23 | Devíza alapállás               |   ****** | DEM      | HOME
 2001-03-20 | Bútor pénz apától              |   ****** | HUF      | Father
 2001-03-21 | Felvett pénz hazahozatala      |   ****** | HUF      | HOME
 2001-03-17 | OBI szekrény vásárlása         |   ****** | HUF      | BANK
 2001-03-24 | Kölcsön részlet vissza         |   ****** | HUF      | Mother
 ...

Quantity values were numbers, I just don't want to put them on the Internet. :) The rule

panel -> @multicol(m/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/,`psql -c "select * ..."`);

does exactly what we wanted: every line that matches a pattern (5 fields separated by vertical bar symbols) is divided according to the subexpression it matched (parts of the pattern in parentheses). Fortunately the second line in the command output, the one which separates the headers from the other rows contains + and - characters, no |'s, doesn't matches the pattern, so it is slightly ignored by kaptain.

Isn't it nice?