Date: Mon, 19 Oct 1998 11:08:53 +0200
From: Stephan Lichtenauer <s_lichtenauer@muenchen.org>
To: linas@linas.org
Subject: [Fwd: ANNOUNCE version 1.1.20]

DESIGN PROPOSAL (ROUGH OUTLINE)
-----------------------------

I thought that there is only one engine that manages runtime data, but
to store and read it it uses the i/o-plugins that handle the data in a very
abstract way, ie they know only as much about it as absolutely necessary so
changes of data representation only affects the engine and not the plugins (in
most cases). Nevertheless i would say that they are backend since they do not
need an ui. Necessary addresses/file names can be obtained in a standardized way
through the application. It could work with the CORBA persistence framework if I
remember right and the GNOME VFS.

Engine
--------

Split the existing engine in the following classes that match the existing data
structures:

- GcAccountGroup
- GcAccount
- GcSplit
- GcTransaction
- GcEngine

These five classes first of all simply use the design already used in the
engine. Additionally I would introduce a class

- GcRegistry

that is used to store some metadata that is used by e.g. plug ins or the user
interface. Since there is in my eyes need for two different classes of metadata,
global and account-group related one, I would give GcAccountGroup as well as
GcEngine a gcGetRegistry method that returns the local and the global registry
object. The registry can store its data in the account database and (global
data) in the config file in the user's root directory. An example for global
metadata of my plugin would be a database of all REUTERS codes of stocks
available, in the local registry (the account-group related one) the plug in can
save e.g. what codes have been selected to be updated etc. An ui could store
general options (e.g. user preferences) in the global registry and e.g. last
position of windows in the local registry.
GcEngine could as well be a metaclass since it only has to represent the engine
as such, with methods like gcReadAccountGroup.
GcSplit could be an abstract class whose functionality can be implemented
in derived concrete classes, e.g. for simple money transfers, buy of securities
(which will be further divided in stocks, futures, bonds) etc. Alternatively
additional
data could be stored in an extended comment field with every split in MIME format.
Infrastructure for that is already partially implemented but is (in my eyes) no
perfectly
clear OOP design.

One GcSplit subclass is GcComment that can store general data like company
statements. Since this could be data that affects more than one account (e.g.
general economical data), it is stored centralized and the GcComment object in
the different accounts only point on this data. GcSplit subclasses that
represent any type of securities will have to have additional fields, e.g. to
store maturities, volume (for special price quotes) etc.

In the design outline on the webpage a transparent mechanism to read data from
different sources is proposed. I would realize this completely with plug in
objects. So I would introduce

- GcPlugIn

to be the base class of all plug ins. How to make plugins independent from ui
toolkits (GTK, QT/KDE), I do not know since they will need an ui in many cases.

- GcIOPlugIn

is derived from it and will be the base class for all i/o plugins, e.g.
GcIOXacc, GcIOQif, GcIOSql, GcIOOnlineBanking, GcIOReadWww (yeah, my plugin!)
etc. Since some of the media is non-persistent, ie it is not sure if you will
get the data again in the future (e.g. from web pages), GcIOPlugIn has a method
gcIsPersistentMedia that returns FALSE in such cases. Then the data obtained
from this source can be copied to a persistent media (e.g. the SQL database).
An IO plugin has a limited lifespan and is only used to read/write data from/to
accounts/account groups/splits resp. create these objects as appropriate when
reading data.

One example:
You make a bank transfer in your account. The data is written to the
GcIOOnlineBanking object that uses the WWW-interface of your bank to give the
data to your bank. Then it reads the state of the transfer (via an online
request to your bank account balance) what will then appear in your account.
Possibly the GcIOOnlineBanking plugin is not persistent, i.e. you will not get a
full track of all of your transactions of the past via online banking, then the
data is stored locally (e.g. via GcIOPlugInXacc).

One account group so can use many IO plugins at the same to get its data.
Perhaps the IO plugins could be based on the GNOME VFS (virtual file system), if
this is not an unwanted dependency.

In this system of course there is one big problem: How to map the data obtained
from a source to the accounts. If you read it from your account group file of
course you have account ids and account name, but if you read from www stock
quotes or MetaStock data or online banking interfaces that is not that simple;
and it also has to work the other way round. So we need a

