gnumed-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Gnumed-devel] Two patches


From: Ian Haywood
Subject: [Gnumed-devel] Two patches
Date: Mon, 24 Jan 2005 06:52:14 +1100
User-agent: Mozilla Thunderbird 0.9 (X11/20041124)

The first is on the database schema, but doesn't affect any client code.
        - minor changes to the demographics tables, I've deleted the tables 
mapbook and address_info,
I can't see them being used in the foreseeable future. Instead I've put 
address.location, street.location,
and urb.location, with a coalesce () in v_basic_address to get the most 
accurate location for any given address.
        - tables for expressing form fields. It's always tempting to try and 
let forms express their own
interface, but I've been relunctant as we could end up re-implementing OIO, (or 
even XUL.) This is an idea where
forms express at a very high level what they want, and the GUI is responsible 
for figuring out how to present it,
have a look at the examples in sql/country.specific/au/gmReference.sql

The second is on the client and quite extensive. gmDemographicRecord is 
re-written to be based around
gmBusinessDBObject, with all the knock-on changes that implies. The search 
routines in gmPatient now
bulk-load this object instead of fetching a list of IDs. The search results are 
now displayed in the
gmDemographics panel instead of a popup.

Ian
? server.patch
? bootstrap/Psql.pyc
? bootstrap/gmAuditSchemaGenerator.pyc
? bootstrap/gmNotificationSchemaGenerator.pyc
? bootstrap/gmScoringSchemaGenerator.pyc
? bootstrap/redo-public-au.log
? bootstrap/redo-public-core.log
? bootstrap/redo-public-de.log
? bootstrap/redo-public-test_data.log
? sql/diff
? sql/gmMeasurements2.sql
? sql/sql.diff
Index: bootstrap/bootstrap_gm_db_system.py
===================================================================
RCS file: 
/cvsroot/gnumed/gnumed/gnumed/server/bootstrap/bootstrap_gm_db_system.py,v
retrieving revision 1.2
diff -u -r1.2 bootstrap_gm_db_system.py
--- bootstrap/bootstrap_gm_db_system.py 12 Jan 2005 14:47:48 -0000      1.2
+++ bootstrap/bootstrap_gm_db_system.py 23 Jan 2005 19:45:42 -0000
@@ -728,13 +728,13 @@
                cursor = self.conn.cursor()
                try:
                        cursor.execute(cmd)
+                       self.conn.commit ()
+                       cursor.close ()
                except libpq.Warning, warning:
-                       _log.LogException(">>>[%s]<<< warning: %s" % (cmd, 
warning), sys.exc_info(), verbose=0)
+                       _log.Log(gmLog.lWarn, warning)
                except StandardError:
                        _log.LogException(">>>[%s]<<< failed" % cmd, 
sys.exc_info(), verbose=1)
                        return None
-               self.conn.commit()
-               cursor.close()
 
                if not self.__db_exists():
                        return None
Index: sql/gmDemographics-GIS-views.sql
===================================================================
RCS file: 
/cvsroot/gnumed/gnumed/gnumed/server/sql/gmDemographics-GIS-views.sql,v
retrieving revision 1.14
diff -u -r1.14 gmDemographics-GIS-views.sql
--- sql/gmDemographics-GIS-views.sql    20 Dec 2004 18:52:02 -0000      1.14
+++ sql/gmDemographics-GIS-views.sql    23 Jan 2005 19:45:42 -0000
@@ -24,7 +24,8 @@
        urb.name as city,
        adr.number as number,
        str.name as street,
-       adr.addendum as addendum
+       adr.addendum as addendum,
+       coalesce (adr.location, str.location, urb.location) as location
 from
        address adr,
        state s,
Index: sql/gmDemographics-Grants.sql
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/server/sql/gmDemographics-Grants.sql,v
retrieving revision 1.7
diff -u -r1.7 gmDemographics-Grants.sql
--- sql/gmDemographics-Grants.sql       21 Dec 2004 09:59:40 -0000      1.7
+++ sql/gmDemographics-Grants.sql       23 Jan 2005 19:45:42 -0000
@@ -30,11 +30,6 @@
        enum_ext_id_types,
        lnk_identity2ext_id,
        lnk_identity2ext_id_id_seq,
-       mapbook,
-       coordinate,
-       coordinate_id_seq,
-       address_info,
-       address_info_id_seq,
        lnk_person_org_address,
        lnk_person_org_address_id_seq,
        lnk_identity2comm,
Index: sql/gmDemographics.sql
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/server/sql/gmDemographics.sql,v
retrieving revision 1.39
diff -u -r1.39 gmDemographics.sql
--- sql/gmDemographics.sql      21 Dec 2004 09:59:40 -0000      1.39
+++ sql/gmDemographics.sql      23 Jan 2005 19:45:42 -0000
@@ -56,8 +56,8 @@
                references state(id)
                on update cascade
                on delete restrict,
-       postcode text
-               not null,
+       postcode text,
+       location point,
        name text
                not null,
        unique (id_state, postcode, name)
@@ -84,7 +84,8 @@
         also useful as a default when adding new streets to an urb';
 COMMENT ON COLUMN urb.name IS
        'the name of the city/town/dwelling';
-
+COMMENT ON COLUMN urb.location is
+       'the location of the urb, as lat/long co-ordinates. Ideally this would 
be NOT NULL';
 -- ===================================================================
 create table street (
        id serial primary key,
@@ -96,6 +97,7 @@
        name text not null,
        postcode text,
        suburb text default null,
+       location point,
        unique(id_urb, name, postcode)
 ) inherits (audit_fields);
 
@@ -111,7 +113,8 @@
        'postcode for systems (such as UK Royal Mail) which specify the street';
 comment on column street.suburb is
        'the suburb this street is in (if any)';
-
+comment on column street.location is
+'the approximate location of the street, as lat/long co-ordinates';
 -- ===================================================================
 create table address (
        id serial primary key,
@@ -120,11 +123,10 @@
                references street(id)
                on update cascade
                on delete restrict,
-       aux_street text default null,
        number text not null,
-       subunit text default null,
        addendum text default null,
-       unique(id_street, aux_street, number, subunit, addendum)
+       location point,
+       unique(id_street, number, addendum)
 ) inherits (audit_fields);
 
 -- FIXME: should be unique(coalesce(field, '')) for aux_street, subunit, 
addendum !
@@ -137,20 +139,15 @@
        'the street this address is at from
         whence the urb is to be found, it
         thus indirectly references urb(id)';
-comment on column address.aux_street is
+comment on column address.addendum is
        'additional street-level information which
         formatters would usually put on lines directly
         below the street line of an address, such as
         postal box directions in CA';
 comment on column address.number is
-       'number of the house';
-comment on column address.subunit is
-       'directions *below* the unit (eg.number) level,
-        such as appartment number, room number, level,
-        entrance or even verbal directions';
-comment on column address.addendum is
-       'any additional information that
-        did not fit anywhere else';
+       'number of the house, can include a falt number, such as 2/72';
+comment on column address.location is
+'the exact location of this address, in latitude-longtitude';
 
 -- ===================================================================
 create table address_type (
@@ -173,51 +170,6 @@
 );
 
 -- ===================================================================
-
--- the following table still needs a lot of work.
--- especially the GPS and map related information needs to be
--- normalized
-
--- added IH 8/3/02
--- table for street civilian type maps, i.e Melways
-create table mapbook (
-       id serial primary key,
-       name char (30)
-);
-
--- ===================================================================
--- table for co-ordinate systems, such at latitude-longitude
--- there are others, military, aviation and country-specific.
--- GPS handsets can display several.
-create table coordinate (
-      id serial primary key,
-      name varchar (30),
-      scale float
-);
-      -- NOTE: this converts distances from the co-ordinate units to
-      -- kilometres.
-      -- theoretically this may be problematic with some systems due to the
-      -- ellipsoid nature of the Earth, but in reality it is unlikely to matter
-
--- ===================================================================
-create table address_info (
-        id serial primary key,
-        id_address integer references address(id),
-        location point,
-        id_coord integer references coordinate (id),
-        mapref char(30),
-        id_map integer references mapbook (id),
-        howto_get_there text,
-        comments text
-) inherits (audit_fields);
-
-select add_table_for_audit('address_info');
-
--- this refers to a SQL point type. This would allow us to do
--- interesting queries, like, how many patients live within
--- 10kms of the clinic.
-
--- ===================================================================
 -- person related tables
 -- ===================================================================
 create table identity (
Index: sql/gmReference-data.sql
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/server/sql/gmReference-data.sql,v
retrieving revision 1.1
diff -u -r1.1 gmReference-data.sql
--- sql/gmReference-data.sql    9 Mar 2004 09:21:56 -0000       1.1
+++ sql/gmReference-data.sql    23 Jan 2005 19:45:42 -0000
@@ -47,6 +47,40 @@
 -- form templates
 -- ===================================================
 
+insert into form_field_types (name) values ('string');
+-- plain text, param is a regex which must match 
+-- after every keypress during editing, to
+-- instantly reject erroneous input
+-- regexes must therefore match partial valid inputs, including
+-- the empty string
+-- [0-9]*            integer
+-- [0-9,]*\.?[0-9]*  float
+-- [0-9]{0,7}|([0-9]{6,7}[A-Z])  HIC provider number
+-- address@hidden email address 
+insert into form_field_types (name) values ('list');
+-- one selected from a list of string values, param is the list, 
+-- separated by '\n'.
+-- whether this is displayed as radio buttons, drop down list, &c, is
+-- up to the GUI layer.
+insert into form_field_types (name) values ('boolean');
+-- boolean value. GUI will usually be some form of tickbox
+insert into form_field_types (name) values ('text');
+-- a larger piece of text. This suggests the GUI element should be multi-line
+-- and have word-processing features like spellcheck. 
+-- maybe allow some simple markup like bold and italic text
+insert into form_field_types (name) values ('date');
+-- a date
+insert into form_field_types (name) values ('entity');
+-- a gmDemographicRecord.cOrg or its descendant (cIdentity). Usually the
+-- addressee of a communication (but doesn't have to be)
+insert into form_field_types (name) values ('address');
+-- an address of the entity (an entity field must be in the form too,
+-- param is the internal_name of this field)
+-- this is a Python dict with fields 'number', 'street', 'addendum', 'city', 
'postcode'
+insert into form_field_types (name) values ('drug_list');
+-- a list of drug-preparations, business layer class yet to be written
+
+
 -- ===================================================
 -- do simple schema revision tracking
 delete from gm_schema_revision where filename='$RCSfile: 
gmReference-data.sql,v $';
Index: sql/gmReference.sql
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/server/sql/gmReference.sql,v
retrieving revision 1.14
diff -u -r1.14 gmReference.sql
--- sql/gmReference.sql 18 Dec 2004 09:55:24 -0000      1.14
+++ sql/gmReference.sql 23 Jan 2005 19:45:42 -0000
@@ -172,10 +172,12 @@
 );
 
 comment on table form_types is 'types of forms which are available,
-generally by purpose (radiology, pathology, sick leave, etc.)
-The form type determines the names and types of variables passed to
-the form engine to create this form.';
+generally by purpose (radiology, pathology, sick leave, etc.)';
 
+create table form_field_types (
+       name text unique,
+       pk serial primary key
+);
 
 create table form_defs (
        pk serial primary key,
@@ -188,8 +190,7 @@
        template text,
        engine char default 'T' not null check (engine in ('T', 'L', 'H')),
        in_use boolean not null default true,
-       electronic boolean not null default false,
-       flags varchar (100) [],
+       url text,
        unique (name_short, name_long),
        unique (name_long, revision)
 ) inherits (audit_fields);
@@ -214,19 +215,44 @@
         to process this form, currently:
         - T: plain text
         - L: LaTeX
-        - H: HL7';
+        - H: Health Layer 7';
 comment on column form_defs.in_use is
        'whether this template is currently actively
         used in a given practice';
-comment on column form_defs.electronic is
-       'True if the form is designed for electronic transmission,
-        such as e-mail. Currently always false as we need
-        appropriate middleware engines to do this (viz. HL7)';
-comment on column form_defs.flags is
-       'an array of flags (boolean options) for this form
-        which the GUI should display to the user,
-        Currently not implemented';
+comment on column form_defs.url is
+       'For electronic forms which are always sent to the same 
+       url (such as reports to a statutory public-health surveilliance 
+       authority)';
+-- ===================================================
+create table form_fields (
+       pk serial primary key,
+       fk_form integer
+               not null
+               references form_defs(pk),
+       long_name varchar (64) not null,
+       internal_name varchar (32),
+       help text,
+       fk_type integer not null references form_field_types (pk),
+       param text,
+       display_order integer,
+       unique (fk_form, long_name),
+       unique (fk_form, internal_name)
+);
 
+comment on table form_fields is
+       'List of fields for a particular form';
+comment on column form_fields.long_name is
+       'The full name of the form field as presented to the user';
+comment on column form_fields.internal_name is
+       'the name of the field as exposed to the form template. Must be a valid 
identifier in the form template''s script language (viz. Python)';
+comment on column form_fields.help is
+       'longer help text';
+comment on column form_fields.fk_type is
+       'the fields type';
+comment on column form_fields.display_order is
+       'used to *suggest* display order, but client may ignore';
+comment on column form_fields.param is
+       'a parameter for the field''s behaviour, meaning is type-dependent';
 -- ===================================================
 create table form_print_defs (
        pk serial primary key,
Index: sql/country.specific/au/gmReference.sql
===================================================================
RCS file: 
/cvsroot/gnumed/gnumed/gnumed/server/sql/country.specific/au/gmReference.sql,v
retrieving revision 1.7
diff -u -r1.7 gmReference.sql
--- sql/country.specific/au/gmReference.sql     15 Dec 2004 12:14:09 -0000      
1.7
+++ sql/country.specific/au/gmReference.sql     23 Jan 2005 19:45:42 -0000
@@ -12,107 +12,54 @@
 
 -- ===================================================================
 
-
-insert into form_types (name) values ('radiology');
-insert into form_types (name) values ('pathology');
-insert into form_types (name) values ('vascular');
--- Variables to be passed to the forms engine for these 3 are
--- +-----------------+-------------------------------------------------+
--- | demo            | the patient's gmDemographicRecord.cPerson object|
--- +-----------------+-------------------------------------------------+
--- | codes           | a list of strings: request codes                |
--- |                 | (coding system TBA)                             |
--- +-----------------+-------------------------------------------------+
--- | request         | the free text request, with modality and        |
--- |                 | anatomic region for study                       |
--- +-----------------+-------------------------------------------------+
--- | notes           | free text clinical notes                        |
--- +-----------------+-------------------------------------------------+
--- | clinical        | the patient's cClinicalRecord object            |
--- +-----------------+-------------------------------------------------+
--- | instructions    | patient's special instructions                  |
--- +-----------------+-------------------------------------------------+
 insert into form_types (name) values ('workplace sick certificate');
--- +-----------------+-------------------------------------------------+
--- | demo            | the patient's gmDemographicRecord.cPerson object|
--- +-----------------+-------------------------------------------------+
--- | commence        | date on which [we declare] the patient fell ill |
--- +-----------------+-------------------------------------------------+
--- | return_to_work  | date on which the patient can return to work    |
--- +-----------------+-------------------------------------------------+
--- | full_duties     | date on which full duties can re-commence       |
--- +-----------------+-------------------------------------------------+
--- | restriction     | if full_duties != return_to_work, a free text   |
--- |                 | description of the work restrictions            |
--- +-----------------+-------------------------------------------------+
--- | max_weight      | a float, further to the above, maximum weight to|
--- |                 | be lifted, in kilos                             |
--- +-----------------+-------------------------------------------------+
 insert into form_types (name) values ('vaccination report');
--- +-----------------+-------------------------------------------------+
--- | begin_date      | the first date of the report period             |
--- +-----------------+-------------------------------------------------+
--- | last_date       | the last date of the report period              |
--- |                 | (the form script will use special methods       |
--- |                 | on gmClinicalRecord to grab the vaccines and    |
--- |                 | patient IDs)                                    |
--- +-----------------+-------------------------------------------------+
+-- a list of persons (usually children) vaccinated, to a central
+-- government registry
 insert into form_types (name) values ('referral');
--- +-----------------+-------------------------------------------------+
--- | demo            | the patient's gmDemographicRecord.cPerson object|
--- +-----------------+-------------------------------------------------+
--- | notes           | free text clinical notes                        |
--- +-----------------+-------------------------------------------------+
--- | clinical        | the patient's cClinicalRecord object            |
--- +-----------------+-------------------------------------------------+
--- | recipient       | the gmDemographicRecord.cPerson or cOrg of the  | 
--- |                 | referral recipient                              |
--- +-----------------+-------------------------------------------------+
--- | meds            | flag, true to include med list (which is grabbed|
--- |                 | from the cClinicalRecord)                       |
--- +-----------------+-------------------------------------------------+
--- | past_history    | flag, true to include the past history (which is|
--- |                 | grabbed from the cClinicalRecord)               |
--- +-----------------+-------------------------------------------------+
+insert into form_types (name) values ('surveillance');
+-- a report of disease case to a public health authority
 insert into form_types (name) values ('prescription');
--- +-----------------+-------------------------------------------------+
--- | demo            | the patient's gmDemographicRecord.cPerson object|
--- +-----------------+-------------------------------------------------+
--- | meds            | a list of cMedication objects                   |
--- +-----------------+-------------------------------------------------+
--- | clinical        | the patient's cClinicalRecord object            |
--- +-----------------+-------------------------------------------------+
+insert into form_types (name) values ('invoice');
+insert into form_types (name) values ('bulk invoice');
+-- a list of services performed to a whole group of patients,
+-- to an insurer.
+insert into form_types (name) values ('request');
+-- pathology/radiology/physio/etc.
+insert into form_types (name) values ('reminder');
+-- a reminder letter to patient, asking them to make an appointment
+
 
-insert into form_defs (pk, name_short, name_long, revision, engine, template) 
values
-(1,
+insert into form_defs (fk_type, country, name_short, name_long, revision, 
engine, template) values
+(5, 'AU', 
  'Standard Referral', 'Standard specialist referral letter for AU', 1, 'L', 
  '\\documentclass{letter}
-\\address{ @"%(title)s %(first)s %(last)s" % sender.get_names ()@ \\\\ 
-@"%(number)s %(street)s" % sender.getAddresses (''work'', 1)@ \\\\
-@"%(urb)s %(postcode)s" % sender.getAddresses (''work'', 1)@ \\\\}
-\\signature{ @"%(title)s %(first)s %(last)s" % sender.get_names ()@}
+\\address{ @"%(title)s %(first)s %(last)s" % user.get_names ()@ \\\\ 
+@"%(number)s %(street)s" % user[''addresses''][''work'']@ \\\\
+@"%(city)s %(postcode)s" % user[''addresses''][''work'']@ \\\\}
+\\signature{ @"%(title)s %(first)s %(last)s" % user.get_names ()@}
 \\begin{document}
-\\begin{letter}{@"%(title)s %(first)s %(last)s" % recipient.get_names ()@ \\\\ 
-@"%(number)s %(street)s" % recipient_address@ \\\\
-@"%(urb)s %(postcode)s" % recipient_address@ \\\\} }
+\\begin{letter}{@"%(title)s %(first)s %(last)s" % addressee.get_names ()@ \\\\ 
+@"%(number)s %(street)s" % address@ \\\\
+@"%(urb)s %(postcode)s" % address@ \\\\} }
 
-\\opening{Dear @"%(title)s %(first)s %(last)s" % recipient.get_names ()@ }
+\\opening{Dear @"%(title)s %(first)s %(last)s" % addressee.get_names ()@ }
 
 \\textbf{Re:} @"%(first)s %(last)s" % patient.get_names ()@, 
-@"%(number)s %(street)s, %(urb)s %(postcode)s" % patient.getAddresses 
(''home'', 1)@, 
+@"%(number)s %(street)s, %(city)s %(postcode)s" % 
patient[''addresses''][''home'']@, 
 DOB: @patient.getDOB ().Format (''%x'')@
 
 @text@
 
address@hidden''include_meds'']@>0
address@hidden@>0
 \\textbf{Medications List}
 
 \\begin{tabular}{lll}
address@hidden''drug''], med[''dose''], med[''direction'']] for med in 
clinical.getMedicationsList ()]@
address@hidden''name''], med[''form''], med[''direction'']] for med in 
clinical.getMedicationsList ()]@
 \\end{tabular}
 \\fi
 
