oddb2xml 2.7.1 → 2.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -2
  3. data/.standard.yml +2 -0
  4. data/Gemfile +3 -3
  5. data/History.txt +24 -0
  6. data/README.md +3 -3
  7. data/Rakefile +24 -23
  8. data/bin/check_artikelstamm +11 -11
  9. data/bin/compare_v5 +23 -23
  10. data/bin/oddb2xml +14 -13
  11. data/lib/oddb2xml/builder.rb +1070 -1038
  12. data/lib/oddb2xml/calc.rb +232 -233
  13. data/lib/oddb2xml/chapter_70_hack.rb +38 -32
  14. data/lib/oddb2xml/cli.rb +252 -236
  15. data/lib/oddb2xml/compare.rb +70 -59
  16. data/lib/oddb2xml/compositions_syntax.rb +451 -430
  17. data/lib/oddb2xml/compressor.rb +20 -20
  18. data/lib/oddb2xml/downloader.rb +157 -129
  19. data/lib/oddb2xml/extractor.rb +295 -295
  20. data/lib/oddb2xml/options.rb +34 -35
  21. data/lib/oddb2xml/parslet_compositions.rb +265 -269
  22. data/lib/oddb2xml/semantic_check.rb +39 -33
  23. data/lib/oddb2xml/util.rb +163 -163
  24. data/lib/oddb2xml/version.rb +1 -1
  25. data/lib/oddb2xml/xml_definitions.rb +32 -33
  26. data/lib/oddb2xml.rb +1 -1
  27. data/oddb2xml.gemspec +34 -34
  28. data/shell.nix +17 -0
  29. data/spec/artikelstamm_spec.rb +111 -110
  30. data/spec/builder_spec.rb +490 -505
  31. data/spec/calc_spec.rb +552 -593
  32. data/spec/check_artikelstamm_spec.rb +26 -26
  33. data/spec/cli_spec.rb +173 -174
  34. data/spec/compare_spec.rb +9 -11
  35. data/spec/composition_syntax_spec.rb +390 -409
  36. data/spec/compressor_spec.rb +48 -48
  37. data/spec/data/transfer.dat +1 -0
  38. data/spec/data_helper.rb +47 -49
  39. data/spec/downloader_spec.rb +251 -260
  40. data/spec/extractor_spec.rb +171 -159
  41. data/spec/fixtures/vcr_cassettes/oddb2xml.json +1 -1
  42. data/spec/galenic_spec.rb +233 -256
  43. data/spec/options_spec.rb +116 -119
  44. data/spec/parslet_spec.rb +896 -863
  45. data/spec/spec_helper.rb +153 -153
  46. data/test_options.rb +39 -42
  47. data/tools/win_fetch_cacerts.rb +2 -3
  48. metadata +42 -12
@@ -1,79 +1,74 @@
1
- # encoding: utf-8
2
-
3
1
  # This file is shared since oddb2xml 2.0.0 (lib/oddb2xml/parse_compositions.rb)
4
2
  # with oddb.org src/plugin/parse_compositions.rb
5
3
  #
6
4
  # It allows an easy parsing of the column P Zusammensetzung of the swissmedic packages.xlsx file
7
5
  #
8
6
 
9
- require 'parslet'
10
- require 'parslet/convenience'
11
- require 'oddb2xml/compositions_syntax'
12
- include Parslet
7
+ require "parslet"
8
+ require "parslet/convenience"
9
+ require_relative "compositions_syntax"
13
10
  VERBOSE_MESSAGES ||= false
14
11
 
15
12
  module ParseUtil
13
+ include Parslet
16
14
  # this class is responsible to patch errors in swissmedic entries after
17
15
  # oddb.org detected them, as it takes sometimes a few days (or more) till they get corrected
18
16
  # Reports the number of occurrences of each entry
19
17
  @@saved_parsed ||= {}
20
- @@nr_saved_parsed_used ||= 0
18
+ @@nr_saved_parsed_used ||= 0
21
19
 
22
20
  class HandleSwissmedicErrors
23
-
24
21
  attr_accessor :nrParsingErrors
25
- class ErrorEntry < Struct.new('ErrorEntry', :pattern, :replacement, :nr_occurrences)
22
+ class ErrorEntry < Struct.new("ErrorEntry", :pattern, :replacement, :nr_occurrences)
26
23
  end
27
24
 
28
25
  def reset_errors
29
26
  @errors = []
30
- @nrLines = 0
31
- @nrParsingErrors = 0
27
+ @nr_lines = 0
28
+ @nr_parsing_errors = 0
32
29
  end
33
30
 
34
31
  # error_entries should be a hash of pattern, replacement
35
32
  def initialize(error_entries)
36
33
  reset_errors
37
- error_entries.each{ |pattern, replacement| @errors << ErrorEntry.new(pattern, replacement, 0) }
34
+ error_entries.each { |pattern, replacement| @errors << ErrorEntry.new(pattern, replacement, 0) }
38
35
  end
39
36
 
40
37
  def report
