oddb2xml 2.5.0 → 2.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/Elexis_Artikelstamm_v003.xsd +387 -0
  4. data/Elexis_Artikelstamm_v5.xsd +513 -0
  5. data/Gemfile +2 -6
  6. data/History.txt +11 -0
  7. data/README.md +35 -27
  8. data/artikelstamm.md +68 -0
  9. data/bin/compare_v5 +41 -0
  10. data/bin/oddb2xml +3 -15
  11. data/data/article_overrides.yaml +51859 -0
  12. data/data/gtin2ignore.yaml +30510 -0
  13. data/data/product_overrides.yaml +4 -0
  14. data/lib/oddb2xml/builder.rb +543 -192
  15. data/lib/oddb2xml/cli.rb +82 -62
  16. data/lib/oddb2xml/compare.rb +189 -0
  17. data/lib/oddb2xml/compressor.rb +6 -3
  18. data/lib/oddb2xml/downloader.rb +79 -64
  19. data/lib/oddb2xml/extractor.rb +67 -40
  20. data/lib/oddb2xml/options.rb +76 -77
  21. data/lib/oddb2xml/parslet_compositions.rb +18 -1
  22. data/lib/oddb2xml/util.rb +25 -3
  23. data/lib/oddb2xml/version.rb +1 -1
  24. data/oddb2xml.gemspec +8 -5
  25. data/oddb2xml.xsd +1 -0
  26. data/spec/artikelstamm_spec.rb +383 -0
  27. data/spec/builder_spec.rb +147 -118
  28. data/spec/calc_spec.rb +3 -15
  29. data/spec/cli_spec.rb +24 -35
  30. data/spec/compare_spec.rb +24 -0
  31. data/spec/compressor_spec.rb +1 -3
  32. data/spec/data/Elexis_Artikelstamm_v5.xsd +513 -0
  33. data/spec/data/Preparations.xml +2200 -0
  34. data/spec/data/Publications.xls +0 -0
  35. data/spec/data/artikelstamm_N_010917.xml +39 -0
  36. data/spec/data/artikelstamm_N_011217.xml +17 -0
  37. data/spec/data/artikelstamm_P_010917.xml +86 -0
  38. data/spec/data/artikelstamm_P_011217.xml +63 -0
  39. data/spec/data/oddb2xml_files_lppv.txt +2 -0
  40. data/spec/data/refdata_NonPharma.xml +38 -0
  41. data/spec/data/refdata_Pharma.xml +220 -0
  42. data/spec/data/swissmedic_orphan.xlsx +0 -0
  43. data/spec/data/swissmedic_package.xlsx +0 -0
  44. data/spec/data/transfer.dat +59 -19
  45. data/spec/data/v5_first.xml +102 -0
  46. data/spec/data/v5_second.xml +184 -0
  47. data/spec/data_helper.rb +72 -0
  48. data/spec/downloader_spec.rb +19 -27
  49. data/spec/extractor_spec.rb +27 -33
  50. data/spec/fixtures/vcr_cassettes/artikelstamm.json +1 -0
  51. data/spec/options_spec.rb +73 -66
  52. data/spec/spec_helper.rb +73 -24
  53. data/test_options.rb +4 -2
  54. metadata +100 -21
  55. data/spec/data/XMLPublications.zip +0 -0
  56. data/spec/data/compressor/oddb_article.xml +0 -0
  57. data/spec/data/compressor/oddb_fi.xml +0 -0
  58. data/spec/data/compressor/oddb_fi_product.xml +0 -0
  59. data/spec/data/compressor/oddb_limitation.xml +0 -0
  60. data/spec/data/compressor/oddb_product.xml +0 -0
  61. data/spec/data/compressor/oddb_substance.xml +0 -0
@@ -0,0 +1,4 @@
1
+ ---
2
+ 3536601:
3
+ DSCRF: '--missing--'
4
+ DSCR: 'KENDURAL Depottabl'
@@ -35,6 +35,12 @@ module Oddb2xml
35
35
  'GENERATED_BY' => "oddb2xml #{VERSION}"
36
36
  }
37
37
  class Builder
38
+ Data_dir = File.expand_path(File.join(File.dirname(__FILE__),'..','..', 'data'))
39
+ @@article_overrides = YAML.load_file(File.join(Data_dir, 'article_overrides.yaml'))
40
+ @@product_overrides = YAML.load_file(File.join(Data_dir, 'product_overrides.yaml'))
41
+ @@ignore_file = File.join(Data_dir, 'gtin2ignore.yaml')
42
+ @@gtin2ignore = YAML.load_file(@@ignore_file) if File.exist?(@@ignore_file)
43
+ @@gtin2ignore ||= []
38
44
  attr_accessor :subject, :refdata, :items, :flags, :lppvs,
39
45
  :actions, :migel, :orphan,
40
46
  :infos, :packs, :infos_zur_rose,
@@ -51,7 +57,7 @@ module Oddb2xml
51
57
  @infos = {}
52
58
  @packs = {}
53
59
  @migel = {}
54
- @infos_zur_rose = {} # zurrose
60
+ @infos_zur_rose ||= {}
55
61
  @actions = []
56
62
  @orphan = []
57
63
  @ean14 = false
@@ -64,7 +70,7 @@ module Oddb2xml
64
70
  end
65
71
  end
66
72
  def to_xml(subject=nil)
67
- Oddb2xml.log "to_xml subject #{subject} #{@subject} for #{self.class}"
73
+ Oddb2xml.log "to_xml subject #{subject || @subject}"
68
74
  if subject
69
75
  self.send('build_' + subject.to_s)
70
76
  elsif @subject
@@ -90,8 +96,8 @@ module Oddb2xml
90
96
  # delete duplicates
91
97
  @migel[ean13] = nil
92
98
  end unless SkipMigelDownloader
93
- if seq = @items[obj[:ean]]
94
- obj[:seq] = seq
99
+ if seq = @items[obj[:ean13]]
100
+ obj[:seq] = seq.clone
95
101
  end
96
102
  @articles << obj
97
103
  @pharmacode[obj[:pharmacode]] = obj
@@ -100,11 +106,11 @@ module Oddb2xml
100
106
  @migel.values.compact.each do |migel|
101
107
  next unless migel[:pharmacode]
