Ga naar inhoud

Expert form

Synopsis

Form for expert training.

Route URL

#/expert

Old form in legacy FMX application

expert2.pas in the old application.

GUI

On form start, execute action Initialize

General layout

  • Top of form
    • Radio group (name: grpSelectData) with items:
      • name: rbGeneralData text: "General data"
      • name: rbAnamnese text: "Anamnese"
      • name: rbImagery text: "Beeldvorming"
      • name: rbPhysResearch text: "Fysisch onderzoek"
      • name: rbLab text: "Laboratorium"
      • name: rbAll Text: "Alles" On change: Execute SelectData action.
    • label lblTitle: 'Kabisa EXPERT'
  • Vertical Centered : 3 columns, see below
  • Bottom of form, row 1: Button array:
    • name: btnClusters, label: 'Clusters', execute action ShowClusters.
    • name: btnLecture, label: 'Les' execute action ShowLecture.
    • name: btnGraphic, label: 'Graphic', execute action ShowGraphic.
    • name: btnRemoveLast, label: 'Remove last arg', execute action RemoveLastArg.
    • Name: btnPowertable, label: 'Powertable' execute action TogglePowerTable.
  • Bottom of form, row 2: buttons:
    • name: btnRestart, label: 'Opnieuw', on click execute action Restart
    • name: btnExit, label: 'Opnieuw', on click execute action Exit

Column 1

  • Grid (name: grdSymptoms) 1 column: symptom name.
  • On dubbelclick: execute SelectSymptom action.
  • On keydown: execute FilterSymptom action.
  • label "lblSearchValue", text initially empty.
  • label "lblSearchHint", text "Type some letters for quick searching"

Column 2

  • Arrow green. On click, execute SelectSymptom action with parameter Present equal to True.
  • Arrow red. On click, execute SelectSymptom action with parameter Present equal to False.
  • Grid (name: grdSelectedSymptoms) 1 column.
  • label "lblAdding", text "quick adding", initially hidden.

Column 3

  • Grid (name: grdDiagnoses), 2 columns.
    • Column 1: probability.
    • Column 2: Diagnosis name.

Actions

  • All data types and data structures in this form are used in the exact same way in Consultation. For this reason, all variables and data types must be defined in a separate unit: Units.Simulation, in a class TSimulation.

  • Define the following types:

    Type
      TDatatype = (dtGeneral,dtAnamnese,dtImagery,dtPhysReseach,dtLab,dtAll,dtDiagnose);
    
      TSelectedSymptom = Class(TObject)
        diagnose: TDiagnose; // only used for nurse in consultation with *diagnose selected
        symptom: TSymptom; // Used in all other cases?
        isPresent: boolean;
      end;
      TSelectedSymptomArray = array of TSelectedSymptom;
    
      TProbability = Record
        diagnose : TDiagnosis;
        Association : TAssociation;
        pro : array[1..64] of double;
        lp : array[1..64] of double;
        temp_lp : double;
        temp_new : double;
        sens : Double;
      end;  
      TProbabilityArray : Array of TProbability;
    
      TPowerTableItem = record
        diagnose : TDiagnosis; // ZIEKE, NRZ
        procent : double; // espr1
        cutoff : double;  
        Symptom: TSymptom; // SYMPE, NRS
        PC : Integer;
        PE : Integer;
        pwText : string;
        Prev : Integer;
        maxPCPE : Double;
        BitmapID : Integer;
      end;  
      TPowerTable = array of TPowerTableItem;
    

Initialize

  • Translate form
  • Set a string searchterm to empty string.
  • Initialize 2 boolean form variables :

    nonpresscanty:=False;
    nonpressplusplus:=False;
    

  • Initialize empty selectedSymptoms : TSelectedSymptomArray, object has the following fields

    • text Synonym text in selected language.
    • symptom of type object Symptom
    • isPresent of type boolean.
  • Execute Restart action
  • Execute SelectData action

