Commit a58d946b authored by Andreas Tille's avatar Andreas Tille
Browse files

New upstream version 1.7.7+dfsg

parent 5e8b0111
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -6,6 +6,18 @@
# rel-1-7-patches
------------------------------------------------

	1.7.7

FIX: EMR/tree: exception on showing visual progress note
FIX: lab/result EA: exception when no previous result available
FIX: meds/substance EA: exception when no LOINC selected
FIX: data/ATC: fix reference data import
FIX: meds/dose EA: exception on saving
FIX: meds/product EA: exception on creating new product
FIX: dist: fix appdata.xml [thanks Andreas]

NEW: configurable invoice ID template [thanks Marc]

	1.7.6

FIX: application metadata files
@@ -1997,6 +2009,13 @@ FIX: missing cast to ::text in dem.date_trunc_utc() calls
# gnumed_v22
------------------------------------------------

	22.7

IMPROVED: lab results plotting scripts for gnuplot
IMPROVED: bills tables grants for invoice ID generation

NEW: multi-results plotting script for gnuplot

	22.6

FIX: properly include fixups in v21-v22 upgrade
+1 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@
	<screenshot type="default">
		<image>https://screenshots.debian.net/screenshots/000/002/117/large.png</image>
		<caption>The main window showing one of the ways to enter Progress Notes during a patient encounter</caption>
	</screenshot
	</screenshot>
 </screenshots>
 <url type="homepage">https://www.gnumed.de</url>
 <update_contact>gnumed-devel_AT_gnu.org</update_contact>