address@hidden''include_pasthx'']@>0
address@hidden''incl_phx'']@>0
 \\textbf{Disease List}
 
 \\begin{tabular}{ll}
@@ -125,28 +72,37 @@
 \\end{letter}
 \\end{document}');
 
-insert into form_defs (pk, name_short, name_long, revision, engine, template) 
values
-(2, 
- 'E-mail Referral', 'E-mail referral letter for Australia', 1, 'T',
--- WARNING: no standard for this actually exists in AU!
--- this is for demonstration purposes only 
-'Re:  @"%(first)s %(last)s" % patient.get_names ()@ 
-      @"%(number)s %(street)s, %(urb)s %(postcode)s" % patient.getAddresses 
(''home'', 1)@, 
-      DOB: @patient.getDOB ().Format (''%x'')@
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 1, 'Addressee', 'addressee', 
+  'Person the referral is sent to', 
+  6,
+  NULL, 1);
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 1, 'Address', 'address', 
+  'Address to mail to', 
+  7,
+  NULL, 2);
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 1, 'Text', 'text', 
+  'Text of referral', 
+  4,
+  NULL, 3);
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 1, 'Include medications', 'incl_meds', 
+  '', 
+  3,
+  NULL, 4);
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 1, 'Include past history', 'incl_phx', 
+  '', 
+  3,
+  NULL, 5);
 
address@hidden@
 
-@(flags[''include_meds''] and "Medications") or ''''@
-@(flags[''include_meds''] and [[med[''drug''], med[''dose''], 
med[''direction'']] for med in clinical.getMedicationsList ()]) or ''''@
 
-@(flags[''include_phx''] and "Past History") or ''''@
-@(flags[''include_phx''] and [[phx[''diagnosis''], phx[''started'']] for phx 
in clinical.getPastHistory ()]) or ''''@
-');
 