102
108
  entry = {
103
- :ean => migel[:ean].to_i,
109
+ :ean13 => migel[:ean13],
104
110
  :pharmacode => migel[:pharmacode],
105
111
  :stat_date => '',
106
- :desc_de => migel[:desc_de],
107
- :desc_fr => migel[:desc_fr],
112
+ :desc_de => migel[:desc_de],
113
+ :desc_fr => migel[:desc_fr],
108
114
  :atc_code => '',
109
115
  :quantity => migel[:quantity],
110
116
  :company_ean => migel[:company_ean],
@@ -114,50 +120,48 @@ module Oddb2xml
114
120
  @articles << entry
115
121
  end unless SkipMigelDownloader
116
122
  nrAdded = 0
117
- if @options[:extended]
123
+ if @options[:extended] || @options[:artikelstamm]
118
124
  Oddb2xml.log("prepare_articles extended prepare_local_index having already #{@articles.size} articles")
119
125
  nrItems = 0
120
- @infos_zur_rose.each{
121
- |ean13, info|
122
- nrItems += 1
123
- pharmacode = info[:pharmacode]
124
- if @pharmacode[pharmacode]
125
- @pharmacode[pharmacode][:price] = info[:price]
126
- @pharmacode[pharmacode][:pub_price] = info[:pub_price]
127
- next
128
- end
129
- obj = {}
130
- found = false
131
- # existing = @refdata.values.find{ |x| x[:ean].eql? ean13 }
132
- existing = @refdata[ean13]
133
- if existing
134
- found = true
135
- existing[:price] = info[:price]
136
- existing[:pub_price] = info[:pub_price]
137
- else
138
- entry = {
139
- :desc => info[:description],
140
- :desc_de => info[:description],
141
- :status => info[:status] == '3' ? 'I' : 'A', # from ZurRose, we got 1,2 or 3 means aktive, aka available in trade
142
- :atc_code => '',
143
- :ean => ean13,
144
- :pharmacode => pharmacode,
145
- :price => info[:price],
146
- :pub_price => info[:pub_price],
147
- :type => info[:type],
148
- }
149
- if pharmacode
150
- @refdata[pharmacode] = entry
126
+ @infos_zur_rose.each do |ean13, info|
127
+ nrItems += 1
128
+ pharmacode = info[:pharmacode]
129
+ if @pharmacode[pharmacode]
130
+ @pharmacode[pharmacode][:price] = info[:price]
131
+ @pharmacode[pharmacode][:pub_price] = info[:pub_price]
132
+ next
133
+ end
134
+ obj = {}
135
+ found = false
136
+ existing = @refdata[ean13]
137
+ if existing
138
+ found = true
139
+ existing[:price] = info[:price]
140
+ existing[:pub_price] = info[:pub_price]
151
141
  else
152
- @refdata[ean13] = entry
142
+ entry = {
143
+ :desc => info[:description],
144
+ :desc_de => info[:description],
145
+ :status => info[:status] == '3' ? 'I' : 'A', # from ZurRose, we got 1,2 or 3 means aktive, aka available in trade
146
+ :atc_code => '',
147
+ :ean13 => ean13,
148
+ :pharmacode => pharmacode,
149
+ :price => info[:price],
150
+ :pub_price => info[:pub_price],
151
+ :type => info[:type],
152
+ }
153
+ if pharmacode
154
+ @refdata[pharmacode] = entry
155
+ else
156
+ @refdata[ean13] = entry
157
+ end
158
+ obj = entry
159
+ end
160
+ if not found and obj.size > 0
161
+ @articles << obj unless @options[:artikelstamm]
162
+ nrAdded += 1
153
163
  end
154
- obj = entry
155
- end
156
- if not found and obj.size > 0
157
- @articles << obj
158
- nrAdded += 1
159
164
  end
160
- }
161
165
  end
162
166
  end
163
167
  Oddb2xml.log("prepare_articles done. Added #{nrAdded} prices. Total #{@articles.size}")
@@ -175,7 +179,7 @@ module Oddb2xml
175
179
  @substances.uniq!
176
180
  @substances.sort!
177
181
  Oddb2xml.log("prepare_substances done. Total #{@substances.size} from #{@items.size} items")
178
- exit 2 if @options[:extended] and @substances.size == 0
182
+ exit 2 if (@options[:extended] || @options[:artikelstamm]) and @substances.size == 0
179
183
  end
180
184
  end
181
185
  def prepare_limitations
@@ -220,12 +224,11 @@ module Oddb2xml
220
224
  @refdata.each_pair do |ean13, item|
221
225
  next if item and item.is_a?(Hash) and item[:atc_code] and /^Q/i.match(item[:atc_code])
222
226
  next if item[:prodno] and @products[item[:prodno]]
223
- next unless ean13.to_s.length == 13
224
227
  obj = {
225
- :seq => @items[ean13] ? @items[ean13] : @items[item[:ean]],
228
+ :seq => @items[ean13] ? @items[ean13] : @items[item[:ean13]],
226
229
  :pac => nil,
227
230
  :no8 => nil,
228
- :ean => item[:ean],
231
+ :ean13 => item[:ean13],
229
232
  :atc => item[:atc_code],
230
233
  :ith => '',
231
234
  :siz => '',
@@ -233,14 +236,17 @@ module Oddb2xml
233
236
  :sub => '',
234
237
  :comp => '',
235
238
  }
236
- if obj[:ean] # via EAN-Code
237
- obj[:no8] = obj[:ean].to_s[4..11]
239
+ # obj[:pexf_refdata] = item[:price]
240
+ # obj[:ppub_refdata] = item[:pub_price=]
241
+ # obj[:pharmacode=] = item[:pharmacode=]
242
+ if obj[:ean13] # via EAN-Code
243
+ obj[:no8] = obj[:ean13].to_s[4..11]
238
244
  end
239
- if obj[:no8] and ppac = @packs[obj[:no8].intern] and # Packungen.xls
245
+ if obj[:no8] && (ppac = @packs[obj[:no8]]) && # Packungen.xls
240
246
  !ppac[:is_tier]
241
247
  # If RefData does not have EAN
242
- if obj[:ean].nil?
243
- obj[:ean] = ppac[:ean].to_i
248
+ if obj[:ean13].nil?
249
+ obj[:ean13] = ppac[:ean13]
244
250
  end
245
251
  # If RefData dose not have ATC-Code
246
252
  if obj[:atc].nil? or obj[:atc].empty?
@@ -252,7 +258,10 @@ module Oddb2xml
252
258
  obj[:sub] = ppac[:substance_swissmedic]
253
259
  obj[:comp] = ppac[:composition_swissmedic]
254
260
  end
255
- if obj[:ean].to_s[0..3] == '7680'
261
+ obj[:price] = item[:price]
262
+ obj[:pub_price] = item[:pub_price]
263
+
264
+ if obj[:ean13].to_s[0..3] == '7680'
256
265
  if item[:prodno]
257
266
  @products[item[:prodno]] = obj
258
267
  else
@@ -272,7 +281,7 @@ module Oddb2xml
272
281
  XML_OPTIONS
273
282
  ) {
274
283
  Oddb2xml.log "build_substance #{@substances.size} substances"
275
- exit 2 if @options[:extended] and @substances.size == 0
284
+ exit 2 if (@options[:extended] || @options[:artikelstamm]) and @substances.size == 0
276
285
  nbr_records = 0
277
286
  @substances.each_with_index do |sub_name, i|
278
287
  xml.SB('DT' => '') do
@@ -429,34 +438,55 @@ module Oddb2xml
429
438
  end
430
439
  Oddb2xml.add_hash(_builder.to_xml)
431
440
  end
432
- def add_missing_products_from_swissmedic
441
+ def add_missing_products_from_swissmedic(add_to_products = false)
433
442
  Oddb2xml.log "build_product add_missing_products_from_swissmedic. Starting with #{@products.size} products and #{@packs.size} @packs"
434
443
  ean13_to_product = {}
435
444
  @products.each{
436
445
  |ean13, obj|
437
- ean13_to_product[ean13] = obj
446
+ ean13_to_product[ean13] = obj
447
+ obj[:pharmacode] ||= @refdata[ean13][:pharmacode]
438
448
  }
439
449
  ausgabe = File.open(File.join(WorkDir, 'missing_in_refdata.txt'), 'w+')
440
450
  size_old = ean13_to_product.size
441
451
  @missing = []
442
452
  Oddb2xml.log "build_product add_missing_products_from_swissmedic. Imported #{size_old} ean13_to_product from @products. Checking #{@packs.size} @packs"
443
- @packs.each_with_index {
444
- |de_idx, i|
445
- ean = de_idx[1][:ean].to_i
446
- next if @refdata[ean]
447
- list_code = de_idx[1][:list_code]
448
- next if list_code and /Tierarzneimittel/.match(list_code)
449
- ean13_to_product[ean] = de_idx[1]
450
- @missing << de_idx[1]
451
- ausgabe.puts "#{ean},#{de_idx[1][:sequence_name]}"
452
- }
453
+ @packs.each_with_index do |de_idx, index|
454
+ ean = de_idx[1][:ean13]
455
+ next if @refdata[ean]
456
+ list_code = de_idx[1][:list_code]
457
+ next if list_code and /Tierarzneimittel/.match(list_code)
458
+ if add_to_products
459
+ @products[ean] = { :seq=>
460
+ {:name_de => de_idx.last[:sequence_name],
461
+ :desc_de => '',
462
+ :name_fr => '',
463
+ :desc_fr => '',
464
+ :atc_code => de_idx.last[:atc_code],
465
+ },
466
+ :pac=>nil,
467
+ :sequence_name => de_idx.last[:sequence_name],
468
+ :no8=> de_idx.last[:prodno],
469
+ :ean13=> ean,
470
+ :atc=> de_idx.last[:atc_code],
471
+ :ith=> de_idx.last[:ith_swissmedic],
472
+ :siz=> de_idx.last[:package_size],
473
+ :eht=> de_idx.last[:einheit_swissmedic],
474
+ :sub=> de_idx.last[:substance_swissmedic],
475
+ :comp=> de_idx.last[:composition_swissmedic],
476
+ :drug_index => de_idx.last[:drug_index],
477
+ }
478
+ end
479
+ ean13_to_product[ean] = de_idx[1]
480
+ @missing << de_idx[1]
481
+ ausgabe.puts "#{ean},#{de_idx[1][:sequence_name]}"
482
+ end
453
483
  corrected_size = ean13_to_product.size
454
484
  Oddb2xml.log "build_product add_missing_products_from_swissmedic. Added #{(corrected_size - size_old)} corrected_size #{corrected_size} size_old #{size_old} ean13_to_product."
455
485
  end
456
486
 
457
487
  def build_product
458
488
  def check_name(obj, lang = :de)
459
- ean = obj[:ean].to_i
489
+ ean = obj[:ean13]
460
490
  refdata = @refdata[ean]
461
491
  if lang == :de
462
492
  name = (refdata && refdata[:desc_de]) ? refdata[:desc_de] : obj[:sequence_name]
@@ -482,7 +512,7 @@ module Oddb2xml
482
512
  xml.PRODUCT(XML_OPTIONS) {
483
513
  list = []
484
514
  @missing.each do |obj|
485
- ean = obj[:ean].to_i
515
+ ean = obj[:ean13]
486
516
  next unless check_name(obj, :de)
487
517
  next unless check_name(obj, :fr)
488
518
  next if /^Q/i.match(obj[:atc])
@@ -496,7 +526,6 @@ module Oddb2xml
496
526
  xml.PRODNO obj[:prodno] if obj[:prodno]
497
527
  xml.DSCRD check_name(obj, :de)
498
528
  xml.DSCRF check_name(obj, :fr)
499
- xml.ATC obj[:atc_code] if obj[:atc_code] and !obj[:atc_code].empty?
500
529
  xml.IT obj[:ith_swissmedic] if obj[:ith_swissmedic]
501
530
  xml.CPT
502
531
  xml.PackGrSwissmedic obj[:package_size] if obj[:package_size]
@@ -505,17 +534,16 @@ module Oddb2xml
505
534
  xml.CompositionSwissmedic obj[:composition_swissmedic] if obj[:composition_swissmedic]
506
535
  }
507
536
  end
508
- # @products.sort! { |a,b| a[:ean] <=> b[:ean] }
509
- @products.each do |ean13, obj|
537
+ @products.sort.to_h.each do |ean13, obj|
510
538
  next if /^Q/i.match(obj[:atc])
511
539
  seq = obj[:seq]
512
- ean = obj[:ean]
540
+ ean = obj[:ean13]
513
541
  next unless check_name(obj, :de)
514
542
  next unless check_name(obj, :fr)
515
543
  xml.PRD('DT' => obj[:last_change]) do
516
544
  nbr_products += 1
517
545
  xml.GTIN ean
518
- ppac = ((_ppac = @packs[ean.to_s[4..11].intern] and !_ppac[:is_tier]) ? _ppac : {})
546
+ ppac = ((_ppac = @packs[ean.to_s[4..11]] and !_ppac[:is_tier]) ? _ppac : {})
519
547
  unless ppac
520
548
  ppac = @packs.find{|pac| pac.ean == ean }.first
521
549
  end
@@ -528,9 +556,9 @@ module Oddb2xml
528
556
  #xml.ADNAMF
529
557
  #xml.SIZE
530
558
  if seq
531
- xml.ADINFD seq[:comment_de] unless seq[:comment_de].empty?
532
- xml.ADINFF seq[:comment_fr] unless seq[:comment_fr].empty?
533
- xml.GENCD seq[:org_gen_code] unless seq[:org_gen_code].empty?
559
+ xml.ADINFD seq[:comment_de] unless seq[:comment_de] && seq[:comment_de].empty?
560
+ xml.ADINFF seq[:comment_fr] unless seq[:comment_fr] && seq[:comment_fr].empty?
561
+ xml.GENCD seq[:org_gen_code] unless seq[:org_gen_code] && seq[:org_gen_code].empty?
534
562
  end
535
563
  #xml.GENGRP
536
564
  xml.ATC obj[:atc] unless obj[:atc].empty?
@@ -637,6 +665,61 @@ module Oddb2xml
637
665
  Oddb2xml.add_hash(_builder.to_xml)
638
666
  end
639
667
 
668
+ def prepare_calc_items(suppress_composition_parsing: false)
669
+ @calc_items = {}
670
+ packungen_xlsx = File.join(Oddb2xml::WorkDir, "swissmedic_package.xlsx")
671
+ idx = 0
672
+ return unless File.exists?(packungen_xlsx)
673
+ workbook = RubyXL::Parser.parse(packungen_xlsx)
674
+ row_nr = 0
675
+ workbook.worksheets[0].each do |row|
676
+ row_nr += 1
677
+ next unless row and row.cells[0] and row.cells[0].value and row.cells[0].value.to_i > 0
678
+ iksnr = "%05i" % row.cells[0].value.to_i
679
+ seqnr = "%02d" % row.cells[1].value.to_i
680
+ if row_nr % 250 == 0
681
+ puts "#{Time.now}: At row #{row_nr} iksnr #{iksnr}";
682
+ $stdout.flush
683
+ end
684
+ ith = COLUMNS_JULY_2015.keys.index(:index_therapeuticus)
685
+ seq_name = COLUMNS_JULY_2015.keys.index(:name_base)
686
+ i_3 = COLUMNS_JULY_2015.keys.index(:ikscd)
687
+ p_1_2 = COLUMNS_JULY_2015.keys.index(:seqnr)
688
+ cat = COLUMNS_JULY_2015.keys.index(:ikscat)
689
+ siz = COLUMNS_JULY_2015.keys.index(:size)
690
+ atc = COLUMNS_JULY_2015.keys.index(:atc_class)
691
+ list_code = COLUMNS_JULY_2015.keys.index(:production_science)
692
+ unit = COLUMNS_JULY_2015.keys.index(:unit)
693
+ sub = COLUMNS_JULY_2015.keys.index(:substances)
694
+ comp = COLUMNS_JULY_2015.keys.index(:composition)
695
+
696
+ no8 = iksnr + sprintf('%03d',row.cells[i_3].value.to_i)
697
+ name = row.cells[seq_name] ? row.cells[seq_name].value : nil
698
+ atc_code = row.cells[atc] ? row.cells[atc].value : nil
699
+ list_code = row.cells[list_code] ? row.cells[list_code].value : nil
700
+ package_size = row.cells[siz] ? row.cells[siz].value : nil
701
+ unit = row.cells[unit] ? row.cells[unit].value : nil
702
+ active_substance = row.cells[sub] ? row.cells[sub].value : nil
703
+ composition = row.cells[comp] ? row.cells[comp].value : nil
704
+
705
+ # skip veterinary product
706
+ next if atc_code and /^Q/i.match(atc_code)
707
+ next if list_code and /Tierarzneimittel/.match(list_code)
708
+ info = nil
709
+ begin
710
+ if suppress_composition_parsing
711
+ info = Calc.new(name, package_size, unit)
712
+ else
713
+ info = Calc.new(name, package_size, unit, active_substance, composition)
714
+ end
715
+ rescue
716
+ puts "#{Time.now}: #{row_nr} iksnr #{iksnr} rescue from Calc.new"
717
+ end
718
+ ean12 = '7680' + no8
719
+ ean13 = (ean12 + Oddb2xml.calc_checksum(ean12))
720
+ @calc_items[ean13] = info
721
+ end
722
+ end
640
723
  def build_calc
641
724
  def emit_substance(xml, substance, emit_active=false)
642
725
  xml.MORE_INFO substance.more_info if substance.more_info
@@ -656,69 +739,22 @@ module Oddb2xml
656
739
  }
