[med-svn] [gnumed-client] 01/06: Imported Upstream version 1.6.5+dfsg
Andreas Tille
tille at debian.org
Mon Apr 25 22:43:30 UTC 2016
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository gnumed-client.
commit 4eefb56b1028fd39fed5fe882945fdf1051354fc
Author: Andreas Tille <tille at debian.org>
Date: Tue Apr 26 00:21:19 2016 +0200
Imported Upstream version 1.6.5+dfsg
---
client/CHANGELOG | 15 ++
client/business/gmHL7.py | 162 +-------------
client/business/gmIncomingData.py | 203 +++++++++++++++++
client/doc/gm-import_incoming.1 | 119 ++++++++++
client/doc/schema/gnumed-entire_schema.html | 2 +-
client/exporters/gmPatientExporter.py | 22 +-
client/gnumed | 28 +--
client/gnumed.py | 2 +-
client/importers/__init__.py | 1 +
client/importers/gmImportIncoming.py | 225 +++++++++++++++++++
client/po/es-gnumed.mo | Bin 510162 -> 510739 bytes
client/po/es.po | 64 +++---
client/pycommon/gmPG2.py | 13 +-
client/pycommon/gmTools.py | 11 +-
.../wxgSelectablySortedDocTreePnl.py | 169 +++++++-------
client/wxpython/gmDocumentWidgets.py | 53 ++++-
client/wxpython/gmListWidgets.py | 249 ++++++++++++++++-----
client/wxpython/gmMeasurementWidgets.py | 5 +-
external-tools/gm-describe_file | 16 +-
external-tools/gm-import_incoming | 26 +++
20 files changed, 1029 insertions(+), 356 deletions(-)
diff --git a/client/CHANGELOG b/client/CHANGELOG
index f2a542f..715f572 100644
--- a/client/CHANGELOG
+++ b/client/CHANGELOG
@@ -6,6 +6,21 @@
# rel-1-6-patches
------------------------------------------------
+ 1.6.5
+
+IMPROVED: list context menu: operate on _selected_ rows
+
+ 1.6.4
+
+FIX: EMR journal exporter on Windows [thanks Marc]
+
+IMPROVED: by-org sort mode in document tree
+IMPROVED: file describer script
+IMPROVED: STIKO tetanus auto hint
+IMPROVED: ES translation [thanks Uwe]
+
+NEW: gm-import_incoming script for external use
+
1.6.3
FIX: exception on creating invoice from bill [thanks Marc]
diff --git a/client/business/gmHL7.py b/client/business/gmHL7.py
index 8e8fef8..784c7a8 100644
--- a/client/business/gmHL7.py
+++ b/client/business/gmHL7.py
@@ -28,6 +28,7 @@ from Gnumed.pycommon import gmBusinessDBObject
from Gnumed.pycommon import gmPG2
from Gnumed.pycommon import gmDateTime
+from Gnumed.business import gmIncomingData
from Gnumed.business import gmPathLab
from Gnumed.business import gmPerson
from Gnumed.business import gmPraxis
@@ -171,156 +172,6 @@ HL7_GENDERS = {
}
#============================================================
-# class to handle unmatched incoming clinical data
-#------------------------------------------------------------
-_SQL_get_incoming_data = u"""SELECT * FROM clin.v_incoming_data_unmatched WHERE %s"""
-
-class cIncomingData(gmBusinessDBObject.cBusinessDBObject):
- """Represents items of incoming data, say, HL7 snippets."""
-
- _cmd_fetch_payload = _SQL_get_incoming_data % u"pk_incoming_data_unmatched = %s"
- _cmds_store_payload = [
- u"""UPDATE clin.incoming_data_unmatched SET
- fk_patient_candidates = %(pk_patient_candidates)s,
- fk_identity_disambiguated = %(pk_identity_disambiguated)s,
- fk_provider_disambiguated = %(pk_provider_disambiguated)s,
- request_id = gm.nullify_empty_string(%(request_id)s),
- firstnames = gm.nullify_empty_string(%(firstnames)s),
- lastnames = gm.nullify_empty_string(%(lastnames)s),
- dob = %(dob)s,
- postcode = gm.nullify_empty_string(%(postcode)s),
- other_info = gm.nullify_empty_string(%(other_info)s),
- type = gm.nullify_empty_string(%(data_type)s),
- gender = gm.nullify_empty_string(%(gender)s),
- requestor = gm.nullify_empty_string(%(requestor)s),
- external_data_id = gm.nullify_empty_string(%(external_data_id)s),
- comment = gm.nullify_empty_string(%(comment)s)
- WHERE
- pk = %(pk_incoming_data_unmatched)s
- AND
- xmin = %(xmin_incoming_data_unmatched)s
- RETURNING
- xmin as xmin_incoming_data_unmatched,
- octet_length(data) as data_size
- """
- ]
- # view columns that can be updated:
- _updatable_fields = [
- u'pk_patient_candidates',
- u'request_id', # request ID as found in <data>
- u'firstnames',
- u'lastnames',
- u'dob',
- u'postcode',
- u'other_info', # other identifying info in .data
- u'data_type',
- u'gender',
- u'requestor', # Requestor of data (e.g. who ordered test results) if available in source data.
- u'external_data_id', # ID of content of .data in external system (e.g. importer) where appropriate
- u'comment', # a free text comment on this row, eg. why is it here, error logs etc
- u'pk_identity_disambiguated',
- u'pk_provider_disambiguated' # The provider the data is relevant to.
- ]
- #--------------------------------------------------------
- def format(self):
- return u'%s' % self
- #--------------------------------------------------------
- def _format_patient_identification(self):
- tmp = u'%s %s %s' % (
- gmTools.coalesce(self._payload[self._idx['lastnames']], u'', u'last=%s'),
- gmTools.coalesce(self._payload[self._idx['firstnames']], u'', u'first=%s'),
- gmTools.coalesce(self._payload[self._idx['gender']], u'', u'gender=%s')
- )
- if self._payload[self._idx['dob']] is not None:
- tmp += u' dob=%s' % gmDateTime.pydt_strftime(self._payload[self._idx['dob']], '%Y %b %d')
- return tmp
-
- patient_identification = property(_format_patient_identification, lambda x:x)
- #--------------------------------------------------------
- def update_data_from_file(self, fname=None):
- # sanity check
- if not (os.access(fname, os.R_OK) and os.path.isfile(fname)):
- _log.error('[%s] is not a readable file' % fname)
- return False
-
- gmPG2.file2bytea (
- query = u"UPDATE clin.incoming_data_unmatched SET data = %(data)s::bytea WHERE pk = %(pk)s",
- filename = fname,
- args = {'pk': self.pk_obj}
- )
-
- # must update XMIN now ...
- self.refetch_payload()
- return True
-
- #--------------------------------------------------------
- def export_to_file(self, aChunkSize=0, filename=None):
-
- if self._payload[self._idx['data_size']] == 0:
- return None
-
- if self._payload[self._idx['data_size']] is None:
- return None
-
- if filename is None:
- filename = gmTools.get_unique_filename(prefix = 'gm-incoming_data_unmatched-')
-
- success = gmPG2.bytea2file (
- data_query = {
- 'cmd': u'SELECT substring(data from %(start)s for %(size)s) FROM clin.incoming_data_unmatched WHERE pk = %(pk)s',
- 'args': {'pk': self.pk_obj}
- },
- filename = filename,
- chunk_size = aChunkSize,
- data_size = self._payload[self._idx['data_size']]
- )
-
- if not success:
- return None
-
- return filename
-
- #--------------------------------------------------------
- def lock(self, exclusive=False):
- return gmPG2.lock_row(table = u'clin.incoming_data_unmatched', pk = self.pk_obj, exclusive = exclusive)
-
- #--------------------------------------------------------
- def unlock(self, exclusive=False):
- return gmPG2.unlock_row(table = u'clin.incoming_data_unmatched', pk = self.pk_obj, exclusive = exclusive)
-
-#------------------------------------------------------------
-def get_incoming_data(order_by=None):
- if order_by is None:
- order_by = u'true'
- else:
- order_by = u'true ORDER BY %s' % order_by
- cmd = _SQL_get_incoming_data % order_by
- rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
- return [ cIncomingData(row = {'data': r, 'idx': idx, 'pk_field': 'pk_incoming_data_unmatched'}) for r in rows ]
-
-#------------------------------------------------------------
-def create_incoming_data(data_type, filename):
- args = {'typ': data_type}
- cmd = u"""
- INSERT INTO clin.incoming_data_unmatched (type, data)
- VALUES (%(typ)s, 'new data'::bytea)
- RETURNING pk"""
- rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
- pk = rows[0]['pk']
- incoming = cIncomingData(aPK_obj = pk)
- if not incoming.update_data_from_file(fname = filename):
- delete_incoming_data(incoming_data = pk)
- return None
- return incoming
-
-#------------------------------------------------------------
-def delete_incoming_data(pk_incoming_data=None):
- args = {'pk': pk_incoming_data}
- cmd = u"DELETE FROM clin.incoming_data_unmatched WHERE pk = %(pk)s"
- gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
- return True
-
-#============================================================
# public API
#============================================================
def extract_HL7_from_XML_CDATA(filename, xml_path, target_dir=None):
@@ -557,7 +408,7 @@ def stage_single_PID_hl7_file(filename, source=None, encoding='utf8'):
# stage
try:
- incoming = create_incoming_data(u'HL7%s' % gmTools.coalesce(source, u'', u' (%s)'), filename)
+ incoming = gmIncomingData.create_incoming_data(u'HL7%s' % gmTools.coalesce(source, u'', u' (%s)'), filename)
if incoming is None:
_log.error(u'cannot stage PID file: %s', filename)
root_logger.removeHandler(local_logger)
@@ -654,7 +505,7 @@ def process_staged_single_PID_hl7_file(staged_item):
try:
success = __import_single_PID_hl7_file(filename, emr = emr)
if success:
- delete_incoming_data(pk_incoming_data = staged_item['pk_incoming_data_unmatched'])
+ gmIncomingData.delete_incoming_data(pk_incoming_data = staged_item['pk_incoming_data_unmatched'])
staged_item.unlock()
root_logger.removeHandler(import_logger)
return True, log_name
@@ -1367,7 +1218,7 @@ def __stage_MSH_as_incoming_data(filename, source=None, logfile=None):
del raw_hl7
# import file
- incoming = create_incoming_data(u'HL7%s' % gmTools.coalesce(source, u'', u' (%s)'), filename)
+ incoming = gmIncomingData.create_incoming_data(u'HL7%s' % gmTools.coalesce(source, u'', u' (%s)'), filename)
if incoming is None:
return None
incoming.update_data_from_file(fname = filename)
@@ -1441,10 +1292,6 @@ if __name__ == "__main__":
#for name in PID_fnames:
# print " ", name
#-------------------------------------------------------
- def test_incoming_data():
- for d in get_incoming_data():
- print d
- #-------------------------------------------------------
def test_stage_hl7_from_xml():
hl7 = extract_HL7_from_XML_CDATA(sys.argv[2], u'.//Message')
print "HL7:", hl7
@@ -1527,7 +1374,6 @@ if __name__ == "__main__":
#-------------------------------------------------------
#test_import_HL7(sys.argv[2])
#test_xml_extract()
- #test_incoming_data()
#test_stage_hl7_from_xml()
#test_stage_hl7()
#test_format_hl7_message()
diff --git a/client/business/gmIncomingData.py b/client/business/gmIncomingData.py
new file mode 100644
index 0000000..37a1c64
--- /dev/null
+++ b/client/business/gmIncomingData.py
@@ -0,0 +1,203 @@
+# -*- coding: utf-8 -*-
+"""Handling of <INCOMING> area."""
+#============================================================
+__author__ = "K.Hilbert <Karsten.Hilbert at gmx.net>"
+__license__ = "GPL v2 or later"
+
+
+import sys
+import os
+import logging
+
+
+if __name__ == '__main__':
+ sys.path.insert(0, '../../')
+
+from Gnumed.pycommon import gmI18N
+if __name__ == '__main__':
+ gmI18N.activate_locale()
+ gmI18N.install_domain()
+from Gnumed.pycommon import gmTools
+from Gnumed.pycommon import gmBusinessDBObject
+from Gnumed.pycommon import gmPG2
+from Gnumed.pycommon import gmDateTime
+
+
+_log = logging.getLogger('gm.import')
+
+#============================================================
+# class to handle unmatched incoming clinical data
+#------------------------------------------------------------
+_SQL_get_incoming_data = u"""SELECT * FROM clin.v_incoming_data_unmatched WHERE %s"""
+
+class cIncomingData(gmBusinessDBObject.cBusinessDBObject):
+ """Represents items of incoming data, say, HL7 snippets."""
+
+ _cmd_fetch_payload = _SQL_get_incoming_data % u"pk_incoming_data_unmatched = %s"
+ _cmds_store_payload = [
+ u"""UPDATE clin.incoming_data_unmatched SET
+ fk_patient_candidates = %(pk_patient_candidates)s,
+ fk_identity_disambiguated = %(pk_identity_disambiguated)s,
+ fk_provider_disambiguated = %(pk_provider_disambiguated)s,
+ request_id = gm.nullify_empty_string(%(request_id)s),
+ firstnames = gm.nullify_empty_string(%(firstnames)s),
+ lastnames = gm.nullify_empty_string(%(lastnames)s),
+ dob = %(dob)s,
+ postcode = gm.nullify_empty_string(%(postcode)s),
+ other_info = gm.nullify_empty_string(%(other_info)s),
+ type = gm.nullify_empty_string(%(data_type)s),
+ gender = gm.nullify_empty_string(%(gender)s),
+ requestor = gm.nullify_empty_string(%(requestor)s),
+ external_data_id = gm.nullify_empty_string(%(external_data_id)s),
+ comment = gm.nullify_empty_string(%(comment)s)
+ WHERE
+ pk = %(pk_incoming_data_unmatched)s
+ AND
+ xmin = %(xmin_incoming_data_unmatched)s
+ RETURNING
+ xmin as xmin_incoming_data_unmatched,
+ octet_length(data) as data_size
+ """
+ ]
+ # view columns that can be updated:
+ _updatable_fields = [
+ u'pk_patient_candidates',
+ u'request_id', # request ID as found in <data>
+ u'firstnames',
+ u'lastnames',
+ u'dob',
+ u'postcode',
+ u'other_info', # other identifying info in .data
+ u'data_type',
+ u'gender',
+ u'requestor', # Requestor of data (e.g. who ordered test results) if available in source data.
+ u'external_data_id', # ID of content of .data in external system (e.g. importer) where appropriate
+ u'comment', # a free text comment on this row, eg. why is it here, error logs etc
+ u'pk_identity_disambiguated',
+ u'pk_provider_disambiguated' # The provider the data is relevant to.
+ ]
+ #--------------------------------------------------------
+ def format(self):
+ return u'%s' % self
+ #--------------------------------------------------------
+ def _format_patient_identification(self):
+ tmp = u'%s %s %s' % (
+ gmTools.coalesce(self._payload[self._idx['lastnames']], u'', u'last=%s'),
+ gmTools.coalesce(self._payload[self._idx['firstnames']], u'', u'first=%s'),
+ gmTools.coalesce(self._payload[self._idx['gender']], u'', u'gender=%s')
+ )
+ if self._payload[self._idx['dob']] is not None:
+ tmp += u' dob=%s' % gmDateTime.pydt_strftime(self._payload[self._idx['dob']], '%Y %b %d')
+ return tmp
+
+ patient_identification = property(_format_patient_identification, lambda x:x)
+
+ #--------------------------------------------------------
+ def update_data_from_file(self, fname=None):
+ # sanity check
+ if not (os.access(fname, os.R_OK) and os.path.isfile(fname)):
+ _log.error('[%s] is not a readable file' % fname)
+ return False
+
+ _log.debug('updating [pk=%s] from [%s]', self.pk_obj, fname)
+ gmPG2.file2bytea (
+ query = u"UPDATE clin.incoming_data_unmatched SET data = %(data)s::bytea WHERE pk = %(pk)s",
+ filename = fname,
+ args = {'pk': self.pk_obj}
+ )
+
+ # must update XMIN now ...
+ self.refetch_payload()
+ return True
+
+ #--------------------------------------------------------
+ def export_to_file(self, aChunkSize=0, filename=None):
+
+ if self._payload[self._idx['data_size']] == 0:
+ return None
+
+ if self._payload[self._idx['data_size']] is None:
+ return None
+
+ if filename is None:
+ filename = gmTools.get_unique_filename(prefix = 'gm-incoming_data_unmatched-')
+
+ success = gmPG2.bytea2file (
+ data_query = {
+ 'cmd': u'SELECT substring(data from %(start)s for %(size)s) FROM clin.incoming_data_unmatched WHERE pk = %(pk)s',
+ 'args': {'pk': self.pk_obj}
+ },
+ filename = filename,
+ chunk_size = aChunkSize,
+ data_size = self._payload[self._idx['data_size']]
+ )
+
+ if not success:
+ return None
+
+ return filename
+
+ #--------------------------------------------------------
+ def lock(self, exclusive=False):
+ return gmPG2.lock_row(table = u'clin.incoming_data_unmatched', pk = self.pk_obj, exclusive = exclusive)
+
+ #--------------------------------------------------------
+ def unlock(self, exclusive=False):
+ return gmPG2.unlock_row(table = u'clin.incoming_data_unmatched', pk = self.pk_obj, exclusive = exclusive)
+
+#------------------------------------------------------------
+def get_incoming_data(order_by=None):
+ if order_by is None:
+ order_by = u'true'
+ else:
+ order_by = u'true ORDER BY %s' % order_by
+ cmd = _SQL_get_incoming_data % order_by
+ rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
+ return [ cIncomingData(row = {'data': r, 'idx': idx, 'pk_field': 'pk_incoming_data_unmatched'}) for r in rows ]
+
+#------------------------------------------------------------
+def create_incoming_data(data_type, filename):
+ args = {'typ': data_type}
+ cmd = u"""
+ INSERT INTO clin.incoming_data_unmatched (type, data)
+ VALUES (%(typ)s, 'new data'::bytea)
+ RETURNING pk"""
+ rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
+ pk = rows[0]['pk']
+ incoming = cIncomingData(aPK_obj = pk)
+ if not incoming.update_data_from_file(fname = filename):
+ _log.debug('cannot update newly created incoming_data record from file, deleting stub')
+ delete_incoming_data(incoming_data = pk)
+ return None
+ return incoming
+
+#------------------------------------------------------------
+def delete_incoming_data(pk_incoming_data=None):
+ args = {'pk': pk_incoming_data}
+ cmd = u"DELETE FROM clin.incoming_data_unmatched WHERE pk = %(pk)s"
+ gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
+ return True
+
+#============================================================
+# main
+#------------------------------------------------------------
+if __name__ == "__main__":
+
+ if len(sys.argv) < 2:
+ sys.exit()
+
+ if sys.argv[1] != 'test':
+ sys.exit()
+
+ from Gnumed.pycommon import gmLog2
+
+ gmDateTime.init()
+ gmTools.gmPaths()
+
+ #-------------------------------------------------------
+ def test_incoming_data():
+ for d in get_incoming_data():
+ print d
+
+ #-------------------------------------------------------
+ test_incoming_data()
diff --git a/client/doc/gm-import_incoming.1 b/client/doc/gm-import_incoming.1
new file mode 100644
index 0000000..b846298
--- /dev/null
+++ b/client/doc/gm-import_incoming.1
@@ -0,0 +1,119 @@
+.\" ========================================================
+.\" license: GPL v2 or later
+.\" ========================================================
+
+.TH gm-import_incoming 1 "2016 April 19th" "gm-import_incoming"
+
+.SH NAME
+.B gm-import_incoming
+- a script to import a file into the INCOMING area of a GNUmed database.
+
+
+.SH SYNOPSIS
+.B gm-import_incoming
+.RB [-h|?]
+.RB [--help]
+.RB [--local-import]
+.RB --file2import=FILE
+.RB --data-type=TYPE
+.RB --user=USER
+.RB [--host=HOST]
+.RB [--port=PORT]
+
+
+.SH DESCRIPTION
+.B gm-import_incoming
+is a script for importing a file into the GNUmed database
+area where incoming files are stored which have not been
+linked to any patient. A type is associated with the data for
+easier recognition.
+
+This script can be used to import for later processing fax
+images, lab data, referral letters, and similar data which
+electronically arrives on your system.
+
+
+.SH OPTIONS
+.PP
+.TP
+.B \--help, -h, -?
+Show a help screen.
+.TP
+.B \--local-import
+At startup adjust the PYTHONPATH such that the GNUmed client
+is run from a local copy of the source tree (say, an unpacked
+tarball) rather than from a proper system-wide installation.
+.TP
+.B \--file2import=FILE
+This is the file to be imported into GNUmed.
+
+Successfully imported files are renamed to FILE.imported
+within the directory in which FILE resides.
+.TP
+.B \--data-type=TYPE
+A short moniker, say, a word or two, used to describe the
+data in FILE to the user when viewed inside GNUmed.
+
+The actual content of TYPE is only limited by the execution
+environment (locale, encoding, ...) of gm-import_incoming.
+GNUmed does not need to understand what TYPE means to the
+user.
+.TP
+.B \--user=USER
+The PostgreSQL user to be used for connecting to the
+database.
+
+Note that this must currently be a GNUmed staff account. It
+will also work with the cluster superuser (usually
+"postgres") or the GNUmed database owner (typically "gm-dbo")
+but using these in production is
+.B strongly
+discouraged for data security reasons.
+.TP
+.B \--host=HOST
+The hostname of the machine PostgreSQL is running on, if
+required.
+
+If this option is not used (or set to an empty string) it
+will default to connecting over UNIX domain sockets.
+.TP
+.B \--port=PORT
+The port PostgreSQL is listening on. Default PostgreSQL
+installations listen on port 5432.
+.PP
+There are no options for database name or password.
+
+The script uses the default database name of the GNUmed
+version the script is released with thereby assuring data
+does not get imported into an older database following an
+upgrade.
+
+If a password is needed (that is, if TRUST, IDENT, or PEER
+authentication is not in use) it must be supplied by either
+setting the $PGPASSFILE environment variable or using a
+standard ~/.pgpass file.
+
+
+.SH ENVIRONMENT
+.TP
+.B PGPASSFILE
+.PP
+ http://www.postgresql.org/docs/devel/static/libpq-pgpass.html
+
+.SH FILES
+.B ~/.pgpass
+.PP
+ http://www.postgresql.org/docs/devel/static/libpq-pgpass.html
+
+
+.SH SEE ALSO
+.PP
+.TP
+.B http://wiki.gnumed.de
+Detailed Wiki-style documentation
+.TP
+.B /usr/share/doc/gnumed/
+Local documentation
+.TP
+.B man -k gm-*
+List man pages on gm-* commands.
diff --git a/client/doc/schema/gnumed-entire_schema.html b/client/doc/schema/gnumed-entire_schema.html
index 95b0a78..c421c7c 100644
--- a/client/doc/schema/gnumed-entire_schema.html
+++ b/client/doc/schema/gnumed-entire_schema.html
@@ -112,7 +112,7 @@
<body>
<!-- Primary Index -->
- <p><br><br>Dumped on 2016-04-06</p>
+ <p><br><br>Dumped on 2016-04-25</p>
<h1><a name="index">Index of database - gnumed_v21</a></h1>
<ul>
diff --git a/client/exporters/gmPatientExporter.py b/client/exporters/gmPatientExporter.py
index 2db8ded..0fedba0 100644
--- a/client/exporters/gmPatientExporter.py
+++ b/client/exporters/gmPatientExporter.py
@@ -19,10 +19,10 @@ import shutil
if __name__ == '__main__':
sys.path.insert(0, '../../')
-from Gnumed.pycommon import gmI18N
-if __name__ == '__main__':
+ from Gnumed.pycommon import gmI18N
gmI18N.activate_locale()
gmI18N.install_domain()
+
from Gnumed.pycommon import gmExceptions
from Gnumed.pycommon import gmPG2
from Gnumed.pycommon import gmTools
@@ -940,7 +940,7 @@ class cEMRJournalExporter:
f.write(u'\n')
f.write(_('Patient: %s (%s), No: %s\n') % (patient['description'], patient['gender'], patient['pk_identity']))
f.write(_('Born : %s, age: %s\n\n') % (
- patient.get_formatted_dob(format = '%Y %b %d', encoding = gmI18N.get_encoding()),
+ patient.get_formatted_dob(format = '%Y %b %d', encoding = 'utf8'),
patient.get_medical_age()
))
@@ -1017,14 +1017,14 @@ class cEMRJournalExporter:
@type target: a python object supporting the write() API
@type patient: <cPerson> instance
"""
- txt = _('Chronological EMR Journal\n')
+ txt = _(u'Chronological EMR Journal\n')
target.write(txt)
target.write(u'=' * (len(txt)-1))
target.write(u'\n')
# demographics
- target.write(_('Patient: %s (%s), No: %s\n') % (patient['description'], patient['gender'], patient['pk_identity']))
- target.write(_('Born : %s, age: %s\n\n') % (
- patient.get_formatted_dob(format = '%Y %b %d', encoding = gmI18N.get_encoding()),
+ target.write(_(u'Patient: %s (%s), No: %s\n') % (patient['description'], patient['gender'], patient['pk_identity']))
+ target.write(_(u'Born : %s, age: %s\n\n') % (
+ patient.get_formatted_dob(format = '%Y %b %d', encoding = 'utf8'),
patient.get_medical_age()
))
for ext_id in patient.external_ids:
@@ -1044,12 +1044,12 @@ class cEMRJournalExporter:
))
target.write(u'%s %10.10s %s %9.9s %s %s %s\n' % (
gmTools.u_box_vert_light,
- _('Encounter'),
+ _(u'Encounter'),
gmTools.u_box_vert_light,
- _('Doc'),
+ _(u'Doc'),
gmTools.u_box_vert_light,
gmTools.u_box_vert_light,
- _('Narrative')
+ _(u'Narrative')
))
target.write(u'%s%12.12s%s%11.11s%s%s%s%72.72s\n' % (
gmTools.u_box_T_right,
@@ -1153,7 +1153,7 @@ class cEMRJournalExporter:
gmTools.u_box_horiz_single * self.__narrative_wrap_len
))
- target.write(_('Exported: %s\n') % gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(), format = '%Y %b %d %H:%M:%S'))
+ target.write(_(u'Exported: %s\n') % gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(), format = '%Y %b %d %H:%M:%S'))
return
diff --git a/client/gnumed b/client/gnumed
index a451d6c..9ffa23a 100755
--- a/client/gnumed
+++ b/client/gnumed
@@ -23,18 +23,6 @@ if [ ! -e ${SYSCONF} ] ; then
fi
-# source systemwide startup extension shell script if it exists
-if [ -r /etc/gnumed/gnumed-startup-local.sh ] ; then
- . /etc/gnumed/gnumed-startup-local.sh
-fi
-
-
-# source local startup extension shell script if it exists
-if [ -r ${HOME}/.gnumed/scripts/gnumed-startup-local.sh ] ; then
- . ${HOME}/.gnumed/scripts/gnumed-startup-local.sh
-fi
-
-
# packages which install the GNUmed python modules into a path not
# already accessible for imports via sys.path (say, /usr/share/gnumed/)
# may need to adjust PYTHONPATH appropriately here
@@ -47,6 +35,18 @@ fi
#export PATH="${PATH}:/usr/share/gnumed/bin"
+# source systemwide startup extension shell script if it exists
+if [ -r /etc/gnumed/gnumed-startup-local.sh ] ; then
+ . /etc/gnumed/gnumed-startup-local.sh
+fi
+
+
+# source local startup extension shell script if it exists
+if [ -r ${HOME}/.gnumed/scripts/gnumed-startup-local.sh ] ; then
+ . ${HOME}/.gnumed/scripts/gnumed-startup-local.sh
+fi
+
+
# now run the client
python -m Gnumed.gnumed ${OPTIONS}
@@ -63,7 +63,7 @@ if [ -r ${HOME}/.gnumed/scripts/gnumed-shutdown-local.sh ] ; then
fi
-# sync the discs just in case so we don't lose log files
-sync
+# maybe sync the discs just in case so we don't loose log files
+#sync
#==================================================
diff --git a/client/gnumed.py b/client/gnumed.py
index e7e7bca..a6dc05d 100644
--- a/client/gnumed.py
+++ b/client/gnumed.py
@@ -88,7 +88,7 @@ against. Please run GNUmed as a non-root user.
sys.exit(1)
#----------------------------------------------------------
-current_client_version = u'1.6.3'
+current_client_version = u'1.6.5'
current_client_branch = u'1.6'
_log = None
diff --git a/client/importers/__init__.py b/client/importers/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/client/importers/__init__.py
@@ -0,0 +1 @@
+
diff --git a/client/importers/gmImportIncoming.py b/client/importers/gmImportIncoming.py
new file mode 100644
index 0000000..16e2fc6
--- /dev/null
+++ b/client/importers/gmImportIncoming.py
@@ -0,0 +1,225 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from __future__ import print_function
+
+__doc__ = """Incoming data importer."""
+__author__ = "K.Hilbert <Karsten.Hilbert at gmx.net>"
+__license__ = "GPL v2 or later"
+#============================================================
+
+# stdlib
+import sys
+import os
+
+
+# do not run as root
+if os.name in ['posix'] and os.geteuid() == 0:
+ print("""
+%s should not be run as root.
+
+Running as <root> can potentially put all your
+medical data at risk. It is strongly advised
+against. Please run as a non-root user.
+""") % sys.argv[0]
+ sys.exit(1)
+
+
+# when attempting to run from a tarball:
+if '--local-import' in sys.argv:
+ sys.path.insert(0, '../../')
+
+
+# stdlib
+import logging
+
+# GNUmed
+from Gnumed.pycommon import gmCfg2
+from Gnumed.pycommon import gmPG2
+from Gnumed.pycommon import gmTools
+
+from Gnumed.business import gmIncomingData
+
+
+_log = logging.getLogger('importer')
+
+# update man page and help text when changing options
+_known_short_options = u'h?'
+_known_long_options = [
+ u'help',
+ u'local-import',
+ u'data-type=',
+ u'file2import=',
+ u'user=',
+ u'host=',
+ u'port='
+]
+
+# update man page when changing options
+_help_text = u"""
+--------------------------------------------------------------------------------------
+Command line:
+ %s
+
+Usage synopsis:
+ %s --local-import --file2import=DATA --data-type=TYPE --user=USER --host=HOST --port=PORT
+
+ --local-import: use when running from a tarball
+
+ DATA: full path of data file to import
+ TYPE: short description of file to be shown in GNUmed (say, "fax")
+ USER: PostgreSQL user in database [%s]
+ HOST: host name of machine running PostgreSQL, if needed
+ PORT: port at which PostgreSQL listens on HOST, if needed, typically 5432
+
+See the man page for more details.
+
+Log file:
+ %s
+--------------------------------------------------------------------------------------"""
+
+#============================================================
+def import_file(data_type, filename):
+
+ _log.info('[%s]: %s', data_type, filename)
+
+ inc = gmIncomingData.create_incoming_data(data_type, filename)
+ if inc is None:
+ _log.error('import failed')
+ else:
+ _log.info('success')
+ target_filename = filename + u'.imported'
+ _log.debug('[%s] -> [%s]', filename, target_filename)
+ try:
+ os.rename(filename, target_filename)
+ except OSError:
+ _log.exception('cannot rename [%s] to [%s]', filename, target_filename)
+ return inc
+
+# u'request_id', # request ID as found in <data>
+# u'firstnames',
+# u'lastnames',
+# u'dob',
+# u'postcode',
+## u'other_info', # other identifying info in .data
+# u'gender',
+# u'requestor', # Requestor of data (e.g. who ordered test results) if available in source data.
+# u'external_data_id', # ID of content of .data in external system (e.g. importer) where appropriate
+# u'comment', # a free text comment on this row, eg. why is it here, error logs etc
+
+#============================================================
+def process_options():
+
+ if _cfg.get(option = '-h', source_order = [('cli', 'return')]):
+ show_usage()
+ sys.exit(0)
+
+ if _cfg.get(option = '-?', source_order = [('cli', 'return')]):
+ show_usage()
+ sys.exit(0)
+
+ if _cfg.get(option = '--help', source_order = [('cli', 'return')]):
+ show_usage()
+ sys.exit(0)
+
+ file2import = _cfg.get(option = '--file2import', source_order = [('cli', 'return')])
+ if file2import is None:
+ exit_with_message('ERROR: option --file2import missing')
+ if file2import is True:
+ exit_with_message('ERROR: data file missing in option --file2import=')
+ try:
+ open(file2import).close()
+ except IOError:
+ _log.exception('cannot open data file')
+ exit_with_message('ERROR: cannot open data file in option --file2import=%s' % file2import)
+
+ datatype = _cfg.get(option = '--data-type', source_order = [('cli', 'return')])
+ if datatype is None:
+ exit_with_message('ERROR: option --data-type missing')
+ if datatype is True:
+ exit_with_message('ERROR: data type missing in option --data-type=')
+ if datatype.strip() == u'':
+ exit_with_message('ERROR: invalid data type in option --data-type=>>>%s<<<' % datatype)
+
+ db_user = _cfg.get(option = '--user', source_order = [('cli', 'return')])
+ if db_user is None:
+ exit_with_message('ERROR: option --user missing')
+ if db_user is True:
+ exit_with_message('ERROR: user name missing in option --user=')
+ if db_user.strip() == u'':
+ exit_with_message('ERROR: invalid user name in option --user=>>>%s<<<' % db_user)
+
+ db_host = _cfg.get(option = '--host', source_order = [('cli', 'return')])
+ if db_host is None:
+ _log.debug('option --host not set, using <UNIX domain socket> on <localhost>')
+ elif db_host is True:
+ exit_with_message('ERROR: host name missing in option --host=')
+ elif db_host.strip() == u'':
+ _log.debug('option --host set to "", using <UNIX domain socket> on <localhost>')
+ db_host = None
+
+ db_port = _cfg.get(option = '--port', source_order = [('cli', 'return')])
+ if db_port is None:
+ _log.debug('option --port not set, using <UNIX domain socket> on <localhost>')
+ elif db_port is True:
+ exit_with_message('ERROR: port value missing in option --port=')
+ elif db_port.strip() == u'':
+ _log.debug('option --port set to "", using <UNIX domain socket> on <localhost>')
+ db_port = None
+ else:
+ converted, db_port = gmTools.input2int(initial = db_port, minval = 1024, maxval = 65535)
+ if not converted:
+ exit_with_message('ERROR: invalid port in option --port=%s (must be 1024...65535)' % db_port)
+
+ gmPG2.log_auth_environment()
+ dsn = gmPG2.make_psycopg2_dsn (
+ database = gmPG2.default_database,
+ host = db_host,
+ port = db_port,
+ user = db_user,
+ password = None # None = force not-required (TRUST/IDENT/PEER) or use-.pgpass-or-$PGPASSFILE
+ )
+ gmPG2._default_dsn = dsn
+
+ return datatype, file2import
+
+# val = _cfg.get(option = '--debug', source_order = [('cli', 'return')])
+# _cfg.set_option (
+# option = u'debug',
+# value = val
+# )
+
+#============================================================
+def exit_with_message(message=None):
+ if message is not None:
+ _log.error(message)
+ print('')
+ print(message)
+ show_usage()
+ sys.exit(1)
+
+#============================================================
+def show_usage():
+ from Gnumed.pycommon import gmLog2
+ print(_help_text % (
+ u' '.join(sys.argv),
+ sys.argv[0],
+ gmPG2.default_database,
+ gmLog2._logfile_name
+ ))
+
+#============================================================
+if __name__ == '__main__':
+
+ _cfg = gmCfg2.gmCfgData()
+ _cfg.add_cli (
+ short_options = _known_short_options,
+ long_options = _known_long_options
+ )
+ datatype, file2import = process_options()
+ inc = import_file(datatype, file2import)
+ sys.exit(0)
+
+# print('Log file:')
+# from Gnumed.pycommon import gmLog2
+# print(' %s' % gmLog2._logfile_name)
diff --git a/client/po/es-gnumed.mo b/client/po/es-gnumed.mo
index 584d1d6..842caba 100644
Binary files a/client/po/es-gnumed.mo and b/client/po/es-gnumed.mo differ
diff --git a/client/po/es.po b/client/po/es.po
index f42b49b..318e24a 100644
--- a/client/po/es.po
+++ b/client/po/es.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: es\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-04-06 11:40+0200\n"
+"POT-Creation-Date: 2016-04-18 21:14+0200\n"
"PO-Revision-Date: 2016-02-24 18:28+0100\n"
"Last-Translator: Karsten Hilbert <Karsten.Hilbert at gmx.net>\n"
"Language-Team: colombiano <es at li.org>\n"
@@ -5552,20 +5552,18 @@ msgstr "editar los detalles del contacto"
msgid "Close this dialog."
msgstr "Cerrar este diálogo."
-msgid "age"
-msgstr "edad"
+msgid "Age"
+msgstr "Edad"
-msgid "review status"
+#, fuzzy
+msgid "Review status"
msgstr "revisar estado"
-msgid "episode"
-msgstr "episodio"
-
-msgid "health issue"
-msgstr "problema de salud"
+msgid "Health issue"
+msgstr "Problema de salud"
-msgid "type"
-msgstr "tipo"
+msgid "Organization"
+msgstr "Organización"
msgid "Sort newest documents to top of tree."
msgstr "Ordenar los documentos más nuevos al principio del árbol."
@@ -5582,6 +5580,10 @@ msgstr "Ordene documentos por el problema de salud al cual pertenecen."
msgid "Sort documents by their type."
msgstr "Ordenar documentos por tipo."
+#, fuzzy
+msgid "Sort documents by the organization they are from."
+msgstr "Ordenar documentos por el episodio al que pertenecen."
+
msgid "Sort documents by"
msgstr "Ordenar documentos por"
@@ -5907,9 +5909,6 @@ msgstr "El nombre de la unidad de la organización."
msgid "The category of the organizational unit."
msgstr "La categoría de la unidad de la organización."
-msgid "Organization"
-msgstr "Organización"
-
msgid "Unit"
msgstr "Unidad de medida"
@@ -6020,13 +6019,14 @@ msgid "&Event"
msgstr "Evento"
msgid "Order by start of encounter a chart entry is linked to."
-msgstr ""
+msgstr "Ordeno por inicio del encuentro enlazado a un ingreso de cartilla."
msgid "Order by time of most recent edit of each chart entry."
-msgstr ""
+msgstr "Ordeno por hora de la más reciente edición de ingreso de cartilla."
msgid "Order by time documented as actual occurrence of each chart entry."
msgstr ""
+"Ordeno por hora documentada como ocurrencia real de cada ingreso de cartilla."
msgid "Edit the selected chart entry."
msgstr "Edito el ingreso de cartilla seleccionado."
@@ -8027,9 +8027,6 @@ msgstr "resultado correcto"
msgid "missing, reported later"
msgstr "perdido, reportado posteriormente"
-msgid "Health issue"
-msgstr "Problema de salud"
-
msgid "External care"
msgstr "Cuidado externo"
@@ -9801,6 +9798,9 @@ msgstr "cuándo"
msgid "who"
msgstr "quién"
+msgid "type"
+msgstr "tipo"
+
msgid "entry"
msgstr "ingreso"
@@ -12144,9 +12144,6 @@ msgstr "Notas"
msgid "Progress Note"
msgstr "Nota de Progreso"
-msgid "Age"
-msgstr "Edad"
-
msgid "N/A"
msgstr "N/D"
@@ -13958,17 +13955,17 @@ msgid "Find next [%s] (<CTRL-N>)"
msgstr "Encuentra siguiente [%s] (<CTRL-N>)"
msgid "Tooltips of selected rows"
-msgstr ""
+msgstr "Herramientas de ayuda sobre filas seleccionadas"
#, fuzzy
msgid "Data (formatted as text) of selected rows"
msgstr "Datos de fila (en una linea)"
msgid "Content (as one line each) of selected rows"
-msgstr ""
+msgstr "Contenido (individual por linea) de las filas seleccionadas"
msgid "Tooltip of current row"
-msgstr ""
+msgstr "Herrmienta de ayuda de la fila actual"
#, fuzzy
msgid "Data (formatted as text) of current row"
@@ -15738,6 +15735,10 @@ msgstr "ordenado por tema de salud"
msgid "sorted by type"
msgstr "ordenado por tipo"
+#, fuzzy
+msgid "sorted by organization"
+msgstr "Agregando una nueva organización"
+
msgid "Cannot load documents. No active patient."
msgstr "No se pueden cargar documentos. No hay paciente activo."
@@ -15823,6 +15824,10 @@ msgstr "%s%7s (%s):%s (%s)"
msgid "%s (unattributed episode)"
msgstr "%s (episodio no atribuido)"
+#, fuzzy
+msgid "unknown organization"
+msgstr "reacción desconocida"
+
#, python-format
msgid "part %2s"
msgstr "parte %2s"
@@ -20894,6 +20899,15 @@ msgstr "Aborto"
msgid "Abort and do NOT connect to GNUmed."
msgstr "Aborto y NO conecto a GNUMed."
+#~ msgid "age"
+#~ msgstr "edad"
+
+#~ msgid "episode"
+#~ msgstr "episodio"
+
+#~ msgid "health issue"
+#~ msgstr "problema de salud"
+
#~ msgid "Row tooltip"
#~ msgstr "Fila de herramienta de ayuda"
diff --git a/client/pycommon/gmPG2.py b/client/pycommon/gmPG2.py
index d3f837d..acc0d09 100644
--- a/client/pycommon/gmPG2.py
+++ b/client/pycommon/gmPG2.py
@@ -88,6 +88,8 @@ FixedOffsetTimezone = dbapi.tz.FixedOffsetTimezone
_default_dsn = None
_default_login = None
+default_database = 'gnumed_v21'
+
postgresql_version_string = None
postgresql_version = None # accuracy: major.minor
@@ -289,6 +291,7 @@ def set_default_client_encoding(encoding = None):
_log.info('setting default client encoding from [%s] to [%s]' % (_default_client_encoding, str(encoding)))
_default_client_encoding = encoding
return True
+
#---------------------------------------------------
def set_default_client_timezone(timezone = None):
@@ -301,6 +304,7 @@ def set_default_client_timezone(timezone = None):
_sql_set_timezone = u'set timezone to %s'
return True
+
#---------------------------------------------------
def __validate_timezone(conn=None, timezone=None):
@@ -334,6 +338,7 @@ def __validate_timezone(conn=None, timezone=None):
conn.rollback()
return is_valid
+
#---------------------------------------------------
def __expand_timezone(conn=None, timezone=None):
"""some timezone defs are abbreviations so try to expand
@@ -366,6 +371,7 @@ where
conn.rollback()
return result
+
#---------------------------------------------------
def __detect_client_timezone(conn=None):
"""This is run on the very first connection."""
@@ -412,6 +418,7 @@ def __detect_client_timezone(conn=None):
_sql_set_timezone = u"set time zone interval %s hour to minute"
_log.info('client system time zone detected as equivalent to [%s]', _default_client_timezone)
+
# =======================================================================
# login API
# =======================================================================
@@ -423,7 +430,7 @@ def __request_login_params_tui():
print "\nPlease enter the required login parameters:"
try:
login.host = prompted_input(prompt = "host ('' = non-TCP/IP)", default = '')
- login.database = prompted_input(prompt = "database", default = 'gnumed_v21')
+ login.database = prompted_input(prompt = "database", default = default_database)
login.user = prompted_input(prompt = "user name", default = '')
tmp = 'password for "%s" (not shown): ' % login.user
login.password = getpass.getpass(tmp)
@@ -502,11 +509,13 @@ def make_psycopg2_dsn(database=None, host=None, port=5432, user=None, password=N
dsn_parts.append('sslmode=prefer')
return ' '.join(dsn_parts)
+
# ------------------------------------------------------
def get_default_login():
# make sure we do have a login
get_default_dsn()
return _default_login
+
# ------------------------------------------------------
def get_default_dsn():
global _default_dsn
@@ -517,6 +526,7 @@ def get_default_dsn():
set_default_login(login=login)
return _default_dsn
+
# ------------------------------------------------------
def set_default_login(login=None):
if login is None:
@@ -563,6 +573,7 @@ def log_auth_environment():
_log.debug('$PGPASSFILE=%s not found')
except Exception:
_log.exception('cannot detect .pgpass and or $PGPASSFILE')
+
# =======================================================================
# netadata API
# =======================================================================
diff --git a/client/pycommon/gmTools.py b/client/pycommon/gmTools.py
index d1c20dc..9e5fb8b 100644
--- a/client/pycommon/gmTools.py
+++ b/client/pycommon/gmTools.py
@@ -630,7 +630,7 @@ def fname_from_path(filename):
return os.path.split(filename)[1]
#---------------------------------------------------------------------------
-def get_unique_filename(prefix=None, suffix=None, tmp_dir=None):
+def get_unique_filename(prefix=None, suffix=None, tmp_dir=None, include_timestamp=False):
"""This introduces a race condition between the file.close() and
actually using the filename.
@@ -645,6 +645,11 @@ def get_unique_filename(prefix=None, suffix=None, tmp_dir=None):
_log.warning('cannot find temporary dir [%s], using system default', tmp_dir)
tmp_dir = None
+ if include_timestamp:
+ ts = pydt.datetime.now().strftime('%m%d-%H%M%S-')
+ else:
+ ts = u''
+
kwargs = {
'dir': tmp_dir,
# make sure file gets deleted as soon as
@@ -653,9 +658,9 @@ def get_unique_filename(prefix=None, suffix=None, tmp_dir=None):
}
if prefix is None:
- kwargs['prefix'] = 'gmd-'
+ kwargs['prefix'] = 'gmd-%s' % ts
else:
- kwargs['prefix'] = prefix
+ kwargs['prefix'] = prefix + ts
if suffix in [None, u'']:
kwargs['suffix'] = '.tmp'
diff --git a/client/wxGladeWidgets/wxgSelectablySortedDocTreePnl.py b/client/wxGladeWidgets/wxgSelectablySortedDocTreePnl.py
index 9e63140..d5f4644 100644
--- a/client/wxGladeWidgets/wxgSelectablySortedDocTreePnl.py
+++ b/client/wxGladeWidgets/wxgSelectablySortedDocTreePnl.py
@@ -1,89 +1,100 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# generated by wxGlade 0.6.3 from "/home/ncq/Projekte/gm-git/gnumed/gnumed/client/wxg/wxgSelectablySortedDocTreePnl.wxg"
+# -*- coding: UTF-8 -*-
+#
+# generated by wxGlade 0.7.1
+#
import wx
-# begin wxGlade: extracode
+# begin wxGlade: dependencies
+import gettext
# end wxGlade
+# begin wxGlade: extracode
+# end wxGlade
class wxgSelectablySortedDocTreePnl(wx.ScrolledWindow):
- def __init__(self, *args, **kwds):
-
- from Gnumed.wxpython import gmDocumentWidgets
-
- # begin wxGlade: wxgSelectablySortedDocTreePnl.__init__
- kwds["style"] = wx.TAB_TRAVERSAL
- wx.ScrolledWindow.__init__(self, *args, **kwds)
- self._rbtn_sort_by_age = wx.RadioButton(self, -1, _("age"), style=wx.RB_GROUP)
- self._rbtn_sort_by_review = wx.RadioButton(self, -1, _("review status"))
- self._rbtn_sort_by_episode = wx.RadioButton(self, -1, _("episode"))
- self._rbtn_sort_by_issue = wx.RadioButton(self, -1, _("health issue"))
- self._rbtn_sort_by_type = wx.RadioButton(self, -1, _("type"))
- self._doc_tree = gmDocumentWidgets.cDocTree(self, -1)
-
- self.__set_properties()
- self.__do_layout()
-
- self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_age_selected, self._rbtn_sort_by_age)
- self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_review_selected, self._rbtn_sort_by_review)
- self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_episode_selected, self._rbtn_sort_by_episode)
- self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_issue_selected, self._rbtn_sort_by_issue)
- self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_type_selected, self._rbtn_sort_by_type)
- # end wxGlade
-
- def __set_properties(self):
- # begin wxGlade: wxgSelectablySortedDocTreePnl.__set_properties
- self.SetScrollRate(10, 10)
- self._rbtn_sort_by_age.SetToolTipString(_("Sort newest documents to top of tree."))
- self._rbtn_sort_by_age.SetValue(1)
- self._rbtn_sort_by_review.SetToolTipString(_("Sort unreviewed documents to top of tree."))
- self._rbtn_sort_by_episode.SetToolTipString(_("Sort documents by the episode they belong to."))
- self._rbtn_sort_by_issue.SetToolTipString(_("Sort documents by the health issue they belong to."))
- self._rbtn_sort_by_type.SetToolTipString(_("Sort documents by their type."))
- # end wxGlade
-
- def __do_layout(self):
- # begin wxGlade: wxgSelectablySortedDocTreePnl.__do_layout
- __szr_main = wx.BoxSizer(wx.VERTICAL)
- __szr_top_radio = wx.BoxSizer(wx.HORIZONTAL)
- __lbl_sort = wx.StaticText(self, -1, _("Sort documents by"))
- __szr_top_radio.Add(__lbl_sort, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 5)
- __szr_top_radio.Add(self._rbtn_sort_by_age, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 10)
- __szr_top_radio.Add(self._rbtn_sort_by_review, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 10)
- __szr_top_radio.Add(self._rbtn_sort_by_episode, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 10)
- __szr_top_radio.Add(self._rbtn_sort_by_issue, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 10)
- __szr_top_radio.Add(self._rbtn_sort_by_type, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 10)
- __szr_main.Add(__szr_top_radio, 0, wx.EXPAND, 0)
- __hline_middle = wx.StaticLine(self, -1)
- __szr_main.Add(__hline_middle, 0, wx.EXPAND, 0)
- __szr_main.Add(self._doc_tree, 1, wx.EXPAND, 0)
- self.SetSizer(__szr_main)
- __szr_main.Fit(self)
- # end wxGlade
-
- def _on_sort_by_age_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
- print "Event handler `_on_sort_by_age_selected' not implemented!"
- event.Skip()
-
- def _on_sort_by_review_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
- print "Event handler `_on_sort_by_review_selected' not implemented!"
- event.Skip()
-
- def _on_sort_by_episode_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
- print "Event handler `_on_sort_by_episode_selected' not implemented!"
- event.Skip()
-
- def _on_sort_by_issue_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
- print "Event handler `_on_sort_by_issue_selected' not implemented!"
- event.Skip()
-
- def _on_sort_by_type_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
- print "Event handler `_on_sort_by_type_selected' not implemented!"
- event.Skip()
-
+ def __init__(self, *args, **kwds):
+
+ from Gnumed.wxpython import gmDocumentWidgets
+
+ # begin wxGlade: wxgSelectablySortedDocTreePnl.__init__
+ kwds["style"] = wx.TAB_TRAVERSAL
+ wx.ScrolledWindow.__init__(self, *args, **kwds)
+ self._rbtn_sort_by_age = wx.RadioButton(self, wx.ID_ANY, _("Age"), style=wx.RB_GROUP)
+ self._rbtn_sort_by_review = wx.RadioButton(self, wx.ID_ANY, _("Review status"))
+ self._rbtn_sort_by_episode = wx.RadioButton(self, wx.ID_ANY, _("Episode"))
+ self._rbtn_sort_by_issue = wx.RadioButton(self, wx.ID_ANY, _("Health issue"))
+ self._rbtn_sort_by_type = wx.RadioButton(self, wx.ID_ANY, _("Type"))
+ self._rbtn_sort_by_org = wx.RadioButton(self, wx.ID_ANY, _("Organization"))
+ self._doc_tree = gmDocumentWidgets.cDocTree(self, wx.ID_ANY)
+
+ self.__set_properties()
+ self.__do_layout()
+
+ self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_age_selected, self._rbtn_sort_by_age)
+ self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_review_selected, self._rbtn_sort_by_review)
+ self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_episode_selected, self._rbtn_sort_by_episode)
+ self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_issue_selected, self._rbtn_sort_by_issue)
+ self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_type_selected, self._rbtn_sort_by_type)
+ self.Bind(wx.EVT_RADIOBUTTON, self._on_sort_by_org_selected, self._rbtn_sort_by_org)
+ # end wxGlade
+
+ def __set_properties(self):
+ # begin wxGlade: wxgSelectablySortedDocTreePnl.__set_properties
+ self.SetScrollRate(10, 10)
+ self._rbtn_sort_by_age.SetToolTipString(_("Sort newest documents to top of tree."))
+ self._rbtn_sort_by_age.SetValue(1)
+ self._rbtn_sort_by_review.SetToolTipString(_("Sort unreviewed documents to top of tree."))
+ self._rbtn_sort_by_episode.SetToolTipString(_("Sort documents by the episode they belong to."))
+ self._rbtn_sort_by_issue.SetToolTipString(_("Sort documents by the health issue they belong to."))
+ self._rbtn_sort_by_type.SetToolTipString(_("Sort documents by their type."))
+ self._rbtn_sort_by_org.SetToolTipString(_("Sort documents by the organization they are from."))
+ # end wxGlade
+
+ def __do_layout(self):
+ # begin wxGlade: wxgSelectablySortedDocTreePnl.__do_layout
+ __szr_main = wx.BoxSizer(wx.VERTICAL)
+ __szr_top_radio = wx.BoxSizer(wx.HORIZONTAL)
+ __lbl_sort = wx.StaticText(self, wx.ID_ANY, _("Sort documents by"))
+ __szr_top_radio.Add(__lbl_sort, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 5)
+ __szr_top_radio.Add(self._rbtn_sort_by_age, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10)
+ __szr_top_radio.Add(self._rbtn_sort_by_review, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10)
+ __szr_top_radio.Add(self._rbtn_sort_by_episode, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10)
+ __szr_top_radio.Add(self._rbtn_sort_by_issue, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10)
+ __szr_top_radio.Add(self._rbtn_sort_by_type, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10)
+ __szr_top_radio.Add(self._rbtn_sort_by_org, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10)
+ __szr_main.Add(__szr_top_radio, 0, wx.EXPAND, 0)
+ __hline_middle = wx.StaticLine(self, wx.ID_ANY)
+ __szr_main.Add(__hline_middle, 0, wx.EXPAND, 0)
+ __szr_main.Add(self._doc_tree, 1, wx.EXPAND, 0)
+ self.SetSizer(__szr_main)
+ __szr_main.Fit(self)
+ self.Layout()
+ # end wxGlade
+
+ def _on_sort_by_age_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
+ print "Event handler '_on_sort_by_age_selected' not implemented!"
+ event.Skip()
+
+ def _on_sort_by_review_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
+ print "Event handler '_on_sort_by_review_selected' not implemented!"
+ event.Skip()
+
+ def _on_sort_by_episode_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
+ print "Event handler '_on_sort_by_episode_selected' not implemented!"
+ event.Skip()
+
+ def _on_sort_by_issue_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
+ print "Event handler '_on_sort_by_issue_selected' not implemented!"
+ event.Skip()
+
+ def _on_sort_by_type_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
+ print "Event handler '_on_sort_by_type_selected' not implemented!"
+ event.Skip()
+
+ def _on_sort_by_org_selected(self, event): # wxGlade: wxgSelectablySortedDocTreePnl.<event_handler>
+ print "Event handler '_on_sort_by_org_selected' not implemented!"
+ event.Skip()
# end of class wxgSelectablySortedDocTreePnl
-
-
diff --git a/client/wxpython/gmDocumentWidgets.py b/client/wxpython/gmDocumentWidgets.py
index d497595..f937da5 100644
--- a/client/wxpython/gmDocumentWidgets.py
+++ b/client/wxpython/gmDocumentWidgets.py
@@ -199,7 +199,7 @@ def save_files_as_new_document(parent=None, filenames=None, document_type=None,
if reference is not None:
doc['ext_ref'] = reference
if pk_org_unit is not None:
- doc['pk_org_unit'] = gmPraxis.gmCurrentPraxisBranch()['pk_org_unit']
+ doc['pk_org_unit'] = pk_org_unit
if date_generated is not None:
doc['clin_when'] = date_generated
if comment is not None:
@@ -1443,6 +1443,11 @@ class cSelectablySortedDocTreePnl(wxgSelectablySortedDocTreePnl.wxgSelectablySor
self._doc_tree.sort_mode = 'type'
self._doc_tree.SetFocus()
self._rbtn_sort_by_type.SetValue(True)
+ #--------------------------------------------------------
+ def _on_sort_by_org_selected(self, evt):
+ self._doc_tree.sort_mode = 'org'
+ self._doc_tree.SetFocus()
+ self._rbtn_sort_by_org.SetValue(True)
#============================================================
class cDocTree(wx.TreeCtrl, gmRegetMixin.cRegetOnPaintMixin, treemixin.ExpansionState):
@@ -1452,7 +1457,7 @@ class cDocTree(wx.TreeCtrl, gmRegetMixin.cRegetOnPaintMixin, treemixin.Expansion
This acts on the current patient.
"""
- _sort_modes = ['age', 'review', 'episode', 'type', 'issue']
+ _sort_modes = ['age', 'review', 'episode', 'type', 'issue', 'org']
_root_node_labels = None
#--------------------------------------------------------
def __init__(self, parent, id, *args, **kwds):
@@ -1470,7 +1475,8 @@ class cDocTree(wx.TreeCtrl, gmRegetMixin.cRegetOnPaintMixin, treemixin.Expansion
'review': tmp % unsigned,
'episode': tmp % _('sorted by episode'),
'issue': tmp % _('sorted by health issue'),
- 'type': tmp % _('sorted by type')
+ 'type': tmp % _('sorted by type'),
+ 'org': tmp % _('sorted by organization')
}
self.root = None
@@ -1758,6 +1764,25 @@ class cDocTree(wx.TreeCtrl, gmRegetMixin.cRegetOnPaintMixin, treemixin.Expansion
self.SetItemHasChildren(intermediate_nodes[inter_label], True)
parent = intermediate_nodes[inter_label]
+ elif self.__sort_mode == 'org':
+ if doc['pk_org'] is None:
+ inter_label = _('unknown organization')
+ else:
+ inter_label = doc['organization']
+ doc_label = _('%s%7s %s:%s (%s)') % (
+ gmTools.bool2subst(doc.has_unreviewed_parts, gmTools.u_writing_hand, u'', u'?'),
+ doc['clin_when'].strftime('%m/%Y'),
+ doc['l10n_type'][:26],
+ gmTools.coalesce(initial = doc['comment'], instead = u'', template_initial = u' %s'),
+ no_parts
+ )
+ if inter_label not in intermediate_nodes:
+ intermediate_nodes[inter_label] = self.AppendItem(parent = self.root, text = inter_label)
+ self.SetItemBold(intermediate_nodes[inter_label], bold = True)
+ self.SetItemPyData(intermediate_nodes[inter_label], None)
+ self.SetItemHasChildren(intermediate_nodes[inter_label], True)
+ parent = intermediate_nodes[inter_label]
+
else:
doc_label = _('%s%7s %s:%s (%s)') % (
gmTools.bool2subst(doc.has_unreviewed_parts, gmTools.u_writing_hand, u'', u'?'),
@@ -1819,7 +1844,7 @@ class cDocTree(wx.TreeCtrl, gmRegetMixin.cRegetOnPaintMixin, treemixin.Expansion
# expand intermediate nodes as well
if self.__expanded_nodes is None:
# but only if there are any
- if self.__sort_mode in ['episode', 'type', 'issue']:
+ if self.__sort_mode in ['episode', 'type', 'issue', 'org']:
for key in intermediate_nodes.keys():
self.Expand(intermediate_nodes[key])
@@ -1914,6 +1939,26 @@ class cDocTree(wx.TreeCtrl, gmRegetMixin.cRegetOnPaintMixin, treemixin.Expansion
return 1
return 1
+ elif self.__sort_mode == 'org':
+ if (data1['organization'] is None) and (data2['organization'] is None):
+ return 0
+ if (data1['organization'] is None) and (data2['organization'] is not None):
+ return 1
+ if (data1['organization'] is not None) and (data2['organization'] is None):
+ return -1
+ txt1 = u'%s %s' % (data1['organization'], data1['unit'])
+ txt2 = u'%s %s' % (data2['organization'], data2['unit'])
+ if txt1 < txt2:
+ return -1
+ if txt1 == txt2:
+ # inner sort: reverse by date
+ if data1[date_field] > data2[date_field]:
+ return -1
+ if data1[date_field] == data2[date_field]:
+ return 0
+ return 1
+ return 1
+
else:
_log.error('unknown document sort mode [%s], reverse-sorting by age', self.__sort_mode)
# reverse sort by date
diff --git a/client/wxpython/gmListWidgets.py b/client/wxpython/gmListWidgets.py
index 14a70d5..fa12810 100644
--- a/client/wxpython/gmListWidgets.py
+++ b/client/wxpython/gmListWidgets.py
@@ -24,9 +24,11 @@ import logging
import thread
import time
import locale
+import os
+import io
+import csv
import re as regex
-#import io
-#import csv
+import datetime as pydt
import wx
@@ -34,6 +36,7 @@ import wx.lib.mixins.listctrl as listmixins
from Gnumed.pycommon import gmTools
+from Gnumed.pycommon import gmDispatcher
_log = logging.getLogger('gm.list_ui')
@@ -1139,6 +1142,7 @@ class cItemPickerDlg(wxgItemPickerDlg.wxgItemPickerDlg):
self._LCTRL_right.set_column_widths()
# print u'%s <-> %s (items)' % (self._LCTRL_left.ItemCount, self._LCTRL_right.ItemCount)
# print u'%s <-> %s (data)' % (len(self._LCTRL_left.data), len(self._LCTRL_right.data))
+
#------------------------------------------------------------
def __remove_selected_picks(self):
if self._LCTRL_right.get_selected_items(only_one = True) == -1:
@@ -1152,6 +1156,7 @@ class cItemPickerDlg(wxgItemPickerDlg.wxgItemPickerDlg):
# print u'%s <-> %s (items)' % (self._LCTRL_left.ItemCount, self._LCTRL_right.ItemCount)
# print u'%s <-> %s (data)' % (len(self._LCTRL_left.data), len(self._LCTRL_right.data))
+
#------------------------------------------------------------
# event handlers
#------------------------------------------------------------
@@ -1473,7 +1478,7 @@ class cReportListCtrl(listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorter
labels = []
for col_idx in range(self.ColumnCount):
col = self.GetColumn(col = col_idx)
- labels.append(col.GetText())
+ labels.append(col.Text)
return labels
column_labels = property(get_column_labels, lambda x:x)
@@ -1580,6 +1585,12 @@ class cReportListCtrl(listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorter
#------------------------------------------------------------
def __show_context_menu(self, item_idx):
+ if item_idx == -1:
+ return
+
+ if self.ItemCount == 0:
+ return
+
items = self.selected_items
if self.__is_single_selection:
if items is None:
@@ -1634,12 +1645,27 @@ class cReportListCtrl(listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorter
self._rclicked_row_cells.append(cell_content)
self._rclicked_row_cells_w_hdr.append(u'%s: %s' % (col_header, cell_content))
-# # export area
-# exp_menu = wx.Menu()
-# menu_item = exp_menu.Append(-1, _('&All rows as CSV'))
-# self.Bind(wx.EVT_MENU, self._all_rows2export_area, menu_item)
-# menu_item = exp_menu.Append(-1, _('&Selected rows as CSV'))
-# self.Bind(wx.EVT_MENU, self._selected_rows2export_area, menu_item)
+ # save to file
+ save_menu = wx.Menu()
+ menu_item = save_menu.Append(-1, _('&All rows'))
+ self.Bind(wx.EVT_MENU, self._all_rows2file, menu_item)
+ menu_item = save_menu.Append(-1, _('All rows as &CSV'))
+ self.Bind(wx.EVT_MENU, self._all_rows2csv, menu_item)
+ menu_item = save_menu.Append(-1, _('&Tooltips of all rows'))
+ self.Bind(wx.EVT_MENU, self._all_row_tooltips2file, menu_item)
+ menu_item = save_menu.Append(-1, _('&Data of all rows'))
+ self.Bind(wx.EVT_MENU, self._all_row_data2file, menu_item)
+
+ if no_of_selected_items > 1:
+ save_menu.AppendSeparator()
+ menu_item = save_menu.Append(-1, _('&Selected rows'))
+ self.Bind(wx.EVT_MENU, self._selected_rows2file, menu_item)
+ menu_item = save_menu.Append(-1, _('&Selected rows as CSV'))
+ self.Bind(wx.EVT_MENU, self._selected_rows2csv, menu_item)
+ menu_item = save_menu.Append(-1, _('&Tooltips of selected rows'))
+ self.Bind(wx.EVT_MENU, self._selected_row_tooltips2file, menu_item)
+ menu_item = save_menu.Append(-1, _('&Data of selected rows'))
+ self.Bind(wx.EVT_MENU, self._selected_row_data2file, menu_item)
# 1) set clipboard to item
clip_menu = wx.Menu()
@@ -1751,8 +1777,7 @@ class cReportListCtrl(listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorter
clip_add_menu.AppendMenu(-1, _(u'Column &%s (current row): %s') % (col_idx+1, col_header), col_add_menu)
# 3) copy item to export area
- #export_area_menu = wx.Menu()
- # put into export area
+ # put into file
# current row
# - fields as one line
# - fields as list
@@ -1766,7 +1791,8 @@ class cReportListCtrl(listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorter
# send signal
# show menu
- self._context_menu.AppendMenu(-1, _('Copy to e&xport area...'), exp_menu)
+ #self._context_menu.AppendMenu(-1, _('Copy to e&xport area...'), exp_menu)
+ self._context_menu.AppendMenu(-1, _('&Save to file...'), save_menu)
self._context_menu.AppendMenu(-1, _('&Copy to clipboard...'), clip_menu)
self._context_menu.AppendMenu(-1, _('Append (&+) to clipboard...'), clip_add_menu)
@@ -2020,44 +2046,167 @@ class cReportListCtrl(listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorter
evt.Skip()
self.__search_match()
-# #------------------------------------------------------------
-# def _all_rows2export_area(self, evt):
-# col_labels = self.column_labels
-#
-# csv_name = gmTools.get_unique_filename(suffix = '.csv')
-# csv_file = io.open(csv_name, mode = 'wt', encoding = 'utf8')
-# csv_writer = csv.DictWriter(csv_file, col_labels)
-# csv_writer.writeheader()
-#
-# for item_idx in range(self.ItemCount):
-# row_dict = {}
-# for col_idx in range(self.ColumnCount):
-# row_dict[col_labels[col_idx]] = self.GetItem(item_idx, col_idx).Text
-# csv_writer.writerow(row_dict)
-#
-# csv_file.close()
-#
-# # signal export area
-#
-# #------------------------------------------------------------
-# def _selected_rows2export_area(self, evt):
-# col_labels = self.column_labels
-#
-# csv_name = gmTools.get_unique_filename(suffix = '.csv')
-# csv_file = io.open(csv_name, mode = 'wb', encoding = 'utf8')
-# csv_writer = csv.DictWriter(csv_file, col_labels)
-# csv_writer.writeheader()
-#
-# for item_idx in self.selected_items:
-# row_dict = {}
-# for col_idx in range(self.ColumnCount):
-# row_dict[col_labels[col_idx]] = self.GetItem(item_idx, col_idx).Text
-# csv_writer.writerow(row_dict)
-#
-# csv_file.close()
-#
-# # signal export area
-#
+ #------------------------------------------------------------
+ def _all_rows2file(self, evt):
+
+ txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-all_rows-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
+ txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
+
+ col_labels = self.column_labels
+ line = u'%s' % u' || '.join(col_labels)
+ txt_file.write(u'%s\n' % line)
+ txt_file.write((u'=' * len(line)) + u'\n')
+
+ for item_idx in range(self.ItemCount):
+ fields = []
+ for col_idx in range(self.ColumnCount):
+ fields.append(self.GetItem(item_idx, col_idx).Text)
+ txt_file.write(u'%s\n' % u' || '.join(fields))
+
+ txt_file.close()
+ gmDispatcher.send(signal = 'statustext', msg = _('All rows saved to [%s].') % txt_name)
+
+ #------------------------------------------------------------
+ def _all_rows2csv(self, evt):
+
+ csv_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-all_rows-%s.csv' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
+ csv_file = io.open(csv_name, mode = 'wb')
+
+ csv_writer = csv.writer(csv_file)
+ csv_writer.writerow([ l.encode('utf-8') for l in self.column_labels ])
+ for item_idx in range(self.ItemCount):
+ fields = []
+ for col_idx in range(self.ColumnCount):
+ fields.append(self.GetItem(item_idx, col_idx).Text)
+ csv_writer.writerow([ f.encode('utf-8') for f in fields ])
+
+ csv_file.close()
+ gmDispatcher.send(signal = 'statustext', msg = _('All rows saved to [%s].') % csv_name)
+
+ #------------------------------------------------------------
+ def _all_row_tooltips2file(self, evt):
+
+ if (self.__data is None) or (self.__item_tooltip_callback is None):
+ return
+
+ txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_tooltips-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
+ txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
+
+ for data in self.data:
+ tt = self.__item_tooltip_callback(data)
+ if tt is None:
+ continue
+ txt_file.write(u'%s\n\n' % tt)
+
+ txt_file.close()
+ gmDispatcher.send(signal = 'statustext', msg = _('All tooltips saved to [%s].') % txt_name)
+
+ #------------------------------------------------------------
+ def _all_row_data2file(self, evt):
+
+ if self.__data is None:
+ return
+
+ txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_data-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
+ txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
+
+ for data in self.data:
+ if hasattr(data, 'format'):
+ txt = data.format()
+ if type(txt) is list:
+ txt = u'\n'.join(txt)
+ else:
+ txt = u'%s' % data
+ txt_file.write(u'%s\n\n' % txt)
+
+ txt_file.close()
+ gmDispatcher.send(signal = 'statustext', msg = _('All data saved to [%s].') % txt_name)
+
+ #------------------------------------------------------------
+ def _selected_rows2file(self, evt):
+
+ txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-some_rows-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
+ txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
+
+ col_labels = self.column_labels
+ line = u'%s' % u' || '.join(col_labels)
+ txt_file.write(u'%s\n' % line)
+ txt_file.write((u'=' * len(line)) + u'\n')
+
+ items = self.selected_items
+ if self.__is_single_selection:
+ items = [items]
+
+ for item_idx in items:
+ fields = []
+ for col_idx in range(self.ColumnCount):
+ fields.append(self.GetItem(item_idx, col_idx).Text)
+ txt_file.write(u'%s\n' % u' || '.join(fields))
+
+ txt_file.close()
+ gmDispatcher.send(signal = 'statustext', msg = _('Selected rows saved to [%s].') % txt_name)
+
+ #------------------------------------------------------------
+ def _selected_rows2csv(self, evt):
+
+ csv_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-some_rows-%s.csv' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
+ csv_file = io.open(csv_name, mode = 'wb')
+
+ csv_writer = csv.writer(csv_file)
+ csv_writer.writerow([ l.encode('utf-8') for l in self.column_labels ])
+
+ items = self.selected_items
+ if self.__is_single_selection:
+ items = [items]
+
+ for item_idx in items:
+ fields = []
+ for col_idx in range(self.ColumnCount):
+ fields.append(self.GetItem(item_idx, col_idx).Text)
+ csv_writer.writerow([ f.encode('utf-8') for f in fields ])
+
+ csv_file.close()
+ gmDispatcher.send(signal = 'statustext', msg = _('Selected rows saved to [%s].') % csv_name)
+
+ #------------------------------------------------------------
+ def _selected_row_tooltips2file(self, evt):
+
+ if (self.__data is None) or (self.__item_tooltip_callback is None):
+ return
+
+ txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_tooltips-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
+ txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
+
+ for data in self.selected_item_data:
+ tt = self.__item_tooltip_callback(data)
+ if tt is None:
+ continue
+ txt_file.write(u'%s\n\n' % tt)
+
+ txt_file.close()
+ gmDispatcher.send(signal = 'statustext', msg = _('Selected tooltips saved to [%s].') % txt_name)
+
+ #------------------------------------------------------------
+ def _selected_row_data2file(self, evt):
+
+ if self.__data is None:
+ return
+
+ txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_data-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
+ txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
+
+ for data in self.selected_item_data:
+ if hasattr(data, 'format'):
+ txt = data.format()
+ if type(txt) is list:
+ txt = u'\n'.join(txt)
+ else:
+ txt = u'%s' % data
+ txt_file.write(u'%s\n\n' % txt)
+
+ txt_file.close()
+ gmDispatcher.send(signal = 'statustext', msg = _('Selected data saved to [%s].') % txt_name)
+
#------------------------------------------------------------
def _tooltip2clipboard(self, evt):
if wx.TheClipboard.IsOpened():
diff --git a/client/wxpython/gmMeasurementWidgets.py b/client/wxpython/gmMeasurementWidgets.py
index 64536b9..41bcac7 100644
--- a/client/wxpython/gmMeasurementWidgets.py
+++ b/client/wxpython/gmMeasurementWidgets.py
@@ -40,6 +40,7 @@ from Gnumed.business import gmForms
from Gnumed.business import gmPersonSearch
from Gnumed.business import gmOrganization
from Gnumed.business import gmHL7
+from Gnumed.business import gmIncomingData
from Gnumed.wxpython import gmRegetMixin
from Gnumed.wxpython import gmPlugin
@@ -270,10 +271,10 @@ def browse_incoming_unmatched(parent=None):
)
if not do_delete:
return False
- return gmHL7.delete_incoming_data(pk_incoming_data = staged_item['pk_incoming_data_unmatched'])
+ return gmIncomingData.delete_incoming_data(pk_incoming_data = staged_item['pk_incoming_data_unmatched'])
#------------------------------------------------------------
def refresh(lctrl):
- incoming = gmHL7.get_incoming_data()
+ incoming = gmIncomingData.get_incoming_data()
items = [ [
gmTools.coalesce(i['data_type'], u''),
u'%s, %s (%s) %s' % (
diff --git a/external-tools/gm-describe_file b/external-tools/gm-describe_file
index 4a516dc..8aefc2f 100755
--- a/external-tools/gm-describe_file
+++ b/external-tools/gm-describe_file
@@ -7,17 +7,19 @@ if test -z "${DESCRIPTION_FILE}" ; then
DESCRIPTION_FILE="${FILE_2_DESCRIBE}.txt"
fi
-echo "-- file ----" > ${DESCRIPTION_FILE}
+echo "---- file ----" > ${DESCRIPTION_FILE}
file "${FILE_2_DESCRIBE}" >> "${DESCRIPTION_FILE}" 2>> "${DESCRIPTION_FILE}"
echo "" >> "${DESCRIPTION_FILE}"
-echo "-- exiftool ----" >> "${DESCRIPTION_FILE}"
-exiftool -ee -m -u "${FILE_2_DESCRIBE}" >> "${DESCRIPTION_FILE}" 2>> "${DESCRIPTION_FILE}"
-#echo "" >> "${DESCRIPTION_FILE}"
-#echo "-- identify ----" >> "${DESCRIPTION_FILE}"
+
+exiftool -g1 -ee -m -u "${FILE_2_DESCRIBE}" >> "${DESCRIPTION_FILE}" 2>> "${DESCRIPTION_FILE}"
+echo "" >> "${DESCRIPTION_FILE}"
+
+#echo "---- identify ----" >> "${DESCRIPTION_FILE}"
# takes a long time on larger images / PDF files
#identify -verbose "${FILE_2_DESCRIBE}" >> "${DESCRIPTION_FILE}" 2>> "${DESCRIPTION_FILE}"
-echo "" >> "${DESCRIPTION_FILE}"
-echo "-- sfinfo ----" >> "${DESCRIPTION_FILE}"
+#echo "" >> "${DESCRIPTION_FILE}"
+
+echo "---- sfinfo ----" >> "${DESCRIPTION_FILE}"
sfinfo "${FILE_2_DESCRIBE}" >> "${DESCRIPTION_FILE}" 2>> "${DESCRIPTION_FILE}"
exit 0
diff --git a/external-tools/gm-import_incoming b/external-tools/gm-import_incoming
new file mode 100755
index 0000000..9c7356e
--- /dev/null
+++ b/external-tools/gm-import_incoming
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+#==================================================
+# This shell script is intended to be placed in
+# /usr/bin/ and should be run to use the importer
+# for external files into the INCOMING area of
+# GNUmed.
+#
+# license: GPL v2 or later
+# Karsten Hilbert
+#--------------------------------------------------
+
+# for debugging this script
+# set -x
+
+OPTIONS=$@
+
+
+# packages which install the GNUmed python modules into a path not
+# already accessible for imports via sys.path (say, /usr/share/gnumed/)
+# may need to adjust PYTHONPATH appropriately here
+#export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}/usr/share/gnumed/"
+
+
+# now run the actual script
+python -m Gnumed.importers.gmImportIncoming ${OPTIONS}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/gnumed-client.git
More information about the debian-med-commit
mailing list