-
-
-insert into form_defs (pk, name_short, name_long, revision, engine, template) 
values
-(3, 
+insert into form_defs (fk_type, country, name_short, name_long, revision, 
engine, template) values
+(7, 'AU', 
 'PBS Script', 'Prescription using the standard form of the Pharmaceutical 
Benefits Scheme', 1, 'L',
 '\\documentclass{a4form}
 \\usepackage{multicol}
@@ -155,23 +111,23 @@
 
 \\begin{document}
 \\begin{page}
address@hidden ("Prescriber No.")@}
address@hidden ("Prescriber No.")@}
address@hidden''ext_ids'']["Prescriber No."address@hidden
address@hidden''ext_ids'']["Prescriber No."address@hidden
 
address@hidden ("Repat No.") or patient.getExternalID ("Medicare No.")@}
address@hidden ("Repat No.") or address@hidden ("Medicare No.")@}
+\\text{35}{33}{80}{@(patient[''ext_ids''].has_key ("Repat No.") and 
patient[''ext_ids''][''Repat No.'']) or patient[''ext_ids'']["Medicare 
No."address@hidden
+\\text{140}{33}{80}{@(patient.[['ext_ids''].has_key (''Repat No.''] and 
patient[''ext_ids'']["Repat No."]) or patient[''ext_ids'']["Medicare 
No."address@hidden
 % use the Department of Veteran''s affairs number if available: these patients 
get extra benefits
 
 \\text{24}{57}{80}{@"%(first)s %(last)s" % patient.get_names ()@}
 \\text{129}{57}{80}{@"(first)s %(last)s" % patient.get_names ()@}
 
-\\text{15}{60}{80}{@"%(number)s %(street)s, %(urb)s %(postcode)s" % 
patient.getAddresses (''home'', 1)@}
-\\text{120}{60}{80}{@"%(number)s %(street)s, %(urb)s %(postcode)s" % 
patient.getAddresses (''home'', 1)@}
+\\text{15}{60}{80}{@"%(number)s %(street)s, %(city)s %(postcode)s" % 
patient[''addresses''][''home''address@hidden
+\\text{120}{60}{80}{@"%(number)s %(street)s, %(city)s %(postcode)s" % 
patient[''addresses''][''home''address@hidden
 
 \\text{9}{72}{50}{\\today}
 \\text{114}{72}{50}{\\today}
 
address@hidden ("Repat No.") is not None@>0
address@hidden''ext_id''s].has_key ("Repat No.")@>0
 \\text{27}{77}{10}{X} % mark as RPBS script
 \\text{132}{77}{10}{X}
 \\else
@@ -179,7 +135,7 @@
 \\text{112}{77}{10}{X}
 \\fi
 
address@hidden''no_brand_substitution'']>0
address@hidden@>0
 \\text{43}{74.5}{7}{X}
 \\text{148}{74.5}{7}{X}
 \\fi
@@ -187,45 +143,196 @@
 \\text{22.5}{84}{82.5}{
 % TODO: drugs business objects yet to be written
 \\vspace{1cm}
-\\hspace{1cm} @"%(title)s %(first)s %(last)s" % sender.get_names ()@
+\\hspace{1cm} @"%(name)s %(form)s %(direction)s " % drug_list@
 }
 
 \\text{127.5}{84}{82.5}{
 % TODO: drugs business objects yet to be written
 \\vspace{1cm}
-\\hspace{1cm} @"%(title)s %(first)s %(last)s" % sender.get_names ()@
+\\hspace{1cm} @"%(name)s %(form)s %(direction)s" % drug_list@
 }
 \\end{page}
 \\end{document}');
 
-insert into form_defs (pk, name_short, name_long, revision, engine, template) 
values
-(4,
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 2, 'Items', 'drug_list', 
+  'Drugs to be prescribed', 
+  8,
+  NULL, 1);
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 2, 'Brand only', 'brand', 
+  'Brand-subsitution by pharmacist forbidden', 
+  3,
+  NULL, 2);
+
+
+insert into form_defs (fk_form, country, name_short, name_long, revision, 
engine, template) values
+(10, 'AU',
  'Basic request', 'A proof-of-concept basic request form', 1, 'L', 
- '\\documentclass[12pt]{article}
+ '
+\\documentclass{a4form}
+\\usepackage{multicol}
+
 \\begin{document}
+\\begin{page}
+\\text{20}{12}{70}{} % to address
 
-\\begin{tabular}{ll}
-\\textbf{Sender:} & @"%(title)s %(first)s %(last)s" % sender.get_names ()@ \\\\
-& @"%(number)s %(street)s" % sender.getAddresses (''work'', 1)@ \\\\
-& @"%(urb)s %(postcode)s" % sender.getAddresses (''work'', 1)@ \\\\
-\\end{tabular}
+\\lineh{20}{40}{170} % horizontal separators
+\\lineh{20}{50}{110}
+\\lineh{20}{80}{170}
+\\lineh{20}{135}{170}
+\\lineh{20}{220}{170}
+\\lineh{20}{255}{170}
+
+\\linev{20}{40}{215} % sidelines
+\\linev{190}{40}{215}
+
+\\linev{130}{40}{95} % broken vertical separator
+\\linev{130}{220}{35}
+
+\\lineh{130}{107}{60} % smaller separators
+\\linev{57}{40}{10}
+\\linev{90}{40}{10}
+
+\\text{22}{45}{20}{Billing}
+\\text{59}{45}{15}{Our ref:}
+\\text{92}{45}{20}{Your ref:}
+\\text{133}{45}{55}{
+\\textbf{Requesting Practitioner}\\
+\\
+@''%(title)s %(firstnames)s %(lastnames)s'' % user.get_names ()@\\\\
+@''%(number)s %(street)s'' % user[''addresses''][``work'']\\\\
+@''%(city)s %(postcode)s'' % user[''addresses''][``work'']\\\\
address@hidden''comms''].has_key (''fax'') and ''FAX: %s\\\\\\\\'' % 
user[''comms''][''fax'']@
address@hidden''comms''].has_key (''telephone'') and ''PHONE: %s\\\\\\\\'' % 
user[''comms''][''telephone'']@
 
+\\text{22}{55}{50}{
 \\begin{tabular}{ll}
 \\textbf{Patient:} & @"%(title)s %(first)s %(last)s" % patient.get_names ()@ 
\\\\
-& @"%(number)s %(street)s" % patient.getAddresses (''home'', 1)@ \\\\
-& @"%(urb)s %(postcode)s" % patient.getAddresses (''home'', 1)@ \\\\
+& @"%(number)s %(street)s" % patient[''addresses''][''home'']@ \\\\
+& @"%(city)s %(postcode)s" % patient[''addresses''][''home'']@ \\\\
 DOB: & @patient.getDOB ().Format (''%x'')@ \\\\
address@hidden''comms''].has_key (''telephone'') and ''PHONE: & %s\\\\\\\\'' % 
patient[''comms''][''telephone'']@
 \\end{tabular}
+}
+\\text{22}{85}{100}{
+\textbf{REQUEST FOR @address@hidden
address@hidden@\\
+\\\\
address@hidden (therapy)@>0
+\\textbf{THERAPY}\\\\
address@hidden@\\\\
+\\fi
+\\
address@hidden (clinical_notes)@>0
+\\textbf{CLINICAL NOTES}\\
address@hidden@\\
+}
 
-\\textbf{Request:}  @request@
address@hidden is not None@>0
+\text{133}{85}{55}{
+\textbf{Copy of report to:}\\
+\\
address@hidden and ''%(title)s %(firstnames)s %(lastnames)s'' % 
copy_to.get_names ()@\\\\
address@hidden and ''%(number)s %(street)s, %(city)s %(postcode)s'' % 
copy_to[''addresses''][''work''address@hidden
address@hidden and copy_to[''comms''].has_key (''fax'') and ''FAX: %s'' % 
copy_to[''comms''][''fax''address@hidden
+}
+\\fi
+\\text{130}{107}{60}{
+\\begin{multicols}{2}
address@hidden@} Routine\\\\
address@hidden@} Urgent\\\\\
address@hidden@} Fax result\\\\
address@hidden@} Phone result\\\\
address@hidden@} Pensioner\\\\
address@hidden''ext id''].has_key (''Repat No.'')@} Veteran\\\\
address@hidden@} Referral pads\\\\
+\\end{multicols}
+}
 
+\\text{22}{140}{165}{
+\\textbf{PATIENT INSTRUCTIONS}\\\\
 
-\\textbf{Clinical Notes:} @clinical_notes@
address@hidden@}
 
+\\text{133}{225}{55}{
+\\textbf{Doctor''s Signature}
+}
+
+\\text{57}{260}{80}{GNUMed v0.1.0}
+
+\\end{page}
+\\end{document}
+');
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Request', 'request', 
+  'The services requested', 
+  4,
+  NULL, 1);
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Clinical Notes', 'clinical_notes', 
+  '', 
+  4,
+  NULL, 2);
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Therapy', 'therapy', 
+  'Description of patient''s current therapy', 
+  4,
+  NULL, 3);
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Patient Instructions', 'instructions', 
+  'Instructions for the Patient', 
+  4,
+  NULL, 4);
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Routine', 'routine', 
+  'Routine request', 
+  3,
+  NULL, 5);
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Urgent', 'urgent', 
+  '', 
+  3, NULL, 6);
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Fax result', 'fax_result', 
+  '', 
+  3, NULL, 7);
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Phone result', 'phone_result', 
+  '', 
+  3, NULL, 8);
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Pensioner', 'pensioner', 
+  'Patient is a pensioner (i.e. asking referree for discount)', 
+  3, NULL, 9);
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Referral pads', 'referral_pad', 
+  '', 
+  3, NULL, 10);
+
+insert into form_fields (fk_form, long_name, internal_name, help, fk_type, 
param, display_order ) values
+( 3, 'Type', 'type', 
+  'The clinical discipline of the referree', 
+  2, 'Pathology
+Radiology
+Vascular
+Cardiology
+Neurophysiology
+Respiratory
+Nuclear Medicine
+Audiology
+Physiotherapy
+Occupational Therapy', 11);
+-- these referral types must match entries in org_category.description in the 
demographics service.
 
address@hidden (instructions)@>0
-\\textbf{Patient Instructions:} 
address@hidden@
-\\fi
 
-\\end{document}');
\ No newline at end of file
? __init__.pyc
? client.patch
? diff
? gm-from-cvs.log
? gm-refcount.lst
? gm-slave-doc-viewer.log
? bitmaps/.xvpics
? business/__init__.pyc
? business/foo
? business/gmAllergy.pyc
? business/gmClinItem.pyc
? business/gmClinNarrative.pyc
? business/gmClinicalRecord.pyc
? business/gmDemographicRecord.pyc
? business/gmEMRStructItems.pyc
? business/gmForms.pyc
? business/gmKVK.pyc
? business/gmMedDoc.pyc
? business/gmOrganization.pyc
? business/gmPathLab.pyc
? business/gmPatient.pyc
? business/gmVaccination.pyc
? business/gmXdtMappings.pyc
? business/gmXdtObjects.pyc
? business/gmXmlDocDesc.pyc
? doc/user-manual/user-manual
? exporters/__init__.pyc
? exporters/gmPatientExporter.pyc
? pycommon/__init__.pyc
? pycommon/gbdbo.diff
? pycommon/gmBackendListener.pyc
? pycommon/gmBorg.pyc
? pycommon/gmBusinessDBObject.pyc
? pycommon/gmCLI.pyc
? pycommon/gmCfg.pyc
? pycommon/gmConfigCommon.pyc
? pycommon/gmDbObject.pyc
? pycommon/gmDispatcher.pyc
? pycommon/gmDrugObject.pyc
? pycommon/gmDrugView.pyc
? pycommon/gmExceptions.pyc
? pycommon/gmGuiBroker.pyc
? pycommon/gmI18N.pyc
? pycommon/gmLog.pyc
? pycommon/gmLoginInfo.pyc
? pycommon/gmMatchProvider.pyc
? pycommon/gmMimeLib.pyc
? pycommon/gmMimeMagic.pyc
? pycommon/gmNull.pyc
? pycommon/gmPG.pyc
? pycommon/gmPatientNameQuery.pyc
? pycommon/gmPsql.pyc
? pycommon/gmPyCompat.pyc
? pycommon/gmSchemaRevisionCheck.pyc
? pycommon/gmScriptingListener.pyc
? pycommon/gmSerialTools.pyc
? pycommon/gmSignals.pyc
? pycommon/gmWhoAmI.pyc
? pycommon/gmdbf.pyc
? wxpython/__init__.pyc
? wxpython/foo
? wxpython/gm-from-cvs.log
? wxpython/gmAbout.pyc
? wxpython/gmAllergyWidgets.pyc
? wxpython/gmAppoint.pyc
? wxpython/gmBMIWidgets.pyc
? wxpython/gmCharacterValidator.pyc
? wxpython/gmCryptoText.pyc
? wxpython/gmDateTimeInput.pyc
? wxpython/gmDemographics.pyc
? wxpython/gmEMRBrowser.pyc
? wxpython/gmEMRTextDump.pyc
? wxpython/gmEditArea.pyc
? wxpython/gmFormPrinter.pyc
? wxpython/gmGP_ActiveProblems.pyc
? wxpython/gmGP_FamilyHistorySummary.pyc
? wxpython/gmGP_HabitsRiskFactors.pyc
? wxpython/gmGP_Inbox.pyc
? wxpython/gmGP_PatientPicture.pyc
? wxpython/gmGP_SocialHistory.pyc
? wxpython/gmGuiElement_AlertCaptionPanel.pyc
? wxpython/gmGuiElement_DividerCaptionPanel.pyc
? wxpython/gmGuiElement_HeadingCaptionPanel.pyc
? wxpython/gmGuiHelpers.pyc
? wxpython/gmGuiMain.pyc
? wxpython/gmHorstSpace.pyc
? wxpython/gmLabWidgets.pyc
? wxpython/gmLabels.pyc
? wxpython/gmListCtrlMapper.pyc
? wxpython/gmLogin.pyc
? wxpython/gmLoginDialog.pyc
? wxpython/gmMacro.pyc
? wxpython/gmMedDocWidgets.pyc
? wxpython/gmMultiColumnList.pyc
? wxpython/gmPatPicWidgets.pyc
? wxpython/gmPatSearchWidgets.pyc
? wxpython/gmPatientHolder.pyc
? wxpython/gmPatientSelector.pyc
? wxpython/gmPhraseWheel.pyc
? wxpython/gmPlugin.pyc
? wxpython/gmPlugin_Patient.pyc
? wxpython/gmPregWidgets.pyc
? wxpython/gmRegetMixin.pyc
? wxpython/gmSQLListControl.pyc
? wxpython/gmSQLSimpleSearch.pyc
? wxpython/gmSelectPerson.pyc
? wxpython/gmShadow.pyc
? wxpython/gmSingleBoxSOAP.py
? wxpython/gmSingleBoxSOAP.pyc
? wxpython/gmTalkback.pyc
? wxpython/gmTerryGuiParts.pyc
? wxpython/gmTimer.pyc
? wxpython/gmTopPanel.pyc
? wxpython/gmVaccWidgets.pyc
? wxpython/gnumed.pyc
? wxpython/images.pyc
? wxpython/images_Archive_plugin.pyc
? wxpython/images_Archive_plugin1.pyc
? wxpython/images_contacts_toolbar16_16.pyc
? wxpython/images_for_gnumed_browser16_16.pyc
? wxpython/images_gnuMedGP_Toolbar.pyc
? wxpython/images_gnumedGP.pyc
? wxpython/images_gnumedGP_notebook.pyc
? wxpython/images_patient_demographics.pyc
? wxpython/test
? wxpython/gui/__init__.pyc
? wxpython/gui/gmAllergiesPlugin.pyc
? wxpython/gui/gmClinicalWindowManager.pyc
? wxpython/gui/gmConfigRegistry.pyc
? wxpython/gui/gmContacts.pyc
? wxpython/gui/gmDemographicsEditor.pyc
? wxpython/gui/gmDrugDisplay.pyc
? wxpython/gui/gmEMRBrowserPlugin.pyc
? wxpython/gui/gmEMRTextDumpPlugin.pyc
? wxpython/gui/gmGuidelines.pyc
? wxpython/gui/gmLabJournal.pyc
? wxpython/gui/gmLock.pyc
? wxpython/gui/gmManual.pyc
? wxpython/gui/gmOffice.pyc
? wxpython/gui/gmPython.pyc
? wxpython/gui/gmRequest.pyc
? wxpython/gui/gmSQL.pyc
? wxpython/gui/gmShowLab.pyc
? wxpython/gui/gmShowMedDocs.pyc
? wxpython/gui/gmSingleBoxSoapPlugin.pyc
? wxpython/gui/gmSnellen.pyc
? wxpython/gui/gmStikoBrowser.pyc
? wxpython/gui/gmVaccinationsPlugin.pyc
? wxpython/gui/gmXdtViewer.pyc
? wxpython/gui/gmplNbSchedule.pyc
? wxpython/gui/plugins.conf
? wxpython/patient/__init__.pyc
? wxpython/patient/gmBMICalc.pyc
? wxpython/patient/gmCrypto.pyc
? wxpython/patient/gmDemographics.pyc
? wxpython/patient/gmGP_Allergies.pyc
? wxpython/patient/gmGP_AnteNatal_3.pyc
? wxpython/patient/gmGP_ClinicalSummary.pyc
? wxpython/patient/gmGP_FamilyHistory.pyc
? wxpython/patient/gmGP_Immunisation.pyc
? wxpython/patient/gmGP_Measurements.pyc
? wxpython/patient/gmGP_PastHistory.pyc
? wxpython/patient/gmGP_Prescriptions.pyc
? wxpython/patient/gmGP_Recalls.pyc
? wxpython/patient/gmGP_Referrals.pyc
? wxpython/patient/gmGP_Requests.pyc
? wxpython/patient/gmGP_ScratchPadRecalls.pyc
? wxpython/patient/gmGP_TabbedLists.pyc
? wxpython/patient/gmPregCalc.pyc
Index: business/gmDemographicRecord.py
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/client/business/gmDemographicRecord.py,v
retrieving revision 1.54
diff -u -r1.54 gmDemographicRecord.py
--- business/gmDemographicRecord.py     18 Aug 2004 09:05:07 -0000      1.54
+++ business/gmDemographicRecord.py     23 Jan 2005 19:50:07 -0000
@@ -9,12 +9,12 @@
 # $Source: 
/cvsroot/gnumed/gnumed/gnumed/client/business/gmDemographicRecord.py,v $
 # $Id: gmDemographicRecord.py,v 1.54 2004/08/18 09:05:07 ncq Exp $
 __version__ = "$Revision: 1.54 $"
-__author__ = "K.Hilbert <address@hidden>, I.Haywood"
+__author__ = "K.Hilbert <address@hidden>, I.Haywood <address@hidden>"
 
 # access our modules
-import sys, os.path, time
+import sys, os.path, time, string
 
-from Gnumed.pycommon import gmLog, gmExceptions, gmPG, gmSignals, 
gmDispatcher, gmMatchProvider, gmI18N
+from Gnumed.pycommon import gmLog, gmExceptions, gmPG, gmSignals, 
gmDispatcher, gmMatchProvider, gmI18N, gmBusinessDBObject
 from Gnumed.business import gmMedDoc
 from Gnumed.pycommon.gmPyCompat import *
 
@@ -24,6 +24,7 @@
 # 3rd party
 import mx.DateTime as mxDT
 
+
 #============================================================
 # map gender abbreviations in a GnuMed demographic service
 # to a meaningful localised string
@@ -33,611 +34,350 @@
        'tf': _('transsexual, female phenotype'),
        'tm': _('transsexual, male phenotype')
 }
-#============================================================
-# virtual ancestor class, SQL and LDAP descendants
-class cDemographicRecord:
-
-       def addName (self, firstname, lastname, activate):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def getTitle(self):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def setDOB (self, dob):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def getDOB(self):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def getCOB ():
-               raise gmExceptions.PureVirtualFunction ()
-
-       def setCOB (self, cob):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def getID(self):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def setTitle (self, title):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def getGender(self):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def setGender(self, gender):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def getAddresses (self, type):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def linkNewAddress (self, addr_type, number, street, urb, postcode, 
state = None, country = None):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def unlinkAddress (self, ID):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def setAddress (self, type, number, street, urb, postcode, state, 
country):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def getCommChannel (self, type):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def linkCommChannel (self, type, comm):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def getMedicalAge(self):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def setOccupation(self, occupation):
-               raise gmExceptions.PureVirtualFunction ()
-
-       def getOccupation(self):
-               raise gmExceptions.PureVirtualFunction ()
 
+#===================================================================
+#class cComm (gmBusinessDBObject.cBusinessDBObject):
+       
+#===================================================================
 
-#============================================================
-# may get preloaded by the waiting list
-class cDemographicRecord_SQL(cDemographicRecord):
-       """Represents the demographic data of a patient.
-       """
+#class cAddress (gmBusinessDBObject.cBusinessObject):
+       # to be honest, I'm not really convinced it means much sense to be able 
to
+       # change addresses and comms. When someone 'changes' their address, 
really they are unbinding
+       # from the old address and binding to a new one,
+       # so I'm going back to having addresses and comms as plain dictionaries
        
-       def __init__(self, aPKey = None):
-               """Fails if
+#===================================================================
+class cOrg (gmBusinessDBObject.cBusinessDBObject):
+       """
+       Organisations
 
-               - no connection to database possible
-               - patient referenced by aPKey does not exist
+       This is also the common ancestor of cIdentity, self._table is used to
+       hide the difference.
+       The aim is to be able to sanely write code which doesn't care whether
+       its talking to an organisation or an individual"""
+       _table = "org"
+
+       _cmd_fetch_payload = "select *, xmin from org where id=%s"
+       _cmds_lock_rows_for_update = ["select 1 from org where id=%(id)s and 
xmin=%(xmin)s"]
+       _cmds_store_payload = ["update org set description=%(description)s, 
id_category=(select id from org_category where description=%(occupation)s) 
where id=%(id)s", "select xmin from org whereid=%(id)s"]
+       _updatable_fields = ["description", "occupation"]
+       _service = 'personalia'
+       #------------------------------------------------------------------
+       def export_demographics (self):
+               if not self.__cache.has_key ('addresses'):
+                       self['addresses']
+               if not self.__cache.has_key ('comms'):
+                       self['comms']
+               return self.__cache
+       #------------------------------------------------------------------
+       def get_addresses (self):
+               """Returns a list of address dictionaries. Fields are
+               - id
+               - number
+               - addendum
+               - street
+               - city
+               - postcode
+               - type"""
+               cmd = """select
+                               vba.id,
+                               vba.number,
+                               vba.addendum, 
+                               vba.street,
+                               vba.city,
+                               vba.postcode,
+                               at.name
+                       from
+                               v_basic_address vba,
+                               lnk_person_org_address lpoa,
+                               address_type at
+                       where
+                               lpoa.id_address = vba.id
+                               and lpoa.id_type = at.id
+                               and lpoa.id_%s = %%s 
+                               """ % self._table     # this operator, then 
passed to SQL layer
+               rows, idx = gmPG.run_ro_query('personalia', cmd, 1, 
[self['id']])
+               if rows is None:
+                       return []
+               elif len(rows) == 0:
+                       return []
+               else:
+                       return [{'id':i[0], 'number':i[1], 'addendum':i[2], 
'street':i[3], 'city':i[4], 'postcode':i[5],
+                                'type':i[6]} for i in rows]
+       #--------------------------------------------------------------------
+       def get_members (self):
                """
