oddb2xml 3.0.25 → 3.0.27
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/.gitignore +3 -1
- data/CLAUDE.md +15 -2
- data/Elexis_Artikelstamm_v6.xsd +559 -0
- data/Gemfile.lock +1 -1
- data/History.txt +6 -0
- data/README.md +74 -1
- data/lib/oddb2xml/builder.rb +73 -11
- data/lib/oddb2xml/cli.rb +4 -4
- data/lib/oddb2xml/fhir_support.rb +6 -0
- data/lib/oddb2xml/options.rb +1 -1
- data/lib/oddb2xml/version.rb +1 -1
- data/oddb2xml.xsd +15 -0
- data/scripts/generate_index_html.sh +202 -0
- data/scripts/run_oddb2xml.sh +38 -1
- data/scripts/setup_mediupdatexml_web.sh +110 -0
- data/scripts/swissmedic_watch.sh +85 -0
- data/scripts/visitor_stats.py +324 -0
- data/spec/artikelstamm_spec.rb +14 -14
- data/spec/fhir_spec.rb +99 -0
- metadata +6 -1
data/History.txt
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
=== 3.0.27 / 29.06.2026
|
|
2
|
+
* New (-e/--extended, -b/--firstbase): carry the BAG Indikationscode (INDC) per article in oddb_article.xml as well. Each SL price-model <ART> now gets one <INDICATION_CODE code="XXXXX.NN" cud_id="DRUG.NN" limcd="DRUG.NN" vdat="..." vtdat="...">limitation text</INDICATION_CODE> child per indication -- the same data as the v6 Artikelstamm <ARTSL> block (3.0.26), but in the flat <INDICATION_CODE> shape already emitted on <PRD> in oddb_product.xml, enriched with the limitation code (limcd) and validity dates (vdat/vtdat). The bundled oddb2xml.xsd now defines <INDICATION_CODE> and references it from both <ART> and <PRD> (the <PRD> reference was missing since the 3.0.7 product-feed addition). Only emitted in --fhir mode (the default), where limitations carry an indication code. See issue #113.
|
|
3
|
+
|
|
4
|
+
=== 3.0.26 / 29.06.2026
|
|
5
|
+
* New (--artikelstamm): emit the Elexis Artikelstamm v6 format with per-article BAG Indikationscodes (INDC). The output bumps from v5 to v6 -- namespace http://elexis.ch/Elexis_Artikelstamm_v6, file artikelstamm_DDMMYYYY_v6.xml/.csv, validated against the new bundled Elexis_Artikelstamm_v6.xsd -- and every SL price-model <ITEM> now carries an <ARTSL> block: <PM>true</PM> plus one <ARTLIM> per limitation with <LIMCD> (BAG limitation code), <INDCD> (the Indikationscode XXXXX.NN), and the validity dates <VDAT>/<VTDAT>. The indication codes were already read from the BAG SL FHIR feed's indicationCode extension (used since 3.0.7 for oddb_product.xml's <INDICATION_CODE>); they are now also carried per package limitation and grouped onto each article. Mandatory on prescriptions/invoices for SL price-model drugs from 2026-07-01 (issue #113). The v6 XSD is the canonical MEDEVIT schema extended with oddb2xml's historical Italian elements (DSCRI / DOSAGE_FORMI) so the output keeps validating. Consumers must switch from the v5 to the v6 file.
|
|
6
|
+
|
|
1
7
|
=== 3.0.25 / 17.06.2026
|
|
2
8
|
* Bugfix (-b/--firstbase): the GS1 Switzerland firstbase CSV (id.gs1.ch) is served with a UTF-8 BOM. FirstbaseExtractor read it with encoding "UTF-8", so the BOM glued onto the first header ("Gtin") and row["Gtin"] returned nil for every row — every line was skipped as having an empty GTIN. The result: @firstbase came out empty and all NONPHARMA articles from the firstbase feed were silently missing, so oddb_article.xml shipped only the ~17k Refdata/Swissmedic base instead of the ~190k GS1 set. Now read with encoding "bom|utf-8", which strips the BOM (live file: all 192'807 rows parse).
|
|
3
9
|
|
data/README.md
CHANGED
|
@@ -56,7 +56,7 @@ see `--help`.
|
|
|
56
56
|
oddb2xml [option]
|
|
57
57
|
produced files are found under data
|
|
58
58
|
-a, --append Additional target nonpharma
|
|
59
|
-
-r, --artikelstamm Create Artikelstamm Version
|
|
59
|
+
-r, --artikelstamm Create Artikelstamm Version 6 for Elexis >= 3.1
|
|
60
60
|
-c, --compress-ext=<s> format F. {tar.gz|zip}
|
|
61
61
|
-e, --extended pharma, non-pharma plus prices and non-pharma from zurrose.
|
|
62
62
|
Products without EAN-Code will also be listed.
|
|
@@ -337,6 +337,56 @@ drugs from **2026-07-01**; from **2027-01-01** insurers may reject
|
|
|
337
337
|
invoices without it. See issue
|
|
338
338
|
[#113](https://github.com/zdavatz/oddb2xml/issues/113).
|
|
339
339
|
|
|
340
|
+
Since 3.0.26 the same codes are also carried per article in the
|
|
341
|
+
**Elexis Artikelstamm v6** output (`--artikelstamm`). Each SL
|
|
342
|
+
price-model `<ITEM>` gains an `<ARTSL>` block with one `<ARTLIM>` per
|
|
343
|
+
limitation:
|
|
344
|
+
|
|
345
|
+
```xml
|
|
346
|
+
<ITEM PHARMATYPE="P">
|
|
347
|
+
<GTIN>7680543780251</GTIN>
|
|
348
|
+
...
|
|
349
|
+
<PRODNO>58398</PRODNO>
|
|
350
|
+
<ARTSL>
|
|
351
|
+
<PM>true</PM>
|
|
352
|
+
<ARTLIMS>
|
|
353
|
+
<ARTLIM>
|
|
354
|
+
<LIMCD>MABTHERA.01</LIMCD> <!-- BAG limitation code -->
|
|
355
|
+
<INDCD>17079.01</INDCD> <!-- Indikationscode XXXXX.NN -->
|
|
356
|
+
<VDAT>2023-05-01T00:00:00</VDAT>
|
|
357
|
+
</ARTLIM>
|
|
358
|
+
<ARTLIM>
|
|
359
|
+
<LIMCD>MABTHERA.04</LIMCD>
|
|
360
|
+
<INDCD>17079.03</INDCD>
|
|
361
|
+
<VDAT>2026-05-01T00:00:00</VDAT>
|
|
362
|
+
<VTDAT>2026-06-30T00:00:00</VTDAT>
|
|
363
|
+
</ARTLIM>
|
|
364
|
+
</ARTLIMS>
|
|
365
|
+
</ARTSL>
|
|
366
|
+
</ITEM>
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
The output bumps from Artikelstamm v5 to **v6** (namespace
|
|
370
|
+
`http://elexis.ch/Elexis_Artikelstamm_v6`, file
|
|
371
|
+
`artikelstamm_DDMMYYYY_v6.xml`, validated against the bundled
|
|
372
|
+
`Elexis_Artikelstamm_v6.xsd`); consumers must switch to the v6 file.
|
|
373
|
+
|
|
374
|
+
Since 3.0.27 the same indication codes are also carried per article in
|
|
375
|
+
the `-e`/`--extended` and `-b`/`--firstbase` feeds: each SL price-model
|
|
376
|
+
`<ART>` in `oddb_article.xml` gets one `<INDICATION_CODE>` child per
|
|
377
|
+
indication — the flat shape already used on `<PRD>` in
|
|
378
|
+
`oddb_product.xml`, enriched with the limitation code and validity
|
|
379
|
+
dates:
|
|
380
|
+
|
|
381
|
+
```xml
|
|
382
|
+
<ART DT="...">
|
|
383
|
+
...
|
|
384
|
+
<INDICATION_CODE code="17079.01" cud_id="MABTHERA.01"
|
|
385
|
+
limcd="MABTHERA.01" vdat="2023-05-01T00:00:00"
|
|
386
|
+
vtdat="">In Kombination ...</INDICATION_CODE>
|
|
387
|
+
</ART>
|
|
388
|
+
```
|
|
389
|
+
|
|
340
390
|
## Limitation texts in `--fhir` mode
|
|
341
391
|
|
|
342
392
|
In 3.0.8 we fixed empty `<DescriptionDe/Fr/It>` on every `<Limitation>`
|
|
@@ -432,6 +482,29 @@ You can also run
|
|
|
432
482
|
for your currently open Terminal to download and set the Certificate.
|
|
433
483
|
|
|
434
484
|
|
|
485
|
+
## Troubleshooting downloads
|
|
486
|
+
|
|
487
|
+
oddb2xml runs a connectivity precheck and prints an `oddb2xml CONNECTIVITY
|
|
488
|
+
WARNING` listing any source host it cannot reach; set
|
|
489
|
+
`ODDB2XML_SKIP_PROXY_CHECK=1` to silence it.
|
|
490
|
+
|
|
491
|
+
A source host may finish the TLS handshake and then **reset the connection
|
|
492
|
+
before sending any HTTP response** (`Errno::ECONNRESET` / "Connection reset by
|
|
493
|
+
peer"; `wget` reports "Read error (Error in the pull function.) in headers").
|
|
494
|
+
When this happens for only one host while everything else is reachable, and a
|
|
495
|
+
normal browser still opens the site, it is usually an **anti-bot gateway (WAF)
|
|
496
|
+
that blocks by TLS fingerprint or source-IP reputation** rather than an outage
|
|
497
|
+
— a User-Agent change does not help. Mitigations: retry later from the same
|
|
498
|
+
host, run the download from a different egress IP, fetch the affected file with
|
|
499
|
+
a browser-grade client (a TLS-impersonating downloader or a headless browser)
|
|
500
|
+
into `downloads/` and continue with `--skip-download`, or ask the source to
|
|
501
|
+
allow-list the host.
|
|
502
|
+
|
|
503
|
+
The deployment driver `scripts/run_oddb2xml.sh` already retries the whole build
|
|
504
|
+
a few times on such transient failures (`ODDB2XML_RETRIES`,
|
|
505
|
+
`ODDB2XML_RETRY_DELAY`); a persistent per-host block still needs one of the
|
|
506
|
+
mitigations above.
|
|
507
|
+
|
|
435
508
|
## Testing
|
|
436
509
|
|
|
437
510
|
* Calling rake spec runs spec tests.
|
data/lib/oddb2xml/builder.rb
CHANGED
|
@@ -1190,6 +1190,9 @@ module Oddb2xml
|
|
|
1190
1190
|
xml.NINCD nincd
|
|
1191
1191
|
}
|
|
1192
1192
|
end
|
|
1193
|
+
# BAG Indikationscodes for SL price-model drugs (issue #113),
|
|
1194
|
+
# mirroring the v6 Artikelstamm <ARTSL> data in oddb_article.xml.
|
|
1195
|
+
append_indication_codes(xml, pac && pac[:limitations])
|
|
1193
1196
|
end
|
|
1194
1197
|
end
|
|
1195
1198
|
xml.RESULT {
|
|
@@ -1562,9 +1565,66 @@ module Oddb2xml
|
|
|
1562
1565
|
end
|
|
1563
1566
|
end
|
|
1564
1567
|
|
|
1568
|
+
# Normalise a BAG validity date to an xs:dateTime for <VDAT>/<VTDAT>.
|
|
1569
|
+
# A bare date (YYYY-MM-DD) gets midnight appended (unzoned, still a valid
|
|
1570
|
+
# xs:dateTime); a full timestamp is passed through; blank -> nil.
|
|
1571
|
+
def elexis_datetime(str)
|
|
1572
|
+
s = str.to_s.strip
|
|
1573
|
+
return nil if s.empty?
|
|
1574
|
+
s = "#{s}T00:00:00" if /\A\d{4}-\d{2}-\d{2}\z/.match?(s)
|
|
1575
|
+
s
|
|
1576
|
+
end
|
|
1577
|
+
|
|
1578
|
+
# Emit the v6 <ARTSL> block for one ITEM: one <ARTLIM> per limitation that
|
|
1579
|
+
# carries a BAG Indikationscode (INDCD). Mandatory on prescriptions/invoices
|
|
1580
|
+
# for SL price-model drugs from 2026-07-01 (issue #113). <PM> is "true"
|
|
1581
|
+
# because the BAG indication code is required only for price-model SL drugs,
|
|
1582
|
+
# which is exactly the set of items that reach this block.
|
|
1583
|
+
def append_artsl(xml, limitations)
|
|
1584
|
+
artlims = (limitations || []).select { |lim| lim[:indcd] && !lim[:indcd].to_s.empty? }
|
|
1585
|
+
artlims = artlims.uniq { |lim| [lim[:code], lim[:indcd]] }
|
|
1586
|
+
return if artlims.empty?
|
|
1587
|
+
xml.ARTSL do
|
|
1588
|
+
xml.PM "true"
|
|
1589
|
+
xml.ARTLIMS do
|
|
1590
|
+
artlims.each do |lim|
|
|
1591
|
+
xml.ARTLIM do
|
|
1592
|
+
xml.LIMCD lim[:code].to_s
|
|
1593
|
+
xml.INDCD lim[:indcd].to_s
|
|
1594
|
+
if (vdat = elexis_datetime(lim[:vdate]))
|
|
1595
|
+
xml.VDAT vdat
|
|
1596
|
+
end
|
|
1597
|
+
if (vtdat = elexis_datetime(lim[:vtdate]))
|
|
1598
|
+
xml.VTDAT vtdat
|
|
1599
|
+
end
|
|
1600
|
+
end
|
|
1601
|
+
end
|
|
1602
|
+
end
|
|
1603
|
+
end
|
|
1604
|
+
end
|
|
1605
|
+
|
|
1606
|
+
# Emit per-article BAG Indikationscodes for the -e/--extended and
|
|
1607
|
+
# -b/--firstbase article feed (oddb_article.xml), one <INDICATION_CODE> per
|
|
1608
|
+
# limitation that carries an INDCD. Same data as the v6 <ARTSL> block, but
|
|
1609
|
+
# in the flat <INDICATION_CODE> shape already used on <PRD> in
|
|
1610
|
+
# oddb_product.xml — enriched here with the limitation code (LIMCD) and the
|
|
1611
|
+
# validity dates (VDAT/VTDAT). See issue #113.
|
|
1612
|
+
def append_indication_codes(xml, limitations)
|
|
1613
|
+
ics = (limitations || []).select { |lim| lim[:indcd] && !lim[:indcd].to_s.empty? }
|
|
1614
|
+
ics = ics.uniq { |lim| [lim[:code], lim[:indcd]] }
|
|
1615
|
+
ics.each do |lim|
|
|
1616
|
+
xml.INDICATION_CODE lim[:desc_de].to_s,
|
|
1617
|
+
code: lim[:indcd].to_s,
|
|
1618
|
+
cud_id: lim[:cud_ref].to_s,
|
|
1619
|
+
limcd: lim[:code].to_s,
|
|
1620
|
+
vdat: elexis_datetime(lim[:vdate]) || "",
|
|
1621
|
+
vtdat: elexis_datetime(lim[:vtdate]) || ""
|
|
1622
|
+
end
|
|
1623
|
+
end
|
|
1624
|
+
|
|
1565
1625
|
def build_artikelstamm
|
|
1566
|
-
@@
|
|
1567
|
-
@csv_file = CSV.open(File.join(WORK_DIR, "artikelstamm_#{Date.today.strftime("%d%m%Y")}
|
|
1626
|
+
@@emitted_v6_gtins = []
|
|
1627
|
+
@csv_file = CSV.open(File.join(WORK_DIR, "artikelstamm_#{Date.today.strftime("%d%m%Y")}_v6.csv"), "w+")
|
|
1568
1628
|
@csv_file << ["gtin", "name", "pkg_size", "galenic_form", "price_ex_factory", "price_public", "prodno", "atc_code", "active_substance", "original", "it-code", "sl-liste"]
|
|
1569
1629
|
@csv_file.sync = true
|
|
1570
1630
|
variant = "build_artikelstamm"
|
|
@@ -1649,10 +1709,10 @@ module Oddb2xml
|
|
|
1649
1709
|
end
|
|
1650
1710
|
end
|
|
1651
1711
|
info = @calc_items[pkg_gtin]
|
|
1652
|
-
if @@
|
|
1712
|
+
if @@emitted_v6_gtins.index(pkg_gtin)
|
|
1653
1713
|
next
|
|
1654
1714
|
else
|
|
1655
|
-
@@
|
|
1715
|
+
@@emitted_v6_gtins << pkg_gtin.clone
|
|
1656
1716
|
end
|
|
1657
1717
|
options = {"PHARMATYPE" => "P"}
|
|
1658
1718
|
xml.ITEM(options) do
|
|
@@ -1771,6 +1831,8 @@ module Oddb2xml
|
|
|
1771
1831
|
end
|
|
1772
1832
|
end
|
|
1773
1833
|
xml.PRODNO prodno if prodno
|
|
1834
|
+
# v6 <ARTSL>: per-article BAG Indikationscodes (issue #113).
|
|
1835
|
+
append_artsl(xml, package[:limitations])
|
|
1774
1836
|
@csv_file << [pkg_gtin, name, package[:unit], measure,
|
|
1775
1837
|
pexf || "",
|
|
1776
1838
|
ppub || "",
|
|
@@ -1780,10 +1842,10 @@ module Oddb2xml
|
|
|
1780
1842
|
end
|
|
1781
1843
|
end
|
|
1782
1844
|
else # non pharma
|
|
1783
|
-
if @@
|
|
1845
|
+
if @@emitted_v6_gtins.index(ean13)
|
|
1784
1846
|
next
|
|
1785
1847
|
else
|
|
1786
|
-
@@
|
|
1848
|
+
@@emitted_v6_gtins << ean13.clone
|
|
1787
1849
|
end
|
|
1788
1850
|
# Set the pharmatype to 'Y' for outdated products, which are no longer found
|
|
1789
1851
|
# in refdata/packungen
|
|
@@ -1896,7 +1958,7 @@ module Oddb2xml
|
|
|
1896
1958
|
elexis_strftime_format = "%FT%T\.%L%:z"
|
|
1897
1959
|
@@cumul_ver = (Date.today.year - 2013) * 12 + Date.today.month
|
|
1898
1960
|
options_xml = {
|
|
1899
|
-
"xmlns" => "http://elexis.ch/
|
|
1961
|
+
"xmlns" => "http://elexis.ch/Elexis_Artikelstamm_v6",
|
|
1900
1962
|
"CREATION_DATETIME" => Time.new.strftime(elexis_strftime_format),
|
|
1901
1963
|
"BUILD_DATETIME" => Time.new.strftime(elexis_strftime_format),
|
|
1902
1964
|
"DATA_SOURCE" => "oddb2xml"
|
|
@@ -2008,7 +2070,7 @@ module Oddb2xml
|
|
|
2008
2070
|
end
|
|
2009
2071
|
end
|
|
2010
2072
|
end
|
|
2011
|
-
Oddb2xml.log "#{variant}. Done #{nr_products} of #{@products.size} products, #{@limitations.size} limitations and #{nr_articles}/#{@nr_articles} articles. @@
|
|
2073
|
+
Oddb2xml.log "#{variant}. Done #{nr_products} of #{@products.size} products, #{@limitations.size} limitations and #{nr_articles}/#{@nr_articles} articles. @@emitted_v6_gtins #{@@emitted_v6_gtins.size}"
|
|
2012
2074
|
# we don't add a SHA256 hash for each element in the article
|
|
2013
2075
|
# Oddb2xml.add_hash(a_builder.to_xml)
|
|
2014
2076
|
# doc = REXML::Document.new( source, { :raw => :all })
|
|
@@ -2018,13 +2080,13 @@ module Oddb2xml
|
|
|
2018
2080
|
lines << " - #{sprintf("%5d", @limitations.size)} limitations"
|
|
2019
2081
|
lines << " - #{sprintf("%5d", @nr_articles)} articles"
|
|
2020
2082
|
lines << " - #{sprintf("%5d", @@gtin2ignore.size)} ignored GTINS"
|
|
2021
|
-
@@
|
|
2083
|
+
@@articlestamm_v6_info_lines = lines
|
|
2022
2084
|
a_builder.to_xml({indent: 4, encoding: "UTF-8"})
|
|
2023
2085
|
end
|
|
2024
2086
|
|
|
2025
2087
|
private_class_method
|
|
2026
|
-
def self.
|
|
2027
|
-
@@
|
|
2088
|
+
def self.articlestamm_v6_info_lines
|
|
2089
|
+
@@articlestamm_v6_info_lines
|
|
2028
2090
|
end
|
|
2029
2091
|
end
|
|
2030
2092
|
end
|
data/lib/oddb2xml/cli.rb
CHANGED
|
@@ -103,8 +103,8 @@ module Oddb2xml
|
|
|
103
103
|
end
|
|
104
104
|
build
|
|
105
105
|
if @options[:artikelstamm] && system("which xmllint")
|
|
106
|
-
|
|
107
|
-
cmd = "xmllint --noout --schema #{
|
|
106
|
+
elexis_v6_xsd = File.expand_path(File.join(__FILE__, "..", "..", "..", "Elexis_Artikelstamm_v6.xsd"))
|
|
107
|
+
cmd = "xmllint --noout --schema #{elexis_v6_xsd} #{@the_files[:artikelstamm]}"
|
|
108
108
|
if system(cmd)
|
|
109
109
|
puts "Validatied #{@the_files[:artikelstamm]}"
|
|
110
110
|
else
|
|
@@ -399,7 +399,7 @@ module Oddb2xml
|
|
|
399
399
|
@the_files[:calc] = "oddb_calc.xml"
|
|
400
400
|
end
|
|
401
401
|
if @options[:artikelstamm]
|
|
402
|
-
@the_files[:artikelstamm] = "artikelstamm_#{Date.today.strftime("%d%m%Y")}
|
|
402
|
+
@the_files[:artikelstamm] = "artikelstamm_#{Date.today.strftime("%d%m%Y")}_v6.xml"
|
|
403
403
|
elsif @options[:address]
|
|
404
404
|
@the_files[:company] = "#{prefix}_betrieb.xml"
|
|
405
405
|
@the_files[:person] = "#{prefix}_medizinalperson.xml"
|
|
@@ -438,7 +438,7 @@ module Oddb2xml
|
|
|
438
438
|
end
|
|
439
439
|
if @options[:artikelstamm]
|
|
440
440
|
lines << "Generated artikelstamm.xml for Elexis"
|
|
441
|
-
lines += Builder.
|
|
441
|
+
lines += Builder.articlestamm_v6_info_lines
|
|
442
442
|
elsif @options[:address]
|
|
443
443
|
{
|
|
444
444
|
"Betrieb" => :@companies,
|
|
@@ -586,6 +586,10 @@ module Oddb2xml
|
|
|
586
586
|
limitation.LimitationNiveau = "" # Not in FHIR
|
|
587
587
|
limitation.LimitationValue = "" # Not in FHIR
|
|
588
588
|
limitation.LimitationCudRef = cud_ref # carried through for FR/IT resolution
|
|
589
|
+
# BAG Indikationscode (XXXXX.NN): the v6 Artikelstamm <ARTSL>/<ARTLIM>
|
|
590
|
+
# block carries it per article (issue #113). Independent of the CUD id
|
|
591
|
+
# (= LimitationCode), so read the explicit indicationCode extension.
|
|
592
|
+
limitation.IndicationCode = lim[:indication_code] || ""
|
|
589
593
|
limitation.DescriptionDe = text_de
|
|
590
594
|
limitation.DescriptionFr = "" # filled by merge_language from FR bundle
|
|
591
595
|
limitation.DescriptionIt = "" # filled by merge_language from IT bundle
|
|
@@ -819,7 +823,9 @@ module Oddb2xml
|
|
|
819
823
|
desc_fr: lim.DescriptionFr || "",
|
|
820
824
|
desc_it: lim.DescriptionIt || "",
|
|
821
825
|
cud_ref: lim.LimitationCudRef,
|
|
826
|
+
indcd: lim.IndicationCode || "",
|
|
822
827
|
vdate: lim.ValidFromDate || "",
|
|
828
|
+
vtdate: lim.ValidThruDate || "",
|
|
823
829
|
del: is_deleted
|
|
824
830
|
}
|
|
825
831
|
end
|
data/lib/oddb2xml/options.rb
CHANGED
|
@@ -17,7 +17,7 @@ module Oddb2xml
|
|
|
17
17
|
produced files are found under data
|
|
18
18
|
EOS
|
|
19
19
|
opt :append, "Additional target nonpharma", default: false
|
|
20
|
-
opt :artikelstamm, "Create Artikelstamm Version
|
|
20
|
+
opt :artikelstamm, "Create Artikelstamm Version 6 for Elexis >= 3.1"
|
|
21
21
|
opt :compress_ext, "format F. {tar.gz|zip}", type: :string, default: nil, short: "c"
|
|
22
22
|
opt :extended, "pharma, non-pharma plus prices and non-pharma from zurrose.
|
|
23
23
|
Products without EAN-Code will also be listed.
|
data/lib/oddb2xml/version.rb
CHANGED
data/oddb2xml.xsd
CHANGED
|
@@ -169,11 +169,25 @@
|
|
|
169
169
|
<xs:element ref="ns1:ARTBAR"/>
|
|
170
170
|
<xs:element minOccurs="0" maxOccurs="unbounded" ref="ns1:ARTPRI"/>
|
|
171
171
|
<xs:element minOccurs="0" ref="ns1:ARTINS"/>
|
|
172
|
+
<xs:element minOccurs="0" maxOccurs="unbounded" ref="ns1:INDICATION_CODE"/>
|
|
172
173
|
</xs:sequence>
|
|
173
174
|
<xs:attribute name="DT" use="required"/>
|
|
174
175
|
<xs:attribute type="xs:string" name="SHA256" use="required"/>
|
|
175
176
|
</xs:complexType>
|
|
176
177
|
</xs:element>
|
|
178
|
+
<xs:element name="INDICATION_CODE">
|
|
179
|
+
<xs:complexType>
|
|
180
|
+
<xs:simpleContent>
|
|
181
|
+
<xs:extension base="xs:string">
|
|
182
|
+
<xs:attribute name="code" type="xs:string"/>
|
|
183
|
+
<xs:attribute name="cud_id" type="xs:string"/>
|
|
184
|
+
<xs:attribute name="limcd" type="xs:string"/>
|
|
185
|
+
<xs:attribute name="vdat" type="xs:string"/>
|
|
186
|
+
<xs:attribute name="vtdat" type="xs:string"/>
|
|
187
|
+
</xs:extension>
|
|
188
|
+
</xs:simpleContent>
|
|
189
|
+
</xs:complexType>
|
|
190
|
+
</xs:element>
|
|
177
191
|
<xs:element name="PHAR" type="xs:integer"/>
|
|
178
192
|
<xs:element name="SMCAT">
|
|
179
193
|
<xs:simpleType>
|
|
@@ -853,6 +867,7 @@
|
|
|
853
867
|
<xs:element minOccurs="0" ref="ns1:IT"/>
|
|
854
868
|
<xs:element minOccurs="0" ref="ns1:ORPH"/>
|
|
855
869
|
<xs:element ref="ns1:CPT"/>
|
|
870
|
+
<xs:element minOccurs="0" maxOccurs="unbounded" ref="ns1:INDICATION_CODE"/>
|
|
856
871
|
<xs:element minOccurs="0" ref="ns1:PackGrSwissmedic"/>
|
|
857
872
|
<xs:element minOccurs="0" ref="ns1:EinheitSwissmedic"/>
|
|
858
873
|
<xs:element minOccurs="0" ref="ns1:SubstanceSwissmedic"/>
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Generate the mediupdatexml.oddb.org landing page ($DOCROOT/index.html) with
|
|
4
|
+
# live product counts:
|
|
5
|
+
# PHARMA = Swissmedic-registered medicines (<SMNO> in default/oddb_article.xml)
|
|
6
|
+
# NONPHARMA = GTINs in the GS1 firstbase download (firstbase.csv, minus header)
|
|
7
|
+
#
|
|
8
|
+
# Usage: generate_index_html.sh DOCROOT [FIRSTBASE_CSV]
|
|
9
|
+
# DOCROOT where to write index.html (default /home/zdavatz/oddb2xml)
|
|
10
|
+
# FIRSTBASE_CSV the GS1 firstbase CSV (default <DOCROOT>-build/downloads/firstbase.csv)
|
|
11
|
+
#
|
|
12
|
+
# Called from run_oddb2xml.sh (after each build) and from
|
|
13
|
+
# setup_mediupdatexml_web.sh (initial creation). Counts that can't be computed
|
|
14
|
+
# are shown as "—".
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
|
+
DOCROOT="${1:-/home/zdavatz/oddb2xml}"
|
|
20
|
+
FIRSTBASE_CSV="${2:-${DOCROOT%/}-build/downloads/firstbase.csv}"
|
|
21
|
+
ARTICLE_XML="${DOCROOT%/}/default/oddb_article.xml"
|
|
22
|
+
|
|
23
|
+
# Apache combined access log(s) for the visitors/region graph (last STATS_DAYS).
|
|
24
|
+
# Readable by the user only if it is in the "adm" group (sudo usermod -aG adm <user>).
|
|
25
|
+
ACCESS_LOG_GLOB="${ACCESS_LOG_GLOB:-/var/log/apache2/mediupdatexml.oddb.org_access.log*}"
|
|
26
|
+
STATS_DAYS="${STATS_DAYS:-14}"
|
|
27
|
+
|
|
28
|
+
# Swiss-style thousands separator (192807 -> 192'807); "—" passes through.
|
|
29
|
+
group() { [[ "$1" =~ ^[0-9]+$ ]] && printf "%s" "$1" | sed -re ":a;s/([0-9])([0-9]{3})($|[^0-9])/\1'\2\3/;ta" || printf "%s" "$1"; }
|
|
30
|
+
|
|
31
|
+
total="—"
|
|
32
|
+
[[ -f "$ARTICLE_XML" ]] && total=$(grep -c '<ART ' "$ARTICLE_XML" || true)
|
|
33
|
+
|
|
34
|
+
pharma="—"
|
|
35
|
+
[[ -f "$ARTICLE_XML" ]] && pharma=$(grep -c '<SMNO>' "$ARTICLE_XML" || true)
|
|
36
|
+
|
|
37
|
+
nonpharma="—"
|
|
38
|
+
[[ -f "$FIRSTBASE_CSV" ]] && nonpharma=$(( $(wc -l < "$FIRSTBASE_CSV") - 1 ))
|
|
39
|
+
|
|
40
|
+
stand=$(date '+%d.%m.%Y %H:%M')
|
|
41
|
+
|
|
42
|
+
# Visitors/sessions/region graph as a ready-to-embed inline-SVG HTML fragment.
|
|
43
|
+
# Self-contained (pure Python stdlib + cached DB-IP country CSV). Stays empty
|
|
44
|
+
# when the access log is unreadable or has no data, so the page degrades to
|
|
45
|
+
# simply omitting the section.
|
|
46
|
+
stats_fragment=""
|
|
47
|
+
if [[ -f "$SCRIPT_DIR/visitor_stats.py" ]]; then
|
|
48
|
+
stats_fragment=$(python3 "$SCRIPT_DIR/visitor_stats.py" \
|
|
49
|
+
"$ACCESS_LOG_GLOB" "$(dirname "$FIRSTBASE_CSV")" "$STATS_DAYS" 2>/dev/null || true)
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
mkdir -p "$DOCROOT"
|
|
53
|
+
|
|
54
|
+
# Logo (self-contained SVG): brand-blue rounded badge, white pharma/Swiss cross
|
|
55
|
+
# flanked by XML angle brackets "< >". Written atomically next to index.html and
|
|
56
|
+
# used both as the top-right header image and as the favicon.
|
|
57
|
+
logo_tmp="${DOCROOT%/}/.logo.svg.$$"
|
|
58
|
+
cat > "$logo_tmp" <<'SVG'
|
|
59
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="oddb2xml">
|
|
60
|
+
<defs>
|
|
61
|
+
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
|
62
|
+
<stop offset="0" stop-color="#1a6dff"/>
|
|
63
|
+
<stop offset="1" stop-color="#0a3d8f"/>
|
|
64
|
+
</linearGradient>
|
|
65
|
+
</defs>
|
|
66
|
+
<rect width="64" height="64" rx="14" fill="url(#g)"/>
|
|
67
|
+
<g stroke="#e2231a" stroke-width="3.4" stroke-linecap="round" stroke-linejoin="round" fill="none">
|
|
68
|
+
<polyline points="15,24 9,32 15,40"/>
|
|
69
|
+
<polyline points="49,24 55,32 49,40"/>
|
|
70
|
+
</g>
|
|
71
|
+
<g fill="#ffffff">
|
|
72
|
+
<rect x="26.5" y="18" width="11" height="28" rx="2.5"/>
|
|
73
|
+
<rect x="18" y="26.5" width="28" height="11" rx="2.5"/>
|
|
74
|
+
</g>
|
|
75
|
+
</svg>
|
|
76
|
+
SVG
|
|
77
|
+
chmod 644 "$logo_tmp"
|
|
78
|
+
mv -f "$logo_tmp" "${DOCROOT%/}/logo.svg"
|
|
79
|
+
|
|
80
|
+
# Write atomically via temp + mv so the page can be refreshed regardless of who
|
|
81
|
+
# owns the existing index.html (setup runs as root, run_oddb2xml.sh as the user);
|
|
82
|
+
# mv only needs write on the directory, which both have.
|
|
83
|
+
tmp="${DOCROOT%/}/.index.html.$$"
|
|
84
|
+
cat > "$tmp" <<HTML
|
|
85
|
+
<!DOCTYPE html>
|
|
86
|
+
<html lang="de">
|
|
87
|
+
<head>
|
|
88
|
+
<meta charset="utf-8">
|
|
89
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
90
|
+
<title>mediupdatexml.oddb.org — Downloads</title>
|
|
91
|
+
<!-- open every link in a new tab -->
|
|
92
|
+
<base target="_blank">
|
|
93
|
+
<meta name="referrer" content="strict-origin-when-cross-origin">
|
|
94
|
+
<link rel="icon" type="image/svg+xml" href="logo.svg">
|
|
95
|
+
<style>
|
|
96
|
+
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
97
|
+
max-width: 820px; margin: 2.5rem auto; padding: 0 1.2rem; color: #1a1a1a; line-height: 1.5; }
|
|
98
|
+
h1 { font-size: 1.6rem; margin-bottom: .2rem; }
|
|
99
|
+
h2 { font-size: 1.15rem; margin-top: 2rem; border-bottom: 1px solid #ddd; padding-bottom: .3rem; }
|
|
100
|
+
.sub { color: #666; margin-top: 0; }
|
|
101
|
+
ul { list-style: none; padding-left: 0; }
|
|
102
|
+
li { margin: .4rem 0; }
|
|
103
|
+
a { color: #0a58ca; text-decoration: none; }
|
|
104
|
+
a:hover { text-decoration: underline; }
|
|
105
|
+
.desc { color: #666; font-size: .9rem; }
|
|
106
|
+
code { background: #f4f4f4; padding: .1rem .3rem; border-radius: 3px; }
|
|
107
|
+
.stats { display: flex; gap: 1.5rem; margin: 1rem 0; flex-wrap: wrap; }
|
|
108
|
+
.stat { background: #f4f7fb; border: 1px solid #dce4ef; border-radius: 8px; padding: .8rem 1.2rem; }
|
|
109
|
+
.stat .n { font-size: 1.5rem; font-weight: 600; color: #0a58ca; }
|
|
110
|
+
.stat .l { color: #555; font-size: .85rem; }
|
|
111
|
+
footer { margin-top: 3rem; color: #888; font-size: .85rem; }
|
|
112
|
+
ul.firms { columns: 2; column-gap: 2rem; }
|
|
113
|
+
ul.firms li { margin: .25rem 0; break-inside: avoid; }
|
|
114
|
+
.topbar { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; }
|
|
115
|
+
.topbar .title h1 { margin: 0 0 .2rem; }
|
|
116
|
+
.topbar .logo { width: 64px; height: 64px; flex: 0 0 auto; }
|
|
117
|
+
</style>
|
|
118
|
+
</head>
|
|
119
|
+
<body>
|
|
120
|
+
<header class="topbar">
|
|
121
|
+
<div class="title">
|
|
122
|
+
<h1>oddb2xml & aips2sqlite Downloads</h1>
|
|
123
|
+
<p class="sub">Schweizer Arzneimitteldaten — täglich aktualisiert (01:00 Uhr). Stand: ${stand}</p>
|
|
124
|
+
</div>
|
|
125
|
+
<a href="mailto:zdavatz@ywesee.com" title="Fragen? zdavatz at ywesee dot com"><img class="logo" src="logo.svg" alt="oddb2xml Logo" width="64" height="64"></a>
|
|
126
|
+
</header>
|
|
127
|
+
|
|
128
|
+
<div class="stats">
|
|
129
|
+
<div class="stat"><div class="n">$(group "$pharma")</div><div class="l">Medikamente (PHARMA)</div></div>
|
|
130
|
+
<div class="stat"><div class="n"><a href="https://id.gs1.ch/01/07612345000961">$(group "$nonpharma")</a></div><div class="l">Firstbase-Produkte (NONPHARMA)</div></div>
|
|
131
|
+
<div class="stat"><div class="n"><a href="default/oddb_article.xml">$(group "$total")</a></div><div class="l">Artikel total (<a href="default/oddb_article.xml"><code>oddb_article.xml</code></a>)</div></div>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<h2>oddb2xml — Artikel-/Produkt-Feeds (<code>-b</code> firstbase)</h2>
|
|
135
|
+
<ul>
|
|
136
|
+
<li><a href="default/">default/</a> <span class="desc">— ohne Preisaufschlag</span></li>
|
|
137
|
+
<li><a href="45/">45/</a> <span class="desc">— Wiederverkaufspreis +45 %</span></li>
|
|
138
|
+
<li><a href="50/">50/</a> <span class="desc">— Wiederverkaufspreis +50 %</span></li>
|
|
139
|
+
<li><a href="55/">55/</a> <span class="desc">— Wiederverkaufspreis +55 %</span></li>
|
|
140
|
+
</ul>
|
|
141
|
+
<p class="desc">Jedes Verzeichnis enthält die gleichen Dateien (Direktlinks zum <code>default/</code>-Feed):
|
|
142
|
+
<a href="default/oddb_article.xml">oddb_article.xml</a>,
|
|
143
|
+
<a href="default/oddb_product.xml">oddb_product.xml</a>,
|
|
144
|
+
<a href="default/oddb_calc.xml">oddb_calc.xml</a>,
|
|
145
|
+
<a href="default/oddb_interaction.xml">oddb_interaction.xml</a>,
|
|
146
|
+
<a href="default/oddb_limitation.xml">oddb_limitation.xml</a>,
|
|
147
|
+
<a href="default/oddb_substance.xml">oddb_substance.xml</a>,
|
|
148
|
+
<a href="default/oddb_code.xml">oddb_code.xml</a> sowie
|
|
149
|
+
<a href="default/oddb2xml.zip">oddb2xml.zip</a> (alle Dateien gepackt).</p>
|
|
150
|
+
|
|
151
|
+
<h2>aips2sqlite — Fachinformationen & AmiKo-Datenbanken</h2>
|
|
152
|
+
<ul>
|
|
153
|
+
<li><a href="/aips2sqlite/fis/">fis/</a> <span class="desc">— Fachinformationen als XML/HTML (DE/FR/IT)</span></li>
|
|
154
|
+
<li><a href="/aips2sqlite/amiko_db_full_idx_de.db">amiko_db_full_idx_de.db</a> <span class="desc">— AmiKo-Datenbank Deutsch</span></li>
|
|
155
|
+
<li><a href="/aips2sqlite/amiko_db_full_idx_fr.db">amiko_db_full_idx_fr.db</a> <span class="desc">— AmiKo-Datenbank Französisch</span></li>
|
|
156
|
+
<li><a href="/aips2sqlite/oddb2xml_swissmedic_sequences.csv">oddb2xml_swissmedic_sequences.csv</a> <span class="desc">— Swissmedic-Sequenzen</span></li>
|
|
157
|
+
<li><a href="/aips2sqlite/atc_codes_used_set.txt">atc_codes_used_set.txt</a> <span class="desc">— verwendete ATC-Codes</span></li>
|
|
158
|
+
<li><a href="/aips2sqlite/">/aips2sqlite/</a> <span class="desc">— gesamtes Verzeichnis durchsuchen</span></li>
|
|
159
|
+
</ul>
|
|
160
|
+
|
|
161
|
+
<h2>MediUpdate XML bei HIN</h2>
|
|
162
|
+
<ul>
|
|
163
|
+
<li><a href="https://www.hin.ch/de/services/mediupdate-xml.cfm">www.hin.ch/de/services/mediupdate-xml.cfm</a></li>
|
|
164
|
+
</ul>
|
|
165
|
+
|
|
166
|
+
<h2>Softwarehäuser, die oddb2xml einsetzen</h2>
|
|
167
|
+
<ul class="firms">
|
|
168
|
+
<li><a href="https://www.advancedconcepts.ch/">Advanced Concepts AG</a></li>
|
|
169
|
+
<li><a href="https://www.bluecare.ch/">Bluecare AG</a></li>
|
|
170
|
+
<li><a href="https://corona.ch/">Corona Informatik AG</a></li>
|
|
171
|
+
<li><a href="https://www.derma2go.com/de/">derma2go AG</a></li>
|
|
172
|
+
<li><a href="https://www.diagnosia.com/">Diagnosia Internetservices GmbH</a></li>
|
|
173
|
+
<li><a href="https://elexis.ch/glp/index.html">Elexis</a></li>
|
|
174
|
+
<li><a href="https://www.emedswiss.ch/">emedSwiss SA</a></li>
|
|
175
|
+
<li><a href="https://gartenmann.ch/">Gartenmann Software AG</a></li>
|
|
176
|
+
<li><a href="https://hexabit.ch/">Hexabit GmbH</a></li>
|
|
177
|
+
<li><a href="https://www.hausarztmedizin.uzh.ch/de.html">Institut für Hausarztmedizin</a></li>
|
|
178
|
+
<li><a href="https://www.itw-informatik.ch/de/">ITW INFORMATIK AG</a></li>
|
|
179
|
+
<li><a href="https://www.lama-media.com/">Lama Media</a></li>
|
|
180
|
+
<li><a href="https://medab.org/country/ch">MEDAB</a></li>
|
|
181
|
+
<li><a href="https://www.pharmedsolutions.ch/">Pharmed Solutions GmbH</a></li>
|
|
182
|
+
<li><a href="https://praxinova.ch/">Praxinova AG</a></li>
|
|
183
|
+
<li><a href="https://seantis.ch/">seantis gmbh</a></li>
|
|
184
|
+
<li><a href="https://www.geteyesoft.ch/">Siplus SA – Eyesoft</a></li>
|
|
185
|
+
<li><a href="https://swiss-mr.ch/">SMR – Swiss Medical Record GmbH</a></li>
|
|
186
|
+
<li><a href="https://triboni.com/site/">Triboni AG</a></li>
|
|
187
|
+
<li><a href="https://www.vitabyte.ch/">Vitabyte AG</a></li>
|
|
188
|
+
<li><a href="https://zollsoft.de/">zollsoft GmbH</a></li>
|
|
189
|
+
</ul>
|
|
190
|
+
|
|
191
|
+
${stats_fragment}
|
|
192
|
+
|
|
193
|
+
<footer>
|
|
194
|
+
Fragen: <a href="mailto:zdavatz@ywesee.com">zdavatz at ywesee dot com</a> · Tel: 043 540 05 50
|
|
195
|
+
</footer>
|
|
196
|
+
</body>
|
|
197
|
+
</html>
|
|
198
|
+
HTML
|
|
199
|
+
chmod 644 "$tmp"
|
|
200
|
+
mv -f "$tmp" "${DOCROOT%/}/index.html"
|
|
201
|
+
|
|
202
|
+
echo "Wrote ${DOCROOT%/}/index.html (PHARMA=$pharma NONPHARMA=$nonpharma)"
|
data/scripts/run_oddb2xml.sh
CHANGED
|
@@ -38,9 +38,35 @@ BUILD_DIR="${BUILD_DIR:-${OUT_DIR%/}-build}"
|
|
|
38
38
|
INCREMENTS="${INCREMENTS:-45 50 55}"
|
|
39
39
|
ODDB2XML_BIN="${ODDB2XML_BIN:-oddb2xml}"
|
|
40
40
|
TRANSFER_CMD="${TRANSFER_CMD:-$SCRIPT_DIR/transfer.sh}"
|
|
41
|
+
# Transient upstream download failures (e.g. Swissmedic resetting the
|
|
42
|
+
# connection, Errno::ECONNRESET) used to abort the whole nightly run under
|
|
43
|
+
# `set -e`. Retry the oddb2xml build a few times before giving up.
|
|
44
|
+
ODDB2XML_RETRIES="${ODDB2XML_RETRIES:-3}"
|
|
45
|
+
ODDB2XML_RETRY_DELAY="${ODDB2XML_RETRY_DELAY:-120}"
|
|
41
46
|
|
|
42
47
|
log() { printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
|
43
48
|
|
|
49
|
+
# run_with_retry <description> -- <command...>
|
|
50
|
+
# Retry a flaky command up to ODDB2XML_RETRIES times, sleeping
|
|
51
|
+
# ODDB2XML_RETRY_DELAY seconds between attempts. Running the command as the
|
|
52
|
+
# `until` condition keeps it exempt from `set -e`, so a failed attempt retries
|
|
53
|
+
# instead of killing the script; the final failure is propagated via return.
|
|
54
|
+
run_with_retry() {
|
|
55
|
+
local desc="$1"; shift
|
|
56
|
+
[[ "${1:-}" == "--" ]] && shift
|
|
57
|
+
local attempt=1 rc=0
|
|
58
|
+
until "$@"; do
|
|
59
|
+
rc=$?
|
|
60
|
+
if [[ $attempt -ge $ODDB2XML_RETRIES ]]; then
|
|
61
|
+
log "ERROR: $desc failed after $attempt attempts (last exit $rc)"
|
|
62
|
+
return $rc
|
|
63
|
+
fi
|
|
64
|
+
log "WARNING: $desc failed (exit $rc), attempt $attempt/$ODDB2XML_RETRIES; retrying in ${ODDB2XML_RETRY_DELAY}s"
|
|
65
|
+
sleep "$ODDB2XML_RETRY_DELAY"
|
|
66
|
+
attempt=$((attempt + 1))
|
|
67
|
+
done
|
|
68
|
+
}
|
|
69
|
+
|
|
44
70
|
# 1. Install / update the published gem unless told otherwise.
|
|
45
71
|
if [[ "${SKIP_GEM_INSTALL:-0}" != "1" ]]; then
|
|
46
72
|
log "Installing oddb2xml gem"
|
|
@@ -69,7 +95,10 @@ build_one() {
|
|
|
69
95
|
|
|
70
96
|
log "Building increment '${inc:-none}' -> $dest"
|
|
71
97
|
rm -f oddb*.zip
|
|
72
|
-
|
|
98
|
+
# On a retry the first build re-downloads from scratch (dl_opt empty), which
|
|
99
|
+
# also clears any partial download left by the failed attempt.
|
|
100
|
+
run_with_retry "oddb2xml build '${inc:-none}'" -- \
|
|
101
|
+
"$ODDB2XML_BIN" "${dl_opt[@]}" -b "${inc_opt[@]}" -c zip
|
|
73
102
|
|
|
74
103
|
shopt -s nullglob
|
|
75
104
|
local zips=(oddb*.zip)
|
|
@@ -89,6 +118,14 @@ for inc in $INCREMENTS; do
|
|
|
89
118
|
done
|
|
90
119
|
build_one "" "default" # final run with no increment
|
|
91
120
|
|
|
121
|
+
# 2b. Refresh the download landing page with the live PHARMA/NONPHARMA counts
|
|
122
|
+
# (PHARMA from default/oddb_article.xml, NONPHARMA from the GS1 firstbase CSV).
|
|
123
|
+
if [[ -x "$SCRIPT_DIR/generate_index_html.sh" ]]; then
|
|
124
|
+
log "Refreshing landing page index.html"
|
|
125
|
+
"$SCRIPT_DIR/generate_index_html.sh" "$OUT_DIR" "$BUILD_DIR/downloads/firstbase.csv" || \
|
|
126
|
+
log "WARNING: could not regenerate index.html"
|
|
127
|
+
fi
|
|
128
|
+
|
|
92
129
|
# 3. Optional hand-off to the transfer step (scripts/transfer.sh).
|
|
93
130
|
if [[ "${RUN_TRANSFER:-0}" == "1" ]]; then
|
|
94
131
|
log "Running transfer: $TRANSFER_CMD"
|