isq 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 44823d1e9f21796b3c62de48a24105fa0b5c906e6990ddd75a1143052363f11b
4
+ data.tar.gz: 2671bb4938158df5c272b790000da4c53e134c9a54d4bd6b553aa57f3e4d6ba4
5
+ SHA512:
6
+ metadata.gz: 29c22ea5b84c9bce2c796164001e708a7fee5967acc0954c9042dbebda36e8f91d4a02d2badad8d6955bd5f66e9ace56600dec1b355d294ed17e34623a529e1c
7
+ data.tar.gz: 5812745292b217fc5ea1acb9b8c802bddfc1f315ea2a748714d5064cbc3698df0267a7a1f18bad52e8999cc11fee38233778f7741f5e3fdc8c9d8b5ad138b81e
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Isq
4
+ class MathConcept < SduSmart::TermEntry
5
+ attribute :identifier, :string
6
+ attribute :pref_label, :string
7
+ attribute :notation, :string, collection: true
8
+ attribute :definition, :string
9
+ attribute :note, :string
10
+
11
+ rdf do
12
+ namespace SduSmart::Rdf::Namespaces::IsoIec80000Namespace,
13
+ SduSmart::Rdf::Namespaces::SmartNamespace,
14
+ Lutaml::Rdf::Namespaces::DctermsNamespace,
15
+ Lutaml::Rdf::Namespaces::SkosNamespace
16
+
17
+ subject { |m| "https://w3id.org/standards/isoiec80000/ontologies/core/#{m.id}" }
18
+
19
+ type "isoiec80000:MathConcept"
20
+
21
+ predicate :identifier,
22
+ namespace: Lutaml::Rdf::Namespaces::DctermsNamespace,
23
+ to: :identifier
24
+
25
+ predicate :prefLabel,
26
+ namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
27
+ to: :pref_label
28
+
29
+ predicate :notation,
30
+ namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
31
+ to: :notation
32
+
33
+ predicate :definition,
34
+ namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
35
+ to: :definition
36
+
37
+ predicate :note,
38
+ namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
39
+ to: :note
40
+
41
+ predicate :hasBindingnessType,
42
+ namespace: SduSmart::Rdf::Namespaces::SmartNamespace,
43
+ to: :bindingness_type
44
+
45
+ predicate :isPartOf,
46
+ namespace: Lutaml::Rdf::Namespaces::DctermsNamespace,
47
+ to: :is_part_of
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Isq
4
+ class Quantity < SduSmart::TermEntry
5
+ attribute :identifier, :string
6
+ attribute :pref_label, :string
7
+ attribute :notation, :string, collection: true
8
+ attribute :definition, :string
9
+ attribute :note, :string
10
+ attribute :has_unit, :string, collection: true
11
+
12
+ rdf do
13
+ namespace SduSmart::Rdf::Namespaces::IsoIec80000Namespace,
14
+ SduSmart::Rdf::Namespaces::SmartNamespace,
15
+ Lutaml::Rdf::Namespaces::DctermsNamespace,
16
+ Lutaml::Rdf::Namespaces::SkosNamespace
17
+
18
+ subject { |m| "https://w3id.org/standards/isoiec80000/ontologies/core/#{m.id}" }
19
+
20
+ type "isoiec80000:Quantity"
21
+
22
+ predicate :identifier,
23
+ namespace: Lutaml::Rdf::Namespaces::DctermsNamespace,
24
+ to: :identifier
25
+
26
+ predicate :prefLabel,
27
+ namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
28
+ to: :pref_label
29
+
30
+ predicate :notation,
31
+ namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
32
+ to: :notation
33
+
34
+ predicate :definition,
35
+ namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
36
+ to: :definition
37
+
38
+ predicate :note,
39
+ namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
40
+ to: :note
41
+
42
+ predicate :hasUnit,
43
+ namespace: SduSmart::Rdf::Namespaces::IsoIec80000Namespace,
44
+ to: :has_unit
45
+
46
+ predicate :hasBindingnessType,
47
+ namespace: SduSmart::Rdf::Namespaces::SmartNamespace,
48
+ to: :bindingness_type
49
+
50
+ predicate :isPartOf,
51
+ namespace: Lutaml::Rdf::Namespaces::DctermsNamespace,
52
+ to: :is_part_of
53
+ end
54
+ end
55
+ end
data/lib/isq/unit.rb ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Isq
4
+ class Unit < SduSmart::TermEntry
5
+ attribute :pref_label, :string
6
+ attribute :notation, :string, collection: true
7
+
8
+ rdf do
9
+ namespace SduSmart::Rdf::Namespaces::IsoIec80000Namespace,
10
+ SduSmart::Rdf::Namespaces::SmartNamespace,
11
+ Lutaml::Rdf::Namespaces::DctermsNamespace,
12
+ Lutaml::Rdf::Namespaces::SkosNamespace
13
+
14
+ subject { |m| "https://w3id.org/standards/isoiec80000/ontologies/core/#{m.id}" }
15
+
16
+ type "isoiec80000:Unit"
17
+
18
+ predicate :prefLabel,
19
+ namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
20
+ to: :pref_label
21
+
22
+ predicate :notation,
23
+ namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
24
+ to: :notation
25
+
26
+ predicate :hasBindingnessType,
27
+ namespace: SduSmart::Rdf::Namespaces::SmartNamespace,
28
+ to: :bindingness_type
29
+
30
+ predicate :isPartOf,
31
+ namespace: Lutaml::Rdf::Namespaces::DctermsNamespace,
32
+ to: :is_part_of
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Isq
4
+ VERSION = "0.1.0"
5
+ end
data/lib/isq.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sdu_smart"
4
+
5
+ module Isq
6
+ autoload :Quantity, "#{__dir__}/isq/quantity"
7
+ autoload :Unit, "#{__dir__}/isq/unit"
8
+ autoload :MathConcept, "#{__dir__}/isq/math_concept"
9
+ autoload :VERSION, "#{__dir__}/isq/version"
10
+ end
@@ -0,0 +1,309 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "json"
5
+ require "fileutils"
6
+
7
+ namespace :export do
8
+ ROOT = File.join(__dir__, "..", "..", "..")
9
+ EXPORT_DIR = ENV.fetch("ISQ_EXPORT_DIR", File.join(ROOT, "browser", "public", "exports"))
10
+ DATASET_DIR = ENV.fetch("ISQ_DATASET_DIR", File.join(ROOT, "iso-iec-80000", "sources", "dataset"))
11
+
12
+ PART_TITLES = {
13
+ "3" => "Space and Time", "4" => "Mechanics", "5" => "Thermodynamics",
14
+ "6" => "Electromagnetism", "7" => "Light and Radiation", "8" => "Acoustics",
15
+ "9" => "Physical Chemistry", "10" => "Atomic and Nuclear", "11" => "Characteristic Numbers",
16
+ "12" => "Condensed Matter", "13" => "Information Science", "2" => "Mathematics",
17
+ }.freeze
18
+
19
+ SMART = "https://w3id.org/standards/smart/ontologies/core/"
20
+ ISO = "https://w3id.org/standards/isoiec80000/ontologies/core/"
21
+ DCTERMS = "http://purl.org/dc/terms/"
22
+ SKOS = "http://www.w3.org/2004/02/skos/core#"
23
+ SKOSXL = "http://www.w3.org/2008/05/skos-xl#"
24
+
25
+ JSONLD_CONTEXT = {
26
+ "@context" => {
27
+ "smart" => SMART,
28
+ "isoiec80000" => ISO,
29
+ "dcterms" => DCTERMS,
30
+ "skos" => SKOS,
31
+ "skosxl" => SKOSXL,
32
+ "rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
33
+ "rdfs" => "http://www.w3.org/2000/01/rdf-schema#",
34
+ "owl" => "http://www.w3.org/2002/07/owl#",
35
+ "xsd" => "http://www.w3.org/2001/XMLSchema#",
36
+ },
37
+ }.freeze
38
+
39
+ def load_entries
40
+ quantities = YAML.load_file(File.join(DATASET_DIR, "quantities.yaml"))
41
+ math = YAML.load_file(File.join(DATASET_DIR, "math.yaml"))
42
+ quantities + math
43
+ end
44
+
45
+ def math?(entry)
46
+ entry["part"].to_s.start_with?("2")
47
+ end
48
+
49
+ def entry_rdf_type(entry)
50
+ math?(entry) ? "isoiec80000:MathConcept" : "isoiec80000:Quantity"
51
+ end
52
+
53
+ def unit_uri(unit)
54
+ sym = unit["symbol"]&.first
55
+ sym ? "isoiec80000:unit-#{sym}" : "isoiec80000:unit-#{unit['en']&.downcase&.gsub(/\s+/, '-')}"
56
+ end
57
+
58
+ def escape_literal(s)
59
+ s.gsub("\\", "\\\\\\\\").gsub('"', '\\"')
60
+ end
61
+
62
+ def generate_turtle(entry)
63
+ id = entry["id"]
64
+ rdf_type = entry_rdf_type(entry)
65
+ lines = []
66
+
67
+ # Term instances for designations
68
+ entry["designations"]&.each_with_index do |des, i|
69
+ tid = "term-#{id}-#{i}"
70
+ text = des.dig("designation", "en", "text")
71
+ next unless text
72
+
73
+ lines << "isoiec80000:#{tid} a smart:Term, skosxl:Label ;"
74
+ lines << " skosxl:literalForm \"#{escape_literal(text)}\"@en ;"
75
+ lines << " smart:hasTermFormType smart:fullForm ."
76
+ lines << ""
77
+ end
78
+
79
+ # Term instances for symbols
80
+ entry["symbols"]&.each_with_index do |sym, i|
81
+ tid = "sym-#{id}-#{i}"
82
+ lines << "isoiec80000:#{tid} a smart:Term, skosxl:Label ;"
83
+ lines << " skosxl:literalForm \"#{escape_literal(sym)}\"@en ;"
84
+ lines << " smart:hasTermFormType smart:symbol ."
85
+ lines << ""
86
+ end
87
+
88
+ # Main entry
89
+ lines << "isoiec80000:#{id} a #{rdf_type}, smart:TermEntry ;"
90
+ lines << " dcterms:identifier \"#{entry['num']}\" ;"
91
+
92
+ # skosxl:prefLabel for first designation
93
+ if entry["designations"]&.any?
94
+ lines << " skosxl:prefLabel isoiec80000:term-#{id}-0 ;"
95
+ end
96
+
97
+ # skosxl:altLabel for remaining designations
98
+ entry["designations"]&.each_with_index do |_des, i|
99
+ next if i == 0
100
+ lines << " skosxl:altLabel isoiec80000:term-#{id}-#{i} ;"
101
+ end
102
+
103
+ # skosxl:altLabel for symbol terms
104
+ entry["symbols"]&.each_with_index do |_sym, i|
105
+ lines << " skosxl:altLabel isoiec80000:sym-#{id}-#{i} ;"
106
+ end
107
+
108
+ if entry["def"]&.dig("en")
109
+ lines << " skos:definition \"\"\"#{escape_literal(entry['def']['en'])}\"\"\"@en ;"
110
+ end
111
+
112
+ if entry["remarks"]&.dig("en")
113
+ lines << " skos:note \"\"\"#{escape_literal(entry['remarks']['en'])}\"\"\"@en ;"
114
+ end
115
+
116
+ if entry["units"]&.any?
117
+ for unit in entry["units"]
118
+ lines << " isoiec80000:hasUnit #{unit_uri(unit)} ;"
119
+ end
120
+ end
121
+
122
+ lines << " smart:hasBindingnessType smart:normative ;"
123
+ lines << " dcterms:isPartOf isoiec80000:part-#{entry['part']} ."
124
+
125
+ lines.join("\n")
126
+ end
127
+
128
+ def generate_unit_turtle(unit)
129
+ uri = unit_uri(unit)
130
+ name = unit["en"]
131
+ lines = ["#{uri} a isoiec80000:Unit, smart:TermEntry ;"]
132
+ lines << " skos:prefLabel \"#{name}\"@en ;"
133
+ if unit["symbol"]&.any?
134
+ for sym in unit["symbol"]
135
+ lines << " skos:notation \"#{sym}\" ;"
136
+ end
137
+ end
138
+ lines << " smart:hasBindingnessType smart:normative ."
139
+ lines.join("\n")
140
+ end
141
+
142
+ def entry_jsonld(entry)
143
+ id = entry["id"]
144
+ part = entry["part"]
145
+
146
+ # Term instances for designations
147
+ terms = entry["designations"]&.each_with_index&.map do |des, i|
148
+ text = des.dig("designation", "en", "text")
149
+ next unless text
150
+
151
+ {
152
+ "@id" => "#{ISO}term-#{id}-#{i}",
153
+ "@type" => ["smart:Term", "skosxl:Label"],
154
+ "skosxl:literalForm" => { "@value" => text, "@language" => "en" },
155
+ "smart:hasTermFormType" => { "@id" => "smart:fullForm" },
156
+ }
157
+ end&.compact || []
158
+
159
+ # Term instances for symbols
160
+ symbol_terms = entry["symbols"]&.each_with_index&.map do |sym, i|
161
+ {
162
+ "@id" => "#{ISO}sym-#{id}-#{i}",
163
+ "@type" => ["smart:Term", "skosxl:Label"],
164
+ "skosxl:literalForm" => { "@value" => sym, "@language" => "en" },
165
+ "smart:hasTermFormType" => { "@id" => "smart:symbol" },
166
+ }
167
+ end || []
168
+
169
+ all_terms = terms + symbol_terms
170
+
171
+ entry_obj = {
172
+ "@id" => "#{ISO}#{id}",
173
+ "@type" => [entry_rdf_type(entry), "smart:TermEntry"],
174
+ "dcterms:identifier" => entry["num"],
175
+ }
176
+
177
+ if terms.any?
178
+ entry_obj["skosxl:prefLabel"] = { "@id" => "#{ISO}term-#{id}-0" }
179
+ end
180
+
181
+ alt_labels = []
182
+ entry["designations"]&.each_with_index do |_des, i|
183
+ next if i == 0
184
+ alt_labels << { "@id" => "#{ISO}term-#{id}-#{i}" }
185
+ end
186
+ entry["symbols"]&.each_with_index do |_sym, i|
187
+ alt_labels << { "@id" => "#{ISO}sym-#{id}-#{i}" }
188
+ end
189
+ entry_obj["skosxl:altLabel"] = alt_labels if alt_labels.any?
190
+
191
+ if entry["def"]&.dig("en")
192
+ entry_obj["skos:definition"] = { "@value" => entry["def"]["en"], "@language" => "en" }
193
+ end
194
+
195
+ if entry["remarks"]&.dig("en")
196
+ entry_obj["skos:note"] = { "@value" => entry["remarks"]["en"], "@language" => "en" }
197
+ end
198
+
199
+ if entry["units"]&.any?
200
+ entry_obj["isoiec80000:hasUnit"] = entry["units"].map { |u| { "@id" => unit_uri(u) } }
201
+ end
202
+
203
+ entry_obj["smart:hasBindingnessType"] = { "@id" => "smart:normative" }
204
+ entry_obj["dcterms:isPartOf"] = { "@id" => "#{ISO}part-#{part}" }
205
+
206
+ { "@graph" => all_terms + [entry_obj] }
207
+ end
208
+
209
+ def generate_part_turtle(part_key, part_entries)
210
+ lines = [
211
+ "@prefix isoiec80000: <#{ISO}> .",
212
+ "@prefix smart: <#{SMART}> .",
213
+ "@prefix dcterms: <#{DCTERMS}> .",
214
+ "@prefix skos: <#{SKOS}> .",
215
+ "@prefix skosxl: <#{SKOSXL}> .",
216
+ "",
217
+ ]
218
+
219
+ # PublicationDocument instance
220
+ lines << "isoiec80000:part-#{part_key} a smart:PublicationDocument ;"
221
+ lines << " dcterms:title \"#{PART_TITLES[part_key] || "Part #{part_key}"}\"@en ;"
222
+ lines << " dcterms:identifier \"ISO 80000-#{part_key}\" ;"
223
+ lines << " smart:hasPublicationType smart:internationalStandard ;"
224
+ lines << " smart:hasBindingnessType smart:normative ."
225
+ lines << ""
226
+
227
+ # Collect unique units
228
+ units = part_entries.each_with_object([]) do |entry, acc|
229
+ entry["units"]&.each do |u|
230
+ key = unit_uri(u)
231
+ acc << u unless acc.any? { |existing| unit_uri(existing) == key }
232
+ end
233
+ end
234
+
235
+ units.each do |u|
236
+ lines << generate_unit_turtle(u)
237
+ lines << ""
238
+ end
239
+
240
+ part_entries.each do |entry|
241
+ lines << generate_turtle(entry)
242
+ lines << ""
243
+ end
244
+
245
+ lines.join("\n")
246
+ end
247
+
248
+ desc "Generate TTL and JSON-LD exports for all entries"
249
+ task :all do
250
+ require "sdu_smart"
251
+
252
+ FileUtils.rm_rf(EXPORT_DIR)
253
+ FileUtils.mkdir_p(EXPORT_DIR)
254
+
255
+ entries = load_entries
256
+ by_part = entries.group_by { |e| e["part"].to_s }
257
+
258
+ all_turtle = []
259
+ all_jsonld = []
260
+
261
+ by_part.each do |part_key, part_entries|
262
+ part_dir = File.join(EXPORT_DIR, "part-#{part_key}")
263
+ FileUtils.mkdir_p(part_dir)
264
+
265
+ # Per-part Turtle
266
+ part_ttl = generate_part_turtle(part_key, part_entries)
267
+ File.write(File.join(part_dir, "index.ttl"), part_ttl)
268
+
269
+ # Per-part JSON-LD
270
+ part_graphs = part_entries.map { |e| entry_jsonld(e) }
271
+ part_jsonld = { **JSONLD_CONTEXT, "@graph" => part_graphs.flat_map { |g| g["@graph"] } }
272
+ File.write(File.join(part_dir, "index.jsonld"), JSON.pretty_generate(part_jsonld))
273
+
274
+ all_turtle << part_ttl
275
+ all_jsonld.concat(part_graphs.flat_map { |g| g["@graph"] })
276
+
277
+ # Per-entry files
278
+ part_entries.each do |entry|
279
+ base = entry["id"]
280
+ entry_json = entry_jsonld(entry)
281
+ File.write(File.join(part_dir, "#{base}.ttl"), generate_turtle(entry))
282
+ File.write(File.join(part_dir, "#{base}.jsonld"), JSON.pretty_generate({ **JSONLD_CONTEXT, **entry_json }))
283
+ end
284
+ end
285
+
286
+ # Full bulk exports
287
+ File.write(File.join(EXPORT_DIR, "iso80000-all.ttl"), all_turtle.join("\n"))
288
+ bulk_jsonld = { **JSONLD_CONTEXT, "@graph" => all_jsonld }
289
+ File.write(File.join(EXPORT_DIR, "iso80000-all.jsonld"), JSON.pretty_generate(bulk_jsonld))
290
+
291
+ # Manifest
292
+ manifest = {
293
+ generated: Time.now.utc.iso8601,
294
+ total_entries: entries.length,
295
+ parts: by_part.transform_values(&:length),
296
+ namespaces: {
297
+ smart: SMART,
298
+ isoiec80000: ISO,
299
+ dcterms: DCTERMS,
300
+ skos: SKOS,
301
+ skosxl: SKOSXL,
302
+ },
303
+ }
304
+ File.write(File.join(EXPORT_DIR, "manifest.json"), JSON.pretty_generate(manifest))
305
+
306
+ puts "Generated exports in #{EXPORT_DIR}:"
307
+ puts " #{entries.length} entries across #{by_part.length} parts"
308
+ end
309
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: isq
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ribose
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: sdu_smart
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: 0.1.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.1.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: lutaml-model
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 0.8.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.8.0
40
+ description: 'Ruby gem extending the SmartSDU Core Ontology with ISO/IEC 80000-specific
41
+ domain classes: Quantity, Unit, and MathConcept. Includes a Rake task for generating
42
+ per-part Turtle and JSON-LD exports from YAML source data.'
43
+ email:
44
+ - open.source@ribose.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - lib/isq.rb
50
+ - lib/isq/math_concept.rb
51
+ - lib/isq/quantity.rb
52
+ - lib/isq/unit.rb
53
+ - lib/isq/version.rb
54
+ - lib/tasks/export.rake
55
+ homepage: https://github.com/metanorma/isq
56
+ licenses:
57
+ - BSD-2-Clause
58
+ metadata:
59
+ homepage_uri: https://github.com/metanorma/isq
60
+ source_code_uri: https://github.com/metanorma/isq
61
+ changelog_uri: https://github.com/metanorma/isq/releases
62
+ rubygems_mfa_required: 'true'
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 3.0.0
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubygems_version: 3.6.9
78
+ specification_version: 4
79
+ summary: ISO/IEC 80000 International System of Quantities — Ruby domain classes and
80
+ RDF export.
81
+ test_files: []