glossarist 2.4.0 → 2.5.1
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 +1 -0
- data/.rubocop_todo.yml +50 -146
- data/CLAUDE.md +85 -0
- data/Gemfile +26 -5
- data/README.adoc +383 -7
- data/TODO.integration/01-gcr-package-cli.md +180 -0
- data/exe/glossarist +1 -53
- data/glossarist.gemspec +3 -2
- data/lib/glossarist/asset.rb +1 -1
- data/lib/glossarist/citation.rb +1 -1
- data/lib/glossarist/cli/package_command.rb +32 -0
- data/lib/glossarist/cli/upgrade_command.rb +34 -0
- data/lib/glossarist/cli/validate_command.rb +56 -0
- data/lib/glossarist/cli.rb +105 -0
- data/lib/glossarist/collection_config.rb +23 -0
- data/lib/glossarist/collections/concept_source_collection.rb +9 -0
- data/lib/glossarist/collections/detailed_definition_collection.rb +18 -0
- data/lib/glossarist/collections/localization_collection.rb +37 -0
- data/lib/glossarist/collections/typed_collection.rb +26 -0
- data/lib/glossarist/collections.rb +21 -4
- data/lib/glossarist/concept.rb +1 -1
- data/lib/glossarist/concept_collector.rb +153 -0
- data/lib/glossarist/concept_data.rb +15 -8
- data/lib/glossarist/concept_date.rb +1 -1
- data/lib/glossarist/concept_document.rb +29 -0
- data/lib/glossarist/concept_enricher.rb +34 -0
- data/lib/glossarist/concept_manager.rb +31 -49
- data/lib/glossarist/concept_reference.rb +45 -0
- data/lib/glossarist/concept_source.rb +1 -1
- data/lib/glossarist/concept_validator.rb +114 -0
- data/lib/glossarist/custom_locality.rb +1 -1
- data/lib/glossarist/dataset_validator.rb +69 -0
- data/lib/glossarist/designation/abbreviation.rb +1 -1
- data/lib/glossarist/designation/base.rb +11 -4
- data/lib/glossarist/designation/expression.rb +1 -1
- data/lib/glossarist/designation/grammar_info.rb +1 -1
- data/lib/glossarist/designation/graphical_symbol.rb +1 -1
- data/lib/glossarist/designation/letter_symbol.rb +1 -1
- data/lib/glossarist/designation/symbol.rb +2 -2
- data/lib/glossarist/designation.rb +8 -11
- data/lib/glossarist/detailed_definition.rb +1 -1
- data/lib/glossarist/error.rb +2 -5
- data/lib/glossarist/gcr_metadata.rb +87 -0
- data/lib/glossarist/gcr_package.rb +223 -0
- data/lib/glossarist/gcr_statistics.rb +35 -0
- data/lib/glossarist/gcr_validator.rb +98 -0
- data/lib/glossarist/locality.rb +1 -1
- data/lib/glossarist/localized_concept.rb +12 -1
- data/lib/glossarist/managed_concept.rb +1 -1
- data/lib/glossarist/managed_concept_data.rb +8 -5
- data/lib/glossarist/non_verb_rep.rb +1 -1
- data/lib/glossarist/reference_extractor.rb +227 -0
- data/lib/glossarist/reference_resolver.rb +169 -0
- data/lib/glossarist/register_data.rb +39 -0
- data/lib/glossarist/related_concept.rb +1 -1
- data/lib/glossarist/resolution_adapter/local.rb +73 -0
- data/lib/glossarist/resolution_adapter/package.rb +22 -0
- data/lib/glossarist/resolution_adapter/remote.rb +60 -0
- data/lib/glossarist/resolution_adapter/route.rb +34 -0
- data/lib/glossarist/resolution_adapter.rb +14 -0
- data/lib/glossarist/schema_migration.rb +334 -0
- data/lib/glossarist/urn_resolver.rb +71 -0
- data/lib/glossarist/utilities.rb +6 -2
- data/lib/glossarist/v1/concept.rb +81 -0
- data/lib/glossarist/v1/cross_references.rb +41 -0
- data/lib/glossarist/v1/register.rb +50 -0
- data/lib/glossarist/v1.rb +9 -0
- data/lib/glossarist/validation_result.rb +38 -0
- data/lib/glossarist/version.rb +1 -1
- data/lib/glossarist.rb +54 -24
- metadata +62 -6
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
class CLI
|
|
5
|
+
class UpgradeCommand
|
|
6
|
+
def initialize(source_dir, options)
|
|
7
|
+
@source_dir = source_dir
|
|
8
|
+
@options = options
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def run
|
|
12
|
+
result = SchemaMigration.upgrade_directory(
|
|
13
|
+
@source_dir,
|
|
14
|
+
output: @options[:output],
|
|
15
|
+
target_version: @options[:target_version],
|
|
16
|
+
cross_references: @options[:cross_references],
|
|
17
|
+
dry_run: @options[:dry_run],
|
|
18
|
+
)
|
|
19
|
+
report(result)
|
|
20
|
+
rescue ArgumentError => e
|
|
21
|
+
warn "Error: #{e.message}"
|
|
22
|
+
exit 1
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def report(result)
|
|
28
|
+
puts "Upgraded #{result[:count]} concepts " \
|
|
29
|
+
"from schema v#{result[:source_version]} to v#{result[:target_version]}"
|
|
30
|
+
puts "Output: #{result[:output]}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
class CLI
|
|
5
|
+
class ValidateCommand
|
|
6
|
+
def initialize(path, options)
|
|
7
|
+
@path = path
|
|
8
|
+
@options = options
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def run
|
|
12
|
+
result = DatasetValidator.new.validate(
|
|
13
|
+
@path,
|
|
14
|
+
strict: @options[:strict],
|
|
15
|
+
reference_path: @options[:reference_path],
|
|
16
|
+
)
|
|
17
|
+
report(result)
|
|
18
|
+
exit_code = result.errors.any? || (@options[:strict] && result.warnings.any?) ? 1 : 0
|
|
19
|
+
exit(exit_code) unless exit_code.zero?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def report(result)
|
|
25
|
+
case @options[:format]
|
|
26
|
+
when "json"
|
|
27
|
+
require "json"
|
|
28
|
+
puts JSON.pretty_generate(result.to_h)
|
|
29
|
+
when "yaml"
|
|
30
|
+
require "yaml"
|
|
31
|
+
puts YAML.dump(result.to_h)
|
|
32
|
+
else
|
|
33
|
+
report_text(result)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def report_text(result)
|
|
38
|
+
if result.valid?
|
|
39
|
+
puts "Valid."
|
|
40
|
+
else
|
|
41
|
+
puts "Invalid."
|
|
42
|
+
result.errors.each { |e| puts " ERROR: #{e}" }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if result.warnings.any?
|
|
46
|
+
result.warnings.each do |w|
|
|
47
|
+
puts " WARNING: #{w}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
total = result.errors.length + result.warnings.length
|
|
52
|
+
puts "#{total} issue(s) found."
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
|
|
5
|
+
module Glossarist
|
|
6
|
+
class CLI < Thor
|
|
7
|
+
desc "generate_latex", "Convert Concepts to Latex format"
|
|
8
|
+
|
|
9
|
+
option :concepts_path, aliases: :p, required: true,
|
|
10
|
+
desc: "Path to yaml concepts directory"
|
|
11
|
+
option :latex_concepts, aliases: :l,
|
|
12
|
+
desc: "File path having list of concepts that should be converted to LATEX format. If not provided all the concepts will be converted to the latex format"
|
|
13
|
+
option :output_file, aliases: :o,
|
|
14
|
+
desc: "Output file path. By default the output will pe printed to the console"
|
|
15
|
+
option :extra_attributes, aliases: :e, type: :array,
|
|
16
|
+
desc: "List of extra attributes that are not in standard Glossarist Concept model"
|
|
17
|
+
def generate_latex
|
|
18
|
+
assets = []
|
|
19
|
+
|
|
20
|
+
if options[:extra_attributes]
|
|
21
|
+
Glossarist.configure do |config|
|
|
22
|
+
config.register_extension_attributes(options[:extra_attributes])
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
concept_set = Glossarist::ConceptSet.new(options[:concepts_path], assets)
|
|
27
|
+
latex_str = concept_set.to_latex(options[:latex_concepts])
|
|
28
|
+
output_latex(latex_str)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
desc "upgrade SOURCE_DIR", "Upgrade dataset to current schema version"
|
|
32
|
+
option :output, aliases: :o, required: true,
|
|
33
|
+
desc: "Output directory or .gcr file path"
|
|
34
|
+
option :target_version, type: :string, default: Glossarist::SchemaMigration::CURRENT_SCHEMA_VERSION,
|
|
35
|
+
desc: "Target schema version (default: current)"
|
|
36
|
+
option :cross_references, type: :string,
|
|
37
|
+
desc: "Path to datasets.yml for cross-reference maps"
|
|
38
|
+
option :dry_run, type: :boolean, default: false,
|
|
39
|
+
desc: "Show what would change without writing"
|
|
40
|
+
def upgrade(source_dir)
|
|
41
|
+
require_relative "cli/upgrade_command"
|
|
42
|
+
Glossarist::CLI::UpgradeCommand.new(source_dir, options).run
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
desc "package DIR", "Create a .gcr ZIP archive from a schema v1 dataset"
|
|
46
|
+
option :output, aliases: :o, required: true,
|
|
47
|
+
desc: "Output .gcr file path"
|
|
48
|
+
option :shortname, type: :string, required: true,
|
|
49
|
+
desc: "Machine-readable dataset ID"
|
|
50
|
+
option :version, type: :string, required: true,
|
|
51
|
+
desc: "Semantic version (e.g. 1.0.0)"
|
|
52
|
+
option :uri_prefix, type: :string,
|
|
53
|
+
desc: "URI namespace prefix (e.g. urn:iec:std:iec:60050)"
|
|
54
|
+
option :title, type: :string, desc: "Dataset title"
|
|
55
|
+
option :description, type: :string, desc: "Dataset description"
|
|
56
|
+
option :owner, type: :string, desc: "Dataset owner"
|
|
57
|
+
option :register_yaml, type: :string,
|
|
58
|
+
desc: "Path to register.yaml to include in package"
|
|
59
|
+
option :tags, type: :array, desc: "Tags for the dataset"
|
|
60
|
+
def package(dir)
|
|
61
|
+
require_relative "cli/package_command"
|
|
62
|
+
Glossarist::CLI::PackageCommand.new(dir, options).run
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
desc "validate PATH",
|
|
66
|
+
"Validate dataset directory or .gcr for schema compliance"
|
|
67
|
+
option :strict, type: :boolean, default: false,
|
|
68
|
+
desc: "Treat warnings as errors"
|
|
69
|
+
option :format, type: :string, default: "text",
|
|
70
|
+
enum: %w[text json yaml],
|
|
71
|
+
desc: "Output format for validation results"
|
|
72
|
+
option :reference_path, type: :string,
|
|
73
|
+
desc: "Path to directory of .gcr files for cross-dataset reference validation"
|
|
74
|
+
def validate(path)
|
|
75
|
+
require_relative "cli/validate_command"
|
|
76
|
+
Glossarist::CLI::ValidateCommand.new(path, options).run
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def method_missing(*args)
|
|
80
|
+
warn "No method found named: #{args[0]}"
|
|
81
|
+
warn "Run with `--help` or `-h` to see available options"
|
|
82
|
+
exit 1
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def respond_to_missing?
|
|
86
|
+
true
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def self.exit_on_failure?
|
|
90
|
+
true
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def output_latex(latex_str)
|
|
96
|
+
output_file_path = options[:output_file]
|
|
97
|
+
|
|
98
|
+
if output_file_path
|
|
99
|
+
File.open(output_file_path, "w") { |file| file.puts latex_str }
|
|
100
|
+
else
|
|
101
|
+
puts latex_str
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
class CollectionConfig < Lutaml::Model::Serializable
|
|
5
|
+
attribute :packages, :hash, collection: true, default: -> { [] }
|
|
6
|
+
attribute :routes, :hash, collection: true, default: -> { [] }
|
|
7
|
+
attribute :remotes, :hash, collection: true, default: -> { [] }
|
|
8
|
+
|
|
9
|
+
yaml do
|
|
10
|
+
map :packages, to: :packages
|
|
11
|
+
map :routes, to: :routes
|
|
12
|
+
map :remote, to: :remotes
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.from_file(path)
|
|
16
|
+
return nil unless File.exist?(path)
|
|
17
|
+
|
|
18
|
+
from_yaml(File.read(path))
|
|
19
|
+
rescue Psych::SyntaxError, Lutaml::Model::InvalidFormatError
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module Collections
|
|
5
|
+
class DetailedDefinitionCollection < TypedCollection
|
|
6
|
+
instances :definitions, DetailedDefinition
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def coerce_other(item)
|
|
11
|
+
case item
|
|
12
|
+
when String then DetailedDefinition.new(content: item)
|
|
13
|
+
else item
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module Collections
|
|
5
|
+
class LocalizationCollection < Lutaml::Model::Collection
|
|
6
|
+
instances :localized_concepts, LocalizedConcept
|
|
7
|
+
|
|
8
|
+
index_by :language_code
|
|
9
|
+
|
|
10
|
+
def [](lang_code)
|
|
11
|
+
find_by(:language_code, lang_code.to_s)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def store(lang_code, localized_concept)
|
|
15
|
+
localized_concept.language_code ||= lang_code.to_s
|
|
16
|
+
push(localized_concept)
|
|
17
|
+
localized_concept
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def keys
|
|
21
|
+
map(&:language_code)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def values
|
|
25
|
+
to_a
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def each_key(&block)
|
|
29
|
+
keys.each(&block)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def each_value(&block)
|
|
33
|
+
values.each(&block)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module Collections
|
|
5
|
+
class TypedCollection < Lutaml::Model::Collection
|
|
6
|
+
def <<(item)
|
|
7
|
+
push(coerce(item))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def coerce(item)
|
|
13
|
+
return item if item.is_a?(self.class.instance_type)
|
|
14
|
+
|
|
15
|
+
case item
|
|
16
|
+
when Hash then self.class.instance_type.new(item)
|
|
17
|
+
else coerce_other(item)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def coerce_other(item)
|
|
22
|
+
item
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module Collections
|
|
5
|
+
autoload :AssetCollection,
|
|
6
|
+
"glossarist/collections/asset_collection"
|
|
7
|
+
autoload :BibliographyCollection,
|
|
8
|
+
"glossarist/collections/bibliography_collection"
|
|
9
|
+
autoload :Collection, "glossarist/collections/collection"
|
|
10
|
+
autoload :ConceptSourceCollection,
|
|
11
|
+
"glossarist/collections/concept_source_collection"
|
|
12
|
+
autoload :DesignationCollection,
|
|
13
|
+
"glossarist/collections/designation_collection"
|
|
14
|
+
autoload :DetailedDefinitionCollection,
|
|
15
|
+
"glossarist/collections/detailed_definition_collection"
|
|
16
|
+
autoload :LocalizationCollection,
|
|
17
|
+
"glossarist/collections/localization_collection"
|
|
18
|
+
autoload :TypedCollection,
|
|
19
|
+
"glossarist/collections/typed_collection"
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/glossarist/concept.rb
CHANGED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
class ConceptCollector
|
|
5
|
+
def self.collect(dir)
|
|
6
|
+
dir = File.expand_path(dir)
|
|
7
|
+
unless File.directory?(dir)
|
|
8
|
+
raise ArgumentError, "#{dir} is not a directory"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
if v2_concepts?(dir)
|
|
12
|
+
collect_v2_concepts(dir)
|
|
13
|
+
elsif managed_concepts?(dir)
|
|
14
|
+
collect_managed_concepts(dir)
|
|
15
|
+
elsif v1_concepts?(dir)
|
|
16
|
+
collect_v1_concepts(dir)
|
|
17
|
+
else
|
|
18
|
+
[]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.each_concept(dir, &block)
|
|
23
|
+
dir = File.expand_path(dir)
|
|
24
|
+
unless File.directory?(dir)
|
|
25
|
+
raise ArgumentError, "#{dir} is not a directory"
|
|
26
|
+
end
|
|
27
|
+
return enum_for(:each_concept, dir) unless block
|
|
28
|
+
|
|
29
|
+
if v2_concepts?(dir)
|
|
30
|
+
each_v2_concept(dir, &block)
|
|
31
|
+
elsif managed_concepts?(dir)
|
|
32
|
+
each_managed_concept(dir, &block)
|
|
33
|
+
elsif v1_concepts?(dir)
|
|
34
|
+
each_v1_concept(dir, &block)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def v1_concepts?(dir)
|
|
42
|
+
concepts_dir = File.join(dir, "concepts")
|
|
43
|
+
File.directory?(concepts_dir) &&
|
|
44
|
+
Dir.glob(File.join(concepts_dir, "*.yaml")).any? do |f|
|
|
45
|
+
V1::Concept.from_file(f)&.termid?
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def v2_concepts?(dir)
|
|
50
|
+
File.directory?(File.join(dir, "geolexica-v2"))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def managed_concepts?(dir)
|
|
54
|
+
concept_dir = File.join(dir, "concepts", "concept")
|
|
55
|
+
File.directory?(concept_dir) &&
|
|
56
|
+
Dir.glob(File.join(concept_dir, "*.yaml")).any?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def collect_v1_concepts(dir)
|
|
60
|
+
concepts = []
|
|
61
|
+
each_v1_concept(dir) { |mc| concepts << mc }
|
|
62
|
+
concepts
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def each_v1_concept(dir)
|
|
66
|
+
concepts_dir = File.join(dir, "concepts")
|
|
67
|
+
files = Dir.glob(File.join(concepts_dir, "*.yaml"))
|
|
68
|
+
files.each do |file|
|
|
69
|
+
v1 = V1::Concept.from_file(file)
|
|
70
|
+
next unless v1
|
|
71
|
+
|
|
72
|
+
yield v1.to_managed_concept
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def collect_v2_concepts(dir)
|
|
77
|
+
v2_dir = File.join(dir, "geolexica-v2")
|
|
78
|
+
if File.directory?(File.join(v2_dir, "concepts"))
|
|
79
|
+
collect_managed_concepts(v2_dir)
|
|
80
|
+
else
|
|
81
|
+
collect_grouped_v2_concepts(v2_dir)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def each_v2_concept(dir, &block)
|
|
86
|
+
v2_dir = File.join(dir, "geolexica-v2")
|
|
87
|
+
if File.directory?(File.join(v2_dir, "concepts"))
|
|
88
|
+
each_managed_concept(v2_dir, &block)
|
|
89
|
+
else
|
|
90
|
+
each_grouped_v2_concept(v2_dir, &block)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def each_grouped_v2_concept(v2_dir, &block)
|
|
95
|
+
collection = ManagedConceptCollection.new
|
|
96
|
+
manager = ConceptManager.new(path: v2_dir)
|
|
97
|
+
manager.load_from_files(collection: collection)
|
|
98
|
+
collection.each(&block)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def collect_grouped_v2_concepts(v2_dir)
|
|
102
|
+
collection = ManagedConceptCollection.new
|
|
103
|
+
manager = ConceptManager.new(path: v2_dir)
|
|
104
|
+
manager.load_from_files(collection: collection)
|
|
105
|
+
collection.to_a
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def collect_managed_concepts(dir) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
109
|
+
concepts = []
|
|
110
|
+
each_managed_concept(dir) { |mc| concepts << mc }
|
|
111
|
+
concepts
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def each_managed_concept(dir) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
115
|
+
concepts_dir = File.join(dir, "concepts")
|
|
116
|
+
concept_files = Dir.glob(File.join(concepts_dir, "concept", "*.yaml"))
|
|
117
|
+
return if concept_files.empty?
|
|
118
|
+
|
|
119
|
+
lc_dir = find_localized_concepts_dir(concepts_dir)
|
|
120
|
+
lc_index = build_lc_index(lc_dir) if lc_dir
|
|
121
|
+
|
|
122
|
+
concept_files.each do |f|
|
|
123
|
+
mc = ManagedConcept.from_yaml(File.read(f))
|
|
124
|
+
next unless mc.data&.id
|
|
125
|
+
|
|
126
|
+
lc_map = mc.data.localized_concepts || {}
|
|
127
|
+
lc_map.each_value do |uuid|
|
|
128
|
+
lc_file = lc_index ? lc_index[uuid] : nil
|
|
129
|
+
next unless lc_file
|
|
130
|
+
|
|
131
|
+
l10n = LocalizedConcept.from_yaml(File.read(lc_file))
|
|
132
|
+
mc.add_localization(l10n)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
yield mc
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def build_lc_index(lc_dir)
|
|
140
|
+
Dir.glob(File.join(lc_dir, "*.{yaml,yml}"))
|
|
141
|
+
.to_h { |f| [File.basename(f, ".*"), f] }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def find_localized_concepts_dir(concepts_dir)
|
|
145
|
+
%w[localized_concept localized-concept].each do |name|
|
|
146
|
+
d = File.join(concepts_dir, name)
|
|
147
|
+
return d if File.directory?(d)
|
|
148
|
+
end
|
|
149
|
+
nil
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -3,18 +3,24 @@ module Glossarist
|
|
|
3
3
|
include Glossarist::Utilities::CommonFunctions
|
|
4
4
|
|
|
5
5
|
attribute :dates, ConceptDate, collection: true
|
|
6
|
-
attribute :definition, DetailedDefinition,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
attribute :definition, DetailedDefinition,
|
|
7
|
+
collection: Collections::DetailedDefinitionCollection,
|
|
8
|
+
initialize_empty: true
|
|
9
|
+
attribute :examples, DetailedDefinition,
|
|
10
|
+
collection: Collections::DetailedDefinitionCollection,
|
|
11
|
+
initialize_empty: true
|
|
10
12
|
attribute :id, :string
|
|
11
13
|
attribute :lineage_source_similarity, :integer
|
|
12
|
-
attribute :notes, DetailedDefinition,
|
|
13
|
-
|
|
14
|
+
attribute :notes, DetailedDefinition,
|
|
15
|
+
collection: Collections::DetailedDefinitionCollection,
|
|
16
|
+
initialize_empty: true
|
|
14
17
|
attribute :release, :string
|
|
15
|
-
attribute :sources, ConceptSource,
|
|
18
|
+
attribute :sources, ConceptSource,
|
|
19
|
+
collection: Collections::ConceptSourceCollection,
|
|
20
|
+
initialize_empty: true
|
|
16
21
|
attribute :terms, Designation::Base, collection: true
|
|
17
22
|
attribute :related, RelatedConcept, collection: true
|
|
23
|
+
attribute :references, ConceptReference, collection: true
|
|
18
24
|
attribute :domain, :string
|
|
19
25
|
attribute :review_date, :date_time
|
|
20
26
|
attribute :review_decision_date, :date_time
|
|
@@ -26,7 +32,7 @@ module Glossarist
|
|
|
26
32
|
attribute :language_code, :string, pattern: /^.{3}$/
|
|
27
33
|
attribute :entry_status, :string
|
|
28
34
|
|
|
29
|
-
|
|
35
|
+
key_value do
|
|
30
36
|
map :dates, to: :dates
|
|
31
37
|
map :definition, to: :definition, value_map: { to: { empty: :empty } }
|
|
32
38
|
map :examples, to: :examples, value_map: { to: { empty: :empty } }
|
|
@@ -39,6 +45,7 @@ module Glossarist
|
|
|
39
45
|
map :terms, to: :terms,
|
|
40
46
|
with: { from: :terms_from_yaml, to: :terms_to_yaml }
|
|
41
47
|
map :related, to: :related
|
|
48
|
+
map :references, to: :references
|
|
42
49
|
map :domain, to: :domain
|
|
43
50
|
map %i[language_code languageCode], to: :language_code
|
|
44
51
|
map %i[entry_status entryStatus], to: :entry_status
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
class ConceptDocument < Lutaml::Model::Serializable
|
|
5
|
+
attribute :concept, ManagedConcept
|
|
6
|
+
attribute :localizations, LocalizedConcept, collection: true
|
|
7
|
+
|
|
8
|
+
yamls do
|
|
9
|
+
sequence do
|
|
10
|
+
map_document 0, to: :concept, type: ManagedConcept
|
|
11
|
+
map_document 1.., to: :localizations, type: LocalizedConcept,
|
|
12
|
+
collection: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.from_managed_concept(managed_concept)
|
|
17
|
+
new(
|
|
18
|
+
concept: managed_concept,
|
|
19
|
+
localizations: managed_concept.localizations&.values || [],
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_managed_concept
|
|
24
|
+
mc = concept
|
|
25
|
+
localizations.each { |l10n| mc.add_localization(l10n) }
|
|
26
|
+
mc
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
class ConceptEnricher
|
|
5
|
+
def inject_references(concepts) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
6
|
+
extractor = ReferenceExtractor.new
|
|
7
|
+
|
|
8
|
+
concepts.each do |mc|
|
|
9
|
+
mc.localizations.each do |l10n|
|
|
10
|
+
refs = extractor.extract_from_localized_concept(l10n)
|
|
11
|
+
next if refs.empty?
|
|
12
|
+
|
|
13
|
+
existing = l10n.data.references || []
|
|
14
|
+
seen_keys = existing.to_set { |r| [r.source, r.concept_id] }
|
|
15
|
+
|
|
16
|
+
refs.each do |ref|
|
|
17
|
+
key = [ref.source, ref.concept_id]
|
|
18
|
+
next if seen_keys.include?(key)
|
|
19
|
+
|
|
20
|
+
seen_keys.add(key)
|
|
21
|
+
existing << ref
|
|
22
|
+
end
|
|
23
|
+
l10n.data.references = existing
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def apply_uri_template(concepts, template)
|
|
29
|
+
concepts.each do |mc|
|
|
30
|
+
mc.data.uri = template.sub("{id}", mc.data.id.to_s)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|