File size: 12,813 Bytes
0e43c47
 
 
 
c47c7dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0e43c47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
db283f8
0e43c47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
db283f8
0e43c47
 
 
 
 
 
 
 
 
 
db283f8
0e43c47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
db283f8
 
 
0e43c47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
db283f8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
OTHERS_ID = 18
SUM_INPUTS = 20
OUTPUT_PATH = 'meddec_annots.json'

sum_examples = [
        [['examples/note%d.txt'%i for i in range(1,n)]]
        for n in range(5,1, -1)
        ]

text_examples = [
        ["a 72 year old female with chronic indwelling foley. GNR identified in her blood and there is concern for possible resistant pseudomonas. vanco discontinued and ceftriaxone replaced with zosyn. "],
        ["This is a 73 year old man with CMML with recent admission to OSH for emergent splenectomy after splenic rupture who is admitted for hypoxemia and worsening bilateral ground glass opacities. He was treated aggressively on the floor with antibiotics and other etiologies (PE, MI, etc) were appropriately addressed. He was fluid resusitated and continued on his CMML regimen. Despite this, the patient became progressively hypotensive and was transferred to the ICU for further care.\n"
            "In the ICU the patient continued to deteriorate and developed progressive hypotension and acidosis despite aggressive fluid repletion, pressor support, and bicarbonate drip. He received >8L NS, 8amps bicarb, pressor support w/ levophed and vasopressin, and maximum ventilatory support. Despite these measures, his lactate continued to trend upwards and he became progressively more hypotensive on the PEEP settings required to adequately oxygenate him. Furthermore, the patient developed tumor lysis syndrome in the setting of his chemotherapy and became anuric producing only 40cc of urine over 8hr. Renal service was called emergently to consider dialysis but the family elected to change his code status to DNR/DNI and focus care on comfort as a priority, after discussion w/ his oncologist Dr [**First Name (STitle) 1557**] and to defer more aggressive therapy."],
            ["48 year old male with complicated past medical history, multiple problems notably including ESRD s/p renal transplant complicated by collapsing FSGS, recent MRSA line sepsis, here with fevers and hypotension at dialysis, code sepsis."
            "He met sepsis criteria with fever, tachycardia and likely source of infection at site of tunneled dialysis catheter. Also had leukocytosis with L shift. CXR clear, urine not produced for sample. No central line placed [**3-5**] lack of access. Treated with 2 doses linezolid PO; d/w renal team - preferred vanco use, patient switched to vanco by level and d/c on vanco at HD. Underwent stim test; failed, started on hydrocort at stress dose levels (50 q6), d/w renal, felt uneccessary, patient started on prednisone taper back to home dose of 5 mg PO qd. Held HTN meds in setting of sepsis. Received dose of vanco prior to d/c."
            "Dialysis Catheter - noted morning after admission to be clotted; question whether this was related to blood draw. Instilled tPA in catheter overnight; were able to use cath in AM for HD. "
            "ESRD - Started on prograf; monitored levels, d/c on home dose. As per pharm, must continue to monitor levels in context of using itraconazole. Continued patient on bactrim for prophylaxis given tacrolimus use. To go to dialysis. 7 point HCT drop noted during admission; thought elevated HCT hemoconcentration. Hemolysis labs neg, no stool to guaiac. "
            "PTT elevation - noted on admission, resolved in ICU. DIC labs negative. PT/PTT elevation at discharge c/w warfarin/SC heparin use."
            "Hypertension: History of HTN, on lopressor and diltiazem. "
            "Pulmonary Aspergillus: Stable. On itraconazole and followed by pulmonary as an outpatient. Continued in house"
            "Atrial fibrillation: He is normally rate controlled with metoprolol and anticoagulated with coumadin. NSR on EKG here, continued warfarin, held beta blocker .  "
            "..."
            "Call your PCP or return to the ED for fevers/chills/shakes, chest pain, shortness of breath, pain at the site of your dialysis catheter, nausea, vomiting, or swelling in your legs/feet. "]
            ]