41
- s = ["Report of changed compositions in #{@nrLines} lines. Had #{@nrParsingErrors} parsing errors" ]
42
- @errors.each {
43
- |entry|
44
- s << " replaced #{entry.nr_occurrences} times '#{entry.pattern}' by '#{entry.replacement}'"
38
+ s = ["Report of changed compositions in #{@nr_lines} lines. Had #{@nr_parsing_errors} parsing errors"]
39
+ @errors.each { |entry|
40
+ s << " replaced #{entry.nr_occurrences} times '#{entry.pattern}' by '#{entry.replacement}'"
45
41
  }
46
42
  s
47
43
  end
48
44
 
49
45
  def apply_fixes(string)
50
46
  result = string.clone
51
- @errors.each{
52
- |entry|
47
+ @errors.each { |entry|
53
48
  intermediate = result.clone
54
- result = result.gsub(entry.pattern, entry.replacement)
49
+ result = result.gsub(entry.pattern, entry.replacement)
55
50
  unless result.eql?(intermediate)
56
- entry.nr_occurrences += 1
57
- puts "#{File.basename(__FILE__)}:#{__LINE__}: fixed \nbefore: #{intermediate}\nafter: #{result}" if $VERBOSE
51
+ entry.nr_occurrences += 1
52
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: fixed \nbefore: #{intermediate}\nafter: #{result}" if $VERBOSE
58
53
  end
59
54
  }
60
- @nrLines += 1
55
+ @nr_lines += 1
61
56
  result
62
57
  end
63
58
  # hepar sulfuris D6 2,2 mg hypericum perforatum D2 0,66 mg where itlacks a comma and should be hepar sulfuris D6 2,2 mg, hypericum perforatum D2 0,66 mg
64
59
  end
65
60
 
66
- def ParseUtil.capitalize(string)
67
- string.split(/\s+/u).collect { |word| word.capitalize }.join(' ').strip
61
+ def self.capitalize(string)
62
+ string.split(/\s+/u).collect { |word| word.capitalize }.join(" ").strip
68
63
  end
69
64
 
70
- def ParseUtil.nr_saved_parsed_used
65
+ def self.nr_saved_parsed_used
71
66
  @@nr_saved_parsed_used
72
67
  end
73
68
 
74
- def ParseUtil.parse_compositions(composition_text, active_agents_string = '')
75
- active_agents = active_agents_string ? active_agents_string.gsub('[', '').downcase.split(/,\s+/) : []
76
- key = [ composition_text, active_agents ]
69
+ def self.parse_compositions(composition_text, active_agents_string = "")
70
+ active_agents = active_agents_string ? active_agents_string.delete("[").downcase.split(/,\s+/) : []
71
+ key = [composition_text, active_agents]
77
72
  saved_value = @@saved_parsed[key]
78
73
  if saved_value
79
74
  @@nr_saved_parsed_used += 1
@@ -81,20 +76,22 @@ module ParseUtil
81
76
  end
82
77
  comps = []
83
78
  lines = composition_text.gsub(/\r\n?/u, "\n").split(/\n/u)
84
- lines.select do
85
- |line|
86
- composition = ParseComposition.from_string(line)
79
+ lines.select do |line|
80
+ composition = ParseComposition.from_string(line)
87
81
  if composition.is_a?(ParseComposition)
88
- composition.substances.each do
89
- |substance_item|
90
- active_substance_name = substance_item.name.downcase.sub(/^cum\s/, '')
91
- substance_item.is_active_agent = (active_agents.find {|x| /#{x.downcase.
92
- gsub('(', '\(').
93
- gsub(')', '\)').
94
- gsub('[', '\[').
95
- gsub(']', '\]')}($|\s)/.match(active_substance_name) } != nil)
96
- substance_item.is_active_agent = true if substance_item.chemical_substance and active_agents.find {|x| x.downcase.eql?(substance_item.chemical_substance.name.downcase) }
97
- end
82
+ composition.substances.each do |substance_item|
83
+ active_substance_name = substance_item.name.downcase.sub(/^cum\s/, "")
84
+ substance_item.is_active_agent = !active_agents.find { |x|
85
+ /#{x.downcase
86
+ .gsub('(', '\(')
87
+ .gsub(')', '\)')
88
+ .gsub('[', '\[')
89
+ .gsub(']', '\]')
90
+ }($|\s)/
91
+ .match(active_substance_name)
92
+ }.nil?
93
+ substance_item.is_active_agent = true if substance_item.chemical_substance && active_agents.find { |x| x.downcase.eql?(substance_item.chemical_substance.name.downcase) }
94
+ end
98
95
  comps << composition
99
96
  end
100
97
  end
@@ -103,202 +100,195 @@ module ParseUtil
103
100
  comps
104
101
  rescue => error
105
102
  puts "error #{error}"
106
- # binding.pry
103
+ # binding.pry
104
+ raise error
107
105
  end
108
-
109
106
  end
110
107
 