SelectData

  • initialize datatype value from grpSelectData radiogroup.
  • Initialize an array displayedsynonyms containing all synonyms for Symptoms for which
    • the property expand equals False
    • the ttype property matches:
      • dtGeneral : ttype<2 or ttype>5
      • dtAnamnese : ttype=2
      • dtImagery : ttype=5
      • dtPhysReseach : ttype=3
      • dtLab : ttype=4
      • dtAll : ttype>=4 The synonyms are are ordered on synonym.sortnr (a reference to the symtom ID must be kept)
  • Reset the searchterm string to empty.
  • In grid grdSymptoms, show the array of displayedsynonyms.text, ordered by sortNr.lang.
  • Make btnRemoveLast visible.

SelectSymptom

  • Has a parameter Present of type boolean.
  • if there are 58 symptoms in selectedSymptoms array, show a message
    Too many arguments
    
    exit action.
  • Determine symptom from selected row in grid grdSymptoms.
  • If the selected symptom is already in the selectedSymptoms array, show a message (string 438)
    Argument already in list
    
    exit action.
  • if the selected symptom is in the excludes of one of the symptoms of in the selectedSymptoms array, show a message:
    [mesexcl] {excl.name} [mesexc2] symptom.name [mesexc3]
    
    exit the action.
  • if the selected symptom is in the implies of one of the symptoms of in the selectedSymptoms array, show a message:
    [mesimpl] {excl.symptom.name} [mesimpl2] symptom.name
    
    exit the action.
  • add selected symptom in to selectedSymptoms array:
  • symptom equal to selected symptom.
  • isPresent equal to action parameter present
  • Display symptoms from selectedSymptoms in grdSelectedSymptoms
  • Calculate diagnosis probabilities as in CalculateProbabilities, passing the symptom and Not IsPresent as parameters .
  • Let symCount equal number of elements in the selectedSymptoms array
  • In grid grdDiagnoses, show the probability records with Pro[symCount] > -2
    • In column 1 : 100*Procent(Pro[symCount])
    • In Column 2 : Diagnose.Name.Lang order by descending Pro[symCount]

FilterSymptom

  • Add pressed key to searchterm (or remove last char, depending on key)
  • if the search term is empty, show all records
  • Filter the array displayedsynonyms so that
    • Only records that contain the search term (case insensitive) are shown.
    • Sort on position of the match in the search term.
    • In grid cell, display matched substring in red.
  • Show the filtered list in grid grdSymptoms,

ShowLecture

  • Make sure form state is preserved.
  • go to route /lecture/return/export

ShowCluster

  • Make sure form state is preserved.
  • go to route /cluster/return/export

ShowGraphic

  • Show Graphic modal dialog using the selected symptoms and probabilities:
  • probabilities
  • selectedsymptoms.

RemoveLastArg

  • set symCount equal to count of elements in selectedsymptoms
  • in Probabilities array, set pl[symCount] and prob[symcount] to zero for all records.
  • Remove last added element from selectedsymptoms
  • set symCount equal to count of elements in selectedsymptoms (should be previous value -1)
  • If the symCount is zero, then
  • Disable btnRemoveLast
  • Disable btnRestart
  • Disable btnPowertable
  • Make lblAdding invisible
  • Make lblSearchHint visible.
  • Redisplay grid grdDiagnoses, show the probability records with Prob[symCount] > -2
    • In column 1 : 100*Procent(Prob[symCount])
    • In Column 2 : Diagnose.Name.Lang order by descending Prob[symCount]

TogglePowerTable

  • When the powertable is visible:

    • make it invisible
    • set caption of btnPowertable to 'Show powertable'
  • If the powertable is not visible,

    • Execute CalculatePowertable
    • Show powertable with items:
    • Column 1 : Diagnosis.Name.CONTINENT
    • Column 2 : Symptom.Name.CONTINENT
    • Column 3 : PC
    • Column 4 : PE
    • Column 5 : Bitmap according to bitmap ID.