-               self.ID = aPKey # == identity.id == primary key
-               if not self._pkey_exists():
-                       raise gmExceptions.ConstructorError, "no patient with 
ID [%s] in database" % aPKey
-
-               self.PUPIC = ""
-               self.__db_cache = {}
-
-               # register backend notification interests ...
-               #if not self._register_interests():
-                       #raise gmExceptions.ConstructorError, "Cannot register 
patient modification interests."
-
-               _log.Log(gmLog.lData, 'instantiated demographic record for 
patient [%s]' % self.ID)
-
-                               
-       #--------------------------------------------------------
-       def cleanup(self):
-               """Do cleanups before dying.
-
-               - note that this may be called in a thread
+               Returns a list of (address dict, cIdentity) tuples 
                """
-               _log.Log(gmLog.lData, 'cleaning up after patient [%s]' % 
self.ID)
-               # FIXME: unlisten from signals etc.
-       #--------------------------------------------------------
-       # internal helper
-       #--------------------------------------------------------
-       def _pkey_exists(self):
-               """Does this primary key exist ?
+               cmd = """select
+                               vba.id,
+                               vba.number,
+                               vba.addendum, 
+                               vba.street,
+                               vba.city,
+                               vba.postcode,
+                               at.name,
+                               vbp.i_id as id,
+                               title,
+                               firstnames,
+                               lastnames,
+                               dob,
+                               cob,
+                               gender,
+                               pupic,
+                               fk_marital_status,
+                               marital_status,
+                               karyotype,
+                               xmin_identity,
+                               preferred
+                       from
+                               v_basic_address vba,
+                               lnk_person_org_address lpoa,
+                               address_type at,
+                               v_basic_person vbp
+                       where
+                               lpoa.id_address = vba.id
+                               and lpoa.id_type = at.id
+                               and lpoa.id_identity = vbp.i_id
+                               and lpoa.id_org = %%s
+                               """
+               rows, idx = gmPG.run_ro_query('personalia', cmd, 1, self['id'])
+               if rows is None:
+                       return []
+               elif len(rows) == 0:
+                       return []
+               else:
+                       return [({'id':i[0], 'number':i[1], 'addendum':i[2], 
'street':i[3], 'city':i[4], 'postcode':i[5], 'type':i[6]}, cIdentity (row = 
{'data':i[7:], 'id':idx[7:], 'pk_field':'id'})) for i in rows] 
+       #------------------------------------------------------------
+       def set_member (self, person, address):
+               """
+               Binds a person to this organisation at this address
+               """
+               cmd = "insert into lnk_person_org_address (id_type, id_address, 
id_org, id_identity) values ((select id from address_types where 
type=%(type)s), create_address (%(number)s, %(addendum)s, %(street)s, %(city)s, 
%(postcode)s), %(org_id)s, %(i_id)s)"
+               address['i_id'] = person['id']
+               address['org_id'] = self['id']
+               if not id_addr:
+                       return (False, None)
+               return gmPG.run_commit2 ('personalia', [(cmd, [address])])
+       #------------------------------------------------------------
+       def unlink_person (self, person):
+               cmd = "delete from lnk_person_org_address where id_org = %s and 
id_identity = %s"
+               return gmPG.run_commit2 ('personalia', [(cmd, [self['id'], 
person['id']])])
+       #------------------------------------------------------------
+       def unlink_address (self, address):
+               cmd = "delete from lnk_person_org_address where id_address = %s 
and id_%s = %%s" % self._table
+               return gmPG.run_commit2 ('personalia', [(cmd, [address['id'], 
self['id']])])
+       #------------------------------------------------------------
+       def get_ext_ids (self):
+               """
+               Returns a list of dictionaries of external IDs
+               Fields:
+               - origin [the origin]
+               - comment [a user comment]
+               - external_id [the actual external ID]
+               """
+               cmd = """select
+               enum_ext_id_types.name, comment, external_id
+               from lnk_%s2ext_id where id_%s = %%s""" % (self._table, 
self._table)
+               rows = gmPG.run_ro_query ('personalia', cmd, None, self['id'])
+               if rows is None:
+                       return []
+               return [{'origin':row[0], 'comment':row[1], 
'external_id':row[2]} for row in rows]
+       #----------------------------------------------------------------
+       def set_ext_id (self, fk_origin, ext_id, comment=None):
+               """
+               @param fk_origin the origin type ID as returned by 
GetExternalIDs
+               @param ext_id the external ID, a free string as far as GNUMed 
is concerned.[1]
+               Set ext_id to None to delete an external id
+               @param comment distinguishes several IDs of one origin
 
-               - true/false/None
+               <b>[1]</b> But beware, language extension packs are entitled to 
add backend triggers to check for validity of external IDs.
                """
-               cmd = "select exists(select id from identity where id = %s)"
-               res = gmPG.run_ro_query('personalia', cmd, None, self.ID)
-               if res is None:
-                       _log.Log(gmLog.lErr, 'check for person ID [%s] 
existence failed' % self.ID)
-                       return None
-               return res[0][0]
-       #--------------------------------------------------------
-       # messaging
-       #--------------------------------------------------------
-       def _register_interests(self):
-               # backend
-               self._backend.Listen(
-                       service = 'personalia',
-                       signal = '"%s.%s"' % (gmSignals.patient_modified(), 
self.ID),
-                       callback = self._patient_modified
-               )
-       #--------------------------------------------------------
-       def _patient_modified(self):
-               # <DEBUG>
-               _log.Log(gmLog.lData, "patient_modified signal received from 
backend")
-               # </DEBUG>
-       #--------------------------------------------------------
-       # API
-       #--------------------------------------------------------
-       def export_demographics (self, all = False):
-               demographics_data = {}
-               demographics_data['id'] = self.getID()
-               demographics_data['names'] = []
-               names = self.get_names(all)
-               if all:
-                       for name in names:
-                               demographics_data['names'].append(name)
+               to_commit = []
+               if comment:
+                       cmd = """delete from lnk_%s2ext_id where
+                       id_%s = %%s and fk_origin = %%s
+                       and comment = %%s""" % (self._table, self._table)
+                       to_commit.append ((cmd, [self.__cache['id'], fk_origin, 
comment]))
+               else:
+                       cmd = """delete from lnk_%s2ext_id where
+                       id_%s = %%s and fk_origin = %%s""" % (self._table, 
self._table)
+                       to_commit.append ((cmd, [self.__cache['id'], 
fk_origin]))
+               if ext_id:
+                       cmd = """insert into lnk_%s2ext_id (id_%s, fk_origin, 
comment)
+                       values (%%s, %%s, %%s)""" % (self._table, self._table)
+                       to_commit.append ((cmd, [self.__cache['id'], ext_id, 
comment]))
+               return gmPG.run_commit2 ('personalia', to_commit)
+       #-------------------------------------------------------
+       def delete_ext_id (self, fk_origin, comment=None):
+               self.set_ext_id (fk_origin, None, comment)
+       #-------------------------------------------------------        
+       def get_comms (self):
+               """A list of ways to communicate"""
+               
+               cmd = """select
+                               ect.description,
+                               lc.url,
+                               lc.is_confidential
+                       from
+                               lnk_%s2comm lc,
+                               enum_comm_types ect
+                       where
+                               lc.id_%s = %%s and
+                               lc.id_type = ect.id
+                               """ % (self._table, self._table)
+               rows, idx = gmPG.run_ro_query('personalia', cmd, 1, self['id'])
+               if rows is None:
+                       return []
+               elif len(rows) == 0:
+                       return []
                else:
-                       demographics_data['names'].append(names)
-               demographics_data['gender'] = self.getGender()
-               demographics_data['title'] = self.getTitle()
-               demographics_data['dob'] = self.getDOB (aFormat = 'DD.MM.YYYY')
-               demographics_data['mage'] =  self.getMedicalAge()
-               address_map = {}
-               adr_types = getAddressTypes()
-               # FIXME: rewrite using the list returned from getAddresses()
-               for adr_type in adr_types:
-                       if not all and adr_type != "home":
-                               continue
-                       address_map[adr_type] = self.getAddresses(adr_type)
-               demographics_data['addresses'] = address_map
-               return demographics_data
+                       return [{'url':i[1],'type':i[0], 'is_confidential':[2]} 
for i in rows]
+       #--------------------------------------------------------------
+       def set_comm (self, id_type, url, is_confidential):
+               """
+               @param id_type is the ID of the comms type from 
getCommChannelTypes
+               """
+               cmd1 = """delete from lnk_%s2comm_channel where
+               id_%s = %%s and url = %%s""" % (self._table, self._table)
+               cmd2 = """insert into lnk_%s2ext_id (id_%s, id_type, url, 
is_confidential)
+               values (%%s, %%s, %%s, %%s)""" % (self._table, self._table)
+               return gmPG.run_commit2 ('personalia', [(cm1, [self['id'], 
url]), (cm2, [self['id'], id_type, url, is_confidential])])
+       #-------------------------------------------------------
+       def delete_comm (self, url):
+               cmd = """delete from lnk_%s2comm_channel where
+               id_%s = %%s and url = %%s""" % (self._table, self._table)
+               return gmPG.run_commit2 ('personalia', [(cmd, [self['id'], 
url])])
+#==============================================================================
+class cIdentity (cOrg):
+       _table = "identity"
+       _cmd_fetch_payload = "select * from v_basic_person where i_id=%s"
+       _cmds_lock_rows_for_update = ["select 1 from identity where id=%(id)s 
and xmin_identity = %(xmin_identity)"]
+       _cmds_store_payload = ["""
+       update identity set title=%(title)s, dob=%(dob)s, cob=%(cob)s, 
gender=%(gender)s,
+       fk_marital_status = %(fk_marital_status)s, karyotype = %(karyotype)s, 
pupic = %(pupic)s where id=%(id)s""", "select xmin_identity from v_basic_person 
where id=%(id)s"]
+       _updatable_fields = ["title", "dob", "cob", "gender", 
"fk_marital_status", "karyotype", "pupic"]        
        #--------------------------------------------------------
-       def get_names(self, all=False):
-               if all:
-                       cmd = """
+       def get_all_names(self):
+               cmd = """
                                select n.firstnames, n.lastnames, i.title
                                from names n, identity i
                                where n.id_identity=%s and i.id=%s"""
-               else:
-                       cmd = """
-                               select vbp.firstnames, vbp.lastnames, i.title
-                               from v_basic_person vbp, identity i
-                               where vbp.i_id=%s and i.id=%s"""
-               rows, idx = gmPG.run_ro_query('personalia', cmd, 1, self.ID, 
self.ID)
+               rows, idx = gmPG.run_ro_query('personalia', cmd, 1, 
self._cache['id'], self.__cache['id'])
                if rows is None:
                        return None
                if len(rows) == 0:
-                       name = {'first': '**?**', 'last': '**?**', 'title': 
'**?**'}
-                       if all:
-                               return [name]
-                       return name
-               if all:
+                       return [{'first': '**?**', 'last': '**?**', 'title': 
'**?**'}]
+               else:
                        names = []
                        for row in rows:
                                names.append({'first': row[0], 'last': row[1], 
'title': row[2]})
                        return names
-               else:
-                       return {'first': rows[0][0], 'last': rows[0][1], 
'title': rows[0][2]}
        #--------------------------------------------------------
-#      def getFullName (self):
-#              cmd  = "select title, firstnames, lastnames from v_basic_person 
where i_id = %s"
-#              r = gmPG.run_ro_query ('personalia', cmd, 0, self.ID)
-#              if r:
-#                      return "%s %s %s" % (r[0][0], r[0][1], r[0][2])
-#              else:
-#                      return _("Unknown")
+       def get_description (self):
+               """
+               Again, allow code reuse where we don't care whther we are 
talking to a person
+               or organisation"""
+               return _("%(title)s %(firstnames)s %(lastnames)s") % self 
        #--------------------------------------------------------
-       def addName(self, firstname, lastname, activate = None):
-               """Add a name and possibly activate it."""
+       def add_name(self, firstnames, lastnames, active=True):
+               """Add a name """
                cmd = "select add_name(%s, %s, %s, %s)"
-               if activate:
-                       activate_str = 'true'
-               else:
-                       activate_str = 'false'
-               gmPG.run_commit ('personalia', [(cmd, [self.ID, firstname, 
lastname, activate_str])])
-       #---------------------------------------------------------      
-       def getTitle(self):
-               cmd = "select title from v_basic_person where i_id = %s"
-               data = gmPG.run_ro_query('personalia', cmd, None, self.ID)
-               if data is None:
-                       return ''
-               if len(data) == 0:
-                       return ''
-               if data[0][0] == None:
-                       return ''
-               return data[0][0]
-       #--------------------------------------------------------
-       def setTitle (self, title):
-               cmd = "update identity set title=%s where id=%s"
-               return gmPG.run_commit ('personalia', [(cmd, [title, self.ID])])
-       #--------------------------------------------------------
-       def getID(self):
-               return self.ID
-       #--------------------------------------------------------
-       def getDOB(self, aFormat = None):
-               if aFormat is None:
-                       cmd = "select dob from identity where id=%s"
-               else:
-                       cmd = "select to_char(dob, '%s') from identity where 
%s" % (aFormat, "id=%s")
-               data = gmPG.run_ro_query('personalia', cmd, None, self.ID)      
        
-               if data is None:
-                       if aFormat is None:
-                               return None
-                       return ''
-               if len(data) == 0:
-                       if aFormat is None:
-                               return None
-                       return ''
-               return data[0][0]
-       #--------------------------------------------------------
-       def setDOB (self, dob):
-               cmd = "update identity set dob = %s where id = %s"
-               return gmPG.run_commit ('personalia', [(cmd, [dob, self.ID])])
-       #---------------------------------------------------------------------
-       def getCOB (self):
-               cmd = "select country.name from country, identity where 
country.code = identity.cob and identity.id = %s"
-               data = gmPG.run_ro_query ('personalia', cmd, None, self.ID)
-               return data and data[0][0]
-       #--------------------------------------------------------------------
-       def setCOB (self, cob):
-               cmd = "update identity set cob = country.code from country 
where identity.id = %s and country.name = %s"
-               success, err_msg = gmPG.run_commit('personalia', [(cmd, 
[self.ID, cob])], return_err_msg=1)
-               if not success:
-                       # user probably gave us invalid country
-                       msg = '%s (%s)' % ((_('invalid country [%s]') % cob), 
err_msg)
-                       print "invalid country!"
-                       raise gmExceptions.InvalidInputError (msg)
-       #----------------------------------------------------------------------
-       def getMaritalStatus (self):
-               cmd = "select name from marital_status, identity where 
marital_status.id = identity.id_marital_status and identity.id = %s"
-               data = gmPG.run_ro_query ('personalia', cmd, None, self.ID)
-               return data and data[0][0]
-       #--------------------------------------------------------------------
-       def setMaritalStatus (self, cob):
-               cmd = "update identity set id_marital_status = (select id from 
marital_status where name = %s) where id = %s"
-               return gmPG.run_commit ('personalia', [(cmd, [cob, self.ID])])
-       #---------------------------------------------------------------------
-       def getOccupation (self):
+               if active:
+                       # junk the cache appropriately
+                       if self._ext_cache.has_key ('description'):
+                               del self._ext_cache['description']
+                       self._payload[self._idx['firstnames']] = firstnames
+                       self._payload[self._idx['lastnames']] = lastnames
+               if self._ext_cache['all_names']:
+                       del self._ext_cache['all_names']
+               active = (active and 't') or 'f'
+               return gmPG.run_commit2 ('personalia', [(cmd, [self['id'], 
firstnames, lastnames, active])])
+       #------------------------------------------------------------
+       def add_address (self, address):
                """
-               Currently we do not support more than one occupation
+               Binds an address to this person
                """
+               cmd = "insert into lnk_person_org_address ((select id from 
address_types where type = %s), id_address, id_identity) values (%(type)s, 
create_address (%(number)s, %(addendum)s, %(street)s, %(city)s, %(postcode)s), 
%(i_id)s)"
+               address["i_id"] = self['id']
+               return gmPG.run_commit2 ('personalia', [(cmd, [address])])
+       #---------------------------------------------------------------------
+       def get_occupation (self):
                cmd = "select o.name from occupation o, lnk_job2person lj2p 
where o.id = lj2p.id_occupation and lj2p.id_identity = %s"
                data = gmPG.run_ro_query ('personalia', cmd, None, self.ID)
-               return data and data[0][0]
+               return data and [i[0] for i in data] 
        #--------------------------------------------------------------------
