oddb2xml 3.0.11 → 3.0.12
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 +1 -1
- data/Gemfile.lock +1 -1
- data/History.txt +4 -0
- data/README.md +1 -1
- data/lib/oddb2xml/fhir_support.rb +8 -1
- data/lib/oddb2xml/semantic_check.rb +11 -1
- data/lib/oddb2xml/version.rb +1 -1
- data/spec/data/check_artikelstamm/artikelstamm_v5_single_element_okay.xml +50 -0
- data/spec/fhir_spec.rb +4 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e12453c6fd722397734fdaa3097a4d30b80d801099158f62278b137abf26f326
|
|
4
|
+
data.tar.gz: 7c6f3a7f2f7b2bfb4e59bfbb49ccee75cfe0a9b82448e4d8804a4dd951740e2b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88ecd436bfc9aa028b9e696ebc4525d018c0563732071c8047535916ee2560f1e3dadf63fd60aaee8d201a205fba1fd81866b598ff96fd3e6558d221dd35d907
|
|
7
|
+
data.tar.gz: c64e06d03256092805e6215536d0b1da6527db831b7e157dc6e6e65535933287444293c30bf7bdd6d31b607f589ac1563e1a186fcce3b8be9e5afb5f8c07f9ad
|
data/CLAUDE.md
CHANGED
|
@@ -47,7 +47,7 @@ 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. Reads the BAG **Indikationscode** (`XXXXX.NN`) from the explicit `indicationCode` extension on each `RegulatedAuthorization.indication[].extension[regulatedAuthorization-limitation]` (BAG SL FHIR export >= v2.0.5; handled from 3.0.10). The BAG changelog states the limitation code (`ClinicalUseDefinition.id`) and the indication code are **independent** fields, so the older derivation — combining each indication CUD's `.NN` id-suffix with the reimbursement RA's `FOPHDossierNumber` — is kept only as a fallback for feeds lacking the extension. Exposed as `item[:indication_codes]` and per-package `:indication_codes` (each entry a `{code:, cud_id:, text:}` hash, where `cud_id` is the `limitationIndication` CUD reference used to resolve the text). From 3.0.7 onwards, `Builder#build_product` emits one `<INDICATION_CODE code="XXXXX.NN" cud_id="DRUG.NN">limitation text</INDICATION_CODE>` child per indication on every `<PRD>` in `oddb_product.xml`; live feed numbers: 539 products / 1,293 codes / 100 % with non-empty indication text. Mandatory on prescriptions/invoices for SL price-model drugs from 2026-07-01 — see issue [#113](https://github.com/zdavatz/oddb2xml/issues/113). **Limitation texts** (3.0.8 onwards): the `regulatedAuthorization-limitation` extension has no inline `limitationText` in the live BAG feed — it carries a `limitationIndication` reference to a `ClinicalUseDefinition` whose `indication.diseaseSymptomProcedure.concept.text` is the actual text. The parser stores the ref as `cud_ref` on each Limitation, `Bundle#cud_text_by_id` resolves DE, and `merge_language` propagates FR/IT from the per-language NDJSON files via the same CUD id. Coverage on the live feed jumped from 0 / 9'108 to 9'108 / 9'108 (issue [#116](https://github.com/zdavatz/oddb2xml/issues/116)).
|
|
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. Reads the BAG **Indikationscode** (`XXXXX.NN`) from the explicit `indicationCode` extension on each `RegulatedAuthorization.indication[].extension[regulatedAuthorization-limitation]` (BAG SL FHIR export >= v2.0.5; handled from 3.0.10). The BAG changelog states the limitation code (`ClinicalUseDefinition.id`) and the indication code are **independent** fields, so the older derivation — combining each indication CUD's `.NN` id-suffix with the reimbursement RA's `FOPHDossierNumber` — is kept only as a fallback for feeds lacking the extension. Exposed as `item[:indication_codes]` and per-package `:indication_codes` (each entry a `{code:, cud_id:, text:}` hash, where `cud_id` is the `limitationIndication` CUD reference used to resolve the text). From 3.0.7 onwards, `Builder#build_product` emits one `<INDICATION_CODE code="XXXXX.NN" cud_id="DRUG.NN">limitation text</INDICATION_CODE>` child per indication on every `<PRD>` in `oddb_product.xml`; live feed numbers: 539 products / 1,293 codes / 100 % with non-empty indication text. Mandatory on prescriptions/invoices for SL price-model drugs from 2026-07-01 — see issue [#113](https://github.com/zdavatz/oddb2xml/issues/113). **Limitation texts** (3.0.8 onwards): the `regulatedAuthorization-limitation` extension has no inline `limitationText` in the live BAG feed — it carries a `limitationIndication` reference to a `ClinicalUseDefinition` whose `indication.diseaseSymptomProcedure.concept.text` is the actual text. The parser stores the ref as `cud_ref` on each Limitation, `Bundle#cud_text_by_id` resolves DE, and `merge_language` propagates FR/IT from the per-language NDJSON files via the same CUD id. Coverage on the live feed jumped from 0 / 9'108 to 9'108 / 9'108 (issue [#116](https://github.com/zdavatz/oddb2xml/issues/116)). **Limitation code / LIMNAMEBAG** (3.0.12 onwards): FHIR has no native BAG limitation code (LIMCD), so `create_limitations_for_package` sets `LimitationCode = cud_ref` (the `limitationIndication` CUD id) instead of `""`. Without this, every FHIR limitation shared an empty `:code`; `Builder#build_artikelstamm` groups its `<LIMITATIONS>` section by code, so all of them collapsed into a single `<LIMITATION>` with an empty `<LIMNAMEBAG>` and only one text survived. Using the CUD id as the key makes each distinct limitation emit and be referenced from its `<PRODUCT>`. The downstream `bin/check_artikelstamm` (`semantic_check.rb`) also crashed on the lone-element output because Ox `:hash_no_attrs` collapses a one-child section into a Hash (and an empty one into nil) — `SemanticCheckXML#get_items` now normalises every section to an Array.
|
|
51
51
|
|
|
52
52
|
8. **Refdata cleanup** (`lib/oddb2xml/refdata_cleanup.rb`) — Compensates for known data-quality issues in upstream Refdata.Articles.xml before they reach the output. Each fix is guarded by a Swissmedic-side heuristic (e.g. comma in `substance_swissmedic` to distinguish mono products from real combinations). Currently fixes the doubled-dose template bug (`X mg / X mg / Stk`). Called from `Builder#apply_refdata_description_cleanups!` at the start of `prepare_articles`. See GitHub issue #112 for the catalogue.
|
|
53
53
|
|
data/Gemfile.lock
CHANGED
data/History.txt
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
=== 3.0.12 / 02.06.2026
|
|
2
|
+
* Artikelstamm: fix FHIR limitations collapsing into a single empty <LIMITATION>. FHIR has no native BAG limitation code (LIMCD), so every limitation was emitted with an empty LIMNAMEBAG. The Artikelstamm groups its <LIMITATIONS> section by code, so all of them collapsed into one entry with an empty name, losing every other limitation text. The CUD id (the limitationIndication reference that already identifies each text) is now used as the limitation code / LIMNAMEBAG, so each distinct limitation is emitted and referenced correctly.
|
|
3
|
+
* check_artikelstamm: fix crash "no implicit conversion of Symbol into Integer" in SemanticCheck. Ox (:hash_no_attrs) collapses a section containing exactly one child element (e.g. a single <LIMITATION>) into a Hash instead of an Array, and an empty section into nil; get_items now normalises every section to an Array so the checks no longer crash on one-element or empty PRODUCTS/LIMITATIONS/ITEMS.
|
|
4
|
+
|
|
1
5
|
=== 3.0.11 / 02.06.2026
|
|
2
6
|
* Artikelstamm: fix crash in the chapter-70 hack. The upstream source page http://www.spezialitaetenliste.ch/varia_De.htm has been rebuilt as a JavaScript single-page app and no longer ships the chapter-70 data table as static HTML, so Chapter70xtractor.parse raised NoMethodError. The chapter-70 (Komplementärarzneimittel) products and their limitations now arrive through the FHIR feed (SL classification "20. KOMPLEMENTÄRARZNEIMITTEL"), so the legacy scraper is now skipped entirely in FHIR mode (the default for --artikelstamm since 3.0.9). For --no-fhir runs the scraper degrades gracefully: it skips non-row nodes and an empty/missing table, logs a warning and returns no items instead of crashing (issue #118).
|
|
3
7
|
|
data/README.md
CHANGED
|
@@ -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/oddb2xml/bin/oddb2xml version 3.0.
|
|
54
|
+
/opt/src/oddb2xml/bin/oddb2xml version 3.0.12
|
|
55
55
|
Usage:
|
|
56
56
|
oddb2xml [option]
|
|
57
57
|
produced files are found under data
|
|
@@ -569,7 +569,14 @@ module Oddb2xml
|
|
|
569
569
|
text_de = lim[:text] || (cud_ref && cud_texts[cud_ref]) || ""
|
|
570
570
|
|
|
571
571
|
limitation = OpenStruct.new
|
|
572
|
-
limitation
|
|
572
|
+
# FHIR has no native BAG limitation code (LIMCD). The CUD id
|
|
573
|
+
# (limitationIndication reference) uniquely identifies each limitation
|
|
574
|
+
# text, so use it as the LIMNAMEBAG key. Without this, every FHIR
|
|
575
|
+
# limitation shares an empty code: the Artikelstamm builder groups its
|
|
576
|
+
# <LIMITATIONS> section by code, collapsing all of them into a single
|
|
577
|
+
# <LIMITATION> with an empty <LIMNAMEBAG> and losing every other text
|
|
578
|
+
# (and crashing the semantic checker on the resulting lone element).
|
|
579
|
+
limitation.LimitationCode = cud_ref || ""
|
|
573
580
|
limitation.LimitationType = "" # Could derive from status
|
|
574
581
|
limitation.LimitationNiveau = "" # Not in FHIR
|
|
575
582
|
limitation.LimitationValue = "" # Not in FHIR
|
|
@@ -31,7 +31,17 @@ module Oddb2xml
|
|
|
31
31
|
# of the whole spec suite
|
|
32
32
|
xx = @hash[:ARTIKELSTAMM] || @hash["ARTIKELSTAMM"]
|
|
33
33
|
comps = xx[component_name.to_sym] || xx[component_name]
|
|
34
|
-
comps.
|
|
34
|
+
return [] unless comps.is_a?(Hash)
|
|
35
|
+
res = comps.values.first
|
|
36
|
+
# Ox (:hash_no_attrs) collapses a section that contains exactly one child
|
|
37
|
+
# element (e.g. a single <LIMITATION>) into a single Hash instead of an
|
|
38
|
+
# Array, and an empty section (<LIMITATIONS/>) into nil/"". Normalise so
|
|
39
|
+
# callers always iterate over an Array of Hashes.
|
|
40
|
+
case res
|
|
41
|
+
when Array then res
|
|
42
|
+
when Hash then [res]
|
|
43
|
+
else []
|
|
44
|
+
end
|
|
35
45
|
end
|
|
36
46
|
|
|
37
47
|
def load_file(name)
|
data/lib/oddb2xml/version.rb
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!--Regression fixture for the Ox single-element collapse: exactly one PRODUCT,
|
|
3
|
+
one LIMITATION and one ITEM. Ox(:hash_no_attrs) returns a single Hash (not an
|
|
4
|
+
Array) for each such section, which crashed SemanticCheck before the get_items
|
|
5
|
+
normalisation. See Jenkins "everyReferencedLimitationIsIncluded" TypeError.-->
|
|
6
|
+
<ARTIKELSTAMM xmlns="http://elexis.ch/Elexis_Artikelstamm_v5" CREATION_DATETIME="2026-06-02T11:00:00.000+01:00" BUILD_DATETIME="2026-06-02T11:00:00.000+01:00" DATA_SOURCE="oddb2xml">
|
|
7
|
+
<PRODUCTS>
|
|
8
|
+
<PRODUCT>
|
|
9
|
+
<PRODNO>2848601</PRODNO>
|
|
10
|
+
<SALECD>A</SALECD>
|
|
11
|
+
<DSCR>Ancopir Inj Lös</DSCR>
|
|
12
|
+
<DSCRF>Ancopir sol inj</DSCRF>
|
|
13
|
+
<ATC>A11DB</ATC>
|
|
14
|
+
<LIMNAMEBAG>070240</LIMNAMEBAG>
|
|
15
|
+
<SUBSTANCE>Verschiedene Kombinationen</SUBSTANCE>
|
|
16
|
+
</PRODUCT>
|
|
17
|
+
</PRODUCTS>
|
|
18
|
+
<LIMITATIONS>
|
|
19
|
+
<LIMITATION>
|
|
20
|
+
<LIMNAMEBAG>070240</LIMNAMEBAG>
|
|
21
|
+
<DSCR>Gesamthaft zugelassen: <b>40</b> Punkte</DSCR>
|
|
22
|
+
<DSCRF>Prescription limitée au maximum à: <b>40</b> points</DSCRF>
|
|
23
|
+
<LIMITATION_PTS>40</LIMITATION_PTS>
|
|
24
|
+
</LIMITATION>
|
|
25
|
+
</LIMITATIONS>
|
|
26
|
+
<ITEMS>
|
|
27
|
+
<ITEM PHARMATYPE="P">
|
|
28
|
+
<GTIN>7680284860144</GTIN>
|
|
29
|
+
<PHAR>0177804</PHAR>
|
|
30
|
+
<SALECD>A</SALECD>
|
|
31
|
+
<DSCR>Ancopir, Injektionslösung</DSCR>
|
|
32
|
+
<DSCRF>--missing--</DSCRF>
|
|
33
|
+
<COMP>
|
|
34
|
+
<NAME>Dr. Grossmann AG, Pharmaca</NAME>
|
|
35
|
+
<GLN/>
|
|
36
|
+
</COMP>
|
|
37
|
+
<PEXF>3.89</PEXF>
|
|
38
|
+
<PPUB>8.55</PPUB>
|
|
39
|
+
<PKG_SIZE>5</PKG_SIZE>
|
|
40
|
+
<MEASURE>Ampulle(n)</MEASURE>
|
|
41
|
+
<MEASUREF>Ampulle(n)</MEASUREF>
|
|
42
|
+
<DOSAGE_FORM>Injektionslösung</DOSAGE_FORM>
|
|
43
|
+
<DOSAGE_FORMF>Solution injectable</DOSAGE_FORMF>
|
|
44
|
+
<SL_ENTRY>true</SL_ENTRY>
|
|
45
|
+
<IKSCAT>B</IKSCAT>
|
|
46
|
+
<DEDUCTIBLE>10</DEDUCTIBLE>
|
|
47
|
+
<PRODNO>2848601</PRODNO>
|
|
48
|
+
</ITEM>
|
|
49
|
+
</ITEMS>
|
|
50
|
+
</ARTIKELSTAMM>
|
data/spec/fhir_spec.rb
CHANGED
|
@@ -157,6 +157,10 @@ describe "FHIR Indikationscode support" do
|
|
|
157
157
|
expect(texts).to include(start_with("In Kombination mit FOLFIRI"))
|
|
158
158
|
# CUD reference is carried through so merge_language can resolve FR/IT.
|
|
159
159
|
expect(pkg[:limitations].map { |l| l[:cud_ref] }).to include("CYRAMZA.01", "CYRAMZA.02")
|
|
160
|
+
# FHIR has no native LIMCD, so the CUD id becomes the limitation code
|
|
161
|
+
# (LIMNAMEBAG). Without this every limitation shares an empty code and the
|
|
162
|
+
# Artikelstamm collapses them into one entry, losing all other texts.
|
|
163
|
+
expect(pkg[:limitations].map { |l| l[:code] }).to include("CYRAMZA.01", "CYRAMZA.02")
|
|
160
164
|
end
|
|
161
165
|
|
|
162
166
|
it "fills DescriptionFr / DescriptionIt from the language-specific bundles" do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: oddb2xml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yasuhiro Asaka, Zeno R.R. Davatz, Niklaus Giger
|
|
@@ -520,6 +520,7 @@ files:
|
|
|
520
520
|
- spec/data/check_artikelstamm/artikelstamm_v5_pharma_has_product.xml
|
|
521
521
|
- spec/data/check_artikelstamm/artikelstamm_v5_prodno_not_uniq.xml
|
|
522
522
|
- spec/data/check_artikelstamm/artikelstamm_v5_product_needs_article.xml
|
|
523
|
+
- spec/data/check_artikelstamm/artikelstamm_v5_single_element_okay.xml
|
|
523
524
|
- spec/data/column_c.txt
|
|
524
525
|
- spec/data/compositions.txt
|
|
525
526
|
- spec/data/compressor/oddb2xml_files_bm_update.txt
|
|
@@ -618,6 +619,7 @@ test_files:
|
|
|
618
619
|
- spec/data/check_artikelstamm/artikelstamm_v5_pharma_has_product.xml
|
|
619
620
|
- spec/data/check_artikelstamm/artikelstamm_v5_prodno_not_uniq.xml
|
|
620
621
|
- spec/data/check_artikelstamm/artikelstamm_v5_product_needs_article.xml
|
|
622
|
+
- spec/data/check_artikelstamm/artikelstamm_v5_single_element_okay.xml
|
|
621
623
|
- spec/data/column_c.txt
|
|
622
624
|
- spec/data/compositions.txt
|
|
623
625
|
- spec/data/compressor/oddb2xml_files_bm_update.txt
|