657
740
  end
658
741
  if substance.salts and substance.salts.size > 0
659
- xml.SALTS {
660
- substance.salts.each { |salt|
661
- xml.SALT {
742
+ xml.SALTS do
743
+ substance.salts.each do |salt|
744
+ xml.SALT do
662
745
  emit_substance(xml, salt)
663
- }
664
- }
665
- }
666
-
746
+ end
747
+ end
748
+ end
667
749
  end
668
750
  end
669
- packungen_xlsx = File.join(Oddb2xml::WorkDir, "swissmedic_package.xlsx")
670
- idx = 0
671
- return unless File.exists?(packungen_xlsx)
672
- workbook = RubyXL::Parser.parse(packungen_xlsx)
673
- items = {}
674
- row_nr = 0
751
+ prepare_calc_items
675
752
  _builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
676
753
  xml.doc.tag_suffix = @tag_suffix
677
754
  datetime = Time.new.strftime('%FT%T%z')
678
- xml.ARTICLES(XML_OPTIONS) {
679
- workbook.worksheets[0].each do |row|
680
- row_nr += 1
681
- next unless row and row.cells[0] and row.cells[0].value and row.cells[0].value.to_i > 0
682
- iksnr = "%05i" % row.cells[0].value.to_i
683
- seqnr = "%02d" % row.cells[1].value.to_i
684
- if row_nr % 250 == 0
685
- puts "#{Time.now}: At row #{row_nr} iksnr #{iksnr}";
686
- $stdout.flush
687
- end
688
- ith = COLUMNS_JULY_2015.keys.index(:index_therapeuticus)
689
- seq_name = COLUMNS_JULY_2015.keys.index(:name_base)
690
- i_3 = COLUMNS_JULY_2015.keys.index(:ikscd)
691
- p_1_2 = COLUMNS_JULY_2015.keys.index(:seqnr)
692
- cat = COLUMNS_JULY_2015.keys.index(:ikscat)
693
- siz = COLUMNS_JULY_2015.keys.index(:size)
694
- atc = COLUMNS_JULY_2015.keys.index(:atc_class)
695
- list_code = COLUMNS_JULY_2015.keys.index(:production_science)
696
- unit = COLUMNS_JULY_2015.keys.index(:unit)
697
- sub = COLUMNS_JULY_2015.keys.index(:substances)
698
- comp = COLUMNS_JULY_2015.keys.index(:composition)
699
-
700
- no8 = iksnr + sprintf('%03d',row.cells[i_3].value.to_i)
701
- name = row.cells[seq_name] ? row.cells[seq_name].value : nil
702
- atc_code = row.cells[atc] ? row.cells[atc].value : nil
703
- list_code = row.cells[list_code] ? row.cells[list_code].value : nil
704
- package_size = row.cells[siz] ? row.cells[siz].value : nil
705
- unit = row.cells[unit] ? row.cells[unit].value : nil
706
- active_substance = row.cells[sub] ? row.cells[sub].value : nil
707
- composition = row.cells[comp] ? row.cells[comp].value : nil
708
-
709
- # skip veterinary product
710
- next if atc_code and /^Q/i.match(atc_code)
711
- next if list_code and /Tierarzneimittel/.match(list_code)
712
- begin
713
- info = Calc.new(name, package_size, unit, active_substance, composition)
714
- rescue
715
- puts "#{Time.now}: #{row_nr} iksnr #{iksnr} rescue from Calc.new"
716
- info = nil
717
- end
718
- ean12 = '7680' + no8
719
- ean13 = (ean12 + Oddb2xml.calc_checksum(ean12))
720
- items[ean13] = info
721
- xml.ARTICLE {
755
+ xml.ARTICLES(XML_OPTIONS) do
756
+ @calc_items.each do |ean13, info|
757
+ xml.ARTICLE do
722
758
  xml.GTIN ean13
723
759
  xml.NAME info.column_c
724
760
  xml.PKG_SIZE info.pkg_size
@@ -732,29 +768,30 @@ module Oddb2xml
732
768
  xml.GALENIC_FORM info.galenic_form.description
733
769
  xml.GALENIC_GROUP info.galenic_group ? info.galenic_group.description : "Unknown"
734
770
  end
735
- xml.COMPOSITIONS {
736
- info.compositions.each { |composition|
737
- xml.COMPOSITION {
771
+ xml.COMPOSITIONS do
772
+ info.compositions.each do |composition|
773
+ xml.COMPOSITION do
738
774
  xml.EXCIPIENS { emit_substance(xml, composition.excipiens) } if composition.excipiens
739
775
  xml.LABEL composition.label if composition.label
740
776
  xml.LABEL_DESCRIPTION composition.label_description if composition.label_description
741
777
  xml.CORRESP composition.corresp if composition.corresp
742
778
  if composition.substances and composition.substances.size > 0
743
- xml.SUBSTANCES {
744
- composition.substances.each { |substance| xml.SUBSTANCE { emit_substance(xml, substance, true) }}
745
- }
779
+ xml.SUBSTANCES do
780
+ composition.substances.each { |substance| xml.SUBSTANCE { emit_substance(xml, substance, true) }}
781
+ end
746
782
  end
747
- }
748
- }
749
- }
750
- } if info and info.compositions
783
+ end
784
+ end
785
+ end
786
+ end if info and info.compositions
751
787
  end
752
- }
788
+ end
753
789
  end
