glossarist 2.6.2 → 2.6.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/.gitignore +2 -1
- data/.rubocop_todo.yml +58 -16
- data/Gemfile +3 -19
- data/README.adoc +117 -0
- data/glossarist.gemspec +1 -0
- data/lib/glossarist/cli/import_command.rb +54 -0
- data/lib/glossarist/cli.rb +29 -8
- data/lib/glossarist/designation/expression.rb +1 -2
- data/lib/glossarist/designation/graphical_symbol.rb +1 -1
- data/lib/glossarist/managed_concept.rb +1 -1
- data/lib/glossarist/rdf/skos_concept.rb +0 -1
- data/lib/glossarist/rdf/skos_vocabulary.rb +0 -1
- data/lib/glossarist/sts/extracted_designation.rb +14 -0
- data/lib/glossarist/sts/extracted_lang_set.rb +16 -0
- data/lib/glossarist/sts/extracted_term.rb +13 -0
- data/lib/glossarist/sts/import_result.rb +24 -0
- data/lib/glossarist/sts/importer.rb +253 -0
- data/lib/glossarist/sts/term_extractor.rb +186 -0
- data/lib/glossarist/sts/term_mapper.rb +118 -0
- data/lib/glossarist/sts.rb +87 -0
- data/lib/glossarist/transforms/concept_to_skos_transform.rb +0 -2
- data/lib/glossarist/version.rb +1 -1
- data/lib/glossarist.rb +10 -7
- metadata +25 -2
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tmpdir"
|
|
4
|
+
require_relative "import_result"
|
|
5
|
+
|
|
6
|
+
module Glossarist
|
|
7
|
+
module Sts
|
|
8
|
+
class Importer
|
|
9
|
+
STRATEGIES = %i[skip replace merge].freeze
|
|
10
|
+
|
|
11
|
+
attr_reader :duplicate_strategy
|
|
12
|
+
|
|
13
|
+
def initialize(duplicate_strategy: :skip)
|
|
14
|
+
unless STRATEGIES.include?(duplicate_strategy)
|
|
15
|
+
raise ArgumentError,
|
|
16
|
+
"duplicate_strategy must be one of #{STRATEGIES.join(', ')}, got #{duplicate_strategy}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
@duplicate_strategy = duplicate_strategy
|
|
20
|
+
@mapper = TermMapper.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def import_new(xml_files, output:, shortname: nil, version: nil, **opts)
|
|
24
|
+
raw_concepts = extract_all_concepts(xml_files)
|
|
25
|
+
concepts, conflicts, skipped = dedup_concepts(raw_concepts)
|
|
26
|
+
|
|
27
|
+
if output.end_with?(".gcr")
|
|
28
|
+
unless shortname
|
|
29
|
+
raise ArgumentError,
|
|
30
|
+
"--shortname is required for GCR output"
|
|
31
|
+
end
|
|
32
|
+
unless version
|
|
33
|
+
raise ArgumentError,
|
|
34
|
+
"--version is required for GCR output"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
create_gcr(concepts, output, shortname: shortname, version: version,
|
|
38
|
+
**opts)
|
|
39
|
+
else
|
|
40
|
+
save_dataset(concepts, output)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
ImportResult.new(
|
|
44
|
+
concepts: concepts,
|
|
45
|
+
conflicts: conflicts,
|
|
46
|
+
source_files: xml_files,
|
|
47
|
+
skipped_count: skipped,
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def import_into_existing(xml_files, dataset_path)
|
|
52
|
+
existing = load_existing(dataset_path)
|
|
53
|
+
new_concepts = extract_all_concepts(xml_files)
|
|
54
|
+
index = build_concept_index(existing)
|
|
55
|
+
|
|
56
|
+
result_state = apply_with_dedup(new_concepts, existing, index)
|
|
57
|
+
|
|
58
|
+
save_to_path(existing, dataset_path)
|
|
59
|
+
|
|
60
|
+
ImportResult.new(
|
|
61
|
+
concepts: existing.managed_concepts,
|
|
62
|
+
conflicts: result_state.conflicts,
|
|
63
|
+
source_files: xml_files,
|
|
64
|
+
skipped_count: result_state.skipped,
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
DedupState = Struct.new(:conflicts, :skipped, keyword_init: true)
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def apply_with_dedup(new_concepts, existing, index)
|
|
73
|
+
state = DedupState.new(conflicts: [], skipped: 0)
|
|
74
|
+
|
|
75
|
+
new_concepts.each do |mc|
|
|
76
|
+
key = concept_key(mc)
|
|
77
|
+
existing_mc = index[key]
|
|
78
|
+
|
|
79
|
+
if existing_mc.nil?
|
|
80
|
+
existing.store(mc)
|
|
81
|
+
index[key] = mc
|
|
82
|
+
else
|
|
83
|
+
state.conflicts << DuplicateConflict.new(
|
|
84
|
+
new_concept: mc, existing_concept: existing_mc, key: key,
|
|
85
|
+
)
|
|
86
|
+
handle_duplicate(existing, existing_mc, mc, index, key, state)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
state
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def handle_duplicate(existing, old_mc, new_mc, index, key, state)
|
|
94
|
+
case duplicate_strategy
|
|
95
|
+
when :skip
|
|
96
|
+
state.skipped += 1
|
|
97
|
+
when :replace
|
|
98
|
+
replace_in_collection(existing, old_mc, new_mc)
|
|
99
|
+
index[key] = new_mc
|
|
100
|
+
when :merge
|
|
101
|
+
merge_concept(old_mc, new_mc)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def extract_all_concepts(xml_files)
|
|
106
|
+
xml_files.flat_map do |path|
|
|
107
|
+
extractor = TermExtractor.new(path)
|
|
108
|
+
terms = extractor.extract
|
|
109
|
+
terms.map { |t| @mapper.map(t) }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def dedup_concepts(concepts) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
114
|
+
seen = {}
|
|
115
|
+
conflicts = []
|
|
116
|
+
skipped = 0
|
|
117
|
+
unique = []
|
|
118
|
+
|
|
119
|
+
concepts.each do |mc|
|
|
120
|
+
key = concept_key(mc)
|
|
121
|
+
if key.first.empty? || seen[key].nil?
|
|
122
|
+
unique << mc
|
|
123
|
+
seen[key] = mc unless key.first.empty?
|
|
124
|
+
else
|
|
125
|
+
conflicts << DuplicateConflict.new(
|
|
126
|
+
new_concept: mc, existing_concept: seen[key], key: key,
|
|
127
|
+
)
|
|
128
|
+
skipped += apply_dedup_to_unique(unique, seen, mc, key)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
[unique, conflicts, skipped]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def apply_dedup_to_unique(unique, seen, new_mc, key)
|
|
136
|
+
case duplicate_strategy
|
|
137
|
+
when :skip
|
|
138
|
+
1
|
|
139
|
+
when :replace
|
|
140
|
+
unique.delete(seen[key])
|
|
141
|
+
unique << new_mc
|
|
142
|
+
seen[key] = new_mc
|
|
143
|
+
0
|
|
144
|
+
when :merge
|
|
145
|
+
merge_concept(seen[key], new_mc)
|
|
146
|
+
0
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def concept_key(managed_concept)
|
|
151
|
+
designation = managed_concept.default_designation.to_s.downcase.strip
|
|
152
|
+
domain = begin
|
|
153
|
+
l10n = managed_concept.default_lang
|
|
154
|
+
l10n&.data&.domain.to_s.downcase.strip
|
|
155
|
+
end
|
|
156
|
+
[designation, domain]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def build_concept_index(collection)
|
|
160
|
+
index = {}
|
|
161
|
+
collection.each do |mc|
|
|
162
|
+
key = concept_key(mc)
|
|
163
|
+
index[key] = mc unless key.first.empty?
|
|
164
|
+
end
|
|
165
|
+
index
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def merge_concept(existing_mc, new_mc)
|
|
169
|
+
new_mc.localizations.each do |l10n|
|
|
170
|
+
lang = l10n.language_code
|
|
171
|
+
if existing_mc.localization(lang).nil?
|
|
172
|
+
existing_mc.add_localization(l10n)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def replace_in_collection(collection, old_mc, new_mc)
|
|
178
|
+
collection.managed_concepts.delete(old_mc)
|
|
179
|
+
collection.store(new_mc)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def load_existing(path)
|
|
183
|
+
collection = ManagedConceptCollection.new
|
|
184
|
+
if path.end_with?(".gcr")
|
|
185
|
+
package = GcrPackage.load(path)
|
|
186
|
+
package.concepts.each { |mc| collection.store(mc) }
|
|
187
|
+
else
|
|
188
|
+
concepts = ConceptCollector.collect(path)
|
|
189
|
+
concepts.each { |mc| collection.store(mc) }
|
|
190
|
+
end
|
|
191
|
+
collection
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def save_to_path(collection, path)
|
|
195
|
+
if path.end_with?(".gcr")
|
|
196
|
+
tmpdir = build_temp_dataset(collection.managed_concepts)
|
|
197
|
+
begin
|
|
198
|
+
GC.start
|
|
199
|
+
tmp_gcr = "#{path}.tmp.#{Process.pid}"
|
|
200
|
+
GcrPackage.create_from_directory(
|
|
201
|
+
tmpdir,
|
|
202
|
+
output: tmp_gcr,
|
|
203
|
+
shortname: File.basename(path, ".gcr"),
|
|
204
|
+
version: "1.0.0",
|
|
205
|
+
)
|
|
206
|
+
FileUtils.rm_f(path)
|
|
207
|
+
FileUtils.mv(tmp_gcr, path)
|
|
208
|
+
ensure
|
|
209
|
+
FileUtils.rm_rf(tmpdir)
|
|
210
|
+
FileUtils.rm_f(tmp_gcr)
|
|
211
|
+
end
|
|
212
|
+
else
|
|
213
|
+
save_dataset(collection.managed_concepts, path)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def save_dataset(concepts, dir)
|
|
218
|
+
concepts_dir = File.join(dir, "concepts")
|
|
219
|
+
FileUtils.mkdir_p(concepts_dir)
|
|
220
|
+
collection = ManagedConceptCollection.new
|
|
221
|
+
concepts.each { |mc| collection.store(mc) }
|
|
222
|
+
collection.save_grouped_concepts_to_files(concepts_dir)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def create_gcr(concepts, output, shortname:, version:, **opts)
|
|
226
|
+
tmpdir = build_temp_dataset(concepts)
|
|
227
|
+
begin
|
|
228
|
+
GcrPackage.create_from_directory(
|
|
229
|
+
tmpdir,
|
|
230
|
+
output: output,
|
|
231
|
+
shortname: shortname,
|
|
232
|
+
version: version,
|
|
233
|
+
**opts,
|
|
234
|
+
)
|
|
235
|
+
ensure
|
|
236
|
+
FileUtils.rm_rf(tmpdir)
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def build_temp_dataset(concepts)
|
|
241
|
+
tmpdir = Dir.mktmpdir("glossarist-sts-import")
|
|
242
|
+
concepts_dir = File.join(tmpdir, "concepts")
|
|
243
|
+
FileUtils.mkdir_p(concepts_dir)
|
|
244
|
+
|
|
245
|
+
collection = ManagedConceptCollection.new
|
|
246
|
+
concepts.each { |mc| collection.store(mc) }
|
|
247
|
+
collection.save_grouped_concepts_to_files(concepts_dir)
|
|
248
|
+
|
|
249
|
+
tmpdir
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module Sts
|
|
5
|
+
class TermExtractor
|
|
6
|
+
def initialize(xml_path)
|
|
7
|
+
raw = File.read(xml_path)
|
|
8
|
+
@standard = ::Sts::IsoSts::Standard.from_xml(raw)
|
|
9
|
+
@source_ref = extract_source_ref
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def extract
|
|
13
|
+
term_secs = collect_term_secs
|
|
14
|
+
term_secs.filter_map do |ts|
|
|
15
|
+
next unless ts.term_entry
|
|
16
|
+
|
|
17
|
+
build_extracted_term(ts)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def collect_term_secs
|
|
24
|
+
secs = []
|
|
25
|
+
walk_sections(@standard.body, secs) if @standard.body
|
|
26
|
+
secs
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def walk_sections(container, collected)
|
|
30
|
+
collect_term_secs_from(container, collected)
|
|
31
|
+
walk_child_secs(container, collected)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def collect_term_secs_from(container, collected)
|
|
35
|
+
secs = container.term_sec
|
|
36
|
+
secs&.each do |ts|
|
|
37
|
+
collected << ts
|
|
38
|
+
walk_sections(ts, collected) if ts.term_sec&.any?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def walk_child_secs(container, collected)
|
|
43
|
+
secs = container_child_secs(container)
|
|
44
|
+
secs&.each { |s| walk_sections(s, collected) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def container_child_secs(container)
|
|
48
|
+
case container
|
|
49
|
+
when ::Sts::IsoSts::Body, ::Sts::IsoSts::Sec
|
|
50
|
+
container.sec
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def build_extracted_term(term_sec)
|
|
55
|
+
entry = term_sec.term_entry
|
|
56
|
+
label_text = extract_label(term_sec)
|
|
57
|
+
|
|
58
|
+
lang_sets = entry.lang_set.filter_map do |ls|
|
|
59
|
+
build_lang_set(ls)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
Sts::ExtractedTerm.new(
|
|
63
|
+
id: entry.id,
|
|
64
|
+
label: label_text,
|
|
65
|
+
source_ref: @source_ref,
|
|
66
|
+
lang_sets: lang_sets,
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def extract_label(term_sec)
|
|
71
|
+
label = term_sec.label
|
|
72
|
+
return nil unless label
|
|
73
|
+
|
|
74
|
+
label.content&.join.to_s.strip
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def build_lang_set(lang_set) # rubocop:disable Metrics/AbcSize
|
|
78
|
+
lang_code = Sts.convert_language_code(lang_set.lang.to_s)
|
|
79
|
+
|
|
80
|
+
Sts::ExtractedLangSet.new(
|
|
81
|
+
language_code: lang_code,
|
|
82
|
+
definition_text: extract_definition_text(lang_set),
|
|
83
|
+
note_texts: extract_note_texts(lang_set),
|
|
84
|
+
example_texts: extract_example_texts(lang_set),
|
|
85
|
+
source_texts: extract_source_texts(lang_set),
|
|
86
|
+
domain: extract_subject_field(lang_set),
|
|
87
|
+
designations: lang_set.tig.filter_map do |tig|
|
|
88
|
+
build_designation(tig)
|
|
89
|
+
end,
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def extract_definition_text(lang_set)
|
|
94
|
+
definitions = lang_set.definition
|
|
95
|
+
return "" unless definitions&.any?
|
|
96
|
+
|
|
97
|
+
definitions.first.value&.join.to_s.strip
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def extract_note_texts(lang_set)
|
|
101
|
+
lang_set.note.filter_map do |n|
|
|
102
|
+
text = n.value&.join.to_s.strip
|
|
103
|
+
text unless text.empty?
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def extract_example_texts(lang_set)
|
|
108
|
+
lang_set.example.filter_map do |e|
|
|
109
|
+
text = e.value&.join.to_s.strip
|
|
110
|
+
text unless text.empty?
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def extract_source_texts(lang_set)
|
|
115
|
+
lang_set.source.filter_map do |s|
|
|
116
|
+
text = s.value&.join.to_s.strip
|
|
117
|
+
text unless text.empty?
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def extract_subject_field(lang_set)
|
|
122
|
+
fields = lang_set.subject_field
|
|
123
|
+
return nil unless fields&.any?
|
|
124
|
+
|
|
125
|
+
text = fields.first.value&.join.to_s.strip
|
|
126
|
+
text unless text.empty?
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def build_designation(tig)
|
|
130
|
+
Sts::ExtractedDesignation.new(
|
|
131
|
+
term: resolve_term_text(tig),
|
|
132
|
+
type: map_term_type(tig),
|
|
133
|
+
normative_status: map_normative_status(tig),
|
|
134
|
+
part_of_speech: tig.pos&.value,
|
|
135
|
+
abbreviation_type: map_abbreviation_type(tig),
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def resolve_term_text(tig)
|
|
140
|
+
tig.term&.value&.join.to_s.strip
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def map_term_type(tig)
|
|
144
|
+
raw = tig.term_type&.value.to_s
|
|
145
|
+
mapped = TERM_TYPE_MAP[raw]
|
|
146
|
+
mapped.nil? || raw.empty? ? "expression" : mapped
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def map_abbreviation_type(tig)
|
|
150
|
+
raw = tig.term_type&.value.to_s
|
|
151
|
+
return nil unless TERM_TYPE_MAP[raw] == "abbreviation"
|
|
152
|
+
|
|
153
|
+
raw == "acronym" ? "acronym" : "truncation"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def map_normative_status(tig)
|
|
157
|
+
NORMATIVE_STATUS_MAP[tig.normative_authorization&.value.to_s]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def extract_source_ref # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
161
|
+
front = @standard.front
|
|
162
|
+
return nil unless front
|
|
163
|
+
|
|
164
|
+
meta = front.iso_meta || front.std_meta
|
|
165
|
+
return nil unless meta
|
|
166
|
+
|
|
167
|
+
refs = meta.std_ref
|
|
168
|
+
return nil unless refs&.any?
|
|
169
|
+
|
|
170
|
+
best_ref = refs.find { |r| r.type == "dated" } ||
|
|
171
|
+
refs.find { |r| r.type == "undated" } ||
|
|
172
|
+
refs.first
|
|
173
|
+
|
|
174
|
+
extract_ref_text(best_ref)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def extract_ref_text(ref)
|
|
178
|
+
if ref.value.is_a?(String)
|
|
179
|
+
ref.value.to_s.strip
|
|
180
|
+
else
|
|
181
|
+
ref.content&.join.to_s.strip
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module Sts
|
|
5
|
+
class TermMapper
|
|
6
|
+
def map(extracted_term)
|
|
7
|
+
concept_id = extracted_term.label || extracted_term.id
|
|
8
|
+
|
|
9
|
+
mc = Glossarist::ManagedConcept.new(data: { id: concept_id })
|
|
10
|
+
|
|
11
|
+
extracted_term.lang_sets.each do |ls|
|
|
12
|
+
mc.add_localization(build_localized_concept(ls,
|
|
13
|
+
extracted_term.source_ref))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
mc
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def build_localized_concept(lang_set, source_ref)
|
|
22
|
+
terms = lang_set.designations.map { |d| build_designation(d) }
|
|
23
|
+
|
|
24
|
+
Glossarist::LocalizedConcept.of_yaml(
|
|
25
|
+
"data" => {
|
|
26
|
+
"language_code" => lang_set.language_code,
|
|
27
|
+
"terms" => terms,
|
|
28
|
+
"definition" => build_definitions(lang_set.definition_text),
|
|
29
|
+
"notes" => build_detailed_definitions(lang_set.note_texts),
|
|
30
|
+
"examples" => build_detailed_definitions(lang_set.example_texts),
|
|
31
|
+
"sources" => build_sources(lang_set.source_texts, source_ref),
|
|
32
|
+
"domain" => lang_set.domain,
|
|
33
|
+
"entry_status" => "valid",
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def build_definitions(text)
|
|
39
|
+
return [] unless text && !text.empty?
|
|
40
|
+
|
|
41
|
+
[{ "content" => text }]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def build_detailed_definitions(texts)
|
|
45
|
+
texts.filter_map do |text|
|
|
46
|
+
next if text.empty?
|
|
47
|
+
|
|
48
|
+
{ "content" => text }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def build_designation(ext_desig)
|
|
53
|
+
case ext_desig.type
|
|
54
|
+
when "abbreviation"
|
|
55
|
+
build_abbreviation_designation(ext_desig)
|
|
56
|
+
when "symbol"
|
|
57
|
+
build_symbol_designation(ext_desig)
|
|
58
|
+
else
|
|
59
|
+
build_expression_designation(ext_desig)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def build_expression_designation(ext_desig)
|
|
64
|
+
hash = {
|
|
65
|
+
"type" => "expression",
|
|
66
|
+
"designation" => ext_desig.term,
|
|
67
|
+
"normative_status" => ext_desig.normative_status,
|
|
68
|
+
}.compact
|
|
69
|
+
|
|
70
|
+
if ext_desig.part_of_speech
|
|
71
|
+
hash["grammar_info"] =
|
|
72
|
+
[{ "part_of_speech" => ext_desig.part_of_speech }]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
hash
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def build_abbreviation_designation(ext_desig)
|
|
79
|
+
{
|
|
80
|
+
"type" => "abbreviation",
|
|
81
|
+
"designation" => ext_desig.term,
|
|
82
|
+
"normative_status" => ext_desig.normative_status,
|
|
83
|
+
"abbreviation_type" => ext_desig.abbreviation_type,
|
|
84
|
+
}.compact
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def build_symbol_designation(ext_desig)
|
|
88
|
+
{
|
|
89
|
+
"type" => "symbol",
|
|
90
|
+
"designation" => ext_desig.term,
|
|
91
|
+
"normative_status" => ext_desig.normative_status,
|
|
92
|
+
}.compact
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def build_sources(source_texts, source_ref)
|
|
96
|
+
sources = []
|
|
97
|
+
if source_ref
|
|
98
|
+
sources << {
|
|
99
|
+
"status" => "identical",
|
|
100
|
+
"type" => "authoritative",
|
|
101
|
+
"origin" => { "text" => source_ref },
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
source_texts.each do |text|
|
|
106
|
+
next if text.empty?
|
|
107
|
+
|
|
108
|
+
sources << {
|
|
109
|
+
"type" => "authoritative",
|
|
110
|
+
"origin" => { "text" => text },
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
sources
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sts"
|
|
4
|
+
|
|
5
|
+
module Glossarist
|
|
6
|
+
module Sts
|
|
7
|
+
autoload :ExtractedDesignation, "#{__dir__}/sts/extracted_designation"
|
|
8
|
+
autoload :ExtractedLangSet, "#{__dir__}/sts/extracted_lang_set"
|
|
9
|
+
autoload :ExtractedTerm, "#{__dir__}/sts/extracted_term"
|
|
10
|
+
autoload :ImportResult, "#{__dir__}/sts/import_result"
|
|
11
|
+
autoload :Importer, "#{__dir__}/sts/importer"
|
|
12
|
+
autoload :TermExtractor, "#{__dir__}/sts/term_extractor"
|
|
13
|
+
autoload :TermMapper, "#{__dir__}/sts/term_mapper"
|
|
14
|
+
|
|
15
|
+
ISO_639_1_TO_639_2 = {
|
|
16
|
+
"aa" => "aar", "ab" => "abk", "af" => "afr", "ak" => "aka",
|
|
17
|
+
"am" => "amh", "an" => "arg", "ar" => "ara", "as" => "asm",
|
|
18
|
+
"av" => "ava", "ay" => "aym", "az" => "aze", "ba" => "bak",
|
|
19
|
+
"be" => "bel", "bg" => "bul", "bh" => "bih", "bi" => "bis",
|
|
20
|
+
"bm" => "bam", "bn" => "ben", "bo" => "bod", "br" => "bre",
|
|
21
|
+
"bs" => "bos", "ca" => "cat", "ce" => "che", "ch" => "cha",
|
|
22
|
+
"co" => "cos", "cr" => "cre", "cs" => "ces", "cu" => "chu",
|
|
23
|
+
"cv" => "chv", "cy" => "cym", "da" => "dan", "de" => "deu",
|
|
24
|
+
"dv" => "div", "dz" => "dzo", "ee" => "ewe", "el" => "ell",
|
|
25
|
+
"en" => "eng", "eo" => "epo", "es" => "spa", "et" => "est",
|
|
26
|
+
"eu" => "eus", "fa" => "fas", "ff" => "ful", "fi" => "fin",
|
|
27
|
+
"fj" => "fij", "fo" => "fao", "fr" => "fra", "fy" => "fry",
|
|
28
|
+
"ga" => "gle", "gd" => "gla", "gl" => "glg", "gn" => "grn",
|
|
29
|
+
"gu" => "guj", "gv" => "glv", "ha" => "hau", "he" => "heb",
|
|
30
|
+
"hi" => "hin", "ho" => "hmo", "hr" => "hrv", "ht" => "hat",
|
|
31
|
+
"hu" => "hun", "hy" => "hye", "hz" => "her", "ia" => "ina",
|
|
32
|
+
"id" => "ind", "ie" => "ile", "ig" => "ibo", "ii" => "iii",
|
|
33
|
+
"ik" => "ipk", "io" => "ido", "is" => "isl", "it" => "ita",
|
|
34
|
+
"iu" => "iku", "ja" => "jpn", "jv" => "jav", "ka" => "kat",
|
|
35
|
+
"kg" => "kon", "ki" => "kik", "kj" => "kua", "kk" => "kaz",
|
|
36
|
+
"kl" => "kal", "km" => "khm", "kn" => "kan", "ko" => "kor",
|
|
37
|
+
"kr" => "kau", "ks" => "kas", "ku" => "kur", "kv" => "kom",
|
|
38
|
+
"kw" => "cor", "ky" => "kir", "la" => "lat", "lb" => "ltz",
|
|
39
|
+
"lg" => "lug", "li" => "lim", "ln" => "lin", "lo" => "lao",
|
|
40
|
+
"lt" => "lit", "lu" => "lub", "lv" => "lav", "mg" => "mlg",
|
|
41
|
+
"mh" => "mah", "mi" => "mri", "mk" => "mkd", "ml" => "mal",
|
|
42
|
+
"mn" => "mon", "mr" => "mar", "ms" => "msa", "mt" => "mlt",
|
|
43
|
+
"my" => "mya", "na" => "nau", "nb" => "nob", "nd" => "nde",
|
|
44
|
+
"ne" => "nep", "ng" => "ndo", "nl" => "nld", "nn" => "nno",
|
|
45
|
+
"no" => "nor", "nr" => "nbl", "nv" => "nav", "ny" => "nya",
|
|
46
|
+
"oc" => "oci", "oj" => "oji", "om" => "orm", "or" => "ori",
|
|
47
|
+
"os" => "oss", "pa" => "pan", "pi" => "pli", "pl" => "pol",
|
|
48
|
+
"ps" => "pus", "pt" => "por", "qu" => "que", "rm" => "roh",
|
|
49
|
+
"rn" => "run", "ro" => "ron", "ru" => "rus", "rw" => "kin",
|
|
50
|
+
"sa" => "san", "sc" => "srd", "sd" => "snd", "se" => "sme",
|
|
51
|
+
"sg" => "sag", "si" => "sin", "sk" => "slk", "sl" => "slv",
|
|
52
|
+
"sm" => "smo", "sn" => "sna", "so" => "som", "sq" => "sqi",
|
|
53
|
+
"sr" => "srp", "ss" => "ssw", "st" => "sot", "su" => "sun",
|
|
54
|
+
"sv" => "swe", "sw" => "swa", "ta" => "tam", "te" => "tel",
|
|
55
|
+
"tg" => "tgk", "th" => "tha", "ti" => "tir", "tk" => "tuk",
|
|
56
|
+
"tl" => "tgl", "tn" => "tsn", "to" => "ton", "tr" => "tur",
|
|
57
|
+
"ts" => "tso", "tt" => "tat", "tw" => "twi", "ty" => "tah",
|
|
58
|
+
"ug" => "uig", "uk" => "ukr", "ur" => "urd", "uz" => "uzb",
|
|
59
|
+
"ve" => "ven", "vi" => "vie", "vo" => "vol", "wa" => "wln",
|
|
60
|
+
"wo" => "wol", "xh" => "xho", "yi" => "yid", "yo" => "yor",
|
|
61
|
+
"za" => "zha", "zh" => "zho", "zu" => "zul"
|
|
62
|
+
}.freeze
|
|
63
|
+
|
|
64
|
+
TERM_TYPE_MAP = {
|
|
65
|
+
"acronym" => "abbreviation",
|
|
66
|
+
"abbreviation" => "abbreviation",
|
|
67
|
+
"fullForm" => "expression",
|
|
68
|
+
"symbol" => "symbol",
|
|
69
|
+
"variant" => "expression",
|
|
70
|
+
"equation" => "expression",
|
|
71
|
+
"formula" => "expression",
|
|
72
|
+
}.freeze
|
|
73
|
+
|
|
74
|
+
NORMATIVE_STATUS_MAP = {
|
|
75
|
+
"preferredTerm" => "preferred",
|
|
76
|
+
"admittedTerm" => "admitted",
|
|
77
|
+
"deprecatedTerm" => "deprecated",
|
|
78
|
+
}.freeze
|
|
79
|
+
|
|
80
|
+
def self.convert_language_code(code)
|
|
81
|
+
return code if code.nil?
|
|
82
|
+
return code if code.length == 3
|
|
83
|
+
|
|
84
|
+
ISO_639_1_TO_639_2[code] || code
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
data/lib/glossarist/version.rb
CHANGED