mesh-medical-subject-headings 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a2aca8f7e1eceac511966cc2a886258c2bc81c19
4
- data.tar.gz: 1c3f83c62564d794239a05084b099dd0160da13b
3
+ metadata.gz: 9e0d8d67721e16e0e016d35056b1ce147d318bd3
4
+ data.tar.gz: 31ec3da2e71025e3bff77a82fe5178531aff3eeb
5
5
  SHA512:
6
- metadata.gz: b06e343f36965d05b05a4689abfe316430f0b4a069432e51609dbbf15b0426edcbad038f129410b6b20d561617472cec601aa1e14c7589683149ec9a6b08183d
7
- data.tar.gz: 7b8dd6e676a376ce773100cec77e7e1ab923492016d375be9af071ac08895bfa5a9e9c4f94e31376ef4a5f2205308638fec9228c940758c85829f58027238b0d
6
+ metadata.gz: 3ea3dd0a355ca538a471afb7846aa5ab35ab206de3440719217722c6f70ec97488fc15a30610e929be39dad7e9eeb8c560b06e277567bdacc6f2b8715c68eadd
7
+ data.tar.gz: db140b61b574f7baa5bd26bba5e5b7d9570851f0992e208da8ce61549233bea5c98016c10ad66ae90ac7b45604a9485df4f2859be87bef56e4fea18c97f2a9e2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ #2.2.0 / 2014-07-10
2
+ * [FEATURE] Headings now have structured_entries which include semantic and lexical information
3
+
1
4
  #2.1.0 / 2014-07-04
2
5
  * [FEATURE] Headings matched with Wikipedia, infobox images and abstracts imported
