oddb2xml 2.7.1 → 2.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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