oddb2xml 2.5.0 → 2.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/Elexis_Artikelstamm_v003.xsd +387 -0
- data/Elexis_Artikelstamm_v5.xsd +513 -0
- data/Gemfile +2 -6
- data/History.txt +11 -0
- data/README.md +35 -27
- data/artikelstamm.md +68 -0
- data/bin/compare_v5 +41 -0
- data/bin/oddb2xml +3 -15
- data/data/article_overrides.yaml +51859 -0
- data/data/gtin2ignore.yaml +30510 -0
- data/data/product_overrides.yaml +4 -0
- data/lib/oddb2xml/builder.rb +543 -192
- data/lib/oddb2xml/cli.rb +82 -62
- data/lib/oddb2xml/compare.rb +189 -0
- data/lib/oddb2xml/compressor.rb +6 -3
- data/lib/oddb2xml/downloader.rb +79 -64
- data/lib/oddb2xml/extractor.rb +67 -40
- data/lib/oddb2xml/options.rb +76 -77
- data/lib/oddb2xml/parslet_compositions.rb +18 -1
- data/lib/oddb2xml/util.rb +25 -3
- data/lib/oddb2xml/version.rb +1 -1
- data/oddb2xml.gemspec +8 -5
- data/oddb2xml.xsd +1 -0
- data/spec/artikelstamm_spec.rb +383 -0
- data/spec/builder_spec.rb +147 -118
- data/spec/calc_spec.rb +3 -15
- data/spec/cli_spec.rb +24 -35
- data/spec/compare_spec.rb +24 -0
- data/spec/compressor_spec.rb +1 -3
- data/spec/data/Elexis_Artikelstamm_v5.xsd +513 -0
- data/spec/data/Preparations.xml +2200 -0
- data/spec/data/Publications.xls +0 -0
- data/spec/data/artikelstamm_N_010917.xml +39 -0
- data/spec/data/artikelstamm_N_011217.xml +17 -0
- data/spec/data/artikelstamm_P_010917.xml +86 -0
- data/spec/data/artikelstamm_P_011217.xml +63 -0
- data/spec/data/oddb2xml_files_lppv.txt +2 -0
- data/spec/data/refdata_NonPharma.xml +38 -0
- data/spec/data/refdata_Pharma.xml +220 -0
- data/spec/data/swissmedic_orphan.xlsx +0 -0
- data/spec/data/swissmedic_package.xlsx +0 -0
- data/spec/data/transfer.dat +59 -19
- data/spec/data/v5_first.xml +102 -0
- data/spec/data/v5_second.xml +184 -0
- data/spec/data_helper.rb +72 -0
- data/spec/downloader_spec.rb +19 -27
- data/spec/extractor_spec.rb +27 -33
- data/spec/fixtures/vcr_cassettes/artikelstamm.json +1 -0
- data/spec/options_spec.rb +73 -66
- data/spec/spec_helper.rb +73 -24
- data/test_options.rb +4 -2
- metadata +100 -21
- data/spec/data/XMLPublications.zip +0 -0
- data/spec/data/compressor/oddb_article.xml +0 -0
- data/spec/data/compressor/oddb_fi.xml +0 -0
- data/spec/data/compressor/oddb_fi_product.xml +0 -0
- data/spec/data/compressor/oddb_limitation.xml +0 -0
- data/spec/data/compressor/oddb_product.xml +0 -0
- data/spec/data/compressor/oddb_substance.xml +0 -0
data/lib/oddb2xml/builder.rb
CHANGED
@@ -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
|
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
|
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[:
|
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
|
-
:
|
109
|
+
:ean13 => migel[:ean13],
|
104
110
|
:pharmacode => migel[:pharmacode],
|
105
111
|
:stat_date => '',
|
106
|
-
:desc_de
|
107
|
-
: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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|
-
|
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[:
|
228
|
+
:seq => @items[ean13] ? @items[ean13] : @items[item[:ean13]],
|
226
229
|
:pac => nil,
|
227
230
|
:no8 => nil,
|
228
|
-
:
|
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
|
-
|
237
|
-
|
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]
|
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[:
|
243
|
-
obj[:
|
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
|
-
|
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
|
-
|
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
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
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[:
|
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[:
|
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
|
-
|
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[:
|
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]
|
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
|
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
|
-
|
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
|
-
|
680
|
-
|
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
|
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
|
-
|
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
|
-
|
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'] +
|
757
|
-
|
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[:
|
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
|
783
|
-
@preparations_only << ean
|
784
|
-
missing_eans << ean
|
819
|
+
unless @articles.collect {|x| x[:ean13] }.index(ean)
|
785
820
|
item = @items[ean].clone
|
786
|
-
|
787
|
-
item[:
|
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[:
|
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[:
|
800
|
-
pac,no8 = nil,obj[:
|
801
|
-
|
802
|
-
pack_info = @packs[no8
|
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[:
|
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
|
-
|
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
|
-
|
856
|
+
zur_rose = nil
|
821
857
|
if !@infos_zur_rose.empty? && ean && @infos_zur_rose[ean]
|
822
|
-
|
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
|
849
|
-
xml.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
|
945
|
-
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
|
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
|
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[:
|
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 |
|
1135
|
-
next unless
|
1136
|
-
|
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[:
|
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[:
|
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]
|
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
|
-
|
1220
|
-
if
|
1221
|
-
row <<
|
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[:
|
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[:
|
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
|