3
6
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mesh-medical-subject-headings (2.1.0)
4
+ mesh-medical-subject-headings (2.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/lib/MESH.rb CHANGED
@@ -6,3 +6,6 @@ require 'MESH/heading'
6
6
  require 'MESH/translator'
7
7
  require 'MESH/classifier'
8
8
  require 'MESH/semantic_types'
9
+ require 'MESH/lexical_types'
10
+ require 'MESH/semantic_relationship_types'
11
+ require 'MESH/entry'
data/lib/MESH/entry.rb ADDED
@@ -0,0 +1,36 @@
1
+ module MESH
2
+
3
+ class Entry
4
+
5
+ attr_accessor :heading, :term, :semantic_types, :semantic_relationship, :lexical_type
6
+
7
+ def initialize(heading, entry_text)
8
+ @heading = heading
9
+ @semantic_types = []
10
+ parts = entry_text.split('|')
11
+ if entry_text.include? '|'
12
+ key = parts.pop
13
+ parts.each_with_index do |part, i|
14
+ case key[i]
15
+ when 'a' # the term itself
16
+ @term = part
17
+ when 'b' # semantic type*
18
+ @semantic_types << MESH::SemanticTypes[part]
19
+ when 'c' # lexical type*
20
+ @lexical_type = MESH::LexicalTypes[part]
21
+ when 'd' # semantic relation*
22
+ @semantic_relationship = MESH::SemanticRelationshipTypes[part]
23
+ when 'e' # thesaurus id
24
+ when 'f' # date
25
+ when 's' # sort version
26
+ when 'v' # entry version
27
+ end
28
+ end
29
+ else
30
+ @term = entry_text
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
data/lib/MESH/heading.rb CHANGED
@@ -2,7 +2,7 @@ module MESH
2
2
  class Heading
3
3
 
4
4
  include Comparable
5
- attr_accessor :unique_id, :tree_numbers, :roots, :parents, :children, :useful, :descriptor_class, :default_locale, :semantic_types, :wikipedia_links
5
+ attr_accessor :unique_id, :tree_numbers, :roots, :parents, :children, :useful, :descriptor_class, :default_locale, :semantic_types, :wikipedia_links, :structured_entries
6
6
  attr_reader :linkified_summary
7
7
 
8
8
  def <=> other
@@ -33,7 +33,6 @@ module MESH
33
33
  @entries[locale] ||= []
34
34
  end
35
35
 
36
-
37
36
  def has_ancestor(heading)
38
37
  return false if parents.empty?
39
38
  return true if parents.include? heading
@@ -112,6 +111,7 @@ module MESH
112
111
  @parents = []
113
112
  @children = []
114
113
  @entries = {}
114
+ @structured_entries = []
115
115
  @original_heading = {}
116
116
  @natural_language_name = {}
117
117
  @summary = {}
@@ -0,0 +1,21 @@
1
+ module MESH
2
+ class LexicalTypes
3
+
4
+ def self.[](key)
5
+ Types[key]
6
+ end
7
+
8
+ Types = {
9
+ 'ABB' => :abbreviation,
10
+ 'ABX' => :embedded_abbreviation,
11
+ 'ACR' => :acronym,
12
+ 'ACX' => :embedded_acronym,
13
+ 'EPO' => :eponym,
14
+ 'LAB' => :lab_number,
15
+ 'NAM' => :proper_name,
16
+ 'NON' => nil,
17
+ 'TRD' => :trade_name
18
+ }
19
+
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module MESH
2
+ class SemanticRelationshipTypes
3
+
4
+ def self.[](key)
5
+ Types[key]
6
+ end
7
+
8
+ Types = {
9
+ 'BRD' => :broader,
10
+ 'EQV' => :equivalent,
11
+ 'NRW' => :narrower,
12
+ 'REL' => :related
13
+ }
14
+
15
+ end
16
+ end
data/lib/MESH/tree.rb CHANGED
@@ -67,9 +67,15 @@ module MESH
67
67
  nln = librarian_parts.nil? ? mh : "#{librarian_parts[2]} #{librarian_parts[1]}"
68
68
  current_heading.set_natural_language_name(nln)
69
69
 
70
- when matches = line.match(/^(?:PRINT )?ENTRY = ([^|]+)/)
71
- entry = matches[1].chomp
72
- current_heading.entries << entry unless current_heading.entries.include? entry
70
+ # when matches = line.match(/^(?:PRINT )?ENTRY = ([^|]+)/)
71
+ # entry = matches[1].chomp
72
+ # current_heading.entries << entry unless current_heading.entries.include? entry
73
+ #
74
+ when matches = line.match(/^(?:PRINT )?ENTRY = (.*)/)
75
+ entry = matches[1]
76
+ term = entry.match(/([^|]+)/)
77
+ current_heading.entries << term[1] unless current_heading.entries.include? term[1]
78
+ current_heading.structured_entries << MESH::Entry.new(current_heading, entry)
73
79
 
74
80
  end
75
81
 
data/lib/MESH/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mesh
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -0,0 +1,91 @@
1
+ require_relative 'test_helper'
2
+
3
+ module MESH
4
+ class HeadingTest < TestBase
5
+
6
+ # PRINT ENTRY = Acetamidophenol|T109|T121|NON|EQV|UNK (19XX)|771118|abbcdef
7
+ # PRINT ENTRY = Hydroxyacetanilide|T109|T121|NON|EQV|UNK (19XX)|740329|abbcdef
8
+ # PRINT ENTRY = Paracetamol|T109|T121|NON|EQV|BAN (19XX)|FDA SRS (2014)|INN (19XX)|740329|abbcdeeef
9
+ # ENTRY = APAP|T109|T121|NON|EQV|BAN (19XX)|FDA SRS (2014)|020515|abbcdeef
10
+ # ENTRY = Acamol|T109|T121|TRD|NRW|NLM (1995)|930902|abbcdef
11
+ # ENTRY = Acephen|T109|T121|TRD|NRW|NLM (1991)|900509|abbcdef
12
+ # ENTRY = Acetaco|T109|T121|TRD|REL|UNK (19XX)|861022|abbcdef
13
+ # ENTRY = Acetominophen|T109|T121|NON|EQV|UNK (19XX)|810625|abbcdef
14
+ # ENTRY = Algotropyl|T109|T121|TRD|REL|UNK (19XX)|800825|abbcdef
15
+ # ENTRY = Anacin-3|T109|T121|TRD|NRW|UNK (19XX)|861119|abbcdef
16
+ # ENTRY = Datril|T109|T121|NON|NRW|UNK (19XX)|861119|abbcdef
17
+ # ENTRY = N-(4-Hydroxyphenyl)acetanilide|T109|T121|NON|EQV|NLM (1996)|950330|abbcdef
18
+ # ENTRY = N-Acetyl-p-aminophenol|T109|T121|NON|EQV|UNK (19XX)|800813|abbcdef
19
+ # ENTRY = Panadol|T109|T121|TRD|NRW|UNK (19XX)|830915|abbcdef
20
+ # ENTRY = Tylenol|T109|T121|TRD|NRW|UNK (19XX)|830223|abbcdef
21
+ # ENTRY = p-Acetamidophenol|T109|T121|NON|EQV|UNK (19XX)|800813|abbcdef
22
+ # ENTRY = p-Hydroxyacetanilide|T109|T121|NON|EQV|UNK (19XX)|800801|abbcdef
23
+ # ENTRY = Anacin 3
24
+ # ENTRY = Anacin3
25
+
26
+ def test_has_heading
27
+ entry = Entry.new(@parent_heading, 'Anacin3')
28
+ assert_equal @parent_heading, entry.heading
29
+ end
30
+
31
+ def test_construct_from_plain_string
32
+ entry = Entry.new(@parent_heading, 'Anacin3')
33
+ assert_equal 'Anacin3', entry.term
34
+ assert_nil entry.semantic_relationship
35
+ end
36
+
37
+ def test_has_lexical_type
38
+ entry = Entry.new(@parent_heading, 'Panadol|T109|T121|TRD|NRW|UNK (19XX)|830915|abbcdef')
39
+ assert_equal 'Panadol', entry.term
40
+ assert_equal :trade_name, entry.lexical_type
41
+ end
42
+
43
+ def test_has_semantic_relationship
44
+ entry = Entry.new(@parent_heading, 'Panadol|T109|T121|TRD|NRW|UNK (19XX)|830915|abbcdef')
45
+ assert_equal 'Panadol', entry.term
46
+ assert_equal :narrower, entry.semantic_relationship
47
+ end
48
+
49
+ def test_has_semantic_types
50
+ entry = Entry.new(@parent_heading, 'Panadol|T109|T121|TRD|NRW|UNK (19XX)|830915|abbcdef')
51
+ assert_equal 'Panadol', entry.term
52
+ assert_equal ['Organic Chemical', 'Pharmacologic Substance'], entry.semantic_types
53
+ end
54
+
55
+ def test_datril
56
+ # ENTRY =
57
+ entry = Entry.new(@parent_heading, 'Datril|T109|T121|NON|NRW|UNK (19XX)|861119|abbcdef')
58
+ assert_equal 'Datril', entry.term
59
+ assert_equal ['Organic Chemical', 'Pharmacologic Substance'], entry.semantic_types
60
+ assert_nil entry.lexical_type
61
+ assert_equal :narrower, entry.semantic_relationship
62
+ end
63
+
64
+ # def test_semantic_relationship
65
+ # skip
66
+ # # ABB (Abbreviation)
67
+ # # ABX (Embedded abbreviation)
68
+ # # ACR (Acronym)
69
+ # # ACX (Embedded acronym)
70
+ # # EPO (Eponym)
71
+ # # LAB (Lab number)
72
+ # # NAM (Proper name)
73
+ # # NON (None)
74
+ # # TRD (Trade name)
75
+ # end
76
+ #
77
+ # def test_related_entries
78
+ # skip
79
+ # # BRD (Broader)
80
+ # # EQV (Equivalent)
81
+ # # NRW (Narrower)
82
+ # # REL (Related)
83
+ # end
84
+
85
+ def setup
86
+ @mesh_tree = @@mesh_tree
87
+ @parent_heading = @mesh_tree.find('D000234')
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,506 @@
1
+ require_relative 'test_helper'
2
+
3
+ module MESH
4
+ class HeadingTest < TestBase
5
+
6
+ def test_have_the_correct_unique_id
7
+ mh = @mesh_tree.find('D000001')
8
+ assert_equal 'D000001', mh.unique_id
9
+ end
10
+
11
+ def test_have_the_correct_tree_number
12
+ mh = @mesh_tree.find('D000001')
13
+ assert_equal 1, mh.tree_numbers.length
14
+ assert_includes mh.tree_numbers, 'D03.438.221.173'
15
+ end
16
+
17
+ def test_have_the_correct_tree_numbers
18
+ mh = @mesh_tree.find('D000224')
19
+ assert_equal 2, mh.tree_numbers.length
20
+ assert_includes mh.tree_numbers, 'C19.053.500.263'
21
+ assert_includes mh.tree_numbers, 'C20.111.163'
22
+ end
23
+
24
+ def test_have_the_correct_root_letters
25
+ mh = @mesh_tree.find('D000224')
26
+ assert_equal ['C'], mh.roots
27
+ mh = @mesh_tree.find('D064946')
28
+ assert_equal ['H', 'N'], mh.roots
29
+ end
30
+
31
+ def test_have_the_correct_descriptor_class
32
+ mh = @mesh_tree.find('D000224')
33
+ assert_equal :topical_descriptor, mh.descriptor_class
34
+ mh = @mesh_tree.find('D005260')
35
+ assert_equal :check_tag, mh.descriptor_class
36
+ end
37
+
38
+ def test_have_the_correct_semantic_type
39
+ mh = @mesh_tree.find('D000224')
40
+ assert_equal ['Disease or Syndrome'], mh.semantic_types
41
+ mh = @mesh_tree.find('D005260')
42
+ assert_equal ['Organism Attribute'], mh.semantic_types
43
+ mh = @mesh_tree.find('D014148')
44
+ assert_equal ['Organic Chemical', 'Pharmacologic Substance'], mh.semantic_types
45
+
46
+ end
47
+
48
+ def test_have_the_correct_original_heading
49
+ mh = @mesh_tree.find('D000224')
50
+ assert_equal 'Addison Disease', mh.original_heading
51
+ mh = @mesh_tree.find('D000014')
52
+ assert_equal 'Abnormalities, Drug-Induced', mh.original_heading
53
+ end
54
+
55
+ def test_have_anglicised_original_heading
56
+ mh = @mesh_tree.find('D001471')
57
+ assert_equal 'Barrett Esophagus', mh.original_heading
58
+ assert_equal 'Barrett Oesophagus', mh.original_heading(:en_gb)
59
+ end
60
+
61
+ def test_have_natural_language_name
62
+ mh = @mesh_tree.find('D000224')
63
+ assert_equal 'Addison Disease', mh.natural_language_name
64
+ mh = @mesh_tree.find('D000014')
65
+ assert_equal 'Drug-Induced Abnormalities', mh.natural_language_name
66
+ end
67
+
68
+ def test_have_anglicised_natural_language_name
69
+ mh = @mesh_tree.find('D001471')
70
+ assert_equal 'Barrett Esophagus', mh.natural_language_name
71
+ assert_equal 'Barrett Oesophagus', mh.natural_language_name(:en_gb)
72
+ end
73
+
74
+ def test_have_the_correct_summary
75
+ mh = @mesh_tree.find('D000238')
76
+ assert_equal 'A benign tumor of the anterior pituitary in which the cells do not stain with acidic or basic dyes.', mh.summary
77
+ end
78
+
79
+ def test_have_anglicised_summary
80
+ mh = @mesh_tree.find('D001471')
81
+ 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
82
+ 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)
83
+ end
84
+
85
+ def test_linkify_summary
86
+ mh = @mesh_tree.find('D001471')
87
+ original_summary = '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.'
88
+ assert_equal original_summary, mh.summary
89
+
90
+ found = {
91
+ 'ESOPHAGUS' => false,
92
+ 'ESOPHAGITIS, REFLUX' => false,
93
+ 'INTESTINE' => false,
94
+ 'STOMACH' => false,
95
+ 'ADENOCARCINOMA' => false
96
+ }
97
+
98
+ linkified_summary = mh.linkify_summary do |text, heading|
99
+ found[text] = true unless heading.nil?
100
+ "<foo>#{text.downcase}</foo>"
101
+ end
102
+
103
+ assert_equal 5, found.length
104
+ assert found['ESOPHAGUS']
105
+ assert found['ESOPHAGITIS, REFLUX']
106
+ assert found['INTESTINE']
107
+ assert found['STOMACH']
108
+ assert found['ADENOCARCINOMA']
109
+
110
+ assert_equal original_summary, mh.summary
111
+ assert_equal 'A condition with damage to the lining of the lower <foo>esophagus</foo> resulting from chronic acid reflux (<foo>esophagitis, reflux</foo>). Through the process of metaplasia, the squamous cells are replaced by a columnar epithelium with cells resembling those of the <foo>intestine</foo> or the salmon-pink mucosa of the <foo>stomach</foo>. Barrett\'s columnar epithelium is a marker for severe reflux and precursor to <foo>adenocarcinoma</foo> of the esophagus.', linkified_summary
112
+
113
+ end
114
+
115
+ def test_linkifies_another_summary
116
+ mh = @mesh_tree.find_by_original_heading('Diabetic Nephropathies')
117
+ linkified_summary = mh.linkify_summary do |text, heading|
118
+ "<linky href=\"#{heading.unique_id}\">#{text.downcase}</linky>"
119
+ end
120
+
121
+ expected_summary = 'KIDNEY injuries associated with diabetes mellitus and affecting KIDNEY GLOMERULUS; ARTERIOLES; KIDNEY TUBULES; and the interstitium. Clinical signs include persistent PROTEINURIA, from microalbuminuria progressing to ALBUMINURIA of greater than 300 mg/24 h, leading to reduced GLOMERULAR FILTRATION RATE and END-STAGE RENAL DISEASE.'
122
+
123
+ expected_linkified = '<linky href="D007668">kidney</linky> injuries associated with diabetes mellitus and affecting <linky href="D007678">kidney glomerulus</linky>; <linky href="D001160">arterioles</linky>; <linky href="D007684">kidney tubules</linky>; and the interstitium. Clinical signs include persistent <linky href="D011507">proteinuria</linky>, from microalbuminuria progressing to <linky href="D000419">albuminuria</linky> of greater than 300 mg/24 h, leading to reduced <linky href="D005919">glomerular filtration rate</linky> and <linky href="D007676">end-stage renal disease</linky>.'
124
+
125
+
126
+ assert_equal expected_summary, mh.summary
127
+ assert_equal expected_linkified, linkified_summary
128
+ end
129
+
130
+ def test_to_s
131
+ mh = @mesh_tree.find('D001471')
132
+ assert_equal 'D001471, Barrett Esophagus, [C06.198.102,C06.405.117.102]', mh.to_s
133
+ end
134
+
135
+ def test_have_the_correct_entries
136
+ expected_entries = ['Activity Cycles', 'Ultradian Cycles', 'Activity Cycle', 'Cycle, Activity', 'Cycle, Ultradian', 'Cycles, Activity', 'Cycles, Ultradian', 'Ultradian Cycle']
137
+ expected_entries.sort!
138
+ mh = @mesh_tree.find('D000204')
139
+ assert_equal expected_entries, mh.entries
140
+ end
141
+
142
+ def test_have_anglicised_entries
143
+ 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']
144
+ 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']
145
+
146
+ expected_entries.sort!
147
+ expected_entries_en.sort!
148
+ mh = @mesh_tree.find('D001471')
149
+ assert_equal expected_entries.sort, mh.entries
150
+ assert_equal expected_entries_en.sort, mh.entries(:en_gb)
151
+ end
152
+
153
+ def test_has_structured_entries
154
+
155
+ mh = @mesh_tree.find_by_original_heading('Acetaminophen')
156
+ assert_equal 19, mh.structured_entries.length
157
+
158
+ expected_terms = ['Acetamidophenol', 'Hydroxyacetanilide', 'Paracetamol', 'APAP', 'Acamol', 'Acephen', 'Acetaco', 'Acetominophen', 'Algotropyl', 'Anacin-3', 'Datril', 'N-(4-Hydroxyphenyl)acetanilide', 'N-Acetyl-p-aminophenol', 'Panadol', 'Tylenol', 'p-Acetamidophenol', 'p-Hydroxyacetanilide', 'Anacin 3', 'Anacin3']
159
+
160
+ actual_terms = mh.structured_entries.map { |tn| tn.term }
161
+
162
+ assert_equal actual_terms, expected_terms
163
+
164
+
165
+ # PRINT ENTRY = Acetamidophenol|T109|T121|NON|EQV|UNK (19XX)|771118|abbcdef
166
+ # PRINT ENTRY = Hydroxyacetanilide|T109|T121|NON|EQV|UNK (19XX)|740329|abbcdef
167
+ # PRINT ENTRY = Paracetamol|T109|T121|NON|EQV|BAN (19XX)|FDA SRS (2014)|INN (19XX)|740329|abbcdeeef
168
+ # ENTRY = APAP|T109|T121|NON|EQV|BAN (19XX)|FDA SRS (2014)|020515|abbcdeef
169
+ # ENTRY = Acamol|T109|T121|TRD|NRW|NLM (1995)|930902|abbcdef
170
+ # ENTRY = Acephen|T109|T121|TRD|NRW|NLM (1991)|900509|abbcdef
171
+ # ENTRY = Acetaco|T109|T121|TRD|REL|UNK (19XX)|861022|abbcdef
172
+ # ENTRY = Acetominophen|T109|T121|NON|EQV|UNK (19XX)|810625|abbcdef
173
+ # ENTRY = Algotropyl|T109|T121|TRD|REL|UNK (19XX)|800825|abbcdef
174
+ # ENTRY = Anacin-3|T109|T121|TRD|NRW|UNK (19XX)|861119|abbcdef
175
+ # ENTRY = Datril|T109|T121|NON|NRW|UNK (19XX)|861119|abbcdef
176
+ # ENTRY = N-(4-Hydroxyphenyl)acetanilide|T109|T121|NON|EQV|NLM (1996)|950330|abbcdef
177
+ # ENTRY = N-Acetyl-p-aminophenol|T109|T121|NON|EQV|UNK (19XX)|800813|abbcdef
178
+ # ENTRY = Panadol|T109|T121|TRD|NRW|UNK (19XX)|830915|abbcdef
179
+ # ENTRY = Tylenol|T109|T121|TRD|NRW|UNK (19XX)|830223|abbcdef
180
+ # ENTRY = p-Acetamidophenol|T109|T121|NON|EQV|UNK (19XX)|800813|abbcdef
181
+ # ENTRY = p-Hydroxyacetanilide|T109|T121|NON|EQV|UNK (19XX)|800801|abbcdef
182
+ # ENTRY = Anacin 3
183
+ # ENTRY = Anacin3
184
+
185
+ end
186
+
187
+ def test_have_a_single_wikipedia_link
188
+
189
+ expected = {
190
+ 'D000001' => 'http://en.wikipedia.org/wiki/A23187',
191
+ 'D000005' => 'http://en.wikipedia.org/wiki/Abdomen',
192
+ 'D000082' => 'http://en.wikipedia.org/wiki/Paracetamol'
193
+ }
194
+
195
+ expected.each do |id, expected_link|
196
+ mh = @mesh_tree.find(id)
197
+ assert_equal 1, mh.wikipedia_links.length
198
+ assert_equal expected_link, mh.wikipedia_links[0][:link]
199
+ end
200
+
201
+ end
202
+
203
+ def test_have_a_single_wikipedia_score
204
+ expected = {
205
+ 'D000001' => 0.5,
206
+ 'D000005' => 1.0,
207
+ 'D000082' => 0.35
208
+ }
209
+
210
+ expected.each do |id, expected_score|
211
+ mh = @mesh_tree.find(id)
212
+ assert_equal 1, mh.wikipedia_links.length
213
+ assert_equal expected_score, mh.wikipedia_links[0][:score]
214
+ end
215
+
216
+ end
217
+
218
+ def test_have_a_single_wikipedia_image
219
+ expected = {
220
+ 'D000001' => 'http://upload.wikimedia.org/wikipedia/commons/thumb/1/17/A23187.png/220px-A23187.png',
221
+ 'D000005' => 'http://upload.wikimedia.org/wikipedia/commons/thumb/3/3b/Abdomen_%28PSF%29.jpg/250px-Abdomen_%28PSF%29.jpg',
222
+ 'D000082' => 'http://upload.wikimedia.org/wikipedia/commons/thumb/2/29/Paracetamol-skeletal.svg/150px-Paracetamol-skeletal.svg.png'
223
+ }
224
+
225
+ expected.each do |id, expected_image|
226
+ mh = @mesh_tree.find(id)
227
+ assert_equal 1, mh.wikipedia_links.length
228
+ assert_equal expected_image, mh.wikipedia_links[0][:image]
229
+ end
230
+ end
231
+
232
+ def test_have_a_single_wikipedia_abstract
233
+ expected = {
234
+ 'D000001' => '| CAS_number = 52665-69-7',
235
+ 'D000005' => 'The abdomen (less formally called the belly, stomach, or tummy), in vertebrates such as mammals, constitutes the part of the body between the thorax (chest) and pelvis. The region enclosed by the abdomen is termed the abdominal cavity.',
236
+ 'D000082' => '| MedlinePlus = a681004'
237
+ }
238
+
239
+ expected.each do |id, expected_abstract|
240
+ mh = @mesh_tree.find(id)
241
+ assert_equal 1, mh.wikipedia_links.length
242
+ assert_equal expected_abstract, mh.wikipedia_links[0][:abstract]
243
+ end
244
+ end
245
+
246
+ def test_have_more_than_one_wikipedia_link
247
+ mh = @mesh_tree.find('D000100')
248
+ expected = %w(
249
+ http://en.wikipedia.org/wiki/Sodium_acetrizoate
250
+ http://en.wikipedia.org/wiki/Acetrizoic_acid
251
+ )
252
+ assert_equal expected, mh.wikipedia_links.map { |l| l[:link] }
253
+ end
254
+
255
+ def test_have_more_than_one_wikipedia_score
256
+ mh = @mesh_tree.find('D000100')
257
+ expected = [0.09, 0.09]
258
+ assert_equal expected, mh.wikipedia_links.map { |l| l[:score] }
259
+ end
260
+
261
+ def test_have_more_than_one_wikipedia_image
262
+ mh = @mesh_tree.find('D000100')
263
+ expected = %w(
264
+ http://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Sodium_acetrizoate.svg/150px-Sodium_acetrizoate.svg.png
265
+ http://upload.wikimedia.org/wikipedia/commons/thumb/2/26/Acetrizoic_acid.png/220px-Acetrizoic_acid.png
266
+ )
267
+ assert_equal expected, mh.wikipedia_links.map { |l| l[:image] }
268
+ end
269
+
270
+ def test_have_more_than_one_wikipedia_abstract
271
+ mh = @mesh_tree.find('D000100')
272
+ expected = ['| CAS_number = 129-63-5', '| CAS_number = 85-36-9']
273
+ assert_equal expected, mh.wikipedia_links.map { |l| l[:abstract] }
274
+ end
275
+
276
+ def test_have_the_correct_parent
277
+ mh = @mesh_tree.find('D000001')
278
+ assert_equal 1, mh.parents.length
279
+ assert_equal 'D001583', mh.parents[0].unique_id
280
+ end
281
+
282
+ def test_have_the_correct_parents
283
+ mh = @mesh_tree.find('D000224')
284
+ p1 = @mesh_tree.find('D000309')
285
+ p2 = @mesh_tree.find('D001327')
286
+ assert_equal 2, mh.parents.length
287
+ assert_includes mh.parents, p1
288
+ assert_includes mh.parents, p2
289
+ end
290
+
291
+ def test_have_the_correct_parents_again
292
+ child = @mesh_tree.find_by_original_heading('Questionnaires')
293
+ assert_equal 1, child.parents.length
294
+ expected_parent = @mesh_tree.find_by_original_heading('Data Collection')
295
+ assert_includes child.parents, expected_parent
296
+ end
297
+
298
+ def test_have_the_correct_children
299
+ parent = @mesh_tree.find_by_tree_number('C19.053.500')
300
+ child1 = @mesh_tree.find_by_tree_number('C19.053.500.263')
301
+ child2 = @mesh_tree.find_by_tree_number('C19.053.500.270')
302
+ child3 = @mesh_tree.find_by_tree_number('C19.053.500.480')
303
+ child4 = @mesh_tree.find_by_tree_number('C19.053.500.740')
304
+
305
+ assert_equal 4, parent.children.length
306
+ assert_includes parent.children, child1
307
+ assert_includes parent.children, child2
308
+ assert_includes parent.children, child3
309
+ assert_includes parent.children, child4
310
+ end
311
+
312
+ def test_have_the_correct_children_again
313
+ parent = @mesh_tree.find_by_original_heading('Data Collection')
314
+
315
+ expected_children = [
316
+ 'Health Surveys',
317
+ 'Interviews as Topic',
318
+ 'Questionnaires',
319
+ 'Records as Topic',
320
+ 'Registries',
321
+ 'Vital Statistics',
322
+ 'Geriatric Assessment',
323
+ 'Nutrition Assessment',
324
+ 'Health Care Surveys',
325
+ 'Narration',
326
+ 'Lot Quality Assurance Sampling',
327
+ 'Checklist',
328
+ 'Health Impact Assessment',
329
+ 'Crowdsourcing'
330
+ ].map { |oh| @mesh_tree.find_by_original_heading(oh) }
331
+
332
+
333
+ assert_equal expected_children.length, parent.children.length
334
+ expected_children.each do |ec|
335
+ assert_includes parent.children, ec
336
+ end
337
+ end
338
+
339
+ def test_have_the_correct_siblings
340
+ skip
341
+ end
342
+
343
+ def test_match_on_conditions_for_original_heading
344
+ mh = @mesh_tree.find('D001471')
345
+ assert mh.matches(original_heading: /^Barrett Esophagus$/)
346
+ end
347
+
348
+ def test_not_match_on_incorrect_condition_for_original_heading
349
+ mh = @mesh_tree.find('D001471')
350
+ refute mh.matches(original_heading: /^Foo$/)
351
+ end
352
+
353
+ def test_match_on_conditions_for_entries
354
+ mh = @mesh_tree.find('D001471')
355
+ assert mh.matches(entries: /Metaplasia/)
356
+ end
357
+
358
+ def test_not_match_on_incorrect_conditions_for_entries
359
+ mh = @mesh_tree.find('D001471')
360
+ refute mh.matches(entries: /Foo/)
361
+ end
362
+
363
+ def test_match_on_descriptor_class
364
+ mh = @mesh_tree.find('D000224')
365
+ assert mh.matches(descriptor_class: :topical_descriptor)
366
+ refute mh.matches(descriptor_class: :check_tag)
367
+ mh = @mesh_tree.find('D005260')
368
+ assert mh.matches(descriptor_class: :check_tag)
369
+ refute mh.matches(descriptor_class: :topical_descriptor)
370
+ end
371
+
372
+ def test_match_on_conditions_for_tree_numbers
373
+ mh = @mesh_tree.find('D001471')
374
+ assert mh.matches(tree_numbers: /C06\.198\.102/)
375
+ assert mh.matches(tree_numbers: /^C06\.198\.102$/)
376
+ assert mh.matches(tree_numbers: /^C06/)
377
+ assert mh.matches(tree_numbers: /\.198\./)
378
+ assert mh.matches(tree_numbers: /^C06\.405\.117\.102$/)
379
+ end
380
+
381
+ def test_not_match_on_incorrect_conditions_for_tree_numbers
382
+ mh = @mesh_tree.find('D001471')
383
+ refute mh.matches(tree_numbers: /Foo/)
384
+ end
385
+
386
+ def test_match_on_conditions_for_summary
387
+ mh = @mesh_tree.find('D001471')
388
+ assert mh.matches(summary: /the lower ESOPHAGUS resulting from chronic acid reflux \(ESOPHAGITIS, REFLUX\)\./)
389
+ end
390
+
391
+ def test_not_match_on_incorrect_conditions_for_summary
392
+ mh = @mesh_tree.find('D001471')
393
+ refute mh.matches(summary: /Foo/)
394
+ end
395
+
396
+ def test_match_on_conditions_for_useful
397
+ mh = @mesh_tree.find('D001471')
398
+ begin
399
+ mh.useful = true
400
+ assert mh.matches(useful: true)
401
+ refute mh.matches(useful: false)
402
+ mh.useful = false
403
+ assert mh.matches(useful: false)
404
+ refute mh.matches(useful: true)
405
+ ensure
406
+ mh.useful = true
407
+ end
408
+ end
409
+
410
+ def test_match_on_multiple_conditions
411
+ mh = @mesh_tree.find('D001471')
412
+ assert mh.matches(original_heading: /^Barrett Esophagus$/, summary: /the lower ESOPHAGUS/)
413
+ end
414
+
415
+ def test_not_match_on_incorrect_multiple_conditions
416
+ mh = @mesh_tree.find('D001471')
417
+ refute mh.matches(original_heading: /^Barrett Esophagus$/, summary: /Foo/)
418
+ refute mh.matches(original_heading: /^Foo/, summary: /the lower ESOPHAGUS/)
419
+ end
420
+
421
+ def test_allow_headings_to_be_marked_as_not_useful
422
+ mh = @mesh_tree.find('D055550')
423
+ mh.useful = true
424
+ assert mh.useful
425
+ mh.useful = false
426
+ refute mh.useful
427
+ mh.useful = true
428
+ assert mh.useful
429
+ end
430
+
431
+ def test_know_its_deepest_position_in_the_tree
432
+ #single tree numbers
433
+ assert_equal 1, @mesh_tree.find('D002319').deepest_position
434
+ assert_equal 2, @mesh_tree.find('D001808').deepest_position
435
+ assert_equal 3, @mesh_tree.find('D001158').deepest_position
436
+ assert_equal 4, @mesh_tree.find('D001981').deepest_position
437
+ end
438
+
439
+ def test_know_its_shallowest_position_in_the_tree
440
+ #single tree numbers
441
+ assert_equal 1, @mesh_tree.find('D002319').shallowest_position
442
+ assert_equal 2, @mesh_tree.find('D001808').shallowest_position
443
+ assert_equal 3, @mesh_tree.find('D001158').shallowest_position
444
+ assert_equal 4, @mesh_tree.find('D001981').shallowest_position
445
+ end
446
+
447
+ def test_know_if_one_heading_is_the_descendant_of_another
448
+ parent = @mesh_tree.find('D002319')
449
+ child = @mesh_tree.find('D001808')
450
+ grandchild = @mesh_tree.find('D001158')
451
+ great_grandchild = @mesh_tree.find('D001981')
452
+ unrelated = @mesh_tree.find('D008091')
453
+
454
+ refute parent.has_descendant(parent), 'should not consider itself a desecendant'
455
+ assert parent.has_descendant(child), "should consider child #{child.inspect} a descendant of #{parent.inspect} in #{parent.children}"
456
+ assert parent.has_descendant(grandchild), "should consider grandchild #{grandchild.inspect} a descendant #{parent.inspect}"
457
+ assert parent.has_descendant(great_grandchild), "should consider great grandchild #{great_grandchild.inspect} a descendant #{parent.inspect}"
458
+ refute parent.has_descendant(unrelated), 'should not consider an unrelated heading a descendant'
459
+ end
460
+
461
+ def test_know_if_one_heading_is_the_ancestor_of_another
462
+ child = @mesh_tree.find('D001981')
463
+ parent = @mesh_tree.find('D001158')
464
+ grandparent = @mesh_tree.find('D001808')
465
+ great_grandparent = @mesh_tree.find('D002319')
466
+ unrelated = @mesh_tree.find('D008091')
467
+
468
+ refute child.has_ancestor(child), 'should not consider itself an ancestor'
469
+ assert child.has_ancestor(parent), "should consider parent #{parent.inspect} an ancestor of #{child.inspect} in #{child.parents}"
470
+ assert child.has_ancestor(grandparent), "should consider grandparent #{grandparent.inspect} an ancestor #{child.inspect}"
471
+ assert child.has_ancestor(great_grandparent), "should consider great grandparent #{great_grandparent.inspect} an ancestor #{child.inspect}"
472
+ refute child.has_ancestor(unrelated), 'should not consider an unrelated heading an ancestor'
473
+ end
474
+
475
+ def test_know_if_headings_are_siblings_at_the_same_level_below_a_common_parent
476
+ parent = @mesh_tree.find_by_tree_number('C19.053.500')
477
+ child1 = @mesh_tree.find_by_tree_number('C19.053.500.263')
478
+ child2 = @mesh_tree.find_by_tree_number('C19.053.500.270')
479
+ child3 = @mesh_tree.find_by_tree_number('C19.053.500.480')
480
+ child4 = @mesh_tree.find_by_tree_number('C19.053.500.740')
481
+ unrelated = @mesh_tree.find('D008091')
482
+ children = [child1, child2, child3, child4]
483
+
484
+ children.each { |c| refute parent.sibling?(c) }
485
+ children.each { |c| refute c.sibling?(parent) }
486
+
487
+ children.each { |c| assert child1.sibling?(c) unless c == child1 }
488
+ children.each { |c| assert c.sibling?(child1) unless c == child1 }
489
+
490
+ children.each { |c| refute unrelated.sibling?(c) }
491
+ children.each { |c| refute c.sibling?(unrelated) }
492
+ end
493
+
494
+ def test_override_inspect_to_prevent_issues_in_test_diagnostics
495
+ mh = @mesh_tree.find('D001471')
496
+ expected = "#{mh.unique_id}, #{mh.original_heading}, [#{mh.tree_numbers.join(',')}]"
497
+ assert_equal expected, mh.inspect
498
+ end
499
+
500
+ def setup
501
+ @mesh_tree = @@mesh_tree
502
+ end
503
+
504
+ end
505
+ end
506
+