gnumed-bugs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Gnumed-bugs] GNUmed does not allow same-day substance discontinuati


From: Karsten Hilbert
Subject: Re: [Gnumed-bugs] GNUmed does not allow same-day substance discontinuations
Date: Thu, 11 Jul 2013 11:33:58 +0200
User-agent: Mutt/1.5.21 (2010-09-15)

On Wed, Jul 10, 2013 at 07:33:08PM +0000, Jim Busser wrote:

> A patient began a drug today, and contacted me today, and I have instructed 
> him to take no more.

That is the single most important motivation to keep
improving GNUmed.

> GNUmed however does not permit the date discontinued to be the same date as 
> started.
> 
> This should be changed, so that the date discontinued need not be
> 
>       > date started
> 
> only
> 
>       > = date started

OK, let's see

        # just for good measure:
        $> cd .../server/bootstrap/
        $> sudo ./fixup-db.sh 18
        $> psql -d gnumed_v18 -U gm-dbo -c '\d clin.substance_intake'

                                               Tabelle »clin.substance_intake«
        Spalte         |           Typ            |                             
    Attribute                                 
-----------------------+--------------------------+---------------------------------------------------------------------------
 pk_audit              | integer                  | not null Vorgabewert 
nextval('audit.audit_fields_pk_audit_seq'::regclass)
 row_version           | integer                  | not null Vorgabewert 0
 modified_when         | timestamp with time zone | not null Vorgabewert now()
 modified_by           | name                     | not null Vorgabewert 
"current_user"()
 pk_item               | integer                  | not null Vorgabewert 
nextval('clin.clin_root_item_pk_item_seq'::regclass)
 clin_when             | timestamp with time zone | not null Vorgabewert now()
 fk_encounter          | integer                  | not null
 fk_episode            | integer                  | 
 narrative             | text                     | 
 soap_cat              | text                     | Vorgabewert 'p'::text
 pk                    | integer                  | not null Vorgabewert 
nextval('clin.substance_intake_pk_seq'::regclass)
 fk_substance          | integer                  | 
 preparation           | text                     | 
 schedule              | text                     | 
 aim                   | text                     | 
 duration              | interval                 | 
 intake_is_approved_of | boolean                  | not null
 is_long_term          | boolean                  | 
 discontinued          | timestamp with time zone | 
 discontinue_reason    | text                     | 
 fk_drug_component     | integer                  | 
Indexe:
    "substance_intake_pkey" PRIMARY KEY, btree (pk)
    "idx_c_subst_int_fk_drug_comp" btree (fk_drug_component)
    "idx_fk_substance_curr_med" btree (fk_substance)
Check-Constraints:
    "clin_root_item_sane_soap_cat" CHECK (soap_cat IS NULL OR (lower(soap_cat) 
= ANY (ARRAY['s'::text, 'o'::text, 'a'::text, 'p'::text, 'u'::text])))
    "clin_subst_intake_either_drug_or_substance" CHECK (fk_drug_component IS 
NULL AND fk_substance IS NOT NULL OR fk_drug_component IS NOT NULL AND 
fk_substance IS NULL)
    "clin_subst_intake_sane_prep" CHECK (fk_drug_component IS NULL AND 
preparation IS NOT NULL OR fk_drug_component IS NOT NULL AND preparation IS 
NULL)

---> There we go:

    "discontinued_after_started" CHECK (clin_when IS NULL OR discontinued IS 
NULL OR discontinued >= clin_when AND discontinued <= now())

So the database does allow it.

    "medication_is_plan" CHECK (soap_cat = 'p'::text)
    "sane_aim" CHECK (gm.is_null_or_non_empty_string(aim) IS TRUE)
    "sane_discontinue_reason" CHECK (discontinued IS NULL AND 
discontinue_reason IS NULL OR discontinued IS NOT NULL AND 
gm.is_null_or_non_empty_string(discontinue_reason) IS TRUE)
    "sane_fk_episode" CHECK (intake_is_approved_of IS FALSE OR 
intake_is_approved_of IS TRUE AND fk_episode IS NOT NULL)
    "sane_schedule" CHECK (gm.is_null_or_non_empty_string(schedule) IS TRUE)
