[Top][All Lists]
[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()
- [Gnumed-devel] Two patches,
Ian Haywood <=
- Re: [Gnumed-devel] Two patches, Karsten Hilbert, 2005/01/24
- Re: [Gnumed-devel] Two patches, Karsten Hilbert, 2005/01/25
- Re: [Gnumed-devel] Two patches, Ian Haywood, 2005/01/25
- Re: [Gnumed-devel] Two patches, Karsten Hilbert, 2005/01/25
- Re: [Gnumed-devel] Two patches, Ian Haywood, 2005/01/25
- Re: [Gnumed-devel] Two patches, Karsten Hilbert, 2005/01/26
- Re: [Gnumed-devel] Two patches, Ian Haywood, 2005/01/26
- Re: [Gnumed-devel] Two patches, Karsten Hilbert, 2005/01/26