Restart

  • Clear list of selected symptoms.
  • Set datatype = dtGeneral
  • Radio button rbGeneralData is checked.
  • Initialize Probabilities array of type TProbabilityArray: For each Diagnosis with Diagnosis.PC.cont<>0, add a record with
    • Pro[1]:=Log(Diagnosis.PC.continent/Sum[Diagnosis.PC.continent])
    • All other PRO[] elements zero
    • All PL[] elements zero
  • Disable btnRemoveLast
  • Clear searchterm.
  • focus on *grdSymptoms

Exit

Navigate to menu

CalculateProbabilities

  • Parameter aSymptom : TSymptom.
  • Parameter IsExclude : Boolean
  • Parameter DoShowMessage : Boolean
  • Parameter MultipleSyms : Boolean
  • let symcount be the number of elements in selectedSymptoms.
  • For symptom 26 and continent 'Travel', set alarm equal to true, in all other cases alarm equals symptom.alarm.
  • let factor be equal to symptom.comune.CONTINENT
  • for every record in Probabilities:
    • Set Association to the TAssociation record associated with the selected aSymptom and the record's Diagnose. If there is no such record, set to Nil.
    • if Association is assigned, set sens to Association.Sens, else set sens to 0
  • For all elements in Probabilities:
    • If Association is empty, let prob[symcount+1] =-13
    • if Association is non-empty let prob[symcount+1] equal prob[symcount];
  • Let p1 equal the sum of all Pro[symcount] amounts from Probabilities, where Association is assigned.
  • Let sompr equal the sum of all Pro[symcount]*sens amounts from Probabilities, where Association is assigned.
  • For each element in Probabilities, where association is assigned:
    • Let
      prev_old:=percent(prob[symcount]);
      
    • calculate temp variable Falsi:
      FALSI:=(sompr- sens*prev_old+(1-p1)*fatcom)/(1-prev_old),
      if Falsi<=0 then
        FALSI=0.00001;
      
    • calculate 2 values, stored in the TProbability record
      If not isExclude then
      begin
         afract:=sens/falsi;
         temp_lp := LG10(fract);
         temp_new := prob[symcount]+LG10(fract);
      end
      else
      begin
         acalcsens:=sens;
         if (aSymptom.id=27) and nonpresscanty then
           aCalcsens:=0.95
         else if (aSymptom.id=117) and nonpressplusplus then
           aCalcsens:=0.81;
         afract:=(1-falsi)/(1-acalcsens);
         temp_lp := LG10(fract);
         temp_new := prob[symcount]-LG10(fract);
      end
      
  • is MultipleSyms is true then call HandleMayCause with parameters MySymptom and DoShowMessage
  • calculate p2:
     p2:=0;
     for P in Probabilities do
       if Assigned(P.Association) then
         p2:=p2+procent(temp_new);
    
  • calculate factor:
      factor:=(1-p2)/(1-p1);
    
  • For each element in workview where assocation is assigned do:
    • calculate CondDao and temp_new:
      CondDao:=( (procent(temp_new)*factor) / (1-procent(temp_new)*faktor_s) )
      if CondDao<0 then
        CondDao:=0.0000000000001
      temp_new := lg10(CONDDAO)
      
  • For all records do:

    • Set Prob[Symcount+1] = temp_new
    • Set lp[Symcount+1] = temp_lp
  • if IsExclude is True then update form variables:

     if (aSymptom.id=27) then
        nonpresscanty:=True
     else if (aSymptom.id=117) then
        nonpressplusplus:=True;
    

HandleMayCause

  • Parameter: MySymptom (TSymtom)
  • Parameter: DoShowMessage
  • Initialize list of causedsymptoms (array):
  • For every symptom S in the MayCause list of MySymptom do:
    • If the Symptom S is in the SelectedSymtoms array, with Present=true then
      • Add the symtom S to causedsymptoms
  • For every symptom S in causedsymptoms do
    • Call droplight with the parameters S, MySymptom and DoShowMessage

