oddb2xml 2.6.9 → 2.7.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +40 -0
- data/.standard.yml +2 -0
- data/Elexis_Artikelstamm_v5.xsd +0 -3
- data/Gemfile +3 -3
- data/History.txt +28 -0
- data/README.md +3 -3
- data/Rakefile +24 -23
- data/bin/check_artikelstamm +11 -11
- data/bin/compare_v5 +23 -23
- data/bin/oddb2xml +14 -13
- data/lib/oddb2xml.rb +1 -1
- data/lib/oddb2xml/builder.rb +1075 -1048
- data/lib/oddb2xml/calc.rb +232 -233
- data/lib/oddb2xml/chapter_70_hack.rb +38 -32
- data/lib/oddb2xml/cli.rb +252 -235
- data/lib/oddb2xml/compare.rb +70 -59
- data/lib/oddb2xml/compositions_syntax.rb +448 -430
- data/lib/oddb2xml/compressor.rb +20 -20
- data/lib/oddb2xml/downloader.rb +156 -128
- data/lib/oddb2xml/extractor.rb +295 -302
- data/lib/oddb2xml/options.rb +34 -35
- data/lib/oddb2xml/parslet_compositions.rb +263 -269
- data/lib/oddb2xml/semantic_check.rb +39 -33
- data/lib/oddb2xml/util.rb +166 -164
- data/lib/oddb2xml/version.rb +1 -1
- data/lib/oddb2xml/xml_definitions.rb +32 -33
- data/oddb2xml.gemspec +32 -31
- data/spec/artikelstamm_spec.rb +116 -135
- data/spec/builder_spec.rb +495 -524
- data/spec/calc_spec.rb +552 -593
- data/spec/check_artikelstamm_spec.rb +26 -26
- data/spec/cli_spec.rb +173 -174
- data/spec/compare_spec.rb +9 -11
- data/spec/composition_syntax_spec.rb +390 -409
- data/spec/compressor_spec.rb +48 -48
- data/spec/data/refdata_NonPharma.xml +0 -3
- data/spec/data/refdata_Pharma.xml +0 -26
- data/spec/data/transfer.dat +1 -0
- data/spec/data/varia_De.htm +2 -2
- data/spec/data_helper.rb +47 -49
- data/spec/downloader_spec.rb +251 -260
- data/spec/extractor_spec.rb +172 -164
- data/spec/galenic_spec.rb +233 -256
- data/spec/options_spec.rb +116 -119
- data/spec/parslet_spec.rb +833 -861
- data/spec/spec_helper.rb +153 -153
- data/test_options.rb +39 -42
- data/tools/win_fetch_cacerts.rb +2 -3
- metadata +48 -5
- data/.travis.yml +0 -29
@@ -1,41 +1,45 @@
|
|
1
|
-
|
2
|
-
require 'ox'
|
1
|
+
require "ox"
|
3
2
|
|
4
3
|
module Oddb2xml
|
5
4
|
def self.log_timestamp(msg)
|
6
5
|
full_msg = "#{Time.now.strftime("%H:%M:%S")}: #{msg}"
|
7
6
|
puts full_msg
|
8
|
-
|
7
|
+
$stdout.flush
|
9
8
|
full_msg
|
10
9
|
end
|
10
|
+
|
11
11
|
class SemanticCheckXML
|
12
12
|
attr_accessor :components
|
13
13
|
attr_reader :keys, :sub_key_names, :filename, :basename, :version, :hash
|
14
|
-
def initialize(filename, components = ["PRODUCTS", "LIMITATIONS", "ITEMS"
|
14
|
+
def initialize(filename, components = ["PRODUCTS", "LIMITATIONS", "ITEMS"])
|
15
15
|
raise "File #{filename} must exist" unless File.exist?(filename)
|
16
16
|
@filename = filename
|
17
17
|
@basename = File.basename(filename)
|
18
18
|
@components = components
|
19
19
|
@hash = load_file(@filename)
|
20
20
|
end
|
21
|
+
|
21
22
|
def self.get_component_key_name(component_name)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
return "LIMNAMEBAG" if /LIMITATION/i.match?(component_name)
|
24
|
+
return "PRODNO" if /PRODUCT/i.match?(component_name)
|
25
|
+
return "GTIN" if /ITEM/i.match?(component_name)
|
26
|
+
raise "Cannot determine keyname for component #{component_name}"
|
26
27
|
end
|
28
|
+
|
27
29
|
def get_items(component_name)
|
28
30
|
# hack to make it spec/check_artikelstamm.rb work if called alone or as part
|
29
31
|
# of the whole spec suite
|
30
|
-
xx= @hash[:ARTIKELSTAMM]
|
32
|
+
xx = @hash[:ARTIKELSTAMM] || @hash["ARTIKELSTAMM"]
|
31
33
|
comps = xx[component_name.to_sym] || xx[component_name]
|
32
34
|
comps.values.first
|
33
35
|
end
|
36
|
+
|
34
37
|
def load_file(name)
|
35
|
-
Oddb2xml.log_timestamp "Reading #{name} #{(File.size(name)/1024/1024).to_i} MB. This may take some time"
|
38
|
+
Oddb2xml.log_timestamp "Reading #{name} #{(File.size(name) / 1024 / 1024).to_i} MB. This may take some time"
|
36
39
|
Ox.load(IO.read(name), mode: :hash_no_attrs)
|
37
40
|
end
|
38
41
|
end
|
42
|
+
|
39
43
|
class SemanticCheck
|
40
44
|
attr_accessor :items, :products, :limitations
|
41
45
|
def initialize(filename)
|
@@ -45,20 +49,20 @@ module Oddb2xml
|
|
45
49
|
|
46
50
|
def everyProductNumberIsUnique
|
47
51
|
puts "#{Time.now.strftime("%H:%M:%S")}: everyProductNumberIsUnique"
|
48
|
-
return false unless
|
49
|
-
|
52
|
+
return false unless products.size > 0
|
53
|
+
products.collect { |x| x[:PRODNO] }.uniq.size == products.size
|
50
54
|
end
|
51
55
|
|
52
56
|
def everyGTINIsUnique
|
53
57
|
puts "#{Time.now.strftime("%H:%M:%S")}: everyGTINIsUnique"
|
54
|
-
return false unless
|
55
|
-
|
58
|
+
return false unless items.size > 0
|
59
|
+
items.collect { |x| x[:GTIN] }.uniq.size == items.size
|
56
60
|
end
|
57
61
|
|
58
62
|
def everyGTINIsNumericOnly
|
59
63
|
puts "#{Time.now.strftime("%H:%M:%S")}: everyGTINIsNumericOnly"
|
60
64
|
items.each do |item|
|
61
|
-
unless /^[0-9]+$/i.match(item[:GTIN])
|
65
|
+
unless /^[0-9]+$/i.match?(item[:GTIN])
|
62
66
|
puts "GTIN is not Numeric Only"
|
63
67
|
return false
|
64
68
|
end
|
@@ -68,13 +72,15 @@ module Oddb2xml
|
|
68
72
|
def everyPharmaArticleHasAProductItem
|
69
73
|
result = true
|
70
74
|
puts "#{Time.now.strftime("%H:%M:%S")}: everyPharmaArticleHasAProductItem"
|
71
|
-
|
75
|
+
all_product_numbers = products.collect { |product| product[:PRODNO] }
|
72
76
|
items.each do |item|
|
73
77
|
next unless item[:PRODNO]
|
74
|
-
unless
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
+
unless item[:Chapter70_HACK]
|
79
|
+
unless all_product_numbers.index(item[:PRODNO])
|
80
|
+
puts "Item #{item[:GTIN]} has no Product #{item[:PRODNO]} #{item[:DSCR]}"
|
81
|
+
result = false
|
82
|
+
end
|
83
|
+
end
|
78
84
|
end
|
79
85
|
result
|
80
86
|
end
|
@@ -82,9 +88,9 @@ module Oddb2xml
|
|
82
88
|
def everyProductHasAtLeastOneArticle
|
83
89
|
result = true
|
84
90
|
puts "#{Time.now.strftime("%H:%M:%S")}: veryProductHasAtLeastOneArticle"
|
85
|
-
|
91
|
+
all_product_numbers = items.collect { |item| item[:PRODNO] }
|
86
92
|
products.each do |product|
|
87
|
-
unless
|
93
|
+
unless all_product_numbers.index(product[:PRODNO])
|
88
94
|
puts "product #{product[:PRODNO]}: has no Item #{product[:DSCR]}"
|
89
95
|
result = false
|
90
96
|
end
|
@@ -95,10 +101,10 @@ module Oddb2xml
|
|
95
101
|
def everyReferencedLimitationIsIncluded
|
96
102
|
result = true
|
97
103
|
puts "#{Time.now.strftime("%H:%M:%S")}: everyReferencedLimitationIsIncluded"
|
98
|
-
|
104
|
+
all_limitations = limitations.collect { |lim| lim[:LIMNAMEBAG] }
|
99
105
|
products.each do |product|
|
100
106
|
next unless product[:LIMNAMEBAG]
|
101
|
-
unless
|
107
|
+
unless all_limitations.index(product[:LIMNAMEBAG])
|
102
108
|
puts "product #{product[:PRODNO]} has no limitation #{product[:LIMNAMEBAG]} #{product[:DSCR]}"
|
103
109
|
result = false
|
104
110
|
end
|
@@ -109,30 +115,30 @@ module Oddb2xml
|
|
109
115
|
def checkPackageSize
|
110
116
|
puts "#{Time.now.strftime("%H:%M:%S")}: checkPackageSize"
|
111
117
|
items.each do |item|
|
112
|
-
if item[
|
113
|
-
puts "WARNING possibly invalid package size #{item[
|
118
|
+
if item["PKG_SIZE"] && item["PKG_SIZE"].length >= 6
|
119
|
+
puts "WARNING possibly invalid package size #{item["PKG_SIZE"]}"
|
114
120
|
pp item
|
115
121
|
end
|
116
122
|
end
|
117
123
|
end
|
118
124
|
|
119
125
|
def allSemanticChecks
|
120
|
-
@limitations = @stammdaten.get_items(
|
121
|
-
@items = @stammdaten.get_items(
|
122
|
-
@products = @stammdaten.get_items(
|
126
|
+
@limitations = @stammdaten.get_items("LIMITATIONS")
|
127
|
+
@items = @stammdaten.get_items("ITEMS")
|
128
|
+
@products = @stammdaten.get_items("PRODUCTS")
|
123
129
|
puts "#{Time.now.strftime("%H:%M:%S")}: Running all semantic checks for #{@stammdaten.filename} for #{products.size} products and #{items.size} items"
|
124
|
-
|
130
|
+
if everyProductNumberIsUnique &&
|
125
131
|
everyGTINIsUnique &&
|
126
132
|
everyGTINIsNumericOnly &&
|
127
133
|
everyPharmaArticleHasAProductItem &&
|
128
134
|
everyProductHasAtLeastOneArticle &&
|
129
135
|
everyReferencedLimitationIsIncluded &&
|
130
136
|
checkPackageSize
|
131
|
-
puts "#{Time.now.strftime("%H:%M:%S")}: Checking #{@stammdaten.filename} failed"
|
132
|
-
false
|
133
|
-
else
|
134
137
|
puts "#{Time.now.strftime("%H:%M:%S")}: Everything is okay"
|
135
138
|
true
|
139
|
+
else
|
140
|
+
puts "#{Time.now.strftime("%H:%M:%S")}: Checking #{@stammdaten.filename} failed"
|
141
|
+
false
|
136
142
|
end
|
137
143
|
rescue => error
|
138
144
|
puts "Execution failed with #{error}"
|
data/lib/oddb2xml/util.rb
CHANGED
@@ -1,235 +1,233 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
require 'htmlentities'
|
1
|
+
require "open-uri"
|
2
|
+
require "htmlentities"
|
4
3
|
|
5
4
|
module Oddb2xml
|
6
|
-
FAKE_GTIN_START =
|
7
|
-
def
|
8
|
-
|
5
|
+
FAKE_GTIN_START = "999999"
|
6
|
+
def self.gen_prodno(iksnr, seqnr)
|
7
|
+
sprintf("%05d", iksnr) + sprintf("%02d", seqnr)
|
9
8
|
end
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
|
10
|
+
def self.uri_open(url)
|
11
|
+
version = RUBY_VERSION.split(".").map { |x| x.to_i }
|
12
|
+
if (version <=> [2, 5, 0]) >= 0
|
13
|
+
URI.parse(url).open
|
14
14
|
else
|
15
|
-
|
15
|
+
IO.popen(url)
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
|
+
def self.calc_checksum(str)
|
19
20
|
str = str.strip
|
20
21
|
sum = 0
|
21
|
-
val =
|
22
|
+
val = str.split(//u)
|
22
23
|
12.times do |idx|
|
23
|
-
fct = ((idx%2)*2)+1
|
24
|
-
sum += fct*val[idx].to_i
|
24
|
+
fct = ((idx % 2) * 2) + 1
|
25
|
+
sum += fct * val[idx].to_i
|
25
26
|
end
|
26
|
-
((10-(sum%10))%10).to_s
|
27
|
+
((10 - (sum % 10)) % 10).to_s
|
27
28
|
end
|
28
29
|
|
29
30
|
unless defined?(RSpec)
|
30
|
-
|
31
|
-
|
31
|
+
WORK_DIR = Dir.pwd
|
32
|
+
DOWNLOADS = "#{Dir.pwd}/downloads"
|
32
33
|
end
|
33
34
|
@options = {}
|
34
|
-
@atc_csv_origin =
|
35
|
+
@atc_csv_origin = "https://github.com/zdavatz/cpp2sqlite/blob/master/input/atc_codes_multi_lingual.txt"
|
35
36
|
@atc_csv_content = {}
|
36
37
|
|
37
|
-
def
|
38
|
+
def self.html_decode(string)
|
38
39
|
german = string
|
39
|
-
german = string.force_encoding(
|
40
|
-
|
40
|
+
german = string.force_encoding("ISO-8859-1").encode("UTF-8") if string.encoding.to_s.eql?("ASCII")
|
41
|
+
until german.eql?(HTMLEntities.new.decode(german))
|
41
42
|
german = HTMLEntities.new.decode(german)
|
42
43
|
end
|
43
|
-
Oddb2xml.patch_some_utf8(german).gsub(
|
44
|
+
Oddb2xml.patch_some_utf8(german).gsub("<br>", "\n")
|
44
45
|
end
|
45
46
|
|
46
|
-
def
|
47
|
+
def self.patch_some_utf8(line)
|
47
48
|
begin
|
48
|
-
line = line.encode(
|
49
|
-
rescue
|
49
|
+
line = line.encode("utf-8")
|
50
|
+
rescue
|
50
51
|
end
|
51
52
|
begin
|
52
|
-
line.
|
53
|
+
line.tr("\u0089", "‰").tr("\u0092", "’").tr("\u0096", "-").tr("\u2013", "-").tr("\u201D", '"').chomp
|
53
54
|
rescue => error
|
54
55
|
puts "#{error}: in #{line}"
|
55
56
|
line
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
puts "#{error}: in #{line}"
|
66
|
-
end
|
60
|
+
def self.convert_to_8859_1(line)
|
61
|
+
# We want to ignore lines which are not really UTF-8 encoded
|
62
|
+
ausgabe = Oddb2xml.patch_some_utf8(line).encode("ISO-8859-1")
|
63
|
+
ausgabe.encode("ISO-8859-1")
|
64
|
+
rescue => error
|
65
|
+
puts "#{error}: in #{line}"
|
67
66
|
end
|
68
67
|
|
69
|
-
def
|
70
|
-
@atc_csv_content
|
68
|
+
def self.add_epha_changes_for_ATC(iksnr, atc_code, force_run: false)
|
69
|
+
@atc_csv_content = {} if force_run
|
71
70
|
if @atc_csv_content.size == 0
|
72
|
-
Oddb2xml.uri_open(@atc_csv_origin).readlines.each{
|
73
|
-
|
74
|
-
|
75
|
-
@atc_csv_content[[items[0], items[1]]] = items[2]
|
71
|
+
Oddb2xml.uri_open(@atc_csv_origin).readlines.each { |line|
|
72
|
+
items = line.split(",")
|
73
|
+
@atc_csv_content[[items[0], items[1]]] = items[2]
|
76
74
|
}
|
77
75
|
|
78
76
|
end
|
79
77
|
new_value = @atc_csv_content[[iksnr.to_s, atc_code]]
|
80
|
-
new_value
|
78
|
+
new_value || atc_code
|
81
79
|
end
|
82
80
|
|
83
|
-
def
|
81
|
+
def self.log(msg)
|
84
82
|
return unless @options[:log]
|
85
83
|
# TODO:: require 'pry'; binding.pry if msg.size > 1000
|
86
84
|
$stdout.puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}: #{msg[0..250]}"
|
87
85
|
$stdout.flush
|
88
86
|
end
|
89
87
|
|
90
|
-
def
|
88
|
+
def self.save_options(options)
|
91
89
|
@options = options
|
92
90
|
end
|
93
91
|
|
94
|
-
def
|
92
|
+
def self.skip_download?
|
95
93
|
@options[:skip_download]
|
96
94
|
end
|
97
95
|
|
98
|
-
def
|
96
|
+
def self.skip_download(file)
|
99
97
|
return false if defined?(VCR)
|
100
|
-
dest = "#{
|
101
|
-
if File.
|
102
|
-
FileUtils.cp(dest, file, :
|
98
|
+
dest = "#{DOWNLOADS}/#{File.basename(file)}"
|
99
|
+
if File.exist?(dest)
|
100
|
+
FileUtils.cp(dest, file, verbose: false, preserve: true) unless File.expand_path(file).eql?(dest)
|
103
101
|
return true
|
104
102
|
end
|
105
103
|
false
|
106
104
|
end
|
107
105
|
|
108
|
-
def
|
109
|
-
src
|
110
|
-
dest = "#{
|
111
|
-
FileUtils.makedirs(
|
112
|
-
#return unless File.exists?(file)
|
113
|
-
return unless file
|
106
|
+
def self.download_finished(file, remove_file = true)
|
107
|
+
src = "#{WORK_DIR}/#{File.basename(file)}"
|
108
|
+
dest = "#{DOWNLOADS}/#{File.basename(file)}"
|
109
|
+
FileUtils.makedirs(DOWNLOADS)
|
110
|
+
# return unless File.exists?(file)
|
111
|
+
return unless file && File.exist?(file)
|
114
112
|
return if File.expand_path(file).eql?(dest)
|
115
|
-
FileUtils.cp(src, dest, :
|
113
|
+
FileUtils.cp(src, dest, verbose: false)
|
116
114
|
Oddb2xml.log("download_finished saved as #{dest} #{File.size(dest)} bytes.")
|
117
115
|
end
|
118
116
|
|
119
117
|
# please keep this constant in sync between (GEM) swissmedic-diff/lib/swissmedic-diff.rb and (GEM) oddb2xml/lib/oddb2xml/extractor.rb
|
120
|
-
def
|
118
|
+
def self.check_column_indices(sheet)
|
121
119
|
row = sheet[5] # Headers are found at row 5 since February 5
|
122
120
|
|
123
121
|
error_2019 = nil
|
124
|
-
0.upto(
|
125
|
-
COLUMNS_FEBRUARY_2019.each{
|
126
|
-
|key, value|
|
122
|
+
0.upto(COLUMNS_FEBRUARY_2019.size - 1).each { |idx| puts "#{idx}: #{row[idx].value}" } if $VERBOSE
|
123
|
+
COLUMNS_FEBRUARY_2019.each { |key, value|
|
127
124
|
header_name = row[COLUMNS_FEBRUARY_2019.keys.index(key)].value.to_s
|
128
125
|
unless value.match(header_name)
|
129
126
|
puts "#{__LINE__}: #{key} -> #{COLUMNS_FEBRUARY_2019.keys.index(key)} #{value}\nbut was #{header_name}" if $VERBOSE
|
130
|
-
error_2019 = "Packungen.xlslx_has_unexpected_column_#{COLUMNS_FEBRUARY_2019.keys.index(key)}_#{key}_#{value
|
127
|
+
error_2019 = "Packungen.xlslx_has_unexpected_column_#{COLUMNS_FEBRUARY_2019.keys.index(key)}_#{key}_#{value}_but_was_#{header_name}"
|
131
128
|
# require 'pry'; binding.pry
|
132
|
-
|
129
|
+
break
|
133
130
|
end
|
134
131
|
}
|
135
|
-
raise
|
132
|
+
raise error_2019.to_s if error_2019
|
136
133
|
end
|
137
134
|
|
138
135
|
# please keep this constant in sync between (GEM) swissmedic-diff/lib/swissmedic-diff.rb and (GEM) oddb2xml/lib/oddb2xml/extractor.rb
|
139
|
-
COLUMNS_FEBRUARY_2019= {
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
136
|
+
COLUMNS_FEBRUARY_2019 = {
|
137
|
+
iksnr: /Zulassungs-Nummer/i, # column-nr: 0
|
138
|
+
seqnr: /Dosisstärke-nummer/i,
|
139
|
+
name_base: /Bezeichnung des Arzneimittels/i,
|
140
|
+
company: /Zulassungsinhaberin/i,
|
141
|
+
production_science: /Heilmittelcode/i,
|
142
|
+
index_therapeuticus: /IT-Nummer/i, # column-nr: 5
|
143
|
+
atc_class: /ATC-Code/i,
|
144
|
+
registration_date: /Erstzul.datum Arzneimittel/i,
|
145
|
+
sequence_date: /Zul.datum Dosisstärke/i,
|
146
|
+
expiry_date: /Gültigkeitsdauer der Zulassung/i,
|
147
|
+
ikscd: /Packungscode/i, # column-nr: 10
|
148
|
+
size: /Packungsgrösse/i,
|
149
|
+
unit: /Einheit/i,
|
150
|
+
ikscat: /Abgabekategorie Packung/i,
|
151
|
+
ikscat_seq: /Abgabekategorie Dosisstärke/i,
|
152
|
+
ikscat_preparation: /Abgabekategorie Arzneimittel/i, # column-nr: 15
|
153
|
+
substances: /Wirkstoff/i,
|
154
|
+
composition: /Zusammensetzung/i,
|
155
|
+
composition_AMZV: /Volldeklaration rev. AMZV umgesetzt/i,
|
156
|
+
indication_registration: /Anwendungsgebiet Arzneimittel/i,
|
157
|
+
indication_sequence: /Anwendungsgebiet Dosisstärke/i, # column-nr 20
|
158
|
+
gen_production: /Gentechnisch hergestellte Wirkstoffe/i,
|
159
|
+
insulin_category: /Kategorie bei Insulinen/i,
|
160
|
+
# swissmedi corrected in february 2018 the typo betäubunsmittel to betäubungsmittel-
|
161
|
+
drug_index: /Verz. bei betäubungsmittel-haltigen Arzneimittel/i
|
162
|
+
}
|
166
163
|
|
167
164
|
COLUMNS_JULY_2015 = {
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
def
|
194
|
-
doc = Nokogiri::XML.parse(string)
|
165
|
+
iksnr: /Zulassungs-Nummer/i, # column-nr: 0
|
166
|
+
seqnr: /Dosisstärke-nummer/i,
|
167
|
+
name_base: /Präparatebezeichnung/i,
|
168
|
+
company: /Zulassungsinhaberin/i,
|
169
|
+
production_science: /Heilmittelcode/i,
|
170
|
+
index_therapeuticus: /IT-Nummer/i, # column-nr: 5
|
171
|
+
atc_class: /ATC-Code/i,
|
172
|
+
registration_date: /Erstzulassungs-datum./i,
|
173
|
+
sequence_date: /Zul.datum Dosisstärke/i,
|
174
|
+
expiry_date: /Gültigkeitsdauer der Zulassung/i,
|
175
|
+
ikscd: /Packungscode/i, # column-nr: 10
|
176
|
+
size: /Packungsgrösse/i,
|
177
|
+
unit: /Einheit/i,
|
178
|
+
ikscat: /Abgabekategorie Arzneimittel/i,
|
179
|
+
ikscat_seq: /Abgabekategorie Dosisstärke/i,
|
180
|
+
ikscat_preparation: /Abgabekategorie Präparat/i, # column-nr: 15
|
181
|
+
substances: /Wirkstoff/i,
|
182
|
+
composition: /Zusammensetzung/i,
|
183
|
+
indication_registration: /Anwendungsgebiet Präparat/i,
|
184
|
+
indication_sequence: /Anwendungsgebiet Dosisstärke/i,
|
185
|
+
gen_production: /Gentechnisch hergestellte Wirkstoffe/i, # column-nr 20
|
186
|
+
insulin_category: /Kategorie bei Insulinen/i,
|
187
|
+
# swissmedi corrected in february 2018 the typo betäubunsmittel to betäubungsmittel-
|
188
|
+
drug_index: /Verz. bei betäubun.*smittel-haltigen Präparaten/i
|
189
|
+
}
|
190
|
+
def self.add_hash(string)
|
191
|
+
doc = Nokogiri::XML.parse(string) do |config|
|
192
|
+
config.huge
|
193
|
+
end
|
195
194
|
nr = 0
|
196
195
|
doc.root.elements.each do |node|
|
197
196
|
nr += 1
|
198
|
-
next if node.name.eql?(
|
199
|
-
node[
|
197
|
+
next if node.name.eql?("RESULT")
|
198
|
+
node["SHA256"] = Digest::SHA256.hexdigest node.text
|
200
199
|
end
|
201
200
|
doc.to_xml
|
202
201
|
end
|
203
202
|
|
204
|
-
def
|
203
|
+
def self.verify_sha256(file)
|
205
204
|
f = File.open(file)
|
206
205
|
doc = Nokogiri::XML(f)
|
207
206
|
nr = 0
|
208
207
|
doc.root.elements.each do |node|
|
209
208
|
nr += 1
|
210
|
-
next if node.name.eql?(
|
209
|
+
next if node.name.eql?("RESULT")
|
211
210
|
sha256 = Digest::SHA256.hexdigest node.text
|
212
|
-
unless node[
|
213
|
-
puts "Verifiying #{node[
|
214
|
-
exit
|
211
|
+
unless node["SHA256"].eql?(sha256)
|
212
|
+
puts "Verifiying #{node["SHA256"]} != expectd #{sha256} against node #{node.text} failed"
|
213
|
+
exit(3)
|
215
214
|
end
|
216
215
|
end
|
217
|
-
|
216
|
+
true
|
218
217
|
end
|
219
218
|
|
220
|
-
def
|
221
|
-
xsd =open(xsd_file).read
|
219
|
+
def self.validate_via_xsd(xsd_file, xml_file)
|
220
|
+
xsd = IO.open(xsd_file).read
|
222
221
|
xsd_rtikelstamm_xml = Nokogiri::XML::Schema(xsd)
|
223
222
|
doc = Nokogiri::XML(File.read(xml_file))
|
224
|
-
xsd_rtikelstamm_xml.validate(doc).each do
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
expect(error.message).to be_nil, msg
|
223
|
+
xsd_rtikelstamm_xml.validate(doc).each do |error|
|
224
|
+
if error.message
|
225
|
+
puts "Failed validating #{xml_file} with #{File.size(xml_file)} bytes using XSD from #{xsd_file}"
|
226
|
+
puts "CMD: xmllint --noout --schema #{xsd_file} #{xml_file}"
|
227
|
+
end
|
228
|
+
msg = "expected #{error.message} to be nil\nfor #{xml_file}"
|
229
|
+
puts msg
|
230
|
+
expect(error.message).to be_nil, msg
|
233
231
|
end
|
234
232
|
end
|
235
233
|
|
@@ -238,36 +236,40 @@ COLUMNS_FEBRUARY_2019= {
|
|
238
236
|
@@no8_to_ean13 = {}
|
239
237
|
@@ean13_to_prodno = {}
|
240
238
|
@@ean13_to_no8 = {}
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
end
|
245
|
-
@@prodno_to_ean13[prodno] ||= []
|
246
|
-
@@prodno_to_ean13[prodno] << ean13
|
247
|
-
@@ean13_to_prodno[ean13] = prodno
|
239
|
+
def self.setEan13forProdno(prodno, ean13)
|
240
|
+
if ean13.to_i == 7680006660045 || ean13.to_i == 7680006660014
|
241
|
+
Oddb2xml.log "setEan13forProdno #{prodno} ean13 #{ean13}"
|
248
242
|
end
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
Oddb2xml.log "@@no8_to_ean13[no8] #{@@no8_to_ean13[no8]} not overridden by #{ean13}"
|
258
|
-
end
|
259
|
-
end
|
260
|
-
def Oddb2xml.getEan13forProdno(prodno)
|
261
|
-
@@prodno_to_ean13[prodno] || []
|
262
|
-
end
|
263
|
-
def Oddb2xml.getEan13forNo8(no8)
|
264
|
-
@@no8_to_ean13[no8] || []
|
265
|
-
end
|
266
|
-
def Oddb2xml.getProdnoForEan13(ean13)
|
267
|
-
@@ean13_to_prodno[ean13]
|
243
|
+
@@prodno_to_ean13[prodno] ||= []
|
244
|
+
@@prodno_to_ean13[prodno] << ean13
|
245
|
+
@@ean13_to_prodno[ean13] = prodno
|
246
|
+
end
|
247
|
+
|
248
|
+
def self.setEan13forNo8(no8, ean13)
|
249
|
+
if ean13.to_i == 7680006660045 || ean13.to_i == 7680006660014
|
250
|
+
Oddb2xml.log "setEan13forNo8 #{no8} ean13 #{ean13}"
|
268
251
|
end
|
269
|
-
|
270
|
-
@@
|
252
|
+
if @@no8_to_ean13[no8].nil?
|
253
|
+
@@no8_to_ean13[no8] = ean13
|
254
|
+
@@ean13_to_no8[ean13] = no8
|
255
|
+
elsif !@@no8_to_ean13[no8].eql?(ean13)
|
256
|
+
Oddb2xml.log "@@no8_to_ean13[no8] #{@@no8_to_ean13[no8]} not overridden by #{ean13}"
|
271
257
|
end
|
258
|
+
end
|
272
259
|
|
260
|
+
def self.getEan13forProdno(prodno)
|
261
|
+
@@prodno_to_ean13[prodno] || []
|
262
|
+
end
|
263
|
+
|
264
|
+
def self.getEan13forNo8(no8)
|
265
|
+
@@no8_to_ean13[no8] || []
|
266
|
+
end
|
267
|
+
|
268
|
+
def self.getProdnoForEan13(ean13)
|
269
|
+
@@ean13_to_prodno[ean13]
|
270
|
+
end
|
271
|
+
|
272
|
+
def self.getNo8ForEan13(ean13)
|
273
|
+
@@ean13_to_no8[ean13]
|
274
|
+
end
|
273
275
|
end
|