keys = ['c', 'g', 'p', 't', 'd', 'p', 'e', 'f', 'a', 'l']
categories = ['Contact related', 'Gathering additional information', 'Defining problem',
        'Treatment goal', 'Drug related', 'Therapeutic procedure related', 'Evaluating test result',
        'Deferment', 'Advice and precaution', 'Legal and insurance related']
det_desc = ['Admit, discharge, follow-up, referral', 
        'Ordering test, consulting colleague, seeking external information',
        'Diagnostic conclusion, evaluation of health state, etiological inference, prognostic judgment',
        'Quantitative or qualitative',
        'Start, stop, alter, maintain, refrain',
        'Start, stop, alter, maintain, refrain',
        'Positive, negative, ambiguous test results',
        'Transfer responsibility, wait and see, change subject',
        'Advice or precaution',
        'Sick leave, drug refund, insurance, disability']
unicode_symbols = [
        "\U0001F91D",  # Handshake
        "\U0001F50D",  # Magnifying glass
        "\U0001F9E9",  # Puzzle piece
        "\U0001F3AF",  # Target
        "\U0001F48A",  # Pill
        "\U00002702",  # Surgical scissors
        "\U0001F9EA",  # Test tube
        "\U000023F0",  # Alarm clock
        "\U000026A0",  # Warning sign
        "\U0001F4C4"   # Document
        ]

colors = ['#8dd3c7', '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb462', '#b3de69', '#fccde5', '#d9d9d9', '#bc80bd']

color_map = {cat: colors[i] for i,cat in enumerate(categories)}


annotator_desc = """
## Interactive Narrative Annotator

This tool allows you to manually annotate medical text for detailed analysis and model training.  Follow these steps:

1. **Input Text:** Enter or paste the text you want to annotate into the text box. You can also use the provided examples.
2. **Generate Predictions (Optional):** Click "Generate Predictions" to pre-annotate the text using a machine learning model. This can serve as a starting point, which you can then review and modify.  Note: Generating predictions will lock the input textbox to prevent accidental edits.
3. **Select Text:** In the **output box below**, highlight the portion of the text you wish to annotate.
4. **Annotate:** Click one of the category buttons to apply the annotation to the selected text. The selected text will be highlighted with the corresponding color.
5. **Remove/Undo:**
    * **Remove (q):** Click "Remove" or press 'q' to remove the annotation from the selected text.
    * **Undo (z):** Click "Undo" or press 'z' to revert the last annotation action.
6. **Download/View Annotations:**
    * **Annotator ID & Discharge Summary ID:** Enter an identifier for the annotator and an optional ID for the discharge summary. These IDs will be included in the downloaded JSON data.
    * **Refresh Annotations:** Click "Refresh Annotations" to display the current annotations in JSON format.
    * **Download Annotated Text:** Click "Download Annotated Text" to save your annotations as a JSON file.
7. **Clear Annotations:** Click "Clear Annotations" to remove all annotations and start over. This will also unlock the input textbox.


**Keyboard Shortcuts:**

* Use the keys indicated on the buttons to annotate selections.
* `q`: Remove annotation
* `z`: Undo last action
"""

label_desc = """
## Decision Extraction & Classification

This tool allows you to quickly identify and categorize key clinical decisions within a single clinical note.

1. **Input Text:** Enter or paste the clinical note into the text box. You can also select from the provided examples.
2. **Run:** Click the "Run" button to process the text.
3. **View Results:** The labeled output will be displayed, highlighting the identified clinical decisions with color-coded categories.

This provides a simplified overview of the patient's history and key decisions at a glance.
"""
vis_desc = """
## Patient Visualization

This tool allows you to visualize the timeline of clinical decisions across multiple clinical notes for a single patient.

1. **Upload Clinical Notes:** Upload multiple clinical notes in text format. You can also select from the provided examples.
2. **Add/Remove Notes:** Use the "+" and "-" buttons to add or remove input fields for additional notes.
3. **Filter Timeline:**
    * **Decision Type:** Select one or more decision types from the dropdown menu to filter the timeline. Select "All" to display all decision types.
    * **Start/End Date:** Enter a start and end date (MM/DD/YYYY) to filter the timeline by date range.
    * **Filter:** Click the "Filter Timeline" button to apply the selected filters.
4. **Generate Summary:** Click the "Generate Summary" button to generate a summarized view of the clinical decisions organized by date and category. This will also update the timeline plot.
5. **View Timeline:** The interactive timeline plot displays the clinical decisions over time. Hover over each data point to view details.

This visualization helps to understand the progression of clinical decisions and identify trends in patient care.
"""