790
+
754
791
  csv_name = File.join(WorkDir, 'oddb_calc.csv')
755
792
  CSV.open(csv_name, "w+", :col_sep => ';') do |csv|
756
- csv << ['gtin'] + items.values.first.headers
757
- items.each do |key, value|
793
+ csv << ['gtin'] + @calc_items.values.first.headers
794
+ @calc_items.each do |key, value|
758
795
  if value and value.to_array
759
796
  csv << [key] + value.to_array
760
797
  else
@@ -764,67 +801,67 @@ module Oddb2xml
764
801
  end
765
802
  Oddb2xml.add_hash(_builder.to_xml)
766
803
  end
804
+
767
805
  def build_article
768
806
  prepare_limitations
769
807
  prepare_articles
770
808
  idx = 0
771
809
  nbr_records = 0
772
- @preparations_only = []
773
810
  Oddb2xml.log "build_article #{idx} of #{@articles.size} articles"
774
811
  _builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
775
812
  xml.doc.tag_suffix = @tag_suffix
776
813
  datetime = Time.new.strftime('%FT%T%z')
777
- eans_from_refdata = @articles.collect{|refdata| refdata[:ean] }
814
+ eans_from_refdata = @articles.collect{|refdata| refdata[:ean13] }
778
815
  eans_from_preparations = @items.keys
779
816
  missing_eans = []