+68 −46
Original line number Diff line number Diff line
@@ -148,28 +148,35 @@ def atc_import(cfg_fname=None, conn=None):

	args = {'ver': version, 'desc': desc, 'url': url, 'name_long': name_long, 'name_short': name_short, 'lang': lang}

	# create data source record
	queries = [
		{
		'cmd': u"""delete from ref.data_source where name_short = %(name_short)s and version = %(ver)s""",
		'args': args
		}, {
		'cmd': u"""
insert into ref.data_source (name_long, name_short, version, description, lang, source) values (
	# find or create data source record
	cmd = u"select pk from ref.data_source where name_short = %(name_short)s and version = %(ver)s"
	rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
	if len(rows) > 0:
		data_src_pk = rows[0][0]
		_log.debug('ATC data source record existed, pk is #%s, refreshing fields', data_src_pk)
		# exists - update
		args['pk'] = data_src_pk
		cmd = u"""UPDATE ref.data_source SET
				name_long = %(name_long)s,
				description = %(desc)s,
				lang = %(lang)s,
				source = %(url)s
			WHERE
				pk = %(pk)s
		"""
		rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
	else:
		_log.debug('ATC data source record not found, creating')
		# create
		cmd = u"""insert into ref.data_source (name_long, name_short, version, description, lang, source) values (
			%(name_long)s,
			%(name_short)s,
			%(ver)s,
			%(desc)s,
			%(lang)s,
			%(url)s
)""",
		'args': args
		}, {
		'cmd': u"""select pk from ref.data_source where name_short = %(name_short)s and version = %(ver)s""",
		'args': args
		}
	]
	rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
		) returning pk"""
		rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
		data_src_pk = rows[0][0]
		_log.debug('ATC data source record created, pk is #%s', data_src_pk)

@@ -235,30 +242,45 @@ insert into ref.data_source (name_long, name_short, version, description, lang,
	_log.debug('ATC staging table loaded')

	# from staging table to real table
	curs = conn.cursor()
	args = {'src_pk': data_src_pk}
	queries = []
	# transfer new records
	cmd = u"""
		insert into ref.atc (
			fk_data_source,
			code,
			term,
			comment,
	unit,
			administration_route
		) select
			%(src_pk)s,
			atc,
			name,
			nullif(comment, ''),
	nullif(unit, ''),
			nullif(adro, '')

from
		FROM
			ref.atc_staging
		WHERE
			not exists (
				select 1 FROM ref.atc WHERE fk_data_source = %(src_pk)s AND code = ref.atc_staging.atc
			)
	"""

	gmPG2.run_rw_queries(link_obj = curs, queries = [{'cmd': cmd, 'args': args}])

	queries.append({'cmd': cmd, 'args': args})
	# update records so pre-existing ones are refreshed
	cmd = u"""
		UPDATE ref.atc SET
			code = r_as.atc,
			term = r_as.name,
			comment = nullif(r_as.comment, ''),
			administration_route = nullif(r_as.adro, '')
		FROM
			(SELECT atc, name, comment, adro FROM ref.atc_staging) AS r_as
		WHERE
			fk_data_source = %(src_pk)s
	"""
	queries.append({'cmd': cmd, 'args': args})
	curs = conn.cursor()
	gmPG2.run_rw_queries(link_obj = curs, queries = queries)
	curs.close()
	conn.commit()
	_log.debug('transfer from ATC staging table to real ATC table done')
+150 −8
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ __license__ = 'GPL v2 or later (details at http://www.gnu.org)'

import sys
import logging
import zlib


if __name__ == '__main__':
@@ -17,12 +18,14 @@ from Gnumed.pycommon import gmPG2
from Gnumed.pycommon import gmBusinessDBObject
from Gnumed.pycommon import gmTools
from Gnumed.pycommon import gmDateTime

from Gnumed.business import gmDemographicRecord
from Gnumed.business import gmDocuments

_log = logging.getLogger('gm.bill')

INVOICE_DOCUMENT_TYPE = u'invoice'

#============================================================
# billables
#------------------------------------------------------------
@@ -540,14 +543,114 @@ def get_bill_receiver(pk_patient=None):
	pass

#------------------------------------------------------------
def get_invoice_id(pk_patient=None):
	return u'GM%s / %s' % (
		pk_patient,
		gmDateTime.pydt_strftime (
			gmDateTime.pydt_now_here(),
			'%Y-%m-%d / %H%M%S'
		)
	)
def generate_invoice_id(template=None, pk_patient=None, person=None, date_format='%Y-%m-%d', time_format='%H%M%S'):
	"""Generate invoice ID string, based on template.

	No template given -> generate old style fixed format invoice ID.

	Placeholders:
		%(pk_pat)s
		%(date)s
		%(time)s
			if included, $counter$ is not *needed* (but still possible)
		%(firstname)s
		%(lastname)s
		%(dob)s

		#counter#
			will be replaced by a counter, counting up from 1 until the invoice id is unique, max 999999
	"""
	assert (None in [pk_patient, person]), u'either of <pk_patient> or <person> can be defined, but not both'

	if (template is None) or (template.strip() == u''):
		# force old style
		template = u'GM%(pk_pat)s / %(date)s / %(time)s'
		date_format = '%Y-%m-%d'
		time_format = '%H%M%S'
	template = template.strip()
	_log.debug('invoice ID template: %s', template)
	if pk_patient is None:
		if person is not None:
			pk_patient = person.ID
	now = gmDateTime.pydt_now_here()
	data = {}
	data['pk_pat'] = gmTools.coalesce(pk_patient, '?')
	data['date'] = gmDateTime.pydt_strftime(now, date_format).strip()
	data['time'] = gmDateTime.pydt_strftime(now, time_format).strip()
	if person is None:
		data['firstname'] = u'?'
		data['lastname'] = u'?'
		data['dob'] = u'?'
	else:
		data['firstname'] = person['firstnames'].replace(' ', gmTools.u_space_as_open_box).strip()
		data['lastname'] = person['lastnames'].replace(' ', gmTools.u_space_as_open_box).strip()
		data['dob'] = person.get_formatted_dob (
			format = date_format,
			encoding = 'utf8',
			none_string = u'?',
			honor_estimation = False
		).strip()
	candidate_invoice_id = template % data
	if u'#counter#' not in candidate_invoice_id:
		if u'%(time)s' in template:
			return candidate_invoice_id

		candidate_invoice_id = candidate_invoice_id + u' [##counter#]'

	_log.debug('invoice id candidate: %s', candidate_invoice_id)
	# get existing invoice IDs consistent with candidate
	search_term = u'^\s*%s\s*$' % gmPG2.sanitize_pg_regex(expression = candidate_invoice_id).replace(u'#counter#', '\d+')
	cmd = u'SELECT invoice_id FROM bill.bill WHERE invoice_id ~* %(search_term)s UNION ALL SELECT invoice_id FROM audit.log_bill WHERE invoice_id ~* %(search_term)s'
	args = {'search_term': search_term}
	rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
	if len(rows) == 0:
		return candidate_invoice_id.replace(u'#counter#', u'1')

	existing_invoice_ids = [ r['invoice_id'].strip() for r in rows ]
	counter = None
	counter_max = 999999
	for idx in range(1, counter_max):
		candidate = candidate_invoice_id.replace(u'#counter#', '%s' % idx)
		if candidate not in existing_invoice_ids:
			counter = idx
			break
	if counter is None:
		# exhausted the range, unlikely (1 million bills are possible
		# even w/o any other invoice ID data) but technically possible
		_log.debug('exhausted uniqueness space of [%s] invoice IDs per template', counter_max)
		counter = '>%s[%s]' % (counter_max, data['time'])

	return candidate_invoice_id.replace(u'#counter#', '%s' % counter)

#------------------------------------------------------------
def lock_invoice_id(invoice_id):
	_log.debug('locking invoice ID: %s', invoice_id)
	crc32 = zlib.crc32(invoice_id)
	adler32 = zlib.adler32(invoice_id)
	_log.debug('crc32: %s', crc32)
	_log.debug('adler32: %s', adler32)
	cmd = u"""SELECT pg_try_advisory_lock(%s, %s)""" % (crc32, adler32)
	rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
	if rows[0][0]:
		return True

	_log.warning('cannot lock invoice ID: [%s] (%s/%s)', invoice_id, crc32, adler32)
	return False

#------------------------------------------------------------
def unlock_invoice_id(invoice_id):
	_log.debug('unlocking invoice ID: %s', invoice_id)
	crc32 = zlib.crc32(invoice_id)
	adler32 = zlib.adler32(invoice_id)
	_log.debug('crc32: %s', crc32)
	_log.debug('adler32: %s', adler32)
	cmd = u"""SELECT pg_advisory_unlock(%s, %s)""" % (crc32, adler32)
	rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
	if rows[0][0]:
		return True

	_log.warning('cannot unlock invoice ID: [%s] (%s/%s)', invoice_id, crc32, adler32)
	return False

#============================================================
# main
@@ -572,6 +675,7 @@ if __name__ == "__main__":
		first_bill = bills[0]
		print first_bill.default_address

	#--------------------------------------------------
	def test_me():
		print "--------------"
		me = cBillable(aPK_obj=1)
@@ -580,6 +684,44 @@ if __name__ == "__main__":
			print field, ':', me[field]
		print "updatable:", me.get_updatable_fields()
		#me['vat']=4; me.store_payload()

	#--------------------------------------------------
	def test_generate_invoice_id():
		from Gnumed.pycommon import gmI18N
		gmI18N.activate_locale()
		gmI18N.install_domain()
		import gmPerson
		for idx in range(1,15):
			print ''
			print 'classic:', generate_invoice_id(pk_patient = idx)
			pat = gmPerson.cPerson(idx)
			template = u'%(firstname).4s%(lastname).4s%(date)s'
			print 'new: template = "%s" => %s' % (
				template,
				generate_invoice_id (
					template = template,
					pk_patient = None,
					person = pat,
					date_format='%d%m%Y',
					time_format='%H%M%S'
				)
			)
			template = u'%(firstname).4s%(lastname).4s%(date)s-#counter#'
			print 'new: template = "%s" => %s' % (
				template,
				generate_invoice_id (
					template = template,
					pk_patient = None,
					person = pat,
					date_format='%d%m%Y',
					time_format='%H%M%S'
				)
			)

		#generate_invoice_id(template=None, pk_patient=None, person=None, date_format='%Y-%m-%d', time_format='%H%M%S')

	#--------------------------------------------------

	#test_me()
	test_default_address()
	test_generate_invoice_id()
+9 −4
Original line number Diff line number Diff line
@@ -1739,25 +1739,30 @@ def get_drug_by_atc(atc=None, preparation=None, link_obj=None):

#------------------------------------------------------------
def create_drug_product(product_name=None, preparation=None, return_existing=False, link_obj=None, doses=None):

	if preparation is None:
		preparation = _('units')

	if preparation.strip() == u'':
		preparation = _('units')

	if return_existing:
		drug = get_drug_by_name(product_name = product_name, preparation = preparation, link_obj = link_obj)
		if drug is not None:
			return drug

	if link_obj is None:
		link_obj = gmPG2.get_connection(readonly = False)
		conn_commit = link_obj.commit
		conn_close = link_obj.close
	else:
		conn_commit = lambda x:x
		conn_close = lambda x:x
	cmd = u'INSERT INTO ref.drug_product (description, preparation) VALUES (%(prod_name)s, %(prep)s) RETURNING pk'
	args = {'prod_name': product_name, 'prep': preparation}
	rows, idx = gmPG2.run_rw_queries(link_obj = link_obj, queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
	product = cDrugProduct(aPK_obj = rows[0]['pk'], link_obj = link_obj)
	if doses is not None:
		product.set_substance_doses_as_components(substance_doses = doses, link_obj = link_obj)

	conn_commit()
	conn_close()
	return product

#------------------------------------------------------------
Loading