glossarist 2.6.5 → 2.6.7
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/release.yml +1 -4
- data/.rubocop_todo.yml +53 -2
- data/CLAUDE.md +27 -2
- data/README.adoc +532 -56
- data/config.yml +68 -1
- data/glossarist.gemspec +2 -0
- data/lib/glossarist/citation.rb +26 -123
- data/lib/glossarist/cli/compare_command.rb +106 -0
- data/lib/glossarist/cli/export_command.rb +11 -14
- data/lib/glossarist/cli/validate_command.rb +111 -20
- data/lib/glossarist/cli.rb +18 -0
- data/lib/glossarist/collections/bibliography_collection.rb +4 -2
- data/lib/glossarist/collections/localization_collection.rb +2 -0
- data/lib/glossarist/comparison_result.rb +35 -0
- data/lib/glossarist/concept.rb +1 -1
- data/lib/glossarist/concept_collector.rb +44 -0
- data/lib/glossarist/concept_comparator.rb +72 -0
- data/lib/glossarist/concept_data.rb +20 -0
- data/lib/glossarist/concept_diff.rb +15 -0
- data/lib/glossarist/concept_document.rb +11 -0
- data/lib/glossarist/concept_manager.rb +19 -5
- data/lib/glossarist/concept_ref.rb +13 -0
- data/lib/glossarist/concept_reference.rb +12 -19
- data/lib/glossarist/concept_validator.rb +6 -1
- data/lib/glossarist/context_configuration.rb +90 -0
- data/lib/glossarist/dataset_validator.rb +8 -4
- data/lib/glossarist/designation/abbreviation.rb +0 -2
- data/lib/glossarist/designation/base.rb +21 -1
- data/lib/glossarist/designation/expression.rb +3 -0
- data/lib/glossarist/designation/letter_symbol.rb +0 -4
- data/lib/glossarist/designation/prefix.rb +17 -0
- data/lib/glossarist/designation/suffix.rb +17 -0
- data/lib/glossarist/designation/symbol.rb +0 -2
- data/lib/glossarist/gcr_metadata.rb +7 -14
- data/lib/glossarist/gcr_package.rb +35 -23
- data/lib/glossarist/gcr_validator.rb +38 -17
- data/lib/glossarist/glossary_definition.rb +5 -0
- data/lib/glossarist/localized_concept.rb +8 -0
- data/lib/glossarist/managed_concept.rb +39 -6
- data/lib/glossarist/managed_concept_data.rb +22 -2
- data/lib/glossarist/non_verb_rep.rb +21 -6
- data/lib/glossarist/pronunciation.rb +32 -0
- data/lib/glossarist/rdf/ext/jsonld_transform_ext.rb +208 -0
- data/lib/glossarist/rdf/ext/mapping_ext.rb +37 -0
- data/lib/glossarist/rdf/ext/mapping_rule_ext.rb +27 -0
- data/lib/glossarist/rdf/ext/member_rule_ext.rb +34 -0
- data/lib/glossarist/rdf/ext/turtle_transform_ext.rb +222 -0
- data/lib/glossarist/rdf/ext.rb +39 -0
- data/lib/glossarist/rdf/gloss_citation.rb +36 -0
- data/lib/glossarist/rdf/gloss_concept.rb +58 -0
- data/lib/glossarist/rdf/gloss_concept_date.rb +24 -0
- data/lib/glossarist/rdf/gloss_concept_reference.rb +29 -0
- data/lib/glossarist/rdf/gloss_concept_source.rb +37 -0
- data/lib/glossarist/rdf/gloss_designation.rb +146 -0
- data/lib/glossarist/rdf/gloss_detailed_definition.rb +24 -0
- data/lib/glossarist/rdf/gloss_grammar_info.rb +57 -0
- data/lib/glossarist/rdf/gloss_locality.rb +25 -0
- data/lib/glossarist/rdf/gloss_localized_concept.rb +67 -0
- data/lib/glossarist/rdf/gloss_non_verbal_rep.rb +31 -0
- data/lib/glossarist/rdf/gloss_pronunciation.rb +32 -0
- data/lib/glossarist/rdf/gloss_reference.rb +55 -0
- data/lib/glossarist/rdf/namespaces/glossarist_namespace.rb +12 -0
- data/lib/glossarist/rdf/namespaces/iso_thes_namespace.rb +12 -0
- data/lib/glossarist/rdf/namespaces/owl_namespace.rb +12 -0
- data/lib/glossarist/rdf/namespaces/prov_namespace.rb +12 -0
- data/lib/glossarist/rdf/namespaces/rdf_namespace.rb +12 -0
- data/lib/glossarist/rdf/namespaces/skosxl_namespace.rb +12 -0
- data/lib/glossarist/rdf/namespaces.rb +8 -2
- data/lib/glossarist/rdf/relationships.rb +19 -0
- data/lib/glossarist/rdf/v3/configuration.rb +15 -0
- data/lib/glossarist/rdf/v3.rb +79 -0
- data/lib/glossarist/rdf.rb +22 -2
- data/lib/glossarist/reference_extractor.rb +15 -24
- data/lib/glossarist/reference_resolver.rb +3 -3
- data/lib/glossarist/related_concept.rb +2 -10
- data/lib/glossarist/schema_migration.rb +39 -0
- data/lib/glossarist/sts/term_mapper.rb +2 -2
- data/lib/glossarist/transforms/concept_to_gloss_transform.rb +355 -0
- data/lib/glossarist/transforms.rb +2 -2
- data/lib/glossarist/urn_resolver.rb +13 -1
- data/lib/glossarist/v1/concept.rb +18 -11
- data/lib/glossarist/v2/citation.rb +36 -0
- data/lib/glossarist/v2/concept_data.rb +46 -0
- data/lib/glossarist/v2/concept_document.rb +18 -0
- data/lib/glossarist/v2/concept_ref.rb +8 -0
- data/lib/glossarist/v2/concept_source.rb +16 -0
- data/lib/glossarist/v2/configuration.rb +13 -0
- data/lib/glossarist/v2/detailed_definition.rb +14 -0
- data/lib/glossarist/v2/localized_concept.rb +9 -0
- data/lib/glossarist/v2/managed_concept.rb +25 -0
- data/lib/glossarist/v2/managed_concept_data.rb +49 -0
- data/lib/glossarist/v2/related_concept.rb +15 -0
- data/lib/glossarist/v2.rb +28 -0
- data/lib/glossarist/v3/bibliography_entry.rb +19 -0
- data/lib/glossarist/v3/bibliography_file.rb +27 -0
- data/lib/glossarist/v3/citation.rb +30 -0
- data/lib/glossarist/v3/concept_data.rb +46 -0
- data/lib/glossarist/v3/concept_document.rb +18 -0
- data/lib/glossarist/v3/concept_ref.rb +8 -0
- data/lib/glossarist/v3/concept_source.rb +16 -0
- data/lib/glossarist/v3/configuration.rb +13 -0
- data/lib/glossarist/v3/detailed_definition.rb +14 -0
- data/lib/glossarist/v3/image_entry.rb +21 -0
- data/lib/glossarist/v3/image_file.rb +31 -0
- data/lib/glossarist/v3/localized_concept.rb +9 -0
- data/lib/glossarist/v3/managed_concept.rb +26 -0
- data/lib/glossarist/v3/managed_concept_data.rb +34 -0
- data/lib/glossarist/v3/related_concept.rb +15 -0
- data/lib/glossarist/v3.rb +36 -0
- data/lib/glossarist/validation/asset_index.rb +4 -3
- data/lib/glossarist/validation/bibliography_index.rb +61 -30
- data/lib/glossarist/validation/rules/asciidoc_xref_rule.rb +2 -15
- data/lib/glossarist/validation/rules/authoritative_source_rule.rb +2 -15
- data/lib/glossarist/validation/rules/base.rb +5 -0
- data/lib/glossarist/validation/rules/bibliography_yaml_rule.rb +2 -3
- data/lib/glossarist/validation/rules/citation_completeness_rule.rb +5 -27
- data/lib/glossarist/validation/rules/dataset_context.rb +8 -3
- data/lib/glossarist/validation/rules/date_validity_rule.rb +1 -1
- data/lib/glossarist/validation/rules/designation_status_rule.rb +0 -1
- data/lib/glossarist/validation/rules/designation_type_rule.rb +1 -5
- data/lib/glossarist/validation/rules/domain_ref_rule.rb +37 -0
- data/lib/glossarist/validation/rules/domain_target_rule.rb +56 -0
- data/lib/glossarist/validation/rules/gcr_context.rb +12 -13
- data/lib/glossarist/validation/rules/image_reference_rule.rb +2 -17
- data/lib/glossarist/validation/rules/locality_completeness_rule.rb +58 -0
- data/lib/glossarist/validation/rules/localization_consistency_rule.rb +72 -0
- data/lib/glossarist/validation/rules/localization_presence_rule.rb +1 -1
- data/lib/glossarist/validation/rules/model_validity_rule.rb +71 -0
- data/lib/glossarist/validation/rules/orphaned_bibliography_rule.rb +1 -13
- data/lib/glossarist/validation/rules/orphaned_images_rule.rb +16 -11
- data/lib/glossarist/validation/rules/ref_shape_rule.rb +68 -0
- data/lib/glossarist/validation/rules/related_concept_cycle_rule.rb +1 -3
- data/lib/glossarist/validation/rules/related_concept_symmetry_rule.rb +1 -3
- data/lib/glossarist/validation/rules/related_concept_target_rule.rb +64 -0
- data/lib/glossarist/validation/rules/schema_version_rule.rb +41 -0
- data/lib/glossarist/validation/rules/source_type_rule.rb +1 -15
- data/lib/glossarist/validation/rules/source_urn_format_rule.rb +65 -0
- data/lib/glossarist/validation/rules/uuid_format_rule.rb +33 -0
- data/lib/glossarist/validation/rules.rb +10 -43
- data/lib/glossarist/validation/validation_issue.rb +14 -11
- data/lib/glossarist/validation_result.rb +12 -22
- data/lib/glossarist/version.rb +1 -1
- data/lib/glossarist.rb +10 -0
- data/memory/project-status.md +43 -0
- data/scripts/migrate_dataset.rb +180 -0
- data/scripts/migrate_isotc204_to_v3.rb +134 -0
- data/scripts/migrate_isotc211_to_v3.rb +153 -0
- data/scripts/migrate_osgeo_to_v3.rb +155 -0
- data/scripts/upgrade_dataset_to_v3.rb +47 -0
- metadata +112 -6
- data/TODO.integration/01-gcr-package-cli.md +0 -180
- data/lib/glossarist/rdf/skos_concept.rb +0 -43
- data/lib/glossarist/rdf/skos_vocabulary.rb +0 -25
- data/lib/glossarist/transforms/concept_to_skos_transform.rb +0 -131
data/config.yml
CHANGED
|
@@ -26,23 +26,90 @@ designation:
|
|
|
26
26
|
base:
|
|
27
27
|
normative_status:
|
|
28
28
|
- preferred
|
|
29
|
-
- deprecated
|
|
30
29
|
- admitted
|
|
30
|
+
- deprecated
|
|
31
|
+
- superseded
|
|
31
32
|
- <símbolo> # in iev-data => '<symbol>'
|
|
32
33
|
- 티에스 # in iev-data => translates to 'TS' I think it is a synonym and not status.
|
|
33
34
|
- prąd startowy # in iev-data => 'starting current' I think it is a synonym and not status.
|
|
35
|
+
relationship_type:
|
|
36
|
+
# Term-level (designation-to-designation within the same concept entry)
|
|
37
|
+
- abbreviated_form_for
|
|
38
|
+
- short_form_for
|
|
34
39
|
|
|
35
40
|
related_concept:
|
|
36
41
|
type:
|
|
42
|
+
# Lifecycle (ISO 10241-1)
|
|
37
43
|
- deprecates
|
|
38
44
|
- supersedes
|
|
39
45
|
- superseded_by
|
|
46
|
+
# Hierarchical (ISO 10241-1 / ISO 25964)
|
|
40
47
|
- narrower
|
|
41
48
|
- broader
|
|
49
|
+
# Hierarchical sub-types — ISO 25964 generic (BTG/NTG)
|
|
50
|
+
- broader_generic
|
|
51
|
+
- narrower_generic
|
|
52
|
+
# Hierarchical sub-types — ISO 25964 partitive (BTP/NTP)
|
|
53
|
+
- broader_partitive
|
|
54
|
+
- narrower_partitive
|
|
55
|
+
# Hierarchical sub-types — ISO 25964 instantial (BTI/NTI)
|
|
56
|
+
- broader_instantial
|
|
57
|
+
- narrower_instantial
|
|
58
|
+
# Equivalence (ISO 10241-1 / ISO 25964 exactMatch / SKOS)
|
|
42
59
|
- equivalent
|
|
60
|
+
# Approximate equivalence (ISO 25964 closeMatch / SKOS)
|
|
61
|
+
- close_match
|
|
62
|
+
# Cross-vocabulary mapping (SKOS)
|
|
63
|
+
- broad_match
|
|
64
|
+
- narrow_match
|
|
65
|
+
- related_match
|
|
66
|
+
# Comparative (ISO 10241-1)
|
|
43
67
|
- compare
|
|
44
68
|
- contrast
|
|
69
|
+
# Associative (ISO 10241-1 / ISO 25964 RT / TBX crossReference)
|
|
45
70
|
- see
|
|
71
|
+
# Associative sub-types (ISO 25964 / TBX)
|
|
72
|
+
- related_concept
|
|
73
|
+
- related_concept_broader
|
|
74
|
+
- related_concept_narrower
|
|
75
|
+
# Associative — spatial/temporal (ISO 25964 / TBX)
|
|
76
|
+
- sequentially_related_concept
|
|
77
|
+
- spatially_related_concept
|
|
78
|
+
- temporally_related_concept
|
|
79
|
+
# Lexical (ISO 12620 / TBX)
|
|
80
|
+
- homograph
|
|
81
|
+
- false_friend
|
|
82
|
+
|
|
83
|
+
iso12620:
|
|
84
|
+
term_type:
|
|
85
|
+
# ISO 12620 term type classification for designations.
|
|
86
|
+
# Orthographic / structural:
|
|
87
|
+
- abbreviation
|
|
88
|
+
- acronym
|
|
89
|
+
- clipped_term
|
|
90
|
+
- full_form
|
|
91
|
+
- initialism
|
|
92
|
+
- short_form
|
|
93
|
+
- transliterated_form
|
|
94
|
+
- transcribed_form
|
|
95
|
+
- variant
|
|
96
|
+
# Symbolic / formulaic:
|
|
97
|
+
- equation
|
|
98
|
+
- formula
|
|
99
|
+
- logical_expression
|
|
100
|
+
- symbol
|
|
101
|
+
# Usage / provenance:
|
|
102
|
+
- common_name
|
|
103
|
+
- entry_term
|
|
104
|
+
- internationalism
|
|
105
|
+
- international_scientific_term
|
|
106
|
+
- part_number
|
|
107
|
+
- phraseological_unit
|
|
108
|
+
- shortcut
|
|
109
|
+
- sku
|
|
110
|
+
- standard_text
|
|
111
|
+
- synonym
|
|
112
|
+
- synonymous_phrase
|
|
46
113
|
|
|
47
114
|
abbreviation:
|
|
48
115
|
type:
|
data/glossarist.gemspec
CHANGED
|
@@ -32,9 +32,11 @@ Gem::Specification.new do |spec|
|
|
|
32
32
|
spec.require_paths = ["lib"]
|
|
33
33
|
|
|
34
34
|
spec.add_dependency "lutaml-model", "~> 0.8.5"
|
|
35
|
+
spec.add_dependency "paint", "~> 2.3"
|
|
35
36
|
spec.add_dependency "relaton", ">= 2.0.0", "< 3"
|
|
36
37
|
spec.add_dependency "rubyzip", ">= 2.3", "< 3"
|
|
37
38
|
spec.add_dependency "sts", "~> 0.5.6"
|
|
39
|
+
spec.add_dependency "table_tennis", "~> 0.0"
|
|
38
40
|
spec.add_dependency "tbx", "~> 0.1"
|
|
39
41
|
spec.add_dependency "thor"
|
|
40
42
|
end
|
data/lib/glossarist/citation.rb
CHANGED
|
@@ -1,122 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Glossarist
|
|
2
4
|
class Citation < Lutaml::Model::Serializable
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# Document version in structured reference.
|
|
16
|
-
# @return [String]
|
|
17
|
-
attribute :version, :string
|
|
5
|
+
class Ref < Lutaml::Model::Serializable
|
|
6
|
+
attribute :source, :string
|
|
7
|
+
attribute :id, :string
|
|
8
|
+
attribute :version, :string
|
|
9
|
+
|
|
10
|
+
key_value do
|
|
11
|
+
map :source, to: :source
|
|
12
|
+
map :id, to: :id
|
|
13
|
+
map :version, to: :version
|
|
14
|
+
end
|
|
15
|
+
end
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
# Referred locality of the document.
|
|
17
|
+
attribute :ref, Ref
|
|
21
18
|
attribute :locality, Locality
|
|
22
|
-
|
|
23
|
-
# Link to document.
|
|
24
|
-
# @return [String]
|
|
25
19
|
attribute :link, :string
|
|
26
|
-
|
|
27
|
-
# Original ref text before parsing.
|
|
28
|
-
# @return [String]
|
|
29
|
-
# @note This attribute is likely to be removed or reworked in future.
|
|
30
|
-
# It is arguably not relevant to Glossarist itself.
|
|
31
20
|
attribute :original, :string
|
|
32
|
-
|
|
33
|
-
attribute :ref, :string
|
|
34
|
-
|
|
35
21
|
attribute :custom_locality, CustomLocality, collection: true
|
|
36
22
|
|
|
37
23
|
key_value do
|
|
38
|
-
map :
|
|
39
|
-
map :text, to: :text, with: { from: :text_from_yaml, to: :text_to_yaml }
|
|
40
|
-
map :source, to: :source,
|
|
41
|
-
with: { from: :source_from_yaml, to: :source_to_yaml }
|
|
42
|
-
map :version, to: :version,
|
|
43
|
-
with: { from: :version_from_yaml, to: :version_to_yaml }
|
|
44
|
-
map :ref, to: :ref, with: { from: :ref_from_yaml, to: :ref_to_yaml }
|
|
24
|
+
map :ref, to: :ref
|
|
45
25
|
map %i[clause locality],
|
|
46
26
|
to: :locality,
|
|
47
|
-
with: { from: :
|
|
27
|
+
with: { from: :locality_from_yaml, to: :locality_to_yaml }
|
|
48
28
|
map :link, to: :link
|
|
49
29
|
map :original, to: :original
|
|
50
30
|
map %i[custom_locality customLocality], to: :custom_locality
|
|
51
31
|
end
|
|
52
32
|
|
|
53
|
-
def
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def ref_to_yaml(model, doc)
|
|
58
|
-
doc["ref"] = if model.structured?
|
|
59
|
-
ref_hash(model)
|
|
60
|
-
else
|
|
61
|
-
model.text
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def id_from_yaml(model, value)
|
|
66
|
-
model.id = value
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def id_to_yaml(_model, _doc)
|
|
70
|
-
# skip, will be handled in ref
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def text_from_yaml(model, value)
|
|
74
|
-
model.text = value
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def text_to_yaml(_model, _doc)
|
|
78
|
-
# skip, will be handled in ref
|
|
33
|
+
def label
|
|
34
|
+
parts = [ref&.source, ref&.id].compact
|
|
35
|
+
parts.empty? ? nil : parts.join(" ")
|
|
79
36
|
end
|
|
80
37
|
|
|
81
|
-
def
|
|
82
|
-
model.source = value
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def source_to_yaml(_model, _doc)
|
|
86
|
-
# skip, will be handled in ref
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def version_from_yaml(model, value)
|
|
90
|
-
model.version = value
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def version_to_yaml(_model, _doc)
|
|
94
|
-
# skip, will be handled in ref
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def ref_hash(model = self)
|
|
98
|
-
{
|
|
99
|
-
"source" => model.source,
|
|
100
|
-
"id" => model.id,
|
|
101
|
-
"version" => model.version,
|
|
102
|
-
}.compact
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def ref=(ref)
|
|
106
|
-
if ref.is_a?(Hash)
|
|
107
|
-
@source = ref["source"]
|
|
108
|
-
@id = ref["id"]
|
|
109
|
-
@version = ref["version"]
|
|
110
|
-
else
|
|
111
|
-
@text = ref
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def clause_from_yaml(model, value) # rubocop:disable Metrics/AbcSize
|
|
116
|
-
# accepts old format like
|
|
117
|
-
# clause: "11"
|
|
118
|
-
# or new format like
|
|
119
|
-
# locality: { type: "clause", reference_from: "11", reference_to: "12" }
|
|
38
|
+
def locality_from_yaml(model, value)
|
|
120
39
|
locality = Locality.new
|
|
121
40
|
|
|
122
41
|
if value.is_a?(Hash)
|
|
@@ -132,29 +51,13 @@ module Glossarist
|
|
|
132
51
|
model.locality = locality
|
|
133
52
|
end
|
|
134
53
|
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
doc["locality"] = {}
|
|
138
|
-
doc["locality"]["type"] = model.locality.type
|
|
139
|
-
|
|
140
|
-
if model.locality.reference_from
|
|
141
|
-
doc["locality"]["reference_from"] = model.locality.reference_from
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
if model.locality.reference_to
|
|
145
|
-
doc["locality"]["reference_to"] = model.locality.reference_to
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def plain?
|
|
151
|
-
(source && id && version).nil?
|
|
152
|
-
end
|
|
54
|
+
def locality_to_yaml(model, doc)
|
|
55
|
+
return unless model.locality
|
|
153
56
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
57
|
+
doc["locality"] = {}
|
|
58
|
+
doc["locality"]["type"] = model.locality.type
|
|
59
|
+
doc["locality"]["reference_from"] = model.locality.reference_from if model.locality.reference_from
|
|
60
|
+
doc["locality"]["reference_to"] = model.locality.reference_to if model.locality.reference_to
|
|
158
61
|
end
|
|
159
62
|
end
|
|
160
63
|
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "paint"
|
|
4
|
+
|
|
5
|
+
module Glossarist
|
|
6
|
+
class CLI
|
|
7
|
+
class CompareCommand
|
|
8
|
+
def initialize(new_path, old_path, options)
|
|
9
|
+
@new_path = new_path
|
|
10
|
+
@old_path = old_path
|
|
11
|
+
@options = options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run
|
|
15
|
+
new_concepts = ConceptCollector.collect(@new_path)
|
|
16
|
+
old_concepts = ConceptCollector.collect(@old_path)
|
|
17
|
+
|
|
18
|
+
result = ConceptComparator.new(
|
|
19
|
+
new_concepts: new_concepts,
|
|
20
|
+
old_concepts: old_concepts,
|
|
21
|
+
).compare(show_diffs: !@options[:no_diffs])
|
|
22
|
+
|
|
23
|
+
report(result)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def report(result)
|
|
29
|
+
case @options[:format]
|
|
30
|
+
when "json"
|
|
31
|
+
output result.to_json
|
|
32
|
+
when "yaml"
|
|
33
|
+
output result.to_yaml
|
|
34
|
+
else
|
|
35
|
+
print_text_report(result)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def print_text_report(result)
|
|
40
|
+
puts
|
|
41
|
+
puts Paint["Concept Comparison", :bold]
|
|
42
|
+
puts
|
|
43
|
+
|
|
44
|
+
print_counts(result)
|
|
45
|
+
print_new_only(result)
|
|
46
|
+
print_old_only(result)
|
|
47
|
+
print_similarity(result)
|
|
48
|
+
|
|
49
|
+
puts
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def print_counts(result)
|
|
53
|
+
puts " #{Paint['New:', :bold]} #{result.new_count} concepts"
|
|
54
|
+
puts " #{Paint['Old:', :bold]} #{result.old_count} concepts"
|
|
55
|
+
puts " #{Paint['Matched:', :bold]} #{result.matched.length}"
|
|
56
|
+
puts " #{Paint['New only:', :bold]} #{result.new_only.length}"
|
|
57
|
+
puts " #{Paint['Old only:', :bold]} #{result.old_only.length}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def print_new_only(result)
|
|
61
|
+
return unless result.new_only.any?
|
|
62
|
+
|
|
63
|
+
puts
|
|
64
|
+
puts " #{Paint['New concepts (not in old):', :green, :bold]}"
|
|
65
|
+
result.new_only.each { |id| puts " + #{id}" }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def print_old_only(result)
|
|
69
|
+
return unless result.old_only.any?
|
|
70
|
+
|
|
71
|
+
puts
|
|
72
|
+
puts " #{Paint['Removed concepts (not in new):', :red, :bold]}"
|
|
73
|
+
result.old_only.each { |id| puts " - #{id}" }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def print_similarity(result)
|
|
77
|
+
return unless result.diffs.any?
|
|
78
|
+
|
|
79
|
+
puts
|
|
80
|
+
puts " #{Paint['Per-concept similarity:', :bold]}"
|
|
81
|
+
result.diffs.each do |diff|
|
|
82
|
+
color = similarity_color(diff.similarity)
|
|
83
|
+
puts " #{diff.concept_id}: #{Paint["#{diff.similarity}%", color]}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def similarity_color(value)
|
|
88
|
+
if value >= 100
|
|
89
|
+
:green
|
|
90
|
+
elsif value >= 90
|
|
91
|
+
:yellow
|
|
92
|
+
else
|
|
93
|
+
:red
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def output(content)
|
|
98
|
+
if @options[:report]
|
|
99
|
+
File.write(@options[:report], content)
|
|
100
|
+
else
|
|
101
|
+
puts content
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -48,8 +48,8 @@ module Glossarist
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def resolve_metadata_from_package(package)
|
|
51
|
-
@options[:shortname] ||= package.metadata
|
|
52
|
-
@options[:uri_prefix] ||= package.metadata
|
|
51
|
+
@options[:shortname] ||= package.metadata.shortname
|
|
52
|
+
@options[:uri_prefix] ||= package.metadata.uri_prefix
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def resolve_shortname
|
|
@@ -73,17 +73,15 @@ module Glossarist
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def export_jsonld(concepts, name, output_dir)
|
|
76
|
-
require "glossarist/transforms/
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
File.write(File.join(output_dir, "#{name}.jsonld"), vocab.to_jsonld)
|
|
76
|
+
require "glossarist/transforms/concept_to_gloss_transform"
|
|
77
|
+
transform = Transforms::ConceptToGlossTransform.new(nil, transform_options)
|
|
78
|
+
File.write(File.join(output_dir, "#{name}.jsonld"), transform.to_jsonld(concepts))
|
|
80
79
|
end
|
|
81
80
|
|
|
82
81
|
def export_turtle(concepts, name, output_dir)
|
|
83
|
-
require "glossarist/transforms/
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
File.write(File.join(output_dir, "#{name}.ttl"), vocab.to_turtle)
|
|
82
|
+
require "glossarist/transforms/concept_to_gloss_transform"
|
|
83
|
+
transform = Transforms::ConceptToGlossTransform.new(nil, transform_options)
|
|
84
|
+
File.write(File.join(output_dir, "#{name}.ttl"), transform.to_turtle(concepts))
|
|
87
85
|
end
|
|
88
86
|
|
|
89
87
|
def export_tbx(concepts, name, output_dir)
|
|
@@ -94,12 +92,11 @@ module Glossarist
|
|
|
94
92
|
end
|
|
95
93
|
|
|
96
94
|
def export_jsonl(concepts, name, output_dir)
|
|
97
|
-
require "glossarist/transforms/
|
|
95
|
+
require "glossarist/transforms/concept_to_gloss_transform"
|
|
98
96
|
File.open(File.join(output_dir, "#{name}.jsonl"), "w") do |f|
|
|
99
97
|
concepts.each do |concept|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
f.write(skos.to_jsonld)
|
|
98
|
+
transform = Transforms::ConceptToGlossTransform.new(concept, transform_options)
|
|
99
|
+
f.write(transform.to_jsonl_line)
|
|
103
100
|
f.write("\n")
|
|
104
101
|
end
|
|
105
102
|
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "paint"
|
|
4
|
+
require "table_tennis"
|
|
5
|
+
|
|
3
6
|
module Glossarist
|
|
4
7
|
class CLI
|
|
5
8
|
class ValidateCommand
|
|
@@ -9,47 +12,135 @@ module Glossarist
|
|
|
9
12
|
end
|
|
10
13
|
|
|
11
14
|
def run
|
|
12
|
-
|
|
15
|
+
text_output = @options[:format] == "text"
|
|
16
|
+
validator = DatasetValidator.new(on_progress: text_output ? method(:print_progress) : nil)
|
|
17
|
+
result = validator.validate(
|
|
13
18
|
@path,
|
|
14
19
|
strict: @options[:strict],
|
|
15
20
|
reference_path: @options[:reference_path],
|
|
16
21
|
)
|
|
22
|
+
|
|
23
|
+
$stderr.print "\r#{' ' * 60}\r" if text_output
|
|
17
24
|
report(result)
|
|
18
|
-
|
|
19
|
-
exit(exit_code) unless exit_code.zero?
|
|
25
|
+
exit(1) unless result.errors.empty? && !strict_failure?(result)
|
|
20
26
|
end
|
|
21
27
|
|
|
22
28
|
private
|
|
23
29
|
|
|
30
|
+
def strict_failure?(result)
|
|
31
|
+
@options[:strict] && result.warnings.any?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def print_progress(current, total)
|
|
35
|
+
pct = (current.to_f / total * 100).round
|
|
36
|
+
bar_width = 30
|
|
37
|
+
filled = (current.to_f / total * bar_width).round
|
|
38
|
+
bar = "#{'█' * filled}#{'░' * (bar_width - filled)}"
|
|
39
|
+
|
|
40
|
+
$stderr.print "\r #{Paint['Validating', :bold]} #{bar} #{current}/#{total} (#{pct}%)"
|
|
41
|
+
$stderr.flush
|
|
42
|
+
end
|
|
43
|
+
|
|
24
44
|
def report(result)
|
|
25
45
|
case @options[:format]
|
|
26
46
|
when "json"
|
|
27
|
-
|
|
28
|
-
puts JSON.pretty_generate(result.to_h)
|
|
47
|
+
puts result.to_json
|
|
29
48
|
when "yaml"
|
|
30
|
-
|
|
31
|
-
puts YAML.dump(result.to_h)
|
|
49
|
+
puts result.to_yaml
|
|
32
50
|
else
|
|
33
|
-
|
|
51
|
+
print_text_output(result)
|
|
52
|
+
print_table_output(result) if result.issues.any?
|
|
34
53
|
end
|
|
35
54
|
end
|
|
36
55
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
56
|
+
def print_text_output(result)
|
|
57
|
+
puts
|
|
58
|
+
puts Paint["Validating #{@path}", :bold]
|
|
59
|
+
puts
|
|
60
|
+
|
|
61
|
+
if result.issues.empty?
|
|
62
|
+
puts " #{Paint['No issues found.', :green, :bold]}"
|
|
63
|
+
return
|
|
43
64
|
end
|
|
44
65
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
66
|
+
print_grouped_issues(result)
|
|
67
|
+
print_summary_line(result)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def print_grouped_issues(result)
|
|
71
|
+
result.issues
|
|
72
|
+
.group_by { |i| i.location || "(dataset)" }
|
|
73
|
+
.sort_by { |loc, issues| [has_errors?(issues) ? 0 : 1, loc] }
|
|
74
|
+
.each { |location, issues| print_location_group(location, issues) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def has_errors?(issues)
|
|
78
|
+
issues.any?(&:error?)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def print_location_group(location, issues)
|
|
82
|
+
puts " #{Paint[location, :cyan, :bold]}"
|
|
83
|
+
issues.sort_by { |i| issue_sort_key(i) }
|
|
84
|
+
.each { |issue| print_issue(issue) }
|
|
85
|
+
puts
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def issue_sort_key(issue)
|
|
89
|
+
[issue.error? ? 0 : 1, issue.code || "", issue.message]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def print_issue(issue)
|
|
93
|
+
color = issue.error? ? :red : :yellow
|
|
94
|
+
label = Paint[issue.error? ? "ERROR" : " WARN", color, :bold]
|
|
95
|
+
code = Paint["%-8s" % (issue.code || ""), :magenta]
|
|
96
|
+
msg_col = 21
|
|
97
|
+
|
|
98
|
+
puts " #{label} #{code} #{issue.message}"
|
|
99
|
+
puts "#{' ' * msg_col}#{Paint[issue.suggestion, :green]}" if issue.suggestion
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def print_summary_line(result)
|
|
103
|
+
error_count = result.issues.count(&:error?)
|
|
104
|
+
warning_count = result.issues.count(&:warning?)
|
|
105
|
+
|
|
106
|
+
status = error_count.positive? ? Paint["INVALID", :red, :bold] : Paint["VALID", :green, :bold]
|
|
107
|
+
|
|
108
|
+
details = []
|
|
109
|
+
details << Paint["#{error_count} error(s)", :red] if error_count.positive?
|
|
110
|
+
details << Paint["#{warning_count} warning(s)", :yellow] if warning_count.positive?
|
|
111
|
+
|
|
112
|
+
puts " #{status} #{details.join(', ')}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def print_table_output(result)
|
|
116
|
+
rows = build_summary_rows(result)
|
|
117
|
+
return if rows.empty?
|
|
118
|
+
|
|
119
|
+
options = {
|
|
120
|
+
title: "Issues by Rule",
|
|
121
|
+
columns: %i[code severity count],
|
|
122
|
+
headers: { code: "Rule", severity: "Level", count: "Count" },
|
|
123
|
+
color_scales: { count: :gw },
|
|
124
|
+
mark: ->(row) { row[:severity] == "error" },
|
|
125
|
+
zebra: true,
|
|
126
|
+
}
|
|
127
|
+
puts
|
|
128
|
+
puts TableTennis.new(rows, options)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def build_summary_rows(result)
|
|
132
|
+
counts = Hash.new(0)
|
|
133
|
+
severities = {}
|
|
134
|
+
|
|
135
|
+
result.issues.each do |issue|
|
|
136
|
+
key = issue.code || "unknown"
|
|
137
|
+
counts[key] += 1
|
|
138
|
+
severities[key] ||= issue.severity
|
|
49
139
|
end
|
|
50
140
|
|
|
51
|
-
|
|
52
|
-
|
|
141
|
+
counts.sort_by { |_, c| -c }.map do |code, count|
|
|
142
|
+
{ code: code, severity: severities[code], count: count }
|
|
143
|
+
end
|
|
53
144
|
end
|
|
54
145
|
end
|
|
55
146
|
end
|
data/lib/glossarist/cli.rb
CHANGED
|
@@ -9,6 +9,7 @@ module Glossarist
|
|
|
9
9
|
autoload :ValidateCommand, "#{__dir__}/cli/validate_command"
|
|
10
10
|
autoload :ImportCommand, "#{__dir__}/cli/import_command"
|
|
11
11
|
autoload :ExportCommand, "#{__dir__}/cli/export_command"
|
|
12
|
+
autoload :CompareCommand, "#{__dir__}/cli/compare_command"
|
|
12
13
|
desc "generate_latex", "Convert Concepts to Latex format"
|
|
13
14
|
|
|
14
15
|
option :concepts_path, aliases: :p, required: true,
|
|
@@ -120,6 +121,23 @@ module Glossarist
|
|
|
120
121
|
CLI::ExportCommand.new(path, options).run
|
|
121
122
|
end
|
|
122
123
|
|
|
124
|
+
desc "compare NEW_PATH OLD_PATH", "Compare two concept datasets"
|
|
125
|
+
option :format, type: :string, default: "text",
|
|
126
|
+
enum: %w[text json yaml],
|
|
127
|
+
desc: "Output format"
|
|
128
|
+
option :report, type: :string,
|
|
129
|
+
desc: "Write report to file"
|
|
130
|
+
option :no_diffs, type: :boolean, default: false,
|
|
131
|
+
desc: "Skip per-concept diff computation"
|
|
132
|
+
def compare(new_path, old_path)
|
|
133
|
+
CLI::CompareCommand.new(new_path, old_path, options).run
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
desc "version", "Show Glossarist version"
|
|
137
|
+
def version
|
|
138
|
+
puts Glossarist::VERSION
|
|
139
|
+
end
|
|
140
|
+
|
|
123
141
|
def method_missing(*args)
|
|
124
142
|
warn "No method found named: #{args[0]}"
|
|
125
143
|
warn "Run with `--help` or `-h` to see available options"
|
|
@@ -39,9 +39,11 @@ module Glossarist
|
|
|
39
39
|
def populate_bibliographies(concepts)
|
|
40
40
|
concepts.each do |concept|
|
|
41
41
|
concept.default_lang.sources.each do |source|
|
|
42
|
-
next if source.origin.
|
|
42
|
+
next if source.origin.ref.nil?
|
|
43
|
+
ref_text = source.origin.ref.source
|
|
44
|
+
next if ref_text.nil?
|
|
43
45
|
|
|
44
|
-
fetch(
|
|
46
|
+
fetch(ref_text)
|
|
45
47
|
end
|
|
46
48
|
end
|
|
47
49
|
end
|