[med-svn] [gnumed-server] 01/03: New upstream version 21.13
Andreas Tille
tille at debian.org
Sat May 20 17:32:34 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository gnumed-server.
commit a15b5e90dde220e9503722ef9eeed16961a6cc9f
Author: Andreas Tille <tille at debian.org>
Date: Sat May 20 18:42:18 2017 +0200
New upstream version 21.13
---
server/bootstrap/fixup_db-v21.conf | 1 +
server/bootstrap/update_db-v20_v21.conf | 5 +-
server/doc/schema/gnumed-entire_schema.html | 887 ++++++++++++++++-----
server/pycommon/gmBackendListener.py | 6 +-
server/pycommon/gmDateTime.py | 15 +-
server/pycommon/gmDispatcher.py | 8 +-
server/pycommon/gmLog2.py | 58 +-
server/pycommon/gmPG2.py | 4 +
.../v20-v21/dynamic/v21-release_notes-dynamic.sql | 23 +-
server/sql/v20-v21/fixups/v21-Constans-TVT-OE.sql | 52 ++
10 files changed, 857 insertions(+), 202 deletions(-)
diff --git a/server/bootstrap/fixup_db-v21.conf b/server/bootstrap/fixup_db-v21.conf
index d8739da..54d29cc 100644
--- a/server/bootstrap/fixup_db-v21.conf
+++ b/server/bootstrap/fixup_db-v21.conf
@@ -41,6 +41,7 @@ v21-clin-get_hints_for_patient-fixup.sql
v21-notifications-dynamic.sql
v21-clin-uppercase_soap_cat-fixup.sql
v21-dem-identity-fixup.sql
+v21-Constans-TVT-OE.sql
$schema$
#----------------------------------
diff --git a/server/bootstrap/update_db-v20_v21.conf b/server/bootstrap/update_db-v20_v21.conf
index f1abb55..1c5a801 100644
--- a/server/bootstrap/update_db-v20_v21.conf
+++ b/server/bootstrap/update_db-v20_v21.conf
@@ -132,6 +132,7 @@ v21-clin-get_hints_for_patient-fixup.sql
v21-notifications-dynamic.sql
v21-clin-uppercase_soap_cat-fixup.sql
v21-dem-identity-fixup.sql
+v21-Constans-TVT-OE.sql
$schema$
#----------------------------------
@@ -261,9 +262,9 @@ automatic hints::::select count(1) + 9 from ref.auto_hint
select count(1) from ref.auto_hint
suppressed hints::::select count(1) from clin.suppressed_hint
select count(1) from clin.suppressed_hint
-raw keyword expansions::::select count(1) + 1 from ref.keyword_expansion
+raw keyword expansions::::select count(1) + 2 from ref.keyword_expansion
select count(1) from ref.keyword_expansion
-mapped keyword expansions::::select count(1) + 1 from ref.v_keyword_expansions
+mapped keyword expansions::::select count(1) + 2 from ref.v_keyword_expansions
select count(1) from ref.v_keyword_expansions
organisations::::select count(1) from dem.org
select count(1) from dem.org
diff --git a/server/doc/schema/gnumed-entire_schema.html b/server/doc/schema/gnumed-entire_schema.html
index 006f69d..581eaa2 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 2017-02-10</p>
+ <p><br><br>Dumped on 2017-05-14</p>
<h1><a name="index">Index of database - gnumed_v21</a></h1>
<ul>
@@ -143,7 +143,7 @@
<li><a name="clin.schema">clin</a></li><ul>
<li><a href="gnumed-entire_schema.html#clin.table.-enum-allergy-type">_enum_allergy_type</a></li><li><a href="gnumed-entire_schema.html#clin.table.allergy">allergy</a></li><li><a href="gnumed-entire_schema.html#clin.table.allergy-state">allergy_state</a></li><li><a href="gnumed-entire_schema.html#clin.table.clin-aux-note">clin_aux_note</a></li><li><a href="gnumed-entire_schema.html#clin.table.clin-diag">clin_diag</a></li><li><a href="gnumed-entire_schema.html#clin.table.clin-item-ty [...]
- <li><a href="gnumed-entire_schema.html#clin.function.-get-recommendation-for-patient-hint-text-integer">_get_recommendation_for_patient_hint(text, integer)</a></li><li><a href="gnumed-entire_schema.html#clin.function.add-coded-phrase-text-text-text">add_coded_phrase(text, text, text)</a></li><li><a href="gnumed-entire_schema.html#clin.function.f-del-booster-must-have-base-immunity">f_del_booster_must_have_base_immunity()</a></li><li><a href="gnumed-entire_schema.html#clin.function.f-f [...]
+ <li><a href="gnumed-entire_schema.html#clin.function.-get-recommendation-for-patient-hint-text-integer">_get_recommendation_for_patient_hint(text, integer)</a></li><li><a href="gnumed-entire_schema.html#clin.function.add-coded-phrase-text-text-text">add_coded_phrase(text, text, text)</a></li><li><a href="gnumed-entire_schema.html#clin.function.f-del-booster-must-have-base-immunity">f_del_booster_must_have_base_immunity()</a></li><li><a href="gnumed-entire_schema.html#clin.function.f-f [...]
</ul>
<li><a name="de-de.schema">de_de</a></li><ul>
@@ -153,7 +153,7 @@
<li><a name="dem.schema">dem</a></li><ul>
<li><a href="gnumed-entire_schema.html#dem.table.address">address</a></li><li><a href="gnumed-entire_schema.html#dem.table.address-type">address_type</a></li><li><a href="gnumed-entire_schema.html#dem.table.country">country</a></li><li><a href="gnumed-entire_schema.html#dem.table.enum-comm-types">enum_comm_types</a></li><li><a href="gnumed-entire_schema.html#dem.table.enum-ext-id-types">enum_ext_id_types</a></li><li><a href="gnumed-entire_schema.html#dem.table.gender-label">gender_l [...]
- <li><a href="gnumed-entire_schema.html#dem.function.add-external-id-type-text-text">add_external_id_type(text, text)</a></li><li><a href="gnumed-entire_schema.html#dem.function.add-name-integer-text-text-boolean">add_name(integer, text, text, boolean)</a></li><li><a href="gnumed-entire_schema.html#dem.function.address-exists-text-text-text-text-text-text-text">address_exists(text, text, text, text, text, text, text)</a></li><li><a href="gnumed-entire_schema.html#dem.function.create-ad [...]
+ <li><a href="gnumed-entire_schema.html#dem.function.add-external-id-type-text-text">add_external_id_type(text, text)</a></li><li><a href="gnumed-entire_schema.html#dem.function.add-name-integer-text-text-boolean">add_name(integer, text, text, boolean)</a></li><li><a href="gnumed-entire_schema.html#dem.function.address-exists-text-text-text-text-text-text-text">address_exists(text, text, text, text, text, text, text)</a></li><li><a href="gnumed-entire_schema.html#dem.function.create-ad [...]
</ul>
<li><a name="gm.schema">gm</a></li><ul>
@@ -182,7 +182,7 @@
</ul>
<li><a name="staging.schema">staging</a></li><ul>
- <li><a href="gnumed-entire_schema.html#staging.table.lab-request">lab_request</a></li><li><a href="gnumed-entire_schema.html#staging.table.loinc-staging">loinc_staging</a></li><li><a href="gnumed-entire_schema.html#staging.table.test-result">test_result</a></li>
+ <li><a href="gnumed-entire_schema.html#staging.view.journal-without-suppressed-hints">journal_without_suppressed_hints</a></li><li><a href="gnumed-entire_schema.html#staging.table.lab-request">lab_request</a></li><li><a href="gnumed-entire_schema.html#staging.table.loinc-staging">loinc_staging</a></li><li><a href="gnumed-entire_schema.html#staging.table.test-result">test_result</a></li>
</ul>
@@ -105194,21 +105194,21 @@ END;</pre>
<hr>
<h2>Function:
- <a href="gnumed-entire_schema.html#clin.schema">clin</a>.<a name="clin.function.get-hints-for-patient-integer">get_hints_for_patient(integer)</a>
+ <a href="gnumed-entire_schema.html#clin.schema">clin</a>.<a name="clin.function.get-hints-for-patient-pk-identity-integer">get_hints_for_patient(_pk_identity integer)</a>
</h2>
<h3>Returns: SET OF v_auto_hints</h3>
<h3>Language: PLPGSQL</h3>
<pre>
DECLARE
- _pk_identity ALIAS FOR $1;
_hint ref.v_auto_hints%rowtype;
_query text;
- _md5_suppressed text;
- _rationale4suppression text;
_suppression_exists boolean; -- does not mean that the suppression applies
+ _md5_at_suppression text;
+ _old_rationale4suppression text;
_hint_currently_applies boolean; -- regardless of whether suppressed or not
_hint_recommendation text;
+ _title text;
-- _exc_state text;
-- _exc_msg text;
-- _exc_detail text;
@@ -105217,133 +105217,138 @@ DECLARE
BEGIN
-- loop over all defined hints
FOR _hint IN SELECT * FROM ref.v_auto_hints WHERE is_active LOOP
+ --raise NOTICE 'checking hint for patient %: %', _pk_identity, _hint.title;
-- 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 ?
+ SELECT (clin.hint_suppression_exists(_pk_identity, _hint.pk_auto_hint)).*
+ INTO STRICT _suppression_exists, _md5_at_suppression, _old_rationale4suppression;
+ -- 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;
- 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;
+ _query := replace(_query, 'clin.v_emr_journal', 'staging.journal_without_suppressed_hints');
+ SELECT (clin.run_hint_query(_hint.title, _query)).*
+ INTO STRICT _hint_currently_applies, _title;
+ -- error ?
+ IF _hint_currently_applies IS NULL THEN
+ --raise NOTICE ' error -> return';
+ _hint.title := _title;
+ _hint.hint := _query;
+ RETURN NEXT _hint;
+ -- process next hint
+ CONTINUE;
+ END IF;
+ -- hint does not apply
+ IF _hint_currently_applies IS FALSE THEN
+ -- does a (previously stored) suppression exist ?
+ IF _suppression_exists IS FALSE THEN
+ -- no, so skip this hint
+ --raise NOTICE ' does not apply -> skip';
CONTINUE;
END IF;
- -- no, new hint does not apply, so ask for
- -- invalidation of suppression (see above)
+ --raise NOTICE ' does not apply but suppression invalid -> return for invalidation';
+ -- hint suppressed but does NOT apply:
+ -- skip hint but invalidate suppression, because:
+ -- * previously the hint must have applied and the user suppressed it,
+ -- * then patient data (or hint definition) changed such that
+ -- the hint does not apply anymore (but the suppression is
+ -- still valid),
+ -- * when patient data 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 will
+ -- still not be returned and shown 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 := 'please_invalidate_suppression';
+ _hint.rationale4suppression := 'magic_tag::does_not_apply::suppression_needs_invalidation';
RETURN NEXT _hint;
CONTINUE;
END IF;
- -- hint is not suppressed
- -- does the hint currently apply ?
- IF _hint_currently_applies THEN
- -- yes: retrieve recommendation
+ --raise NOTICE ' applies';
+ -- but is there a suppression ?
+ IF _suppression_exists IS FALSE THEN
+ --raise NOTICE ' return';
+ -- no: retrieve recommendation
SELECT clin._get_recommendation_for_patient_hint(_hint.recommendation_query, _pk_identity) INTO STRICT _hint_recommendation;
_hint.recommendation := _hint_recommendation;
+ -- return hint
RETURN NEXT _hint;
+ CONTINUE;
END IF;
- -- no: ignore it and process next hint in LOOP
+ -- yes, is suppressed
+ --raise NOTICE ' is suppressed';
+ -- is the suppression still valid ?
+ -- -> yes, suppression valid
+ IF _md5_at_suppression = _hint.md5_sum THEN
+ --raise NOTICE '-> suppression valid, ignoring hint';
+ -- hint applies, suppressed, suppression valid: skip this hint
+ CONTINUE;
+ END IF;
+ -- -> no, suppression not valid
+ -- hint definition must have changed so ignore the suppression but
+ -- provide previous rationale for suppression to the user
+ _hint.rationale4suppression := _old_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 LOOP;
RETURN;
END;</pre>
<hr>
<h2>Function:
+ <a href="gnumed-entire_schema.html#clin.schema">clin</a>.<a name="clin.function.hint-suppression-exists-o-rationale-integer-o-md5-integer">hint_suppression_exists(_o_rationale integer, _o_md5 integer)</a>
+ </h2>
+<h3>Returns: record</h3>
+<h3>Language: PLPGSQL</h3>
+
+ <pre>
+--DECLARE
+-- _md5_suppressed text;
+-- _old_rationale4suppression text;
+BEGIN
+ SELECT
+ md5_sum,
+ rationale
+ INTO
+ _o_md5,
+ _o_rationale
+ FROM clin.suppressed_hint WHERE
+ fk_hint = _pk_hint
+ AND
+ fk_encounter IN (
+ SELECT pk FROM clin.encounter WHERE fk_patient = _pk_identity
+ );
+ IF FOUND THEN
+ _o_exists := TRUE;
+ ELSE
+ _o_exists := FALSE;
+ END IF;
+END;</pre>
+
+ <hr>
+ <h2>Function:
<a href="gnumed-entire_schema.html#clin.schema">clin</a>.<a name="clin.function.move-waiting-list-entry-integer-integer">move_waiting_list_entry(integer, integer)</a>
</h2>
<h3>Returns: boolean</h3>
@@ -105478,6 +105483,43 @@ END;</pre>
<hr>
<h2>Function:
+ <a href="gnumed-entire_schema.html#clin.schema">clin</a>.<a name="clin.function.run-hint-query-o-title-text-o-applies-text">run_hint_query(_o_title text, _o_applies text)</a>
+ </h2>
+<h3>Returns: record</h3>
+<h3>Language: PLPGSQL</h3>
+
+ <pre>
+BEGIN
+ BEGIN
+ EXECUTE _query INTO STRICT _o_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;
+ _o_applies := NULL;
+ _o_title := ('ERROR checking for [' || _title || '] !')::TEXT;
+ RETURN;
+ END;
+ _o_title := _title;
+END;</pre>
+
+ <hr>
+ <h2>Function:
<a href="gnumed-entire_schema.html#clin.schema">clin</a>.<a name="clin.function.trf-activate-issue-on-opening-episode">trf_activate_issue_on_opening_episode()</a>
</h2>
<h3>Returns: trigger</h3>
@@ -106123,69 +106165,6 @@ end;</pre>
<hr>
<h2>Function:
- <a href="gnumed-entire_schema.html#clin.schema">clin</a>.<a name="clin.function.trf-sane-identity-comment">trf_sane_identity_comment()</a>
- </h2>
-<h3>Returns: trigger</h3>
-<h3>Language: PLPGSQL</h3>
- <p>Ensures unique(identity.dob, names.firstnames, names.lastnames, identity.comment)</p>
- <pre>
-DECLARE
- _identity_row record;
- _names_row record;
-BEGIN
- if TG_TABLE_NAME = 'identity' then
- if TG_OP = 'UPDATE' then
- if NEW.comment IS NOT DISTINCT FROM OLD.comment then
- return NEW;
- end if;
- end if;
- _identity_row := NEW;
- select * into _names_row from dem.names where id_identity = NEW.pk;
- else
- select * into _identity_row from dem.identity where pk = NEW.id_identity;
- _names_row := NEW;
- end if;
- -- any row with
- PERFORM 1 FROM
- dem.v_all_persons
- WHERE
- -- same firstname
- firstnames = _names_row.firstnames
- and
- -- same lastname
- lastnames = _names_row.lastnames
- and
- -- same gender
- gender is not distinct from _identity_row.gender
- and
- -- same dob (day)
- dob_only is not distinct from _identity_row.dob
- and
- -- same discriminator
- comment is not distinct from _identity_row.comment
- and
- -- but not the currently updated or inserted row
- pk_identity != _identity_row.pk
- ;
- if FOUND then
- RAISE EXCEPTION
- '% on %.%: More than one person with (firstnames=%), (lastnames=%), (dob=%), (comment=%)',
- TG_OP,
- TG_TABLE_SCHEMA,
- TG_TABLE_NAME,
- _names_row.firstnames,
- _names_row.lastnames,
- _identity_row.dob,
- _identity_row.comment
- USING ERRCODE = 'unique_violation'
- ;
- RETURN NULL;
- end if;
- return NEW;
-END;</pre>
-
- <hr>
- <h2>Function:
<a href="gnumed-entire_schema.html#clin.schema">clin</a>.<a name="clin.function.trf-sanity-check-enc-epi-ins-upd">trf_sanity_check_enc_epi_ins_upd()</a>
</h2>
<h3>Returns: trigger</h3>
@@ -125689,6 +125668,81 @@ BEGIN
return OLD;
END;</pre>
+ <hr>
+ <h2>Function:
+ <a href="gnumed-entire_schema.html#dem.schema">dem</a>.<a name="dem.function.trf-sane-identity-comment">trf_sane_identity_comment()</a>
+ </h2>
+<h3>Returns: trigger</h3>
+<h3>Language: PLPGSQL</h3>
+ <p>Ensures unique(identity.dob, names.firstnames, names.lastnames, identity.comment)</p>
+ <pre>
+DECLARE
+ _identity_row record;
+ _names_row record;
+ _other_identities integer[];
+BEGIN
+ -- working on dem.identity
+ if TG_TABLE_NAME = 'identity' then
+ -- UPDATEs ...
+ if TG_OP = 'UPDATE' then
+ -- ... which do NOT change .comment ...
+ if NEW.comment IS NOT DISTINCT FROM OLD.comment then
+ -- ... are safe because they were successfully INSERTed before
+ return NEW;
+ end if;
+ end if;
+ -- but INSERTs need checking
+ _identity_row := NEW;
+ select * into _names_row from dem.names where id_identity = NEW.pk;
+ -- working on dem.names
+ else
+ select * into _identity_row from dem.identity where pk = NEW.id_identity;
+ _names_row := NEW;
+ end if;
+ -- there cannot be any combination of identical
+ -- (dob, firstname, lastname, identity.comment)
+ -- so, look for clashing rows
+ SELECT array_agg(pk_identity) INTO _other_identities FROM
+ dem.v_person_names d_vpn
+ join dem.identity d_i on (d_i.pk = d_vpn.pk_identity)
+ WHERE
+ -- same firstname
+ d_vpn.firstnames = _names_row.firstnames
+ AND
+ -- same lastname
+ d_vpn.lastnames = _names_row.lastnames
+ AND
+ -- same gender
+ d_i.gender is not distinct from _identity_row.gender
+ AND
+ -- same dob (day)
+ date_trunc('day', d_i.dob) is not distinct from date_trunc('day', _identity_row.dob)
+ AND
+ -- same discriminator
+ d_i.comment is not distinct from _identity_row.comment
+ AND
+ -- but not the currently updated or inserted row
+ d_i.pk != _identity_row.pk
+ ;
+ if coalesce(array_length(_other_identities, 1), 0) > 0 then
+ RAISE EXCEPTION
+ '% on %.%: More than one person with (firstnames=%), (lastnames=%), (dob=%), (comment=%): % & %',
+ TG_OP,
+ TG_TABLE_SCHEMA,
+ TG_TABLE_NAME,
+ _names_row.firstnames,
+ _names_row.lastnames,
+ _identity_row.dob,
+ _identity_row.comment,
+ _identity_row.pk,
+ _other_identities
+ USING ERRCODE = 'unique_violation'
+ ;
+ RETURN NULL;
+ end if;
+ return NEW;
+END;</pre>
+
<!-- gmgm -->
@@ -127353,7 +127407,10 @@ BEGIN
_cmd := 'create constraint trigger zzz_tr_announce_' || _schema_name || '_' || _table_name || '_ins_upd';
_cmd := _cmd || ' after insert or update';
_cmd := _cmd || ' on ' || _qualified_table;
- _cmd := _cmd || ' deferrable';
+ -- needed so a SELECT inside, say, _identity_accessor_SQL running
+ -- concurrently to a "lengthy" TX does not create a serialization
+ -- failure by being a rw-dependancy pivot
+ _cmd := _cmd || ' deferrable initially deferred';
_cmd := _cmd || ' for each row';
_cmd := _cmd || ' execute procedure gm.trf_announce_table_ins_upd(''' || _payload || ''', ''' || _pk_accessor_SQL || ''', ''' || _identity_accessor_SQL || ''');';
execute _cmd;
@@ -127362,7 +127419,10 @@ BEGIN
_cmd := 'create constraint trigger zzz_tr_announce_' || _schema_name || '_' || _table_name || '_del';
_cmd := _cmd || ' after delete';
_cmd := _cmd || ' on ' || _qualified_table;
- _cmd := _cmd || ' deferrable';
+ -- needed so a SELECT inside, say, _identity_accessor_SQL running
+ -- concurrently to a "lengthy" TX does not create a serialization
+ -- failure by being a rw-dependancy pivot
+ _cmd := _cmd || ' deferrable initially deferred';
_cmd := _cmd || ' for each row';
_cmd := _cmd || ' execute procedure gm.trf_announce_table_del(''' || _payload || ''', ''' || _pk_accessor_SQL || ''', ''' || _identity_accessor_SQL || ''');';
execute _cmd;
@@ -141113,6 +141173,469 @@ END;</pre>
<hr>
+ <h2>View:
+
+ <a href="gnumed-entire_schema.html#staging.schema">staging</a>.<a name="staging.view.journal-without-suppressed-hints">journal_without_suppressed_hints</a>
+ </h2>
+
+
+
+ <table width="100%" cellspacing="0" cellpadding="3">
+ <caption>staging.journal_without_suppressed_hints Structure</caption>
+ <tr>
+ <th>F-Key</th>
+ <th>Name</th>
+ <th>Type</th>
+ <th>Description</th>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>pk_patient</td>
+ <td>integer</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>modified_when</td>
+ <td>timestamp with time zone</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>clin_when</td>
+ <td>timestamp with time zone</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>modified_by</td>
+ <td>text</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>soap_cat</td>
+ <td>text</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>narrative</td>
+ <td>text</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>pk_encounter</td>
+ <td>integer</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>pk_episode</td>
+ <td>integer</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>pk_health_issue</td>
+ <td>integer</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>src_pk</td>
+ <td>integer</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>src_table</td>
+ <td>text</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>row_version</td>
+ <td>integer</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>health_issue</td>
+ <td>text</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>issue_laterality</td>
+ <td>character varying</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>issue_active</td>
+ <td>boolean</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>issue_clinically_relevant</td>
+ <td>boolean</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>issue_confidential</td>
+ <td>boolean</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>episode</td>
+ <td>text</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>episode_open</td>
+ <td>boolean</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>encounter_started</td>
+ <td>timestamp with time zone</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>encounter_last_affirmed</td>
+ <td>timestamp with time zone</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr1">
+ <td>
+
+ </td>
+ <td>encounter_type</td>
+ <td>text</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ <tr class="tr0">
+ <td>
+
+ </td>
+ <td>encounter_l10n_type</td>
+ <td>text</td>
+ <td><i>
+
+
+
+
+ </i>
+
+ </td>
+ </tr>
+
+ </table>
+
+ <!-- Inherits -->
+
+
+
+
+ <!-- Constraint List -->
+
+
+ <!-- Foreign Key Discovery -->
+
+
+ <!-- Indexes -->
+
+
+ <!-- View Definition -->
+
+ <pre>
+SELECT v_emr_journal.pk_patient
+,
+ v_emr_journal.modified_when
+,
+ v_emr_journal.clin_when
+,
+ v_emr_journal.modified_by
+,
+ v_emr_journal.soap_cat
+,
+ v_emr_journal.narrative
+,
+ v_emr_journal.pk_encounter
+,
+ v_emr_journal.pk_episode
+,
+ v_emr_journal.pk_health_issue
+,
+ v_emr_journal.src_pk
+,
+ v_emr_journal.src_table
+,
+ v_emr_journal.row_version
+,
+ v_emr_journal.health_issue
+,
+ v_emr_journal.issue_laterality
+,
+ v_emr_journal.issue_active
+,
+ v_emr_journal.issue_clinically_relevant
+,
+ v_emr_journal.issue_confidential
+,
+ v_emr_journal.episode
+,
+ v_emr_journal.episode_open
+,
+ v_emr_journal.encounter_started
+,
+ v_emr_journal.encounter_last_affirmed
+,
+ v_emr_journal.encounter_type
+,
+ v_emr_journal.encounter_l10n_type
+
+FROM clin.v_emr_journal
+
+WHERE (v_emr_journal.src_table <> 'clin.suppressed_hint'::text);</pre>
+
+
+ <!-- List off permissions -->
+
+
+ <p>
+ <a href="gnumed-entire_schema.html#index">Index</a> -
+ <a href="gnumed-entire_schema.html#staging.schema">Schema staging</a>
+ </p>
+
+ <hr>
<h2>Table:
<a href="gnumed-entire_schema.html#staging.schema">staging</a>.<a name="staging.table.lab-request">lab_request</a>
diff --git a/server/pycommon/gmBackendListener.py b/server/pycommon/gmBackendListener.py
index d9345ec..df0eaac 100644
--- a/server/pycommon/gmBackendListener.py
+++ b/server/pycommon/gmBackendListener.py
@@ -238,7 +238,11 @@ class gmBackendListener(gmBorg.cBorg):
if item.startswith(u'row PK='):
pk_row = int(item.split(u'=')[1])
if item.startswith(u'person PK='):
- pk_identity = int(item.split(u'=')[1])
+ try:
+ pk_identity = int(item.split(u'=')[1])
+ except ValueError:
+ _log.exception(u'error in change notification trigger')
+ pk_identity = -1
# try sending intra-client signals:
# 1) generic signal
self.__messages_sent += 1
diff --git a/server/pycommon/gmDateTime.py b/server/pycommon/gmDateTime.py
index 191c3c4..44e0286 100644
--- a/server/pycommon/gmDateTime.py
+++ b/server/pycommon/gmDateTime.py
@@ -540,7 +540,7 @@ def format_interval(interval=None, accuracy_wanted=None, none_string=None, verbo
tag = u' ' + _('second')
else:
tag = u's'
- tmp += u' %s%s' % (int(mins), tag)
+ tmp += u' %s%s' % (int(secs), tag)
if tmp == u'':
if verbose:
@@ -2235,10 +2235,19 @@ if __name__ == '__main__':
]
#-----------------------------------------------------------------------
def test_format_interval():
+ intv = pyDT.timedelta(minutes=1, seconds=2)
+ for acc in _accuracy_strings.keys():
+ print ('[%s]: "%s" -> "%s"' % (acc, intv, format_interval(intv, acc)))
+ return
+
for tmp in intervals_as_str:
intv = str2interval(str_interval = tmp)
+ if intv is None:
+ print(tmp, '->', intv)
+ continue
for acc in _accuracy_strings.keys():
print ('[%s]: "%s" -> "%s"' % (acc, tmp, format_interval(intv, acc)))
+
#-----------------------------------------------------------------------
def test_format_interval_medically():
@@ -2471,11 +2480,11 @@ if __name__ == '__main__':
#test_cFuzzyTimeStamp()
#test_get_pydt()
#test_str2interval()
- #test_format_interval()
+ test_format_interval()
#test_format_interval_medically()
#test_str2pydt()
#test_pydt_strftime()
#test_calculate_apparent_age()
- test_is_leap_year()
+ #test_is_leap_year()
#===========================================================================
diff --git a/server/pycommon/gmDispatcher.py b/server/pycommon/gmDispatcher.py
index f0dd6af..8a32d2b 100644
--- a/server/pycommon/gmDispatcher.py
+++ b/server/pycommon/gmDispatcher.py
@@ -102,7 +102,7 @@ def connect(receiver=None, signal=Any, sender=Any, weak=1):
raise ValueError('gmDispatcher.connect(): must define <receiver>')
if signal not in known_signals:
- _log.error('unknown signal [%(sig)s]', {'sig': signal})
+ _log.warning('unknown signal [%(sig)s]', {'sig': signal})
if signal is not Any:
signal = str(signal)
@@ -141,7 +141,7 @@ def disconnect(receiver, signal=Any, sender=Any, weak=1):
Disconnecting is not required. The use of disconnect is the same as for
connect, only in reverse. Think of it as undoing a previous connection."""
if signal not in known_signals:
- _log.error('unknown signal [%(sig)s]', {'sig': signal})
+ _log.warning('unknown signal [%(sig)s]', {'sig': signal})
if signal is not Any:
signal = str(signal)
@@ -150,13 +150,13 @@ def disconnect(receiver, signal=Any, sender=Any, weak=1):
try:
receivers = connections[senderkey][signal]
except KeyError:
- _log.error('no receivers for signal %(sig)s from sender %(sender)s', {'sig': repr(signal), 'sender': sender})
+ _log.warning('no receivers for signal %(sig)s from sender %(sender)s', {'sig': repr(signal), 'sender': sender})
print('DISPATCHER ERROR: no receivers for signal %s from sender %s' % (repr(signal), sender))
return
try:
receivers.remove(receiver)
except ValueError:
- _log.error('receiver [%(rx)s] not connected to signal [%(sig)s] from [%(sender)s]', {'rx': receiver, 'sig': repr(signal), 'sender': sender})
+ _log.warning('receiver [%(rx)s] not connected to signal [%(sig)s] from [%(sender)s]', {'rx': receiver, 'sig': repr(signal), 'sender': sender})
print("DISPATCHER ERROR: receiver [%s] not connected to signal [%s] from [%s]" % (receiver, repr(signal), sender))
_cleanupConnections(senderkey, signal)
#---------------------------------------------------------------------
diff --git a/server/pycommon/gmLog2.py b/server/pycommon/gmLog2.py
index 0697db6..f804126 100644
--- a/server/pycommon/gmLog2.py
+++ b/server/pycommon/gmLog2.py
@@ -51,6 +51,9 @@ import io
import codecs
import locale
import datetime as pydt
+import random
+import time
+import calendar
_logfile_name = None
@@ -139,6 +142,13 @@ def flush():
# logger.debug(' %s: %s', attr, getattr(v, attr))
#===============================================================
+def log_instance_state(instance):
+ logger = logging.getLogger('gm.logging')
+ logger.debug('state of %s', instance)
+ for attr in [ a for a in dir(instance) if not a.startswith('__') ]:
+ logger.debug(' %s: %s', attr, getattr(instance, attr))
+
+#===============================================================
def log_stack_trace(message=None, t=None, v=None, tb=None):
logger = logging.getLogger('gm.logging')
@@ -238,9 +248,35 @@ def set_string_encoding(encoding=None):
_string_encoding = locale.getpreferredencoding(do_setlocale=False)
logger.info(u'setting python.str -> python.unicode encoding to <%s> (locale.getpreferredencoding)', _string_encoding)
return True
+
#===============================================================
# internal API
#===============================================================
+__words2hide = []
+
+def add_word2hide(word):
+ if word not in __words2hide:
+ __words2hide.append(word)
+
+#---------------------------------------------------------------
+__original_logger_write_func = None
+
+def __safe_logger_write_func(s):
+ for word in __words2hide:
+ # random is seeded from system time at import,
+ # jump ahead, scrambled by whatever is "now"
+ random.jumpahead(calendar.timegm(time.gmtime()))
+ # from that generate a replacement string valid for
+ # *this* round of replacements of *this* word,
+ # this approach won't mitigate guessing trivial passwords
+ # from replacements of known data (a known-plaintext attack)
+ # but will make automated searching for replaced strings
+ # in the log more difficult
+ bummer = hex(random.randint(0, sys.maxint)).lstrip(u'0x')
+ s = s.replace(word, bummer)
+ __original_logger_write_func(s)
+
+#---------------------------------------------------------------
def __setup_logging():
set_string_encoding()
@@ -259,6 +295,10 @@ def __setup_logging():
_logfile = io.open(_logfile_name, mode = 'wt', encoding = 'utf8', errors = 'replace')
+ global __original_logger_write_func
+ __original_logger_write_func = _logfile.write
+ _logfile.write = __safe_logger_write_func
+
logging.basicConfig (
format = fmt,
datefmt = '%Y-%m-%d %H:%M:%S',
@@ -266,12 +306,16 @@ def __setup_logging():
stream = _logfile
)
+ logging.captureWarnings(True)
+
logger = logging.getLogger('gm.logging')
logger.critical(u'-------- start of logging ------------------------------')
logger.info(u'log file is <%s>', _logfile_name)
logger.info(u'log level is [%s]', logging.getLevelName(logger.getEffectiveLevel()))
logger.info(u'log file encoding is <utf8>')
logger.info(u'initial python.str -> python.unicode encoding is <%s>', _string_encoding)
+ logger.debug(u'log file .write() patched from original %s to patched %s', __original_logger_write_func, __safe_logger_write_func)
+
#---------------------------------------------------------------
def __get_logfile_name():
@@ -309,6 +353,7 @@ def __get_logfile_name():
_logfile_name = os.path.join(dir_name, default_logfile_name)
return True
+
#===============================================================
# main
#---------------------------------------------------------------
@@ -316,10 +361,19 @@ __setup_logging()
if __name__ == '__main__':
+ if len(sys.argv) < 2:
+ sys.exit()
+
+ if sys.argv[1] != u'test':
+ sys.exit()
+
#-----------------------------------------------------------
def test():
logger = logging.getLogger('gmLog2.test')
logger.error("I expected to see %s::test()" % __file__)
+ add_word2hide(u'super secret passphrase')
+ logger.debug('credentials: super secret passphrase')
+
try:
int(None)
except:
@@ -327,6 +381,4 @@ if __name__ == '__main__':
log_stack_trace()
flush()
#-----------------------------------------------------------
- if len(sys.argv) > 1 and sys.argv[1] == u'test':
- test()
-#===============================================================
+ test()
diff --git a/server/pycommon/gmPG2.py b/server/pycommon/gmPG2.py
index 70a4003..4eb2bd6 100644
--- a/server/pycommon/gmPG2.py
+++ b/server/pycommon/gmPG2.py
@@ -434,6 +434,7 @@ def __request_login_params_tui():
login.user = prompted_input(prompt = "user name", default = '')
tmp = 'password for "%s" (not shown): ' % login.user
login.password = getpass.getpass(tmp)
+ gmLog2.add_word2hide(login.password)
login.port = prompted_input(prompt = "port", default = 5432)
except KeyboardInterrupt:
_log.warning("user cancelled text mode login dialog")
@@ -465,7 +466,10 @@ def __request_login_params_gui_wx():
if login is None:
raise gmExceptions.ConnectionError(_("Can't connect to database without login information!"))
+ gmLog2.add_word2hide(login.password)
+
return login
+
#---------------------------------------------------
def request_login_params():
"""Request login parameters for database connection."""
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 30fc8bb..cce6ee1 100644
--- a/server/sql/v20-v21/dynamic/v21-release_notes-dynamic.sql
+++ b/server/sql/v20-v21/dynamic/v21-release_notes-dynamic.sql
@@ -17,17 +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.12 (database v21.12)',
- 'GNUmed 1.6.12 Release Notes:
+ 'Release Notes for GNUmed 1.6.13 (database v21.13)',
+ 'GNUmed 1.6.13 Release Notes:
- 1.6.12
+ 1.6.13
-FIX: patient merging [thanks Marc]
+FIX: editing of drug products
+FIX: formatting of intervals with seconds [thanks Rickard]
+FIX: robustify backend listener against change notification trigger errors
+FIX: backport once-only detection of unicode char selector
+FIX: improper handling of notebook page change events
+FIX: error handling on uploading DICOM to Orthanc
- 21.12
+IMPROVED: more fully prevent logfile based password leaks
+IMPROVED: add listing of latest vaccination per indication
+IMPROVED: export area change listening and sortability
+IMPROVED: episode edit area behaviour
+IMPROVED: add measurement by clicking empty cell in grid
-IMPROVED: logging on dem.identity/dem.names uniqueness violation
+NEW: add Constans algorithm for upper extremity DVT
');
-- --------------------------------------------------------------
-select gm.log_script_insertion('v21-release_notes-dynamic.sql', '21.12');
+select gm.log_script_insertion('v21-release_notes-dynamic.sql', '21.13');
diff --git a/server/sql/v20-v21/fixups/v21-Constans-TVT-OE.sql b/server/sql/v20-v21/fixups/v21-Constans-TVT-OE.sql
new file mode 100644
index 0000000..2299587
--- /dev/null
+++ b/server/sql/v20-v21/fixups/v21-Constans-TVT-OE.sql
@@ -0,0 +1,52 @@
+-- ==============================================================
+-- GNUmed database schema change script
+--
+-- License: GPL v2 or later
+-- Author: Karsten Hilbert
+--
+-- ==============================================================
+\set ON_ERROR_STOP 1
+--set default_transaction_read_only to off;
+
+-- --------------------------------------------------------------
+delete from ref.keyword_expansion where keyword = 'algo-TVT_OE';
+
+insert into ref.keyword_expansion (
+ fk_staff,
+ keyword,
+ textual_data
+) values (
+ null,
+ 'algo-TVT_OE',
+'Constans: Diagnostik TVT Obere Extremität
+-----------------------------------------------------------
+DÄB / 114 / 244-9 / 7.April 2014
+DOI 10.3238/artebl.2017.0244
+
+- Schwellung/Schmerz/Venenzeichnung/Zyanose
+- Armschwäche/-parästhesie
+- ZVK/Schrittmacher/Tumorleiden
+- Einengung Thoraxapertur (Rucksack, Anatomie)
+
+Klinischer Verdacht auf TVT-OE ?: $[Verdacht: Ja/Nein]$
+
+ Constans-Kriterien
+
+$[0/1]$ ZVK oder Schrittmacher
+$[0/1]$ lokalisierter Schmerz
+$[0/1]$ einseitige Schwellung
+$[0/-1]$ alternative Diagnose wahrscheinliche Ursache
+
+Summe $[Summe]$
+
+0/1 - TVT-OE unwahrscheinlich
+ -> D-Dimer
+ < 500µg/l -> TVT-OE weitgehend ausgeschlossen
+ > 500µg/l -> Sonographie
+
+2/3 - TVT-OE wahrscheinlich
+ -> Sonographie
+');
+
+-- --------------------------------------------------------------
+select gm.log_script_insertion('v21-Constans-TVT-OE.sql', '21.13');
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/gnumed-server.git
More information about the debian-med-commit
mailing list