oddb2xml 2.0.5 → 2.0.6

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.
@@ -0,0 +1,598 @@
1
+ # encoding: utf-8
2
+
3
+ # This file is shared since oddb2xml 2.0.0 (lib/oddb2xml/parse_compositions.rb)
4
+ # with oddb.org src/plugin/parse_compositions.rb
5
+ #
6
+ # It allows an easy parsing of the column P Zusammensetzung of the swissmedic packages.xlsx file
7
+ #
8
+
9
+ require 'parslet'
10
+ require 'parslet/convenience'
11
+ require 'oddb2xml/compositions_syntax'
12
+ include Parslet
13
+ VERBOSE_MESSAGES = false
14
+
15
+ module ParseUtil
16
+ # this class is responsible to patch errors in swissmedic entries after
17
+ # oddb.org detected them, as it takes sometimes a few days (or more) till they get corrected
18
+ # Reports the number of occurrences of each entry
19
+ class HandleSwissmedicErrors
20
+
21
+ attr_accessor :nrParsingErrors
22
+ class ErrorEntry < Struct.new('ErrorEntry', :pattern, :replacement, :nr_occurrences)
23
+ end
24
+
25
+ def reset_errors
26
+ @errors = []
27
+ @nrLines = 0
28
+ @nrParsingErrors = 0
29
+ end
30
+
31
+ # error_entries should be a hash of pattern, replacement
32
+ def initialize(error_entries)
33
+ reset_errors
34
+ error_entries.each{ |pattern, replacement| @errors << ErrorEntry.new(pattern, replacement, 0) }
35
+ end
36
+
37
+ def report
38
+ s = ["Report of changed compositions in #{@nrLines} lines. Had #{@nrParsingErrors} parsing errors" ]
39
+ @errors.each {
40
+ |entry|
41
+ s << " replaced #{entry.nr_occurrences} times '#{entry.pattern}' by '#{entry.replacement}'"
42
+ }
43
+ s
44
+ end
45
+
46
+ def apply_fixes(string)
47
+ result = string.clone
48
+ @errors.each{
49
+ |entry|
50
+ intermediate = result.clone
51
+ result = result.gsub(entry.pattern, entry.replacement)
52
+ unless result.eql?(intermediate)
53
+ entry.nr_occurrences += 1
54
+ puts "Fixed #{result}" if VERBOSE_MESSAGES
55
+ end
56
+ }
57
+ @nrLines += 1
58
+ result
59
+ end
60
+ # 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
61
+ end
62
+
63
+ def ParseUtil.capitalize(string)
64
+ string.split(/\s+/u).collect { |word| word.capitalize }.join(' ').strip
65
+ end
66
+
67
+ def ParseUtil.parse_compositions(composition_text, active_agents_string = '')
68
+ active_agents = active_agents_string ? active_agents_string.downcase.split(/,\s+/) : []
69
+ comps = []
70
+ lines = composition_text.gsub(/\r\n?/u, "\n").split(/\n/u)
71
+ lines.select {
72
+ |line|
73
+ composition = ParseComposition.from_string(line)
74
+ if composition.is_a?(ParseComposition)
75
+ composition.substances.each {
76
+ |substance_item|
77
+ substance_item.is_active_agent = (active_agents.find {|x| x.downcase.eql?(substance_item.name.downcase) } != nil)
78
+ 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) }
79
+ }
80
+ comps << composition
81
+ end
82
+ }
83
+ comps << ParseComposition.new(composition_text.split(/,|:|\(/)[0]) if comps.size == 0
84
+ comps
85
+ end
86
+ end
87
+
88
+ class IntLit < Struct.new(:int)
89
+ def eval; int.to_i; end
90
+ end
91
+ class QtyLit < Struct.new(:qty)
92
+ def eval; qty.to_i; end
93
+ end
94
+
95
+ class CompositionTransformer < Parslet::Transform
96
+ rule(:int => simple(:int)) { IntLit.new(int) }
97
+ rule(:number => simple(:nb)) {
98
+ nb.match(/[eE\.]/) ? Float(nb) : Integer(nb)
99
+ }
100
+ rule(
101
+ :qty_range => simple(:qty_range),
102
+ :unit => simple(:unit)) {
103
+ ParseDose.new(qty_range, unit)
104
+ }
105
+ rule(
106
+ :qty_range => simple(:qty_range)) {
107
+ ParseDose.new(qty_range)
108
+ }
109
+ rule(
110
+ :qty => simple(:qty),
111
+ :unit => simple(:unit)) {
112
+ ParseDose.new(qty, unit)
113
+ }
114
+ rule(
115
+ :unit => simple(:unit)) { ParseDose.new(nil, unit) }
116
+ rule(
117
+ :qty => simple(:qty)) { ParseDose.new(qty, nil) }
118
+
119
+ @@substances ||= []
120
+ @@excipiens = nil
121
+ def CompositionTransformer.clear_substances
122
+ @@substances = []
123
+ @@excipiens = nil
124
+ @@corresp = nil
125
+ end
126
+ def CompositionTransformer.substances
127
+ @@substances.clone
128
+ end
129
+ def CompositionTransformer.excipiens
130
+ @@excipiens ? @@excipiens.clone : nil
131
+ end
132
+ def CompositionTransformer.corresp
133
+ @@corresp ? @@corresp.clone : nil
134
+ end
135
+
136
+ rule(:ratio => simple(:ratio) ) {
137
+ |dictionary|
138
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
139
+ @@substances.last.more_info = dictionary[:ratio].to_s if @@substances.last
140
+ }
141
+ rule(:substance => sequence(:substance),
142
+ :ratio => simple(:ratio)) {
143
+ |dictionary|
144
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
145
+ @@substances.last.more_info = dictionary[:ratio].to_s if @@substances.last
146
+ }
147
+
148
+ rule(:solvens => simple(:solvens) ) {
149
+ |dictionary|
150
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
151
+ substance = ParseSubstance.new(dictionary[:solvens].to_s)
152
+ substance.more_info = 'Solvens'
153
+ @@substances << substance
154
+ }
155
+ rule(:lebensmittel_zusatz => simple(:lebensmittel_zusatz),
156
+ :more_info => simple(:more_info),
157
+ :digits => simple(:digits)) {
158
+ |dictionary|
159
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
160
+ substance = ParseSubstance.new("#{dictionary[:lebensmittel_zusatz]} #{dictionary[:digits]}")
161
+ substance.more_info = dictionary[:more_info].to_s.sub(/:$/, '')
162
+ @@substances << substance
163
+ }
164
+ rule(:lebensmittel_zusatz => simple(:lebensmittel_zusatz),
165
+ :digits => simple(:digits)) {
166
+ |dictionary|
167
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
168
+ @@substances << ParseSubstance.new("#{dictionary[:lebensmittel_zusatz]} #{dictionary[:digits]}")
169
+ dictionary[:substance]
170
+ }
171
+ rule(:substance => simple(:substance)) {
172
+ |dictionary|
173
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
174
+ }
175
+ rule(:substance_name => simple(:substance_name),
176
+ :dose => simple(:dose),
177
+ ) {
178
+ |dictionary|
179
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
180
+ @@substances << ParseSubstance.new(dictionary[:substance_name].to_s, dictionary[:dose])
181
+ }
182
+ rule(:substance_ut => sequence(:substance_ut),
183
+ ) {
184
+ |dictionary|
185
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
186
+ nil
187
+ }
188
+ rule(:for_ut => sequence(:for_ut),
189
+ ) {
190
+ |dictionary|
191
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
192
+ if dictionary[:for_ut].size > 1
193
+ @@substances[-2].salts << dictionary[:for_ut].last.clone
194
+ @@substances.delete(dictionary[:for_ut].last)
195
+ end
196
+ nil
197
+ }
198
+
199
+ rule(:substance_name => simple(:substance_name),
200
+ :dose => simple(:dose),
201
+ :substance_corresp => sequence(:substance_corresp),
202
+ ) {
203
+ |dictionary|
204
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
205
+ substance = ParseSubstance.new(dictionary[:substance_name].to_s, dictionary[:dose])
206
+ substance.chemical_substance = @@substances.last
207
+ @@substances.delete_at(-1)
208
+ @@substances << substance
209
+ }
210
+
211
+ rule(:mineralia => simple(:mineralia),
212
+ :more_info => simple(:more_info),
213
+ :substance_name => simple(:substance_name),
214
+ :dose => simple(:dose),
215
+ ) {
216
+ |dictionary|
217
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
218
+ substance = ParseSubstance.new(dictionary[:substance_name].to_s, dictionary[:dose])
219
+ substance.more_info = dictionary[:mineralia].to_s + ' ' + dictionary[:more_info].to_s
220
+ # TODO: fix alia
221
+ @@substances << substance
222
+ }
223
+ rule(:substance_name => simple(:substance_name),
224
+ :conserv => simple(:conserv),
225
+ :dose => simple(:dose),
226
+ ) {
227
+ |dictionary|
228
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}"
229
+ substance = ParseSubstance.new(dictionary[:substance_name], ParseDose.new(dictionary[:dose].to_s))
230
+ @@substances << substance
231
+ substance.more_info = dictionary[:conserv].to_s.sub(/:$/, '')
232
+ }
233
+
234
+ rule(:substance_name => simple(:substance_name),
235
+ :mineralia => simple(:mineralia),
236
+ ) {
237
+ |dictionary|
238
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}"
239
+ substance = ParseSubstance.new(dictionary[:substance_name])
240
+ substance.more_info = dictionary[:mineralia].to_s.sub(/:$/, '')
241
+ @@substances << substance
242
+ }
243
+ rule(:substance_name => simple(:substance_name),
244
+ :more_info => simple(:more_info),
245
+ ) {
246
+ |dictionary|
247
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
248
+ substance = ParseSubstance.new(dictionary[:substance_name])
249
+ @@substances << substance
250
+ substance.more_info = dictionary[:more_info].to_s.sub(/:$/, '')
251
+ }
252
+ rule(:substance_name => simple(:substance_name),
253
+ :residui => simple(:residui),
254
+ ) {
255
+ |dictionary|
256
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
257
+ binding.pry
258
+ substance = ParseSubstance.new(dictionary[:substance_name])
259
+ @@substances << substance
260
+ substance.more_info = dictionary[:residui].to_s.sub(/:$/, '')
261
+ }
262
+ rule(:qty => simple(:qty),
263
+ :unit => simple(:unit),
264
+ :dose_right => simple(:dose_right),
265
+ ) {
266
+ |dictionary|
267
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
268
+ ParseDose.new(dictionary[:qty].to_s, dictionary[:unit].to_s + ' et ' + dictionary[:dose_right].to_s )
269
+ }
270
+
271
+ rule(:substance_name => simple(:substance_name),
272
+ :qty => simple(:qty),
273
+ ) {
274
+ |dictionary|
275
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}"
276
+ @@substances << ParseSubstance.new(dictionary[:substance_name].to_s.strip, ParseDose.new(dictionary[:qty].to_s))
277
+ }
278
+
279
+ rule(:substance_name => simple(:substance_name),
280
+ :dose_corresp => simple(:dose_corresp),
281
+ ) {
282
+ |dictionary|
283
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}"
284
+ @@substances << ParseSubstance.new(dictionary[:substance_name].to_s, dictionary[:dose_corresp])
285
+ }
286
+ rule(:description => simple(:description),
287
+ :substance_name => simple(:substance_name),
288
+ :qty => simple(:qty),
289
+ :more_info => simple(:more_info),
290
+ ) {
291
+ |dictionary|
292
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
293
+ substance = ParseSubstance.new(dictionary[:substance_name], ParseDose.new(dictionary[:qty].to_s))
294
+ @@substances << substance
295
+ substance.more_info = dictionary[:more_info].to_s
296
+ substance.description = dictionary[:description].to_s
297
+ substance
298
+ }
299
+ rule(:der => simple(:der),
300
+ ) {
301
+ |dictionary|
302
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
303
+ @@substances << ParseSubstance.new(dictionary[:der].to_s)
304
+ }
305
+ rule(:der => simple(:der),
306
+ :substance_corresp => sequence(:substance_corresp),
307
+ ) {
308
+ |dictionary|
309
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
310
+ substance = ParseSubstance.new(dictionary[:der].to_s)
311
+ substance.chemical_substance = @@substances.last
312
+ @@substances.delete_at(-1)
313
+ @@substances << substance
314
+ }
315
+ rule(:histamin => simple(:histamin),
316
+ ) {
317
+ |dictionary|
318
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: histamin dictionary #{dictionary}"
319
+ @@substances << ParseSubstance.new(dictionary[:histamin].to_s)
320
+ }
321
+ rule(:substance_name => simple(:substance_name),
322
+ ) {
323
+ |dictionary|
324
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
325
+ @@substances << ParseSubstance.new(dictionary[:substance_name].to_s)
326
+ }
327
+ rule(:one_substance => sequence(:one_substance)) {
328
+ |dictionary|
329
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}"
330
+ @@substances << ParseSubstance.new(dictionary[:one_substance])
331
+ }
332
+ rule(:one_substance => sequence(:one_substance)) {
333
+ |dictionary|
334
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}"
335
+ @@substances << ParseSubstance.new(dictionary[:one_substance])
336
+ }
337
+
338
+ rule(:substance_name => simple(:substance_name),
339
+ :substance_ut => sequence(:substance_ut),
340
+ :dose => simple(:dose),
341
+ ) {
342
+ |dictionary|
343
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}"
344
+ @@substances.last.salts << ParseSubstance.new(dictionary[:substance_name].to_s, dictionary[:dose])
345
+ nil
346
+ }
347
+
348
+ rule(:mineralia => simple(:mineralia),
349
+ :dose => simple(:dose),
350
+ :substance_name => simple(:substance_name),
351
+ ) {
352
+ |dictionary|
353
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
354
+ dose = dictionary[:dose].is_a?(ParseDose) ? dictionary[:dose] : ParseDose.new(dictionary[:dose].to_s)
355
+ substance = ParseSubstance.new(dictionary[:substance_name], dose)
356
+ substance.more_info = dictionary[:mineralia].to_s
357
+ @@substances << substance
358
+ # @@substances << ParseSubstance.new(dictionary[:substance_name].to_s, dictionary[:dose])
359
+ }
360
+
361
+ rule(:mineralia => simple(:mineralia),
362
+ :dose => simple(:dose),
363
+ :substance_ut => simple(:substance_ut),
364
+ ) {
365
+ |dictionary|
366
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}"
367
+ dose = dictionary[:dose].is_a?(ParseDose) ? dictionary[:dose] : ParseDose.new(dictionary[:dose].to_s)
368
+ substance = ParseSubstance.new(dictionary[:substance_ut], dose)
369
+ substance.more_info = dictionary[:mineralia].to_s
370
+ binding.pry
371
+ @@substances << substance
372
+ nil
373
+ }
374
+
375
+
376
+ rule(:mineralia => simple(:mineralia),
377
+ :substance_ut => simple(:substance_ut),
378
+ ) {
379
+ |dictionary|
380
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}"
381
+ binding.pry
382
+ @@substances.last.salts << ParseSubstance.new(dictionary[:substance_name].to_s, dictionary[:dose])
383
+ nil
384
+ }
385
+ rule( :more_info => simple(:more_info),
386
+ :substance_name => simple(:substance_name),
387
+ :dose => simple(:dose),
388
+ ) {
389
+ |dictionary|
390
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
391
+ dose = dictionary[:dose].is_a?(ParseDose) ? dictionary[:dose] : ParseDose.new(dictionary[:dose].to_s)
392
+ substance = ParseSubstance.new(dictionary[:substance_name], dose)
393
+ substance.more_info = dictionary[:more_info].to_s
394
+ @@substances << substance
395
+ }
396
+
397
+ rule(:excipiens => simple(:excipiens),
398
+ ) {
399
+ |dictionary|
400
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
401
+ @@excipiens = dictionary[:excipiens].is_a?(ParseDose) ? ParseSubstance.new('excipiens', dictionary[:excipiens]) : nil
402
+ }
403
+
404
+ rule(:substance_name => simple(:substance_name),
405
+ :dose_pro => simple(:dose_pro),
406
+ ) {
407
+ |dictionary|
408
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
409
+ dose = dictionary[:dose_pro].is_a?(ParseDose) ? dictionary[:dose_pro] : ParseDose.new(dictionary[:dose_pro].to_s)
410
+ substance = ParseSubstance.new(dictionary[:substance_name], dose)
411
+ @@excipiens = dose
412
+ @@substances << substance
413
+ }
414
+ rule(:substance_name => simple(:substance_name),
415
+ :dose => simple(:dose),
416
+ :dose_pro => simple(:dose_pro),
417
+ ) {
418
+ |dictionary|
419
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
420
+ dose = dictionary[:dose_pro].is_a?(ParseDose) ? dictionary[:dose_pro] : ParseDose.new(dictionary[:dose_pro].to_s)
421
+ dose_pro = dictionary[:dose_pro].is_a?(ParseDose) ? dictionary[:dose_pro] : ParseDose.new(dictionary[:dose_pro].to_s)
422
+ substance = ParseSubstance.new(dictionary[:substance_name], dose)
423
+ @@excipiens = dose_pro
424
+ @@substances << substance
425
+ }
426
+
427
+ rule(:dose_pro => simple(:dose_pro),
428
+ ) {
429
+ |dictionary|
430
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
431
+ dictionary[:dose_pro]
432
+ }
433
+
434
+ rule(:corresp => simple(:corresp),
435
+ ) {
436
+ |dictionary|
437
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: dictionary #{dictionary}" if VERBOSE_MESSAGES
438
+ @@corresp = dictionary[:corresp].to_s
439
+ }
440
+ end
441
+
442
+ class ParseDose
443
+ attr_reader :qty, :qty_range
444
+ attr_accessor :unit
445
+ def initialize(qty=nil, unit=nil)
446
+ puts "ParseDose.new from #{qty.inspect} #{unit.inspect} #{unit.inspect}" if VERBOSE_MESSAGES
447
+ if qty and (qty.is_a?(String) || qty.is_a?(Parslet::Slice))
448
+ string = qty.to_s.gsub("'", '')
449
+ if string.index('-') and (string.index('-') > 0)
450
+ @qty_range = string
451
+ elsif string.index(/\^|\*|\//)
452
+ @qty = string
453
+ else
454
+ @qty = string.index('.') ? string.to_f : string.to_i
455
+ end
456
+ elsif qty
457
+ @qty = qty.eval
458
+ else
459
+ @qty = 1
460
+ end
461
+ @unit = unit ? unit.to_s : nil
462
+ end
463
+ def eval
464
+ self
465
+ end
466
+ def to_s
467
+ return @unit unless @qty or @qty_range
468
+ res = "#{@qty}#{@qty_range}"
469
+ res = "#{res} #{@unit}" if @unit
470
+ res
471
+ end
472
+ end
473
+
474
+ class ParseSubstance
475
+ attr_accessor :name, :qty, :unit, :chemical_substance, :chemical_qty, :chemical_unit, :is_active_agent, :dose, :cdose, :is_excipiens
476
+ attr_accessor :description, :more_info, :salts
477
+ def initialize(name, dose=nil)
478
+ puts "ParseSubstance.new from #{name.inspect} #{dose.inspect}" if VERBOSE_MESSAGES
479
+ @name = ParseUtil.capitalize(name.to_s)
480
+ @name.sub!(/\baqua\b/i, 'aqua')
481
+ @name.sub!(/\bDER\b/i, 'DER')
482
+ @name.sub!(/\bad pulverem\b/i, 'ad pulverem')
483
+ @name.sub!(/\bad iniectabilia\b/i, 'ad iniectabilia')
484
+ @name.sub!(/\bad suspensionem\b/i, 'ad suspensionem')
485
+ @name.sub!(/\bad solutionem\b/i, 'ad solutionem')
486
+ @name.sub!(/\bpro compresso\b/i, 'pro compresso')
487
+ @name.sub!(/\bpro\b/i, 'pro')
488
+ @name.sub!(/ Q\.S\. /i, ' q.s. ')
489
+ @name.sub!(/\s+\bpro$/i, '')
490
+ @dose = dose if dose
491
+ @salts = []
492
+ end
493
+ def qty
494
+ return @dose.qty_range if @dose and @dose.qty_range
495
+ @dose ? @dose.qty : @qty
496
+ end
497
+ def unit
498
+ return @unit if @unit
499
+ @dose ? @dose.unit : @unit
500
+ end
501
+ def to_string
502
+ s = "#{@name}:"
503
+ s = " #{@qty}" if @qty
504
+ s = " #{@unit}" if @unit
505
+ s += @chemical_substance.to_s if chemical_substance
506
+ s
507
+ end
508
+ end
509
+
510
+ class ParseComposition
511
+ attr_accessor :source, :label, :label_description, :substances, :galenic_form, :route_of_administration,
512
+ :corresp
513
+
514
+ ErrorsToFix = { /(sulfuris D6\s[^\s]+\smg)\s([^,]+)/ => '\1, \2',
515
+ /(\d+)\s+\-\s*(\d+)/ => '\1-\2',
516
+ 'o.1' => '0.1',
517
+ 'g DER:' => 'g, DER:',
518
+ /(excipiens ad solutionem pro \d+ ml), corresp\./ => '\1 corresp.',
519
+ /^(pollinis allergeni extractum[^\:]+\:)/ => 'A): \1',
520
+ /^(acari allergeni extractum 5000 U\.\:)/ => 'A): \1',
521
+ }
522
+ @@errorHandler = ParseUtil::HandleSwissmedicErrors.new( ErrorsToFix )
523
+
524
+ def initialize(source)
525
+ @substances ||= []
526
+ puts "ParseComposition.new from #{source.inspect} @substances #{@substances.inspect}" if VERBOSE_MESSAGES
527
+ @source = source.to_s
528
+ end
529
+ def ParseComposition.reset
530
+ @@errorHandler = ParseUtil::HandleSwissmedicErrors.new( ErrorsToFix )
531
+ end
532
+ def ParseComposition.report
533
+ @@errorHandler.report
534
+ end
535
+ def ParseComposition.from_string(string)
536
+ return nil if string == nil or string.eql?('.') or string.eql?('')
537
+ stripped = string.gsub(/^"|["\n]+$/, '')
538
+ return nil unless stripped
539
+ @@errorHandler.nrParsingErrors += 1
540
+ if /(U\.I\.|U\.)$/.match(stripped)
541
+ cleaned = stripped
542
+ else
543
+ cleaned = stripped.sub(/[\.]+$/, '')
544
+ end
545
+ value = nil
546
+ puts "ParseComposition.from_string #{string}" if VERBOSE_MESSAGES # /ng-tr/.match(Socket.gethostbyname(Socket.gethostname).first)
547
+
548
+ cleaned = @@errorHandler.apply_fixes(cleaned)
549
+ puts "ParseComposition.new cleaned #{cleaned}" if VERBOSE_MESSAGES and not cleaned.eql?(stripped)
550
+
551
+ CompositionTransformer.clear_substances
552
+ result = ParseComposition.new(cleaned)
553
+ parser3 = CompositionParser.new
554
+ transf3 = CompositionTransformer.new
555
+ begin
556
+ if defined?(RSpec)
557
+ ast = transf3.apply(parser3.parse_with_debug(cleaned))
558
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: ==> #{ast}" if VERBOSE_MESSAGES
559
+ else
560
+ ast = transf3.apply(parser3.parse(cleaned))
561
+ end
562
+ rescue Parslet::ParseFailed => error
563
+ puts "#{File.basename(__FILE__)}:#{__LINE__}: failed parsing ==> #{cleaned}"
564
+ return nil
565
+ end
566
+ result.source = string
567
+ return result unless ast
568
+ return result if ast.is_a?(Parslet::Slice)
569
+ # pp ast; binding.pry
570
+
571
+ result.substances = CompositionTransformer.substances
572
+ excipiens = CompositionTransformer.excipiens
573
+ result.corresp = CompositionTransformer.corresp if CompositionTransformer.corresp
574
+ if excipiens and excipiens.unit
575
+ pro_qty = "/#{excipiens.qty} #{excipiens.unit}".sub(/\/1\s+/, '/')
576
+ result.substances.each {
577
+ |substance|
578
+ substance.chemical_substance.unit = "#{substance.chemical_substance.unit}#{pro_qty}" if substance.chemical_substance
579
+ substance.dose.unit = "#{substance.dose.unit}#{pro_qty}" if substance.unit and not substance.unit.eql?(excipiens.unit)
580
+ }
581
+ end
582
+ if ast.is_a?(Array) and ast.first.is_a?(Hash)
583
+ label = ast.first[:label].to_s if ast.first[:label]
584
+ label_description = ast.first[:label_description].to_s if ast.first[:label_description]
585
+ elsif ast and ast.is_a?(Hash)
586
+ label = ast[:label].to_s if ast[:label]
587
+ label_description = ast[:label_description].to_s if ast[:label_description]
588
+ end
589
+ if label
590
+ 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)
591
+ result.label = label
592
+ end
593
+ result.label_description = label_description
594
+ end
595
+ @@errorHandler.nrParsingErrors -=1 if result.substances.size > 0 or result.corresp
596
+ return result
597
+ end
598
+ end
@@ -1,3 +1,3 @@
1
1
  module Oddb2xml
2
- VERSION = "2.0.5"
2
+ VERSION = "2.0.6"
3
3
  end
data/oddb2xml.gemspec CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_dependency 'spreadsheet', '~> 1.0.0'
29
29
  spec.add_dependency 'rubyXL', '~> 3.3.1'
30
30
  spec.add_dependency 'sax-machine', '~> 0.1.0'
31
+ spec.add_dependency 'parslet', '~> 1.7.0'
31
32
 
32
33
  spec.add_development_dependency "bundler"
33
34
  spec.add_development_dependency "rake"
data/spec/builder_spec.rb CHANGED
@@ -41,7 +41,7 @@ def check_validation_via_xsd
41
41
  }
42
42
  end
43
43
  describe Oddb2xml::Builder do
44
- NrExtendedArticles = 73
44
+ NrExtendedArticles = 78
45
45
  NrPharmaAndNonPharmaArticles = 60
46
46
  NrPharmaArticles = 5
47
47
  include ServerMockHelper