780
817
  eans_from_preparations.each do |ean|
781
818
  next if ean.to_i == 0
782
- unless eans_from_refdata.index(ean)
783
- @preparations_only << ean
784
- missing_eans << ean
819
+ unless @articles.collect {|x| x[:ean13] }.index(ean)
785
820
  item = @items[ean].clone
786
- next if defined?(RSpec) && !item[:pharmacode]
787
- item[:ean] = ean
821
+ item[:pharmacode] ||= '123456' if defined?(RSpec)
822
+ item[:ean13] = ean
788
823
  item[:_type] = :preparations_xml
789
824
  item[:desc_de] = item[:name_de] + ' ' + item[:desc_de]
790
825
  item[:desc_fr] = item[:name_fr] + ' ' + item[:desc_fr]
791
826
  @articles << item
792
827
  end
828
+ unless eans_from_refdata.index(ean)
829
+ missing_eans << ean
830
+ end
793
831
  end
794
832
  xml.ARTICLE(XML_OPTIONS) {
795
- @articles.sort! { |a,b| a[:ean] <=> b[:ean] }
833
+ @articles.sort! { |a,b| a[:ean13] <=> b[:ean13] }
796
834
  @articles.each do |obj|
797
835
  idx += 1
798
836
  Oddb2xml.log "build_article #{idx} of #{@articles.size} articles" if idx % 500 == 0
799
- item = @items[obj[:ean]]
800
- pac,no8 = nil,obj[:ean].to_s[4..11] # BAG-XML(SL/LS)
801
- pack_info = nil
802
- pack_info = @packs[no8.intern] if no8 # info from Packungen.xlsx from swissmedic_info
837
+ item = @items[obj[:ean13]]
838
+ pac,no8 = nil,obj[:ean13].to_s[4..11] # BAG-XML(SL/LS)
839
+ pack_info = nil
840
+ pack_info = @packs[no8] if no8 # info from Packungen.xlsx from swissmedic_info
803
841
  ppac = nil # Packungen
804
- ean = obj[:ean]
842
+ ean = obj[:ean13]
805
843
  next if pack_info and /Tierarzneimittel/.match(pack_info[:list_code])
806
844
  next if obj[:desc_de] and /ad us vet/i.match(obj[:desc_de])
807
- next if obj[:desc_de] and obj[:desc_de].length < 3
808
- next if obj[:desc_fr] and obj[:desc_fr].length < 3
809
845
  pharma_code = obj[:pharmacode]
810
- ean = 0 if sprintf('%013d', ean).match(/^000000/)
846
+ # ean = 0 if sprintf('%013d', ean).match(/^000000/)
811
847
  if obj[:seq]
812
848
  pac = obj[:seq][:packages][obj[:pharmacode]]
813
849
  pac = obj[:seq][:packages][ean] unless pac
814
- elsif ean > 0
850
+ else
815
851
  pac = @items[ean][:packages][ean] if @items and ean and @items[ean] and @items[ean][:packages]
816
852
  end
817
853
  if no8
818
854
  ppac = ((_ppac = pack_info and !_ppac[:is_tier]) ? _ppac : nil)
819
855
  end
820
- info_zur_rose = nil
856
+ zur_rose = nil
821
857
  if !@infos_zur_rose.empty? && ean && @infos_zur_rose[ean]
822
- info_zur_rose = @infos_zur_rose[ean] # zurrose
858
+ zur_rose = @infos_zur_rose[ean] # zurrose
823
859
  end
824
860
  xml.ART('DT' => obj[:last_change] ? obj[:last_change] : '') do
825
861
  nbr_records += 1
826
862
  xml.REF_DATA (obj[:refdata] || @migel[pharma_code]) ? '1' : '0'
827
- xml.PHAR sprintf('%07d', obj[:pharmacode]) if obj[:pharmacode]
863
+ # xml.PHAR sprintf('%07d', obj[:pharmacode]) if obj[:pharmacode]
864
+ xml.PHAR obj[:pharmacode] if obj[:pharmacode]
828
865
  #xml.GRPCD
829
866
  #xml.CDS01
830
867
  #xml.CDS02
@@ -845,12 +882,10 @@ module Oddb2xml
845
882
  #xml.HOSPCD
846
883
  #xml.CLINCD
847
884
  #xml.ARTTYP
848
- if info_zur_rose
849
- xml.VAT info_zur_rose[:vat]
885
+ if zur_rose
886
+ xml.VAT zur_rose[:vat]
850
887
  end
851
-
852
- nincd = detect_nincd(obj)
853
- (nincd and nincd == 13) ? xml.SALECD('A') : xml.SALECD( (info_zur_rose && info_zur_rose[:cmut] != '3') ? 'A' : 'I') # XML_OPTIONS
888
+ emit_salecd(xml, ean, obj)
854
889
  if pac and pac[:limitation_points]
855
890
  #xml.INSLIM
856
891
  xml.LIMPTS pac[:limitation_points] unless pac[:limitation_points].empty?
@@ -865,6 +900,9 @@ module Oddb2xml
865
900
  xml.BG(flag ? 'Y' : 'N')
866
901
  end
867
902
  #xml.EXP
903
+ if item and item[:substances] and substance = item[:substances].first
904
+ xml.QTY "#{substance[:quantity]}#{substance[:unit] ? ' ' + substance[:unit] : ''}"
905
+ end if false # TODO: get qty/unit from refdata name
868
906
  xml.DSCRD obj[:desc_de] if obj[:desc_de] and not obj[:desc_de].empty?
869
907
  xml.DSCRF obj[:desc_fr] if obj[:desc_fr] and not obj[:desc_fr].empty?
870
908
  xml.DSCRF obj[:desc_de] if !obj[:desc_fr] or obj[:desc_fr].empty?
@@ -922,7 +960,7 @@ module Oddb2xml
922
960
  }
923
961
  xml.ARTBAR {
924
962
  xml.CDTYP 'E13'
925
- xml.BC /^9999|^0000|^0$/.match(ean.to_s) ? 0 : sprintf('%013d', ean)
963
+ xml.BC ean # /^9999|^0000|^0$/.match(ean.to_s) ? 0 : sprintf('%013d', ean)
926
964
  xml.BCSTAT 'A' # P is alternative
927
965
  #xml.PHAR2
928
966
  } if ean
@@ -941,21 +979,22 @@ module Oddb2xml
941
979
  }
942
980
  end
943
981
  end
944
- if info_zur_rose
945
- price = info_zur_rose[:price]
982
+ if zur_rose
983
+ price = zur_rose[:price]
946
984
  xml.ARTPRI {
947
985
  xml.PTYP "ZURROSE"
948
986
  xml.PRICE price
949
987
  }
950
988
  xml.ARTPRI {
951
989
  xml.PTYP "ZURROSEPUB"
952
- xml.PRICE info_zur_rose[:pub_price]
990
+ xml.PRICE zur_rose[:pub_price]
953
991
  }
954
992
  xml.ARTPRI {
955
993
  xml.PTYP "RESELLERPUB"
956
994
  xml.PRICE (price.to_f*(1 + (@options[:percent].to_f/100))).round_by(0.05).round(2)
957
995
  } if @options[:percent] != nil
958
996
  end
997
+ nincd = detect_nincd(obj)
959
998
  if nincd
960
999
  xml.ARTINS {
961
1000
  xml.NINCD nincd
@@ -971,7 +1010,7 @@ module Oddb2xml
971
1010
  }
972
1011
  }
973
1012
  end
974
- Oddb2xml.log "build_article. Done #{idx} of #{@articles.size} articles " + ( @preparations_only.size > 0 ? ('. Only in preparations.xml '+ @preparations_only.join(' ')) : '')
1013
+ Oddb2xml.log "build_article. Done #{idx} of #{@articles.size} articles"
975
1014
  Oddb2xml.add_hash(_builder.to_xml)
