mesh-medical-subject-headings 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ require 'MESH/version'
2
+ require 'MESH/mesh'
@@ -0,0 +1,214 @@
1
+ require_relative 'translator'
2
+
3
+ module MESH
4
+ class Mesh
5
+
6
+ attr_accessor :unique_id, :original_heading, :tree_numbers, :parents, :children, :natural_language_name, :summary, :entries, :useful
7
+
8
+ def original_heading(locale = nil)
9
+ return @original_heading if locale.nil?
10
+ @@translator.translate(@original_heading)
11
+ end
12
+
13
+ def natural_language_name(locale = nil)
14
+ return @natural_language_name if locale.nil?
15
+ @@translator.translate(@natural_language_name)
16
+ end
17
+
18
+ def summary(locale = nil)
19
+ return @summary if locale.nil?
20
+ @@translator.translate(@summary)
21
+ end
22
+
23
+ def entries(locale = nil)
24
+ return @entries if locale.nil?
25
+ @entries.map { |entry| @@translator.translate(entry) }.sort
26
+ end
27
+
28
+ def self.configure(args)
29
+ return if @@configured
30
+ raise ArgumentError.new('MeshHeadingGraph requires a filename in order to configure itself') unless not args[:filename].nil?
31
+ gzipped_file = File.open(args[:filename])
32
+ file = Zlib::GzipReader.new(gzipped_file)
33
+ current_heading = Mesh.new
34
+ file.each_line do |line|
35
+ if line.match(/^\*NEWRECORD$/) #Then store the previous record before continuing
36
+ unless current_heading.unique_id.nil?
37
+ current_heading.entries.sort!
38
+ @@headings << current_heading
39
+ @@by_unique_id[current_heading.unique_id] = current_heading
40
+ current_heading.tree_numbers.each do |tree_number|
41
+ @@by_tree_number[tree_number] = current_heading
42
+ end
43
+ end
44
+ current_heading = Mesh.new
45
+ end
46
+
47
+ matches = line.match(/^UI = (.*)/)
48
+ current_heading.unique_id = matches[1] unless matches.nil?
49
+
50
+ matches = line.match(/^MN = (.*)/)
51
+ current_heading.tree_numbers << matches[1] unless matches.nil?
52
+
53
+ matches = line.match(/^MS = (.*)/)
54
+ current_heading.summary = matches[1] unless matches.nil?
55
+
56
+ matches = line.match(/^MH = (.*)/)
57
+ unless matches.nil?
58
+ mh = matches[1]
59
+ current_heading.original_heading = mh
60
+ current_heading.natural_language_name = mh
61
+ current_heading.entries << mh
62
+ librarian_parts = mh.match(/(.*), (.*)/)
63
+ current_heading.natural_language_name = "#{librarian_parts[2]} #{librarian_parts[1]}" unless librarian_parts.nil?
64
+ end
65
+
66
+ matches = line.match(/^(?:PRINT )?ENTRY = ([^|]+)/)
67
+ unless matches.nil?
68
+ mh = matches[1].chomp
69
+ current_heading.entries << mh
70
+ end
71
+
72
+ end
73
+
74
+ @@by_unique_id.each do |id, heading|
75
+ heading.tree_numbers.each do |tree_number|
76
+ #D03.438.221.173
77
+ parts = tree_number.split('.')
78
+ if parts.size > 1
79
+ parts.pop
80
+ parent_tree_number = parts.join '.'
81
+ parent = @@by_tree_number[parent_tree_number]
82
+ heading.parents << parent unless parent.nil?
83
+ parent.children << heading unless parent.nil?
84
+ end
85
+ end
86
+ end
87
+ @@configured = true
88
+ end
89
+
90
+ def self.find(unique_id)
91
+ raise 'MeshHeadingGraph.configure must be called before use' unless @@configured
92
+ return @@by_unique_id[unique_id]
93
+ end
94
+
95
+ def self.find_by_tree_number(tree_number)
96
+ raise 'MeshHeadingGraph.configure must be called before use' unless @@configured
97
+ return @@by_tree_number[tree_number]
98
+ end
99
+
100
+ def self.where(conditions)
101
+ matches = []
102
+ @@headings.each do |heading|
103
+ matches << heading if heading.matches(conditions)
104
+ end
105
+ matches
106
+ end
107
+
108
+ def self.each
109
+ for i in 0 ... @@headings.size
110
+ yield @@headings[i] if @@headings[i].useful
111
+ end
112
+ end
113
+
114
+ def self.match_in_text(text)
115
+ matches = []
116
+ text = text.downcase
117
+ @@headings.each do |heading|
118
+ heading.entries.each do |entry|
119
+ entry = entry.downcase
120
+ start = /^#{Regexp.quote(entry)}\W+/
121
+ middle = /\W+#{Regexp.quote(entry)}\W+/
122
+ at_end = /\W+#{Regexp.quote(entry)}$/
123
+ if start.match(text) || middle.match(text) || at_end.match(text)
124
+ matches << {heading: heading, matched: entry}
125
+ end
126
+ end
127
+ end
128
+ matches
129
+ end
130
+
131
+ def matches(conditions)
132
+ conditions.each do |field, pattern|
133
+ field_content = self.send(field)
134
+ if field_content.kind_of?(Array)
135
+ return false unless field_content.find { |fc| pattern =~ fc }
136
+ elsif field_content.is_a?(TrueClass) || field_content.is_a?(FalseClass)
137
+ return false unless field_content == pattern
138
+ else
139
+ return false unless pattern =~ field_content
140
+ end
141
+ end
142
+ return true
143
+ end
144
+
145
+ def inspect
146
+ return "#{@unique_id}, #{@original_heading}"
147
+ end
148
+
149
+ private
150
+
151
+ @@configured = false
152
+ @@headings = []
153
+ @@by_unique_id = {}
154
+ @@by_tree_number = {}
155
+ @@default_locale = 'en-US'
156
+ @@translator = Translator.new
157
+
158
+ def initialize
159
+ @useful = true
160
+ @tree_numbers = []
161
+ @parents = []
162
+ @children = []
163
+ @entries = []
164
+ end
165
+
166
+ end
167
+ end
168
+
169
+ #
170
+ #*NEWRECORD
171
+ #RECTYPE = D
172
+ #MH = Calcimycin
173
+ #AQ = AA AD AE AG AI AN BI BL CF CH CL CS CT DU EC HI IM IP ME PD PK PO RE SD ST TO TU UR
174
+ #ENTRY = A-23187|T109|T195|LAB|NRW|NLM (1991)|900308|abbcdef
175
+ #ENTRY = A23187|T109|T195|LAB|NRW|UNK (19XX)|741111|abbcdef
176
+ #ENTRY = Antibiotic A23187|T109|T195|NON|NRW|NLM (1991)|900308|abbcdef
177
+ #ENTRY = A 23187
178
+ #ENTRY = A23187, Antibiotic
179
+ #MN = D03.438.221.173
180
+ #PA = Anti-Bacterial Agents
181
+ #PA = Calcium Ionophores
182
+ #MH_TH = FDA SRS (2014)
183
+ #MH_TH = NLM (1975)
184
+ #ST = T109
185
+ #ST = T195
186
+ #N1 = 4-Benzoxazolecarboxylic acid, 5-(methylamino)-2-((3,9,11-trimethyl-8-(1-methyl-2-oxo-2-(1H-pyrrol-2-yl)ethyl)-1,7-dioxaspiro(5.5)undec-2-yl)methyl)-, (6S-(6alpha(2S*,3S*),8beta(R*),9beta,11alpha))-
187
+ # RN = 37H9VM9WZL
188
+ #RR = 52665-69-7 (Calcimycin)
189
+ #PI = Antibiotics (1973-1974)
190
+ #PI = Carboxylic Acids (1973-1974)
191
+ #MS = An ionophorous, polyether antibiotic from Streptomyces chartreusensis. It binds and transports CALCIUM and other divalent cations across membranes and uncouples oxidative phosphorylation while inhibiting ATPase of rat liver mitochondria. The substance is used mostly as a biochemical tool to study the role of divalent cations in various biological systems.
192
+ # OL = use CALCIMYCIN to search A 23187 1975-90
193
+ #PM = 91; was A 23187 1975-90 (see under ANTIBIOTICS 1975-83)
194
+ #HN = 91(75); was A 23187 1975-90 (see under ANTIBIOTICS 1975-83)
195
+ #MED = *62
196
+ #MED = 847
197
+ #M90 = *299
198
+ #M90 = 2405
199
+ #M85 = *454
200
+ #M85 = 2878
201
+ #M80 = *316
202
+ #M80 = 1601
203
+ #M75 = *300
204
+ #M75 = 823
205
+ #M66 = *1
206
+ #M66 = 3
207
+ #M94 = *153
208
+ #M94 = 1606
209
+ #MR = 20130708
210
+ #DA = 19741119
211
+ #DC = 1
212
+ #DX = 19840101
213
+ #UI = D000001
214
+ #
@@ -0,0 +1,126 @@
1
+ module MESH
2
+ class Translator
3
+
4
+ def translate(input)
5
+ input = input.clone
6
+ @enus_to_engb.each do |match, replacement|
7
+ start_middle_and_end(input, match.downcase, replacement.downcase)
8
+ start_middle_and_end(input, match.capitalize, replacement.capitalize)
9
+ start_middle_and_end(input, match.upcase, replacement.upcase)
10
+ end
11
+ input
12
+ end
13
+
14
+ def initialize
15
+ @enus_to_engb = {
16
+ 'abrigment' => 'abrigement',
17
+ 'acknowledgment' => 'acknowledgement',
18
+ 'airplane' => 'aeroplane',
19
+ 'aluminum' => 'aluminium',
20
+ 'amortize' => 'amortise',
21
+ 'analyze' => 'analyse',
22
+ 'anemia' => 'anaemia',
23
+ 'anesthesia' => 'anaesthesia',
24
+ 'anesthetic' => 'anaesthetic',
25
+ 'annex' => 'annexe',
26
+ 'apprize' => 'apprise',
27
+ 'ardor' => 'ardour',
28
+ 'bisulfate' => 'bisulphate',
29
+ 'caliber' => 'calibre',
30
+ 'celiac' => 'coeliac',
31
+ 'center' => 'centre',
32
+ 'color' => 'colour',
33
+ 'curb' => 'kerb',
34
+ 'cyanmethemoglobin' => 'cyanmethaemoglobin',
35
+ 'defecalgesiophobia' => 'defaecalgesiophobia',
36
+ 'defense' => 'defence',
37
+ 'dialyze' => 'dialyse',
38
+ 'diarrhea' => 'diarrhoea',
39
+ 'diarrheagenic' => 'diarrhoeagenic',
40
+ 'disulfide' => 'disulphide',
41
+ 'dysbetalipoproteinemia' => 'dysbetalipoproteinaemia',
42
+ 'ecology' => 'oecology',
43
+ 'edema' => 'oedema',
44
+ 'electrolyze' => 'electrolyse',
45
+ 'endobrachyesophagus' => 'endobrachyoesophagus',
46
+ 'enrollment' => 'enrolment',
47
+ 'eolian' => 'aeolian',
48
+ 'esophagus' => 'oesophagus',
49
+ 'esophagitis' => 'oesophagitis',
50
+ 'estrogen' => 'oestrogen',
51
+ 'etiology' => 'aetiology',
52
+ 'favor' => 'favour',
53
+ 'favorite' => 'favourite',
54
+ 'fervor' => 'fervour',
55
+ 'fetus' => 'foetus',
56
+ 'fiber' => 'fibre',
57
+ 'flavor' => 'flavour',
58
+ 'fuscocerulius' => 'fuscocaerulius',
59
+ 'genuflection' => 'genuflexion',
60
+ 'gonorrhea' => 'gonorrhoea',
61
+ 'gynecology' => 'gynaecology',
62
+ 'harbor' => 'harbour',
63
+ 'hematemesis' => 'haematemesis',
64
+ 'hemoglobin' => 'haemoglobin',
65
+ 'hemorrhoid' => 'haemorrhoid',
66
+ 'homeopath' => 'homoeopath',
67
+ 'honor' => 'honour',
68
+ 'humor' => 'humour',
69
+ 'ichthyohemotoxism' => 'ichthyohaemotoxism',
70
+ 'inflection' => 'inflexion',
71
+ 'jewelry' => 'jewellery',
72
+ 'judgment' => 'judgement',
73
+ 'kinesiesthesiometer' => 'kinesiaesthesiometer',
74
+ 'labor' => 'labour',
75
+ 'leukemia' => 'leukaemia',
76
+ 'leveling' => 'levelling',
77
+ 'license' => 'licence',
78
+ 'lodgment' => 'lodgement',
79
+ 'luster' => 'lustre',
80
+ 'maneuver' => 'manoeuvre',
81
+ 'marvelous' => 'marvellous',
82
+ 'menorrhea' => 'menorrhoea',
83
+ 'meter' => 'metre',
84
+ 'microhematocrit' => 'microhaematocrit',
85
+ 'mold' => 'mould',
86
+ 'molder' => 'moulder',
87
+ 'molt' => 'moult',
88
+ 'neighbor' => 'neighbour',
89
+ 'occipitolevoposterior' => 'occipitolaevoposterior',
90
+ 'offense' => 'offence',
91
+ 'organize' => 'organise',
92
+ 'orthopedics' => 'orthopaedics',
93
+ 'paralyze' => 'paralyse',
94
+ 'pediatrician' => 'paediatrician',
95
+ 'pediatrics' => 'paediatrics',
96
+ 'phony' => 'phoney',
97
+ 'plow' => 'plough',
98
+ 'pretense' => 'pretence',
99
+ 'rigor' => 'rigour',
100
+ 'savor' => 'savour',
101
+ 'sepulcher' => 'sepulchre',
102
+ 'specter' => 'spectre',
103
+ 'sulfate' => 'sulphate',
104
+ 'sulfethylthiadiazole' => 'sulfaethylthiadiazole',
105
+ 'synesthesia' => 'synaesthesia',
106
+ 'theater' => 'theatre',
107
+ 'tire' => 'tyre',
108
+ 'tumor' => 'tumour',
109
+ 'urohematoporphyrin' => 'urohaematoporphyrin',
110
+ 'vapor' => 'vapour',
111
+ 'vaporize' => 'vaporise'
112
+ }
113
+
114
+ end
115
+
116
+ private
117
+
118
+ def start_middle_and_end(input, match, replacement)
119
+ input.gsub!(/^#{Regexp.quote(match)}$/, replacement) #alone
120
+ input.gsub!(/^#{Regexp.quote(match)}(\W+)/) { "#{replacement}#{$1}" } #start
121
+ input.gsub!(/(\W+)#{Regexp.quote(match)}(\W+)/) { "#{$1}#{replacement}#{$2}" } #middle
122
+ input.gsub!(/(\W+)#{Regexp.quote(match)}$/) { "#{$1}#{replacement}" } #end
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,3 @@
1
+ module Mesh
2
+ VERSION = "1.2.0"
3
+ end
@@ -0,0 +1,440 @@
1
+ require_relative 'test_helper'
2
+
3
+ module MESH
4
+ describe 'Testing MESH:Mesh core functions' do
5
+
6
+ it 'should yield to a block for each' do
7
+ block_called = false
8
+ MESH::Mesh.each do |h|
9
+ block_called = true
10
+ break
11
+ end
12
+ assert block_called
13
+ end
14
+
15
+ it 'should not have nil headings' do
16
+ MESH::Mesh.each do |h|
17
+ refute_nil h
18
+ end
19
+ end
20
+
21
+ it 'should find by unique id' do
22
+ mh = MESH::Mesh.find('D000001')
23
+ refute_nil mh
24
+ end
25
+
26
+ it 'should find by tree number' do
27
+ mh = MESH::Mesh.find_by_tree_number('G14.640.079')
28
+ refute_nil mh
29
+ assert_equal 'D000065', mh.unique_id
30
+ end
31
+
32
+ it 'should have the correct unique id' do
33
+ mh = MESH::Mesh.find('D000001')
34
+ assert_equal 'D000001', mh.unique_id
35
+ end
36
+
37
+ it 'should have the correct tree number' do
38
+ mh = MESH::Mesh.find('D000001')
39
+ assert_equal 1, mh.tree_numbers.length
40
+ assert_includes mh.tree_numbers, 'D03.438.221.173'
41
+ end
42
+
43
+ it 'should have the correct tree numbers' do
44
+ mh = MESH::Mesh.find('D000224')
45
+ assert_equal 2, mh.tree_numbers.length
46
+ assert_includes mh.tree_numbers, 'C19.053.500.263'
47
+ assert_includes mh.tree_numbers, 'C20.111.163'
48
+ end
49
+
50
+ it 'should have the correct original heading' do
51
+ mh = MESH::Mesh.find('D000224')
52
+ assert_equal 'Addison Disease', mh.original_heading
53
+ mh = MESH::Mesh.find('D000014')
54
+ assert_equal 'Abnormalities, Drug-Induced', mh.original_heading
55
+ end
56
+
57
+ it 'should have anglicised original heading' do
58
+ mh = MESH::Mesh.find('D001471')
59
+ assert_equal 'Barrett Esophagus', mh.original_heading
60
+ assert_equal 'Barrett Oesophagus', mh.original_heading('en-GB')
61
+ end
62
+
63
+ it 'should have natural language name' do
64
+ mh = MESH::Mesh.find('D000224')
65
+ assert_equal 'Addison Disease', mh.natural_language_name
66
+ mh = MESH::Mesh.find('D000014')
67
+ assert_equal 'Drug-Induced Abnormalities', mh.natural_language_name
68
+ end
69
+
70
+ it 'should have anglicised natural language name' do
71
+ mh = MESH::Mesh.find('D001471')
72
+ assert_equal 'Barrett Esophagus', mh.natural_language_name
73
+ assert_equal 'Barrett Oesophagus', mh.natural_language_name('en-GB')
74
+ end
75
+
76
+ it 'should have the correct summary' do
77
+ mh = MESH::Mesh.find('D000238')
78
+ assert_equal 'A benign tumor of the anterior pituitary in which the cells do not stain with acidic or basic dyes.', mh.summary
79
+ end
80
+
81
+ it 'should have anglicised summary' do
82
+ mh = MESH::Mesh.find('D001471')
83
+ assert_equal 'A condition with damage to the lining of the lower ESOPHAGUS resulting from chronic acid reflux (ESOPHAGITIS, REFLUX). Through the process of metaplasia, the squamous cells are replaced by a columnar epithelium with cells resembling those of the INTESTINE or the salmon-pink mucosa of the STOMACH. Barrett\'s columnar epithelium is a marker for severe reflux and precursor to ADENOCARCINOMA of the esophagus.', mh.summary
84
+ assert_equal 'A condition with damage to the lining of the lower OESOPHAGUS resulting from chronic acid reflux (OESOPHAGITIS, REFLUX). Through the process of metaplasia, the squamous cells are replaced by a columnar epithelium with cells resembling those of the INTESTINE or the salmon-pink mucosa of the STOMACH. Barrett\'s columnar epithelium is a marker for severe reflux and precursor to ADENOCARCINOMA of the oesophagus.', mh.summary('en-GB')
85
+ end
86
+
87
+ it 'should have the correct entries' do
88
+ expected_entries = ['Activity Cycles', 'Ultradian Cycles', 'Activity Cycle', 'Cycle, Activity', 'Cycle, Ultradian', 'Cycles, Activity', 'Cycles, Ultradian', 'Ultradian Cycle']
89
+ expected_entries.sort!
90
+ mh = MESH::Mesh.find('D000204')
91
+ assert_equal expected_entries, mh.entries
92
+ end
93
+
94
+ it 'should have anglicised entries' do
95
+ expected_entries = ['Barrett Esophagus', 'Barrett Syndrome', 'Esophagus, Barrett', 'Barrett Epithelium', 'Barrett Metaplasia', 'Barrett\'s Esophagus', 'Barrett\'s Syndrome', 'Barretts Esophagus', 'Barretts Syndrome', 'Epithelium, Barrett', 'Esophagus, Barrett\'s', 'Syndrome, Barrett', 'Syndrome, Barrett\'s']
96
+ expected_entries_en = ['Barrett Oesophagus', 'Barrett Syndrome', 'Oesophagus, Barrett', 'Barrett Epithelium', 'Barrett Metaplasia', 'Barrett\'s Oesophagus', 'Barrett\'s Syndrome', 'Barretts Oesophagus', 'Barretts Syndrome', 'Epithelium, Barrett', 'Oesophagus, Barrett\'s', 'Syndrome, Barrett', 'Syndrome, Barrett\'s']
97
+ expected_entries.sort!
98
+ expected_entries_en.sort!
99
+ mh = MESH::Mesh.find('D001471')
100
+ assert_equal expected_entries, mh.entries
101
+ assert_equal expected_entries_en, mh.entries('en-GB')
102
+ end
103
+
104
+ it 'should have the correct parent' do
105
+ mh = MESH::Mesh.find('D000001')
106
+ assert_equal 1, mh.parents.length
107
+ assert_equal 'D001583', mh.parents[0].unique_id
108
+ end
109
+
110
+ it 'should have the correct parents' do
111
+ mh = MESH::Mesh.find('D000224')
112
+ p1 = MESH::Mesh.find('D000309')
113
+ p2 = MESH::Mesh.find('D001327')
114
+ assert_equal 2, mh.parents.length
115
+ assert_includes mh.parents, p1
116
+ assert_includes mh.parents, p2
117
+ end
118
+
119
+ it 'should have the correct children' do
120
+ parent = MESH::Mesh.find_by_tree_number('C19.053.500')
121
+ child1 = MESH::Mesh.find_by_tree_number('C19.053.500.263')
122
+ child2 = MESH::Mesh.find_by_tree_number('C19.053.500.270')
123
+ child3 = MESH::Mesh.find_by_tree_number('C19.053.500.480')
124
+ child4 = MESH::Mesh.find_by_tree_number('C19.053.500.740')
125
+
126
+ assert_equal 4, parent.children.length
127
+ assert_includes parent.children, child1
128
+ assert_includes parent.children, child2
129
+ assert_includes parent.children, child3
130
+ assert_includes parent.children, child4
131
+ end
132
+
133
+ it 'should match on conditions for original_heading' do
134
+ mh = MESH::Mesh.find('D001471')
135
+ assert mh.matches(original_heading: /^Barrett Esophagus$/)
136
+ end
137
+
138
+ it 'should not match on incorrect condition for original_heading' do
139
+ mh = MESH::Mesh.find('D001471')
140
+ refute mh.matches(original_heading: /^Foo$/)
141
+ end
142
+
143
+ it 'should match on conditions for entries' do
144
+ mh = MESH::Mesh.find('D001471')
145
+ assert mh.matches(entries: /Metaplasia/)
146
+ end
147
+
148
+ it 'should not match on incorrect conditions for entries' do
149
+ mh = MESH::Mesh.find('D001471')
150
+ refute mh.matches(entries: /Foo/)
151
+ end
152
+
153
+ it 'should match on conditions for summary' do
154
+ mh = MESH::Mesh.find('D001471')
155
+ assert mh.matches(summary: /the lower ESOPHAGUS resulting from chronic acid reflux \(ESOPHAGITIS, REFLUX\)\./)
156
+ end
157
+
158
+ it 'should not match on incorrect conditions for summary' do
159
+ mh = MESH::Mesh.find('D001471')
160
+ refute mh.matches(summary: /Foo/)
161
+ end
162
+
163
+ it 'should match on conditions for useful' do
164
+ mh = MESH::Mesh.find('D001471')
165
+ begin
166
+ mh.useful = true
167
+ assert mh.matches(useful: true)
168
+ refute mh.matches(useful: false)
169
+ mh.useful = false
170
+ assert mh.matches(useful: false)
171
+ refute mh.matches(useful: true)
172
+ ensure
173
+ mh.useful = true
174
+ end
175
+ end
176
+
177
+ it 'should match on multiple conditions' do
178
+ mh = MESH::Mesh.find('D001471')
179
+ assert mh.matches(original_heading: /^Barrett Esophagus$/, summary: /the lower ESOPHAGUS/)
180
+ end
181
+
182
+ it 'should not match on incorrect multiple conditions' do
183
+ mh = MESH::Mesh.find('D001471')
184
+ refute mh.matches(original_heading: /^Barrett Esophagus$/, summary: /Foo/)
185
+ refute mh.matches(original_heading: /^Foo/, summary: /the lower ESOPHAGUS/)
186
+ end
187
+
188
+ it 'should match headings that occur in given text' do
189
+ expected_ids = %w(D001491 D001769 D001792 D001842 D001853 D001853 D002470 D002477 D002477 D002648 D002648 D002875 D002965 D002999 D003062 D003561 D003593 D003643 D004194 D004314 D004314 D004314 D004314 D004813 D004912 D005091 D005123 D005123 D005293 D005333 D005385 D005385 D005544 D005796 D006128 D006225 D006309 D006321 D006331 D006664 D007107 D007223 D007231 D007231 D007231 D007239 D007246 D007938 D008099 D008168 D008214 D008214 D008423 D008533 D008533 D008607 D008722 D009035 D009055 D009132 D009154 D009154 D009190 D009196 D009369 D009666 D010372 D010641 D011153 D012008 D012106 D012146 D012306 D012307 D012380 D012680 D012867 D013534 D013577 D013601 D013812 D013921 D013961 D014034 D014157 D014171 D014314 D014960 D015032 D015994 D015995 D016424 D016433 D017584 D017668 D018387 D018388 D019021 D019070 D019368 D019369 D032882 D036801 D036801 D038042 D041905 D052016 D055016)
190
+ expected = expected_ids.map { |id| MESH::Mesh.find(id) }
191
+ actual = MESH::Mesh.match_in_text(@example_text)
192
+ assert expected, actual
193
+ end
194
+
195
+ it 'should match headings at start of test' do
196
+ text = 'Leukemia, lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec pretium leo diam, quis adipiscing purus bibendum eu.'
197
+ matches = MESH::Mesh.match_in_text(text)
198
+ assert_equal 1, matches.length
199
+ assert_equal MESH::Mesh.find('D007938'), matches[0][:heading]
200
+ end
201
+
202
+ it 'should match headings at end of test' do
203
+ text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec pretium leo diam, quis adipiscing purus bibendum eu leukemia'
204
+ matches = MESH::Mesh.match_in_text(text)
205
+ assert_equal 1, matches.length
206
+ assert_equal MESH::Mesh.find('D007938'), matches[0][:heading]
207
+ end
208
+
209
+ it 'should allow headings to be marked as not useful' do
210
+ mh = MESH::Mesh.find('D055550')
211
+ mh.useful = true
212
+ assert mh.useful
213
+ mh.useful = false
214
+ refute mh.useful
215
+ mh.useful = true
216
+ assert mh.useful
217
+ end
218
+
219
+ it 'should match on useful in where' do
220
+ begin
221
+ expected = [MESH::Mesh.find('D012000'), MESH::Mesh.find('D064906'), MESH::Mesh.find('D064966')]
222
+ expected.each { |mh| mh.useful = false }
223
+ actual = MESH::Mesh.where(useful: false)
224
+ assert_equal expected, actual
225
+ ensure
226
+ expected.each { |mh| mh.useful = true }
227
+ end
228
+ end
229
+
230
+ it 'should only include useful headings in .each' do
231
+ begin
232
+ MESH::Mesh.each do |mh|
233
+ mh.useful = false
234
+ end
235
+ MESH::Mesh.where(unique_id: /^D0000/).each do |mh|
236
+ mh.useful = true
237
+ end
238
+ count = 0
239
+ MESH::Mesh.each do |mh|
240
+ assert mh.useful
241
+ count += 1
242
+ end
243
+ assert_equal 95, count
244
+ ensure
245
+ MESH::Mesh.where(useful: false).each do |mh|
246
+ mh.useful = true
247
+ end
248
+ end
249
+ end
250
+
251
+ it 'should override inspect to prevent issues in test diagnostics' do
252
+ mh = MESH::Mesh.find('D001471')
253
+ expected = "#{mh.unique_id}, #{mh.original_heading}"
254
+ assert_equal expected, mh.inspect
255
+ end
256
+
257
+ it 'should allow headings to be found with a where() match on original_heading' do
258
+ expected = [MESH::Mesh.find('D003561'), MESH::Mesh.find('D016238')]
259
+ actual = MESH::Mesh.where(original_heading: /^Cyta/)
260
+ assert_equal expected, actual
261
+ end
262
+ #MESH::Mesh.where(:entries, /.*Fish.*/)
263
+
264
+ before do
265
+
266
+ @example_text = 'Leukaemia in Downs Syndrome
267
+ Overview
268
+ Downs Syndrome Leukaemia The link between Downs Syndrome and Leukaemia
269
+ Epidemiology
270
+ Aetiology
271
+ Future research Implications for other leukaemias Treatment
272
+ Downs Syndrome
273
+ Originally described in 1866
274
+ Associated with Trisomy 21 in 1959
275
+ Prevalence 1/1000 births
276
+ 95% due to chromosomal non-disjunction; 5% due to translocations Risk factors:
277
+ increased maternal age
278
+ 1/1000 maternal age 30 years
279
+ 9/1000 maternal age 40 years
280
+ infertility treatment
281
+ Clinical Features
282
+ physical appearance
283
+ intellectual disability
284
+ developmental delay
285
+ sensory abnormalities congenital heart disease
286
+ Alzheimers Disease
287
+ GI malformations
288
+ thyroid disorders
289
+ poor immune system
290
+ LEUKAEMIA
291
+ Downs Syndrome
292
+ Leukaemia
293
+ cancer
294
+ WBC proliferation in the bone marrow
295
+ Classification:
296
+ acute/chronic
297
+ type of WBC
298
+ Current leukaemia model:
299
+ 2 co-operating mutations
300
+ 1 leading to impaired differentiation
301
+ 1 leading to increased proliferation/cell survival
302
+ Picture: Hitzler & Zipursky, 2005
303
+ Leukaemia
304
+ Acute lymphoblastic leukaemia (ALL)
305
+ derived from B lymphocyte or T lymphocyte precursors
306
+ 80% childhood leukaemia
307
+ Acute myeloid leukaemia (AML)
308
+ e.g. myeloid, monocytic, megakaryocytic, erythroid
309
+ 20% childhood leukaemia
310
+ Acute megakaryoblastic leukaemia (AMKL)
311
+ AML subtype: leukaemic cells have platelet precursor phenotype
312
+ 6% childhood AML cases
313
+ Leukaemia in Downs Syndrome
314
+ 10-20 fold increased risk of leukaemia
315
+ ALL
316
+ 80% childhood leukaemia; 60% Downs Syndrome leukaemia
317
+ 20 times higher incidence children with Downs Syndrome compared to children without Downs Syndrome AML
318
+ 20% childhood leukaemia; 40% Downs Syndrome leukaemia AMKL
319
+ 6% childhood AML; 62% Downs Syndrome AML
320
+ 500 times higher incidence children with Downs Syndrome compared to children without Downs Syndrome
321
+ Leukaemia in Downs Syndrome
322
+ AML in Downs Syndrome
323
+ AMKL in most cases
324
+ younger median age of onset
325
+ 2 in Downs Syndrome
326
+ 8 in non-Downs Syndrome
327
+ myelodysplastic syndrome more common prior to leukaemia
328
+ Transient Leukaemia
329
+ Transient Leukaemia
330
+ Also termed: Transient Abnormal Myelopoiesis and Transient Myeloproliferative Disorder
331
+ 10% newborn infants with Downs Syndrome
332
+ peripheral blood contains clonal population of megakaryoblasts
333
+ cannot be distinguished from AMKL blasts by routine methods
334
+ usually clinically silent
335
+ usually disappear within 3 months
336
+ majority of cases totally resolve
337
+ However
338
+ can be fatal
339
+ 20% develop MDS and AMKL by the age of 4 yearsTransient Leukaemia
340
+ Leukaemic cells in Transient Leukaemia and AMKL can:
341
+ show variable megakaryocytic differentiation
342
+ show features of multiple haematopoietic lineages Evidence that Transient Leukaemia is a precursor for AMKL
343
+ near identical morphology, immunophenotype, ultrastructure
344
+ clone-specific GATA1 mutations
345
+ GATA1: X chromosome, zinc-finger transcription factor, essential for differentiation of megakaryocytic, erythroid and basophillic lineages
346
+ therefore have common cell of originLeukaemic cells in Transient Leukaemia and AMKL in Downs Syndrome can form megakaryocytic, erythroid or basophillic lineages
347
+ GATA1
348
+ all Transient Leukaemia and AMKL cases have GATA1 mutations
349
+ most abrogate splicing of exon 2 or produce stop codon prior to alternative start codon at position 84
350
+ lack N-terminal domain
351
+ mutations disappear upon remission
352
+ disease specific mutations
353
+ leukemogenisis model: transcription factor mutation blocks differentiation
354
+ GATA1 mutation determines haematopoietic lineage
355
+ GATA1 mutations present in Transient Leukaemia at birth
356
+ mutations in utero
357
+ proportion of Downs Syndrome fetuses acquire GATA1 mutation
358
+ large clone = Transient Leukaemia
359
+ small clone = no clinical signs
360
+ Aetiology
361
+ Three distinct steps:
362
+ fetal heamatopoietic cell with trisomy 21
363
+ rare Transient Leukaemia cases in people without Downs syndrome
364
+ acquired trisomy 21 only in haematopoietic cells
365
+ mutation of GATA1
366
+ expression of shortened
367
+ GATA1 (GATA1s) extra, as of yet unknown event
368
+ not all cases of Transient
369
+ Leukaemia progress to AMKL
370
+ Picture: Hitzler & Zipursky, 2005
371
+ Aetiology
372
+ Transient Leukaemia with clinical signs of disease
373
+ Transient Leukaemia with no clinical signs of diseasePicture: Ahmed et al, 2004
374
+ Future Research
375
+ Loss of GATA1 function in people without Downs Syndrome results in:
376
+ accumulation of abnormally differentiated megakaryocytes
377
+ thrombocytopenia
378
+ NO LEUKAEMIC TRANSFORMATION
379
+ discovered by Shivdasani et al, 1997
380
+ What is the effect of Trisomy 21
381
+ What advantage does GATA1 mutation provide to people with Downs SyndromeWhat is the second-hitImplications for other leukaemias
382
+ current acute leukaemia model:
383
+ 2 co-operating mutations
384
+ 1 leading to impaired differentiation
385
+ 1 leading to increased proliferation/cell survival
386
+ This means that that the sequence of Transient Leukaemia to AMKL as seen in Downs Syndrome is a chance to investigate this model of leukaemia and discover the timing and nature of the 2 necessary events.
387
+ Treatment of Leukaemia in Downs Syndrome
388
+ AML (AMKL)
389
+ increased sensitivity to cytarabine
390
+ 80% 5 year survival
391
+ failure usually due to toxicity (mucositis and infection)ALL
392
+ similar treatment as in AML
393
+ 60-70% cure rate (75-85% in population without Downs Syndrome)
394
+ no increased sensitivity, but increased toxicity
395
+ dose reduction would increase risk of relapse
396
+ supportive care
397
+ References
398
+ Ahmed, M., Sternberg, A., Hall, G., Thomas, A., Smith, O., OMarcaigh, A., Wynn, R., Stevens, R., Addison, M., King, D., Stewart, B., Gibson, B., Roberts, I., Vyas, P. (2004). Natural History of GATA1 mutations in Down syndrome, Blood, 103(7):2480-2489.
399
+ Hitzler, J.K., Cheung, J., Li, Y., Scherer, S.W., Zipursky, A. (2003). GATA1 mutations in transient leukaemia and acute megakaryoblastic leukaemia of Down syndrome, Blood, 101(11):4301-4304.
400
+ Hitzler, J.K., Zipursky, A. (2005). Origins of leukaemia in children with down syndrome, Cancer, 5:11-20.
401
+ Puumala, S.E., Ross, J.A., Olshan, A.F., Robison, L.L., Smith, F.O., Spector, L.G. (2007). Reproductive history, infertility treatment, and the risk of acute leukaemia in children with down syndrome, Cancer, [Epub ahead of print].
402
+ Shivdasani, R.A., Fujiwara, Y., McDevitt, M.A., Orkin, S.H. (1997). A loneage-selective knockout establishes the critical role of transcription factor GATA-1 in megakaryocyte growth and platelet development, Embo J., 16:3965-3973.
403
+ Slordahl, S.H. et al. (1993). Leukaemic blasts with markers of four cell lineages in Down\'s syndrome (megakaryoblastic leukaemia), Med. Pediatr. Oncol., 21:254-258.
404
+ Vyas, P., Crispino, J.D. (2007). Molecular insights into Down syndrome-associated leukemia, Current Opinion in Pediatrics, 19:9-14.
405
+ Webb, D., Roberts, I., Vyas, P. (2007). Haematology of Down syndrome, Arch. Dis. Child. Fetal Neonatal Ed., [published online 5 Sep 2007].
406
+ http://www.intellectualdisability.info/home.htm
407
+ http://news.bbc.co.uk/nol/shared/spl/hi/pop_ups/05/health_shifting_perspectives/img/1.jpg
408
+ Originally described by John Langdon Down
409
+ Associated with Trisomy 21 by Professor Jerome Lejeune
410
+ Overall incidence is 1/1000, so 600 babies with Downs syndrome are born in the UK each year
411
+ It is estimated that there are 60,000 people living with Downs syndrome in the UK
412
+ 80% of children with Downs Syndrome are born to mothers under 35 years of age
413
+ Research suggests that infertility treatment increases risk of chromosomal abnormalitiesFacial appearance: flat profile, flat nasal bridge, small nose, eyes that slant upwards and outwards often with an epicanthic fold a fold of skin that runs vertically between the lids at the inner corner of the eye), small mouth.
414
+ Body appearance: reduced muscle tone, big space between first and second toe, broad hands with short fingers and a little finger that curves inwards, often a single palmar crease.
415
+ Sensory abnormalities: visual and hearing
416
+ Congenital Heart Disease is present in 50% of people with Downs SyndromeThere are two methods of classifying leukaemias: acute/chronic or according to the type of WBC that is proliferating abnormally ALL describes leukaemia where the cancerous cell is derived from precursors of B or less commonly T lymphocytes. ALL makes up 80% of all childhood leukaemias.
417
+ AML describes leukaemia where the cancerous cell is not B or T cell derived and the subtype is determined by the phenotype of the leukaemic cells. AML makes up 20% of childhood leukaemias.
418
+ AMKL is a subtype of AML where the leukaemic cell looks like platelet precursors. AMKL makes up 6% of childhood AML cases.ALL makes up 80% of childhood leukaemia, but 60% of leukaemia in children with DS. Children with DS have a 20 fold increased risk of developing ALL when compared to children without DS.
419
+ AML makes up 20% of childhood leukaemia, but 40% of leukaemia in children with DS.
420
+ AMKL makes up 6% of childhood AML, but in children with Downs Syndrome makes up 62% of AML cases. Children with DS have a 500 fold increased risk of developing AMKL when compared to children without DS.The rest of this talk is going to focus on AMKL in DS. The majority of AML cases in DS are AMKL and have a younger age of onset than in the population without DS. AMKL in DS much more commonly begins as a MDS. This is a process of abnormal megakaryocytic differentiation. Furthermore, children with DS are at risk of Transient Leukaemia a condition that is almost exclusive to children with DS.Tranisent leukaemia is only found in infants with DS and is found in about 10% of newborns with DS. These children are born with a clonal population of megakaryoblasts in their blood. These megakaryoblasts cannot be distinguished from the blasts of AMKL by routine methods and usually spontaneously disappear within the first 3 months of life.
421
+ TL begins in utero and usually does not cause any symptoms. The majority of cases resolve and the children do not have any lasting haematological problems. There is no evidence as to how TL spontaneously resolves. However, it can also be fatal due liver damage or complications in the lungs or heart and 20% of children with TL proceed to develop MDS and AMKL by the age of 4 years.mutations of GATA1 are present in the cells of TL and AMKL in Downs Syndrome and these cells can become megakaryocytes, erythroid cells or basophils! The megakaryoctic lineage is particularly dependant on the level of expression of GATA1.
422
+ MULTIPLE HAEMATOPOIETIC LINEAGES: for example precursor cells of erythrocytes and basophils. Ferritin is found in the cytoplasm of DS-AMKL cells.
423
+ This slide equates the aeitology to the model of leukaemia I mentioned earlier, with points 2 and 3 being the 2 mutations.
424
+ 2) It is currently not known if it is the absence of normal GATA1 or the presence of GATA1s that causes leukaemic proliferation of megakaryocytes. It could be both, because GATA1s fails to suppress some pro-proliferative genes such as GATA2 and MYC) although these are pro-proliferative for erythrocytes.The previous slide shows us how a child with DS can progress to developing AMKL. Of course we know that not all children with DS or indeed all children with DS and Transient Leukaemia proceed to develop AMKL. This slide shows us all the possible routes children with DS can take.
425
+ Normal
426
+ GATA1s small clone or large clone
427
+ clonal extinction normal
428
+ clonal expansion plus additional genetic event AMKL
429
+ AMKL death or remission (normal)Although our understanding of leukaemia in DS have progressed a long way over recent years, there still remain unknowns in the aetiology.
430
+ Loss of GATA1 function has different effect on people without DS and people with DS therefore what is the effect of Trisomy 21
431
+ why are GATA1 mutations so relatively common in children with DS It has been postulated that there may be some selective advantage, but it is only a hypothesis.
432
+ thirdly, what is the second-hit We know that there must be a second genetic event, with GATA1 mutation being the first, but we do not know what this is yet.NB: GATA1 mutation is the first mutation for DS-AMKL.AML failure of treatment usually due to toxicity due to mucositis and infection. Resistant disease and relapse are rare.
433
+ ALL there is no increased sensitivity to treatment, but there is increased risk of toxicity. Cannot reduce the dose because of the high risk of relapse and so the emphasis is now on improving supportive care.
434
+ '
435
+
436
+ end
437
+
438
+
439
+ end
440
+ end