oddb2xml 2.5.0 → 2.5.1
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.
- 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
|