Fremdschlüssel-Constraints:
    "substance_intake_fk_drug_component_fkey" FOREIGN KEY (fk_drug_component) 
REFERENCES ref.lnk_substance2brand(pk) ON UPDATE RESTRICT ON DELETE RESTRICT
    "substance_intake_fk_substance_fkey" FOREIGN KEY (fk_substance) REFERENCES 
ref.consumable_substance(pk) ON UPDATE CASCADE ON DELETE CASCADE
Fremdschlüsselverweise von:
    TABLE "clin.lnk_substance2episode" CONSTRAINT 
"lnk_substance2episode_fk_substance_fkey" FOREIGN KEY (fk_substance) REFERENCES 
clin.substance_intake(pk) ON UPDATE CASCADE ON DELETE RESTRICT
Trigger:
    tr_delete_intake_document_deleted BEFORE DELETE ON clin.substance_intake 
FOR EACH ROW EXECUTE PROCEDURE clin.trf_delete_intake_document_deleted()
    tr_delete_intake_turns_other_components_into_substances AFTER DELETE ON 
clin.substance_intake FOR EACH ROW EXECUTE PROCEDURE 
clin.trf_delete_intake_turns_other_components_into_substances()
    tr_insert_intake_links_all_drug_components AFTER INSERT ON 
clin.substance_intake FOR EACH ROW EXECUTE PROCEDURE 
clin.trf_insert_intake_links_all_drug_components()
    tr_insert_intake_prevent_duplicate_component_links BEFORE INSERT ON 
clin.substance_intake FOR EACH ROW EXECUTE PROCEDURE 
clin.trf_insert_intake_prevent_duplicate_component_links()
    tr_insert_update_intake_prevent_duplicate_substance_links AFTER INSERT OR 
UPDATE ON clin.substance_intake DEFERRABLE INITIALLY DEFERRED FOR EACH ROW 
EXECUTE PROCEDURE 
clin.trf_insert_update_intake_prevent_duplicate_substance_links()
    tr_narrative_mod AFTER INSERT OR DELETE OR UPDATE ON clin.substance_intake 
DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE PROCEDURE 
clin.trf_announce_narrative_mod()
    tr_sanity_check_enc_epi_insert BEFORE INSERT ON clin.substance_intake FOR 
EACH ROW EXECUTE PROCEDURE clin.trf_sanity_check_enc_epi_insert()
    tr_sanity_check_substance_episode BEFORE INSERT OR UPDATE ON 
clin.substance_intake FOR EACH ROW EXECUTE PROCEDURE 
clin.trf_sanity_check_substance_episode()
    tr_substance_intake_mod AFTER INSERT OR DELETE OR UPDATE ON 
clin.substance_intake DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE 
PROCEDURE clin.trf_announce_substance_intake_mod()
    tr_undiscontinue_unsets_reason BEFORE INSERT OR UPDATE ON 
clin.substance_intake FOR EACH ROW EXECUTE PROCEDURE 
clin.trf_undiscontinue_unsets_reason()
    tr_update_intake_must_link_all_drug_components AFTER UPDATE ON 
clin.substance_intake DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE 
PROCEDURE clin.trf_update_intake_must_link_all_drug_components()
    tr_update_intake_updates_all_drug_components AFTER UPDATE ON 
clin.substance_intake DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE 
PROCEDURE clin.trf_update_intake_updates_all_drug_components()
    zt_del_substance_intake BEFORE DELETE ON clin.substance_intake FOR EACH ROW 
EXECUTE PROCEDURE audit.ft_del_substance_intake()
    zt_ins_substance_intake BEFORE INSERT ON clin.substance_intake FOR EACH ROW 
EXECUTE PROCEDURE audit.ft_ins_substance_intake()
    zt_upd_substance_intake BEFORE UPDATE ON clin.substance_intake FOR EACH ROW 
EXECUTE PROCEDURE audit.ft_upd_substance_intake()
Erbt von: clin.clin_root_item


Citing from the CHANGELOG:

        1.3.2

        FIX: exception on substance-discontinued in the future [thanks 
S.Hilbert]

