oddb2xml 2.7.1 → 2.7.2
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/.github/workflows/ruby.yml +1 -1
- data/.standard.yml +2 -0
- data/Gemfile +3 -3
- data/History.txt +8 -0
- data/README.md +1 -1
- 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 +1070 -1038
- data/lib/oddb2xml/calc.rb +232 -233
- data/lib/oddb2xml/chapter_70_hack.rb +38 -32
- data/lib/oddb2xml/cli.rb +252 -236
- 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 +153 -127
- data/lib/oddb2xml/extractor.rb +302 -289
- 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 +163 -163
- data/lib/oddb2xml/version.rb +1 -1
- data/lib/oddb2xml/xml_definitions.rb +32 -33
- data/oddb2xml.gemspec +31 -32
- data/spec/artikelstamm_spec.rb +111 -110
- data/spec/builder_spec.rb +489 -505
- 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/transfer.dat +1 -0
- data/spec/data_helper.rb +47 -49
- data/spec/downloader_spec.rb +247 -260
- data/spec/extractor_spec.rb +171 -159
- 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 +154 -153
- data/test_options.rb +39 -42
- data/tools/win_fetch_cacerts.rb +2 -3
- metadata +19 -3
data/lib/oddb2xml/compare.rb
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
|
2
|
-
require 'xmlsimple'
|
1
|
+
require "xmlsimple"
|
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 StammXML
|
12
12
|
V3_NAME_REG = /_([N,P])_/
|
13
13
|
attr_accessor :components
|
14
14
|
attr_reader :keys, :sub_key_names, :filename, :basename, :version, :hash
|
15
|
-
def initialize(filename, components =
|
15
|
+
def initialize(filename, components = ["ITEMS"])
|
16
16
|
raise "File #{filename} must exist" unless File.exist?(filename)
|
17
17
|
@filename = filename
|
18
18
|
@basename = File.basename(filename)
|
19
|
-
@version =
|
19
|
+
@version = V3_NAME_REG.match(filename) ? 3 : 5
|
20
20
|
@components = components
|
21
21
|
if @version == 5
|
22
22
|
@hash = load_file(@filename)
|
@@ -24,41 +24,47 @@ module Oddb2xml
|
|
24
24
|
raise "Unsupported version #{@version}"
|
25
25
|
end
|
26
26
|
end
|
27
|
+
|
27
28
|
def self.get_component_key_name(component_name)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
return "LIMNAMEBAG" if /LIMITATION/i.match?(component_name)
|
30
|
+
return "PRODNO" if /PRODUCT/i.match?(component_name)
|
31
|
+
return "GTIN" if /ITEM/i.match?(component_name)
|
32
|
+
raise "Cannot determine keyname for component #{component_name}"
|
32
33
|
end
|
34
|
+
|
33
35
|
def get_limitation_from_v5(item)
|
34
|
-
get_item(
|
36
|
+
get_item("PRODUCTS", item["PRODNO"].first.to_i)["LIMNAMEBAG"] ? ["true"] : nil
|
35
37
|
end
|
38
|
+
|
36
39
|
def get_field_from_v5_product(item, field_name)
|
37
|
-
get_item(
|
40
|
+
get_item("PRODUCTS", item["PRODNO"].first.to_i)[field_name]
|
38
41
|
end
|
42
|
+
|
39
43
|
def get_items(component_name)
|
40
44
|
if @version == 3
|
41
|
-
|
45
|
+
@hash[component_name]
|
42
46
|
else
|
43
|
-
|
47
|
+
@hash[component_name].first.values.first
|
44
48
|
end
|
45
|
-
items
|
46
49
|
end
|
50
|
+
|
47
51
|
def get_item(component_name, id)
|
48
52
|
keyname = StammXML.get_component_key_name(component_name)
|
49
|
-
get_items(component_name).find{|item| item[keyname].first.to_i == id}
|
53
|
+
get_items(component_name).find { |item| item[keyname].first.to_i == id }
|
50
54
|
end
|
55
|
+
|
51
56
|
def load_file(name)
|
52
|
-
Oddb2xml.log_timestamp "Reading #{name} #{(File.size(name)/1024/1024).to_i} MB. This may take some time"
|
57
|
+
Oddb2xml.log_timestamp "Reading #{name} #{(File.size(name) / 1024 / 1024).to_i} MB. This may take some time"
|
53
58
|
XmlSimple.xml_in(IO.read(name))
|
54
59
|
end
|
55
60
|
end
|
61
|
+
|
56
62
|
class CompareV5
|
57
63
|
DEFAULTS = {
|
58
|
-
:
|
59
|
-
:
|
60
|
-
:
|
61
|
-
:
|
64
|
+
components: ["PRODUCTS", "LIMITATIONS", "ITEMS"],
|
65
|
+
fields_to_ignore: ["COMP", "DOSAGE_FORMF", "MEASUREF"],
|
66
|
+
fields_as_floats: ["PEXT", "PEXF", "PPUB"],
|
67
|
+
min_diff_for_floats: 0.01
|
62
68
|
}
|
63
69
|
def initialize(left, right, options = DEFAULTS.clone)
|
64
70
|
@options = options
|
@@ -68,37 +74,38 @@ module Oddb2xml
|
|
68
74
|
@occurrences = {}
|
69
75
|
@report = []
|
70
76
|
end
|
71
|
-
|
72
|
-
|
77
|
+
|
78
|
+
def get_keys(items, key = "GTIN")
|
79
|
+
items.collect { |item| item[key].first.to_i }
|
73
80
|
end
|
81
|
+
|
74
82
|
def get_names(items)
|
75
|
-
|
83
|
+
items.collect { |item| item.keys }.flatten.uniq.sort
|
76
84
|
end
|
85
|
+
|
77
86
|
def compare
|
78
87
|
show_header("Start comparing #{@left.filename} with #{@right.filename}")
|
79
88
|
(@left.components & @right.components).each do |name|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
(
|
96
|
-
compare_details(name, compare_names, id)
|
97
|
-
end
|
98
|
-
key_results_details(name, compare_names, l_keys, r_keys)
|
99
|
-
rescue => error
|
100
|
-
puts "Execution failed with #{error}"
|
89
|
+
puts "\n#{Time.now.strftime("%H:%M:%S")}: Comparing #{name} in #{@left.basename} with #{@right.basename}"
|
90
|
+
key = StammXML.get_component_key_name(name)
|
91
|
+
left_items = @left.get_items(name)
|
92
|
+
next unless left_items
|
93
|
+
right_items = @right.get_items(name)
|
94
|
+
next unless right_items
|
95
|
+
@diff_stat[name] = {}
|
96
|
+
@occurrences[name] = {}
|
97
|
+
@diff_stat[name][NR_COMPARED] = 0
|
98
|
+
l_names = get_names(left_items)
|
99
|
+
r_names = get_names(right_items)
|
100
|
+
compare_names = l_names & r_names
|
101
|
+
l_keys = get_keys(left_items, key)
|
102
|
+
r_keys = get_keys(right_items, key)
|
103
|
+
(l_keys & r_keys).each do |id|
|
104
|
+
compare_details(name, compare_names, id)
|
101
105
|
end
|
106
|
+
key_results_details(name, compare_names, l_keys, r_keys)
|
107
|
+
rescue => error
|
108
|
+
puts "Execution failed with #{error}"
|
102
109
|
end
|
103
110
|
show_header("Summary comparing #{@left.filename} with #{@right.filename}")
|
104
111
|
puts "Ignored differences in #{@options[:fields_to_ignore]}. Signaled when differences in #{@options[:fields_as_floats]} were bigger than #{@options[:min_diff_for_floats]}"
|
@@ -116,28 +123,31 @@ module Oddb2xml
|
|
116
123
|
puts "Execution failed with #{error}"
|
117
124
|
raise error
|
118
125
|
end
|
126
|
+
|
119
127
|
private
|
120
|
-
|
121
|
-
|
128
|
+
|
129
|
+
NR_COMPARED = "NR_COMPARED"
|
130
|
+
COUNT = "_count"
|
122
131
|
def show_header(header)
|
123
132
|
text = Oddb2xml.log_timestamp(header)
|
124
133
|
pad = 5
|
125
134
|
puts
|
126
|
-
puts
|
127
|
-
puts
|
128
|
-
puts
|
135
|
+
puts "-" * (text.length + 2 * pad)
|
136
|
+
puts "".ljust(pad) + text
|
137
|
+
puts "-" * (text.length + 2 * pad)
|
129
138
|
puts
|
130
139
|
end
|
140
|
+
|
131
141
|
def compare_details(component_name, compare_names, id)
|
132
142
|
l_item = @left.get_item(component_name, id)
|
133
143
|
r_item = @right.get_item(component_name, id)
|
134
144
|
found_one = false
|
135
145
|
length = 32
|
136
146
|
found = false
|
137
|
-
detail_name = l_item[
|
147
|
+
detail_name = l_item["DSCR"] ? l_item["DSCR"].first[0..length - 1].rjust(length) : "".rjust(length)
|
138
148
|
details = "Diff in #{id.to_s.ljust(15)} #{detail_name}"
|
139
149
|
diff_name = component_name
|
140
|
-
diff_name +=
|
150
|
+
diff_name += "S" unless /S$/.match?(diff_name)
|
141
151
|
@diff_stat[diff_name] ||= {}
|
142
152
|
@occurrences[diff_name] ||= {}
|
143
153
|
@diff_stat[diff_name][NR_COMPARED] ||= 0
|
@@ -154,33 +164,34 @@ module Oddb2xml
|
|
154
164
|
r_float = r_value ? r_value.first.to_f : 0.0
|
155
165
|
next if (l_float - r_float).abs < @options[:min_diff_for_floats]
|
156
166
|
end
|
157
|
-
next if (r_value.is_a?(Array) &&
|
158
|
-
|
167
|
+
next if (r_value.is_a?(Array) && "--missing--".eql?(r_value.first)) || (l_value.is_a?(Array) && "--missing--".eql?(l_value.first))
|
168
|
+
# TODO: get_field_from_v5_product
|
159
169
|
next if r_value.to_s.eql?(l_value.to_s)
|
160
170
|
next if r_value.to_s.upcase.eql?(l_value.to_s.upcase) && @options[:case_insensitive]
|
161
171
|
details += " #{sub_key}: '#{l_value}' != '#{r_value}'"
|
162
172
|
found = found_one = true
|
163
173
|
@diff_stat[diff_name][sub_key] += 1
|
164
174
|
end
|
165
|
-
puts details.gsub(/[\[\]]/,
|
175
|
+
puts details.gsub(/[\[\]]/, "") if found
|
166
176
|
end
|
167
177
|
|
168
178
|
def show_keys(keys, batch_size = 20)
|
169
179
|
0.upto(keys.size) do |idx|
|
170
180
|
next unless idx % batch_size == 0
|
171
|
-
puts
|
181
|
+
puts " " + keys[idx..(idx + batch_size - 1)].join(" ")
|
172
182
|
end
|
173
183
|
end
|
184
|
+
|
174
185
|
def key_results_details(component_name, compare_names, l_keys, r_keys)
|
175
|
-
component_name +=
|
176
|
-
@report <<
|
186
|
+
component_name += "S" unless /S$/.match?(component_name)
|
187
|
+
@report << "#{component_name}: Found #{l_keys.size} items only in #{@left.basename} #{r_keys.size} items only in #{@right.basename}, compared #{@diff_stat[component_name][NR_COMPARED]} items"
|
177
188
|
keys = r_keys - l_keys
|
178
|
-
head = "#{component_name}: #{
|
189
|
+
head = "#{component_name}: #{keys.size} keys only in #{@right.basename}"
|
179
190
|
puts "#{head}: Keys were #{keys.size}"
|
180
191
|
show_keys(keys)
|
181
192
|
@report << head
|
182
193
|
keys = l_keys - r_keys
|
183
|
-
head = "#{component_name}: #{
|
194
|
+
head = "#{component_name}: #{keys.size} keys only in #{@left.basename}"
|
184
195
|
puts "#{head}: Keys were #{keys.size}"
|
185
196
|
show_keys(keys)
|
186
197
|
@report << head
|
@@ -1,554 +1,572 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
# This file is shared since oddb2xml 2.0.0 (lib/oddb2xml/parse_compositions.rb)
|
4
2
|
# with oddb.org src/plugin/parse_compositions.rb
|
5
3
|
#
|
6
4
|
# It allows an easy parsing of the column P Zusammensetzung of the swissmedic packages.xlsx file
|
7
5
|
#
|
8
6
|
|
9
|
-
require
|
10
|
-
require
|
11
|
-
include Parslet
|
7
|
+
require "parslet"
|
8
|
+
require "parslet/convenience"
|
12
9
|
|
13
10
|
class CompositionParser < Parslet::Parser
|
14
|
-
|
11
|
+
include Parslet
|
15
12
|
# Single character rules
|
16
|
-
rule(:lparen)
|
17
|
-
rule(:rparen)
|
18
|
-
rule(:comma)
|
13
|
+
rule(:lparen) { str("(") }
|
14
|
+
rule(:rparen) { str(")") }
|
15
|
+
rule(:comma) { str(",") }
|
19
16
|
|
20
|
-
rule(:space)
|
21
|
-
rule(:space?)
|
17
|
+
rule(:space) { match('\s').repeat(1) }
|
18
|
+
rule(:space?) { space.maybe }
|
22
19
|
|
23
20
|
# Things
|
24
|
-
rule(:digit) { match(
|
21
|
+
rule(:digit) { match("[0-9]") }
|
25
22
|
rule(:digits) { digit.repeat(1) }
|
26
23
|
rule(:number) {
|
27
24
|
(
|
28
|
-
str(
|
29
|
-
str(
|
25
|
+
str("-").maybe >> (
|
26
|
+
str("0") | (match("[1-9]") >> match("[0-9']").repeat)
|
30
27
|
) >> (
|
31
|
-
(str(
|
32
|
-
(match([
|
33
|
-
|
34
|
-
match(
|
28
|
+
(str("*") >> digit.repeat(1)).maybe >>
|
29
|
+
(match([".,^"]) >> digit.repeat(1)).repeat(1)
|
30
|
+
).maybe >> (
|
31
|
+
match("[eE]") >> (str("+") | str("-")).maybe >> digit.repeat(1)
|
35
32
|
).maybe
|
36
33
|
)
|
37
34
|
}
|
38
|
-
rule(:radio_isotop) {
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
rule(:radio_isotop) {
|
36
|
+
match["a-zA-Z"].repeat(1) >> lparen >> digits >> str("-") >> match["a-zA-Z"].repeat(1 - 3) >> rparen >>
|
37
|
+
((space? >> match["a-zA-Z"]).repeat(1)).repeat(0)
|
38
|
+
} # e.g. Xenonum (133-Xe) or yttrii(90-Y) chloridum zum Kalibrierungszeitpunkt
|
39
|
+
rule(:ratio_value) { match['0-9:\-\.,'].repeat(1) >> space? } # eg. ratio: 1:1, ratio: 1:1.5-2.4., ratio: 1:0.68-0.95, 1:4,1
|
42
40
|
|
43
41
|
# handle stuff like acidum 9,11-linolicum or 2,2'-methylen-bis(6-tert.-butyl-4-methyl-phenolum) specially. it must contain at least one a-z
|
44
|
-
rule(:umlaut) { match([
|
45
|
-
rule(:identifier_D12) { match[
|
42
|
+
rule(:umlaut) { match(["éàèèçïöäüâ"]) }
|
43
|
+
rule(:identifier_D12) { match["a-zA-Z"] >> match["0-9"].repeat(1) }
|
46
44
|
|
47
45
|
# TODO: why do we have to hard code these identifiers?
|
48
46
|
rule(:fix_coded_identifiers) {
|
49
47
|
str("2,2'-methylen-bis(6-tert.-butyl-4-methyl-phenolum)") |
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
48
|
+
str("A + B") |
|
49
|
+
str("CRM 197") |
|
50
|
+
str("F.E.I.B.A.") |
|
51
|
+
str("LA ") >> digit.repeat(1, 2) >> str("% TM") |
|
52
|
+
str("TM:") | str("&") |
|
53
|
+
str("ethanol.") |
|
54
|
+
str("poloxamerum 238") |
|
55
|
+
str("polysorbatum ") >> digit >> digit
|
58
56
|
}
|
59
57
|
|
60
58
|
# TODO: Sind diese Abkürzung wirklich Teil eines Substanznamens?
|
61
|
-
rule(:identifier_abbrev_with_comma)
|
62
|
-
str(
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
59
|
+
rule(:identifier_abbrev_with_comma) {
|
60
|
+
str("aquos") |
|
61
|
+
str("ca.") |
|
62
|
+
str("deklar.") |
|
63
|
+
str("spag.") |
|
64
|
+
str("spec.") |
|
65
|
+
str("spp.") |
|
66
|
+
str("ssp.") |
|
67
|
+
str("var.")
|
70
68
|
}
|
71
69
|
rule(:fix_coded_doses) {
|
72
|
-
digit >> digit.maybe >> space >> str(
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
}
|
77
|
-
rule(:identifier)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
70
|
+
digit >> digit.maybe >> space >> str("per centum ") >> str("q.s.").maybe |
|
71
|
+
str("50/50") |
|
72
|
+
str("1g/9.6 cm²") |
|
73
|
+
str("9 g/L 5.4 ml")
|
74
|
+
}
|
75
|
+
rule(:identifier) {
|
76
|
+
fix_coded_identifiers |
|
77
|
+
identifier_abbrev_with_comma |
|
78
|
+
fix_coded_doses |
|
79
|
+
str("q.s.") |
|
80
|
+
identifier_D12 |
|
81
|
+
identifier_without_comma |
|
82
|
+
identifier_with_comma
|
83
|
+
}
|
85
84
|
|
86
85
|
rule(:identifier_with_comma) {
|
87
|
-
match['0-9,\-'].repeat(0) >> (match[
|
86
|
+
match['0-9,\-'].repeat(0) >> (match["a-zA-Z"] | umlaut) >> (match(["_,"]).maybe >> (match['0-9a-zA-Z\-\'\/'] | umlaut)).repeat(0)
|
88
87
|
}
|
89
88
|
|
90
89
|
rule(:identifier_without_comma) {
|
91
|
-
match['0-9\',\-'].repeat(0) >> (match[
|
92
|
-
|
90
|
+
match['0-9\',\-'].repeat(0) >> (match["a-zA-Z"] | umlaut) >> (match(["_"]).maybe >> (match['0-9a-zA-Z\-\'\/'] | umlaut)).repeat(0) >>
|
91
|
+
lparen >> (rparen.absent? >> any).repeat(1) >> rparen
|
93
92
|
}
|
94
|
-
rule(:one_word) { match[
|
93
|
+
rule(:one_word) { match["a-zA-Z"] >> match["0-9"].repeat(1) | match["a-zA-Z"].repeat(1) }
|
95
94
|
rule(:in_parent) { lparen >> one_word.repeat(1) >> rparen }
|
96
95
|
rule(:words_nested) { one_word.repeat(1) >> in_parent.maybe >> space? >> one_word.repeat(0) }
|
97
96
|
# dose
|
98
97
|
# 150 U.I. hFSH et 150 U.I. hLH
|
99
98
|
rule(:units) {
|
100
|
-
|
101
|
-
str(
|
102
|
-
str(
|
103
|
-
str(
|
104
|
-
str(
|
105
|
-
str(
|
106
|
-
str(
|
107
|
-
str(
|
108
|
-
str(
|
109
|
-
str(
|
110
|
-
str(
|
111
|
-
str(
|
112
|
-
str(
|
113
|
-
str(
|
114
|
-
str(
|
115
|
-
str(
|
116
|
-
str(
|
117
|
-
str(
|
118
|
-
str(
|
119
|
-
str(
|
120
|
-
str(
|
121
|
-
str(
|
122
|
-
str(
|
123
|
-
str(
|
124
|
-
str(
|
125
|
-
str(
|
126
|
-
str(
|
127
|
-
str(
|
128
|
-
str(
|
129
|
-
str(
|
130
|
-
str(
|
131
|
-
str(
|
132
|
-
str(
|
133
|
-
str(
|
134
|
-
str(
|
135
|
-
str(
|
136
|
-
str(
|
137
|
-
str(
|
138
|
-
str(
|
139
|
-
str(
|
140
|
-
str(
|
141
|
-
str(
|
142
|
-
str(
|
143
|
-
str(
|
144
|
-
str(
|
145
|
-
str(
|
146
|
-
str(
|
147
|
-
str(
|
148
|
-
str(
|
99
|
+
str("cm²") |
|
100
|
+
str("g/dm²") |
|
101
|
+
str("g/l") |
|
102
|
+
str("g/L") |
|
103
|
+
str("% V/V") |
|
104
|
+
str("µg/24 h") |
|
105
|
+
str("µg/g") |
|
106
|
+
str("µg") |
|
107
|
+
str("ng") |
|
108
|
+
str("guttae") |
|
109
|
+
str("mg/g") |
|
110
|
+
str("mg/ml") |
|
111
|
+
str("MBq/ml") |
|
112
|
+
str("MBq") |
|
113
|
+
str("CFU") |
|
114
|
+
str("mg") |
|
115
|
+
str("Mg") |
|
116
|
+
str("kJ") |
|
117
|
+
str("G") |
|
118
|
+
str("g") |
|
119
|
+
str("I.E.") |
|
120
|
+
str("l") |
|
121
|
+
str("µl") |
|
122
|
+
str("U. Ph. Eur.") |
|
123
|
+
str("ml") |
|
124
|
+
str("µmol") |
|
125
|
+
str("mmol/l") |
|
126
|
+
str("mmol") |
|
127
|
+
str("Mio CFU") |
|
128
|
+
str("Mio U.I.") |
|
129
|
+
str("Mio U.") |
|
130
|
+
str("Mio. U.I.") |
|
131
|
+
str("Mio. U.") |
|
132
|
+
str("Mia. U.I.") |
|
133
|
+
str("Mia. U.") |
|
134
|
+
str("S.U.") |
|
135
|
+
str("U. Botox") |
|
136
|
+
str("U.I. hFSH") |
|
137
|
+
str("U.I. hCG") |
|
138
|
+
str("U.I. hLH") |
|
139
|
+
str("U.I.") |
|
140
|
+
str("U./ml") |
|
141
|
+
str("U.K.I.") |
|
142
|
+
str("U.") |
|
143
|
+
str("Mia.") |
|
144
|
+
str("Mrd.") |
|
145
|
+
str("% m/m") |
|
146
|
+
str("% m/m") |
|
147
|
+
str("%")
|
149
148
|
}
|
150
149
|
rule(:dose_unit) { units.as(:unit) }
|
151
|
-
rule(:qty_range)
|
152
|
-
rule(:qty_unit)
|
153
|
-
rule(:dose_qty)
|
154
|
-
rule(:min_max)
|
150
|
+
rule(:qty_range) { (number >> space? >> (str("+/-") | str(" - ") | str(" -") | str("-") | str("±")) >> space? >> number).as(:qty_range) }
|
151
|
+
rule(:qty_unit) { dose_qty >> (space >> dose_unit).maybe }
|
152
|
+
rule(:dose_qty) { number.as(:qty) }
|
153
|
+
rule(:min_max) { (str("min.") | str("max.") | str("ca.") | str("<")) >> space? }
|
155
154
|
# 75 U.I. hFSH et 75 U.I. hLH
|
156
|
-
rule(:dose_fsh) { qty_unit >> space >> str(
|
157
|
-
rule(:dose_per) { (digits >> str(
|
158
|
-
rule(:dose)
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
155
|
+
rule(:dose_fsh) { qty_unit >> space >> str("et") >> space >> qty_unit.as(:dose_right) }
|
156
|
+
rule(:dose_per) { (digits >> str("/") >> digits).as(:qty) }
|
157
|
+
rule(:dose) {
|
158
|
+
dose_fsh |
|
159
|
+
dose_per |
|
160
|
+
(min_max.maybe >>
|
161
|
+
((qty_range >> (space >> dose_unit).maybe) | (qty_unit | dose_qty | dose_unit)) >> space?) >>
|
162
|
+
str("pro dosi").maybe >> space?
|
163
|
+
}
|
164
|
+
rule(:dose_with_unit) {
|
165
|
+
min_max.maybe >>
|
166
|
+
dose_fsh |
|
167
|
+
(qty_range >> space >> dose_unit |
|
168
|
+
dose_qty >> space >> dose_unit
|
169
|
+
) >>
|
170
|
+
space?
|
171
|
+
}
|
172
|
+
rule(:operator) { match("[+]") >> space? }
|
172
173
|
|
173
174
|
# Grammar parts
|
174
|
-
rule(:useage) {
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
175
|
+
rule(:useage) {
|
176
|
+
(any >> str("berzug:")) | # match Überzug
|
177
|
+
str("antiox.:") |
|
178
|
+
str("arom.:") |
|
179
|
+
str("conserv.:") |
|
180
|
+
str("color.:")
|
181
|
+
}
|
180
182
|
|
181
183
|
# Match Wirkstoffe like E 270
|
182
|
-
rule(:lebensmittel_zusatz) {
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
rule(:der_identifier) { str(
|
184
|
+
rule(:lebensmittel_zusatz) {
|
185
|
+
str("E").as(:lebensmittel_zusatz) >> space >>
|
186
|
+
(digits >> match["(a-z)"].repeat(0, 3)).as(:digits) >>
|
187
|
+
(space >> dose.as(:dose_lebensmittel_zusatz)).maybe >> space?
|
188
|
+
}
|
189
|
+
# DER: 1:4 or DER: 3.5:1 or DER: 6-8:1 or DER: 4.0-9.0:1'
|
190
|
+
rule(:der_identifier) { str("DER:") >> space >> digit >> match['0-9\.\-:'].repeat }
|
189
191
|
rule(:der) { (der_identifier).as(:substance_name) >> space? >> dose.maybe.as(:dose) }
|
190
192
|
rule(:forbidden_in_substance_name) {
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
193
|
+
min_max |
|
194
|
+
useage |
|
195
|
+
excipiens_identifiers |
|
196
|
+
pro_identifiers |
|
197
|
+
corresp_substance_label |
|
198
|
+
(digits.repeat(1) >> space >> str(":")) | # match 50 %
|
199
|
+
str(", corresp.") |
|
200
|
+
str("Beutel: ") |
|
201
|
+
str("Mio ") |
|
202
|
+
str("ad emulsionem") |
|
203
|
+
str("ad globulos") |
|
204
|
+
str("ad pulverem") |
|
205
|
+
str("ad q.s. ") |
|
206
|
+
str("ad solutionem") |
|
207
|
+
str("ad suspensionem") |
|
208
|
+
str("ana partes") |
|
209
|
+
str("ana ") |
|
210
|
+
str("aqua ad ") |
|
211
|
+
str("aqua q.s. ") |
|
212
|
+
str("corresp. ca.,") |
|
213
|
+
str("et ") |
|
214
|
+
str("excipiens") |
|
215
|
+
str("partes") |
|
216
|
+
str("pro capsula") |
|
217
|
+
str("pro dosi") |
|
218
|
+
str("pro vitroe") |
|
219
|
+
str("q.s. ad ") |
|
220
|
+
str("q.s. pro ") |
|
221
|
+
str("ratio:") |
|
222
|
+
str("ut alia: ") |
|
223
|
+
str("ut ")
|
224
|
+
}
|
223
225
|
rule(:name_without_parenthesis) {
|
224
226
|
(
|
225
|
-
(str(
|
226
|
-
(radio_isotop | str(
|
227
|
+
(str("(") | forbidden_in_substance_name).absent? >>
|
228
|
+
(radio_isotop | str("> 1000") | str("> 500") | identifier.repeat(1) >> str(".").maybe) >>
|
227
229
|
space?
|
228
230
|
).repeat(1)
|
229
231
|
}
|
230
232
|
|
231
|
-
rule(:part_with_parenthesis) {
|
232
|
-
|
233
|
-
|
233
|
+
rule(:part_with_parenthesis) {
|
234
|
+
lparen >> ((lparen | rparen).absent? >> any).repeat(1) >>
|
235
|
+
(part_with_parenthesis | rparen >> str("-like:") | rparen) >> space?
|
236
|
+
}
|
234
237
|
rule(:name_with_parenthesis) {
|
235
238
|
forbidden_in_substance_name.absent? >>
|
236
|
-
|
237
|
-
|
239
|
+
((comma | lparen).absent? >> any).repeat(0) >> part_with_parenthesis >>
|
240
|
+
(forbidden_in_substance_name.absent? >> (identifier.repeat(1) | part_with_parenthesis | rparen) >> space?).repeat(0)
|
238
241
|
}
|
239
|
-
rule(:substance_name) {
|
242
|
+
rule(:substance_name) {
|
243
|
+
(
|
240
244
|
name_with_parenthesis |
|
241
245
|
name_without_parenthesis
|
242
246
|
) >>
|
243
|
-
|
244
|
-
|
245
|
-
rule(:simple_substance) { substance_name.as(:substance_name) >> space? >> dose.maybe.as(:dose)}
|
246
|
-
rule(:simple_subtance_with_digits_in_name_and_dose)
|
247
|
+
str("pro dosi").maybe >> space?
|
248
|
+
}
|
249
|
+
rule(:simple_substance) { substance_name.as(:substance_name) >> space? >> dose.maybe.as(:dose) }
|
250
|
+
rule(:simple_subtance_with_digits_in_name_and_dose) {
|
247
251
|
substance_lead.maybe.as(:more_info) >> space? >>
|
248
|
-
|
249
|
-
|
252
|
+
(name_without_parenthesis >> space? >> ((digits.repeat(1) >> (str(" %") | str("%")) | digits.repeat(1)))).as(:substance_name) >>
|
253
|
+
space >> dose_with_unit.as(:dose)
|
250
254
|
}
|
251
255
|
|
252
|
-
|
253
256
|
rule(:substance_more_info) { # e.g. "acari allergeni extractum 5000 U.:
|
254
|
-
|
255
|
-
|
257
|
+
(str("ratio:").absent? >> (identifier | digits) >> space?).repeat(1) >> space? >> (str("U.:") | str(":") | str(".:")) >> space?
|
258
|
+
}
|
256
259
|
|
257
260
|
rule(:pro_identifiers) {
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
}
|
272
|
-
rule(:excipiens_dose) {
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
261
|
+
str("ut aqua ad iniectabilia q.s. ad emulsionem pro ") |
|
262
|
+
str("aqua ").maybe >> str("ad iniectabilia q.s. ad solutionem pro ") |
|
263
|
+
str("aqua ").maybe >> str("ad solutionem pro ") |
|
264
|
+
str("aqua ").maybe >> str("q.s. ad emulsionem pro ") |
|
265
|
+
str("aqua ").maybe >> str("q.s. ad gelatume pro ") |
|
266
|
+
str("aqua ").maybe >> str("q.s. ad solutionem pro ") |
|
267
|
+
str("aqua ").maybe >> str("q.s. ad suspensionem pro ") |
|
268
|
+
str("doses pro vase ") |
|
269
|
+
str("excipiens ad emulsionem pro ") |
|
270
|
+
str("excipiens ad pulverem pro ") |
|
271
|
+
str("excipiens ad solutionem pro ") |
|
272
|
+
str("pro vase ") |
|
273
|
+
str("q.s. ad pulverem pro ")
|
274
|
+
}
|
275
|
+
rule(:excipiens_dose) {
|
276
|
+
pro_identifiers.as(:excipiens_description) >> space? >> dose.as(:dose) >> space? >> ratio.maybe.as(:ratio) >>
|
277
|
+
space? >> str("corresp.").maybe >> space? >> dose.maybe.as(:dose_corresp)
|
278
|
+
}
|
279
|
+
|
280
|
+
rule(:excipiens_identifiers) {
|
281
|
+
str("ad globulos") |
|
282
|
+
str("ad pulverem") |
|
283
|
+
str("ad solutionem") |
|
284
|
+
str("aether q.s.") |
|
285
|
+
str("ana partes") |
|
286
|
+
str("aqua ad iniectabilia q.s. ad solutionem") |
|
287
|
+
str("aqua ad iniectabilia") |
|
288
|
+
str("aqua q.s. ad") |
|
289
|
+
str("excipiens pro compresso obducto") |
|
290
|
+
str("excipiens pro compresso") |
|
291
|
+
str("excipiens pro praeparatione") |
|
292
|
+
str("excipiens") |
|
293
|
+
str("pro charta") |
|
294
|
+
str("pro praeparatione") |
|
295
|
+
str("pro vitro") |
|
296
|
+
str("q.s. ad") |
|
297
|
+
str("q.s. pro praeparatione") |
|
298
|
+
str("saccharum ad") |
|
299
|
+
str("solvens (i.v.): aqua ad iniectabilia")
|
300
|
+
}
|
301
|
+
|
302
|
+
rule(:excipiens) {
|
303
|
+
substance_lead.maybe.as(:more_info) >> space? >>
|
304
|
+
(excipiens_dose | (excipiens_identifiers >> any.repeat(0)).as(:excipiens_description)) >>
|
305
|
+
any.repeat(0)
|
306
|
+
}
|
307
|
+
|
308
|
+
rule(:substance_lead) {
|
309
|
+
useage >> space? |
|
310
|
+
str("Beutel:") >> space? |
|
311
|
+
str("residui:") >> space? |
|
312
|
+
str("mineralia:") >> str(":") >> space? |
|
313
|
+
str("Solvens:") >> space? |
|
314
|
+
substance_more_info
|
315
|
+
}
|
310
316
|
rule(:corresp_substance_label) {
|
311
|
-
|
312
|
-
str(
|
313
|
-
str(
|
314
|
-
str(
|
315
|
-
str(
|
316
|
-
|
317
|
-
rule(:ratio) { str(
|
318
|
-
|
319
|
-
rule(:solvens) {
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
317
|
+
str(", corresp. ca.,") |
|
318
|
+
str("corresp. ca.,") |
|
319
|
+
str("corresp.,") |
|
320
|
+
str("corresp.") |
|
321
|
+
str(", corresp.")
|
322
|
+
}
|
323
|
+
rule(:ratio) { str("ratio:") >> space >> ratio_value }
|
324
|
+
|
325
|
+
rule(:solvens) {
|
326
|
+
(str("Solvens:") | str("Solvens (i.m.):")) >> space >> (any.repeat).as(:more_info) >> space? >>
|
327
|
+
(substance.as(:substance) >> str("/L").maybe).maybe >>
|
328
|
+
any.maybe
|
329
|
+
}
|
330
|
+
# Perhaps we could have some syntax sugar to make this more easy?
|
331
|
+
#
|
332
|
+
def tag(opts = {})
|
333
|
+
opts[:close] || false
|
334
|
+
end
|
335
|
+
|
336
|
+
# TODO: what does ut alia: impl?
|
330
337
|
rule(:substance_ut) {
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
338
|
+
(space? >> (str("pro dosi ut ") | str("ut ")) >>
|
339
|
+
space? >> str("alia:").absent? >> substance
|
340
|
+
) >>
|
341
|
+
space?
|
342
|
+
}
|
336
343
|
|
337
344
|
rule(:corresp_substance) {
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
345
|
+
(corresp_substance_label) >> space? >>
|
346
|
+
(
|
347
|
+
substance |
|
348
|
+
dose.as(:dose_corresp_2) |
|
349
|
+
excipiens.as(:excipiens)
|
350
|
+
)
|
344
351
|
}
|
345
352
|
|
346
353
|
rule(:substance) {
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
(space? >> str(
|
354
|
+
(
|
355
|
+
simple_subtance_with_digits_in_name_and_dose |
|
356
|
+
der |
|
357
|
+
substance_lead.maybe.as(:more_info) >> space? >> lebensmittel_zusatz |
|
358
|
+
substance_lead.maybe.as(:more_info) >> space? >> simple_substance >> str("pro dosi").maybe
|
359
|
+
).as(:substance) >>
|
360
|
+
(space? >> str(", ").maybe >> ratio.maybe).as(:ratio) >>
|
354
361
|
space? >> corresp_substance.maybe.as(:chemical_substance) >>
|
355
362
|
space? >> substance_ut.repeat(0).as(:substance_ut)
|
356
363
|
}
|
357
|
-
rule(:histamin) { str(
|
358
|
-
rule(:praeparatio){
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
rule(:
|
365
|
-
rule(:
|
366
|
-
rule(:
|
364
|
+
rule(:histamin) { str("U = Histamin Equivalent Prick").as(:histamin) }
|
365
|
+
rule(:praeparatio) {
|
366
|
+
((one_word >> space?).repeat(1).as(:description) >> str(":") >> space?).maybe >>
|
367
|
+
(name_with_parenthesis | name_without_parenthesis).repeat(1).as(:substance_name) >>
|
368
|
+
number.as(:qty) >> space >> str("U.:") >> space? >>
|
369
|
+
((identifier >> space?).repeat(1).as(:more_info) >> space?).maybe
|
370
|
+
}
|
371
|
+
rule(:substance_separator) { (str(", et ") | comma | str("et ") | str("ut alia: ")) >> space? }
|
372
|
+
rule(:one_substance) { (praeparatio | histamin | substance) >> space? >> ratio.as(:ratio).maybe >> space? }
|
373
|
+
rule(:all_substances) { (one_substance >> substance_separator.maybe).repeat(1) >> space? >> excipiens.as(:excipiens).maybe }
|
374
|
+
rule(:composition) { all_substances }
|
367
375
|
rule(:long_labels) {
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
376
|
+
str("Praeparatio sicca cum solvens: praeparatio sicca:") |
|
377
|
+
str("Praeparatio cryodesiccata") >> (str(":").absent? >> any).repeat(0) >> str(":") |
|
378
|
+
str("Tela cum praeparatione (Panel ") >> digit >> str("):")
|
379
|
+
}
|
372
380
|
rule(:label_id) {
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
381
|
+
(
|
382
|
+
str("V") |
|
383
|
+
str("IV") |
|
384
|
+
str("III") |
|
385
|
+
str("II") |
|
386
|
+
str("I") |
|
387
|
+
str("A") |
|
388
|
+
str("B") |
|
389
|
+
str("C") |
|
390
|
+
str("D") |
|
391
|
+
str("E")
|
384
392
|
)
|
385
393
|
}
|
386
|
-
rule(:label_separator) {
|
387
|
-
rule(:label) {
|
388
|
-
|
389
|
-
|
394
|
+
rule(:label_separator) { (str("):") | str(")")) }
|
395
|
+
rule(:label) {
|
396
|
+
label_id.as(:label) >> space? >>
|
397
|
+
label_separator >> str(",").absent? >>
|
398
|
+
(space? >> (match(/[^:]/).repeat(0)).as(:label_description) >> str(":") >> space).maybe
|
399
|
+
}
|
400
|
+
rule(:leading_label) {
|
401
|
+
label_id >> label_separator >> (str(" et ") | str(", ") | str(" pro usu: ") | space) >>
|
402
|
+
label_id >> label_separator >> any.repeat(1) |
|
403
|
+
long_labels.as(:label) |
|
404
|
+
label
|
390
405
|
}
|
391
|
-
rule(:leading_label) { label_id >> label_separator >> (str(' et ') | str(', ') | str(' pro usu: ') | space) >>
|
392
|
-
label_id >> label_separator >> any.repeat(1) |
|
393
|
-
long_labels.as(:label) |
|
394
|
-
label
|
395
|
-
}
|
396
406
|
rule(:corresp_label) {
|
397
|
-
str(
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
}
|
407
|
-
rule(:corresp_line) {
|
408
|
-
|
409
|
-
|
407
|
+
str("aqua ") |
|
408
|
+
str("excipiens ") |
|
409
|
+
str("doses ") |
|
410
|
+
str("Pulver: ") |
|
411
|
+
str("Diluens: ") |
|
412
|
+
str("Solutio reconstituta:") |
|
413
|
+
str("Corresp., ") |
|
414
|
+
str("Corresp. ") |
|
415
|
+
str("corresp. ")
|
416
|
+
}
|
417
|
+
rule(:corresp_line) {
|
418
|
+
corresp_label >> any.repeat(1).as(:corresp) |
|
419
|
+
((label_id >> label_separator >> space? >> str("et ").maybe).repeat(1) >> any.repeat(1)).as(:corresp)
|
420
|
+
}
|
410
421
|
rule(:corresp_line_neu) { corresp_label >> any.repeat(1).as(:corresp) }
|
411
422
|
|
412
423
|
rule(:solvens_label) {
|
413
|
-
str(
|
414
|
-
|
415
|
-
|
424
|
+
str("Solvens (i.v.)") |
|
425
|
+
str("Solvens (i.m.)") |
|
426
|
+
str("Solvens")
|
416
427
|
}
|
417
|
-
rule(:solvens_line) { solvens_label.as(:label) >> (str(
|
428
|
+
rule(:solvens_line) { solvens_label.as(:label) >> (str(": ") >> composition.repeat(1).as(:composition) | any.repeat(1).as(:corresp)) }
|
418
429
|
|
419
430
|
rule(:multiple_et_line) {
|
420
|
-
|
431
|
+
((label_id >> label_separator >> space? >> (str("pro usu") | str("et "))).repeat(1) >> any.repeat(1)).as(:corresp)
|
421
432
|
}
|
422
433
|
|
423
|
-
rule(:polvac) { label_id.as(:label) >> label_separator >> space? >> composition.as(:composition) >> space? >> str(
|
434
|
+
rule(:polvac) { label_id.as(:label) >> label_separator >> space? >> composition.as(:composition) >> space? >> str(".").maybe >> space? }
|
424
435
|
|
425
|
-
rule(:label_comment_excipiens) { label >> space? >> excipiens.as(:excipiens) >> space? >> str(
|
436
|
+
rule(:label_comment_excipiens) { label >> space? >> excipiens.as(:excipiens) >> space? >> str(".").maybe >> space? }
|
426
437
|
|
427
|
-
rule(:label_id_composition) {
|
428
|
-
|
429
|
-
|
438
|
+
rule(:label_id_composition) {
|
439
|
+
label_id.as(:label) >> label_separator >>
|
440
|
+
((space >> identifier).repeat(1) >> str(":")).maybe.as(:label_description) >>
|
441
|
+
composition.as(:composition)
|
442
|
+
}
|
430
443
|
|
431
444
|
rule(:expression_comp) {
|
432
445
|
solvens_line |
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
446
|
+
multiple_et_line |
|
447
|
+
label_id_composition |
|
448
|
+
corresp_line_neu |
|
449
|
+
leading_label.maybe >> space? >> composition.as(:composition) >> space? >> str(".").maybe >> space? |
|
450
|
+
polvac |
|
451
|
+
label_comment_excipiens |
|
452
|
+
excipiens.as(:composition) |
|
453
|
+
space.repeat(3)
|
441
454
|
}
|
442
455
|
|
443
456
|
rule(:expression_compD) {
|
444
457
|
solvens_line |
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
458
|
+
multiple_et_line.as("multiple_et_line") |
|
459
|
+
label_id_composition.as("label_id_composition") |
|
460
|
+
corresp_line_neu.as("corresp_line_neu") |
|
461
|
+
leading_label.maybe >> space? >> composition.as(:composition) >> space? >> str(".").maybe >> space? |
|
462
|
+
polvac.as("polvac") |
|
463
|
+
label_comment_excipiens.as("label_comment_excipiens") |
|
464
|
+
excipiens.as(:composition) |
|
465
|
+
space.repeat(3)
|
453
466
|
}
|
454
467
|
|
455
468
|
root :expression_comp
|
456
469
|
end
|
457
470
|
|
458
471
|
class GalenicFormParser < CompositionParser
|
472
|
+
rule(:prepation_separator) { str(", ") | str("\n") }
|
459
473
|
|
460
|
-
rule(:
|
461
|
-
|
462
|
-
|
463
|
-
}
|
474
|
+
rule(:prepation_name) {
|
475
|
+
((prepation_separator | lparen).absent? >> any).repeat(1)
|
476
|
+
}
|
464
477
|
rule(:dose_with_pro) {
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
478
|
+
(match("[0-9a-zA-Z]").repeat(1) >>
|
479
|
+
str("/") >>
|
480
|
+
match("[0-9a-zA-Z'%]").repeat(1)
|
481
|
+
).maybe
|
469
482
|
}
|
470
483
|
|
471
484
|
rule(:gal_form) {
|
472
485
|
qty_unit_silent.maybe >>
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
486
|
+
(((str("\n") # |
|
487
|
+
str(",") >> space? >> qty_unit_silent |
|
488
|
+
digits >> str("%")
|
489
|
+
).absent? >> any).repeat(1) >>
|
490
|
+
(lparen >> (rparen.absent? >> any).repeat(1) >> rparen).maybe
|
491
|
+
).as(:galenic_form) >>
|
492
|
+
space?
|
481
493
|
}
|
482
494
|
|
483
495
|
rule(:standard_galenic) {
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
rule(:qty_unit_silent) { number >> space >> units
|
493
|
-
rule(:name_then_dose) {
|
496
|
+
prepation_name.as(:prepation_name) >> space? >>
|
497
|
+
prepation_separator >> space? >>
|
498
|
+
(name_without_parenthesis >> qty_unit_silent >> prepation_separator).maybe >>
|
499
|
+
(qty_unit_silent >> space?).maybe >>
|
500
|
+
(dose_with_pro >> space? >> str(",") >> space?).maybe >>
|
501
|
+
gal_form >> space?
|
502
|
+
}
|
503
|
+
|
504
|
+
rule(:qty_unit_silent) { number >> space >> units }
|
505
|
+
rule(:name_then_dose) {
|
506
|
+
((space.absent? >> any).repeat(1) >>
|
494
507
|
space >> qty_unit_silent).as(:prepation_name) >> space?.as(:galenic_form)
|
495
|
-
|
496
|
-
|
497
|
-
rule(:only_name) {
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
508
|
+
}
|
509
|
+
|
510
|
+
rule(:only_name) {
|
511
|
+
any.repeat(1).as(:prepation_name) >> space?.as(:galenic_form)
|
512
|
+
}
|
513
|
+
|
514
|
+
rule(:name_comma_gal_form) {
|
515
|
+
(space.absent? >> any).repeat(1).as(:prepation_name) >>
|
516
|
+
comma >> space >>
|
517
|
+
any.repeat(1).as(:galenic_form)
|
518
|
+
}
|
519
|
+
rule(:simple_name) {
|
520
|
+
((match(["a-zA-Z0-9,%"]) | str("-") | umlaut).repeat(1)) >>
|
521
|
+
(lparen >> (rparen.absent? >> any.repeat(1)) >> rparen).maybe
|
522
|
+
}
|
507
523
|
rule(:name_gal_form) { # e.g. Dicloabak 0,1% Augentropfen or 35 Clear-Flex 3,86 % Peritonealdialyselösung
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
524
|
+
(simple_name >> space).repeat(1).as(:prepation_name) >>
|
525
|
+
space? >>
|
526
|
+
(dose_with_pro >> space?).maybe >>
|
527
|
+
(digits.absent? >> gal_form) >> space?
|
512
528
|
}
|
513
529
|
|
514
530
|
rule(:name_without_comma_gal_form) { # Sulfure de Rhénium (186Re)-RE-186-MM-1 Cis bio International
|
515
|
-
((str(
|
516
|
-
|
517
|
-
|
518
|
-
}
|
519
|
-
|
520
|
-
# Phytopharma foie et bile capsules/Leber-Galle Kapseln
|
521
|
-
rule(:leber_gallen_kapseln) {
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
531
|
+
((str(", ") | str(",") >> match(["A-Z"])).absent? >> any).repeat(1).as(:prepation_name) >>
|
532
|
+
comma >> space? >>
|
533
|
+
gal_form >> space?
|
534
|
+
}
|
535
|
+
|
536
|
+
# Phytopharma foie et bile capsules/Leber-Galle Kapseln
|
537
|
+
rule(:leber_gallen_kapseln) {
|
538
|
+
((str("/Leber-Galle Kapseln").absent? >> any).repeat(1)).as(:prepation_name) >>
|
539
|
+
str("/") >> any.repeat(1).as(:galenic_form) >>
|
540
|
+
space?
|
541
|
+
}
|
542
|
+
# Plak-out Spray 0,1 %
|
543
|
+
rule(:plak_out_spray) {
|
544
|
+
str("Plak-out Spray 0,1 %").as(:prepation_name) >>
|
545
|
+
space? >> str("dummy").maybe.as(:galenic_form) >> space?
|
546
|
+
}
|
547
|
+
rule(:galenic) {
|
548
|
+
plak_out_spray |
|
549
|
+
leber_gallen_kapseln |
|
550
|
+
standard_galenic |
|
551
|
+
name_comma_gal_form |
|
552
|
+
name_then_dose >> space? |
|
553
|
+
name_without_comma_gal_form |
|
554
|
+
name_gal_form |
|
555
|
+
only_name >> space? |
|
556
|
+
space?
|
557
|
+
}
|
540
558
|
|
541
559
|
# for debugging purposes only
|
542
|
-
rule(:galenicD)
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
560
|
+
rule(:galenicD) {
|
561
|
+
plak_out_spray.as(:plak_out_spray) |
|
562
|
+
leber_gallen_kapseln.as(:leber_gallen_kapseln) |
|
563
|
+
standard_galenic.as(:standard_galenic) |
|
564
|
+
name_comma_gal_form.as(:name_comma_gal_form) |
|
565
|
+
name_then_dose.as(:name_then_dose) >> space? |
|
566
|
+
name_without_comma_gal_form.as(:name_without_comma_gal_form) |
|
567
|
+
name_gal_form.as(:name_gal_form) |
|
568
|
+
only_name.as(:only_name) >> space? |
|
569
|
+
space?
|
570
|
+
}
|
553
571
|
root :galenic
|
554
|
-
end
|
572
|
+
end
|