111
- class IntLit < Struct.new(:int)
112
- def eval; int.to_i; end
108
+ class IntLit < Struct.new(:int)
109
+ def eval
110
+ int.to_i
111
+ end
113
112
  end
114
- class QtyLit < Struct.new(:qty)
115
- def eval; qty.to_i; end
113
+
114
+ class QtyLit < Struct.new(:qty)
115
+ def eval
116
+ qty.to_i
117
+ end
116
118
  end
117
119
 
118
120
  class CompositionTransformer < Parslet::Transform
119
121
  @@more_info = nil
120
- def CompositionTransformer.get_ratio(parse_info)
122
+ def self.get_ratio(parse_info)
121
123
  if parse_info[:ratio]
122
- if parse_info[:ratio].to_s.length > 0 and parse_info[:ratio].to_s != ', '
123
- parse_info[:ratio].to_s.sub(/^,\s+/, '').sub(/,\s+$/,'')
124
- else
125
- nil
124
+ if (parse_info[:ratio].to_s.length > 0) && (parse_info[:ratio].to_s != ", ")
125
+ parse_info[:ratio].to_s.sub(/^,\s+/, "").sub(/,\s+$/, "")
126
126
  end
127
- else
128
- nil
129
127
  end
130
128
  end
131
129
 
132
- def CompositionTransformer.check_e_substance(substance)
133
- return unless /^E \d\d\d/.match(substance.name)
130
+ def self.check_e_substance(substance)
131
+ return unless /^E \d\d\d/.match?(substance.name)
134
132
  unless substance.more_info
135
133
  case substance.name[2]
136
134
  when "1"
137
- substance.more_info = 'color.'
135
+ substance.more_info = "color."
138
136
  when "2"
139
- substance.more_info = 'conserv.'
140
- else
137
+ substance.more_info = "conserv."
141
138
  end
142
139
  substance.more_info ||= @@more_info
143
140
  end
144
141
  @@more_info = substance.more_info
145
142
  end
146
143
 
147
- def CompositionTransformer.add_excipiens(info)
148
- @@more_info = nil
149
- @@excipiens = ParseSubstance.new(info[:excipiens_description] ? info[:excipiens_description] : 'Excipiens')
150
- @@excipiens.dose = info[:dose] if info[:dose]
144
+ def self.add_excipiens(info)
145
+ @@more_info = nil
146
+ @@excipiens = ParseSubstance.new(info[:excipiens_description] || "Excipiens")
147
+ @@excipiens.dose = info[:dose] if info[:dose]
151
148
  @@excipiens.more_info = CompositionTransformer.get_ratio(info)
152
- @@excipiens.cdose = info[:dose_corresp] if info[:dose_corresp]
149
+ @@excipiens.cdose = info[:dose_corresp] if info[:dose_corresp]
153
150
  @@excipiens.more_info = info[:more_info] if info[:more_info]
154
151
  end
155
152
 