976
1015
  end
977
1016
  def build_fi
@@ -1121,19 +1160,18 @@ module Oddb2xml
1121
1160
  Oddb2xml.add_hash(_builder.to_xml)
1122
1161
  end
1123
1162
  def detect_nincd(de_idx)
1124
- if @lppvs[de_idx[:ean].to_s] # LPPV
1163
+ if @lppvs[de_idx[:ean13].to_s] # LPPV
1125
1164
  20
1126
1165
  elsif @items[de_idx[:pharmacode]] # BAG-XML (SL/LS)
1127
1166
  10
1128
1167
  elsif (de_idx[:migel] or # MiGel (xls)
1129
- de_idx[:_type] == :preparations_xml or
1130
1168
  de_idx[:_type] == :nonpharma) # MiGel (swissindex)
1131
1169
  13
1132
1170
  else
1133
1171
  # fallback via EAN
1134
- bag_entry_via_ean = @items.values.select do |i|
1135
- next unless i[:packages]
1136
- i[:packages].values.select {|_pac| _pac[:ean].to_s == de_idx[:ean].to_s }.length != 0
1172
+ bag_entry_via_ean = @items.values.select do |item|
1173
+ next unless item[:packages]
1174
+ item[:packages].values.select {|_pac| _pac[:ean13].to_s.eql?(de_idx[:ean13].to_s) }.length != 0
1137
1175
  end
1138
1176
  if bag_entry_via_ean.length > 0
1139
1177
  10
@@ -1191,12 +1229,12 @@ module Oddb2xml
1191
1229
  prepare_articles
1192
1230
  rows = []
1193
1231
  @articles.each do |obj|
1194
- ean = obj[:ean]
1232
+ ean = obj[:ean13]
1195
1233
  next if ((ean.to_s.length != 13) and !ean14)
1196
1234
  next if obj[:type] == :nonpharma
1197
1235
  row = ''
1198
1236
  pack_info = nil
1199
- if (x = @packs.find{|k,v| v[:ean].to_i == ean})
1237
+ if (x = @packs.find{|k,v| v[:ean13].eql?(ean)})
1200
1238
  pack_info = x[1]
1201
1239
  end
1202
1240
  # Oddb2tdat.parse
@@ -1209,16 +1247,16 @@ module Oddb2xml
1209
1247
  end
1210
1248
  # :swissmedic_numbers
1211
1249
  if pac
1212
- no8 = pac[:swissmedic_number8].intern
1250
+ no8 = pac[:swissmedic_number8]
1213
1251
  end
1214
1252
  if pac and pac[:prices] == nil and no8
1215
1253
  ppac = ((ppac = pack_info and ppac[:is_tier]) ? ppac : nil)
1216
1254
  pac = ppac if ppac
1217
1255
  end
1218
1256
  row << "%#{DAT_LEN[:RECA]}s" % '11'
1219
- info_zur_rose = @infos_zur_rose[ean] # zurrose
1220
- if info_zur_rose && info_zur_rose[:cmut]
1221
- row << info_zur_rose[:cmut]
1257
+ zur_rose = @infos_zur_rose[ean] # zurrose
1258
+ if zur_rose && zur_rose[:cmut]
1259
+ row << zur_rose[:cmut]
1222
1260
  else
1223
1261
  row << '1'
1224
1262
  end
@@ -1285,7 +1323,7 @@ module Oddb2xml
1285
1323
  rows = []
1286
1324
  @articles.each do |obj|
1287
1325
  row = ''
1288
- next if ((obj[:ean].to_s.length != 13) and !ean14)
1326
+ next if ((obj[:ean13].to_s.length != 13) and !ean14)
1289
1327
  # Oddb2tdat.parse_migel
1290
1328
  row << "%#{DAT_LEN[:RECA]}s" % '11'
1291
1329
  row << "%#{DAT_LEN[:CMUT]}s" % if (phar = obj[:pharmacode] and phar.size > 3)
@@ -1306,11 +1344,324 @@ module Oddb2xml
1306
1344
  row << "%#{DAT_LEN[:CBGG]}s" % '0'
1307
1345
  row << "%#{DAT_LEN[:CIKS]}s" % ' ' # no category
1308
1346
  row << "%0#{DAT_LEN[:ITHE]}d" % 0
1309
- row << obj[:ean].to_s.rjust(DAT_LEN[:CEAN], '0')
1347
+ row << obj[:ean13].to_s.rjust(DAT_LEN[:CEAN], '0')
1310
1348
  row << "%#{DAT_LEN[:CMWS]}s" % '1' # nonpharma
1311
1349
  rows << row
1312
1350
  end
1313
1351
  rows.join("\n")
1314
1352
  end