-       def setOccupation (self, occupation):
-               # does occupation already exist ?
-               cmd = "select o.id from occupation o where o.name = %s"
-               data = gmPG.run_ro_query ('personalia', cmd, None, occupation)
-               # error
-               if data is None:
-                       gmLog.gmDefLog.Log(gmLog.lErr, 'cannnot check for 
occupation')
-                       return None
-               # none found
-               if len(data) == 0:
-                       cmd1 = "insert into occupation (name) values (%s)"
-                       cmd2 = "select currval('occupation_id_seq')"
-                       data = gmPG.run_commit ('personalia', [
-                               (cmd1, [occupation]),
-                               (cmd2, [])
-                       ])
-                       if (data is None) or (len(data) == 0):
-                               _log.Log(gmLog.lErr, 'cannot add occupation 
details')
-                               return None
-
-               id_occupation = data[0][0]
-               # delete pre-existing link as required
-               cmd1 = """
+       def add_occupation (self, occupation):
+               # does occupation already exist ? -> backend can sort this out
+               cmd = "insert into lnk_job2person (id_identity, id_occupation) 
values (%s, create_occupation (%s))"
+               if self._ext_cache.has_key ('occupation'):
+                       self._ext_cache['occupation'].append (occupation)
+               return gmPG.run_commit2 ('personalia', [(cmd, [self['id'], 
occupation])])
+       #----------------------------------------------------------------------
+       def delete_occupation (self, occupation):
+               if self._ext_cache.has_key ('occupation'):
+                       del self._ext_cache[self._ext_cache.index (occupation)]
+               cmd = """
                delete from
-                       lnk_job2person
+               lnk_job2person
                where
-                       id_identity = %s"""
-               # creating new link
-               cmd2 = """insert into lnk_job2person (id_identity, 
id_occupation) values (%s, %s)"""
-               return gmPG.run_commit ('personalia', [
-                       (cmd1, [self.ID]),
-                       (cmd2, [self.ID, id_occupation])
-               ])
+               id_identity = %s and id_occupation = (select id from occupation 
where name = %s)"""
+               return gmPG.run_commit2 ('personalia', [(cmd, [self['id'], 
occupation])])
        #----------------------------------------------------------------------
        def get_relatives(self):
                cmd = """
 select
-       v.id, t.description , v.firstnames, v.lastnames, v.gender, v.dob
+        t.description, v.id as id, title, firstnames, lastnames, dob, cob, 
gender, karyotype, pupic, fk_marital_status,
+       marital_status, xmin_identity, preferred
 from
        v_basic_person v, relation_types t, lnk_person2relative l
 where
-       l.id_identity = %s and
+       (l.id_identity = %s and
        v.id = l.id_relative and
-       t.id = l.id_relation_type
+       t.id = l.id_relation_type) or
+       (l.id_relation = %s and
+       v.id = i.id_identity and
+       t.inverse = l.id_relation_type)
 """
-               data, idx = gmPG.run_ro_query('personalia', cmd, 1, self.ID)
+               data, idx = gmPG.run_ro_query('personalia', cmd, 1, 
[self['id'], self['id']])
                if data is None:
                        return []
                if len(data) == 0:
                        return []
-               return [{
-                       'id': r[idx['id']],
-                       'firstnames': r[idx['firstnames']],
-                       'lastnames': r[idx['lastnames']],
-                       'gender': r[idx['gender']],
-                       'dob': r[idx['dob']],
-                       'description': r[idx['description']]
-                       } for r in data ]
+               return [(r[0], cIdentity (row = {'data':r[1:], 'idx':idx, 
'pk_field':'id'})) for r in data ]
        #--------------------------------------------------------
-       def link_new_relative(self, rel_type = 'parent'):
-               from Gnumed.business.gmPatient import create_dummy_identity
-               # create new relative
-               id_new_relative = create_dummy_identity()
-               relative = gmPerson(id_new_relative)
-               # pre-fill with data from ourselves
-               relative_demographics = relative.get_demographic_record()
-               relative_demographics.copyAddresses(self)
-               relative_demographics.addName( '**?**', 
self.get_names()['last'], activate = 1)
+       def set_relative(self, rel_type, relation):
+               """
+               @param rel_type the relationship type, None to delete 
relationships
+               @type rel_type string
+               @param relation the cIdentity of the relation
+               """
+               cmd1 = """delete from lnk_person2relative where
+               (id_identity = %s and id_relative = %s) or (id_identity = %s 
and id_relative = %s)"""
                # and link the two
-               cmd = """
+               cmd2 = """
                        insert into lnk_person2relative (
                                id_identity, id_relative, id_relation_type
                        ) values (
                                %s, %s, (select id from relation_types where 
description = %s)
                        )"""
-               success = gmPG.run_commit ("personalia", [(cmd, 
(id_new_relative, self.ID, rel_type))])
-               if success:
-                       return id_new_relative
-               return None
-       #--------------------------------------------------------
-       def getGender(self):
-               cmd = "select gender from v_basic_person where i_id = %s"
-               data = gmPG.run_ro_query('personalia', cmd, None, self.ID)
-               if data is None:
-                       return ''
-               if len(data) == 0:
-                       return ''
-               return data[0][0]
-       #--------------------------------------------------------
-       def setGender(self, gender):
-               gender = gender.lower()
-               cmd = "update identity set gender = %s where id = %s"
-               return gmPG.run_commit ('personalia', [(cmd, [gender, 
self.ID])])
-       #--------------------------------------------------------
-       def getAddresses(self, addr_type = None, firstonly = 0):
-               """Return a patient's addresses. 
-                       addr_type is the address_type.name, not address_type.id
-
-               - return all types if addr_type is None
-               """
-               vals = {'pat_id': self.ID}
-               if addr_type is None:
-                       addr_where = ''
-               else:
-                       addr_where = 'and at.name = %(addr_type)s'
-                       vals['addr_type'] = addr_type
-               if firstonly:
-                       limit = ' limit 1'
-               else:
-                       limit = ''
-               cmd = """select
-                               vba.addr_id,
-                               vba.number,
-                               vba.street,
-                               vba.city,
-                               vba.postcode,
-                               at.name
-                       from
-                               v_basic_address vba,
-                               lnk_person_org_address lpoa,
-                               address_type at
-                       where
-                               lpoa.id_address = vba.addr_id
-                               and lpoa.id_type = at.id
-                               and lpoa.id_identity = %%(pat_id)s
-                               %s %s""" % (addr_where, limit)
-               rows, idx = gmPG.run_ro_query('personalia', cmd, 1, vals)
-               if rows is None:
-                       return []
-               if len(rows) == 0:
-                       return []
-               ret = [{
-                       'ID': r[idx['addr_id']],
-                       'number': r[idx['number']],
-                       'street': r[idx['street']],
-                       'urb': r[idx['city']],
-                       'postcode': r[idx['postcode']],
-                       'type': r[idx['name']]
-               } for r in rows]
-               if firstonly:
-                       return ret[0]
+               if self._ext_cache.has_key ('relatives'):
+                       del self._ext_cache['relatives']
+               if rel_type:
+                       return gmPG.run_commit2 ('personalia', [(cmd1, 
[self['id'], relation['id'], relation['id'], aelf['id']]),
+                                                               (cmd2, 
[relation['id'], self['id'], rel_type])])
                else:
-                       return ret
-       #--------------------------------------------------------
-       def unlinkAddress (self, ID):
-               cmd = "delete from lnk_person_org_address where id_identity = 
%s and id_address = %s"
-               return gmPG.run_commit ('personalia', [(cmd, [self.ID, ID])])
-       #--------------------------------------------------------
-       def copyAddresses(self, a_demographic_record):
-               addr_types = getAddressTypes()
-               for addr_type in addr_types:
-                       addresses = a_demographic_record.getAddresses(addr_type)
-                       if addresses is None:
-                               continue
-                       for addr in addresses:
-                               self.linkNewAddress(addr_type, addr['number'], 
addr['street'], addr['urb'], addr['postcode'])
-       #--------------------------------------------------------
-       def linkNewAddress (self, addr_type, number, street, urb, postcode, 
state = None, country = None):
-               """Adds a new address into this persons list of addresses.
-               """
-               if state is None:
-                       state, country = guess_state_country(urb, postcode)
-
-               # address already in database ?
-               cmd = """
-                       select addr_id from v_basic_address
-                       where
-                               number = %s and
-                               street = %s and
-                               city = %s and
-                               postcode = %s and
-                               state = %s and
-                               country = %s
-                       """
-               data = gmPG.run_ro_query ('personalia', cmd, None, number, 
street, urb, postcode, state, country)
-               if data is None:
-                       s = " ".join( ( addr_type, number, street, urb, 
postcode, state, country ) )
-                       _log.Log(gmLog.lErr, 'cannot check for address 
existence (%s)' % s)
-                       return None
-
-               # delete any pre-existing link for this identity and the given 
address type
-               cmd = """
-                       delete from lnk_person_org_address
-                       where
-                               id_identity = %s and
-                               id_type = (select id from address_type where 
name = %s)
-                       """
-               gmPG.run_commit ('personalia', [(cmd, [self.ID, addr_type])])
-
-               # yes, address already there, just add the link
-               if len(data) > 0:
-                       addr_id = data[0][0]
-                       cmd = """
-                               insert into lnk_person_org_address 
(id_identity, id_address, id_type)
-                               values (%s, %s, (select id from address_type 
where name = %s))
-                               """
-                       return gmPG.run_commit ("personalia", [(cmd, (self.ID, 
addr_id, addr_type))])
-
-               # no, insert new address and link it, too
-               cmd1 = """
-                       insert into v_basic_address (number, street, city, 
postcode, state, country)
-                       values (%s, %s, %s, %s, %s, %s)
-                       """
-               cmd2 = """
-                       insert into lnk_person_org_address (id_identity, 
id_address, id_type)
-                       values (%s, currval ('address_id_seq'), (select id from 
address_type where name = %s))
-                       """
-               return gmPG.run_commit ("personalia", [
-                       (cmd1, (number, street, urb, postcode, state, country)),
-                       (cmd2, (self.ID, addr_type))
-                       ]
-               )
-       #------------------------------------------------------------
-       # FIXME: should we support named channel types, too ?
-       def linkCommChannel (self, channel_type, url):
-               """Links a comm channel with a patient. Adds it first if needed.
-
-               (phone no., email, etc.).
-               channel_type is an ID as returned from getCommChannelTypeID()
-               """     
-               # does channel already exist ?
-               cmd = "select cc.id from comm_channel cc where cc.id_type = %s 
and cc.url = %s"
-               data = gmPG.run_ro_query ('personalia', cmd, None, 
channel_type, url)
-               # error
-               if data is None:
-                       _log(gmLog.lErr, 'cannnot check for comm channel 
details')
-                       return None
-               # none found
-               if len(data) == 0:
-                       cmd1 = "insert into comm_channel (id_type, url) values 
(%s, %s)"
-                       cmd2 = "select currval('comm_channel_id_seq')"
-                       data = gmPG.run_commit ('personalia', [
-                               (cmd1, [channel_type, url]),
-                               (cmd2, [])
-                       ])
-                       if (data is None) or (len(data) == 0):
-                               _log.Log(gmLog.lErr, 'cannot add communication 
channel details')
-                               return None
-
-               id_channel = data[0][0]
-               # delete pre-existing link as required
-               cmd1 = """
-               delete from
-                       lnk_identity2comm_chan
-               where
-                       id_identity = %s
-                               and
-                       exists(
-                               select 1
-                               from comm_channel cc
-                               where cc.id_type = %s and cc.id = id_comm)"""
-               # creating new link
-               cmd2 = """insert into lnk_identity2comm_chan (id_identity, 
id_comm) values (%s, %s)"""
-               return gmPG.run_commit ('personalia', [
-                       (cmd1, [self.ID, channel_type]),
-                       (cmd2, [self.ID, id_channel])
-               ])
-       #-------------------------------------------------------------
-       def getCommChannel (self, channel=None):
-               """
-               Gets the comm channel url, given its type ID
-               If ID default, a mapping of all IDs and urls
-               """
-               if channel:
-                       data = gmPG.run_ro_query ('personalia', """
-                       select cc.url from comm_channel cc, 
lnk_identity2comm_chan lp2cc where
-                       cc.id_type = %s and lp2cc.id_identity = %s and 
lp2cc.id_comm = cc.id
-                       """, None, channel, self.ID)
-                       return data and data[0][0]
-               else:
-                       data = gmPG.run_ro_query ('personalia', """
-                       select cc.id_type, cc.url
-                       from
-                       comm_channel cc,
-                       lnk_identity2comm_chan lp2cc
-                       where
-                       cc.id = lp2cc.id_comm and lp2cc.id_identity = %s
-                       """, None, self.ID)
-                       return dict(rows)
+                       return gmPG.run_commit2 ('personalia', [(cmd1, 
[self['id'], relation['id'], relation['id'], aelf['id']])])
+       #----------------------------------------------------------------------
+       def delete_relative(self, relation):
+               self.set_relative (None, relation)
        #----------------------------------------------------------------------
-       def getMedicalAge(self):
-               dob = self.getDOB()
+       def get_medical_age(self):
+               dob = self['dob']
                if dob is None:
                        return '??'
                return dob2medical_age(dob)
        #----------------------------------------------------------------------
-       def addExternalID(self, external_id = None, origin = 'DEFAULT', comment 
= None):
-               # FIXME: should we support named origins, too ?
-               if external_id is None:
-                       _log.Log(gmLog.lErr, 'must have external ID to add it')
-                       return None
-               args = {
-                       'ID': self.ID,
-                       'ext_ID': external_id,
-                       'origin': origin,
-                       'comment': comment,
-                       }
-               if comment:
-                       cmd1 = 'insert into lnk_identity2ext_id (id_identity, 
external_id, fk_origin, comment) values (%(ID)s, %(ext_ID)s, %(origin)s, 
%(comment)s)'
-               else:
-                       cmd1 = 'insert into lnk_identity2ext_id (id_identity, 
external_id, fk_origin) values (%(ID)s, %(ext_ID)s, %(origin)s)'
-               cmd2 = "select currval('lnk_identity2ext_id_id_seq')"
-               result = gmPG.run_commit('personalia', [
-                       (cmd1, [args]),
-                       (cmd2, [])
-               ])
-               if result is None:
-                       _log.Log(gmLog.lErr, 'cannot link external ID 
address@hidden (%s)' % (external_id, origin, comment))
-                       return None
-               return result[0][0]
-       #------------------------------------------------------------
-       def removeExternalID(self, pk_external_ID):
-               cmd = 'delete from lnk_identity2ext_id where id=%s'
-               return gmPG.run_commit('personalia', [(cmd, [pk_external_ID])])
-       #---------------------------------------------------------------
-       def listExternalIDs (self):
-               """
-               Returns a list of dictionaries of external IDs
-               Fields:
-               - id [the key used to delete ext. IDs
-               - origin [the origin code as returned by getExternalIDTypes]
-               - comment [a user comment]
-               - external_id [the actual external ID]
-               """
-               cmd = "select id, fk_origin, comment, external_id from 
lnk_identity2ext_id where id_identity = %s"
-               rows = gmPG.run_ro_query ('personalia', cmd, None, self.ID)
-               if rows is None:
-                       return []
-               return [{'id':row[0], 'origin':row[1], 'comment':row[2], 
'external_id':row[3]} for row in rows]
-       #----------------------------------------------------------------
-       def getExternalID (self, type):
-               """
-               Gets an external ID by name
-               """
-               cmd = "select external_id from lnk_identity2ext_id, 
enum_ext_id_types where enum_ext_id_types.name = %s and enum_ext_id_types.pk = 
lnk_identity2ext_id.fk_type and id_identity = %s"
-               rows = gmPG.run_ro_query ("personalia", cmd, None, type, 
self.ID)
-               if rows:
-                       return rows[0][0]
-               else:
-                       return None
 #================================================================
 # convenience functions
 #================================================================
@@ -664,35 +404,35 @@
        return "%sm%ss" % (age.minutes, age.seconds)
 #----------------------------------------------------------------
 def getAddressTypes():
-       """Gets a simple list of address types."""
-       row_list = gmPG.run_ro_query('personalia', "select name from 
address_type")
+       """Gets a dictionary mapping address type names to the ID"""
+       row_list = gmPG.run_ro_query('personalia', "select name, id from 
address_type")
        if row_list is None:
-               return []
+               return {}
        if len(row_list) == 0:
-               return []
-       return [row[0] for row in row_list]
+               return {}
+       return dict (row_list)
 #----------------------------------------------------------------
 def getMaritalStatusTypes():
-       """Gets a simple list of types of marital status."""
-       row_list = gmPG.run_ro_query('personalia', "select name from 
marital_status")
+       """Gets a dictionary matching marital status types to their internal 
ID"""
+       row_list = gmPG.run_ro_query('personalia', "select name, pk from 
marital_status")
        if row_list is None:
-               return []
+               return {}
        if len(row_list) == 0:
-               return []
-       return [row[0] for row in row_list]
+               return {}
+       return dict (row_list)
 #------------------------------------------------------------------
 def getExtIDTypes (context = 'p'):
-       """Gets list of [code, ID type] from the backend for the given context
+       """Gets dictionary mapping ext ID names to internal code from the 
backend for the given context
        """
        # FIXME: error handling
-       rl = gmPG.run_ro_query('personalia', "select pk, name from 
enum_ext_id_types where context = %s", None, context)
+       rl = gmPG.run_ro_query('personalia', "select name, pk from 
enum_ext_id_types where context = %s", None, context)
        if rl is None:
-               return []
-       return rl
+               return {}
+       return dict (rl)
 #----------------------------------------------------------------
 def getCommChannelTypes():
-       """Gets the dictionary of ID->comm channels"""
-       row_list = gmPG.run_ro_query('personalia', "select id, description from 
enum_comm_types")
+       """Gets the dictionary of comm channel types to internal ID"""
+       row_list = gmPG.run_ro_query('personalia', "select description, id from 
enum_comm_types")
        if row_list is None:
                return None
        if len (row_list) == 0:
@@ -845,21 +585,17 @@
                        }]
                gmMatchProvider.cMatchProvider_SQL.__init__ (self, source)
 
-class NameMP (gmMatchProvider.cMatchProvider_SQL):
+class NameMP (gmMatchProvider.cMatchProvider):
        """
        List of names
        """
-       def __init__ (self):
-               source =[{
-                       'service':'personalia',
-                       'table':'names',
-                       'pk':'id_identity',
-                       'column':'lastnames',
-                       'result':"lastnames || ', ' || firstnames",
-                       'limit':5,
-                       'extra conditions':{'occupation':'exists (select 1 from 
lnk_job2person where id_occupation = %s and lnk_job2person.id_identity = 
names.id_identity)'}
-                       }]
-               gmMatchProvider.cMatchProvider_SQL.__init__ (self, source)
+       def getMatches (self, fragment):
+               cmd = "select search_identity (%s)"
+               data, idx = gmPG.run_ro_query ('personalia', cmd, 1, fragment)
+               if data is None:
+                       _log.Log(gmLog.lErr, "cannot search for identity")
+                       return None
+               return [{'data':cIdentity (idx, i), 'label':"%s %s %s" % 
(i[idx['title']], i[idx['firstnames']], i[idx['lastnames']])} for i in data]
 
 #------------------------------------------------------------
 class OrgCategoryMP (gmMatchProvider.cMatchProvider_SQL):
@@ -889,24 +625,22 @@
        _log.SetAllLogLevels(gmLog.lData)
        gmDispatcher.connect(_patient_selected, gmSignals.patient_selected())
        while 1:
-               pID = raw_input('a patient ID: ')
-               if pID == '-1':
+               pID = raw_input('a patient: ')
+               if pID == '':
                        break
                try:
-                       myPatient = cDemographicRecord_SQL(aPKey = pID)
+                       myPatient = cIdentity (aPK_obj = pID)
                except:
                        _log.LogException('Unable to set up patient with ID 
[%s]' % pID, sys.exc_info())
                        print "patient", pID, "can not be set up"
                        continue
-               print "ID       ", myPatient.getID ()
-               print "name     ", myPatient.get_names (1)
-               print "title    ", myPatient.getTitle ()
-               print "dob      ", myPatient.getDOB (aFormat = 'DD.MM.YYYY')
-               print "med age  ", myPatient.getMedicalAge()
-               adr_types = getAddressTypes()
-               print "adr types", adr_types
-               for type_name in adr_types:
-                       print "adr (%s)" % type_name, 
myPatient.getAddresses(type_name)
+               print "ID       ", myPatient['id']
+               print "name     ", myPatient['description']
+               print "title    ", myPatient['title']
+               print "dob      ", myPatient['dob']
+               print "med age  ", myPatient['medical_age']
+               for adr in myPatient['addresses']:
+                       print "address  ", adr  
                print "--------------------------------------"
 #============================================================
 # $Log: gmDemographicRecord.py,v $
Index: business/gmForms.py
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/client/business/gmForms.py,v
retrieving revision 1.26
diff -u -r1.26 gmForms.py
--- business/gmForms.py 20 Aug 2004 13:19:06 -0000      1.26
+++ business/gmForms.py 23 Jan 2005 19:50:07 -0000
@@ -39,13 +39,12 @@
                self.template = template
                self.flags = flags
                self.pk_def = pk_def
-               self.patient = gmPatient.gmCurrentPatient ()
                self.whoami = gmWhoAmI.cWhoAmI ()
                self.workplace = self.whoami.get_workplace ()
 
        def convert (self, item):
                """
-               Perform whatever character set conversions are reuired for this 
form
+               Perform whatever character set conversions are required for 
this form
                """
                return item
 
@@ -62,7 +61,7 @@
 
        def cleanup (self):
                """
-               A sop to TeX which can't act as a true filter to delete 
temporary files
+               A sop to TeX which can't act as a true filter: to delete 
temporary files
                """
                pass
 
@@ -315,6 +314,10 @@
                os.chdir (self.oldcwd)
                os.rmdir (self.tmp)
 
+#===========================================================
+class HL7Form (gmFormEngine):
+       pass
+
 #============================================================
 # convenience functions
 #------------------------------------------------------------
Index: business/gmPatient.py
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/client/business/gmPatient.py,v
retrieving revision 1.56
diff -u -r1.56 gmPatient.py
--- business/gmPatient.py       2 Sep 2004 00:52:10 -0000       1.56
+++ business/gmPatient.py       23 Jan 2005 19:50:08 -0000
@@ -34,12 +34,12 @@
        # handlers for __getitem__
        _get_handler = {}
 
-       def __init__(self, aPKey = None):
-               self.__ID = aPKey                       # == identity.id == 
primary key
-               if not self._pkey_exists():
-                       raise gmExceptions.ConstructorError, "No person with ID 
[%s] in database." % aPKey
-
-               self.__db_cache = {}
+       def __init__(self, demo_record = None):
+               self.__ID = demo_record['id']   # == identity.id == primary key
+               # if not self._pkey_exists():
+               # redundant as this checks the demographic database, but we 
already have
+               # this object. 
+               self.__db_cache = {'demographic record':demo_record}
 
                # register backend notification interests ...
                if not self._register_interests():
@@ -63,18 +63,6 @@
        #--------------------------------------------------------
        # internal helper
        #--------------------------------------------------------
-       def _pkey_exists(self):
-               """Does this primary key exist ?
-
-               - true/false/None
-               """
-               cmd = "select exists(select id from identity where id = %s)"
-               res = gmPG.run_ro_query('personalia', cmd, None, self.__ID)
-               if res is None:
-                       _log.Log(gmLog.lErr, 'check for person ID [%s] 
existence failed' % self.__ID)
-                       return None
-               return res[0][0]
-       #--------------------------------------------------------
        def _register_interests(self):
                return 1
        #--------------------------------------------------------
@@ -125,18 +113,7 @@
                return self.__db_cache['clinical record']
        #--------------------------------------------------------
        def get_demographic_record(self):
-               if self.__db_cache.has_key('demographic record'):
-                       return self.__db_cache['demographic record']
-               tstart = time.time()
-               try:
-                       # FIXME: we need some way of setting the type of 
backend such that
-                       # to instantiate the correct type of demographic record 
class
-                       self.__db_cache['demographic record'] = 
gmDemographicRecord.cDemographicRecord_SQL(aPKey = self.__ID)
-               except StandardError:
-                       _log.LogException('cannot instantiate demographic 
record for person [%s]' % self.__ID, sys.exc_info())
-                       return None
-               duration = time.time() - tstart
-               print "get_demographic_record() took %s seconds" % duration
+               # because we are instantiated with it, it always exists
                return self.__db_cache['demographic record']
        #--------------------------------------------------------
        def get_document_folder(self):
@@ -187,13 +164,13 @@
 
        There may be many instances of this but they all share state.
        """
-       def __init__(self, aPKey = None):
+       def __init__(self, patient_demographic_record = None):
                """Change or get currently active patient.
 
-               aPKey:
+               patient_demographic_record:
                * None: get currently active patient
                * -1: unset currently active patient
-               * other: set active patient to aPKey if possible
+               * other: set active patient if possible
                """
                gmBorg.cBorg.__init__(self)
 
@@ -210,31 +187,31 @@
                        self.unlock()
 
                # user wants copy of current patient
-               if aPKey is None:
+               if  patient_demographic_record is None:
                        return None
 
                # same ID, no change needed
-               if self._patient['ID'] == aPKey:
+               if self._patient['id'] == patient_demographic_record['id']:
                        return None
 
                # user wants different patient
-               _log.Log(gmLog.lData, 'patient change [%s] -> [%s] requested' % 
(self._patient['ID'], aPKey))
+               _log.Log(gmLog.lData, 'patient change [%s] -> [%s] requested' % 
(self._patient['ID'], patient_demographic_record['id']))
 
                # but not if patient is locked
                if self._locked:
-                       _log.Log(gmLog.lErr, 'patient [%s] is locked, cannot 
change to [%s]' % (self._patient['ID'], aPKey))
+                       _log.Log(gmLog.lErr, 'patient [%s] is locked, cannot 
change to [%s]' % (self._patient['ID'], patient_demographic_record['id']))
                        # FIXME: exception ?
                        return None
 
                # user wants to explicitely unset current patient
-               if aPKey == -1:
+               if patient_demographic_record == -1:
                        _log.Log(gmLog.lData, 'explicitely unsetting patient')
                        new_pat = gmNull.cNull()
                else:
                        try:
-                               new_pat = gmPerson(aPKey)
+                               new_pat = gmPerson(patient_demographic_record)
                        except:
-                               _log.LogException('cannot connect with patient 
[%s]' % aPKey, sys.exc_info())
+                               _log.LogException('cannot connect with patient 
[%s]' % patient_demographic_record['ID'], sys.exc_info())
                                # returning None on INTERNAL errors (and thus 
silently
                                # staying the same patient) is a design decision
                                # FIXME: maybe raise exception here ?
@@ -363,6 +340,8 @@
        #--------------------------------------------------------
        def get_patient_ids(self, search_term = None, a_locale = None, 
search_dict = None):
                """Get patient IDs for given parameters.
+               These are actually demographic objects now,
+               but the caller shouldn't really care
 
                - either search term or search dict
                - search dict contains structured data that doesn't need to be 
parsed
@@ -403,21 +382,23 @@
                        for cmd in query_list:
                                # FIXME: actually, we should pass in the parsed 
search_term
                                if search_dict is None:
-                                       rows = gmPG.run_ro_query(self.curs, cmd)
+                                       rows, idx = 
gmPG.run_ro_query(self.curs, cmd, True)
                                else:
-                                       rows = gmPG.run_ro_query(self.curs, 
cmd, None, search_dict)
+                                       rows, idx = 
gmPG.run_ro_query(self.curs, cmd, True, search_dict)
                                if rows is None:
                                        _log.Log(gmLog.lErr, 'cannot fetch 
patient IDs')
                                else:
-                                       pat_ids.extend(rows)
+                                       pat_ids.append((rows, idx))
                        # if we got patients don't try more query levels
                        if len(pat_ids) > 0:
                                break
-
-               pat_id_list = []
-               for row in pat_ids:
-                       pat_id_list.append(row[0])
-               return pat_id_list
+               pat_demos = []
+               try:
+                       for rows, idx in pat_ids:
+                               pat_demos.extend 
([gmDemographicRecord.cIdentity (row={'pk_field':'id', 'data':row, 'idx':idx}) 
for row in rows])
+               except:
+                       _log.LogException ("cannot create patient demographic 
objects", sys.exc_info (), verbose=0)
+               return pat_demos
        #--------------------------------------------------------
        # internal helpers
        #--------------------------------------------------------
@@ -498,8 +479,8 @@
                # "<digits>" - GnuMed patient ID or DOB
                if re.match("^(\s|\t)*\d+(\s|\t)*$", raw):
                        tmp = raw.strip()
-                       queries.append(["SELECT i_id FROM v_basic_person WHERE 
i_id = '%s'" % tmp])
-                       queries.append(["SELECT i_id FROM v_basic_person WHERE 
dob='%s'::timestamp" % raw])
+                       queries.append(["SELECT * FROM v_basic_person WHERE 
i_id = '%s'" % tmp])
+                       queries.append(["SELECT * FROM v_basic_person WHERE 
dob='%s'::timestamp" % raw])
                        return queries
 
                # "#<di git  s>" - GnuMed patient ID
@@ -509,15 +490,15 @@
                        tmp = tmp.replace(' ', '')
                        tmp = tmp.replace('\t', '')
                        # this seemingly stupid query ensures the id actually 
exists
-                       queries.append(["SELECT i_id FROM v_basic_person WHERE 
i_id = '%s'" % tmp])
+                       queries.append(["SELECT * FROM v_basic_person WHERE 
i_id = '%s'" % tmp])
                        # but might also be an external ID
                        tmp = raw.replace('#', '')
                        tmp = tmp.strip()
                        tmp = tmp.replace(' ', '*#DUMMY#*')
                        tmp = tmp.replace('\t', '*#DUMMY#*')
                        tmp = tmp.replace('*#DUMMY#*', '(\s|\t|-|/)*')
-                       queries.append(["select id_identity from 
lnk_identity2ext_id where external_id ~* '^%s'" % tmp])
-                       queries.append(["select id_identity from 
lnk_identity2ext_id where external_id ~* '%s'" % tmp])
+                       queries.append(["select vba.* from lnk_identity2ext_id 
li2ei, v_basic_person vba where vba.i_id = li2ei.id_identity and 
li2ei.external_id ~* '^%s'" % tmp])
+                       queries.append(["select vba.* from lnk_identity2ext_id 
li2ei, v_basic_person vba where vba.i_id = li2ei.id_identity and 
li2ei.external_id ~* '%s'" % tmp])
                        return queries
 
                # "#<di/git s/orc-hars>" - external ID (or PUPIC)
@@ -529,16 +510,16 @@
                        tmp = tmp.replace('-', '*#DUMMY#*')
                        tmp = tmp.replace('/', '*#DUMMY#*')
                        tmp = tmp.replace('*#DUMMY#*', '(\s|\t|-|/)*')
-                       queries.append(["select id_identity from 
lnk_identity2ext_id where external_id ~* '^%s'" % tmp])
-                       queries.append(["select id_identity from 
lnk_identity2ext_id where external_id ~* '%s'" % tmp])
+                       queries.append(["select vba.* from lnk_identity2ext_id 
li2ei, v_basic_person vba where vba.i_id = li2ei.id_identity and 
li2ei.external_id ~* '%s'" % tmp])
+                       queries.append(["select vba.* from lnk_identity2ext_id 
li2ei, v_basic_person vba where vba.i_id = li2ei.id_identity and 
li2ei.external_id ~* '%s'" % tmp])
                        return queries
 
                # "<d igi ts>" - DOB or patient ID
                if re.match("^(\d|\s|\t)+$", raw):
-                       queries.append(["SELECT i_id FROM v_basic_person WHERE 
dob='%s'::timestamp" % raw])
+                       queries.append(["SELECT * FROM v_basic_person WHERE 
dob='%s'::timestamp" % raw])
                        tmp = raw.replace(' ', '')
                        tmp = tmp.replace('\t', '')
-                       queries.append(["SELECT i_id FROM v_basic_person WHERE 
i_id LIKE '%s%%'" % tmp])
+                       queries.append(["SELECT * FROM v_basic_person WHERE 
i_id LIKE '%s%%'" % tmp])
                        return queries
 
                # "<Z(.|/|-/ )I  FF ERN>" - DOB
@@ -548,22 +529,22 @@
                        # apparently not needed due to PostgreSQL smarts...
                        #tmp = tmp.replace('-', '.')
                        #tmp = tmp.replace('/', '.')
-                       queries.append(["SELECT i_id FROM v_basic_person WHERE 
dob='%s'::timestamp" % tmp])
+                       queries.append(["SELECT * FROM v_basic_person WHERE 
dob='%s'::timestamp" % tmp])
                        return queries
 
                # " , <alpha>" - first name
                if re.match("^(\s|\t)*,(\s|\t)*([^0-9])+(\s|\t)*$", raw):
                        tmp = raw.split(',')[1].strip()
                        tmp = self.__normalize(tmp)
-                       queries.append(["SELECT DISTINCT id_identity FROM names 
WHERE firstnames ~ '^%s'" % self.__make_sane_caps(tmp)])
-                       queries.append(["SELECT DISTINCT id_identity FROM names 
WHERE firstnames ~* '^%s'" % tmp])
+                       queries.append(["SELECT DISTINCT ON (id_identity) vbp.* 
FROM names, v_basic_person vbp WHERE names.firstnames ~ '^%s' and vbp.i_id = 
names.id_identity" % self.__make_sane_caps(tmp)])
+                       queries.append(["SELECT DISTINCT ON (id_identity) vbp.* 
FROM names, v_basic_person vbp WHERE names.firstnames ~ '^%s' and vbp.i_id = 
names.id_identity" % tmp])
                        return queries
 
                # "*|$<...>" - DOB
                if re.match("^(\s|\t)*(\*|\$).+$", raw):
                        tmp = raw.replace('*', '')
                        tmp = tmp.replace('$', '')
-                       queries.append(["SELECT i_id FROM v_basic_person WHERE 
dob='%s'::timestamp" % tmp])
+                       queries.append(["SELECT * FROM v_basic_person WHERE 
dob='%s'::timestamp" % tmp])
                        return queries
 
                return None
@@ -606,7 +587,7 @@
                except KeyError:
                        pass
 
-               queries.append(['select i_id from v_basic_person where %s' % ' 
and '.join(where_snippets)])
+               queries.append(['select * from v_basic_person where %s' % ' and 
'.join(where_snippets)])
                # sufficient data ?
                if len(queries) == 0:
                        _log.Log(gmLog.lErr, 'invalid search dict structure')
@@ -639,13 +620,13 @@
                        # there's no intermediate whitespace due to the regex
                        tmp = normalized.strip()
                        # assumption: this is a last name
-                       queries.append(["SELECT DISTINCT id_identity FROM names 
WHERE lastnames  ~ '^%s'" % self.__make_sane_caps(tmp)])
-                       queries.append(["SELECT DISTINCT id_identity FROM names 
WHERE lastnames  ~* '^%s'" % tmp])
+                       queries.append(["SELECT DISTINCT ON (id_identity) vbp.* 
FROM v_basic_person vbp, names WHERE vbp.i_id = names.id_identity and 
names.lastnames  ~ '^%s'" % self.__make_sane_caps(tmp)])
+                       queries.append(["SELECT DISTINCT ON (id_identity) vbp.* 
FROM v_basic_person vbp, names WHERE vbp.i_id = names.id_identity and 
names.lastnames  ~* '^%s'" % tmp])
                        # assumption: this is a first name
-                       queries.append(["SELECT DISTINCT id_identity FROM names 
WHERE firstnames ~ '^%s'" % self.__make_sane_caps(tmp)])
-                       queries.append(["SELECT DISTINCT id_identity FROM names 
WHERE firstnames ~* '^%s'" % tmp])
+                       queries.append(["SELECT DISTINCT ON (id_identity) vbp.* 
FROM v_basic_person vbp, names WHERE vbp.i_id = names.id_identity and 
names.firstnames ~ '^%s'" % self.__make_sane_caps(tmp)])
+                       queries.append(["SELECT DISTINCT ON (id_identity) vbp.* 
FROM v_basic_person vbp, names WHERE vbp.i_id = names.id_identity and 
names.firstnames ~* '^%s'" % tmp])
                        # name parts anywhere in name
-                       queries.append(["SELECT DISTINCT id_identity FROM names 
WHERE firstnames || lastnames ~* '%s'" % tmp])
+                       queries.append(["SELECT DISTINCT ON (id_identity) vbp.* 
FROM v_basic_person vbp, names WHERE vbp.i_id = names.id_identity and 
names.firstnames || names.lastnames ~* '%s'" % tmp])
                        return queries
 
                # try to split on (major) part separators
@@ -675,22 +656,22 @@
                                        # assumption: first last
                                        queries.append(
                                                [
-                                                "SELECT DISTINCT id_identity 
FROM names WHERE firstnames ~ '^%s' AND lastnames ~ '^%s'" % 
(self.__make_sane_caps(name_parts[0]), self.__make_sane_caps(name_parts[1]))
+                                                "SELECT DISTINCT ON 
(id_identity) vbp.* FROM v_basic_person vbp, names WHERE vbp.i_id = 
names.id_identity and names,firstnames ~ '^%s' AND lastnames ~ '^%s'" % 
(self.__make_sane_caps(name_parts[0]), self.__make_sane_caps(name_parts[1]))
                                                ]
                                        )
                                        queries.append([
-                                                "SELECT DISTINCT id_identity 
FROM names WHERE firstnames ~* '^%s' AND lastnames ~* '^%s'" % (name_parts[0], 
name_parts[1])
+                                                "SELECT DISTINCT ON 
(id_identity) vbp.* FROM v_basic_person vbp, names WHERE vbp.i_id = 
names.id_identity and names.firstnames ~* '^%s' AND lastnames ~* '^%s'" % 
(name_parts[0], name_parts[1])
                                        ])
                                        # assumption: last first
                                        queries.append([
-                                               "SELECT DISTINCT id_identity 
FROM names WHERE firstnames ~ '^%s' AND lastnames ~ '^%s'" % 
(self.__make_sane_caps(name_parts[1]), self.__make_sane_caps(name_parts[0]))
+                                               "SELECT DISTINCT ON 
(id_identity) vbp.* FROM v_basic_person vbp, names WHERE vbp.i_id = 
names.id_identity and names.firstnames ~ '^%s' AND lastnames ~ '^%s'" % 
(self.__make_sane_caps(name_parts[1]), self.__make_sane_caps(name_parts[0]))
                                        ])
                                        queries.append([
-                                               "SELECT DISTINCT id_identity 
FROM names WHERE firstnames ~* '^%s' AND lastnames ~* '^%s'" % (name_parts[1], 
name_parts[0])
+                                               "SELECT DISTINCT ON 
(id_identity) vbp.* FROM v_basic_person vbp, names WHERE vbp.i_id = 
names.id_identity and names.firstnames ~* '^%s' AND lastnames ~* '^%s'" % 
(name_parts[1], name_parts[0])
                                        ])
                                        # name parts anywhere in name - third 
order query ...
                                        queries.append([
-                                               "SELECT DISTINCT id_identity 
FROM names WHERE firstnames || lastnames ~* '%s' AND firstnames || lastnames ~* 
'%s'" % (name_parts[0], name_parts[1])
+                                               "SELECT DISTINCT ON 
(id_identity) vbp.* FROM v_basic_person vbp, names WHERE vbp.i_id = 
names.id_identity and names.firstnames || names.lastnames ~* '%s' AND 
firstnames || lastnames ~* '%s'" % (name_parts[0], name_parts[1])
                                        ])
                                        return queries
                                # FIXME: either "name date" or "date date"
@@ -703,21 +684,21 @@
                                if date_count == 1:
                                        # assumption: first, last, dob - first 
order
                                        queries.append([
-                                               "SELECT DISTINCT id_identity 
FROM names WHERE firstnames ~ '^%s' AND lastnames ~ '^%s' AND 
dob='%s'::timestamp" % (self.__make_sane_caps(name_parts[0]), 
self.__make_sane_caps(name_parts[1]), date_part)
+                                               "SELECT DISTINCT ON 
(id_identity) vbp.* FROM v_basic_person vbp, names WHERE vbp.i_id = 
names.id_identity and names.firstnames ~ '^%s' AND names.lastnames ~ '^%s' AND 
dob='%s'::timestamp" % (self.__make_sane_caps(name_parts[0]), 
self.__make_sane_caps(name_parts[1]), date_part)
                                        ])
                                        queries.append([
-                                               "SELECT DISTINCT id_identity 
FROM names WHERE firstnames ~* '^%s' AND lastnames ~* '^%s' AND 
dob='%s'::timestamp" % (name_parts[0], name_parts[1], date_part)
+                                               "SELECT DISTINCT ON 
(id_identity) vbp.* FROM v_basic_person vbp, names WHERE vbp.i_id = 
names.id_identity and firstnames ~* '^%s' AND names.lastnames ~* '^%s' AND 
dob='%s'::timestamp" % (name_parts[0], name_parts[1], date_part)
                                        ])
                                        # assumption: last, first, dob - second 
order query
                                        queries.append([
-                                               "SELECT DISTINCT id_identity 
FROM names WHERE firstnames ~ '^%s' AND lastnames ~ '^%s' AND 
dob='%s'::timestamp" % (self.__make_sane_caps(name_parts[1]), 
self.__make_sane_caps(name_parts[0]), date_part)
+                                               "SELECT DISTINCT ON 
(id_identity) vbp.* FROM v_basic_person vbp, names WHERE vbp.i_id = 
names.id_identity and names.firstnames ~ '^%s' AND names.lastnames ~ '^%s' AND 
dob='%s'::timestamp" % (self.__make_sane_caps(name_parts[1]), 
self.__make_sane_caps(name_parts[0]), date_part)
                                        ])
                                        queries.append([
-                                               "SELECT DISTINCT id_identity 
FROM names WHERE firstnames ~* '^%s' AND lastnames ~* '^%s' AND 
dob='%s'::timestamp" % (name_parts[1], name_parts[0], date_part)
+                                               "SELECT DISTINCT ON 
(id_identity) vbp.* FROM v_basic_person vbp, names WHERE vbp.i_id = 
names.id_identity and names.firstnames ~* '^%s' AND names.lastnames ~* '^%s' 
AND dob='%s'::timestamp" % (name_parts[1], name_parts[0], date_part)
                                        ])
                                        # name parts anywhere in name - third 
order query ...
                                        queries.append([
-                                               "SELECT DISTINCT id_identity 
FROM names WHERE firstnames || lastnames ~* '%s' AND firstnames || lastnames ~* 
'%s' AND dob='%s'::timestamp" % (name_parts[0], name_parts[1], date_part)
+                                               "SELECT DISTINCT ON 
(id_identity) vbp.* FROM v_basic_person vbp, names WHERE vbp.i_id = 
names.id_identity and names.firstnames || names.lastnames ~* '%s' AND 
names.firstnames || names.lastnames ~* '%s' AND dob='%s'::timestamp" % 
(name_parts[0], name_parts[1], date_part)
                                        ])
                                        return queries
                                # FIXME: "name name name" or "name date date"
@@ -859,7 +840,7 @@
                        for where_clause in wheres:
                                if len(where_clause) > 0:
                                        queries.append([
-                                               "SELECT i_id FROM 
v_basic_person WHERE %s" % string.join(where_clause, ' AND ')
+                                               "SELECT * FROM v_basic_person 
WHERE %s" % string.join(where_clause, ' AND ')
                                        ])
                                else:
                                        queries.append([])
@@ -887,9 +868,9 @@
                return False
        tstart = time.time()
        pat = gmCurrentPatient()
-       old_ID = pat.get_ID()
+       old_ID = pat.get_ID ()
        # nothing to do
-       if old_ID == anID:
+       if old_ID == anID['id']:
                return True
        # attempt to switch
        try:
Index: pycommon/gmSignals.py
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/client/pycommon/gmSignals.py,v
retrieving revision 1.5
diff -u -r1.5 gmSignals.py
--- pycommon/gmSignals.py       15 Jul 2004 07:57:20 -0000      1.5
+++ pycommon/gmSignals.py       23 Jan 2005 19:50:08 -0000
@@ -146,19 +146,19 @@
 
 def wish_display_plugin ():
        """
-       This event expressed the desire that aplugin be displayed
-       .I.e the plugin manger *receives* this evet, it generates the above
+       This event expressed the desire that a plugin be displayed
+       I.e the plugin manger *receives* this event, it generates the above
        - name: plugin unique name
        """
        return "wish_display_plugin"
 
-def new_sidebar ():
+def search_result ():
        """
-       A new sidebar widget
--widget: the wxWindow to display
-- name: the unique plugin ma e(for unloading)
-"""
-       return "new_sidebar"
+       The results of a patient search
+       - ids: a list of gmDemographicRecord.cIdentity objects
+       - display_fields: a list of fields to display
+       """
+       return "search_result"
 
        
 #============================================================= 
Index: wxpython/gmDemographics.py
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmDemographics.py,v
retrieving revision 1.49
diff -u -r1.49 gmDemographics.py
--- wxpython/gmDemographics.py  18 Dec 2004 13:45:51 -0000      1.49
+++ wxpython/gmDemographics.py  23 Jan 2005 19:50:08 -0000
@@ -14,7 +14,7 @@
 __license__ = 'GPL (details at http://www.gnu.org)'
 
 # standard library
-import cPickle, zlib, shutil, time, string
+import cPickle, zlib, shutil, time, string, sys
 
 # 3rd party
 from mx import DateTime
@@ -31,6 +31,7 @@
 _whoami = gmWhoAmI.cWhoAmI()
 _cfg = gmCfg.gmDefCfgFile
 
+ID_Popup_OpenPatient = wx.wxNewId ()
 ID_Popup_SaveDisplayLayout = wx.wxNewId()
 ID_Popup_AddPerson = wx.wxNewId()
 ID_Popup_AddAddressForPerson = wx.wxNewId()
@@ -271,66 +272,28 @@
                self.gb = gmGuiBroker.GuiBroker()
 
                self.__createdemographicgui()
+               self.__register_events ()
 
        #-----------------------------------------------------------
        def  __createdemographicgui(self):
                
#------------------------------------------------------------------------
-               #create patient list, add column headers and data
+               #create patient list
                
#-----------------------------------------------------------------------
-               patientlist_ID = wx.wxNewId()
+               self.patientlist_ID = wx.wxNewId()
                self.patientlist = wx.wxListCtrl (
                        self,
-                       patientlist_ID,
+                       self.patientlist_ID,
                        wx.wxPyDefaultPosition,
                        size=(400,10),
                        style = wx.wxLC_REPORT | wx.wxSUNKEN_BORDER | 
wx.wxLC_VRULES | wx.wxLC_HRULES
                )
-               self.patientlist.InsertColumn(0, _("Name"))
-               self.patientlist.InsertColumn(1, "")
-               self.patientlist.InsertColumn(2, _("Street"))
-               self.patientlist.InsertColumn(4, _("Place"))
-               self.patientlist.InsertColumn(5, _("Postcode"), 
wx.wxLIST_FORMAT_CENTRE)
-               self.patientlist.InsertColumn(6, _("Date of Birth"), 
wx.wxLIST_FORMAT_CENTRE)
-               self.patientlist.InsertColumn(7, _("Sex"), 
wx.wxLIST_FORMAT_CENTRE)
-               self.patientlist.InsertColumn(8, _("Home Phone"), 
wx.wxLIST_FORMAT_CENTRE)
-
                opt_val, set = gmCfg.getDBParam(
                        workplace = _whoami.get_workplace(),
                        option="widgets.demographics.patientlist.column_sizes"
                )
-               if not opt_val or len(opt_val) == 0:
-                       print 'patient list column sizes: using defaults'
-                       self.patientcolumnslist = 
['100','100','250','200','60','100','50','100']
-               else:
-                       self.patientcolumnslist = opt_val
-               print self.patientcolumnslist
-               # set column widths
-               for i in range (0,8):
-                       
self.patientlist.SetColumnWidth(i,int(self.patientcolumnslist[i]))
-#              #-------------------------------------------------------------
-#              #loop through the PatientData array and add to the list control
-#              #note the different syntax for the first coloum of each row
-#              #i.e. here > self.patientlist.InsertStringItem(x, data[0])!!
-#              #-------------------------------------------------------------
-#              items = PatientData.items()
-#              for x in range(len(items)):
-#                      key, data = items[x]
-#                      print x, data[0],data[1],data[2],data[3],data[4],data[5]
-#                      self.patientlist.InsertStringItem(x, data[0])
-#                      self.patientlist.SetStringItem(x, 1, data[1])
-#                      self.patientlist.SetStringItem(x, 2, data[2])
-#                      self.patientlist.SetStringItem(x, 3, data[3])
-#                      self.patientlist.SetStringItem(x, 4, data[4])
-#                      self.patientlist.SetStringItem(x, 5, data[5])
-#                      self.patientlist.SetStringItem(x, 6, data[6])
-#                      self.patientlist.SetStringItem(x, 7, data[7])
-#                      self.patientlist.SetItemData(x, key)
-#              #--------------------------------------------------------
-#              #note the number passed to the wx.wxColumnSorterMixin must be
-#              #the 1 based count of columns
-#              #--------------------------------------------------------
-#              self.itemDataMap = PatientData
-
+               self.patientcolumns = {_('Name'):100, _('Address'):250, _("Home 
Phone"):60, _("Sex"):50, _("Date of Birth"):60}
+               if opt_val and len(opt_val):
+                       self.patientcolumns.update (dict ([i.split (':') for i 
in opt_val]))
                sizer_top_patientlist = wx.wxBoxSizer(wx.wxHORIZONTAL)
                sizer_top_patientlist.Add(
                        self.patientlist,
@@ -341,14 +304,14 @@
                )
                # adjust layout
                self.SetSizer(sizer_top_patientlist)
-#              self.SetSizer( self.main_splitWindow)
                self.SetAutoLayout(True)
                sizer_top_patientlist.Fit(self)
-               self.__register_events()
 
        def __register_events(self):
                # patient list popup menu
                wx.EVT_RIGHT_UP(self.patientlist, 
self._on_RightClick_patientlist)
+               wx.EVT_LIST_ITEM_ACTIVATED (self.patientlist, 
self.patientlist_ID, self._on_list_click)
+               wx.EVT_MENU(self, ID_Popup_OpenPatient, 
self._on_Popup_OpenPatient)
                wx.EVT_MENU(self, ID_Popup_SaveDisplayLayout, 
self._on_PopupSaveDisplayLayout)
                wx.EVT_MENU(self, ID_Popup_AddPerson , self._on_Popup_AddPerson)
                wx.EVT_MENU(self, ID_Popup_AddAddressForPerson, 
self._on_Popup_AddAddressForPerson)
@@ -362,39 +325,42 @@
                wx.EVT_MENU(self, ID_Popup_SaveDisplayLayout, 
self._on_PopupSaveDisplayLayout)
                wx.EVT_MENU(self, ID_Popup_BuildSQL, self._on_Popup_BuildSQL)
                wx.EVT_MENU(self, ID_Popup_Help, self._on_PopupHelp)
-               #wx.EVT_MENU(self, ID_Popup_AddPerson 3, self._on_PopupThirteen)
-               #wx.EVT_MENU(self, ID_Popup_AddPerson 4, 
self._on_Popup_DeletePersonteen)
+               gmDispatcher.connect (signal=gmSignals.search_result (), 
receiver=self._on_search)
 
        def _on_RightClick_patientlist(self, event):
-#              Maximise Viewing Area
-#              Minimise Viewing Area
-#              ---------------------
-#              Add Person
-#              Add Address for person
-#              Add Family Member
-#              --------------------------
-#              Delete Person
-#              Delete Address for person
-#              Undo Delete
-#              ------------------------------------
-#              Sort A_Z
-#              Sort Z_A
-#              --------------
-#              Change Font
-#              Save Display Layout
-#              --------------------------
-#              Build SQL
-#              -------------------
-#              Help
-#              ----------------
-#              Exit
-
-               #self.log.WriteText("OnRightClick\n")
+               """
+               Maximise Viewing Area
+               Minimise Viewing Area
+               ---------------------
+               Add Person
+               Add Address for person
+               Add Family Member
+               --------------------------
+               Delete Person
+               Delete Address for person
+               Undo Delete
+               ------------------------------------
+               Sort A_Z
+               Sort Z_A
+               --------------
+               Change Font
+               Save Display Layout
+               --------------------------
+               Build SQL
+               -------------------
+               Help
+               ----------------
+               Exit
+               """
 
                
#-----------------------------------------------------------------
                # make a menu to popup over the patient list
                
#-----------------------------------------------------------------
                self.menu_patientlist = wx.wxMenu()
+               #Trigger routine to open new patient
+               item = wx.wxMenuItem(self.menu_patientlist, 
ID_Popup_OpenPatient ,"Open As Patient")
+               
item.SetBitmap(images_patient_demographics.getperson_addBitmap())
+               self.menu_patientlist.AppendItem(item)
                #Trigger routine to clear all textboxes to add entirely new 
person
                item = wx.wxMenuItem(self.menu_patientlist, ID_Popup_AddPerson 
,"Add Person")
                
item.SetBitmap(images_patient_demographics.getperson_addBitmap())
@@ -447,13 +413,26 @@
                item.SetBitmap(images_patient_demographics.gethelpBitmap())
                self.menu_patientlist.AppendItem(item)
                self.menu_patientlist.AppendSeparator()
-
-
                # Popup the menu.  If an item is selected then its handler
                # will be called before PopupMenu returns.
                self.PopupMenu(self.menu_patientlist, event.GetPosition())
                self.menu_patientlist.Destroy()
 
+       def _on_list_click (self, event):
+               self.__load_patient (self.ids_in_list[event.getIndex ()])
+
+       def _on_Popup_OpenPatient (self, event):
+               sel = self.patientlist.GetNextItem (-1, wx.wxLIST_NEXT_ALL, 
wx.wxLIST_STATE_SELECTED)
+               if sel > -1:
+                       self.__load_patient (self.ids_in_list[sel])
+
+       def __load_patient (self, patient):
+               wx.wxBeginBusyCursor ()
+               try:
+                       gmPatient.set_active_patient (patient)
+               except:
+                       _log.LogException ("loading patient %d" % 
patient['id'], sys.exc_info (), verbose=0)
+               wx.wxEndBusyCursor ()
 
        def _on_Popup_AddPerson(self, event):
               print 'I\'m adding a person.....'
@@ -531,24 +510,51 @@
                )
                wx.wxEndBusyCursor()
        #----------------------------------------------------------
-       def _on_PopupEleven(self, event):
-               self.log.WriteText("Popup nine\n")
-
-       def _on_PopupTwelve(self, event):
-               self.log.WriteText("Popup nine\n")
-
-       def _on_PopupThirteen(self, event):
-               self.log.WriteText("Popup nine\n")
-
-       def _on_Popup_DeletePersonteen(self, event):
-               self.log.WriteText("Popup nine\n")
-
-       def _on_PopupFifteen(self, event):
-               self.log.WriteText("Popup nine\n")
+       def _on_search (self, ids, display_fields = ['name', 'dob', 
'home_address', 'gender', 'home_phone']):
+               """
+               Receives a list of gmDemographicRecord.cIdentity objects to 
display
+               """
+               n = 0
+               self.patientlist.ClearAll ()
+               trans = {'name':_('Name'), 'home_address':_('Address'), 
'gender':_('Sex'), 'home_phone':_('Home Phone'), 'dob':_("Date of Birth")}
+               for i in display_fields:
+                       if i in ['dob', 'gender', 'home_phone']:
+                               self.patientlist.InsertColumn (n, trans[i], 
wx.wxLIST_FORMAT_CENTRE)
+                       else:
+                               self.patientlist.InsertColumn (n, trans[i])
+                       
self.patientlist.SetColumnWidth(n,int(self.patientcolumns[trans[i]]))
+                       n+=1
+               try:
+                       for i in range (0, len (ids)):
+                               self.patientlist.InsertStringItem (i, getattr 
(self, '_form_%s' % display_fields[0]) (ids[i]))
+                               for j in range (1, len (display_fields)):
+                                       self.patientlist.SetStringItem (i, j, 
getattr (self, '_form_%s' % display_fields[j]) (ids[i]))
+               except:
+                       _log.LogException ("inserting into listbox", 
sys.exc_info (), verbose=0)
+               self.ids_in_list = ids
 
-       def _on_Popup_UndoDeleteteen(self, event):
-               self.log.WriteText("Popup nine\n")
+       def _form_name (self, i):
+               return _("%(lastnames)s, %(firstnames)s") % i
 
+       def _form_home_address (self, i):
+               for a in i['addresses']:
+                       if a['type'] == 'home':
+                               return _("%(number)s %(street)s %(addendum)s, 
%(city)s %(postcode)s") % a
+               if i['addresses']:
+                       return _("%(number)s %(street)s %(addendum)s, %(city)s 
%(postcode)s") % i['addresses'][0]
+               return _("[No address recorded]")
+
+       def _form_gender (self, i):
+               return i['gender']
+
+       def _form_dob (self, i):
+               return i['dob'].Format (_("%d/%m/%y"))
+
+       def _form_home_phone (self, i):
+               for c in i['comms']:
+                       if c['type'] == 'telephone':
+                               return c['url']
+               return _("No telephone")
        
 class PatientDetailWindow(wx.wxPanel):         
        def __init__(self, parent, id= -1):
@@ -573,8 +579,8 @@
                        'm': _('Male'),
                        'f': _("Female"),
                        '?': _("Unknown"),
-                       'tm': _('Trans. Male'),
-                       'tf': _('Trans. Female'),
+                       'tm': _('Transexual to Male'),
+                       'tf': _('Transexual to Female'),
                        'h':_('Hermaphrodite')
                }
                self.__createdemographicgui()
Index: wxpython/gmEMRBrowser.py
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmEMRBrowser.py,v
retrieving revision 1.6
diff -u -r1.6 gmEMRBrowser.py
--- wxpython/gmEMRBrowser.py    31 Oct 2004 00:37:13 -0000      1.6
+++ wxpython/gmEMRBrowser.py    23 Jan 2005 19:50:08 -0000
@@ -143,8 +143,7 @@
                """
                # EMR tree root item
                demos = self.__pat.get_demographic_record()
-               names = demos.get_names()
-               root_item = self.__emr_tree.AddRoot(_('%s %s EMR') % 
(names['first'], names['last']))
+               root_item = self.__emr_tree.AddRoot(_('%s %s EMR') % 
(demos['firstnames'], demos['lastnames']))
 
                # Obtain all the tree from exporter
                self.__exporter.get_historical_tree(self.__emr_tree)
Index: wxpython/gmGuiMain.py
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmGuiMain.py,v
retrieving revision 1.175
diff -u -r1.175 gmGuiMain.py
--- wxpython/gmGuiMain.py       1 Oct 2004 13:17:35 -0000       1.175
+++ wxpython/gmGuiMain.py       23 Jan 2005 19:50:09 -0000
@@ -521,9 +521,7 @@
                pat = gmPatient.gmCurrentPatient()
                if pat.is_connected():
                        demos = pat.get_demographic_record()
-                       names = demos.get_names()
-                       fname = names['first'][:1]
-                       pat_str = "%s %s.%s (%s) #%d" % (demos.getTitle()[:4], 
fname, names['last'], demos.getDOB(aFormat = 'DD.MM.YYYY'), int(pat['ID']))
+                       pat_str = "%s %s.%s (%s) #%d" % (demos['title'][:4], 
demos['firstnames'], demos['lastnames'], demos['dob'].Format (_('%d/%m/%y')), 
int(demos['id']))
                else:
                        pat_str = _('no patient')
 
Index: wxpython/gmPatSearchWidgets.py
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmPatSearchWidgets.py,v
retrieving revision 1.10
diff -u -r1.10 gmPatSearchWidgets.py
--- wxpython/gmPatSearchWidgets.py      20 Oct 2004 12:40:55 -0000      1.10
+++ wxpython/gmPatSearchWidgets.py      23 Jan 2005 19:50:09 -0000
@@ -345,7 +345,6 @@
                # FIXME: error handling
 
                self.__prev_search_term = None
-               self.__prev_pats = []
                self.__pat_picklist_col_defs = []
 
                self._lclick_count = 0
@@ -385,27 +384,6 @@
        def _on_patient_selected(self, **kwargs):
                wx.wxCallAfter(self._display_name)
        #--------------------------------------------------------
-       def SetActivePatient(self, anID = None, data = None):
-               if not gmPatient.set_active_patient(anID):
-                       _log.Log (gmLog.lErr, 'cannot change active patient')
-                       return None
-
-               # remember patient ?
-               if data is None:
-                       return True
-
-               # only unique patients
-               for prev_pat in self.__prev_pats:
-                       if prev_pat[0] == anID:
-                               return True
-               self.__prev_pats.append(data)
-
-               # and only 10 of them
-               if len(self.__prev_pats) > 10:
-                       self.__prev_pats.pop(0)
-
-               return True
-       #--------------------------------------------------------
        # utility methods
        #--------------------------------------------------------
        def _display_name(self):
@@ -476,19 +454,8 @@
                if evt.AltDown():
                        # ALT-L, ALT-P - list of previously active patients
                        if keycode in [ord('l'), ord('p')]:
-                               if self.__prev_pats == []:
-                                       return True
                                # show list
-                               dlg = cPatientPickList(parent = self)
-                               dlg.SetItems(self.__prev_pats, 
self.__pat_picklist_col_defs)
-                               result = dlg.ShowModal()
-                               dlg.Destroy()
-                               # and process selection
-                               if result > 0:
-                                       # and make our selection known to others
-                                       wx.wxBeginBusyCursor()
-                                       self.SetActivePatient(anID = result)
-                                       wx.wxEndBusyCursor()
+                               gmDispatcher.send (gmSignals.search_result (), 
ids=[])
                                return True
 
                        # ALT-N - enter new patient
@@ -506,6 +473,7 @@
                                        # show some message here ...
                                        return True
                                picklist, col_order = 
gmKVK.kvks_extract_picklist(kvks)
+                               # FIXME: make this return 
gmDemographicRecord.cIdentity
                                # show list
                                dlg = cPatientPickList(parent = self, title = 
_("please select a KVK"))
                                dlg.SetItems(picklist, col_order)
@@ -567,39 +535,8 @@
                        )
                        return None
 
-               curs = self.__conn.cursor()
-               # only one matching patient
-               if len(ids) == 1:
-                       # make our selection known to others
-                       pats_data, self.__pat_picklist_col_defs = 
self.__pat_expander(curs, ids)
-                       curs.close()
-                       if len(pats_data) == 0:
-                               wx.wxEndBusyCursor()
-                               return None
-                       self.SetActivePatient(ids[0], pats_data[0])
-                       wx.wxEndBusyCursor()
-                       return None
-
-               # more than one matching patient
-               start = time.time()
-               pats_data, self.__pat_picklist_col_defs = 
self.__pat_expander(curs, ids)
-               duration = time.time() - start
-               _log.Log (gmLog.lInfo, "patient data fetched in %3.3f seconds" 
% duration)
-               curs.close()
-               # let user select from pick list
-               picklist = cPatientPickList(parent = self)
-               picklist.SetItems(pats_data, self.__pat_picklist_col_defs)
-               picklist.Centre()
-               wx.wxEndBusyCursor()
-               result = picklist.ShowModal()
-               wx.wxBeginBusyCursor()
-               picklist.Destroy()
-               for pat in pats_data:
-                       if result == pat[0]:
-                               self.SetActivePatient(anID=result, data=pat)
-                               break
-
-               wx.wxEndBusyCursor()
+               gmDispatcher.send (gmSignals.search_result (), ids=ids)
+               wx.wxEndBusyCursor ()
                return None
 #============================================================
 # main
Index: wxpython/gmTopPanel.py
===================================================================
RCS file: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmTopPanel.py,v
retrieving revision 1.54
diff -u -r1.54 gmTopPanel.py
--- wxpython/gmTopPanel.py      17 Oct 2004 16:01:44 -0000      1.54
+++ wxpython/gmTopPanel.py      23 Jan 2005 19:50:09 -0000
@@ -323,12 +323,11 @@
        #----------------------------------------------
        def __on_patient_selected(self, **kwargs):
                demr = self.curr_pat.get_demographic_record()
-               age = demr.getMedicalAge()
+               age = demr['medical_age']
                # FIXME: if the age is below, say, 2 hours we should fire
                # a timer here that updates the age in increments of 1 minute 
... :-)
                self.txt_age.SetValue(age)
-               name = demr.get_names()
-               self.patient_selector.SetValue('%s, %s' % (name['last'], 
name['first']))
+               self.patient_selector.SetValue('%s, %s' % (demr['lastnames'], 
demr['firstnames']))
 
                # update episode selector
                self.combo_episodes.Clear()

reply via email to

[Prev in Thread] Current Thread [Next in Thread]