156
- rule(:corresp => simple(:corresp),
157
- ) {
158
- |dictionary|
159
- puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
160
- @@corresp = dictionary[:corresp].to_s
153
+ rule(corresp: simple(:corresp)) { |dictionary|
154
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
155
+ @@corresp = dictionary[:corresp].to_s
161
156
  }
162
- rule( :substance_name => simple(:substance_name),
163
- :dose => simple(:dose),
164
- ) {
165
- |dictionary|
166
- puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
167
- dose = dictionary[:dose].is_a?(ParseDose) ? dictionary[:dose] : nil
168
- substance = ParseSubstance.new(dictionary[:substance_name], dose)
169
- @@substances << substance
170
- substance
157
+ rule(substance_name: simple(:substance_name),
158
+ dose: simple(:dose)) { |dictionary|
159
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
160
+ dose = dictionary[:dose].is_a?(ParseDose) ? dictionary[:dose] : nil
161
+ substance = ParseSubstance.new(dictionary[:substance_name], dose)
162
+ @@substances << substance
163
+ substance
171
164
  }
172
165
 
173
- rule( :more_info => simple(:more_info),
174
- ) {
175
- |dictionary|
176
- puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
177
- @@corresp = dictionary[:more_info].to_s.strip.sub(/:$/, '')
166
+ rule(more_info: simple(:more_info)) { |dictionary|
167
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
168
+ @@corresp = dictionary[:more_info].to_s.strip.sub(/:$/, "")
178
169
  }
179
- rule( :more_info => simple(:more_info),
180
- :substance_name => simple(:substance_name),
181
- :dose => simple(:dose),
182
- ) {
183
- |dictionary|
184
- puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
185
- dose = dictionary[:dose].is_a?(ParseDose) ? dictionary[:dose] : nil
186
- substance = ParseSubstance.new(dictionary[:substance_name].to_s, dose)
187
- substance.more_info = dictionary[:more_info].to_s.strip.sub(/:$/, '') if dictionary[:more_info] and dictionary[:more_info].to_s.length > 0
188
- CompositionTransformer.check_e_substance(substance)
189
- @@substances << substance
190
- substance
170
+ rule(more_info: simple(:more_info),
171
+ substance_name: simple(:substance_name),
172
+ dose: simple(:dose)) { |dictionary|
173
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
174
+ dose = dictionary[:dose].is_a?(ParseDose) ? dictionary[:dose] : nil
175
+ substance = ParseSubstance.new(dictionary[:substance_name].to_s, dose)
176
+ substance.more_info = dictionary[:more_info].to_s.strip.sub(/:$/, "") if dictionary[:more_info] && (dictionary[:more_info].to_s.length > 0)
177
+ CompositionTransformer.check_e_substance(substance)
178
+ @@substances << substance
179
+ substance
191
180
  }
192
181
 
193
- rule(:lebensmittel_zusatz => simple(:lebensmittel_zusatz),
194
- :more_info => simple(:more_info),
195
- :digits => simple(:digits)) {
196
- |dictionary|
197
- puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
198
- substance = ParseSubstance.new("#{dictionary[:lebensmittel_zusatz]} #{dictionary[:digits]}")
199
- substance.more_info = dictionary[:more_info].to_s.strip.sub(/:$/, '') if dictionary[:more_info] and dictionary[:more_info].to_s.length > 0
200
- CompositionTransformer.check_e_substance(substance)
201
- @@substances << substance
202
- substance
182
+ rule(lebensmittel_zusatz: simple(:lebensmittel_zusatz),
183
+ more_info: simple(:more_info),
184
+ digits: simple(:digits)) { |dictionary|
185
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
186
+ substance = ParseSubstance.new("#{dictionary[:lebensmittel_zusatz]} #{dictionary[:digits]}")
187
+ substance.more_info = dictionary[:more_info].to_s.strip.sub(/:$/, "") if dictionary[:more_info] && (dictionary[:more_info].to_s.length > 0)
188
+ CompositionTransformer.check_e_substance(substance)
189
+ @@substances << substance
190
+ substance
203
191
  }
204
- rule(:excipiens => subtree(:excipiens),
205
- ) {
206
- |dictionary|
207
- puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
208
- info = dictionary[:excipiens].is_a?(Hash) ? dictionary[:excipiens] : dictionary[:excipiens].first
209
- if info[:excipiens_description] or
210
- info[:dose] or
211
- info[:dose_corresp] or
212
- info[:more_info] or
213
- CompositionTransformer.get_ratio(dictionary)
214
- CompositionTransformer.add_excipiens(info)
215
- info
216
- end
217
- nil
192
+ rule(excipiens: subtree(:excipiens)) { |dictionary|
193
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
194
+ info = dictionary[:excipiens].is_a?(Hash) ? dictionary[:excipiens] : dictionary[:excipiens].first
195
+ if info[:excipiens_description] ||
196
+ info[:dose] ||
197
+ info[:dose_corresp] ||
198
+ info[:more_info] ||
199
+ CompositionTransformer.get_ratio(dictionary)
200
+ CompositionTransformer.add_excipiens(info)
201
+ info
202
+ end
203
+ nil
218
204
  }
219
- rule(:composition => subtree(:composition),
220
- ) {
221
- |dictionary|
222
- puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
223
- info = dictionary[:composition].is_a?(Hash) ? dictionary[:composition] : dictionary[:composition].first
224
- CompositionTransformer.add_excipiens(info) if info.is_a?(Hash)
225
- info
226
- }
227
- rule(:substance => simple(:substance),
228
- :chemical_substance => simple(:chemical_substance),
229
- :substance_ut => sequence(:substance_ut),
230
- :ratio => simple(:ratio),
231
- ) {
232
- |dictionary|
233
- puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
234
- ratio = CompositionTransformer.get_ratio(dictionary)
235
- if ratio and ratio.length > 0
236
- if dictionary[:substance].more_info
237
- dictionary[:substance].more_info += ' ' + ratio.strip
238
- else
239
- dictionary[:substance].more_info = ratio.strip
240
- end
241
- end
242
- if dictionary[:chemical_substance]
243
- dictionary[:substance].chemical_substance = dictionary[:chemical_substance]
244
- @@substances -= [dictionary[:chemical_substance]]
245
- end
246
- if dictionary[:substance_ut].size > 0
247
- dictionary[:substance].salts += dictionary[:substance_ut].last.salts
248
- dictionary[:substance_ut].last.salts = []
249
- dictionary[:substance].salts << dictionary[:substance_ut].last
250
- @@substances -= dictionary[:substance_ut]
251
- end
252
- dictionary[:substance]
205
+ rule(composition: subtree(:composition)) { |dictionary|
206
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
207
+ info = dictionary[:composition].is_a?(Hash) ? dictionary[:composition] : dictionary[:composition].first
208
+ CompositionTransformer.add_excipiens(info) if info.is_a?(Hash)
209
+ info
210
+ }
211
+ rule(substance: simple(:substance),
212
+ chemical_substance: simple(:chemical_substance),
213
+ substance_ut: sequence(:substance_ut),
214
+ ratio: simple(:ratio)) { |dictionary|
215
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
216
+ ratio = CompositionTransformer.get_ratio(dictionary)
217
+ if ratio && (ratio.length > 0)
218
+ if dictionary[:substance].more_info
219
+ dictionary[:substance].more_info += " " + ratio.strip
220
+ else
221
+ dictionary[:substance].more_info = ratio.strip
222
+ end
223
+ end
224
+ if dictionary[:chemical_substance]
225
+ dictionary[:substance].chemical_substance = dictionary[:chemical_substance]
226
+ @@substances -= [dictionary[:chemical_substance]]
227
+ end
228
+ if dictionary[:substance_ut].size > 0
229
+ dictionary[:substance].salts += dictionary[:substance_ut].last.salts
230
+ dictionary[:substance_ut].last.salts = []
231
+ dictionary[:substance].salts << dictionary[:substance_ut].last
232
+ @@substances -= dictionary[:substance_ut]
233
+ end
234
+ dictionary[:substance]
253
235
  }
254
236
 
255
- rule(:int => simple(:int)) { IntLit.new(int) }
256
- rule(:number => simple(:nb)) {
257
- nb.match(/[eE\.]/) ? Float(nb) : Integer(nb)
237
+ rule(int: simple(:int)) { IntLit.new(int) }
238
+ rule(number: simple(:nb)) {
239
+ /[eE.]/.match?(nb) ? Float(nb) : Integer(nb)
258
240
  }
259
241
  rule(
260
- :qty_range => simple(:qty_range),
261
- :unit => simple(:unit)) {
262
- ParseDose.new(qty_range, unit)
263
- }
242
+ qty_range: simple(:qty_range),
243
+ unit: simple(:unit)
244
+ ) {
245
+ ParseDose.new(qty_range, unit)
246
+ }
264
247
  rule(
265
- :qty_range => simple(:qty_range)) {
266
- ParseDose.new(qty_range)
267
- }
248
+ qty_range: simple(:qty_range)
249
+ ) {
250
+ ParseDose.new(qty_range)
251
+ }
268
252
  rule(
269
- :qty => simple(:qty),
270
- :unit => simple(:unit)) {
271
- ParseDose.new(qty, unit)
272
- }
253
+ qty: simple(:qty),
254
+ unit: simple(:unit)
255
+ ) {
256
+ ParseDose.new(qty, unit)
257
+ }
273
258
  rule(
274
- :unit => simple(:unit)) { ParseDose.new(nil, unit) }
259
+ unit: simple(:unit)
260
+ ) { ParseDose.new(nil, unit) }
275
261
  rule(
276
- :qty => simple(:qty)) { ParseDose.new(qty, nil) }
262
+ qty: simple(:qty)
263
+ ) { ParseDose.new(qty, nil) }
277
264
  rule(
278
- :qty => simple(:qty),
279
- :unit => simple(:unit),
280
- :dose_right => simple(:dose_right),
281
- ) {
265
+ qty: simple(:qty),
266
+ unit: simple(:unit),
267
+ dose_right: simple(:dose_right)
268
+ ) {
282
269
  dose = ParseDose.new(qty, unit)
283
- dose.unit = dose.unit.to_s + ' et ' + ParseDose.new(dose_right).to_s
270
+ dose.unit = dose.unit.to_s + " et " + ParseDose.new(dose_right).to_s
284
271
  dose
285
272
  }
286
273
 
287
274
  @@substances ||= []
288
- @@excipiens = nil
289
- def CompositionTransformer.clear_substances
290
- @@more_info = nil
275
+ @@excipiens = nil
276
+ def self.clear_substances
277
+ @@more_info = nil
291
278
  @@substances = []
292
- @@excipiens = nil
293
- @@corresp = nil
279
+ @@excipiens = nil
280
+ @@corresp = nil
294
281
  end
295
- def CompositionTransformer.substances
282
+
283
+ def self.substances
296
284
  @@substances.clone
297
285
  end
298
- def CompositionTransformer.excipiens
286
+
287
+ def self.excipiens
299
288
  @@excipiens ? @@excipiens.clone : nil
300
289
  end
301
- def CompositionTransformer.corresp
290
+
291
+ def self.corresp
302
292
  @@corresp ? @@corresp.clone : nil
303
293
  end
304
294
  end
@@ -306,29 +296,31 @@ end
306
296
  class ParseDose
307
297
  attr_reader :qty, :qty_range
308
298
  attr_accessor :unit
309
- def initialize(qty=nil, unit=nil)
299
+ def initialize(qty = nil, unit = nil)
310
300
  puts "ParseDose.new from #{qty.inspect} #{unit.inspect} #{unit.inspect}" if VERBOSE_MESSAGES
311
- if qty and (qty.is_a?(String) || qty.is_a?(Parslet::Slice))
312
- string = qty.to_s.gsub("'", '')
313
- if string.index('-') and (string.index('-') > 0)
301
+ if qty && (qty.is_a?(String) || qty.is_a?(Parslet::Slice))
302
+ string = qty.to_s.delete("'")
303
+ if string.index("-") && (string.index("-") > 0)
314
304
  @qty_range = string
315
305
  elsif string.index(/\^|\*|\//)
316
- @qty = string
306
+ @qty = string
317
307
  else
318
- @qty = string.index('.') ? string.to_f : string.to_i
308
+ @qty = string.index(".") ? string.to_f : string.to_i
319
309
  end
320
310
  elsif qty
321
- @qty = qty.eval
311
+ @qty = qty.eval
322
312
  else
323
313
  @qty = 1
324
314
  end
325
315
  @unit = unit ? unit.to_s : nil
326
316
  end
317
+
327
318
  def eval
328
319
  self
329
320
  end
321
+
330
322
  def to_s
331
- return @unit unless @qty or @qty_range
323
+ return @unit unless @qty || @qty_range
332
324
  res = "#{@qty}#{@qty_range}"
333
325
  res = "#{res} #{@unit}" if @unit
334
326
  res
@@ -336,32 +328,36 @@ class ParseDose
336
328
  end
337
329
 
338
330
  class ParseSubstance
339
- attr_accessor :name, :qty, :unit, :chemical_substance, :chemical_qty, :chemical_unit, :is_active_agent, :dose, :cdose, :is_excipiens
340
- attr_accessor :description, :more_info, :salts
341
- def initialize(name, dose=nil)
331
+ attr_accessor :name, :chemical_substance, :chemical_qty, :chemical_unit, :is_active_agent, :dose, :cdose, :is_excipiens
332
+ attr_accessor :description, :more_info, :salts
333
+ attr_writer :unit, :qty
334
+ def initialize(name, dose = nil)
342
335
  puts "ParseSubstance.new from #{name.inspect} #{dose.inspect}" if VERBOSE_MESSAGES
343
336
  @name = ParseUtil.capitalize(name.to_s)
344
- @name.sub!(/\baqua\b/i, 'aqua')
345
- @name.sub!(/\bDER\b/i, 'DER')
346
- @name.sub!(/\bad pulverem\b/i, 'ad pulverem')
347
- @name.sub!(/\bad iniectabilia\b/i, 'ad iniectabilia')
348
- @name.sub!(/\bad suspensionem\b/i, 'ad suspensionem')
349
- @name.sub!(/\bad solutionem\b/i, 'ad solutionem')
350
- @name.sub!(/\bpro compresso\b/i, 'pro compresso')
351
- @name.sub!(/\bpro\b/i, 'pro')
352
- @name.sub!(/ Q\.S\. /i, ' q.s. ')
353
- @name.sub!(/\s+\bpro$/i, '')
337
+ @name.sub!(/\baqua\b/i, "aqua")
338
+ @name.sub!(/\bDER\b/i, "DER")
339
+ @name.sub!(/\bad pulverem\b/i, "ad pulverem")
340
+ @name.sub!(/\bad iniectabilia\b/i, "ad iniectabilia")
341
+ @name.sub!(/\bad suspensionem\b/i, "ad suspensionem")
342
+ @name.sub!(/\bad solutionem\b/i, "ad solutionem")
343
+ @name.sub!(/\bpro compresso\b/i, "pro compresso")
344
+ @name.sub!(/\bpro\b/i, "pro")
345
+ @name.sub!(/ Q\.S\. /i, " q.s. ")
346
+ @name.sub!(/\s+\bpro$/i, "")
354
347
  @dose = dose if dose
355
348
  @salts = []
356
349
  end
350
+
357
351
  def qty
358
- return @dose.qty_range if @dose and @dose.qty_range
352
+ return @dose.qty_range if @dose&.qty_range
359
353
  @dose ? @dose.qty : @qty
360
354
  end
355
+
361
356
  def unit
362
357
  return @unit if @unit
363
358
  @dose ? @dose.unit : @unit
364
359
  end
360
+
365
361
  def to_string
366
362
  s = "#{@name}:"
367
363
  s = " #{@qty}" if @qty
@@ -372,51 +368,55 @@ class ParseSubstance
372
368
  end
373
369
 
374
370
  class ParseComposition
375
- attr_accessor :source, :label, :label_description, :substances, :galenic_form, :route_of_administration,
376
- :corresp, :excipiens
377
-
378
- ErrorsToFix = { /(\d+)\s+\-\s*(\d+)/ => '\1-\2',
379
- 'o.1' => '0.1',
380
- /\s+(mg|g) DER:/ => ' \1, DER:',
381
- ' mind. ' => ' min. ',
382
- ' streptococci pyogen. ' => ' streptococci pyogen ',
383
- ' ut excipiens' => ', excipiens',
384
- ' Corresp. ' => ' corresp. ',
385
- ',,' => ',',
386
- 'avena elatior,dactylis glomerata' => 'avena elatior, dactylis glomerata',
387
- ' color.: corresp. ' => ' corresp.',
388
- / U\.: (excipiens) / => ' U. \1 ',
389
- / U\.: (alnus|betula|betula|betulae) / => ' U., \1 ',
390
- /^(acari allergeni extractum (\(acarus siro\)|).+\s+U\.\:)/ => 'A): \1',
391
- 'Solvens: alprostadilum' => 'alprostadilum',
392
- }
393
- @@errorHandler = ParseUtil::HandleSwissmedicErrors.new( ErrorsToFix )
371
+ attr_accessor :source, :label, :label_description, :substances, :galenic_form, :route_of_administration,
372
+ :corresp, :excipiens
373
+
374
+ ERRORS_TO_FIX = {
375
+ /(\d+)\s+-\s*(\d+)/ => '\1-\2',
376
+ "o.1" => "0.1",
377
+ /polymerisat(i|um) \d:\d/ => "polymerisatum",
378
+ /\s+(mg|g) DER:/ => ' \1, DER:',
379
+ " mind. " => " min. ",
380
+ " streptococci pyogen. " => " streptococci pyogen ",
381
+ " ut excipiens" => ", excipiens",
382
+ " Corresp. " => " corresp. ",
383
+ ",," => ",",
384
+ "avena elatior,dactylis glomerata" => "avena elatior, dactylis glomerata",
385
+ " color.: corresp. " => " corresp.",
386
+ / U\.: (excipiens) / => ' U. \1 ',
387
+ / U\.: (alnus|betula|betula|betulae) / => ' U., \1 ',
388
+ /^(acari allergeni extractum (\(acarus siro\)|).+\s+U\.:)/ => 'A): \1',
389
+ "Solvens: alprostadilum" => "alprostadilum",
390
+ }
391
+ @@error_handler = ParseUtil::HandleSwissmedicErrors.new(ERRORS_TO_FIX)
394
392
 
395
393
  def initialize(source)
396
394
  @substances ||= []
397
395
  puts "ParseComposition.new from #{source.inspect} @substances #{@substances.inspect}" if VERBOSE_MESSAGES
398
396
  @source = source.to_s
399
397
  end
400
- def ParseComposition.reset
401
- @@errorHandler = ParseUtil::HandleSwissmedicErrors.new( ErrorsToFix )
398
+
399
+ def self.reset
400
+ @@error_handler = ParseUtil::HandleSwissmedicErrors.new(ERRORS_TO_FIX)
402
401
  end
403
- def ParseComposition.report
404
- @@errorHandler.report
402
+
403
+ def self.report
404
+ @@error_handler.report
405
405
  end
406
- def ParseComposition.from_string(string)
407
- return nil if string == nil or string.eql?('.') or string.eql?('')
408
- stripped = string.gsub(/^"|["\n]+$/, '')
406
+
407
+ def self.from_string(string)
408
+ return nil if string.nil? || string.eql?(".") || string.eql?("")
409
+ stripped = string.gsub(/^"|["\n]+$/, "")
409
410
  return nil unless stripped
410
- if /(U\.I\.|U\.)$/.match(stripped)
411
- cleaned = stripped
411
+ cleaned = if /(U\.I\.|U\.)$/.match?(stripped)
412
+ stripped
412
413
  else
413
- cleaned = stripped.sub(/[\.]+$/, '')
414
+ stripped.sub(/\.+$/, "")
414
415
  end
415
- value = nil
416
416
  puts "ParseComposition.from_string #{string}" if VERBOSE_MESSAGES # /ng-tr/.match(Socket.gethostbyname(Socket.gethostname).first)
417
417
 
418
- cleaned = @@errorHandler.apply_fixes(cleaned)
419
- puts "ParseComposition.new cleaned #{cleaned}" if VERBOSE_MESSAGES and not cleaned.eql?(stripped)
418
+ cleaned = @@error_handler.apply_fixes(cleaned)
419
+ puts "ParseComposition.new cleaned #{cleaned}" if VERBOSE_MESSAGES && !cleaned.eql?(stripped)
420
420
  CompositionTransformer.clear_substances
421
421
  result = ParseComposition.new(cleaned)
422
422
  parser = CompositionParser.new
@@ -430,8 +430,8 @@ class ParseComposition
430
430
  ast = transf.apply(parser.parse(cleaned))
431
431
  end
432
432
  rescue Parslet::ParseFailed => error
433
- @@errorHandler.nrParsingErrors += 1
434
- puts "#{File.basename(__FILE__)}:#{__LINE__}: failed parsing ==> #{cleaned}"
433
+ @@error_handler.nrParsingErrors += 1
434
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: failed parsing ==> #{cleaned} #{error}"
435
435
  return nil
436
436
  end
437
437
  result.source = string
@@ -441,50 +441,46 @@ class ParseComposition
441
441
  result.substances = CompositionTransformer.substances
442
442
  result.excipiens = CompositionTransformer.excipiens
443
443
  result.corresp = CompositionTransformer.corresp if CompositionTransformer.corresp
444
- if result.excipiens and result.excipiens.unit
445
- pro_qty = "/#{result.excipiens.qty} #{result.excipiens.unit}".sub(/\/1\s+/, '/')
446
- result.substances.each {
447
- |substance|
448
- next unless substance.is_a?(ParseSubstance)
449
- substance.chemical_substance.unit = "#{substance.chemical_substance.unit}#{pro_qty}" if substance.chemical_substance
450
- substance.dose.unit = "#{substance.dose.unit}#{pro_qty}" if substance.unit and not substance.unit.eql?(result.excipiens.unit)
444
+ if result&.excipiens&.unit
445
+ pro_qty = "/#{result.excipiens.qty} #{result.excipiens.unit}".sub(/\/1\s+/, "/")
446
+ result.substances.each { |substance|
447
+ next unless substance.is_a?(ParseSubstance)
448
+ substance.chemical_substance.unit = "#{substance.chemical_substance.unit}#{pro_qty}" if substance.chemical_substance
449
+ substance.dose.unit = "#{substance.dose.unit}#{pro_qty}" if substance.unit && !substance.unit.eql?(result.excipiens.unit)
451
450
  }
452
451
  end
453
- if ast.is_a?(Array) and ast.first.is_a?(Hash)
452
+ if ast.is_a?(Array) && ast.first.is_a?(Hash)
454
453
  label = ast.first[:label].to_s if ast.first[:label]
455
454
  label_description = ast.first[:label_description].to_s if ast.first[:label_description]
456
- elsif ast and ast.is_a?(Hash)
457
- label = ast[:label].to_s if ast[:label]
455
+ elsif ast&.is_a?(Hash)
456
+ label = ast[:label].to_s if ast[:label]
458
457
  label_description = ast[:label_description].to_s if ast[:label_description]
459
458
  end
460
459
  if label
461
- if label and not /((A|B|C|D|E|I|II|III|IV|\)+)\s+et\s+(A|B|C|D|E|I|II|III|IV|\))+)/.match(label)
462
- result.label = label
460
+ if label && !/((A|B|C|D|E|I|II|III|IV|\)+)\s+et\s+(A|B|C|D|E|I|II|III|IV|\))+)/.match(label)
461
+ result.label = label
463
462
  end