title = """
<h1 style="text-align: center;">Medical Decisions Extraction, Visualization, and Annotation</h1>

<p style="font-size:1.2em;">This application offers three interactive tools for working with clinical text data:</p>
<p style="font-size:1.2em;">1. <b>Decision Extraction & Classification:</b> Process individual notes and receive highlighted key clinical decisions.</br>
2. <b>Patient Visualization:</b> Upload multiple notes to visualize the timeline of decisions.</br>
3. <b>Interactive Narrative Annotator:</b> Manually annotate text for detailed analysis and model training.</p>
"""

desc = '### Decision Categories\n'
desc += '| | |\n| --- | --- |\n'
for i,cat in enumerate(categories):
    desc += f'| {unicode_symbols[i]} **{cat}** | {det_desc[i]}|\n'

shortcut_js_template = """
<script>
function shortcuts(e) {
switch (e.target.tagName.toLowerCase()) {
        case "input":
        case "textarea":
        break;
        default:
        if (e.key.toLowerCase() === 'q') {
                document.getElementById('btn_q').click();
        } else if (e.key.toLowerCase() === 'z') {
                document.getElementById('btn_z').click();
        } else {
                const buttonKeys = %s;
                for (const key of buttonKeys) {
                if (e.key.toLowerCase() === key) {
                        document.getElementById(`btn_${key}`).click();
                        break;
                }
                }
        }
}
}
document.addEventListener('keypress', shortcuts, false);
</script>
"""

# JavaScript for handling text selection
select_js = """
function(x) {
const element = document.getElementById('annotated-text');
if (!element) {
        console.log("Element not found");
        return [0, 0];
}

const selection = window.getSelection();
if (!selection.rangeCount) {
        console.log("No selection found");
        return [0, 0];
}

const range = selection.getRangeAt(0);

if (!element.contains(range.commonAncestorContainer)) {
        console.log("Selection not within element");
        return [0, 0];
}

const start = getCharacterOffset(element, range.startContainer, range.startOffset);
const end = getCharacterOffset(element, range.endContainer, range.endOffset);


console.log("Selection:", start, end);
return [start, end];


function getCharacterOffset(root, node, offset) {
        let currentOffset = 0;

        const iterator = document.createNodeIterator(root, NodeFilter.SHOW_TEXT);
        let currentNode;

        while (currentNode = iterator.nextNode()) {
        if (currentNode === node) {
                return currentOffset + offset;
        }
        currentOffset += currentNode.textContent.length;
        }
        return currentOffset;
}
}
"""

css="""
.category-legend {border:1px dashed black;}
.text-sm {font-size: 1.5rem; line-height: 200%;}
.gr-sample-textbox {width: 1000px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;}
.gallery {width: 1000px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;}
.text-limit label textarea {height: 150px !important; overflow: scroll; }
.text-gray-500 {color: #111827; font-weight: 600; font-size: 1.25em; margin-top: 1.6em; margin-bottom: 0.6em; 'line-height: 1.6;}
#sum-out {border: 2px solid #007bff; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); max-height: 300px; overflow-y: auto;}
#annotated-text { font-family: monospace; padding: 10px; border: 1px solid #ccc; }
#refresh_btn { max-width: 20px; max-height: 40px;}
.locked-input { background-color: #f0f0f0; border: 1px solid #ccc; pointer-events: none; }
body {
  --text-sm: 12px;
  --text-md: 16px;
  --text-lg: 18px;
  --input-text-size: 16px;
  --section-text-size: 16px;
  --input-background: --neutral-50;
  }
"""