1353
+ def emit_salecd(xml, ean13, obj)
1354
+ zur_rose = nil
1355
+ if !@infos_zur_rose.empty? && ean13 && @infos_zur_rose[ean13]
1356
+ zur_rose = @infos_zur_rose[ean13] # zurrose
1357
+ end
1358
+ nincd = detect_nincd(obj)
1359
+ status = (nincd && nincd == 13) ? 'A' : (zur_rose && zur_rose[:cmut] != '3') ? 'A' : 'I'
1360
+ xml.SALECD(status) { xml.comment( "expiry_date #{obj[:expiry_date]}") if obj[:expiry_date] }
1361
+ end
1362
+
1363
+ def build_artikelstamm
1364
+ @@emitted_v5_gtins = []
1365
+ @csv_file = CSV.open(File.join(WorkDir, "Elexis_Artikelstamm_v5.csv"), "w+")
1366
+ @csv_file << ['gtin', 'price', 'galenic_form', 'pkg_size', 'pexf', 'ppub', 'iksnr', 'atc_code', 'active_substance', 'original', 'it-code', 'sl-liste']
1367
+ @csv_file.sync = true
1368
+ variant = "build_artikelstamm"
1369
+ # @infos_zur_rose.delete_if { |key, val| val[:cmut].eql?('3') } # collect only active zur rose item
1370
+ # No. Marco did not filter it, eg. 8804121 in rtikelstamm_oddb2xml_051217_v5.xm
1371
+ def check_name(obj, lang = :de)
1372
+ ean = obj[:ean13]
1373
+ refdata = @refdata[ean]
1374
+ if lang == :de
1375
+ name = (refdata && refdata[:desc_de]) ? refdata[:desc_de] : obj[:sequence_name]
1376
+ elsif lang == :fr
1377
+ name = (refdata && refdata[:desc_fr]) ? refdata[:desc_fr] : obj[:sequence_name]
1378
+ else
1379
+ return '--missing--'
1380
+ end
1381
+ return '--missing--' if !name || name.empty? || name.length < 3
1382
+ name
1383
+ end
1384
+ def override(xml, id, field, default_value)
1385
+ has_overrides = /\d{13}/.match(id.to_s) ? @@article_overrides[id.to_i] : @@product_overrides[id.to_i]
1386
+ unless (has_overrides && has_overrides[field.to_s])
1387
+ cmd = "xml.#{field} \"#{default_value.to_s.gsub('"','')}\""
1388
+ else
1389
+ new_value = has_overrides[field.to_s]
1390
+ if new_value.to_s.eql?(default_value.to_s)
1391
+ xml.comment('obsolete override')
1392
+ cmd = "xml.#{field} \"#{new_value}\""
1393
+ else
1394
+ xml.comment("override #{default_value.to_s} with")
1395
+ cmd ="xml.#{field} \"#{new_value}\""
1396
+ end
1397
+ end
1398
+ eval cmd if default_value
1399
+ end
1400
+ def emit_items(xml)
1401
+ nr_items = 0
1402
+ gtins_to_article = {}
1403
+ @articles.each {|article| gtins_to_article[article[:ean13]] = article }
1404
+ gtins = gtins_to_article.keys + @infos_zur_rose.keys + @packs.values.collect{|x| x[:ean13]}
1405
+ gtins = (gtins-@@gtin2ignore)
1406
+ gtins.sort!.uniq!
1407
+ @nr_items = gtins.size
1408
+ gtins.each do |ean13|
1409
+ pac,no8 = nil,ean13.to_s[4..11] # BAG-XML(SL/LS)
1410
+ next if ean13 == 0
1411
+ obj = gtins_to_article[ean13] || @infos_zur_rose[ean13]
1412
+ if obj
1413
+ obj = @packs[no8].merge(obj) if @packs[no8]
1414
+ else
1415
+ obj = @packs[no8] # obj not yet in refdata. Use data from swissmedic_package.xlsx
1416
+ end
1417
+ nr_items += 1
1418
+ Oddb2xml.log "build_article #{nr_items} of #{gtins.size} articles" if nr_items % 5000 == 0
1419
+ item = @items[ean13]
1420
+ pack_info = nil
1421
+ pack_info = @packs[no8] if no8 && /#{ean13}/.match(@packs[no8].to_s) # info from Packungen.xlsx from swissmedic_info
1422
+ next if pack_info && /Tierarzneimittel/.match(pack_info[:list_code])
1423
+ next if obj[:desc_de] && /ad us vet/i.match(obj[:desc_de])
1424
+ sequence = obj[:seq]
1425
+ unless sequence
1426
+ if @packs[no8] && /#{ean13}/.match(@packs[no8].to_s)
1427
+ sequence = {:packages =>{ean13 => @packs[no8]}}
1428
+ obj[:seq] = sequence.clone
1429
+ end
1430
+ end
1431
+ if no8
1432
+ ppac = ((_ppac = pack_info and !_ppac[:is_tier]) ? _ppac : nil)
1433
+ end
1434
+ if sequence
1435
+ unless obj[:seq][:packages].keys.index(ean13)
1436
+ # puts "unable to find #{ean13} in #{obj[:seq][:packages].keys}"
1437
+ next
1438
+ end
1439
+ sequence[:packages].each do |gtin, package|
1440
+ pkg_gtin = package[:ean13].clone
1441
+ pharma_code = @refdata[pkg_gtin]
1442
+ if @refdata[pkg_gtin] && @refdata[pkg_gtin][:pharmacode]
1443
+ pharma_code = @refdata[pkg_gtin][:pharmacode]
1444
+ else
1445
+ pharma_code = obj[:pharmacode]
1446
+ end
1447
+ #
1448
+ info = @calc_items[pkg_gtin]
1449
+ if @@emitted_v5_gtins.index(pkg_gtin)
1450
+ next
1451
+ else
1452
+ @@emitted_v5_gtins << pkg_gtin.clone
1453
+ end
1454
+ options = {'PHARMATYPE' => 'P'}
1455
+ xml.ITEM(options) do
1456
+ name = (@refdata[pkg_gtin] ? @refdata[pkg_gtin][:desc_de] : nil) || obj[:desc_de] || obj[:sequence_name]
1457
+ xml.GTIN pkg_gtin.to_s.rjust(13, '0')
1458
+ override(xml, pkg_gtin, :PHAR, pharma_code)
1459
+ xml.SALECD('A')
1460
+ # maxLength for DSCR is 50 for Artikelstamm v3
1461
+ xml.DSCR(name) # for description for zur_rose
1462
+ xml.DSCRF(obj[:desc_fr] || '--missing--')
1463
+ xml.COMP do # Manufacturer
1464
+ xml.NAME obj[:company_name]
1465
+ xml.GLN obj[:company_ean]
1466
+ end
1467
+ pexf = ppub = nil
1468
+ if package[:prices]
1469
+ pexf ||= package[:prices][:exf_price][:price]
1470
+ ppub ||= package[:prices][:pub_price][:price]
1471
+ elsif @items[ean13] && @items[ean13][:packages] && @items[ean13][:packages][ean13] && (bag_prices = @items[ean13][:packages][ean13][:prices])
1472
+ pexf ||= bag_prices[:exf_price][:price]
1473
+ ppub ||= bag_prices[:pub_price][:price]
1474
+ else
1475
+ pexf ||= obj[:price]
1476
+ ppub ||= obj[:pub_price]
1477
+ end
1478
+ ppub = nil if ppub && ppub.size == 0
1479
+ pexf = nil if pexf && pexf.size == 0
1480
+ xml.PEXF pexf if pexf
1481
+ xml.PPUB ppub if ppub
1482
+ measure = ''
1483
+ if info
1484
+ # MEASSURE Measurement Unit,e.g. Pills or milliliters
1485
+ # <DSCR>HIRUDOID Creme 3 mg/g 40 g</DSCR>
1486
+ xml.PKG_SIZE info.pkg_size.to_i if info.pkg_size
1487
+ if info.measure
1488
+ measure = info.measure
1489
+ elsif info.pkg_size && info.unit
1490
+ measure = info.pkg_size + ' ' + info.unit
1491
+ elsif info.pkg_size
1492
+ measure = info.pkg_size
1493
+ end
1494
+ xml.MEASURE measure
1495
+ xml.MEASUREF measure
1496
+ # Die Darreichungsform dieses Items. zB Tablette(n) oder Spritze(n)
1497
+ xml.DOSAGE_FORM info.galenic_form.descriptions['de'] if info.galenic_form.descriptions['de']
1498
+ xml.DOSAGE_FORMF info.galenic_form.descriptions['fr'] if info.galenic_form.descriptions['fr']
1499
+ end
1500
+ xml.SL_ENTRY 'true' if @items[pkg_gtin]
1501
+ xml.IKSCAT package[:swissmedic_category] if package[:swissmedic_category] && package[:swissmedic_category].length > 0
1502
+ xml.GENERIC_TYPE sequence[:org_gen_code] if sequence[:org_gen_code] && !sequence[:org_gen_code].empty?
1503
+ xml.LPPV 'true' if @lppvs[pkg_gtin.to_s] # detect_nincd
1504
+ case item[:deductible]
1505
+ when 'Y'; xml.DEDUCTIBLE 20; # 20%
1506
+ when 'N'; xml.DEDUCTIBLE 10; # 10%
1507
+ else # xml.DEDUCTIBLE '' # k.A.
1508
+ end if item && item[:deductible]
1509
+ xml.PRODNO ppac[:prodno] if ppac && ppac[:prodno] # pkg_gtin.to_s[4..11]
1510
+ csv = []
1511
+ @csv_file << [pkg_gtin, name, package[:unit], measure,
1512
+ pexf ? pexf : '',
1513
+ ppub ? ppub : '',
1514
+ package[:prodno], package[:atc_code], package[:substance_swissmedic],
1515
+ sequence[:org_gen_code], package[:ith_swissmedic],
1516
+ @items[pkg_gtin] ? 'SL' : '',
1517
+ ]
1518
+ end
1519
+ end
1520
+ else # non pharma
1521
+ @csv_file << [ ean13, (obj[:desc_de] || obj[:description]), '', '',
1522
+ obj[:price], obj[:pub_price], '', '', '', '', '', '' ]
1523
+ if @@emitted_v5_gtins.index(ean13)
1524
+ next
1525
+ else
1526
+ @@emitted_v5_gtins << ean13.clone
1527
+ end
1528
+ # Set the pharmatype to 'Y' for outdated products, which are no longer found
1529
+ # in refdata/packungen
1530
+ patched_pharma_type = (/^7680/.match(ean13.to_s.rjust(13, '0')) ? 'P': 'N' )
1531
+ xml.ITEM({'PHARMATYPE' => patched_pharma_type }) do
1532
+ xml.GTIN ean13.to_s.rjust(13, '0')
1533
+ xml.PHAR obj[:pharmacode]
1534
+ emit_salecd(xml, ean13, obj)
1535
+ xml.DSCR(obj[:desc_de] || obj[:description]) # for description for zur_rose
1536
+ xml.DSCRF(obj[:desc_fr] || '--missing--')
1537
+ xml.COMP do
1538
+ xml.GLN obj[:company_ean]
1539
+ end if obj[:company_ean] && !obj[:company_ean].empty?
1540
+ xml.PEXF obj[:price] if obj[:price] && !obj[:price].empty?
1541
+ xml.PPUB obj[:pub_price] if obj[:pub_price] && !obj[:pub_price].empty?
1542
+ end
1543
+ end
1544
+ end
1545
+ @csv_file.close if @csv_file && !@csv_file.closed?
1546
+ nr_items
1547
+ end
1548
+ unless @prepared
1549
+ prepare_limitations
1550
+ prepare_articles
1551
+ prepare_products
1552
+ add_missing_products_from_swissmedic(true)
1553
+ prepare_calc_items(suppress_composition_parsing: true)
1554
+ @prepared = true
1555
+ @old_rose_size = @infos_zur_rose.size
1556
+ @infos_zur_rose.each do |ean13, value|
1557
+ if /^7680/.match(ean13.to_s) && @options[:artikelstamm]
1558
+ @infos_zur_rose.delete(ean13)
1559
+ end
1560
+ end if false
1561
+ @new_rose_size = @infos_zur_rose.size
1562
+ end
1563
+ nr_products = 0
1564
+ nr_articles = 0
1565
+ @nr_articles = 0
1566
+ used_limitations = []
1567
+ Oddb2xml.log "#{variant}: Deleted #{@old_rose_size - @new_rose_size} entries from ZurRose where GTIN start with 7680 (Swissmedic)"
1568
+ # Oddb2xml.log "#{variant} #{nr_products} of #{@products.size} articles and ignore #{@@gtin2ignore.size} GTINS specified via #{@@ignore_file}"
1569
+ _builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
1570
+ xml.doc.tag_suffix = @tag_suffix
1571
+ datetime = Time.new.strftime('%FT%T%z')
1572
+ elexis_strftime_format = "%FT%T\.%L%:z"
1573
+ @@cumul_ver = (Date.today.year-2013)*12+Date.today.month
1574
+ options_xml = {
1575
+ 'xmlns' => 'http://elexis.ch/Elexis_Artikelstamm_v5',
1576
+ 'CREATION_DATETIME' => Time.new.strftime(elexis_strftime_format),
1577
+ 'BUILD_DATETIME' => Time.new.strftime(elexis_strftime_format),
1578
+ 'DATA_SOURCE' => 'oddb2xml'
1579
+ }
1580
+ emitted_prodno = []
1581
+ xml.comment("Produced by #{__FILE__} version #{VERSION} at #{Time.now}")
1582
+ xml.ARTIKELSTAMM(options_xml) do
1583
+ xml.PRODUCTS do
1584
+ products = @products.sort_by { |ean13, obj| ean13 }
1585
+ products.each do |product|
1586
+ ean13 = product[0]
1587
+ obj = product[1]
1588
+ next if /^Q/i.match(obj[:atc])
1589
+ sequence = obj[:seq]
1590
+ ean = obj[:ean13]
1591
+ next unless check_name(obj, :de)
1592
+ ppac = ((_ppac = @packs[ean.to_s[4..11]] and !_ppac[:is_tier]) ? _ppac : {})
1593
+ unless ppac
1594
+ ppac = @packs.find{|pac| pac.ean == ean }.first
1595
+ end
1596
+ prodno = ppac[:prodno] if ppac[:prodno] and !ppac[:prodno].empty?
1597
+ next unless prodno
1598
+ next unless sequence && sequence[:name_de]
1599
+ next if emitted_prodno.index(prodno)
1600
+ emitted_prodno << prodno
1601
+ nr_products += 1
1602
+ xml.PRODUCT do
1603
+ xml.PRODNO prodno
1604
+ if sequence
1605
+ xml.SALECD('A') # these products are always active!
1606
+ override(xml, prodno, :DSCR, (sequence[:name_de] + ' ' + sequence[:desc_de]).strip)
1607
+ override(xml, prodno, :DSCRF, (sequence[:name_fr] + ' ' + sequence[:desc_fr]).strip)
1608
+ end
1609
+ xml.ATC sequence[:atc_code] if sequence[:atc_code] && !sequence[:atc_code].empty?
1610
+ if sequence[:packages] && (first_package = sequence[:packages].values.first) &&
1611
+ (first_limitation = first_package[:limitations].first)
1612
+ lim_code = first_limitation[:code]
1613
+ used_limitations << lim_code unless used_limitations.index(lim_code)
1614
+ xml.LIMNAMEBAG lim_code
1615
+ end
1616
+ if sequence && sequence[:substances]
1617
+ value = nil
1618
+ if sequence[:substances].size > 1
1619
+ value = 'Verschiedene Kombinationen'
1620
+ elsif sequence[:substances].first
1621
+ value = sequence[:substances].first[:name]
1622
+ else
1623
+ value = obj[:sub]
1624
+ end
1625
+ override(xml, prodno, :SUBSTANCE, value) if value
1626
+ end
1627
+ end
1628
+ end
1629
+ end
1630
+ emitted_lim_code = []
1631
+ xml.LIMITATIONS do
1632
+ @limitations.sort! { |left, right| left[:code] <=> right[:code] }
1633
+ @limitations.each do |lim|
1634
+ next unless used_limitations.index(lim[:code])
1635
+ next if emitted_lim_code.index(lim[:code])
1636
+ emitted_lim_code << lim[:code]
1637
+ xml.LIMITATION do
1638
+ xml.LIMNAMEBAG lim[:code] # original LIMCD
1639
+ xml.DSCR lim[:desc_de]
1640
+ xml.DSCRF lim[:desc_fr]
1641
+ xml.LIMITATION_PTS (lim[:value].to_s.length > 1 ? lim[:value] : 1)
1642
+ end
1643
+ end
1644
+ end
1645
+ xml.ITEMS do
1646
+ nr_articles = emit_items(xml)
1647
+ end
1648
+ end
1649
+ end
1650
+ Oddb2xml.log "#{variant}. Done #{nr_products} of #{@products.size} products, #{@limitations.size} limitations and #{nr_articles}/#{@nr_articles} articles. @@emitted_v5_gtins #{@@emitted_v5_gtins.size}"
1651
+ # we don't add a SHA256 hash for each element in the article
1652
+ # Oddb2xml.add_hash(_builder.to_xml)
1653
+ # doc = REXML::Document.new( source, { :raw => :all })
1654
+ # doc.write( $stdout, 0 )
1655
+ lines = []
1656
+ lines << " - #{sprintf('%5d', @products.size)} products"
1657
+ lines << " - #{sprintf('%5d', @limitations.size)} limitations"
1658
+ lines << " - #{sprintf('%5d', @nr_articles)} articles"
1659
+ lines << " - #{sprintf('%5d', @@gtin2ignore.size)} ignored GTINS"
1660
+ @@articlestamm_v5_info_lines = lines
1661
+ _builder.to_xml({:indent => 4, :encoding => 'UTF-8'})
1662
+ end
1663
+ def self.articlestamm_v5_info_lines
1664
+ @@articlestamm_v5_info_lines
1665
+ end
1315
1666
  end
1316
1667
  end