The corresponding patch:

commit ce565e79efe12a8a5ea702ebe46c5a2ecf5da6c1
Author: Karsten Hilbert <address@hidden>
#Date:   Tue Apr 2 15:21:45 2013 +0200

    Fix exception on substance discontinuation in the future
    
        GNUmed sticks to its current plan of recording "state of affair"
        with regard to substance intake. Respectively, one cannot
        document discontinuation before it has happened.
        Reported by S.Hilbert

diff --git a/gnumed/CHANGELOG b/gnumed/CHANGELOG
index eafc780..9523bd3 100644
--- a/gnumed/CHANGELOG
+++ b/gnumed/CHANGELOG
@@ -11,6 +11,7 @@
 FIX: failure to fully escape % in Xe(La)TeX engine [thanks V.Banait]
 FIX: formatting error in current substance intake list
 FIX: collision of placeholder replacement and text expansion filling
+FIX: exception on substance-discontinued in the future [thanks S.Hilbert]
 
 IMPROVED: record deletion of inbox messages in EMR
 IMPROVED: create recalls from vaccinations manager
diff --git a/gnumed/gnumed/client/wxpython/gmMedicationWidgets.py 
b/gnumed/gnumed/client/wxpython/gmMedicationWidgets.py
index 46a87b3..aaab804 100644
--- a/gnumed/gnumed/client/wxpython/gmMedicationWidgets.py
+++ b/gnumed/gnumed/client/wxpython/gmMedicationWidgets.py
@@ -1493,17 +1493,21 @@ class 
cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl,
                        else:
                                self._PRW_duration.display_as_valid(True)
 
-               # end must be > start if at all
+               # end must be "< now()" AND "> start" if at all
                end = self._DP_discontinued.GetData()
                if end is not None:
-                       start = self._DP_started.GetData()
-                       if start > end:
-                               self._DP_started.display_as_valid(False)
+                       if end > gmDateTime.pydt_now_here():
                                self._DP_discontinued.display_as_valid(False)
                                validity = False
                        else:
-                               self._DP_started.display_as_valid(True)
-                               self._DP_discontinued.display_as_valid(True)
+                               start = self._DP_started.GetData()
+                               if start > end:
+                                       self._DP_started.display_as_valid(False)
+                                       
self._DP_discontinued.display_as_valid(False)
+                                       validity = False
+                               else:
+                                       self._DP_started.display_as_valid(True)
+                                       
self._DP_discontinued.display_as_valid(True)
 
                if validity is False:
                        gmDispatcher.send(signal = 'statustext', msg = _('Input 
incomplete/invalid for saving as substance intake.'))


The code thus reads:

        # end must be "< now()" AND "> start" if at all
        end = self._DP_discontinued.GetData()
        if end is not None:
                # not in the future:
                if end > gmDateTime.pydt_now_here():
                        self._DP_discontinued.display_as_valid(False)
                        validity = False
                else:
                        # and not before it started
                        if start > end:
                                self._DP_started.display_as_valid(False)
                                self._DP_discontinued.display_as_valid(False)
                                validity = False
                        else:
                                self._DP_started.display_as_valid(True)
                                self._DP_discontinued.display_as_valid(True)

So, this really should work. Going checking... OK, I can
reproduce it. So let's instrument the code a bit... GNUmed
seems to think that end is > than now(). Ah, there's the
rub. The started PRW is pre-filled with now() and will
include the time. The discontinued PRW is not pre-filled but
rather filled in by the user. The user chooses (or types the
date for) today. This will be without the time. GNUmed
assumes 11:11:11.111am. Which can trigger two bugs:

- start was after 11:11am which makes discontinued > start
- now is before 11:11am wich makes discontinued > now

I have now normalized the checks to behave properly and also
give a better hint (1.3.7).

Thanks for reporting.

Karsten
-- 
GPG key ID E4071346 @ gpg-keyserver.de
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346

Attachment: screenshot_001.png
Description: PNG image

Attachment: screenshot_002.png
Description: PNG image


reply via email to

[Prev in Thread] Current Thread [Next in Thread]