- GcDataMapper

class that has at least two methods gcFindAccounts and gcFindSources. Both get a

- GcMapInfo

struct as parameter that contains information on how to find the account or the
source. Both return a vector of GcMapInfos that contain information about
the matches. When you call gcFindAccounts you normally fill the fields with data
like the banking account number, REUTERS codes etc. and the method returns a
list of accounts that could match. If there is more or less then one account the
user could be prompted to help; the data obtained from a succesful match will be
stored in the registry by the GcDataMapper so it can be used in the future and
to do reverse mapping. Reverse mapping is what gcFindSources is for. There you
fill the GcMapInfo with the things like accountId, account name, comments etc.
and the GcDataMapper tries to find (with the help of its registry if there is
already some data available) some sources (e.g. quotes web pages, online banking
interfaces, company web pages etc.) and the IO plugins for this account. Again
user help could be involved the first time or later again if the user wants to
modify the matches. How to actually do the mapping is job of the GcDataMapper,
it could use regexp libraries etc. The most simple implementation would to be a
simple user query where the user has to find the matches.

Example:
When I have an account called "Silicon Graphics stocks" and I want to obtain
stock quotes from the web I have to find a web page where to get them from. When
I have a plug in for that I could deliver it with a database containing some
addresses for stock quotes for REUTERS-code "SGI" (or in Germany we have the
very easy-to-remember six-digit numbers, eg 872981 for SGI), this database will
be appended to the data mapper database. But now we have to find out that "SGI"
is what we are searching for, this is the mapper doing with eg regexp. He now
finds "SGI" beside some other sources, now the user could select the one(s) he
wants. The mapper stores the selection so he can do this automatically in the
future and also in reverse. If the quotes plugin has a cron-mechanism to update
some selected quotes every 1.5 seconds, the data mapper knows where the "SGI"
data goes to now. The same with online banking: map the accountId/-name on the
bank code/account number etc. if I have a remittance, and get accountId/-name
when there comes in a bank statement. So the mapper is a kind of "dynamic
linking" mechnism (with memory, so a little bit static, too, yes) to allow the
engine to search and parametrize the necessary plugins and to find out what to
do with the data they deliver.

A second type would be the

- GcReportPlugIn

that is used for report generation/data analyzing. A report interface that
generates HTML code (that can then be translated to SGML, PS, TeX etc. via an
additional tool) is proposed on the developers web page. So I would propose an
ui that is an HTML designer, very much like a wordprocessor or the Mathematica
frontend, and include automatically generated reports (tables for account
balances, graphs etc) with a reserved HTML keyword that also allows you to
specify some layout parameters like size and position. When the document finally
is generated, a parser calls for every of these keywords the appropriate report
plugin and replaces it with the output of the plugin (that has to be pure HTML
code with embedded images etc.). Chaining of report plug ins could be helpful,
e.g. first filter stock quotes from noise with a plugin before they are used in
a technical analysis tool.

Finding and linking the plugins to the engine could esaily be done via the CORBA
repository.

Report plugins first of all have to have a kind of
string gcMakeReport(string params);
method that gets the parameters stored in the HTML command (e.g. accoundId, data
range, graph type etc.), this has to be obtained through dialogs that are
maintained by the plugin itself, this is why I have said I do not know how to
make plugins toolkit-independent) and returns the generated HTML-code. They
have to have a second method to display this dialog and a
general plug-in mechanism that allows to find and load GnuCash corba plugins;
this of course is already introduced in the GcPlugIn class.
If plugins are chainable the gcMakeReport has to be modified/extended that they
get can get an account(-group) as input and return a new, modified
account(-group) that could be then input for a second plugin (eg the one that
finally creates the HTML-code). A usage for that could be a filter that eliminates
noise in stock quotes and so generates new quotes that are then used as input to a
Chaikin
Oscillator plugin.


I hope it is in line with all the other proposals (e.g. scripting, budget engine
etc).
I did not mention script languages in this document, I think it should be
possible to access the engine via CORBA bindings for all these languages and to
even create new classes or derive them, so writing plugins should be easily
possible with compiled languages as well as with interpreted ones.


Stephan Lichtenauer
Rassosiedlung 25
82284 Grafrath
s_lichtenauer@muenchen.org


