medattrib 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/LICENSE +176 -0
- data/README.md +85 -0
- data/bin/medattrib +25 -0
- data/lib/list_tagger.rb +36 -0
- data/lib/list_taggers/dose_form_tagger.rb +93 -0
- data/lib/list_taggers/frequency_tagger.rb +91 -0
- data/lib/list_taggers/route_tagger.rb +185 -0
- data/lib/medattrib.rb +80 -0
- data/lib/run_medattrib.rb +23 -0
- data/lib/run_medattrib.rb~ +9 -0
- data/lib/tagger.rb +91 -0
- data/lib/taggers/dispense_quantity_tagger.rb +37 -0
- data/lib/taggers/dose_amount_tagger.rb +68 -0
- data/lib/taggers/duration_tagger.rb +28 -0
- data/lib/taggers/indication_tagger.rb +30 -0
- data/lib/taggers/refill_tagger.rb +58 -0
- data/lib/taggers/strength_tagger.rb +38 -0
- data/lib/taggers/strength_unit_tagger.rb +37 -0
- data/lib/taggers/substitution_tagger.rb +44 -0
- data/lib/taggers/timing_tagger.rb +40 -0
- metadata +82 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright 2015 The MITRE Corporation
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
=end
|
|
16
|
+
|
|
17
|
+
class RouteTagger < ListTagger
|
|
18
|
+
def initialize
|
|
19
|
+
@name = 'route'
|
|
20
|
+
@list = [/ORAL(?:ly)?/i,
|
|
21
|
+
/p\.o\./i,
|
|
22
|
+
/po\b/i,
|
|
23
|
+
/by\s+mouth/i,
|
|
24
|
+
/per\s+(?:oris|rectum|nostril|mouth)/i,
|
|
25
|
+
/INTRAVENOUS(?:ly)?/i,
|
|
26
|
+
/SUBCU(?:TANEOUS(?:ly)?)?/i,
|
|
27
|
+
/subq\.?/i,
|
|
28
|
+
/INTRAPERITONEAL(?:ly)?/i,
|
|
29
|
+
/INTRAMUSCULAR(?:ly)?/i,
|
|
30
|
+
/INTRATHORACIC/i,
|
|
31
|
+
/INTRA-ARTICULAR(?:ly)?/i,
|
|
32
|
+
/INTRADERMAL(?:ly)?/i,
|
|
33
|
+
/EPIDURAL(?:ly)?/i,
|
|
34
|
+
/INTRASINAL(?:ly)?/i,
|
|
35
|
+
/TOPICAL(?:ly)?/i,
|
|
36
|
+
/AURICULAR(?:ly)?/i,
|
|
37
|
+
/OTIC/i,
|
|
38
|
+
/((?:right|left|both)\s+)?ears?/i,
|
|
39
|
+
/NASAL(?:ly)?/i,
|
|
40
|
+
/nose/i,
|
|
41
|
+
/opthalimic/i,
|
|
42
|
+
/eyes?/i,
|
|
43
|
+
/VAGINAL(?:ly)?/i,
|
|
44
|
+
/RECTAL (?:ly)?/i,
|
|
45
|
+
/URETHRAL(?:ly)?/i,
|
|
46
|
+
/INHALATION/i,
|
|
47
|
+
/INTRASYNOVIAL(?:ly)?/i,
|
|
48
|
+
/INTRATUMOR/i,
|
|
49
|
+
/INTRAVASCULAR(?:ly)?/i,
|
|
50
|
+
/INTRASPINAL(?:ly)?/i,
|
|
51
|
+
/INTRACAVITARY/i,
|
|
52
|
+
/SUBLINGUAL(?:ly)?/i,
|
|
53
|
+
/INTRABURSAL/i,
|
|
54
|
+
/INTRACARDIAC/i,
|
|
55
|
+
/INTRAUTERINE/i,
|
|
56
|
+
/CAUDAL BLOCK/i,
|
|
57
|
+
/BUCCAL(?:ly)?/i,
|
|
58
|
+
/CNBLK/i,
|
|
59
|
+
/INTRATHECAL(?:ly)?/i,
|
|
60
|
+
/RETROBULBAR/i,
|
|
61
|
+
/INTRATRACHEAL(?:ly)?/i,
|
|
62
|
+
/INTRAOCULAR(?:ly)?/i,
|
|
63
|
+
/INTRA-ARTERIAL(?:ly)?/i,
|
|
64
|
+
/DENTAL(?:ly)?/i,
|
|
65
|
+
/PERIODONTAL(?:ly)?/i,
|
|
66
|
+
/IMPLANTATION/i,
|
|
67
|
+
/INTRALESIONAL/i,
|
|
68
|
+
/INTRAPLEURAL/i,
|
|
69
|
+
/PERIARTICULAR/i,
|
|
70
|
+
/INTRAGASTRIC/i,
|
|
71
|
+
/INTRADUODENAL/i,
|
|
72
|
+
/INTRAVENTRICULAR/i,
|
|
73
|
+
/INTRATENDINOUS/i,
|
|
74
|
+
/PERIDURAL/i,
|
|
75
|
+
/INTRACERVICAL/i,
|
|
76
|
+
/INTRADURAL/i,
|
|
77
|
+
/SUBMUCOSAL/i,
|
|
78
|
+
/VOCAL CORDS/i,
|
|
79
|
+
/INTRA-ABDOMINAL/i,
|
|
80
|
+
/EXTRACORPOREAL/i,
|
|
81
|
+
/NERVE BLOCK/i,
|
|
82
|
+
/INTRA-AMNIOTIC/i,
|
|
83
|
+
/INTRAPROSTATIC/i,
|
|
84
|
+
/INVITRO/i,
|
|
85
|
+
/SUBARACHNOID/i,
|
|
86
|
+
/INTRABRONCHIAL/i,
|
|
87
|
+
/CONJUNCTIVAL/i,
|
|
88
|
+
/NASOGASTRIC/i,
|
|
89
|
+
/INTRAESOPHAGEAL/i,
|
|
90
|
+
/INTERSTITIAL/i,
|
|
91
|
+
/IV\b/i,
|
|
92
|
+
/IM\b/i,
|
|
93
|
+
/SUBCONJUNCTIVAL/i,
|
|
94
|
+
/SURGICAL/i,
|
|
95
|
+
/AN,INFILTRATION/i,
|
|
96
|
+
/AN,SURFACE/i,
|
|
97
|
+
/INTRATHECAL/i,
|
|
98
|
+
/ORAL-21/i,
|
|
99
|
+
/ORAL-28/i,
|
|
100
|
+
/ORAL-20/i,
|
|
101
|
+
/BUCCAL\/SUBLINGUAL/i,
|
|
102
|
+
/SOFT TISSUE/i,
|
|
103
|
+
/INTRATESTICULAR/i,
|
|
104
|
+
/URETERAL/i,
|
|
105
|
+
/PERCUTANEOUS/i,
|
|
106
|
+
/S(?:C|Q)\b/i,
|
|
107
|
+
/INFUSION/i,
|
|
108
|
+
/INHALATION, NASAL/i,
|
|
109
|
+
/PERFUSION/i,
|
|
110
|
+
/INTRACORONAL, DENTAL/i,
|
|
111
|
+
/PERFUSION\/CARDIAC/i,
|
|
112
|
+
/INTRACORONARY/i,
|
|
113
|
+
/ENDOCAVITARY/i,
|
|
114
|
+
/INTRADISCAL/i,
|
|
115
|
+
/(?:TRANS)?MUCOSAL/i,
|
|
116
|
+
/mucous membrane/i,
|
|
117
|
+
/INTRADUCTAL/i,
|
|
118
|
+
/TRANSTYMPANIC/i,
|
|
119
|
+
/PERFUSION, BILIARY/i,
|
|
120
|
+
/INTRAEPIDERMAL/i,
|
|
121
|
+
/INTRAGINGIVAL/i,
|
|
122
|
+
/INTRALUMINAL/i,
|
|
123
|
+
/INTRAVITREAL/i,
|
|
124
|
+
/ENTERAL/i,
|
|
125
|
+
/INTRAPERICARDIAL/i,
|
|
126
|
+
/INTRALYMPHATIC/i,
|
|
127
|
+
/INTRATUBULAR/i,
|
|
128
|
+
/INTRAOVARIAN/i,
|
|
129
|
+
/TRANSTRACHEAL/i,
|
|
130
|
+
/SPINAL/i,
|
|
131
|
+
/ELECTRO-OSMOSIS/i,
|
|
132
|
+
/(?:TRANS)?DERMAL/i,
|
|
133
|
+
/PHOTOPHERESIS/i,
|
|
134
|
+
/THROAT/i,
|
|
135
|
+
/INFILTRATION/i,
|
|
136
|
+
/INTRABILIARY/i,
|
|
137
|
+
/INTRACARTILAGINOUS/i,
|
|
138
|
+
/UNASSIGNED/i,
|
|
139
|
+
/LARYNGEAL/i,
|
|
140
|
+
/INTRAILEAL/i,
|
|
141
|
+
/IRRIGATION/i,
|
|
142
|
+
/IONTOPHORESIS/i,
|
|
143
|
+
/AN,SYMPATHETIC NBLK/i,
|
|
144
|
+
/INTRAVESICAL/i,
|
|
145
|
+
/INTRAVESICAL/i,
|
|
146
|
+
/INTRATYMPANIC/i,
|
|
147
|
+
/ENDOTRACHEAL/i,
|
|
148
|
+
/EXTRA-AMNIOTIC/i,
|
|
149
|
+
/INTRACORPORUS CAVERNOSUM/i,
|
|
150
|
+
/INTRACEREBRAL/i,
|
|
151
|
+
/INTRACISTERNAL/i,
|
|
152
|
+
/INTRACORNEAL/i,
|
|
153
|
+
/INTRAMEDULLARY/i,
|
|
154
|
+
/INTRAMENINGEAL/i,
|
|
155
|
+
/OROPHARYNGEAL/i,
|
|
156
|
+
/PARENTERAL/i,
|
|
157
|
+
/PERINEURAL/i,
|
|
158
|
+
/INTRACAUDAL/i,
|
|
159
|
+
/INTRAPULMONARY/i,
|
|
160
|
+
/AURICULAR/i,
|
|
161
|
+
/(?:SUB-)?CUTANEOUS/i,
|
|
162
|
+
/ENDOCERVICAL/i,
|
|
163
|
+
/INTRACAVERNOUS/i,
|
|
164
|
+
/ENDOSINUSIAL/i,
|
|
165
|
+
/OCCLUSIVE DRESSING TECHNIQUE/i,
|
|
166
|
+
/RESPIRATORY/i,
|
|
167
|
+
/INTRAVENOUS DRIP/i,
|
|
168
|
+
/INTRAVENOUS BOLUS/i,
|
|
169
|
+
/HEMODIALYSIS/i,
|
|
170
|
+
/TRANSPLACENTAL/i,
|
|
171
|
+
/INTRAEPICARDIAL/i,
|
|
172
|
+
/INTRAHEPATIC/i,
|
|
173
|
+
/INTRALINGUAL/i,
|
|
174
|
+
/INTRAMAMMARY/i,
|
|
175
|
+
/INTRANODAL/i,
|
|
176
|
+
/INTRAOMENTUM/i,
|
|
177
|
+
/INTRARUMINAL/i,
|
|
178
|
+
/SUBGINGIVAL/i,
|
|
179
|
+
/SUBRETINAL/i,
|
|
180
|
+
/TRANSENDOCARDIAL/i,
|
|
181
|
+
/OPHTHALMIC/i,
|
|
182
|
+
/\b(?:OD|OS)\b/i,
|
|
183
|
+
/apply\s+to\s+(?:skin|affected\s+areas?)/i]
|
|
184
|
+
end
|
|
185
|
+
end
|
data/lib/medattrib.rb
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright 2015 The MITRE Corporation
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
A replacement for MedAttrib.pl.
|
|
18
|
+
|
|
19
|
+
The MedAttrib class method parse(text) returns an
|
|
20
|
+
AnnotatedString which contains the original text and a list of the tags
|
|
21
|
+
found. Because "doseamount" depends on the presence of other tags,
|
|
22
|
+
the taggers have been split into pre- and post-doseamount taggers.
|
|
23
|
+
|
|
24
|
+
Example usage:
|
|
25
|
+
tagger = MedAttrib.new
|
|
26
|
+
annotated_string = tagger.parse("take daily.")
|
|
27
|
+
# annotated_string is now "take daily." with a "freq" tag labeling "daily" in standoff annotation (class AnnotatedString). This can be printed as inline xml using AnnotatedString#to_s:
|
|
28
|
+
|
|
29
|
+
puts annotated_string
|
|
30
|
+
#=> "take <freq>daily</freq>."
|
|
31
|
+
=end
|
|
32
|
+
|
|
33
|
+
gem 'standoff', '>=0.0.3' #data model
|
|
34
|
+
require 'standoff'
|
|
35
|
+
|
|
36
|
+
require_relative 'tagger.rb' #taggers based on regex contexts
|
|
37
|
+
require_relative 'list_tagger.rb' #taggers based on term lists
|
|
38
|
+
|
|
39
|
+
#Tagger child classes:
|
|
40
|
+
require_relative 'taggers/dose_amount_tagger.rb'
|
|
41
|
+
require_relative 'taggers/strength_tagger.rb'
|
|
42
|
+
require_relative 'taggers/strength_unit_tagger.rb'
|
|
43
|
+
require_relative 'taggers/indication_tagger.rb'
|
|
44
|
+
require_relative 'taggers/duration_tagger.rb'
|
|
45
|
+
require_relative 'taggers/dispense_quantity_tagger.rb'
|
|
46
|
+
require_relative 'taggers/refill_tagger.rb'
|
|
47
|
+
require_relative 'taggers/substitution_tagger.rb'
|
|
48
|
+
require_relative 'taggers/timing_tagger.rb'
|
|
49
|
+
|
|
50
|
+
#ListTagger child classes:
|
|
51
|
+
require_relative 'list_taggers/dose_form_tagger.rb'
|
|
52
|
+
require_relative 'list_taggers/route_tagger.rb'
|
|
53
|
+
require_relative 'list_taggers/frequency_tagger.rb'
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class MedAttrib
|
|
57
|
+
def parse (text)
|
|
58
|
+
text = REXML::Text.new(text, false, nil, false).to_s # escape special characters
|
|
59
|
+
taggersExceptDoseAmount = [StrengthTagger.new,
|
|
60
|
+
StrengthUnitTagger.new,
|
|
61
|
+
DoseFormTagger.new,
|
|
62
|
+
RouteTagger.new,
|
|
63
|
+
IndicationTagger.new,
|
|
64
|
+
FrequencyTagger.new,
|
|
65
|
+
TimingTagger.new,
|
|
66
|
+
SubstitutionTagger.new,
|
|
67
|
+
RefillTagger.new,
|
|
68
|
+
DurationTagger.new,
|
|
69
|
+
DispenseQuantityTagger.new]
|
|
70
|
+
|
|
71
|
+
tags = []
|
|
72
|
+
taggersExceptDoseAmount.each {|tagger| tags += tagger.parse_main text}
|
|
73
|
+
as = Standoff::AnnotatedString.new(:signal => text, :tags => tags)
|
|
74
|
+
DoseAmountTagger.new.parse_annotated_string as
|
|
75
|
+
partial_xml = as.to_s
|
|
76
|
+
as
|
|
77
|
+
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
=begin
|
|
4
|
+
Copyright 2015 The MITRE Corporation
|
|
5
|
+
|
|
6
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
you may not use this file except in compliance with the License.
|
|
8
|
+
You may obtain a copy of the License at
|
|
9
|
+
|
|
10
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
|
|
12
|
+
Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
See the License for the specific language governing permissions and
|
|
16
|
+
limitations under the License.
|
|
17
|
+
=end
|
|
18
|
+
|
|
19
|
+
require './medattrib.rb'
|
|
20
|
+
|
|
21
|
+
while line = gets do
|
|
22
|
+
puts runall(line).to_s
|
|
23
|
+
end
|
data/lib/tagger.rb
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright 2015 The MITRE Corporation
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
=end
|
|
16
|
+
|
|
17
|
+
class Tagger
|
|
18
|
+
|
|
19
|
+
@@unit_search_global =
|
|
20
|
+
/(?:units?|micrograms?|milligrams?|mg|grams?|millimoles?
|
|
21
|
+
|milliequivalents?|percent|(?:au|pnu)(?:\/|per\s*)ml
|
|
22
|
+
|mg\.?(?:(?:\/|per\s*)(one|five|1|5)?\s*(?:ml|cc)\.?)?
|
|
23
|
+
|\%|gr\.?|meq\.?|mmol\.?|ugm?s?|mcg)/i
|
|
24
|
+
|
|
25
|
+
@@number_word_search =
|
|
26
|
+
/(?:one|two|three|four|five|six|seven|eight|nine|ten|fifteen|twenty|thirty|sixty)/
|
|
27
|
+
|
|
28
|
+
@@number_numeric_search =
|
|
29
|
+
/(?:\d+\/)?\d+(?:\.\d+)?/
|
|
30
|
+
|
|
31
|
+
@@number_numeric_or_word_search =
|
|
32
|
+
/(?:(?:#{@@number_numeric_search})|(?:#{@@number_word_search}))/
|
|
33
|
+
|
|
34
|
+
@@number_hundred_thousand_search =
|
|
35
|
+
/(?:\shundred|\sthousand)/
|
|
36
|
+
|
|
37
|
+
# search for number (word or numeric) with optional hundred or thousand modifier
|
|
38
|
+
@@number_complete_search =
|
|
39
|
+
/(?:#{@@number_numeric_or_word_search})(?:\s(?:#{@@number_hundred_thousand_search}))?/
|
|
40
|
+
|
|
41
|
+
@content_group = 1
|
|
42
|
+
|
|
43
|
+
# @@numberSearch =
|
|
44
|
+
# /(?:#{@@number_numeric_or_word_search})
|
|
45
|
+
# (?:\shundred|\sthousand)?(?:(?:-|\s+to\s+)
|
|
46
|
+
# (?:\d+(?:\.\d+)?|one|two)
|
|
47
|
+
# (?:\shundred|\sthousand)?)?/i
|
|
48
|
+
|
|
49
|
+
def parse_text(text, precondition, search, content_group = @content_group)
|
|
50
|
+
tags = []
|
|
51
|
+
if text =~ precondition
|
|
52
|
+
|
|
53
|
+
#if there are multiple instances, this will find them all
|
|
54
|
+
#and record them as separate tags, as well as preserving their
|
|
55
|
+
#location in the text
|
|
56
|
+
window = text.dup
|
|
57
|
+
textlocation = 0
|
|
58
|
+
while window =~ search
|
|
59
|
+
instance = search.match window
|
|
60
|
+
beginning = textlocation + instance.begin(content_group)
|
|
61
|
+
ending = textlocation + instance.end(content_group)
|
|
62
|
+
textlocation = ending
|
|
63
|
+
content = instance[content_group]
|
|
64
|
+
window = window.slice instance.end(content_group)..window.length
|
|
65
|
+
normalized = normalize content
|
|
66
|
+
attributes = normalized ? {:normalized => normalized} : {}
|
|
67
|
+
tags << Standoff::Tag.new(:content => content,
|
|
68
|
+
:attributes => attributes,
|
|
69
|
+
:name => @name,
|
|
70
|
+
:start => beginning,
|
|
71
|
+
:end => ending)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
return tags
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
@default_precondition = //
|
|
79
|
+
@default_search = //
|
|
80
|
+
@content_group = 1
|
|
81
|
+
|
|
82
|
+
def parse_main(text)
|
|
83
|
+
tags = parse_text(text, @default_precondition, @default_search, @content_group)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def normalize(text)
|
|
87
|
+
# placeholder for Tagger-subclass-specific normalization mappers,
|
|
88
|
+
# e.g. "once a day" => :qd for the FrequencyTagger
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright 2015 The MITRE Corporation
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
=end
|
|
16
|
+
|
|
17
|
+
class DispenseQuantityTagger < Tagger
|
|
18
|
+
def initialize
|
|
19
|
+
@default_precondition = /(?:dispense|quantity)\s+(?:$be\s+)?\d+/i
|
|
20
|
+
@default_search = /((?:(?:dispense|quantity)\s+)(?:$be\s+)?)(\d+)/i
|
|
21
|
+
@content_group = 2
|
|
22
|
+
@name = 'dispense'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def parse_text(text, precondition, search, content_group)
|
|
26
|
+
tags = super text, @default_precondition, @default_search
|
|
27
|
+
local_precondition = /(?x)(?:dispense|quantity)\s+(?:$be\s+)?(?:one|two|three|
|
|
28
|
+
four|five|six|seven|eight|nine|ten|eleven|twelve|fifteen|
|
|
29
|
+
twenty|thirty|forty|fifty|sixty)/i
|
|
30
|
+
local_search = /(?x)((?:dispense|quantity)\s+(?:$be\s+)?)(one|two|
|
|
31
|
+
three|four|five|six|seven|eight|nine|ten|eleven|
|
|
32
|
+
twelve|thirty|forty|fifty|sixty)/i
|
|
33
|
+
tags += super text, local_precondition, local_search
|
|
34
|
+
return tags
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright 2015 The MITRE Corporation
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
=end
|
|
16
|
+
|
|
17
|
+
class DoseAmountTagger < Tagger
|
|
18
|
+
# TODO replace the use of regular expressions searching for "<doseform" with checking for presence of the standoff annotations
|
|
19
|
+
def initialize
|
|
20
|
+
@name = 'doseamount'
|
|
21
|
+
@content_group = 1
|
|
22
|
+
|
|
23
|
+
@shared_search = /(?x)(?:puffs?|tablespoons?|teaspoons?|bolus(?:es)?|
|
|
24
|
+
scoops?|caps?fuls?|ampules?|tubes?|drops?|gtts?|
|
|
25
|
+
milliliters?|ml|c\.c\.|cc\b|\bL\b|tsp|tbsp)/i
|
|
26
|
+
@default_precondition = /(<doseform)/i
|
|
27
|
+
@default_search = /(?x)((?:\d\s+)?(?:\d(?:\/|-))?\d+(?:\.\d+)?
|
|
28
|
+
(?:\s?(?:-|\/)\s+\d+)?|one|two)(\s+<strength>
|
|
29
|
+
(?:[^<])+<\/strength>\s+<strength_unit>(?:[^<])+
|
|
30
|
+
<\/strength_unit>)?(\s+<doseform)/i
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse_annotated_string(as)
|
|
35
|
+
new_tags = parse_text as.to_s, @default_precondition, @default_search
|
|
36
|
+
local_precondition = /((#{@shared_search})|<route)/i
|
|
37
|
+
local_search = /(?x)((?:(?:\d\s+)?(?:\d(?:\/|-))?(?:\d+(?:\.\d+)?
|
|
38
|
+
(?:\s?(?:-|\/)\s?\d+)?)|one|two))(\s+
|
|
39
|
+
(?:(?:#{@shared_search})|<route))/i
|
|
40
|
+
|
|
41
|
+
new_tags += parse_text as.to_s, local_precondition, local_search
|
|
42
|
+
as.tags += new_tags
|
|
43
|
+
return as
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def normalize(dose)
|
|
47
|
+
re = {:int => /^\d+$/,
|
|
48
|
+
:decimal => /^(\d+)?\.\d+$/,
|
|
49
|
+
:fraction => /^(\d+) ?\/ ?(\d+)$/,
|
|
50
|
+
:compound => /^(\d+) (\d+) ?\/ ?(\d+)$/,
|
|
51
|
+
:range => /^([0-9\/\.]+) ?- ?([0-9\/\.]+)$/}
|
|
52
|
+
x = case dose
|
|
53
|
+
when re[:int]
|
|
54
|
+
Integer dose
|
|
55
|
+
when re[:decimal]
|
|
56
|
+
Float dose
|
|
57
|
+
when re[:fraction] # simple fraction
|
|
58
|
+
num, denom = dose.split '/'
|
|
59
|
+
Float(num) / Float(denom)
|
|
60
|
+
when re[:compound] # compound fraction
|
|
61
|
+
Integer(dose[re[:compound], 1]) + Float(dose[re[:compound], 2] )/Float(dose[re[:compound], 3])
|
|
62
|
+
when re[:range]
|
|
63
|
+
normalize(dose[re[:range], 1]) .. normalize(dose[re[:range], 2])
|
|
64
|
+
else
|
|
65
|
+
[nil,"one","two","three","four","five","six","seven","eight","nine","ten"].index dose.downcase
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright 2015 The MITRE Corporation
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
=end
|
|
16
|
+
|
|
17
|
+
class DurationTagger < Tagger
|
|
18
|
+
def initialize
|
|
19
|
+
@shared_search = /(?x)(?:\d+(?:\-\d)?|one|two|three|four|five|six|seven|
|
|
20
|
+
eight|nine|ten|fifteen|twenty|thirty|sixty)\s*
|
|
21
|
+
(?:days?|weeks?|wks|months?|d\b)/i
|
|
22
|
+
@default_precondition = /#{@shared_search}/i
|
|
23
|
+
@default_search = /(?x)((?:for|x)\s?(?:a\stotal\sof\s+)?(?:up\s+to\s+)?
|
|
24
|
+
(?:the\snext\s+)?)(#{@shared_search})/i
|
|
25
|
+
@content_group = 2
|
|
26
|
+
@name = 'duration'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright 2015 The MITRE Corporation
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
=end
|
|
16
|
+
|
|
17
|
+
class IndicationTagger < Tagger
|
|
18
|
+
def initialize
|
|
19
|
+
@shared_search = /(?x)(?:(?:(?:severe|high|hyper|hypo|low)\s*)?
|
|
20
|
+
(?:(?:chest\s+)?pain|cholesterol(?:emia)
|
|
21
|
+
|constipation|allergic\s+rhinitis|allergy|anxiety|bone\s+
|
|
22
|
+
loss|indigestion|headache|nausea|osteoporosis|wheez
|
|
23
|
+
(?:e|ing)|edema|discomfort|itch(?:ing)?|c\.?\s*diff))/i
|
|
24
|
+
@default_precondition = /#{@shared_search}/i
|
|
25
|
+
@default_search = /(?x)((?:p\.?r\.?n\.?|(?:as\s+needed\s+)?
|
|
26
|
+
for|prevention\s+of)\s+)(#{@shared_search})/i
|
|
27
|
+
@content_group = 2
|
|
28
|
+
@name = 'indication'
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright 2015 The MITRE Corporation
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
=end
|
|
16
|
+
|
|
17
|
+
class RefillTagger < Tagger
|
|
18
|
+
def initialize
|
|
19
|
+
@default_precondition = /\d+\s+refills?/i
|
|
20
|
+
@default_search = /(\d+)(\s+refills?)/i
|
|
21
|
+
@content_group = 1
|
|
22
|
+
@name = 'refill'
|
|
23
|
+
end
|
|
24
|
+
def parse_text(text, precondition, search, content_group)
|
|
25
|
+
tags = super text, @default_precondition, @default_search, @content_group
|
|
26
|
+
|
|
27
|
+
local_precondition = /refill\s+\d+\s+times?/i
|
|
28
|
+
local_search = /(refill\s+)(\d+)(\s+times?)/i
|
|
29
|
+
local_content_group = 2
|
|
30
|
+
tags += super text, local_precondition, local_search, local_content_group
|
|
31
|
+
|
|
32
|
+
@precondition = /refills?\s+times\s+\d+/i
|
|
33
|
+
@search = /(refills?\s+times\s+)(\d+)/i
|
|
34
|
+
tags += super text, local_precondition, local_search, local_content_group
|
|
35
|
+
|
|
36
|
+
local_precondition = /(?x)\b(?:no|zero|one|two|three|four|five|six|seven|
|
|
37
|
+
eight|nine|ten|eleven|twelve)\s+refills?/i
|
|
38
|
+
local_search = /(?x)\b(no|zero|one|two|three|four|five|six|seven|
|
|
39
|
+
eight|nine|ten|eleven|twelve)(\s+refills?)/i
|
|
40
|
+
local_content_group = 1
|
|
41
|
+
tags += super text, local_precondition, local_search, local_content_group
|
|
42
|
+
|
|
43
|
+
@precondition = /(?x)refill\s+(?:no|zero|one|two|three|four|five|six|
|
|
44
|
+
seven|eight|nine|ten|eleven|twelve)\s+times?/i
|
|
45
|
+
@search = /(?x)(refill\s+)(no|zero|one|two|three|four|five|six|
|
|
46
|
+
seven|eight|nine|ten|eleven|twelve)(\s+times?)/i
|
|
47
|
+
local_content_group = 2
|
|
48
|
+
tags += super text, local_precondition, local_search, local_content_group
|
|
49
|
+
|
|
50
|
+
@precondition = /(?x)(?:refills?\s+(?:times\s+)?)(?:none|zero|one|two|
|
|
51
|
+
three|four|five|six|seven|eight|nine|ten|eleven|twelve)+/i
|
|
52
|
+
@search = /(?x)(refills?\s+(?:times\s+)?)(none|zero|one|two|
|
|
53
|
+
three|four|five|six|seven|eight|nine|ten|eleven|twelve)/i
|
|
54
|
+
|
|
55
|
+
tags += super text, local_precondition, local_search, local_content_group
|
|
56
|
+
return tags
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright 2015 The MITRE Corporation
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
=end
|
|
16
|
+
|
|
17
|
+
class StrengthTagger < Tagger
|
|
18
|
+
def initialize
|
|
19
|
+
@shared_search = /(?x)(?:units?|micrograms?|milligrams?|grams?|millimoles?
|
|
20
|
+
|milliequivalents?|percent|(?:au|pnu)(?:\/|per\s*)ml
|
|
21
|
+
|mg\.?(?:(?:\/|per\s*)(one|five|1|5)?\s*(?:ml|cc)\.?)?
|
|
22
|
+
|\%|gr\.?|meq\.?|mmol\.?|ugm?s?|mcg)/i
|
|
23
|
+
@default_precondition = /\s(\.\d+)\s+#{@shared_search}/i
|
|
24
|
+
@default_search = /\s(\.\d+)\s*(#{@shared_search})/i
|
|
25
|
+
@content_group = 1
|
|
26
|
+
@name = 'strength'
|
|
27
|
+
end
|
|
28
|
+
def parse_text(text, precondition, search, content_group)
|
|
29
|
+
tags = super text, @default_precondition, @default_search
|
|
30
|
+
local_precondition = /#{@shared_search}/i
|
|
31
|
+
local_search = /(?x)\s((?:(?:\d+\/)?\d+(?:\.\d+)?|one|two)
|
|
32
|
+
(?:\shundred|\sthousand)?(?:(?:-|\s+to\s+)
|
|
33
|
+
(?:\d+(?:\.\d+)?|one|two)
|
|
34
|
+
(?:\shundred|\sthousand)?)?)\s*(#{@shared_search})/i
|
|
35
|
+
tags += super text, local_precondition, local_search
|
|
36
|
+
return tags
|
|
37
|
+
end
|
|
38
|
+
end
|