This is an automated email from the git hooks/post-receive script. tille pushed a commit to branch master in repository gnumed-server.
commit b205d81875a414197b75b8b8d3588e4d063007ab Author: Andreas Tille <[email protected]> Date: Tue Sep 13 15:38:05 2016 +0200 New upstream version 21.9 --- server/bootstrap/fixup_db-v21.conf | 1 + server/bootstrap/update_db-v20_v21.conf | 1 + server/doc/schema/gnumed-entire_schema.html | 2 +- server/pycommon/gmDateTime.py | 42 +++- server/pycommon/gmShellAPI.py | 2 + server/pycommon/gmTools.py | 5 +- .../v20-v21/dynamic/v21-release_notes-dynamic.sql | 42 ++-- .../v21-clin-get_hints_for_patient-fixup.sql | 218 +++++++++++++++++++++ 8 files changed, 284 insertions(+), 29 deletions(-) diff --git a/server/bootstrap/fixup_db-v21.conf b/server/bootstrap/fixup_db-v21.conf index a14fab1..b5160c4 100644 --- a/server/bootstrap/fixup_db-v21.conf +++ b/server/bootstrap/fixup_db-v21.conf @@ -36,6 +36,7 @@ v21-CD_DVD-sleeve.sql v21-AMTS_Medikationsplan-fixup.sql v21-ref-auto_hint-smoking_status-fixup.sql v21-ref-GKV_CU-fixup.sql +v21-clin-get_hints_for_patient-fixup.sql $schema$ #---------------------------------- diff --git a/server/bootstrap/update_db-v20_v21.conf b/server/bootstrap/update_db-v20_v21.conf index d0de655..9b09bb2 100644 --- a/server/bootstrap/update_db-v20_v21.conf +++ b/server/bootstrap/update_db-v20_v21.conf @@ -127,6 +127,7 @@ v21-CD_DVD-sleeve.sql v21-AMTS_Medikationsplan-fixup.sql v21-ref-auto_hint-smoking_status-fixup.sql v21-ref-GKV_CU-fixup.sql +v21-clin-get_hints_for_patient-fixup.sql $schema$ #---------------------------------- diff --git a/server/doc/schema/gnumed-entire_schema.html b/server/doc/schema/gnumed-entire_schema.html index 3061072..967cdf5 100644 --- a/server/doc/schema/gnumed-entire_schema.html +++ b/server/doc/schema/gnumed-entire_schema.html @@ -112,7 +112,7 @@ <body> <!-- Primary Index --> - <p><br><br>Dumped on 2016-08-01</p> + <p><br><br>Dumped on 2016-09-12</p> <h1><a name="index">Index of database - gnumed_v21</a></h1> <ul> diff --git a/server/pycommon/gmDateTime.py b/server/pycommon/gmDateTime.py index 37aed7c..191c3c4 100644 --- a/server/pycommon/gmDateTime.py +++ b/server/pycommon/gmDateTime.py @@ -343,6 +343,17 @@ def pydt_replace(dt=None, year=None, month=None, day=None, hour=None, minute=Non return dt.replace(year = year, month = month, day = day, hour = hour, minute = minute, second = second, microsecond = microsecond, tzinfo = tzinfo) #--------------------------------------------------------------------------- +def pydt_is_today(dt): + now = pyDT.datetime.now(gmCurrentLocalTimezone) + if dt.day != now.day: + return False + if dt.month != now.month: + return False + if dt.year != now.year: + return False + return True + +#--------------------------------------------------------------------------- def pydt_now_here(): """Returns NOW @ HERE (IOW, in the local timezone.""" return pyDT.datetime.now(gmCurrentLocalTimezone) @@ -429,8 +440,11 @@ def format_interval(interval=None, accuracy_wanted=None, none_string=None, verbo tag = _('interval_format_tag::years::y')[-1:] tmp += u'%s%s' % (int(years), tag) - if accuracy_wanted < acc_months: + if tmp == u'': + if verbose: + return _(u'0 years') + return u'0%s' % _('interval_format_tag::years::y')[-1:] return tmp.strip() if months > 0: @@ -444,6 +458,10 @@ def format_interval(interval=None, accuracy_wanted=None, none_string=None, verbo tmp += u' %s%s' % (int(months), tag) if accuracy_wanted < acc_weeks: + if tmp == u'': + if verbose: + return _(u'0 months') + return u'0%s' % _('interval_format_tag::months::m')[-1:] return tmp.strip() if weeks > 0: @@ -457,6 +475,10 @@ def format_interval(interval=None, accuracy_wanted=None, none_string=None, verbo tmp += u' %s%s' % (int(weeks), tag) if accuracy_wanted < acc_days: + if tmp == u'': + if verbose: + return _(u'0 weeks') + return u'0%s' % _('interval_format_tag::weeks::w')[-1:] return tmp.strip() if days > 0: @@ -470,6 +492,10 @@ def format_interval(interval=None, accuracy_wanted=None, none_string=None, verbo tmp += u' %s%s' % (int(days), tag) if accuracy_wanted < acc_hours: + if tmp == u'': + if verbose: + return _(u'0 days') + return u'0%s' % _('interval_format_tag::days::d')[-1:] return tmp.strip() if hours > 0: @@ -483,6 +509,10 @@ def format_interval(interval=None, accuracy_wanted=None, none_string=None, verbo tmp += u' %s%s' % (int(hours), tag) if accuracy_wanted < acc_minutes: + if tmp == u'': + if verbose: + return _(u'0 hours') + return u'0/24' return tmp.strip() if mins > 0: @@ -496,6 +526,10 @@ def format_interval(interval=None, accuracy_wanted=None, none_string=None, verbo tmp += u' %s%s' % (int(mins), tag) if accuracy_wanted < acc_seconds: + if tmp == u'': + if verbose: + return _(u'0 minutes') + return u'0/60' return tmp.strip() if secs > 0: @@ -508,7 +542,13 @@ def format_interval(interval=None, accuracy_wanted=None, none_string=None, verbo tag = u's' tmp += u' %s%s' % (int(mins), tag) + if tmp == u'': + if verbose: + return _(u'0 seconds') + return u'0s' + return tmp.strip() + #--------------------------------------------------------------------------- def format_interval_medically(interval=None): """Formats an interval. diff --git a/server/pycommon/gmShellAPI.py b/server/pycommon/gmShellAPI.py index 9b442c2..a21b2d4 100644 --- a/server/pycommon/gmShellAPI.py +++ b/server/pycommon/gmShellAPI.py @@ -139,6 +139,7 @@ def detect_external_binary(binary=None): _log.debug('not running under Windows, not testing .exe/.bat') return (False, None) + #=========================================================================== def find_first_binary(binaries=None): found = False @@ -153,6 +154,7 @@ def find_first_binary(binaries=None): break return (found, binary) + #=========================================================================== def run_command_in_shell(command=None, blocking=False, acceptable_return_codes=None): """Runs a command in a subshell via standard-C system(). diff --git a/server/pycommon/gmTools.py b/server/pycommon/gmTools.py index 781f5b1..79def17 100644 --- a/server/pycommon/gmTools.py +++ b/server/pycommon/gmTools.py @@ -77,6 +77,7 @@ u_up_arrow = u'\u2191' u_arrow2right = u'\u2192' # --> u_down_arrow = u'\u2193' u_left_arrow_with_tail = u'\u21a2' # <--< +u_arrow2right_from_bar = u'\u21a6' # |-> u_arrow2right_until_vertical_bar = u'\u21e5' # -->| u_sum = u'\u2211' # sigma u_almost_equal_to = u'\u2248' # approximately / nearly / roughly @@ -112,12 +113,14 @@ u_male = u'\u2642' u_male_female = u'\u26a5' u_checkmark_thin = u'\u2713' u_checkmark_thick = u'\u2714' +u_arrow2right_thick = u'\u2794' u_writing_hand = u'\u270d' u_pencil_1 = u'\u270e' u_pencil_2 = u'\u270f' u_pencil_3 = u'\u2710' u_latin_cross = u'\u271d' -u_kanji_yen = u'\u5186' # Yen kanji +u_arrow2right_until_black_diamond = u'\u291e' # ->* +u_kanji_yen = u'\u5186' # Yen kanji u_replacement_character = u'\ufffd' u_link_symbol = u'\u1f517' diff --git a/server/sql/v20-v21/dynamic/v21-release_notes-dynamic.sql b/server/sql/v20-v21/dynamic/v21-release_notes-dynamic.sql index 417de48..a05d886 100644 --- a/server/sql/v20-v21/dynamic/v21-release_notes-dynamic.sql +++ b/server/sql/v20-v21/dynamic/v21-release_notes-dynamic.sql @@ -17,36 +17,26 @@ INSERT INTO dem.message_inbox ( ) VALUES ( (select pk from dem.staff where db_user = 'any-doc'), (select pk_type from dem.v_inbox_item_type where type = 'memo' and category = 'administrative'), - 'Release Notes for GNUmed 1.6.8 (database v21.8)', - 'GNUmed 1.6.8 Release Notes: + 'Release Notes for GNUmed 1.6.9 (database v21.9)', + 'GNUmed 1.6.9 Release Notes: - 1.6.8 + 1.6.9 -FIX: remove dynamic hint lacking evidence of clinical relevance -FIX: off-by-one calculation of substance intake end date -FIX: faulty use of $<gender_mapper>$ in Begleitbrief template -FIX: fix EMR access deadlock in encounter display widget [thanks Marc] -FIX: exception on non-ASCII VCF data -FIX: exception displaying birthday/age of patient w/ estimated DOB -FIX: list sorting by column header click +FIX: faulty detection of dynamic hint applicability +FIX: exception on Orthanc port out of bounds +FIX: setting address from list in receiver selection widget +FIX: no EMR user interaction when updating active encounter display +FIX: faulty by-day measurements display after patient change -IMPROVED: document tree orgs sort mode tooltips -IMPROVED: file description shell script -IMPROVED: less in-your-face default list tooltip -IMPROVED: update AMTS Medikationsplan to 2.3 -IMPROVED: log file placement -IMPROVED: form template EA information -IMPROVED: logging of EMR access locking -IMPROVED: EMR journal: show applicable dynamic hints -IMRPOVED: ES translation [thanks Uwe] -IMPROVED: show comm channels of org units in receiver selection -IMPROVED: show doc sources as receiver selection candidates -IMPROVED: letter receiver selection widget layout -IMPROVED: logging of patient change encounter editing +IMPROVED: start-end formatting of substance intake +IMPROVED: select unicode character from SOAP STC context menu +IMPROVED: edit test results by context menu from lists +IMPROVED: AMTS data file generation (v2 -> v2.3) +IMPROVED: color of focussed line in STC-based SOAP editor +IMPROVED: information in Hx box of patient overview plugin -NEW: placeholder $ph_cfg::encoding::$ -NEW: blanko AMTS Medikationsplan ~2.3 +NEW: placeholder $<if_not_empty>$ '); -- -------------------------------------------------------------- -select gm.log_script_insertion('v21-release_notes-dynamic.sql', '21.8'); +select gm.log_script_insertion('v21-release_notes-dynamic.sql', '21.9'); diff --git a/server/sql/v20-v21/fixups/v21-clin-get_hints_for_patient-fixup.sql b/server/sql/v20-v21/fixups/v21-clin-get_hints_for_patient-fixup.sql new file mode 100644 index 0000000..12b2b9c --- /dev/null +++ b/server/sql/v20-v21/fixups/v21-clin-get_hints_for_patient-fixup.sql @@ -0,0 +1,218 @@ +-- ============================================================== +-- GNUmed database schema change script +-- +-- License: GPL v2 or later +-- Author: [email protected] +-- +-- ============================================================== +\set ON_ERROR_STOP 1 + +set check_function_bodies to on; +--set default_transaction_read_only to off; + +-- -------------------------------------------------------------- +drop view if exists staging.journal_without_suppressed_hints cascade; + +create view staging.journal_without_suppressed_hints as +select * from clin.v_emr_journal +where + src_table != 'clin.suppressed_hint' +; + +grant select on staging.journal_without_suppressed_hints to group "gm-staff"; + +-- -------------------------------------------------------------- +drop function if exists clin.get_hints_for_patient(integer) cascade; + +create function clin.get_hints_for_patient(integer) + returns setof ref.v_auto_hints + language 'plpgsql' + as ' +DECLARE + _pk_identity ALIAS FOR $1; + _hint ref.v_auto_hints%rowtype; + _query text; + _sanitized_query text; + _md5_suppressed text; + _rationale4suppression text; + _suppression_exists boolean; -- does not mean that the suppression applies + _hint_currently_applies boolean; -- regardless of whether suppressed or not + _hint_recommendation text; +-- _exc_state text; +-- _exc_msg text; +-- _exc_detail text; +-- _exc_hint text; +-- _exc_context text; +BEGIN + -- loop over all defined hints + FOR _hint IN SELECT * FROM ref.v_auto_hints WHERE is_active LOOP + + -- is the hint suppressed ? + SELECT + md5_sum, + rationale + INTO + _md5_suppressed, + _rationale4suppression + FROM clin.suppressed_hint WHERE + fk_hint = _hint.pk_auto_hint + AND + fk_encounter IN ( + SELECT pk FROM clin.encounter WHERE fk_patient = _pk_identity + ); + IF FOUND THEN + _suppression_exists := TRUE; + ELSE + _suppression_exists := FALSE; + END IF; + + -- does the hint currently apply ? (regardless of whether it is suppressed) + _query := replace(_hint.query, ''ID_ACTIVE_PATIENT'', _pk_identity::text); + BEGIN + EXECUTE _query INTO STRICT _hint_currently_applies; + EXCEPTION + --WHEN insufficient_privilege THEN RAISE WARNING ''auto hint query failed: %'', _query; + WHEN others THEN + RAISE WARNING ''auto hint query failed: %'', _query; + -- only available starting with PG 9.2: + --GET STACKED DIAGNOSTICS + -- _exc_state = RETURNED_SQLSTATE, + -- _exc_msg = MESSAGE_TEXT, + -- _exc_detail = PG_EXCEPTION_DETAIL, + -- _exc_hint = PG_EXCEPTION_HINT, + -- _exc_context = PG_EXCEPTION_CONTEXT; + --RAISE WARNING ''SQL STATE: %'', _exc_state; + --RAISE WARNING ''MESSAGE: %'', _exc_msg; + --RAISE WARNING ''DETAIL: %'', _exc_detail; + --RAISE WARNING ''HINT: %'', _exc_hint; + --RAISE WARNING ''CONTEXT: %'', _exc_context; + -- workaround for 9.1: + RAISE WARNING ''SQL STATE: %'', SQLSTATE; + RAISE WARNING ''MESSAGE: %'', SQLERRM; + _hint.title := ''ERROR checking for ['' || _hint.title || ''] !''; + _hint.hint := _query; + RETURN NEXT _hint; + -- process next hint + CONTINUE; + END; + + -- does it _really_ apply ? + -- re-run against staging.journal_without_suppressed_hints which does + -- not contain suppressed hints -- because it may have applied only + -- because clin.v_emr_journal does contain formatted suppressed hints + IF _hint_currently_applies THEN + _sanitized_query := replace(_query, ''clin.v_emr_journal'', ''staging.journal_without_suppressed_hints''); + -- but only re-run if actually different + IF _sanitized_query <> _query THEN + BEGIN + EXECUTE _sanitized_query INTO STRICT _hint_currently_applies; + EXCEPTION + --WHEN insufficient_privilege THEN RAISE WARNING ''sanitized auto hint query failed: %'', _sanitized_query; + WHEN others THEN + RAISE WARNING ''sanitized auto hint query failed: %'', _sanitized_query; + -- only available starting with PG 9.2: + --GET STACKED DIAGNOSTICS + -- _exc_state = RETURNED_SQLSTATE, + -- _exc_msg = MESSAGE_TEXT, + -- _exc_detail = PG_EXCEPTION_DETAIL, + -- _exc_hint = PG_EXCEPTION_HINT, + -- _exc_context = PG_EXCEPTION_CONTEXT; + --RAISE WARNING ''SQL STATE: %'', _exc_state; + --RAISE WARNING ''MESSAGE: %'', _exc_msg; + --RAISE WARNING ''DETAIL: %'', _exc_detail; + --RAISE WARNING ''HINT: %'', _exc_hint; + --RAISE WARNING ''CONTEXT: %'', _exc_context; + -- workaround for 9.1: + RAISE WARNING ''SQL STATE: %'', SQLSTATE; + RAISE WARNING ''MESSAGE: %'', SQLERRM; + _hint.title := ''ERROR checking for ['' || _hint.title || ''] !''; + _hint.hint := _sanitized_query; + RETURN NEXT _hint; + -- process next hint + CONTINUE; + END; + END IF; + END IF; + + IF _suppression_exists THEN + -- is the hint definition still the same as at the time of suppression ? + IF _md5_suppressed = _hint.md5_sum THEN + -- yes, but does this hint currently apply ? + IF _hint_currently_applies THEN + -- suppressed, suppression valid, and hint applies: skip this hint + CONTINUE; + END IF; + -- suppressed, suppression valid, hint does NOT apply: + -- skip but invalidate suppression, because: + -- * previously the hint applied and the user suppressed it, + -- * then the patient changed such that the hint does not + -- apply anymore (but the suppression is still valid), + -- * when the patient changes again, the hint might apply again + -- * HOWEVER - since the suppression would still be valid - the + -- hint would magically get suppressed again (which is + -- medically unsafe) ... + -- after invalidation, the hint will no longer be suppressed, + -- however - since it does not currently apply it - it will + -- still not be returned until it applies again ... + -- + -- ----------------------------------------------------------------------- + -- UNFORTUNATELY, the following is currently not _possible_ because + -- we are running inside a READONLY transaction (due to inherent + -- security risks when running arbitrary user queries [IOW the hint + -- SQL] against the database) and we cannot execute a + -- sub-transaction as READWRITE :-/ + -- + --UPDATE clin.suppressed_hint + --SET md5_sum = ''invalidated''::text -- will not ever match any md5 sum + --WHERE + -- fk_encounter IN ( + -- SELECT pk FROM clin.encounter WHERE fk_patient = _pk_identity + -- ) + -- AND + -- fk_hint = _hint.pk_auto_hint; + -- ----------------------------------------------------------------------- + -- + -- hence our our workaround is to, indeed, return the hint but + -- tag it with a magic rationale, by means of which the client + -- can detect it to be in need of invalidation: + _hint.title := ''HINT DOES NOT APPLY BUT NEEDS INVALIDATION OF EXISTING SUPPRESSION ['' || _hint.title || ''].''; + _hint.rationale4suppression := ''magic_tag::please_invalidate_suppression''; + RETURN NEXT _hint; + CONTINUE; + END IF; + -- suppression exists but hint definition must have changed + -- does the new hint apply ? + IF _hint_currently_applies THEN + -- yes: ignore the suppression but provide previous + -- rationale for suppression to the user + _hint.rationale4suppression := _rationale4suppression; + -- retrieve recommendation + SELECT clin._get_recommendation_for_patient_hint(_hint.recommendation_query, _pk_identity) INTO STRICT _hint_recommendation; + _hint.recommendation := _hint_recommendation; + RETURN NEXT _hint; + CONTINUE; + END IF; + -- no, new hint does not apply, so ask for + -- invalidation of suppression (see above) + _hint.title := ''HINT DOES NOT APPLY BUT NEEDS INVALIDATION OF EXISTING SUPPRESSION ['' || _hint.title || ''].''; + _hint.rationale4suppression := ''magic_tag::please_invalidate_suppression''; + RETURN NEXT _hint; + CONTINUE; + END IF; + + -- hint is not suppressed + -- does the hint currently apply ? + IF _hint_currently_applies THEN + -- yes: retrieve recommendation + SELECT clin._get_recommendation_for_patient_hint(_hint.recommendation_query, _pk_identity) INTO STRICT _hint_recommendation; + _hint.recommendation := _hint_recommendation; + RETURN NEXT _hint; + END IF; + -- no: ignore it and process next hint in LOOP + + END LOOP; + RETURN; +END;'; + +-- -------------------------------------------------------------- +select gm.log_script_insertion('v21-clin-get_hints_for_patient-fixup.sql', '21.9'); -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/gnumed-server.git _______________________________________________ debian-med-commit mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-med-commit