464
- result.label_description = label_description.gsub(/:+$/, '').strip if label_description
463
+ result.label_description = label_description.gsub(/:+$/, "").strip if label_description
465
464
  end
466
- result.corresp = ast[:corresp].to_s.sub(/:\s+/, '') if not result.corresp and ast.is_a?(Hash) and ast[:corresp]
467
- return result
465
+ result.corresp = ast[:corresp].to_s.sub(/:\s+/, "") if !result.corresp && ast.is_a?(Hash) && ast[:corresp]
466
+ result
468
467
  end
469
468
  end
470
469
 
471
470
  class GalenicFormTransformer < CompositionTransformer
472
-
473
- rule( :preparation_name => simple(:preparation_name),
474
- :galenic_form => simple(:preparation_name),
475
- ) {
476
- |dictionary|
477
- puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
478
- name = dictionary[:preparation_name] ? dictionary[:preparation_name].to_s : nil
479
- form = dictionary[:galenic_form] ? dictionary[:galenic_form].to_s : nil
480
- # name, form
471
+ rule(preparation_name: simple(:preparation_name),
472
+ galenic_form: simple(:preparation_name)) { |dictionary|
473
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
474
+ dictionary[:preparation_name] ? dictionary[:preparation_name].to_s : nil
475
+ dictionary[:galenic_form] ? dictionary[:galenic_form].to_s : nil
476
+ # name, form
481
477
  }
482
478
  end
483
479
 
484
480
  class ParseGalenicForm
485
- def ParseGalenicForm.from_string(string)
486
- return nil if string == nil
487
- stripped = string.gsub(/^"|["\n]+$/, '')
481
+ def self.from_string(string)
482
+ return nil if string.nil?
483
+ stripped = string.gsub(/^"|["\n]+$/, "")
488
484
  return nil unless stripped
489
485
  puts "ParseGalenicForm.from_string #{string}" if VERBOSE_MESSAGES # /ng-tr/.match(Socket.gethostbyname(Socket.gethostname).first)
490
486
 
@@ -499,13 +495,13 @@ class ParseGalenicForm
499
495
  ast = transf.apply(parser.parse(string))
500
496
  end
501
497
  rescue Parslet::ParseFailed => error
502
- @@errorHandler.nrParsingErrors += 1
503
- puts "#{File.basename(__FILE__)}:#{__LINE__}: failed parsing ==> #{string}"
498
+ @@error_handler.nrParsingErrors += 1
499
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: failed parsing ==> #{string} #{error}"
504
500
  return nil
505
501
  end
506
502
  return [] unless ast
507
- form = ast[:galenic_form] ? ast[:galenic_form].to_s.sub(/^\/\s+/, '') : nil
503
+ form = ast[:galenic_form] ? ast[:galenic_form].to_s.sub(/^\/\s+/, "") : nil
508
504
  name = ast[:prepation_name] ? ast[:prepation_name].to_s.strip : nil
509
- return [name, form]
505
+ [name, form]
510
506
  end
511
507
  end