oddb2xml 3.0.2 → 3.0.4
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/CLAUDE.md +4 -2
- data/Gemfile.lock +1 -1
- data/History.txt +11 -0
- data/README.md +14 -8
- data/lib/oddb2xml/cli.rb +8 -3
- data/lib/oddb2xml/downloader.rb +5 -10
- data/lib/oddb2xml/extractor.rb +19 -21
- data/lib/oddb2xml/fhir_support.rb +86 -26
- data/lib/oddb2xml/options.rb +3 -3
- data/lib/oddb2xml/version.rb +1 -1
- data/spec/builder_spec.rb +1 -1
- data/spec/data/Refdata.Articles.xml +853 -0
- data/spec/downloader_spec.rb +4 -4
- data/spec/extractor_spec.rb +3 -3
- data/spec/fixtures/vcr_cassettes/oddb2xml.json +2092 -1980
- data/spec/options_spec.rb +2 -0
- data/spec/spec_helper.rb +5 -3
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a0a4cbd5ff288013064ffb6d756ed0961fd98e52b6960fbfd32b4328a039369
|
|
4
|
+
data.tar.gz: 60c9943c0a7f344ecf87e04f16314da1b8a29e0801a8d7e814d265932f89e443
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75abbcde0acf8e33b8489dd03fc06e6bd8796f571444b0a7f7eb3cded37a25f0938d8db07a7ef6ef0023412e3ab2336c046be3447fb5dc371dd02f06c5950e12
|
|
7
|
+
data.tar.gz: 9cf7e4c5eecd5d708dea61b4c6e17a206f6f4eaa65742ae2ee0c7ea3a49d0b4e4ae2dad7d9ffd3c2e61191e8703693ea29b3aa194d517226f828282ff5b3f891
|
data/CLAUDE.md
CHANGED
|
@@ -37,9 +37,9 @@ The system follows a **download → extract → build → compress** pipeline:
|
|
|
37
37
|
|
|
38
38
|
1. **CLI** (`lib/oddb2xml/cli.rb`) — Entry point. Parses options via Optimist (`options.rb`), orchestrates the pipeline, manages multi-threaded downloads.
|
|
39
39
|
|
|
40
|
-
2. **Downloaders**
|
|
40
|
+
2. **Downloaders** — 11 subclasses of `Downloader`, each fetching from a specific Swiss data source. 10 live in `lib/oddb2xml/downloader.rb`; the FHIR downloader lives in `lib/oddb2xml/fhir_support.rb`. Files cached in `./downloads/`.
|
|
41
41
|
|
|
42
|
-
3. **Extractors** (`lib/oddb2xml/extractor.rb`) — Matching extractor classes that parse downloaded files into Ruby hashes. Formats include XML (nokogiri/sax-machine), XLSX (rubyXL),
|
|
42
|
+
3. **Extractors** (`lib/oddb2xml/extractor.rb`) — Matching extractor classes that parse downloaded files into Ruby hashes. Formats include XML (nokogiri/sax-machine), XLSX (rubyXL), CSV, and fixed-width text. Refdata uses the new SwissReg XML format from a zip download (`files.refdata.ch`).
|
|
43
43
|
|
|
44
44
|
4. **Builder** (`lib/oddb2xml/builder.rb`) — The largest file (~1900 lines). Merges extracted data and generates output XML/DAT files. Methods follow `prepare_*` (data assembly) and `build_*` (output generation) naming.
|
|
45
45
|
|
|
@@ -47,6 +47,8 @@ The system follows a **download → extract → build → compress** pipeline:
|
|
|
47
47
|
|
|
48
48
|
6. **Compressor** (`lib/oddb2xml/compressor.rb`) — Optional ZIP/TAR.GZ output compression.
|
|
49
49
|
|
|
50
|
+
7. **FHIR support** (`lib/oddb2xml/fhir_support.rb`) — Self-contained module providing `FhirDownloader` and FHIR NDJSON parsing. Activated via `--fhir` (or `--fhir-url=<URL>`). Downloads per-language NDJSON files (`foph-sl-export-latest-{de,fr,it}.ndjson`) from `epl.bag.admin.ch` to populate French and Italian product names/descriptions. Maps legal status codes `756005022007` and `756005022008` to Swissmedic category D.
|
|
51
|
+
|
|
50
52
|
### Key data identifiers
|
|
51
53
|
- **GTIN/EAN13**: Primary article identifier (13-digit barcode)
|
|
52
54
|
- **Pharmacode**: Swiss pharmacy code
|
data/Gemfile.lock
CHANGED
data/History.txt
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
+
=== 3.0.4 / 24.04.2026
|
|
2
|
+
* Firstbase: switch -b/--firstbase from the deprecated pillbox.oddb.org XLSX to the GS1 Switzerland CSV at https://id.gs1.ch/01/07612345000961 (full firstbase barcode registry, ~189k items). Downloaded file is now firstbase.csv; FirstbaseExtractor now parses CSV with headers instead of XLSX.
|
|
3
|
+
|
|
4
|
+
=== 3.0.3 / 24.04.2026
|
|
5
|
+
* FHIR: download per-language NDJSON files (foph-sl-export-latest-{de,fr,it}.ndjson) so French and Italian product names/descriptions are populated
|
|
6
|
+
* FHIR: map legal status code 756005022008 to Swissmedic category D (in addition to 756005022007)
|
|
7
|
+
|
|
1
8
|
=== 3.0.2 / 09.03.2026
|
|
2
9
|
* Use raw.githubusercontent.com URL for ATC CSV to avoid 429 Too Many Requests errors
|
|
3
10
|
* Add retry logic with exponential backoff for HTTP 429 errors in uri_open
|
|
4
11
|
* Remove obsolete Ruby version check in uri_open (Ruby >= 2.5 already required)
|
|
12
|
+
* Migrate Refdata from SOAP API to new SwissReg zip download (files.refdata.ch)
|
|
13
|
+
* Fix UTF-8 encoding for German umlauts in SORTD field (e.g. ö → Ö)
|
|
14
|
+
* Restore Italian description fallback to German when not available from Refdata
|
|
15
|
+
* Fix Optimist short option conflict for --fhir and --fhir_url options
|
|
5
16
|
|
|
6
17
|
=== 2.7.9 / 19.09.22
|
|
7
18
|
* Remove newly generated DSCRI when not running --artikelstamm and
|
data/README.md
CHANGED
|
@@ -25,7 +25,7 @@ creates .dat files according to ([IGM-11](http://dev.ywesee.com/uploads/att/IGM.
|
|
|
25
25
|
* oddb.dat
|
|
26
26
|
* oddb_with_migel.dat
|
|
27
27
|
|
|
28
|
-
the files are using [
|
|
28
|
+
the files are using [Refdata](https://files.refdata.ch/simis-public-prod/Articles/1.0/Refdata.Articles.zip), [BAG-XML](http://bag.e-mediat.net/SL2007.Web.External/Default.aspx?webgrab=ignore) and [Swissmedic](http://www.swissmedic.ch/daten/00080/00251/index.html?lang=de) as sources.
|
|
29
29
|
|
|
30
30
|
The following additional data is in the files:
|
|
31
31
|
|
|
@@ -51,7 +51,7 @@ HIN (http://hin.ch) creates daily the actual file. They can be downloaded from `
|
|
|
51
51
|
see `--help`.
|
|
52
52
|
|
|
53
53
|
```
|
|
54
|
-
/opt/src/
|
|
54
|
+
/opt/src/oddb2xml/bin/oddb2xml version 3.0.4
|
|
55
55
|
Usage:
|
|
56
56
|
oddb2xml [option]
|
|
57
57
|
produced files are found under data
|
|
@@ -61,6 +61,10 @@ see `--help`.
|
|
|
61
61
|
-e, --extended pharma, non-pharma plus prices and non-pharma from zurrose.
|
|
62
62
|
Products without EAN-Code will also be listed.
|
|
63
63
|
File oddb_calc.xml will also be generated
|
|
64
|
+
--fhir Use FHIR NDJSON format from FOPH/BAG instead of XML
|
|
65
|
+
from Spezialitätenliste. Downloads per-language
|
|
66
|
+
NDJSON files (de, fr, it) from epl.bag.admin.ch.
|
|
67
|
+
--fhir-url=<s> Specific FHIR NDJSON URL to download (implies --fhir)
|
|
64
68
|
-f, --format=<s> File format F, default is xml. {xml|dat}
|
|
65
69
|
If F is given, -o option is ignored. (Default: xml)
|
|
66
70
|
-i, --include Include target option for ean14 for 'dat' format.
|
|
@@ -80,6 +84,8 @@ see `--help`.
|
|
|
80
84
|
Downloaded files are saved under downloads
|
|
81
85
|
--log log important actions
|
|
82
86
|
-u, --use-ra11zip=<s> Use the ra11.zip (a zipped transfer.dat from Galexis)
|
|
87
|
+
-b, --firstbase Build all NONPHARMA articles on firstbase
|
|
88
|
+
(GS1 Switzerland CSV from id.gs1.ch)
|
|
83
89
|
-v, --version Print version and exit
|
|
84
90
|
-h, --help Show this message
|
|
85
91
|
```
|
|
@@ -106,8 +112,8 @@ FR
|
|
|
106
112
|
|
|
107
113
|
## Supported ruby version
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
You will need ruby >= 2.5 to work correctly. Current development happens on Ruby 3.2 (`.ruby-version`).
|
|
116
|
+
CI runs on Ruby 3.0, 3.1 and 3.2 via GitHub Actions — see the badge above for the latest spec results.
|
|
111
117
|
|
|
112
118
|
|
|
113
119
|
## XSD files
|
|
@@ -274,17 +280,17 @@ We use the following files:
|
|
|
274
280
|
|
|
275
281
|
* https://www.swissmedic.ch/arzneimittel/00156/00221/00222/00230/index.html?lang=de (Präparateliste und zugelassene Packungen)
|
|
276
282
|
* https://raw.githubusercontent.com/zdavatz/oddb2xml_files/master/interactions_de_utf8.csv
|
|
277
|
-
*
|
|
283
|
+
* https://files.refdata.ch/simis-public-prod/Articles/1.0/Refdata.Articles.zip
|
|
278
284
|
* http://bag.e-mediat.net/SL2007.Web.External/File.axd?file=XMLPublications.zip
|
|
279
285
|
* https://www.medregbm.admin.ch/Publikation/CreateExcelListBetriebs
|
|
280
286
|
* https://www.medregbm.admin.ch/Publikation/CreateExcelListMedizinalPersons
|
|
281
287
|
* http://zurrose.com/fileadmin/main/lib/download.php?file=/fileadmin/user_upload/downloads/ProduktUpdate/IGM11_mit_MwSt/Vollstamm/transfer.dat
|
|
282
|
-
* https://
|
|
283
|
-
* https://index.ws.e-mediat.net/Swissindex/NonPharma/ws_Pharma_V101.asmx
|
|
288
|
+
* https://raw.githubusercontent.com/zdavatz/oddb2xml_files/master/NON-Pharma.xls
|
|
284
289
|
* http://download.swissmedicinfo.ch/ (AipsDownload)
|
|
285
290
|
* https://raw.githubusercontent.com/zdavatz/oddb2xml_files/master/LPPV.txt
|
|
286
|
-
* https://raw.githubusercontent.com/epha/robot/master/data/manual/swissmedic/atc.csv
|
|
287
291
|
* https://raw.githubusercontent.com/zdavatz/cpp2sqlite/master/input/atc_codes_multi_lingual.txt
|
|
292
|
+
* https://epl.bag.admin.ch/static/fhir/foph-sl-export-latest-{de,fr,it}.ndjson (FHIR NDJSON, used with `--fhir`)
|
|
293
|
+
* https://id.gs1.ch/01/07612345000961 (GS1 Switzerland firstbase CSV — full barcode registry, used with `-b`/`--firstbase`)
|
|
288
294
|
|
|
289
295
|
## Rules for matching GTIN (aka EAN13), product number and IKSNR
|
|
290
296
|
|
data/lib/oddb2xml/cli.rb
CHANGED
|
@@ -285,10 +285,15 @@ module Oddb2xml
|
|
|
285
285
|
# instead of Thread.new do
|
|
286
286
|
|
|
287
287
|
downloader = FhirDownloader.new(@options)
|
|
288
|
-
|
|
289
|
-
|
|
288
|
+
fhir_files = downloader.download
|
|
289
|
+
total_bytes = if fhir_files.is_a?(Hash)
|
|
290
|
+
fhir_files.values.sum { |f| File.size(f) }
|
|
291
|
+
else
|
|
292
|
+
File.size(fhir_files)
|
|
293
|
+
end
|
|
294
|
+
Oddb2xml.log("FhirDownloader downloaded #{total_bytes} bytes")
|
|
290
295
|
@mutex.synchronize do
|
|
291
|
-
hsh = FhirExtractor.new(
|
|
296
|
+
hsh = FhirExtractor.new(fhir_files).to_hash
|
|
292
297
|
@items = hsh
|
|
293
298
|
Oddb2xml.log("FhirExtractor added #{@items.size} items from FHIR")
|
|
294
299
|
@items
|
data/lib/oddb2xml/downloader.rb
CHANGED
|
@@ -135,6 +135,7 @@ module Oddb2xml
|
|
|
135
135
|
end
|
|
136
136
|
end
|
|
137
137
|
end
|
|
138
|
+
xml.force_encoding("UTF-8") if xml.encoding.name != "UTF-8"
|
|
138
139
|
xml
|
|
139
140
|
end
|
|
140
141
|
end
|
|
@@ -248,13 +249,7 @@ module Oddb2xml
|
|
|
248
249
|
end
|
|
249
250
|
|
|
250
251
|
def init
|
|
251
|
-
|
|
252
|
-
log_level: :info,
|
|
253
|
-
log: false, # $stdout
|
|
254
|
-
raise_errors: true,
|
|
255
|
-
wsdl: @url
|
|
256
|
-
}
|
|
257
|
-
@client = Savon::Client.new(config)
|
|
252
|
+
# No SOAP client needed - we download a zip file directly
|
|
258
253
|
end
|
|
259
254
|
|
|
260
255
|
def download
|
|
@@ -347,14 +342,14 @@ module Oddb2xml
|
|
|
347
342
|
end
|
|
348
343
|
|
|
349
344
|
class FirstbaseDownloader < Downloader
|
|
350
|
-
BASE_URL = "
|
|
345
|
+
BASE_URL = "https://id.gs1.ch/01/07612345000961"
|
|
351
346
|
include DownloadMethod
|
|
352
347
|
def initialize(type = :orphan, options = {})
|
|
353
|
-
@url = BASE_URL
|
|
348
|
+
@url = BASE_URL
|
|
354
349
|
end
|
|
355
350
|
|
|
356
351
|
def download
|
|
357
|
-
@file2save = File.join(DOWNLOADS, "firstbase.
|
|
352
|
+
@file2save = File.join(DOWNLOADS, "firstbase.csv")
|
|
358
353
|
report_download(@url, @file2save)
|
|
359
354
|
begin
|
|
360
355
|
download_as(@file2save, "w+")
|
data/lib/oddb2xml/extractor.rb
CHANGED
|
@@ -202,7 +202,8 @@ module Oddb2xml
|
|
|
202
202
|
|
|
203
203
|
class RefdataExtractor < Extractor
|
|
204
204
|
def initialize(xml, type)
|
|
205
|
-
@type = (type ==
|
|
205
|
+
@type = (type.to_s.upcase == "PHARMA" ? "PHARMA" : "NONPHARMA")
|
|
206
|
+
xml = xml.dup.force_encoding("UTF-8") if xml.encoding.name != "UTF-8"
|
|
206
207
|
super(xml)
|
|
207
208
|
end
|
|
208
209
|
|
|
@@ -242,6 +243,7 @@ module Oddb2xml
|
|
|
242
243
|
item[:desc_it] = name.FullName
|
|
243
244
|
end
|
|
244
245
|
end
|
|
246
|
+
item[:desc_it] = item[:desc_de] if item[:desc_it].empty? # refdata has no italian name
|
|
245
247
|
item[:atc_code] = article.MedicinalProduct.ProductClassification.Atc || ""
|
|
246
248
|
item[:company_name] = article.PackagedProduct.Holder.Name || ""
|
|
247
249
|
item[:company_ean] = article.PackagedProduct.Holder.Identifier || ""
|
|
@@ -606,32 +608,28 @@ module Oddb2xml
|
|
|
606
608
|
|
|
607
609
|
class FirstbaseExtractor < Extractor
|
|
608
610
|
def initialize(file)
|
|
609
|
-
@
|
|
611
|
+
@file = file
|
|
610
612
|
end
|
|
611
613
|
|
|
612
614
|
def to_hash
|
|
613
615
|
data = {}
|
|
614
|
-
return data unless @
|
|
615
|
-
@
|
|
616
|
-
|
|
617
|
-
if
|
|
618
|
-
puts "Empty row (#{i}) in firstbase"
|
|
619
|
-
next
|
|
620
|
-
end
|
|
621
|
-
gtin = row[0].value.to_s.gsub(/^0+/, '')
|
|
616
|
+
return data unless @file && File.exist?(@file)
|
|
617
|
+
CSV.foreach(@file, headers: true, encoding: "UTF-8") do |row|
|
|
618
|
+
gtin = row["Gtin"].to_s.gsub(/^0+/, "")
|
|
619
|
+
next if gtin.empty?
|
|
622
620
|
data[gtin] = {
|
|
623
621
|
gtin: gtin,
|
|
624
|
-
gln: row[
|
|
625
|
-
target_market: row[
|
|
626
|
-
gpc: row[
|
|
627
|
-
trade_item_description_de: row[
|
|
628
|
-
trade_item_description_en:
|
|
629
|
-
trade_item_description_fr: row[
|
|
630
|
-
trade_item_description_it: row[
|
|
631
|
-
manufacturer_name: row[
|
|
632
|
-
start_availability_date: row[
|
|
633
|
-
gross_weight:
|
|
634
|
-
net_weight:
|
|
622
|
+
gln: row["InformationProviderGln"].to_s,
|
|
623
|
+
target_market: row["TargetMarketCountryCode"].to_s,
|
|
624
|
+
gpc: row["GpcCategoryCode"].to_s,
|
|
625
|
+
trade_item_description_de: row["TradeItemDescription_DE"].to_s,
|
|
626
|
+
trade_item_description_en: "",
|
|
627
|
+
trade_item_description_fr: row["TradeItemDescription_FR"].to_s,
|
|
628
|
+
trade_item_description_it: row["TradeItemDescription_IT"].to_s,
|
|
629
|
+
manufacturer_name: row["InformationProviderPartyName"].to_s,
|
|
630
|
+
start_availability_date: row["Date_Created_Batch"].to_s,
|
|
631
|
+
gross_weight: "",
|
|
632
|
+
net_weight: "",
|
|
635
633
|
}
|
|
636
634
|
end
|
|
637
635
|
data
|
|
@@ -15,62 +15,69 @@ module Oddb2xml
|
|
|
15
15
|
|
|
16
16
|
BASE_URL = "https://epl.bag.admin.ch"
|
|
17
17
|
STATIC_FHIR_PATH = "/static/fhir"
|
|
18
|
+
LANGUAGES = %w[de fr it].freeze
|
|
18
19
|
|
|
19
20
|
def initialize(options = {})
|
|
20
21
|
@options = options
|
|
21
|
-
|
|
22
|
-
super(options, @url)
|
|
22
|
+
super(options, BASE_URL)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
# Returns either a single file path String (when --fhir_url is used) or a
|
|
26
|
+
# Hash of { "de" => path, "fr" => path, "it" => path } for per-language
|
|
27
|
+
# NDJSON files.
|
|
25
28
|
def download
|
|
26
|
-
|
|
29
|
+
if @options[:fhir_url]
|
|
30
|
+
@url = @options[:fhir_url]
|
|
31
|
+
download_one(@url)
|
|
32
|
+
else
|
|
33
|
+
files = {}
|
|
34
|
+
LANGUAGES.each do |lang|
|
|
35
|
+
url = "#{BASE_URL}#{STATIC_FHIR_PATH}/foph-sl-export-latest-#{lang}.ndjson"
|
|
36
|
+
path = download_one(url)
|
|
37
|
+
files[lang] = path if path
|
|
38
|
+
end
|
|
39
|
+
raise "FhirDownloader: no FHIR files downloaded successfully" if files.empty?
|
|
40
|
+
files
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def download_one(url)
|
|
47
|
+
@url = url
|
|
48
|
+
filename = File.basename(url)
|
|
27
49
|
file = File.join(WORK_DIR, filename)
|
|
28
50
|
@file2save = File.join(DOWNLOADS, filename)
|
|
29
51
|
|
|
30
|
-
report_download(
|
|
52
|
+
report_download(url, @file2save)
|
|
31
53
|
|
|
32
|
-
# Check if we should skip download (file exists and is recent)
|
|
33
54
|
if skip_download?
|
|
34
55
|
Oddb2xml.log "FhirDownloader: Skip downloading #{@file2save} (#{format_size(File.size(@file2save))}, less than 24h old)"
|
|
35
56
|
return File.expand_path(@file2save)
|
|
36
57
|
end
|
|
37
58
|
|
|
38
59
|
begin
|
|
39
|
-
# Download the file
|
|
40
60
|
download_as(file, "w+")
|
|
41
61
|
|
|
42
|
-
# Validate NDJSON format
|
|
43
62
|
if validate_ndjson(@file2save)
|
|
44
63
|
line_count = count_ndjson_lines(@file2save)
|
|
45
|
-
Oddb2xml.log "FhirDownloader: NDJSON validation successful (#{line_count} bundles, #{format_size(File.size(@file2save))})"
|
|
64
|
+
Oddb2xml.log "FhirDownloader: NDJSON validation successful for #{filename} (#{line_count} bundles, #{format_size(File.size(@file2save))})"
|
|
46
65
|
else
|
|
47
|
-
Oddb2xml.log "FhirDownloader: WARNING - NDJSON validation failed!"
|
|
66
|
+
Oddb2xml.log "FhirDownloader: WARNING - NDJSON validation failed for #{filename}!"
|
|
48
67
|
end
|
|
49
68
|
|
|
50
69
|
File.expand_path(@file2save)
|
|
51
70
|
rescue Timeout::Error, Errno::ETIMEDOUT
|
|
52
71
|
retrievable? ? retry : raise
|
|
53
72
|
rescue => error
|
|
54
|
-
Oddb2xml.log "FhirDownloader: Error downloading
|
|
55
|
-
|
|
73
|
+
Oddb2xml.log "FhirDownloader: Error downloading #{filename}: #{error.message}"
|
|
74
|
+
nil
|
|
56
75
|
ensure
|
|
57
76
|
Oddb2xml.download_finished(@file2save, false)
|
|
58
77
|
FileUtils.rm_f(file, verbose: true) if File.exist?(file) && file != @file2save
|
|
59
78
|
end
|
|
60
79
|
end
|
|
61
80
|
|
|
62
|
-
private
|
|
63
|
-
|
|
64
|
-
def find_latest_fhir_url
|
|
65
|
-
agent = Mechanize.new
|
|
66
|
-
response = agent.get "https://epl.bag.admin.ch/api/sl/public/resources/current"
|
|
67
|
-
resources = JSON.parse(response.body)
|
|
68
|
-
"https://epl.bag.admin.ch/static/" + resources["fhir"]["fileUrl"]
|
|
69
|
-
rescue => e
|
|
70
|
-
Oddb2xml.log "FhirDownloader: Error finding latest URL: #{e.message}"
|
|
71
|
-
nil
|
|
72
|
-
end
|
|
73
|
-
|
|
74
81
|
def skip_download?
|
|
75
82
|
@options[:skip_download] || (File.exist?(@file2save) && file_age_hours(@file2save) < 24)
|
|
76
83
|
end
|
|
@@ -552,7 +559,7 @@ module Oddb2xml
|
|
|
552
559
|
"B"
|
|
553
560
|
when "756005022005"
|
|
554
561
|
"C"
|
|
555
|
-
when "756005022007"
|
|
562
|
+
when "756005022007", "756005022008"
|
|
556
563
|
"D"
|
|
557
564
|
when "756005022009"
|
|
558
565
|
"E"
|
|
@@ -565,8 +572,16 @@ module Oddb2xml
|
|
|
565
572
|
|
|
566
573
|
# FHIR Extractor - Compatible with existing BagXmlExtractor
|
|
567
574
|
class FhirExtractor < Extractor
|
|
568
|
-
|
|
569
|
-
|
|
575
|
+
# Accepts either a single NDJSON file path (back-compat) or a Hash
|
|
576
|
+
# { "de" => path, "fr" => path, "it" => path } of per-language files.
|
|
577
|
+
def initialize(fhir_files)
|
|
578
|
+
if fhir_files.is_a?(Hash)
|
|
579
|
+
@fhir_files = fhir_files
|
|
580
|
+
@fhir_file = fhir_files["de"] || fhir_files.values.first
|
|
581
|
+
else
|
|
582
|
+
@fhir_files = {"de" => fhir_files}
|
|
583
|
+
@fhir_file = fhir_files
|
|
584
|
+
end
|
|
570
585
|
end
|
|
571
586
|
|
|
572
587
|
def to_hash
|
|
@@ -712,9 +727,54 @@ module Oddb2xml
|
|
|
712
727
|
end
|
|
713
728
|
end
|
|
714
729
|
|
|
730
|
+
# Merge names/descriptions from additional language files
|
|
731
|
+
@fhir_files.each do |lang, file|
|
|
732
|
+
next if file == @fhir_file
|
|
733
|
+
next unless file && File.exist?(file)
|
|
734
|
+
merge_language(data, file, lang)
|
|
735
|
+
end
|
|
736
|
+
|
|
715
737
|
Oddb2xml.log "FhirExtractor: Extracted #{data.size} packages"
|
|
716
738
|
data
|
|
717
739
|
end
|
|
740
|
+
|
|
741
|
+
private
|
|
742
|
+
|
|
743
|
+
def merge_language(data, file, lang)
|
|
744
|
+
Oddb2xml.log "FhirExtractor: Merging #{lang} names/descriptions from #{file}"
|
|
745
|
+
result = FhirPreparationsEntry.parse(file)
|
|
746
|
+
name_accessor = "Name#{lang.capitalize}"
|
|
747
|
+
name_key = "name_#{lang}".to_sym
|
|
748
|
+
desc_key = "desc_#{lang}".to_sym
|
|
749
|
+
|
|
750
|
+
result.Preparations.Preparation.each do |seq|
|
|
751
|
+
next unless seq && seq.Packs && seq.Packs.Pack
|
|
752
|
+
|
|
753
|
+
translated_name = seq.respond_to?(name_accessor) ? seq.send(name_accessor) : nil
|
|
754
|
+
|
|
755
|
+
seq.Packs.Pack.each do |pac|
|
|
756
|
+
next unless pac.GTIN
|
|
757
|
+
ean13 = pac.GTIN.to_s
|
|
758
|
+
item = data[ean13]
|
|
759
|
+
next unless item
|
|
760
|
+
|
|
761
|
+
if translated_name && !translated_name.empty?
|
|
762
|
+
item[name_key] = translated_name
|
|
763
|
+
if item[:packages][ean13]
|
|
764
|
+
item[:packages][ean13][name_key] = translated_name
|
|
765
|
+
end
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
# The FHIR parser assigns pkg.description to all three
|
|
769
|
+
# Description* fields; in a language-specific file this is the
|
|
770
|
+
# description in that language.
|
|
771
|
+
desc = pac.DescriptionDe
|
|
772
|
+
if desc && !desc.empty? && item[:packages][ean13]
|
|
773
|
+
item[:packages][ean13][desc_key] = desc
|
|
774
|
+
end
|
|
775
|
+
end
|
|
776
|
+
end
|
|
777
|
+
end
|
|
718
778
|
end
|
|
719
779
|
end
|
|
720
780
|
|
data/lib/oddb2xml/options.rb
CHANGED
|
@@ -22,8 +22,8 @@ module Oddb2xml
|
|
|
22
22
|
opt :extended, "pharma, non-pharma plus prices and non-pharma from zurrose.
|
|
23
23
|
Products without EAN-Code will also be listed.
|
|
24
24
|
File oddb_calc.xml will also be generated"
|
|
25
|
-
opt :fhir, "Use FHIR NDJSON format from FOPH/BAG instead of XML from Spezialitätenliste", default: false
|
|
26
|
-
opt :fhir_url, "Specific FHIR NDJSON URL to download (implies --fhir)", type: :string, default: nil
|
|
25
|
+
opt :fhir, "Use FHIR NDJSON format from FOPH/BAG instead of XML from Spezialitätenliste", default: false, short: :none
|
|
26
|
+
opt :fhir_url, "Specific FHIR NDJSON URL to download (implies --fhir)", type: :string, default: nil, short: :none
|
|
27
27
|
opt :format, "File format F, default is xml. {xml|dat}
|
|
28
28
|
If F is given, -o option is ignored.", type: :string, default: "xml"
|
|
29
29
|
opt :include, "Include target option for ean14 for 'dat' format.
|
|
@@ -45,7 +45,7 @@ module Oddb2xml
|
|
|
45
45
|
opt :log, "log important actions", short: :none
|
|
46
46
|
opt :use_ra11zip, "Use the ra11.zip (a zipped transfer.dat from Galexis)",
|
|
47
47
|
default: File.exist?("ra11.zip") ? "ra11.zip" : nil, type: :string
|
|
48
|
-
opt :firstbase, "Build all NONPHARMA articles on firstbase", short: "b", default: false
|
|
48
|
+
opt :firstbase, "Build all NONPHARMA articles on firstbase (GS1 Switzerland CSV from id.gs1.ch)", short: "b", default: false
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
@opts[:percent] = @opts[:increment]
|
data/lib/oddb2xml/version.rb
CHANGED
data/spec/builder_spec.rb
CHANGED
|
@@ -10,7 +10,7 @@ ARTICLE_ATTRIBUTE_TESTS = [
|
|
|
10
10
|
["ARTICLE", "PROD_DATE", Oddb2xml::DATE_REGEXP],
|
|
11
11
|
["ARTICLE", "VALID_DATE", Oddb2xml::DATE_REGEXP],
|
|
12
12
|
["ARTICLE/ART", "SHA256", /[a-f0-9]{32}/],
|
|
13
|
-
["ARTICLE/ART", "DT",
|
|
13
|
+
["ARTICLE/ART", "DT", //]
|
|
14
14
|
]
|
|
15
15
|
|
|
16
16
|
ARTICLE_MISSING_ELEMENTS = [
|