Richard Terry wrote:
I would be far more elemental than that.
I can send in some basic specs. I would not try to make anything
which plugs into the gui framework or loads or saves data for starters.
Simply a stand along wxPython frame contining a modified editor such
as that in the wxPython demo, where we see how hard it is to make
'out of bounds' some areas of the lines ie functionally is like this.
1)Frame contains the editor
2)Editor and controls and looks like this and contains this text
______________________________
| Subjective : |
| Objective : |
| Assessment: |
| Plan : |
______________________________
[btn_ok] [btn_cancel]
===============================
list heading = "There are n SOAP elements this
consultation" (n = listindex +1)
=========================================
wxList
================================
Need array to contain inputted data eg textinput() with 0-3 elements
Inital code should do the following.
-white background of editor
- Text foreground prompts RGB 0,0, 131
ok, aesthetics?
- If user clicks anywhere over the edit area prompts the cursor
should be re-directed to the first character space on the
corresponding line (or the end of any existing characters on that line)
ok, rapid entry
- If user types on line allow input to end of line
-If text exists on line and enter key pressed, cursor is re-set to
start of next line underneath
-If on Plan line and enter pressed then give focus to the Ok button
not hard to do, but what if you need more space for S/ or O/ ?
-If Text exists on 1 or more lines of editor and ok button clicked
then save text
if textinput(0) will contain any text on the Subjective line
(excluding the subjective heading)
(1) will contain any text on Objective line
(excluding the Objective heading)
etc
After text saved to array, place a 'summary' as first element in the
list array looking like below
- finally invoke btn_clear to clear the editing area, and reset
cursor to after 'Subjective'
ie
Before the Ok button is clicked:
______________________________
| Subjective : earache |
| Objective : red drum |
| Assessment: middle ear infection |
| Plan : script amoxil |
______________________________
[btn_ok] [btn_cancel]
===============================
There are 0 SOAP elements this consultation
=========================================
wxList is empty
================================
After the Ok button is clicked:
______________________________
| Subjective : |
| Objective : |
| Assessment: |
| Plan : |
______________________________
[btn_ok] [btn_cancel]
===============================
There are 1 SOAP elements this consultation"
=======================================
wxList now contains
Middle Ear Infection - (earache, red drum, Plan:script amoxil)
================================
Now one additional functionality.
If the user clicks on this item the list, you replace the strings in
the array(0-3) back into the corresponding lines of the editing area.
This should be fairly simple to anyone who can program. If you get to
that point you have the whole thing beat. At no point try and save
the data or link to the database.
The next clever thing to do is the following:
Starting with this:
_______________________________
| Subjective : earache |
| Objective : red drum |
| Assessment: middle ear infection |
| Plan : script amoxil |
______________________________
Make this happen when the text reaches the end of the line:
_______________________________
| Subjective : earache, fever, cough, |
| vomiting, rash |
| Objective : red drum |
| Assessment: middle ear infection |
| Plan : script amoxil |
______________________________
Ie you will have to 'shift down' the prompts and insert a blank line
in the editor as the users text gets to the end of the line.
(similarly you will have to delete a blank line if one is made blank
by deleting text). There are scads of ways to do this. When I did my
text mode text and graphics editor I just kept a map of where this
were, and inserted blank lines into the control via code as needed, I
don't know how wxPython handles this.
ok, this might be easy in some gui environment that allow resizing of
widgets. I don't know if resize works in wxWidgets ,
but I'll take a look. If it doesn't work, there are several workarounds .
- the most flexible is to do what Swing did with AWT in java, and
reimplement a widget ontop of a drawing area, and take care
of events at the lowest level, e.g. mouse movements, clicks, keyboard
input.
Once you've acheieved this the whole thing is beaten. Next step is to
add some smart promtps eg BP= (inserted at the cursor by a control
key - but colour code them) and only allow this type of things on
Objective lines. Next thing is to use the phrase wheel code to
auto-complete.
Can you enumerate the smart prompts you are thinking of , what key
bindings they'll be?
Is it e.g. ctrl-A B -> BP , ctrl-A T - Temp , Ctrl-A R resp
examination mini-form .
I need to get an idea of the scope of "smart prompting"
Frankly, when I use MD, I just like typing a quick note and add in
my s/ o/ a/ p/ labels with the keyboard ( or no labels really, a
carriage return will do)
s/ 2 days l earache, fever, tummy pain ; iutd; no phx, nka, no meds
curr, not had panadol,
o/e wt 15kg , T 38.5 ears dull, r > l, throat nad , chest clear,
abdo lax nontender
a/ ?r otitis media ? urti
p/ 24 hr delay antis, amoxil 250mg tds delayed script. panamax 240/5
4ml qid , r/v 1-2/7
What I'd like the computer to do is parse this for me , mainly the
script really.
Sometimes I'll scrawl the history, and then bring up MD's click as
systems examination tabbed dialog, which isn't too bad,
and do it that way, but touching the mouse breaks the flow of typing
(which is a good excuse not to talk with the patient, and pretend
you're alone just
dealing with a computer ).
The idea I had in mind is that in the P/ part, if you type script-
amoxil 500mg tds , panamax 1g qid prn, imz - fluvax l s/c deltoid
given, Ix - lipids, glucose, psa , ref - ongoing Mr Jones ENT,
it adds action items on a tree to the right of the edit area.
These action items have a "do" hyperlink, and there might be a "do
all" hyperlink,
and when that is clicked, a confirmation of action dialog is brought
up, with the actions listed, whether they go to the printer, how many
pages each item
takes, which printer the item goes to / or alternatively a item will
be sent as a request message electronically to a third party. Some
actions might be "planned" or scheduled in the future, and they just
need confirmation for adding to the "TODO" part of the patient's
medical record.
Regards
Ricahrd
I'll need to look at the phrasewheel code again. I don't think the
phrasewheel code is that easy to use, except when dealing with plugged in
phrase wheel matchers written by Karsten ( it's his design, so it's
easiest for him to do phrasewheel configuration). Many of them are
direct to sql
bound matchers, but I think eventually K will come up with business
object intermediate matchers , e.g. because they provide for caching
better.
Another old idea is to do a text only version of this clinical editor
, no wx stuff at all, so that the functionality can be worked out first,
and then add the gui acrobatics later.
I've been playing around with the regex re package again.
Regular expressions is a kind of language for parsers.
it's like looking for "*.doc" files in dos command line, only
it's more expressive.
In summary this is its syntax:
. is any character.
\w is a word alphanumeric character. \W is any non word character
\d is a digit character. \D is any non digit character
\s is a whitespace character (including tabs). \S is any non white
space character
* is 0 or more occurences of the previous regex expression, + is 1 or
more , and ? is 0 or 1 occurence.
() means everything inside the brackets is one regex expression, and
also means anything that matches the regex inside
the brackets will be treated as a GROUP ; when there are multiple
nested groups like ( ((\w+)(\d+))\w+) the outer group is group 0, the
next inner 2 groups are groups 1, and 2, and the innermost groups are
3, and 4.
The attached program uses regex to try to match keyed patterns, and
returns the category key to which the a pattern matches.
So you can use regex for categorising items, and if you have a nested
structure, the categories could be heirarchical.
e.g. try typing >" 3 days headache, runny nose, cough; requests
medical certificate"
So in this example, you can get a reason category, a duration , and
symptoms by systems list of the S/ text that is typed.
The re package is in the python html documentation , under the library
section.
The main re commands are :
my_re_prog= re.compile( re_expression)
my_re_prog.findall( input_text) returns all the text that matches in
the input text , starting anywhere.
my_re_prog.search( input_text) returns a match object do which
further commands can be issued to examine the result ( in the re
python html documentation).
now, if I knew how to implement the re package, then I'd consider
myself a good programmer.
------------------------------------------------------------------------
site= [ 'finger', 'wrist', 'thumb', 'hand', 'forearm', 'elbow', 'arm',
'shoulder', 'neck', 'back', 'lumbar', 'thoracic', 'ribs', 'sacral', 'pelvis',
'side', 'loin', 'hips', 'thigh','hamstring', 'groin', 'knee', 'shin', 'calf',
'ankle', 'heel', 'foot', 'toe', 'bottom', 'bum', 'eyelid', 'ear canal',
'(pinna|ear helix|helix\s*\w*\s+ear)']
mskSite = ''.join(('(', '|'.join(site),')'))
pain='(sore\w*|pain\w*|hurt\w*|ache\w*)'
discharge='(discha\w+|runn\w+|sticky|gummy|water\w*)'
blood='((bleed\w*)|(blood\w*))'
immune='((vacc\w*)|(immun\w*))'
lac='((lac\w*)|cut|(wound\w*))'
absc='(boil|infect\w*|abscess\w*)'
lump='(lump|swelling|mass)'
i_time='(((\d+|few|some|many)\s*((day\w?)|(d)\s|(wk\w*)|(wee\w+)|(mo\w*)|(mnt\w*)|(yr\w*)|(year\w*)){1}))'
s = [ 'reason', 'gen', 'cvs', 'rs', 'gi', 'ug', 'cns', 'msk', 'skin' , 'ent',
'opth', 'gyn']
s1= [ ('duration', [i_time]),
('reason', ['med\w*\s+cert\w*', 'workcover', 'taxi\s+lic\w+', '((emp\w*|work)\s+(med\w*|phys\w*))|((exam\w*)|(assess\w*))', 'script\w*', 'checkup|(rev\w*)', '('+immune+'\s+'+i_time+')','('+i_time+'\s+'+immune+')' ,'(accid\w*)|(injur\w*)', 'unwell' ] ),
('gen', ['hot', 'cold', 'feve\w+|febrile', 'weak\w+', 'tired', 'yellow|jaund\w+', 'pale|pall\w+', 'unwell', '(low)|(w\w*t loss)|(loss of w\w*t)'] ) ,( 'cvs', ['chest pain', 'palpitations', 'soboe', 'blackout', 'dizz', 'faint', 'bp' ] ),
('rs' , ['sob', 'cough', 'phlegm', 'wheeze', 'pleuritic', '(tight\w*|chest\w*){2}'] ),
('gi' , ['central\s+abdo','epigastric', 'ruq','lif', 'rif', 'luq',
'(abdo\w+\s+pain)', 'heartburn|reflux', 'regurg', 'vomit', 'naus', 'diarrh',
'constip', '(abdo\w+|upset){2}', '(alt.+bowel)|(bowel.+alt\w+)',
'change.+bow\w+', 'bow.+change\w*',
'((incr\w+|decr\w+|fewer|more|less)|(bowel\s+mot\w+|move\w+))', 'pr bleed',
'coffee grounds', 'black.*motion'] ),
('ug', ['blood.*urine', 'haematuria', 'urgency', 'frequency',
'dysuria', 'hesitancy', 'nocturia'] ),
('cns', ['headache', 'dizziness', 'numbness', 'weakness', 'stiffness',
'tremor', 'difficult|walk', 'fall', 'photophob', 'neck stiff', 'one|sided' ] ),
('msk' , [
mskSite + '\s+'+pain , pain+'\s+'+mskSite ,
mskSite + '\s+'+blood, blood+'\s*\w*\s+'+mskSite ,
mskSite + '\s+'+lac, lac+'\s*\w*\s+'+mskSite ,
mskSite + '\s+'+absc, absc+'\s*\w*\s+'+mskSite ,
mskSite + '\s+'+lump, lump+'\s*\w*\s+'+mskSite ,
'(myalgia)|(musc\w+\s+'+pain+')', 'weak\w+'
] ),
('ent' ,[
'dry throat', '('+pain+'\s+throat)' , '(throat\s+'+pain+')', '(coryza|run\w+\s+nose)'
'(ear\s*'+pain+')', '('+pain+'\s+ear'+')',
'(ear\s*'+discharge+')', '('+discharge+'\s+ear'+')' ,
'(nose\s*'+pain+')', '('+pain+'\s+nose'+')',
'(nose\s*'+discharge+')', '('+discharge+'\s+nose'+')'
'(nose\s*'+blood+')', '('+blood+'\s+nose'+')'
] ),
('opth', [
'(eye\s*'+pain+')', '('+pain+'\s+eye'+')',
'(eye\s*'+discharge+')', '('+discharge+'\s+eye'+')',
'(eyelid\s*'+pain+')', '('+pain+'\s+eyelid'+')'
]
),
('gyn', [
'(no period|amenorrh\w+|late period)',
'(menorrhagia|heavy period|dub|period\s? too
long|period\s? too heavy)',
'(recent (prog\w*\s+)?implant|recent depot prog\w+)',
'frequent periods',
'infrequent periods',
'pelvic pain',
'(period pains|painful period|dysmenorrh\w+)'
]
) ]
re_map = {}
import re
for (k,x) in s1:
re_map[k]= []
for y in x:
print y, k
re_map[k].append ( re.compile(y) )
while 1:
inp = raw_input('>')
model = []
for k,v in re_map.items():
for r in v:
terms = r.findall(inp)
if terms <> []:
model.append( (k, (terms, r.pattern) ) )
print "The Model found was ", model