DropLight

  • Parameter: CausedSymptom (TSymptom)
  • Parameter: MySymptom (TSymptom)
  • Parameter: DoShowMessage
  • Let symcount equal the number of elements in selectedsymptoms
  • let Idx be the index of MySymptom in in selectedsymptoms
  • For each element P in Probabilities where Association is assigned
    • Calculate Factor as P.LP[Idx]
    • If Factor>0 and P.LP[SymCount]>0 then
      • if P.LP[SymCount]>FActor then
        • set P.Prob[SymCount]=P.Prob[SymCount-1]-Factor
      • if P.LP[SymCount]<=FActor then
        • set P.Prob[SymCount]=P.Prob[SymCount-1]
  • if DoShowMessage is true then show message: (strings 540,443,444)
    Please notice the following: if {{CausedSymtom.Name.Language}} may be present because of the presence of {{MySymtom.Name.Language}}
    , or vice-versa: these characteristics are linked. We drop the lowest confirming power of both.
    

CalculatePowerTable

  • Initialize powertable array of type TPowerTable.
  • let symcount equal the number of elements in the selectedSymptoms array
  • For every record P in probabilities where (P.Pro[Symcount+1]>=-2),

    • Let PCT equal * Procent(P.Pro[Symcount+1])*
    • Fetch All associated records A from Diagnose.Associations where:

      • A.DiagID=P.Diagnose.Id
      • PCT>=P.Diagnose.cutOff or (Pct>=0.01)
      • A.SymID.ttype>1
      • A.SymId is not in the selectedSymptoms array
      • A.SymId is not in the excludes of an element of the selectedSymptoms array
      • A.SymId is not in the implies of an element of the selectedSymptoms array
      • A.SymId is not in the maycause records of an element of the selectedSymptoms array
    • For each record A, initialize a TPowerTableItem record and add it to powertable. Set the fields of the record to:

    • Diagnose: equal to P.Diagnose
    • Procent: equal to Procent(P.Pro[Symcount])
    • PC, PE, prev calculate from CalculatePCPE using the A record.
    • Bitmap calculate from CalculateBitmap using PC and PE.

CalculatePCPE

  • Argument: A of type TAssociation
  • Return values: prev, pc, pe
  • let fatcom equal Symptom.comune with Symptom.id equal A.symId.
  • let symcount equal the number of elements in the selectedSymptoms array
  • Initiate P1, p2 to zero.
  • Loop over probabilities array associated with the current symptom (A.Symptom), let P be the probability record of the current item.

    • Calculate the following sums:

      P1:=P1+Procent(P.pro[symcount])
      P2:=P2+Procent(P.pro[symcount])*GetAssociation(P.Diagnose.Id,A.SymId).sens
      

    • Note that the Association field of the Probability record P must not be used for this.

    • If P.Diagnose.id equals the A.DiagId then additionally set
      prev:=procent(P.pro[symcount]);
      P3:=GetAssociation(P.Diagnose.Id,A.SymId).sens;
      
    • if prev=1 then set
      fp:=0.000000001;
      
    • if prev<>1 then Calculate fp:
      fp:= (p2-Prev*p3+(1-P1)*fatcom) / (1-Prev);
      if fp=0 then fp:=0.000000001;
      
  • Calculate return value PC:

    pc:=p3/fp;
    

  • if p3<>1 then calculate return value PE
    PE:=(1-fp)/(1-p3);
    
  • if p3 equals 1 then return value PE:=0

CalculateBitmap

  • 2 entry parameters PC,PE (double)
  • The bitmap reference is sum of 2 numbers, forming a 2-digit number when added together.
  • Each number is calculated as follows
    Function CalcDigit(N : integer) : integer;
    
    Begin
    if n<1 then
      result:=0
    else if n<6 then
      result:=1
    else if n<17 then
      result:=2
    else if n<58 then
      result:=3
    else
      result:=4
    
    The result of CalculateBitmap is then calculated as
    Result:=CalcDigit(trunc(PC))*10 + CalcDigit(Trunc(PE))