glossarist 2.8.2 → 2.8.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/.rubocop_todo.yml +413 -63
- data/Gemfile +1 -0
- data/glossarist.gemspec +1 -1
- data/lib/glossarist/bibliography_data.rb +41 -0
- data/lib/glossarist/bibliography_entry.rb +13 -0
- data/lib/glossarist/citation.rb +8 -2
- data/lib/glossarist/cli/export_command.rb +10 -5
- data/lib/glossarist/cli/validate_command.rb +21 -5
- data/lib/glossarist/collection.rb +2 -2
- data/lib/glossarist/collections/bibliography_collection.rb +2 -1
- data/lib/glossarist/collections/collection.rb +2 -2
- data/lib/glossarist/collections/localization_collection.rb +4 -4
- data/lib/glossarist/concept_collector.rb +6 -6
- data/lib/glossarist/concept_document.rb +2 -1
- data/lib/glossarist/concept_manager.rb +6 -7
- data/lib/glossarist/concept_set.rb +4 -4
- data/lib/glossarist/concept_store.rb +38 -48
- data/lib/glossarist/dataset_validator.rb +2 -1
- data/lib/glossarist/gcr_package_definition.rb +37 -0
- data/lib/glossarist/gcr_statistics.rb +2 -2
- data/lib/glossarist/glossary_definition.rb +1 -1
- data/lib/glossarist/glossary_store.rb +201 -0
- data/lib/glossarist/managed_concept_collection.rb +2 -2
- data/lib/glossarist/rdf/gloss_citation.rb +8 -4
- data/lib/glossarist/rdf/gloss_concept.rb +6 -3
- data/lib/glossarist/rdf/gloss_concept_date.rb +4 -2
- data/lib/glossarist/rdf/gloss_concept_reference.rb +6 -3
- data/lib/glossarist/rdf/gloss_concept_source.rb +6 -3
- data/lib/glossarist/rdf/gloss_designation.rb +56 -25
- data/lib/glossarist/rdf/gloss_grammar_info.rb +19 -9
- data/lib/glossarist/rdf/gloss_locality.rb +6 -3
- data/lib/glossarist/rdf/gloss_localized_concept.rb +14 -7
- data/lib/glossarist/rdf/gloss_non_verbal_rep.rb +9 -4
- data/lib/glossarist/rdf/gloss_pronunciation.rb +13 -6
- data/lib/glossarist/rdf/gloss_reference.rb +8 -4
- data/lib/glossarist/rdf/namespaces.rb +3 -2
- data/lib/glossarist/rdf/relationship_predicates.rb +14 -7
- data/lib/glossarist/rdf.rb +2 -1
- data/lib/glossarist/register_data.rb +68 -18
- data/lib/glossarist/schema_migration.rb +8 -5
- data/lib/glossarist/transforms/concept_to_gloss_transform.rb +23 -10
- data/lib/glossarist/v2/concept_data.rb +2 -1
- data/lib/glossarist/v2/concept_document.rb +1 -1
- data/lib/glossarist/v3/concept_data.rb +2 -1
- data/lib/glossarist/v3/concept_document.rb +1 -1
- data/lib/glossarist/validation/asset_index.rb +2 -2
- data/lib/glossarist/validation/bibliography_index.rb +2 -1
- data/lib/glossarist/validation/rules/asciidoc_xref_rule.rb +2 -1
- data/lib/glossarist/validation/rules/authoritative_source_rule.rb +1 -1
- data/lib/glossarist/validation/rules/bibliography_yaml_rule.rb +1 -1
- data/lib/glossarist/validation/rules/citation_completeness_rule.rb +1 -1
- data/lib/glossarist/validation/rules/concept_count_rule.rb +2 -3
- data/lib/glossarist/validation/rules/concept_id_rule.rb +0 -1
- data/lib/glossarist/validation/rules/concept_id_uniqueness_rule.rb +0 -1
- data/lib/glossarist/validation/rules/concept_mention_rule.rb +1 -2
- data/lib/glossarist/validation/rules/concept_status_rule.rb +1 -2
- data/lib/glossarist/validation/rules/concept_uri_rule.rb +1 -1
- data/lib/glossarist/validation/rules/date_type_rule.rb +5 -3
- data/lib/glossarist/validation/rules/date_validity_rule.rb +1 -1
- data/lib/glossarist/validation/rules/definition_content_rule.rb +1 -2
- data/lib/glossarist/validation/rules/domain_target_rule.rb +1 -1
- data/lib/glossarist/validation/rules/duplicate_term_rule.rb +1 -2
- data/lib/glossarist/validation/rules/entry_status_rule.rb +1 -2
- data/lib/glossarist/validation/rules/filename_id_rule.rb +2 -3
- data/lib/glossarist/validation/rules/image_reference_rule.rb +3 -2
- data/lib/glossarist/validation/rules/l10n_uuid_integrity_rule.rb +1 -2
- data/lib/glossarist/validation/rules/language_coverage_rule.rb +1 -2
- data/lib/glossarist/validation/rules/language_list_rule.rb +2 -3
- data/lib/glossarist/validation/rules/locality_completeness_rule.rb +3 -1
- data/lib/glossarist/validation/rules/localization_consistency_rule.rb +1 -1
- data/lib/glossarist/validation/rules/localization_presence_rule.rb +0 -1
- data/lib/glossarist/validation/rules/model_validity_rule.rb +1 -1
- data/lib/glossarist/validation/rules/orphaned_bibliography_rule.rb +2 -1
- data/lib/glossarist/validation/rules/orphaned_images_rule.rb +6 -4
- data/lib/glossarist/validation/rules/orphaned_l10n_files_rule.rb +1 -2
- data/lib/glossarist/validation/rules/preferred_term_rule.rb +1 -2
- data/lib/glossarist/validation/rules/related_concept_cycle_rule.rb +2 -2
- data/lib/glossarist/validation/rules/related_concept_rule.rb +1 -2
- data/lib/glossarist/validation/rules/related_concept_symmetry_rule.rb +1 -1
- data/lib/glossarist/validation/rules/related_concept_target_rule.rb +1 -1
- data/lib/glossarist/validation/rules/source_type_rule.rb +2 -2
- data/lib/glossarist/validation/rules/source_urn_format_rule.rb +2 -2
- data/lib/glossarist/validation/rules/terms_presence_rule.rb +1 -1
- data/lib/glossarist/validation/rules/uuid_format_rule.rb +1 -1
- data/lib/glossarist/version.rb +1 -1
- data/lib/glossarist.rb +4 -0
- data/scripts/migrate_dataset.rb +14 -8
- data/scripts/migrate_isotc204_to_v3.rb +3 -1
- data/scripts/migrate_isotc211_to_v3.rb +5 -3
- data/scripts/migrate_osgeo_to_v3.rb +4 -2
- data/scripts/upgrade_dataset_to_v3.rb +0 -0
- metadata +13 -5
data/lib/glossarist/citation.rb
CHANGED
|
@@ -56,8 +56,14 @@ module Glossarist
|
|
|
56
56
|
|
|
57
57
|
doc["locality"] = {}
|
|
58
58
|
doc["locality"]["type"] = model.locality.type
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
if model.locality.reference_from
|
|
60
|
+
doc["locality"]["reference_from"] =
|
|
61
|
+
model.locality.reference_from
|
|
62
|
+
end
|
|
63
|
+
if model.locality.reference_to
|
|
64
|
+
doc["locality"]["reference_to"] =
|
|
65
|
+
model.locality.reference_to
|
|
66
|
+
end
|
|
61
67
|
end
|
|
62
68
|
end
|
|
63
69
|
end
|
|
@@ -74,14 +74,18 @@ module Glossarist
|
|
|
74
74
|
|
|
75
75
|
def export_jsonld(concepts, name, output_dir)
|
|
76
76
|
require "glossarist/transforms/concept_to_gloss_transform"
|
|
77
|
-
transform = Transforms::ConceptToGlossTransform.new(nil,
|
|
78
|
-
|
|
77
|
+
transform = Transforms::ConceptToGlossTransform.new(nil,
|
|
78
|
+
transform_options)
|
|
79
|
+
File.write(File.join(output_dir, "#{name}.jsonld"),
|
|
80
|
+
transform.to_jsonld(concepts))
|
|
79
81
|
end
|
|
80
82
|
|
|
81
83
|
def export_turtle(concepts, name, output_dir)
|
|
82
84
|
require "glossarist/transforms/concept_to_gloss_transform"
|
|
83
|
-
transform = Transforms::ConceptToGlossTransform.new(nil,
|
|
84
|
-
|
|
85
|
+
transform = Transforms::ConceptToGlossTransform.new(nil,
|
|
86
|
+
transform_options)
|
|
87
|
+
File.write(File.join(output_dir, "#{name}.ttl"),
|
|
88
|
+
transform.to_turtle(concepts))
|
|
85
89
|
end
|
|
86
90
|
|
|
87
91
|
def export_tbx(concepts, name, output_dir)
|
|
@@ -95,7 +99,8 @@ module Glossarist
|
|
|
95
99
|
require "glossarist/transforms/concept_to_gloss_transform"
|
|
96
100
|
File.open(File.join(output_dir, "#{name}.jsonl"), "w") do |f|
|
|
97
101
|
concepts.each do |concept|
|
|
98
|
-
transform = Transforms::ConceptToGlossTransform.new(concept,
|
|
102
|
+
transform = Transforms::ConceptToGlossTransform.new(concept,
|
|
103
|
+
transform_options)
|
|
99
104
|
f.write(transform.to_jsonl_line)
|
|
100
105
|
f.write("\n")
|
|
101
106
|
end
|
|
@@ -37,7 +37,8 @@ module Glossarist
|
|
|
37
37
|
filled = (current.to_f / total * bar_width).round
|
|
38
38
|
bar = "#{'█' * filled}#{'░' * (bar_width - filled)}"
|
|
39
39
|
|
|
40
|
-
$stderr.print "\r #{Paint['Validating',
|
|
40
|
+
$stderr.print "\r #{Paint['Validating',
|
|
41
|
+
:bold]} #{bar} #{current}/#{total} (#{pct}%)"
|
|
41
42
|
$stderr.flush
|
|
42
43
|
end
|
|
43
44
|
|
|
@@ -96,18 +97,33 @@ module Glossarist
|
|
|
96
97
|
msg_col = 21
|
|
97
98
|
|
|
98
99
|
puts " #{label} #{code} #{issue.message}"
|
|
99
|
-
|
|
100
|
+
if issue.suggestion
|
|
101
|
+
puts "#{' ' * msg_col}#{Paint[issue.suggestion,
|
|
102
|
+
:green]}"
|
|
103
|
+
end
|
|
100
104
|
end
|
|
101
105
|
|
|
102
106
|
def print_summary_line(result)
|
|
103
107
|
error_count = result.issues.count(&:error?)
|
|
104
108
|
warning_count = result.issues.count(&:warning?)
|
|
105
109
|
|
|
106
|
-
status = error_count.positive?
|
|
110
|
+
status = if error_count.positive?
|
|
111
|
+
Paint["INVALID", :red,
|
|
112
|
+
:bold]
|
|
113
|
+
else
|
|
114
|
+
Paint["VALID", :green,
|
|
115
|
+
:bold]
|
|
116
|
+
end
|
|
107
117
|
|
|
108
118
|
details = []
|
|
109
|
-
|
|
110
|
-
|
|
119
|
+
if error_count.positive?
|
|
120
|
+
details << Paint["#{error_count} error(s)",
|
|
121
|
+
:red]
|
|
122
|
+
end
|
|
123
|
+
if warning_count.positive?
|
|
124
|
+
details << Paint["#{warning_count} warning(s)",
|
|
125
|
+
:yellow]
|
|
126
|
+
end
|
|
111
127
|
|
|
112
128
|
puts " #{status} #{details.join(', ')}"
|
|
113
129
|
end
|
|
@@ -7,7 +7,7 @@ module Glossarist
|
|
|
7
7
|
class BibliographyCollection < Relaton::Db
|
|
8
8
|
def initialize(_concepts, global_cache, local_cache)
|
|
9
9
|
@version_mismatch = check_cache_version(local_cache) ||
|
|
10
|
-
|
|
10
|
+
check_cache_version(global_cache)
|
|
11
11
|
super(global_cache, local_cache)
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -40,6 +40,7 @@ module Glossarist
|
|
|
40
40
|
concepts.each do |concept|
|
|
41
41
|
concept.default_lang.sources.each do |source|
|
|
42
42
|
next if source.origin.ref.nil?
|
|
43
|
+
|
|
43
44
|
ref_text = source.origin.ref.source
|
|
44
45
|
next if ref_text.nil?
|
|
45
46
|
|
|
@@ -136,25 +136,25 @@ module Glossarist
|
|
|
136
136
|
end
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
-
def each_v2_concept(dir, &
|
|
139
|
+
def each_v2_concept(dir, &)
|
|
140
140
|
if v2_flat_concepts?(dir)
|
|
141
|
-
each_grouped_v2_concepts(File.join(dir, "concepts"), &
|
|
141
|
+
each_grouped_v2_concepts(File.join(dir, "concepts"), &)
|
|
142
142
|
else
|
|
143
143
|
v2_dir = File.join(dir, "geolexica-v2")
|
|
144
144
|
if File.directory?(File.join(v2_dir, "concepts"))
|
|
145
|
-
each_managed_concept(v2_dir, &
|
|
145
|
+
each_managed_concept(v2_dir, &)
|
|
146
146
|
else
|
|
147
|
-
each_grouped_v2_concepts(v2_dir, &
|
|
147
|
+
each_grouped_v2_concepts(v2_dir, &)
|
|
148
148
|
end
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
def each_grouped_v2_concepts(v2_dir, &
|
|
152
|
+
def each_grouped_v2_concepts(v2_dir, &)
|
|
153
153
|
collection = ManagedConceptCollection.new
|
|
154
154
|
manager = ConceptManager.new(path: v2_dir)
|
|
155
155
|
manager.version = detect_schema_version(v2_dir)
|
|
156
156
|
manager.load_from_files(collection: collection)
|
|
157
|
-
collection.each(&
|
|
157
|
+
collection.each(&)
|
|
158
158
|
end
|
|
159
159
|
|
|
160
160
|
def collect_grouped_v2_concepts(v2_dir)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Glossarist
|
|
4
4
|
class ConceptDocument < Lutaml::Model::Serializable
|
|
5
|
+
attribute :id, :string
|
|
5
6
|
attribute :concept, ManagedConcept
|
|
6
7
|
attribute :localizations, LocalizedConcept, collection: true
|
|
7
8
|
|
|
@@ -9,7 +10,7 @@ module Glossarist
|
|
|
9
10
|
sequence do
|
|
10
11
|
map_document 0, to: :concept, type: ManagedConcept
|
|
11
12
|
map_document 1.., to: :localizations, type: LocalizedConcept,
|
|
12
|
-
|
|
13
|
+
collection: true
|
|
13
14
|
end
|
|
14
15
|
end
|
|
15
16
|
|
|
@@ -132,13 +132,12 @@ module Glossarist
|
|
|
132
132
|
if v1_collection?
|
|
133
133
|
File.join(path, "concept-*.{yaml,yml}")
|
|
134
134
|
else
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
concepts_glob
|
|
135
|
+
candidates = [
|
|
136
|
+
File.join(path, "concept", "*.{yaml,yml}"),
|
|
137
|
+
File.join(path, "concepts", "*.{yaml,yml}"),
|
|
138
|
+
File.join(path, "*.{yaml,yml}"),
|
|
139
|
+
]
|
|
140
|
+
candidates.find { |g| !Dir.glob(g).empty? }
|
|
142
141
|
end
|
|
143
142
|
end
|
|
144
143
|
|
|
@@ -36,7 +36,7 @@ module Glossarist
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def to_latex_from_file(entries_file)
|
|
39
|
-
File.readlines(entries_file).
|
|
39
|
+
File.readlines(entries_file).filter_map do |concept_name|
|
|
40
40
|
concept = concept_map[concept_name.strip.downcase]
|
|
41
41
|
|
|
42
42
|
if concept.nil?
|
|
@@ -44,7 +44,7 @@ module Glossarist
|
|
|
44
44
|
else
|
|
45
45
|
latex_template(concept)
|
|
46
46
|
end
|
|
47
|
-
end.
|
|
47
|
+
end.join("\n")
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def read_concepts(concepts)
|
|
@@ -72,9 +72,9 @@ module Glossarist
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def concept_map
|
|
75
|
-
@concept_map ||= concepts.managed_concepts.
|
|
75
|
+
@concept_map ||= concepts.managed_concepts.to_h do |concept|
|
|
76
76
|
[concept.default_designation.downcase, concept]
|
|
77
|
-
end
|
|
77
|
+
end
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
end
|
|
@@ -4,26 +4,23 @@ require "lutaml/store"
|
|
|
4
4
|
|
|
5
5
|
module Glossarist
|
|
6
6
|
class ConceptStore
|
|
7
|
-
#
|
|
8
|
-
# YAML string
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# model exactly, and explicit metadata fields let the store index and
|
|
12
|
-
# query by uuid/identifier without deserializing.
|
|
13
|
-
class Serializer
|
|
14
|
-
def serialize(model)
|
|
7
|
+
# Serializes ConceptDocument for storage in lutaml-store.
|
|
8
|
+
# Stores the YAML stream string to preserve the full concept + localizations.
|
|
9
|
+
class ConceptDocumentSerializer
|
|
10
|
+
def serialize(concept_document)
|
|
15
11
|
{
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"_identifier" => model.identifier,
|
|
12
|
+
"_yamls" => concept_document.to_yamls,
|
|
13
|
+
"_id" => concept_document.id,
|
|
19
14
|
}
|
|
20
15
|
end
|
|
21
16
|
|
|
22
17
|
def deserialize(data, model_class)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
doc = model_class.from_yamls(data["_yamls"])
|
|
19
|
+
doc.id = data["_id"]
|
|
20
|
+
concept = doc.concept
|
|
21
|
+
concept.uuid = doc.id if doc.id && concept
|
|
22
|
+
doc.localizations.each { |l10n| concept&.add_localization(l10n) }
|
|
23
|
+
doc
|
|
27
24
|
end
|
|
28
25
|
end
|
|
29
26
|
|
|
@@ -32,62 +29,55 @@ module Glossarist
|
|
|
32
29
|
def initialize(adapter: :memory)
|
|
33
30
|
@db = Lutaml::Store::DatabaseStore.new(
|
|
34
31
|
adapter: adapter,
|
|
35
|
-
models: [
|
|
32
|
+
models: [concept_document_registration],
|
|
36
33
|
)
|
|
37
34
|
end
|
|
38
35
|
|
|
39
|
-
def
|
|
40
|
-
db.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def fetch(uuid)
|
|
44
|
-
db.fetch(model: ManagedConcept, uuid: uuid)
|
|
45
|
-
end
|
|
36
|
+
def load_glossary(path)
|
|
37
|
+
documents = db.load_all(
|
|
38
|
+
V3::ConceptDocument, path: path, format: :yamls, layout: :grouped
|
|
39
|
+
)
|
|
46
40
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
documents.each do |doc|
|
|
42
|
+
concept = doc.concept
|
|
43
|
+
concept.uuid = doc.id
|
|
44
|
+
db.save(doc)
|
|
45
|
+
end
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
db.update(model: ManagedConcept, uuid: uuid, attributes: attributes)
|
|
47
|
+
documents
|
|
53
48
|
end
|
|
54
49
|
|
|
55
|
-
def
|
|
56
|
-
db.
|
|
50
|
+
def fetch(uuid)
|
|
51
|
+
doc = db.fetch(model: V3::ConceptDocument, id: uuid)
|
|
52
|
+
doc&.concept
|
|
57
53
|
end
|
|
58
54
|
|
|
59
|
-
def
|
|
60
|
-
db.all(model:
|
|
55
|
+
def concepts
|
|
56
|
+
db.all(model: V3::ConceptDocument).map(&:concept)
|
|
61
57
|
end
|
|
62
58
|
|
|
63
59
|
def count
|
|
64
|
-
db.count(model:
|
|
60
|
+
db.count(model: V3::ConceptDocument)
|
|
65
61
|
end
|
|
66
62
|
|
|
67
63
|
def exists?(uuid)
|
|
68
|
-
db.exists?(model:
|
|
64
|
+
db.exists?(model: V3::ConceptDocument, id: uuid)
|
|
69
65
|
end
|
|
70
66
|
|
|
71
67
|
def clear
|
|
72
|
-
all.each
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def load_from_directory(path, format: :yaml, layout: :separate)
|
|
76
|
-
db.import_all(ManagedConcept, path: path, format: format, layout: layout)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def save_to_directory(path, format: :yaml, layout: :separate)
|
|
80
|
-
db.save_all(all, path: path, format: format, layout: layout)
|
|
68
|
+
db.all(model: V3::ConceptDocument).each do |doc|
|
|
69
|
+
db.destroy(model: V3::ConceptDocument, id: doc.id)
|
|
70
|
+
end
|
|
81
71
|
end
|
|
82
72
|
|
|
83
73
|
private
|
|
84
74
|
|
|
85
|
-
def
|
|
75
|
+
def concept_document_registration
|
|
86
76
|
{
|
|
87
|
-
model:
|
|
88
|
-
key: :
|
|
89
|
-
dir: "
|
|
90
|
-
serializer:
|
|
77
|
+
model: V3::ConceptDocument,
|
|
78
|
+
key: :id,
|
|
79
|
+
dir: "concepts",
|
|
80
|
+
serializer: ConceptDocumentSerializer.new,
|
|
91
81
|
}
|
|
92
82
|
end
|
|
93
83
|
end
|
|
@@ -28,7 +28,8 @@ module Glossarist
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def validate_directory(path, reference_path: nil)
|
|
31
|
-
result = ConceptValidator.new(path,
|
|
31
|
+
result = ConceptValidator.new(path,
|
|
32
|
+
on_progress: @on_progress).validate_all
|
|
32
33
|
|
|
33
34
|
if reference_path
|
|
34
35
|
ref_result = validate_directory_cross_references(path, reference_path)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/store"
|
|
4
|
+
|
|
5
|
+
module Glossarist
|
|
6
|
+
module GcrPackageDefinition
|
|
7
|
+
def self.definition(concept_document_class: V3::ConceptDocument)
|
|
8
|
+
Lutaml::Store::PackageDefinition.new(
|
|
9
|
+
name: :gcr,
|
|
10
|
+
metadata_model: GcrMetadata,
|
|
11
|
+
metadata_file: "metadata.yaml",
|
|
12
|
+
) do |pkg|
|
|
13
|
+
pkg.model(
|
|
14
|
+
model: concept_document_class,
|
|
15
|
+
dir: "concepts",
|
|
16
|
+
layout: :grouped,
|
|
17
|
+
key: :id,
|
|
18
|
+
default_format: :yamls,
|
|
19
|
+
serializer: ConceptStore::ConceptDocumentSerializer.new,
|
|
20
|
+
)
|
|
21
|
+
pkg.model(
|
|
22
|
+
model: RegisterData,
|
|
23
|
+
file: "register.yaml",
|
|
24
|
+
key: :key,
|
|
25
|
+
default_format: :yaml,
|
|
26
|
+
)
|
|
27
|
+
pkg.model(
|
|
28
|
+
model: BibliographyData,
|
|
29
|
+
file: "bibliography.yaml",
|
|
30
|
+
key: :shortname,
|
|
31
|
+
default_format: :yaml,
|
|
32
|
+
)
|
|
33
|
+
pkg.asset("images", type: :directory)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -21,8 +21,8 @@ module Glossarist
|
|
|
21
21
|
|
|
22
22
|
new(
|
|
23
23
|
total_concepts: concepts.length,
|
|
24
|
-
languages: l10ns.
|
|
25
|
-
concepts_by_status: l10ns.
|
|
24
|
+
languages: l10ns.filter_map(&:language_code).sort.uniq,
|
|
25
|
+
concepts_by_status: l10ns.filter_map(&:entry_status).tally,
|
|
26
26
|
concepts_with_definitions: count_with(l10ns, :definition),
|
|
27
27
|
concepts_with_sources: count_with(l10ns, :sources),
|
|
28
28
|
)
|
|
@@ -30,7 +30,7 @@ module Glossarist
|
|
|
30
30
|
CONCEPT_STATUSES = config.dig("concept", "status").freeze
|
|
31
31
|
|
|
32
32
|
DESIGNATION_RELATIONSHIP_TYPES = config.dig("designation",
|
|
33
|
-
|
|
33
|
+
"relationship_type")&.freeze
|
|
34
34
|
|
|
35
35
|
ISO12620_TERM_TYPES = config.dig("iso12620", "term_type").freeze
|
|
36
36
|
end
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/store"
|
|
4
|
+
require "zip"
|
|
5
|
+
|
|
6
|
+
module Glossarist
|
|
7
|
+
class GlossaryStore
|
|
8
|
+
attr_reader :package
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@package = nil
|
|
12
|
+
@concept_document_class = V3::ConceptDocument
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# ── Load ──
|
|
16
|
+
|
|
17
|
+
def load_directory(path, format: nil)
|
|
18
|
+
metadata = load_metadata_from_directory(path)
|
|
19
|
+
@concept_document_class = resolve_concept_document_class(metadata)
|
|
20
|
+
|
|
21
|
+
definition = GcrPackageDefinition.definition(
|
|
22
|
+
concept_document_class: @concept_document_class,
|
|
23
|
+
)
|
|
24
|
+
@package = Lutaml::Store::PackageStore.load(
|
|
25
|
+
definition, path, transport: :directory, format: format
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
apply_metadata(metadata)
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def load_zip(path, format: nil)
|
|
33
|
+
metadata = load_metadata_from_zip(path)
|
|
34
|
+
@concept_document_class = resolve_concept_document_class(metadata)
|
|
35
|
+
|
|
36
|
+
definition = GcrPackageDefinition.definition(
|
|
37
|
+
concept_document_class: @concept_document_class,
|
|
38
|
+
)
|
|
39
|
+
@package = Lutaml::Store::PackageStore.load(
|
|
40
|
+
definition, path, transport: :zip, format: format
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
apply_metadata(metadata)
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def load(path, format: nil)
|
|
48
|
+
ext = File.extname(path).downcase
|
|
49
|
+
if [".gcr", ".zip"].include?(ext)
|
|
50
|
+
load_zip(path, format: format)
|
|
51
|
+
else
|
|
52
|
+
load_directory(path, format: format)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# ── Save ──
|
|
57
|
+
|
|
58
|
+
def save_directory(path, format: nil, formats: {})
|
|
59
|
+
@package.save(path, transport: :directory, format: format,
|
|
60
|
+
formats: formats)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def save_zip(path, format: nil, formats: {})
|
|
64
|
+
@package.save(path, transport: :zip, format: format, formats: formats)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# ── Concepts ──
|
|
68
|
+
|
|
69
|
+
def concepts
|
|
70
|
+
@package.models_for(@concept_document_class).map(&:to_managed_concept)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def concept(uuid)
|
|
74
|
+
doc = @package.fetch_model(@concept_document_class, uuid)
|
|
75
|
+
doc&.to_managed_concept
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def add_concept(managed_concept)
|
|
79
|
+
ensure_package
|
|
80
|
+
doc = @concept_document_class.from_managed_concept(managed_concept)
|
|
81
|
+
doc.id = managed_concept.uuid
|
|
82
|
+
@package.add_model(doc)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def remove_concept(uuid)
|
|
86
|
+
@package.remove_model(@concept_document_class, uuid)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def concept_count
|
|
90
|
+
@package.model_count(@concept_document_class)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def concept_exists?(uuid)
|
|
94
|
+
@package.model_exists?(@concept_document_class, uuid)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# ── Metadata ──
|
|
98
|
+
|
|
99
|
+
def metadata
|
|
100
|
+
@package&.metadata
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def metadata=(value)
|
|
104
|
+
ensure_package
|
|
105
|
+
@package.metadata = value
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# ── Register Data ──
|
|
109
|
+
|
|
110
|
+
def register_data
|
|
111
|
+
@package.models_for(RegisterData).first
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def register_data=(value)
|
|
115
|
+
ensure_package
|
|
116
|
+
existing = register_data
|
|
117
|
+
@package.remove_model(RegisterData, existing.key) if existing
|
|
118
|
+
@package.add_model(value)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# ── Bibliography ──
|
|
122
|
+
|
|
123
|
+
def bibliography
|
|
124
|
+
@package.models_for(BibliographyData).first
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def bibliography=(value)
|
|
128
|
+
ensure_package
|
|
129
|
+
existing = bibliography
|
|
130
|
+
@package.remove_model(BibliographyData, existing.shortname) if existing
|
|
131
|
+
@package.add_model(value)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# ── Images ──
|
|
135
|
+
|
|
136
|
+
def image(path)
|
|
137
|
+
@package.asset(path)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def add_image(path, content)
|
|
141
|
+
ensure_package
|
|
142
|
+
@package.add_asset(path, content)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def image_paths
|
|
146
|
+
@package.asset_paths.select { |p| p.start_with?("images/") }
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# ── Stats ──
|
|
150
|
+
|
|
151
|
+
def stats
|
|
152
|
+
@package&.stats
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# ── Convenience ──
|
|
156
|
+
|
|
157
|
+
def build_metadata(shortname:, version:, **opts)
|
|
158
|
+
GcrMetadata.from_concepts(concepts, register_data: register_data, options: {
|
|
159
|
+
shortname: shortname,
|
|
160
|
+
version: version,
|
|
161
|
+
**opts,
|
|
162
|
+
})
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
def ensure_package
|
|
168
|
+
return if @package
|
|
169
|
+
|
|
170
|
+
definition = GcrPackageDefinition.definition(
|
|
171
|
+
concept_document_class: @concept_document_class,
|
|
172
|
+
)
|
|
173
|
+
@package = Lutaml::Store::PackageStore.new(definition)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def resolve_concept_document_class(metadata)
|
|
177
|
+
version = metadata&.schema_version.to_s
|
|
178
|
+
ConceptDocument.for_version(version)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def load_metadata_from_directory(path)
|
|
182
|
+
file_path = File.join(path, "metadata.yaml")
|
|
183
|
+
return nil unless File.exist?(file_path)
|
|
184
|
+
|
|
185
|
+
GcrMetadata.from_yaml(File.read(file_path, encoding: "utf-8"))
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def load_metadata_from_zip(path)
|
|
189
|
+
Zip::File.open(path) do |zf|
|
|
190
|
+
entry = zf.find_entry("metadata.yaml")
|
|
191
|
+
return nil unless entry
|
|
192
|
+
|
|
193
|
+
GcrMetadata.from_yaml(entry.get_input_stream.read)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def apply_metadata(metadata)
|
|
198
|
+
@package.metadata = metadata if metadata && @package
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|