Saturday, July 18, 2009

There is more than one way to write code

CVS systems are great. You can check in your work and others can change it e.g. for coding style. gmDeviceWidgets py was cleaned up by Karsten.

Change is here : http://cvsmonitor.gnumed.de/cvsmonitor.pl?cmd=viewBrowseVersion&version=1.14&file=gnumed/client/wxpython/gmDeviceWidgets.py&module=GNUmed.gnumed

Needless to say I did not even recognize it anymore since huge chunks of the code have been replaced :-)

Friday, July 17, 2009

GNUmed plugin development - part 13

Be aware that some things have changed. The file GNUmedDeviceParserElementTree.py has been moved from test-area/shilbert/ to business/gmDevices.py

There are now four files involved in this plugin.
- gmDevices.py in /business - responsible for the XML data extraction part
- gmDeviceWidgets.py in /wxpython - responsible for calling gmDevices.py and compiling the ui widgets
- gmCardiacDevicePluginPnl.py in /wxGladeWidgets - is the python code representation of the ui widgets
- gmCardiacDevicePluginPnl.wxg in /wxg - is the xml representation of the ui widgets

If I need to change the plugin's appearance I need to touch first gmCardiacDevicePluginPnl.wxg with wxGlade and transfer it into the python code representation gmCardiacDevicePluginPnl.py. This files gets imported and the controls filled with data in gmDeviceWidgets.py. The business logic of data compilation from and to screen is handled in gmDevices.py

Next up is getting the device data from the database instead of an XML file. For the time being it was agreed that the xml file is simply stored as yet another document. To store it GNUmed was started and the plugin 'Document import' was used. I was asked for an associated episode. 'Routine postop cardiac device checkup' was entered. 'Device check up' was chosen as document type. This was entered via the menu item 'GNUmed>Manage master data>document types'.

A quick chat with Karsten revealed the basic code:

pat = gmPerson.gmCurrentPatient()
doc_folder = pat.get_document_folder()
docs = doc_folder.get_documents()
This returns a list of cMedDoc objects which can be parsed like that:
for doc in docs:
    doc['comment'] = ... etc.
doc_folder.get_documents() returns elements ordered by clin_when so the first element is the newest one I am looking for.
for doc in docs:
             if doc['type'] == 'cardiac device checkup report':
                         selected_docs.append(doc)
if not len(selected_docs) == 0:
             # since get_documents() is sorted I simply get the first one as the most recent one
             # for now assume that the xml file provide the cardiac device context.
             # that pretty much means logical connection of leads and generator is provided in the xml
             xml = selected_docs[0].get_parts()[0].export_to_file()
             tree = etree.parse(xml)
             DevicesDisplayed = gmDevices.device_status_as_text(tree)
             for line in DevicesDisplayed:
                        text = text + line
else:
             _log.info('There are no device checkup reports in the database')
             text = _('There are no device checkup reports in the database')
To construct the line
xml = selected_docs[0].get_parts()[0].export_to_file()
one has to take a good look at the file gmMedDoc.py in /business. Using that code it is now possible to get the XML from the database and display the current device status.

Currently the XML is entered via the default the 'attach document' plugin. There is a nifty solution to a problem. How to check for documents of a type that might not yet exist in the database ?

When the cardiac device plugin is initiated the document type is automatically added to the document types in the database. The file gmDeviceWidgets holds the following code for that.
def __init__(self, *args, **kwargs):
         # check if report types exist in db, if not create them
         dtype = gmMedDoc.create_document_type('cardiac device checkup report')
         dtype.set_translation(_('cardiac device checkup report'))
The next step is to get rid of saving to a file and reading from that. Another issue is to get refresh going when entering new data.

Thursday, July 09, 2009

GNUmed plugin development - part 12

Show me the code. Part 11 talked about the xml handling. I put everything in the CVS to make it available.
That was almost two months ago. Due to time constraints I only recently picked up again. I had to realize that my python skills are very rusty and that I had to learn to read and write at the same time.

In the meantime both the XML files as well as the Parser have gone through 2 rewrites. I threw the code out where I felt it became too complex for me to handle. Check out the latest version now.

now run:
python GNUmedDeviceParserElementTree.py
and observe the following output:
-------------------------------------------------------------
Device(ICD): St. Jude Medical Atlas DR (active)   Battery: St. Jude Medical (St. Jude Medical)  Implanted: 23.03.2002


RA-lead in RA-position: Medtronic Sprint XL (active,no comment on lead) Implanted: 23.04.2009
last check: 23.04.2004 Sensing: 7mV Threshold 1.4mV Impedance: 300 Ohm

RV-lead in RV-position: Medtronic Sprint fidelis (inactive,no comment on lead) Implanted: 23.04.2009
last check: 23.04.2009" Sensing: 7mV Threshold 1.4mV Impedance: 300 Ohm

LV-lead in LV-position: St. Jude QuickSite XL (active,no comment on LV lead) Implanted: 23.04.2009
last check: 23.03.2004 Sensing: 7mV Threshold 1.4mV Impedance: 300 Ohm
Now what you see here has been parsed from the XML. No more fake input. Next step is to first replace all hardcoded English strings with gettext-equivalents so translators can have a go at them. To achieve that we will take a look at other plugins that already have that feature.

Take a look at gmMeasurementsGridPlugin.py. The first few lines look like this.

import logging

from Gnumed.wxpython import gmPlugin, gmMeasurementWidgets
from Gnumed.pycommon import gmI18N

_log = logging.getLogger('gm.ui')
_log.info(__version__)
All of the above but the second and third line were added to the top of the parser. To tell gettext where to replace I marked them like this:
_('this is a string that needs a translation') vs. 'this is a string that needs a translation'
So the _() construct identifies the strings to gettext. The following lines were added to the main section.
from Gnumed.pycommon import gmI18N
gmI18N.activate_locale()
gmI18N.install_domain()
Running the parser now displayes the same result even after all 'I am a string' occurences having been replaced by _('I am a string'). The next part will try to get the parser connected to gmCardiacDevicePlugin and get this plugin displayed in GNUmed. Once this has been accomplished the XML needs to be read from the database instead of a file.

Wednesday, July 01, 2009

How to build GNUmed packages for Windows

A few people have responded to my call for regarding packaging. Despite the fact that this only touches the Mac as no volunteers showed up for Windows or any distribution besides Debian I have continued to put up documentation on how to package GNUmed for various OS and distributions.

The last few hours I have rebuilt packages for Windows. It should have taken only 20 minutes but this time I decided to document everything in a step by step guide.

The guide can be found in the Wiki at http://wiki.gnumed.de/bin/view/Gnumed/GNUmedNSIS

If you follow that guide you should be able to roll Windows packages yourself. Anyone willing to help out with packaging new releases please let us know at gnumed-devel@gnu.org.