relaton 3.0.2 → 3.0.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/lib/relaton/3gpp/data_fetcher.rb +3 -2
- data/lib/relaton/bipm/data_outcomes_parser.rb +2 -1
- data/lib/relaton/bipm/si_brochure_parser.rb +3 -2
- data/lib/relaton/bipm.rb +0 -1
- data/lib/relaton/core/data_fetcher.rb +3 -2
- data/lib/relaton/core/processor.rb +30 -1
- data/lib/relaton/db/CLAUDE.md +14 -2
- data/lib/relaton/db/cache.rb +5 -2
- data/lib/relaton/db/registry.rb +1 -1
- data/lib/relaton/db.rb +10 -6
- data/lib/relaton/ecma/processor.rb +8 -0
- data/lib/relaton/equivalence.rb +2 -2
- data/lib/relaton/gb/scraper.rb +2 -2
- data/lib/relaton/ieee/data_fetcher.rb +2 -1
- data/lib/relaton/iho/processor.rb +6 -0
- data/lib/relaton/index/file_io.rb +9 -8
- data/lib/relaton/index/shard_source.rb +3 -2
- data/lib/relaton/iso/hit_collection.rb +20 -3
- data/lib/relaton/jcgm/meetings_parser.rb +2 -2
- data/lib/relaton/nist/CLAUDE.md +2 -1
- data/lib/relaton/nist/bibliography.rb +31 -0
- data/lib/relaton/nist/cswp.rb +137 -0
- data/lib/relaton/nist/cswp.yaml +155 -0
- data/lib/relaton/nist/data_fetcher.rb +3 -2
- data/lib/relaton/nist/processor.rb +20 -0
- data/lib/relaton/nist.rb +1 -0
- data/lib/relaton/sdo/store.rb +3 -1
- data/lib/relaton/version.rb +1 -1
- data/lib/relaton/xsf/hit.rb +2 -1
- metadata +40 -76
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6cef1665e29f9d4a6e8ab48528253675e5b901399666138306c2be69c0d1b6e
|
|
4
|
+
data.tar.gz: e4eef10cab8373db1d911a4b68342b35c1bb335c15f986d24697f286e83d9a96
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 41185be7ff82e3b45667eb33380fb3eee8e548e3ea24039d8743c5ed50b2fcc1b1c252536f87a1d6b0c4821e41e17b08a45e861af1d6886a8abe9df287a800a6
|
|
7
|
+
data.tar.gz: 3235473497b828e0f5d8890a59b6548994b2ab113d665f010d24e1970c6b24eaa398b298d7f83ba3b770153c558c712059ff5147b5f39d86b61ad5e35312d930
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
1
2
|
require "fileutils"
|
|
2
3
|
require "net/http"
|
|
3
4
|
require "uri"
|
|
@@ -64,7 +65,7 @@ module Relaton
|
|
|
64
65
|
# @return [String, nil] file name
|
|
65
66
|
#
|
|
66
67
|
def get_file(renewal) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
67
|
-
@current =
|
|
68
|
+
@current = Yeptris::Psych.load_file CURRENT if File.exist? CURRENT
|
|
68
69
|
@current ||= {}
|
|
69
70
|
uri = URI(CSV_URL)
|
|
70
71
|
n = 0
|
|
@@ -183,7 +184,7 @@ module Relaton
|
|
|
183
184
|
# @return [Relaton3gpp::BibliographicItem, nil] merged bibliographic item or nil if no merge has been done
|
|
184
185
|
#
|
|
185
186
|
def merge_duplication(bib, file)
|
|
186
|
-
hash =
|
|
187
|
+
hash = Yeptris::Psych.load_file file
|
|
187
188
|
existed = Item.from_hash hash
|
|
188
189
|
changed = update_source? bib, existed
|
|
189
190
|
bib1, bib2, chng = transposed_relation bib, existed
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
1
2
|
module Relaton::Bipm
|
|
2
3
|
class DataOutcomesParser
|
|
3
4
|
SHORTTYPE = {
|
|
@@ -130,7 +131,7 @@ module Relaton::Bipm
|
|
|
130
131
|
fr_file = en_file.sub "en", "fr"
|
|
131
132
|
[en_file, fr_file].map do |file|
|
|
132
133
|
if File.exist? file
|
|
133
|
-
data =
|
|
134
|
+
data = Yeptris::Psych.safe_load(File.read(file, encoding: "UTF-8"))
|
|
134
135
|
path = file
|
|
135
136
|
end
|
|
136
137
|
[path, data]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
1
2
|
require "moxml"
|
|
2
3
|
|
|
3
4
|
module Relaton::Bipm
|
|
@@ -60,8 +61,8 @@ module Relaton::Bipm
|
|
|
60
61
|
warn_duplicate = false
|
|
61
62
|
item2 = Item.from_yaml File.read(outfile, encoding: "UTF-8")
|
|
62
63
|
fix_si_brochure_id item2
|
|
63
|
-
hash1 =
|
|
64
|
-
hash2 =
|
|
64
|
+
hash1 = Yeptris::Psych.safe_load item1.to_yaml
|
|
65
|
+
hash2 = Yeptris::Psych.safe_load item2.to_yaml
|
|
65
66
|
Item.from_yaml deep_merge(hash1, hash2).to_yaml
|
|
66
67
|
else
|
|
67
68
|
warn_duplicate = true
|
data/lib/relaton/bipm.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
1
2
|
require "yeptris"
|
|
2
3
|
require "fileutils"
|
|
3
4
|
|
|
@@ -121,7 +122,7 @@ module Relaton
|
|
|
121
122
|
kept = ledger.merge(ledger_new).select do |_, row|
|
|
122
123
|
row.is_a?(Hash) && row["file"] && @written.include?(row["file"])
|
|
123
124
|
end
|
|
124
|
-
File.write ledger_path,
|
|
125
|
+
File.write ledger_path, Yeptris::Psych.dump(kept), encoding: "UTF-8"
|
|
125
126
|
@ledger = kept
|
|
126
127
|
end
|
|
127
128
|
|
|
@@ -349,7 +350,7 @@ module Relaton
|
|
|
349
350
|
|
|
350
351
|
def ledger
|
|
351
352
|
@ledger ||= if File.exist?(ledger_path)
|
|
352
|
-
|
|
353
|
+
Yeptris::Psych.safe_load_file(ledger_path) || {}
|
|
353
354
|
else
|
|
354
355
|
{}
|
|
355
356
|
end
|
|
@@ -67,7 +67,7 @@ module Relaton
|
|
|
67
67
|
# @return [Pubid::Identifier, nil]
|
|
68
68
|
def cache_pubid(ref)
|
|
69
69
|
pubid_class&.parse(ref)
|
|
70
|
-
rescue ArgumentError, ::Pubid::Errors::Error
|
|
70
|
+
rescue ArgumentError, ::Pubid::Errors::Error
|
|
71
71
|
# relaton-bib carries the log facade; core specs load without it.
|
|
72
72
|
require "relaton/bib"
|
|
73
73
|
Relaton::Bib::Util.info "does not parse as this flavor's pubid; " \
|
|
@@ -134,6 +134,21 @@ module Relaton
|
|
|
134
134
|
copy
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
+
# A copy of `pubid` with each given component the parse leaves nil, for
|
|
138
|
+
# #item_cache_key. `pubid` itself when there is nothing to add.
|
|
139
|
+
#
|
|
140
|
+
# @param pubid [Pubid::Identifier]
|
|
141
|
+
# @param values [Hash{Symbol => String, nil}]
|
|
142
|
+
# @return [Pubid::Identifier]
|
|
143
|
+
def with_components(pubid, **values)
|
|
144
|
+
values = values.select { |attr, value| value && pubid.public_send(attr).nil? }
|
|
145
|
+
return pubid if values.empty?
|
|
146
|
+
|
|
147
|
+
copy = pubid.class.from_hash pubid.to_hash
|
|
148
|
+
values.each { |attr, value| copy.public_send "#{attr}=", value }
|
|
149
|
+
copy
|
|
150
|
+
end
|
|
151
|
+
|
|
137
152
|
# The cache key for a query: the parsed pubid with the `year` and
|
|
138
153
|
# `all_parts` options folded in. The publication date range is not part
|
|
139
154
|
# of the identity, so it never enters the key.
|
|
@@ -153,6 +168,20 @@ module Relaton
|
|
|
153
168
|
pubid
|
|
154
169
|
end
|
|
155
170
|
|
|
171
|
+
# The cache key of a fetched document: its primary docidentifier as
|
|
172
|
+
# parsed (`pubid`), plus the components that docid omits by design and
|
|
173
|
+
# the item keeps elsewhere. A flavor whose printed docid drops the
|
|
174
|
+
# edition (IHO `B-12`, ECMA `ECMA-262`) overrides this, or every
|
|
175
|
+
# edition gets one key and one cache file (relaton#408). The same set
|
|
176
|
+
# as relaton-cli's `MachineIndex::REFINABLE_KEYS`.
|
|
177
|
+
#
|
|
178
|
+
# @param pubid [Pubid::Identifier] the docid's parse; not mutated
|
|
179
|
+
# @param _item [Relaton::Bib::ItemData] the fetched document
|
|
180
|
+
# @return [Pubid::Identifier]
|
|
181
|
+
def item_cache_key(pubid, _item)
|
|
182
|
+
pubid
|
|
183
|
+
end
|
|
184
|
+
|
|
156
185
|
def initialize
|
|
157
186
|
raise "This is an abstract class!"
|
|
158
187
|
end
|
data/lib/relaton/db/CLAUDE.md
CHANGED
|
@@ -223,6 +223,17 @@ relaton#189 item 2 / relaton#204. Two lutaml-store `FileSystem` stores under
|
|
|
223
223
|
both naming the same file. There are no redirect entries. A document is
|
|
224
224
|
deleted with the last row that points to it; a row whose document is gone is
|
|
225
225
|
a miss.
|
|
226
|
+
- **The item row's key must tell editions apart (relaton#408).** `Db#item_key`
|
|
227
|
+
parses the primary docidentifier and passes it through
|
|
228
|
+
`Core::Processor#item_cache_key(pubid, item)`. A flavor whose printed docid
|
|
229
|
+
omits the edition by design overrides the hook to add it from the item:
|
|
230
|
+
IHO adds `version` from `edition` (`IHO B-12` → `IHO B-12 3.0.0`), ECMA adds
|
|
231
|
+
`edition` and the `volume` extent (`ECMA-262` → `ECMA-262 ed16`), through
|
|
232
|
+
`#with_components`, which fills only components the parse leaves nil, on a
|
|
233
|
+
copy. Without it every edition got one item key and one file, and a fetch of
|
|
234
|
+
`IHO B-12 2.0.3` overwrote the file the `IHO B-12 3.0.0` query row named. A
|
|
235
|
+
cache with such a file is dropped by the next `Relaton::VERSION` bump
|
|
236
|
+
(`grammar_hash`), so no separate cache-version bump was made.
|
|
226
237
|
|
|
227
238
|
Matching: the **exact** row first (canonical `to_hash`, not pubid `==`, which
|
|
228
239
|
is false after a `from_hash` round trip for some flavors, e.g. UN). Only a
|
|
@@ -233,8 +244,9 @@ still applies and it never answers with another query's year-stripped copy.
|
|
|
233
244
|
`ISO 19115:2003 === ISO 19115-1:2003` (and the same for IEC, BSI, JIS, GB).
|
|
234
245
|
`Cache#subset_of?` therefore also requires that every component the row adds
|
|
235
246
|
(at any depth) is allowed: only a language for the dated fallback
|
|
236
|
-
(`LANGUAGE_COMPONENTS`), a year/date/month
|
|
237
|
-
`
|
|
247
|
+
(`LANGUAGE_COMPONENTS`), a year/date/month, an edition/version (the
|
|
248
|
+
`item_cache_key` components; not a volume, which names another document) or a
|
|
249
|
+
language for the date-range `candidates` (`EDITION_COMPONENTS`). A row with no `root.number` (DOI, ISBN)
|
|
238
250
|
goes to one of 256 digest buckets (`<flavor>/~<hex>`).
|
|
239
251
|
|
|
240
252
|
Expiry (`Cache#valid_row?`): an undated row, and **every** `not_found` row,
|
data/lib/relaton/db/cache.rb
CHANGED
|
@@ -36,8 +36,11 @@ module Relaton
|
|
|
36
36
|
# reads an omitted `part` as "any part" (`ISO 19115:2003 ===
|
|
37
37
|
# ISO 19115-1:2003`), which names another document.
|
|
38
38
|
LANGUAGE_COMPONENTS = %w[language languages].freeze
|
|
39
|
-
# What a row may add to a key and still be one of its editions.
|
|
40
|
-
|
|
39
|
+
# What a row may add to a key and still be one of its editions. An
|
|
40
|
+
# `edition` or a `version` comes from Core::Processor#item_cache_key
|
|
41
|
+
# (IHO, ECMA; relaton#408). A `volume` names another document.
|
|
42
|
+
EDITION_COMPONENTS =
|
|
43
|
+
(%w[year date month edition version] + LANGUAGE_COMPONENTS).freeze
|
|
41
44
|
|
|
42
45
|
# @return [String]
|
|
43
46
|
attr_reader :dir
|
data/lib/relaton/db/registry.rb
CHANGED
data/lib/relaton/db.rb
CHANGED
|
@@ -226,7 +226,7 @@ module Relaton
|
|
|
226
226
|
# reference they cannot read is not-found, not a failure (relaton#412).
|
|
227
227
|
def unreadable_reference?(error)
|
|
228
228
|
(defined?(Pubid::Errors::Error) && error.is_a?(Pubid::Errors::Error)) ||
|
|
229
|
-
|
|
229
|
+
false
|
|
230
230
|
end
|
|
231
231
|
|
|
232
232
|
# The flavor routing picks for `fetch_async`'s per-flavor queue; nil
|
|
@@ -382,14 +382,18 @@ module Relaton
|
|
|
382
382
|
[query, query && processor.cache_key(code, year, opts, query)]
|
|
383
383
|
end
|
|
384
384
|
|
|
385
|
-
# The key of a fetched document, from its primary identifier
|
|
386
|
-
# identifier
|
|
385
|
+
# The key of a fetched document, from its primary identifier and what
|
|
386
|
+
# that identifier omits (Core::Processor#item_cache_key, relaton#408).
|
|
387
|
+
# The identifier is data, so an unparseable one gives no key.
|
|
387
388
|
def item_key(bib, stdclass)
|
|
388
389
|
docid = bib.docidentifier.detect(&:primary) || bib.docidentifier.first
|
|
389
390
|
return unless docid&.content
|
|
390
391
|
|
|
391
|
-
cache_key docid.content, nil, {}, stdclass
|
|
392
|
-
|
|
392
|
+
key = cache_key docid.content, nil, {}, stdclass
|
|
393
|
+
return key unless key.is_a? ::Pubid::Identifier
|
|
394
|
+
|
|
395
|
+
@registry[stdclass].item_cache_key key, bib
|
|
396
|
+
rescue ::Pubid::Errors::Error
|
|
393
397
|
nil
|
|
394
398
|
end
|
|
395
399
|
|
|
@@ -456,7 +460,7 @@ module Relaton
|
|
|
456
460
|
def copublisher_query(code, opts, stdclass, publisher)
|
|
457
461
|
query = begin
|
|
458
462
|
@registry[stdclass].query_pubid(code, opts)
|
|
459
|
-
rescue ::Pubid::Errors::Error
|
|
463
|
+
rescue ::Pubid::Errors::Error
|
|
460
464
|
nil
|
|
461
465
|
end
|
|
462
466
|
return query if query
|
|
@@ -21,6 +21,14 @@ module Relaton
|
|
|
21
21
|
Bibliography.get(code, date, opts)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
# An ECMA record prints `ECMA-262` with no ` ed<N>`/` vol<N>`, and keeps
|
|
25
|
+
# them in `edition` and in a `volume` extent (relaton#408).
|
|
26
|
+
def item_cache_key(pubid, item)
|
|
27
|
+
volume = item.extent.flat_map(&:locality)
|
|
28
|
+
.detect { |l| l.type == "volume" }&.reference_from
|
|
29
|
+
with_components pubid, edition: item.edition&.content, volume: volume
|
|
30
|
+
end
|
|
31
|
+
|
|
24
32
|
#
|
|
25
33
|
# Fetch all the documents from a source
|
|
26
34
|
#
|
data/lib/relaton/equivalence.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require "
|
|
1
|
+
require "yeptris/psych"
|
|
2
2
|
|
|
3
3
|
module Relaton
|
|
4
4
|
# Cross-SDO equivalence as data: chain patterns and explicit equivalences
|
|
@@ -23,7 +23,7 @@ module Relaton
|
|
|
23
23
|
else
|
|
24
24
|
[path]
|
|
25
25
|
end
|
|
26
|
-
files.each { |f| tables <<
|
|
26
|
+
files.each { |f| tables << Yeptris::Psych.safe_load_file(f, aliases: true) }
|
|
27
27
|
tables
|
|
28
28
|
end
|
|
29
29
|
|
data/lib/relaton/gb/scraper.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
1
2
|
# encoding: UTF-8
|
|
2
3
|
# frozen_string_literal: true
|
|
3
4
|
|
|
4
|
-
require "yaml"
|
|
5
5
|
require "gb_agencies"
|
|
6
6
|
|
|
7
7
|
module Relaton
|
|
@@ -132,7 +132,7 @@ module Relaton
|
|
|
132
132
|
# @param pref [String]
|
|
133
133
|
# @return [Hash{String=>String}]
|
|
134
134
|
def prefix(pref)
|
|
135
|
-
@prefixes ||=
|
|
135
|
+
@prefixes ||= Yeptris::Psych.load_file File.join(__dir__, "yaml/prefixes.yaml")
|
|
136
136
|
@prefixes[pref]
|
|
137
137
|
end
|
|
138
138
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
1
2
|
require "etc"
|
|
2
3
|
require "zip"
|
|
3
4
|
require_relative "../ieee"
|
|
@@ -77,7 +78,7 @@ module Relaton
|
|
|
77
78
|
# @param file [String]
|
|
78
79
|
# @return [String, nil]
|
|
79
80
|
def primary_docid(file)
|
|
80
|
-
yaml =
|
|
81
|
+
yaml = Yeptris::Psych.safe_load(File.read(file, encoding: "UTF-8"),
|
|
81
82
|
permitted_classes: [Symbol, Date, Time])
|
|
82
83
|
id = yaml && yaml["docidentifier"]&.find do |i|
|
|
83
84
|
i["type"] == "IEEE" && i["primary"] == true && i["trademark"].nil?
|
|
@@ -25,6 +25,12 @@ module Relaton
|
|
|
25
25
|
Bibliography.get(code, date, opts)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# An IHO record prints `B-12` and keeps its edition in `edition`, so
|
|
29
|
+
# the edition goes on the key as the version (relaton#408).
|
|
30
|
+
def item_cache_key(pubid, item)
|
|
31
|
+
with_components pubid, version: item.edition&.content
|
|
32
|
+
end
|
|
33
|
+
|
|
28
34
|
# @param xml [String]
|
|
29
35
|
# @return [Relaton::Iho::ItemData]
|
|
30
36
|
def from_xml(xml)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
1
2
|
module Relaton
|
|
2
3
|
module Index
|
|
3
4
|
#
|
|
@@ -219,12 +220,12 @@ module Relaton
|
|
|
219
220
|
end
|
|
220
221
|
|
|
221
222
|
def load_index(yaml, save = false)
|
|
222
|
-
index =
|
|
223
|
+
index = Yeptris::Psych.safe_load(yaml, permitted_classes: [Symbol])
|
|
223
224
|
save index if save
|
|
224
225
|
return deserialize_pubid(index) if check_format(index)
|
|
225
226
|
|
|
226
227
|
report_invalid_index(save, "Wrong structure of")
|
|
227
|
-
rescue Psych::SyntaxError
|
|
228
|
+
rescue Yeptris::Psych::SyntaxError
|
|
228
229
|
report_invalid_index(save, "YAML parsing error when reading")
|
|
229
230
|
rescue InvalidIndexError
|
|
230
231
|
report_invalid_index(save, "Wrong structure of")
|
|
@@ -430,7 +431,7 @@ module Relaton
|
|
|
430
431
|
end
|
|
431
432
|
yaml = yaml.read unless yaml.is_a?(String)
|
|
432
433
|
Util.info "Downloaded index from `#{url}` for sqlite materialization", progname
|
|
433
|
-
raw =
|
|
434
|
+
raw = Yeptris::Psych.safe_load(yaml, permitted_classes: [Symbol])
|
|
434
435
|
return nil unless check_format(raw)
|
|
435
436
|
|
|
436
437
|
Enumerator.new do |y|
|
|
@@ -439,7 +440,7 @@ module Relaton
|
|
|
439
440
|
y << [number, number, r[:id], r[:file]]
|
|
440
441
|
end
|
|
441
442
|
end
|
|
442
|
-
rescue Psych::SyntaxError, SocketError, OpenURI::HTTPError, Errno::ECONNRESET,
|
|
443
|
+
rescue Yeptris::Psych::SyntaxError, SocketError, OpenURI::HTTPError, Errno::ECONNRESET,
|
|
443
444
|
OpenSSL::SSL::SSLError => e
|
|
444
445
|
Util.info "SQLite index build failed (#{e.message}); falling back", progname
|
|
445
446
|
nil
|
|
@@ -512,8 +513,8 @@ module Relaton
|
|
|
512
513
|
return unless yaml
|
|
513
514
|
|
|
514
515
|
begin
|
|
515
|
-
raw =
|
|
516
|
-
rescue Psych::SyntaxError
|
|
516
|
+
raw = Yeptris::Psych.safe_load(yaml, permitted_classes: [Symbol])
|
|
517
|
+
rescue Yeptris::Psych::SyntaxError
|
|
517
518
|
warn_local_index_error("YAML parsing error when reading")
|
|
518
519
|
delete_sidecar
|
|
519
520
|
return [[], []]
|
|
@@ -613,7 +614,7 @@ module Relaton
|
|
|
613
614
|
yaml = zip.entries.first.get_input_stream.read
|
|
614
615
|
end
|
|
615
616
|
Util.info "Downloaded index from `#{url}`", progname
|
|
616
|
-
raw =
|
|
617
|
+
raw = Yeptris::Psych.safe_load(yaml, permitted_classes: [Symbol])
|
|
617
618
|
if check_format(raw)
|
|
618
619
|
save raw
|
|
619
620
|
raw = nil # release the parsed copy; read_raw_file loads the sidecar's
|
|
@@ -622,7 +623,7 @@ module Relaton
|
|
|
622
623
|
warn_remote_index_error "Wrong structure of"
|
|
623
624
|
[[], []]
|
|
624
625
|
end
|
|
625
|
-
rescue Psych::SyntaxError
|
|
626
|
+
rescue Yeptris::Psych::SyntaxError
|
|
626
627
|
warn_remote_index_error "YAML parsing error when reading"
|
|
627
628
|
[[], []]
|
|
628
629
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
1
2
|
require "yeptris"
|
|
2
3
|
require "json"
|
|
3
4
|
require "net/http"
|
|
@@ -152,11 +153,11 @@ module Relaton
|
|
|
152
153
|
yaml = unzip(body)
|
|
153
154
|
raise Error, "empty archive at `#{url(path)}`" unless yaml
|
|
154
155
|
|
|
155
|
-
index =
|
|
156
|
+
index = Yeptris::Psych.safe_load(yaml, permitted_classes: [Symbol])
|
|
156
157
|
raise Error, "wrong structure of `#{url(path)}`" unless @file_io.check_basic_format(index)
|
|
157
158
|
|
|
158
159
|
@file_io.deserialize_pubid(index)
|
|
159
|
-
rescue FileIO::InvalidIndexError, Psych::SyntaxError, Zip::Error, Zlib::Error => e
|
|
160
|
+
rescue FileIO::InvalidIndexError, Yeptris::Psych::SyntaxError, Zip::Error, Zlib::Error => e
|
|
160
161
|
raise Error, "cannot read `#{url(path)}`: #{e.message}"
|
|
161
162
|
end
|
|
162
163
|
end
|
|
@@ -88,13 +88,30 @@ module Relaton
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def pubid_match?(pubid)
|
|
91
|
-
match_excludings =
|
|
92
|
-
|
|
91
|
+
match_excludings = base_match_excludings
|
|
92
|
+
unless pubid.typed_stage&.abbr&.include?("DIR")
|
|
93
|
+
match_excludings = match_excludings + [:edition]
|
|
94
|
+
end
|
|
93
95
|
# Only the candidate is built via .create (from the index) and so may
|
|
94
96
|
# carry a compound part; `ref_pubid_no_year` is always a parsed pubid,
|
|
95
97
|
# already split, so it needs no normalization.
|
|
96
98
|
cand = normalize_compound_part(exclude_id_attrs(pubid, *match_excludings))
|
|
97
|
-
cand ==
|
|
99
|
+
cand == ref_excluded(match_excludings)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# The candidate's excludings set varies only by the DIR exception; the
|
|
103
|
+
# reference side is invariant per distinct set. Exclude the ref's
|
|
104
|
+
# (possibly deep) base chain once per set, not once per candidate —
|
|
105
|
+
# on a large vocabulary compile this chain ran per candidate per
|
|
106
|
+
# lookup and dominated the bibitem-resolution phase (relaton#423).
|
|
107
|
+
def base_match_excludings
|
|
108
|
+
@base_match_excludings ||= translate_excludings(excludings) + [:all_parts]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def ref_excluded(match_excludings)
|
|
112
|
+
@ref_excluded ||= {}
|
|
113
|
+
@ref_excluded[match_excludings] ||=
|
|
114
|
+
exclude_id_attrs(ref_pubid_no_year, *match_excludings)
|
|
98
115
|
end
|
|
99
116
|
|
|
100
117
|
# @TODO TEMP WORKAROUND (pubid 2.x migration): the v1-generated index
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
1
2
|
require "fileutils"
|
|
2
|
-
require "yaml"
|
|
3
3
|
require "date"
|
|
4
4
|
|
|
5
5
|
module Relaton::Jcgm
|
|
@@ -65,7 +65,7 @@ module Relaton::Jcgm
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def load_yaml(file)
|
|
68
|
-
|
|
68
|
+
Yeptris::Psych.safe_load File.read(file, encoding: "UTF-8"), permitted_classes: [Date, Time]
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
# "JCGM 17th Meeting (2012)" — ordinal via pubid's Meeting.ordinal.
|
data/lib/relaton/nist/CLAUDE.md
CHANGED
|
@@ -64,7 +64,8 @@ All data models inherit from `Lutaml::Model::Serializable` and use declarative a
|
|
|
64
64
|
- **Per-record resilience (mirrors the ISO flavor).** A single bad record must never abort the crawl. `#write_file` skips (never `id.content`-crashes on) a record whose MODS entry yields **no docidentifier** (e.g. a DOI stored only in `<identifier type="doi">`, which `ModsParser#parse_doi` doesn't read), and records ids that `#pubid` can't parse (new series like `NIST RB`/`NIST CHIPS`) as "not indexed" — the YAML is still written, only the index-v2 entry is skipped. Both classes accumulate in `#failures`; the `#report_errors` override emits each at `:error` (via `#log_error` → `Util.error`), which the shared `Core::DataFetcher` gh_issue machinery turns into one "Error fetching documents" GitHub issue in CI (`GITHUB_REPOSITORY` + `GITHUB_TOKEN`). `#pubid` no longer warns per-failure; reporting is centralized in `#report_errors`.
|
|
65
65
|
- **Draft-stage rows come from CSRC (relaton#313).** The MODS collection has no draft records, so `#fetch_csrc` adds the CSRC pubs-export feed (`PubsExport.instance.data`) after the MODS pass. It builds each row **exactly as the runtime builds a CSRC hit** — `HitCollection#hit_data` (which calls `#pubs_export_id`) → `Scraper.parse_page` — so the crawled id and the live id are one string, with the stage, revision and update from the row's uri and DOI (`NIST FIPS 197/Upd1 ipd`, `NIST SP 800-108r1 ipd`, `NIST SP 1800-12 2pd`). Only a row whose id carries a **stage** is written; finals come from MODS, so a CSRC row never shadows a base document. On the 914-row fixture feed this writes 302 staged records. Don't go back to a hand-built `"NIST #{series} #{docnumber} #{stage}"` id: it dropped the numbered drafts (`2pd`…), the `/UpdN`, and spelled revisions `Rev. 1`. A stage that CSRC does not list (e.g. `NIST FIPS 197 fpd`) still answers through the consumer's #303 family fallback. Three more rules: `#csrc_bib` clears the `fetched` date the runtime stamps, or every draft changes each day and `Core#force_write` rewrites ~300 files per incremental crawl; CSRC lists a republished draft twice (`…/ipd` and `…/ipd-(1)`, one DOI, so one id), so each id is written once and the plain uri wins; and only the build step is rescued (to a `#failures` entry) — a write error stops the crawl, as in the MODS pass. `#pubs_export_id` must not mutate its row (`sub`, not `sub!`): the crawler and the runtime share the `PubsExport` data.
|
|
66
66
|
- **A CSRC relation names the related document (relaton#314).** `Scraper#related_id` finds the related feed row by its uri and derives its id with `HitCollection.pubs_export_id` (the class method the instance method delegates to). The entry's own `docidentifier` drops the update and the part, and the hit's code is the record's own id, so neither is the related id. `HitCollection#search_filter` ends in an `ensure` that returns `arr`: every branch must set `arr`, because an earlier `return` is overridden there.
|
|
67
|
-
- **CSWP comes from CSRC, not the NIST Library (relaton-nist#104).** The NIST Library announced CSWP data, but its MODS release (`allrecords-MODS.xml`, and the MARC `allrecords.xml`) holds **no** CSWP record — 0 on the Sept2026 release. It publishes CSWPs only as RIS/BibTeX files under `bib/` on its `nist-pages` branch. So the index has only the CSRC draft CSWP rows (`#fetch_csrc`), and a final CSWP answers from CSRC at runtime: `HitCollection#search` asks the CSRC feed (`from_json`) and asks the index (`from_ga`) only when CSRC gives no hit. That is the chosen rule — CSRC wins, no merge — because CSRC lists every numbered CSWP with more metadata (affiliations, status, supersedes, comment period).
|
|
67
|
+
- **CSWP comes from CSRC, not the NIST Library (relaton-nist#104).** The NIST Library announced CSWP data, but its MODS release (`allrecords-MODS.xml`, and the MARC `allrecords.xml`) holds **no** CSWP record — 0 on the Sept2026 release. It publishes CSWPs only as RIS/BibTeX files under `bib/` on its `nist-pages` branch. So the index has only the CSRC draft CSWP rows (`#fetch_csrc`), and a final CSWP answers from CSRC at runtime: `HitCollection#search` asks the CSRC feed (`from_json`) and asks the index (`from_ga`) only when CSRC gives no hit. That is the chosen rule — CSRC wins, no merge — because CSRC lists every numbered CSWP with more metadata (affiliations, status, supersedes, comment period). `hit_collection_spec.rb` pins the CSRC answer.
|
|
68
|
+
- **Legacy dated CSWP ids and CSWP translations come from a static table (relaton#406).** CSRC lists neither the ~30 **legacy dated ids** (`NIST CSWP 01142020`, the same paper as `CSWP 9`, from before NIST numbered the series) nor the translations (`NIST.CSWP.6.spa`). `cswp.yaml` holds both, built once from the 84 RIS files (header of the file says how), and `Cswp` reads it. `Bibliography.get` asks `Cswp.lookup` **first** — the parenthetical-date regex after it would strip `(spa)`. A legacy id fetches the numbered id through the normal CSRC path and adds the dated id as a non-primary docidentifier (`06222021-draft` → the CSRC `CSWP 19 ipd`); a translation, dated or numbered, answers an offline record from the table (`NIST CSWP 6(spa)`, the RIS title/authors/year/DOI, `language`/`script` from `Cswp::LANGUAGES`, a `translatedFrom` relation to the original). `normalize` reads `NIST CSWP 6 spa`, `NIST CSWP 6(spa)` and `NIST.CSWP.6.spa` as one key. `Processor#cache_pubid` returns nil (so `Db` hands `get` the String and does not cache) for **every legacy id** and for a translation that pubid does not render back. A legacy answer is the numbered record plus the dated id, and `Db` would store it under the numbered id's row, so a later `NIST CSWP 9` would carry `NIST CSWP 01142020`. pubid reads `04162018ar` as `04162018a`, `6 spa` as `6(spa)`, and fails on `04162018bg`/`02122014pl`. A translation answers only for its own RIS year. Not mapped, on purpose: `02042022-1`…`-5` have no SN and share the title "Cybersecurity White Paper: EO Response", so they still answer not-found. The CSRC `ipd` rows of `27`/`29`/`32` already answer, so the table leaves out their RIS files. A new translation means a new table row.
|
|
68
69
|
- **`#write_file` serializes before it indexes**, so a record that fails to serialize leaves no index row naming a file that was never written. That is how a `Bib::Date` in `Scraper.fetch_dates` hid: `Nist::ItemData` takes `Nist::Date`, and YAML serialization rejects the parent class (the same strictness as the docidentifier note below).
|
|
69
70
|
- `ModsParser` — maps MODS XML (via `loc_mods`) to `ItemData`
|
|
70
71
|
- `Scraper` — fetches items from GitHub YAML or CSRC JSON
|
|
@@ -39,6 +39,9 @@ module Relaton
|
|
|
39
39
|
# read back as its printed form; the pubid itself is not changed.
|
|
40
40
|
ref = ref.to_s unless ref.is_a?(String)
|
|
41
41
|
return fetch_ref_err(ref, year, []) if ref.match?(/\sEP$/)
|
|
42
|
+
# Before the parsing below, which reads "(spa)" as a date.
|
|
43
|
+
cswp = Cswp.lookup(ref)
|
|
44
|
+
return cswp_get(ref, cswp, year, opts) if cswp
|
|
42
45
|
|
|
43
46
|
/^(?<code2>[^(]+)(?:\((?<date2>\w+\s(?:\d{2},\s)?\d{4})\))?\s?\(?(?:(?<=\()(?<stage>(?:I|F|\d)PD))?/ =~ ref
|
|
44
47
|
stage ||= /(?<=\.)PD-\w+(?=\.)/.match(ref)&.to_s
|
|
@@ -62,6 +65,34 @@ module Relaton
|
|
|
62
65
|
|
|
63
66
|
private
|
|
64
67
|
|
|
68
|
+
#
|
|
69
|
+
# Get a CSWP that CSRC does not list (relaton#406). A legacy dated id
|
|
70
|
+
# answers the numbered CSRC record, with the dated id as an extra
|
|
71
|
+
# docidentifier; a translation answers its record from the RIS table.
|
|
72
|
+
#
|
|
73
|
+
# @param ref [String] reference
|
|
74
|
+
# @param cswp [Array] the Cswp.lookup result
|
|
75
|
+
# @param year [String, nil] year
|
|
76
|
+
# @param opts [Hash] options
|
|
77
|
+
#
|
|
78
|
+
# @return [Relaton::Nist::ItemData, nil] bibliographic item
|
|
79
|
+
#
|
|
80
|
+
def cswp_get(ref, cswp, year, opts)
|
|
81
|
+
type, id, dated = cswp
|
|
82
|
+
if type == :translation
|
|
83
|
+
return fetch_ref_err(ref, year, [id[:year]]) if year && year.to_s != id[:year]
|
|
84
|
+
|
|
85
|
+
item = Cswp.translation_item(id)
|
|
86
|
+
Util.info "Found: `#{item.docidentifier.first.content}`", key: ref
|
|
87
|
+
return item
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
Util.info "Legacy dated CSWP id, fetching `#{id}`", key: ref
|
|
91
|
+
item = nistbib_get(id, year, opts)
|
|
92
|
+
item&.docidentifier&.<< Docidentifier.new(content: dated, type: "NIST")
|
|
93
|
+
item
|
|
94
|
+
end
|
|
95
|
+
|
|
65
96
|
#
|
|
66
97
|
# Get NIST document by reference
|
|
67
98
|
#
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Nist
|
|
5
|
+
#
|
|
6
|
+
# CSWP ids the CSRC pubs-export feed does not list (relaton#406): the
|
|
7
|
+
# legacy dated ids from before the series was numbered, and the
|
|
8
|
+
# translations. The table (`cswp.yaml`) is built from the NIST Library RIS
|
|
9
|
+
# files; see the header of that file.
|
|
10
|
+
#
|
|
11
|
+
# This file loads no flavor model at require time, so the processor can
|
|
12
|
+
# ask #lookup on the cold path. #translation_item needs the models.
|
|
13
|
+
#
|
|
14
|
+
module Cswp
|
|
15
|
+
# ISO 639-2 code of a translation => [ISO 639-1 code, ISO 15924 script]
|
|
16
|
+
LANGUAGES = {
|
|
17
|
+
"ara" => %w[ar Arab], "bul" => %w[bg Cyrl], "fre" => %w[fr Latn],
|
|
18
|
+
"gre" => %w[el Grek], "ind" => %w[id Latn], "jpn" => %w[ja Jpan],
|
|
19
|
+
"kor" => %w[ko Kore], "may" => %w[ms Latn], "nor" => %w[no Latn],
|
|
20
|
+
"pol" => %w[pl Latn], "por" => %w[pt Latn], "spa" => %w[es Latn],
|
|
21
|
+
"ukr" => %w[uk Cyrl]
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
# @return [Hash] the table as written in cswp.yaml
|
|
26
|
+
def table
|
|
27
|
+
@table ||= Yeptris::Psych.load_file File.expand_path("cswp.yaml", __dir__)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# The comparable form of a CSWP reference: no publisher, the
|
|
32
|
+
# separators (".", "(...)") read as spaces, upper case.
|
|
33
|
+
# "NIST CSWP 6(spa)", "NIST.CSWP.6.spa" and "NIST CSWP 6 spa" all
|
|
34
|
+
# give "CSWP 6 SPA".
|
|
35
|
+
#
|
|
36
|
+
# @param ref [String]
|
|
37
|
+
# @return [String]
|
|
38
|
+
def normalize(ref)
|
|
39
|
+
ref.to_s.sub(/\ANIST[.\s]+/i, "").gsub(/[().]/, " ").squeeze(" ").strip.upcase
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
#
|
|
43
|
+
# Find a reference in the table.
|
|
44
|
+
#
|
|
45
|
+
# @param ref [String]
|
|
46
|
+
# @return [Array, nil] `[:legacy, numbered id, dated id]` for a
|
|
47
|
+
# legacy id of an English CSWP, `[:translation, entry]` for a
|
|
48
|
+
# translation (dated or numbered), nil otherwise
|
|
49
|
+
def lookup(ref)
|
|
50
|
+
key = normalize ref
|
|
51
|
+
return unless key.start_with? "CSWP "
|
|
52
|
+
|
|
53
|
+
key = key.delete_prefix "CSWP "
|
|
54
|
+
dated, target = legacy[key]
|
|
55
|
+
entry = translations[(target || key).upcase]
|
|
56
|
+
return [:translation, entry] if entry
|
|
57
|
+
|
|
58
|
+
[:legacy, "NIST CSWP #{target}", "NIST CSWP #{dated}"] if target
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# Build the record of a translation from its RIS fields.
|
|
63
|
+
#
|
|
64
|
+
# @param entry [Hash] a translation entry from #lookup
|
|
65
|
+
# @return [Relaton::Nist::ItemData]
|
|
66
|
+
def translation_item(entry) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
67
|
+
number, lang_code = entry[:id].split
|
|
68
|
+
language, script = LANGUAGES.fetch lang_code
|
|
69
|
+
id = "NIST CSWP #{number}(#{lang_code})"
|
|
70
|
+
ItemData.new(
|
|
71
|
+
type: "standard",
|
|
72
|
+
docidentifier: [
|
|
73
|
+
Docidentifier.new(content: id, type: "NIST", primary: true),
|
|
74
|
+
Docidentifier.new(content: entry[:doi].split("/").last, type: "DOI"),
|
|
75
|
+
],
|
|
76
|
+
title: [Bib::Title.new(content: entry[:title], language: "en", script: "Latn")],
|
|
77
|
+
source: [Bib::Uri.new(type: "doi", content: "https://doi.org/#{entry[:doi]}")],
|
|
78
|
+
date: [Date.new(type: "published", at: entry[:year])],
|
|
79
|
+
contributor: entry[:authors].map { |name| author name } << publisher,
|
|
80
|
+
language: [language],
|
|
81
|
+
script: [script],
|
|
82
|
+
relation: [original_relation("NIST CSWP #{number}")],
|
|
83
|
+
place: [Bib::Place.new(city: "Gaithersburg", region: [Bib::Place::RegionType.new(iso: "MD")])],
|
|
84
|
+
ext: Ext.new(doctype: Doctype.new(content: "standard"), flavor: "nist"),
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
# dated id (upper case) => [dated id, numbered id]
|
|
91
|
+
def legacy
|
|
92
|
+
@legacy ||= table["legacy"].to_h { |dated, numbered| [dated.upcase, [dated, numbered]] }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# translation id (upper case) => entry
|
|
96
|
+
def translations
|
|
97
|
+
@translations ||= table["translations"].to_h do |id, fields|
|
|
98
|
+
[id.upcase, fields.transform_keys(&:to_sym).merge(id: id, authors: fields["authors"] || [])]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @param name [String] RIS author, "Surname, Forename"
|
|
103
|
+
# @return [Bib::Contributor]
|
|
104
|
+
def author(name)
|
|
105
|
+
surname, forename = name.split(/,\s*/, 2)
|
|
106
|
+
fullname = Bib::FullName.new(
|
|
107
|
+
surname: Bib::LocalizedString.new(content: surname, language: "en", script: "Latn"),
|
|
108
|
+
forename: [Bib::FullNameType::Forename.new(content: forename, language: "en", script: "Latn")],
|
|
109
|
+
)
|
|
110
|
+
Bib::Contributor.new(
|
|
111
|
+
person: Bib::Person.new(name: fullname),
|
|
112
|
+
role: [Bib::Contributor::Role.new(type: "author")],
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @return [Bib::Contributor]
|
|
117
|
+
def publisher
|
|
118
|
+
org = Bib::Organization.new(
|
|
119
|
+
name: [Bib::TypedLocalizedString.new(content: "National Institute of Standards and Technology")],
|
|
120
|
+
abbreviation: Bib::LocalizedString.new(content: "NIST"),
|
|
121
|
+
)
|
|
122
|
+
Bib::Contributor.new(organization: org, role: [Bib::Contributor::Role.new(type: "publisher")])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @param id [String] the original's id
|
|
126
|
+
# @return [Relaton::Nist::Relation]
|
|
127
|
+
def original_relation(id)
|
|
128
|
+
bib = ItemData.new(
|
|
129
|
+
formattedref: Bib::Formattedref.new(content: id),
|
|
130
|
+
docidentifier: [Docidentifier.new(content: id, type: "NIST", primary: true)],
|
|
131
|
+
)
|
|
132
|
+
Relation.new(type: "translatedFrom", bibitem: bib)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# CSWP ids the CSRC pubs-export feed does not list (relaton#406).
|
|
2
|
+
#
|
|
3
|
+
# Source: the RIS files `bib/NIST.CSWP.*.ris` on the `nist-pages` branch of
|
|
4
|
+
# usnistgov/NIST-Tech-Pubs (the NIST Library MODS/MARC releases hold no CSWP).
|
|
5
|
+
#
|
|
6
|
+
# legacy: a dated id from before the series was numbered -> the numbered id,
|
|
7
|
+
# matched by the exact RIS title (TI). A dated translation maps to the
|
|
8
|
+
# numbered translation. `06222021-draft` has no numbered RIS; its title is
|
|
9
|
+
# the CSRC `CSWP 19` initial public draft.
|
|
10
|
+
# Not mapped: `02042022-1`..`-5` have no SN and share the title
|
|
11
|
+
# "Cybersecurity White Paper: EO Response", so no numbered id matches.
|
|
12
|
+
#
|
|
13
|
+
# translations: numbered id + ISO 639-2 language code -> the RIS fields (TI,
|
|
14
|
+
# PY, DO, AU). The original is the numbered id without the language code.
|
|
15
|
+
legacy:
|
|
16
|
+
"01142020": "9"
|
|
17
|
+
"01162020es": "10 spa"
|
|
18
|
+
"01162020id": "10 ind"
|
|
19
|
+
"01162020pt": "10 por"
|
|
20
|
+
"01262018": "5"
|
|
21
|
+
"02042020-1": "11"
|
|
22
|
+
"02042020-2": "11A"
|
|
23
|
+
"02042020-3": "11B"
|
|
24
|
+
"02042020-4": "11C"
|
|
25
|
+
"02042020-5": "11D"
|
|
26
|
+
"02042020-6": "11E"
|
|
27
|
+
"02042020-7": "11F"
|
|
28
|
+
"02122014": "1"
|
|
29
|
+
"02122014pl": "1 pol"
|
|
30
|
+
"02192014": "2"
|
|
31
|
+
"04162018": "6"
|
|
32
|
+
"04162018ar": "6 ara"
|
|
33
|
+
"04162018bg": "6 bul"
|
|
34
|
+
"04162018es": "6 spa"
|
|
35
|
+
"04162018fr": "6 fre"
|
|
36
|
+
"04162018id": "6 ind"
|
|
37
|
+
"04162018pt": "6 por"
|
|
38
|
+
"04212016": "4"
|
|
39
|
+
"04222019": "8"
|
|
40
|
+
"04232020": "13"
|
|
41
|
+
"04282021": "15"
|
|
42
|
+
"06032014": "3"
|
|
43
|
+
"06222021-draft": "19 ipd"
|
|
44
|
+
"09102018": "7"
|
|
45
|
+
"09292021": "21"
|
|
46
|
+
translations:
|
|
47
|
+
"1 pol":
|
|
48
|
+
title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.0 (Polish translation)
|
|
49
|
+
year: "2014"
|
|
50
|
+
doi: 10.6028/NIST.CSWP.1.pol
|
|
51
|
+
authors:
|
|
52
|
+
- Stine, Kevin
|
|
53
|
+
"6 ara":
|
|
54
|
+
title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.1 (Arabic translation)
|
|
55
|
+
year: "2018"
|
|
56
|
+
doi: 10.6028/NIST.CSWP.6.ara
|
|
57
|
+
authors:
|
|
58
|
+
- Stine, Kevin
|
|
59
|
+
"6 bul":
|
|
60
|
+
title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.1 (Bulgarian translation)
|
|
61
|
+
year: "2018"
|
|
62
|
+
doi: 10.6028/NIST.CSWP.6.bul
|
|
63
|
+
authors:
|
|
64
|
+
- Stine, Kevin
|
|
65
|
+
"6 fre":
|
|
66
|
+
title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.1 (French Translation)
|
|
67
|
+
year: "2018"
|
|
68
|
+
doi: 10.6028/NIST.CSWP.6.fre
|
|
69
|
+
authors:
|
|
70
|
+
- Stine, Kevin
|
|
71
|
+
"6 gre":
|
|
72
|
+
title: Greek Translation of the Framework for Improving Critical Infrastructure Cybersecurity
|
|
73
|
+
year: "2018"
|
|
74
|
+
doi: 10.6028/NIST.CSWP.6.gre
|
|
75
|
+
authors:
|
|
76
|
+
- Stine, Kevin
|
|
77
|
+
"6 ind":
|
|
78
|
+
title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.1 (Indonesian Translation)
|
|
79
|
+
year: "2018"
|
|
80
|
+
doi: 10.6028/NIST.CSWP.6.ind
|
|
81
|
+
authors:
|
|
82
|
+
- Stine, Kevin
|
|
83
|
+
"6 jpn":
|
|
84
|
+
title: Japanese Translation of Framework for Improving Critical Infrastructure Cybersecurity Version 1.1 (Cybersecurity Framework)
|
|
85
|
+
year: "2018"
|
|
86
|
+
doi: 10.6028/NIST.CSWP.6.jpn
|
|
87
|
+
authors:
|
|
88
|
+
- Stine, Kevin
|
|
89
|
+
"6 kor":
|
|
90
|
+
title: Korean Translation of the Framework for Improving Critical Infrastructure Cybersecurity Version 1.1
|
|
91
|
+
year: "2024"
|
|
92
|
+
doi: 10.6028/NIST.CSWP.6.kor
|
|
93
|
+
authors:
|
|
94
|
+
- Stine, Kevin
|
|
95
|
+
"6 may":
|
|
96
|
+
title: Malay Translation of the Framework for Improving Critical Infrastructure Cybersecurity V1.1
|
|
97
|
+
year: "2018"
|
|
98
|
+
doi: 10.6028/NIST.CSWP.6.may
|
|
99
|
+
authors:
|
|
100
|
+
- Stine, Kevin
|
|
101
|
+
"6 nor":
|
|
102
|
+
title: Norwegian Translation of the Framework for Improving Critical Infrastructure Cybersecurity Version 1.1
|
|
103
|
+
year: "2018"
|
|
104
|
+
doi: 10.6028/NIST.CSWP.6.nor
|
|
105
|
+
authors:
|
|
106
|
+
- Stine, Kevin
|
|
107
|
+
"6 por":
|
|
108
|
+
title: Portuguese Translation of the Framework for Improving Critical Infrastructure Cybersecurity Version 1.1 (Cybersecurity Framework)
|
|
109
|
+
year: "2018"
|
|
110
|
+
doi: 10.6028/NIST.CSWP.6.por
|
|
111
|
+
authors:
|
|
112
|
+
- Stine, Kevin
|
|
113
|
+
"6 spa":
|
|
114
|
+
title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.1 (Spanish translation)
|
|
115
|
+
year: "2018"
|
|
116
|
+
doi: 10.6028/NIST.CSWP.6.spa
|
|
117
|
+
authors:
|
|
118
|
+
- Stine, Kevin
|
|
119
|
+
"6 ukr":
|
|
120
|
+
title: Ukranian Translation of the Framework for Improving Critical Infrastructure Cybersecurity Version 1.1 (Cybersecurity Framework)
|
|
121
|
+
year: "2018"
|
|
122
|
+
doi: 10.6028/NIST.CSWP.6.ukr
|
|
123
|
+
authors:
|
|
124
|
+
- Stine, Kevin
|
|
125
|
+
"10 ara":
|
|
126
|
+
title: "NIST Privacy Framework: A Tool for Improving Privacy Through Enterprise Risk Management, Version 1.0 (Arabic translation)"
|
|
127
|
+
year: "2022"
|
|
128
|
+
doi: 10.6028/NIST.CSWP.10.ara
|
|
129
|
+
authors:
|
|
130
|
+
- Lefkovitz, Naomi
|
|
131
|
+
- Boeckl, Katie
|
|
132
|
+
"10 ind":
|
|
133
|
+
title: Indonesian Translation of the NIST Privacy Framework Version 1.0
|
|
134
|
+
year: "2020"
|
|
135
|
+
doi: 10.6028/NIST.CSWP.10.ind
|
|
136
|
+
authors:
|
|
137
|
+
- Lefkovitz, Naomi
|
|
138
|
+
"10 may":
|
|
139
|
+
title: "NIST Privacy Framework: A Tool for Improving Privacy Through Enterprise Risk Management, Version 1.0 (Malay translation)"
|
|
140
|
+
year: "2024"
|
|
141
|
+
doi: 10.6028/NIST.CSWP.10.may
|
|
142
|
+
authors:
|
|
143
|
+
- Lefkovitz, Naomi
|
|
144
|
+
"10 por":
|
|
145
|
+
title: Portuguese Translation of the NIST Privacy Framework Version 1.0
|
|
146
|
+
year: "2021"
|
|
147
|
+
doi: 10.6028/NIST.CSWP.10.por
|
|
148
|
+
authors:
|
|
149
|
+
- Lefkovitz, Naomi
|
|
150
|
+
"10 spa":
|
|
151
|
+
title: Spanish Translation of the NIST Privacy Framework Version 1.0
|
|
152
|
+
year: "2021"
|
|
153
|
+
doi: 10.6028/NIST.CSWP.10.spa
|
|
154
|
+
authors:
|
|
155
|
+
- Lefkovitz, Naomi
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "yeptris/psych"
|
|
4
|
+
|
|
4
5
|
require "mechanize"
|
|
5
6
|
require "loc_mods"
|
|
6
7
|
require_relative "../nist"
|
|
@@ -183,7 +184,7 @@ module Relaton
|
|
|
183
184
|
end
|
|
184
185
|
|
|
185
186
|
def series
|
|
186
|
-
@series ||=
|
|
187
|
+
@series ||= Yeptris::Psych.load_file File.expand_path("series.yaml", __dir__)
|
|
187
188
|
end
|
|
188
189
|
end
|
|
189
190
|
end
|
|
@@ -21,6 +21,26 @@ module Relaton
|
|
|
21
21
|
Bibliography.get(code, date, opts)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
# Two kinds of Cswp table reference reach `get` as written and are not
|
|
25
|
+
# cached (relaton#406). A legacy dated id answers the numbered record
|
|
26
|
+
# with the dated id added, and Db would store that record under the
|
|
27
|
+
# numbered id's row, so a later "NIST CSWP 9" would carry the dated id.
|
|
28
|
+
# A translation that pubid reads in another form ("NIST CSWP
|
|
29
|
+
# 04162018ar" as "...04162018a", "NIST CSWP 6 spa" as "...6(spa)").
|
|
30
|
+
#
|
|
31
|
+
# @param ref [String]
|
|
32
|
+
# @return [Pubid::Nist::Identifier, nil]
|
|
33
|
+
def cache_pubid(ref)
|
|
34
|
+
pubid = super
|
|
35
|
+
return if pubid.nil?
|
|
36
|
+
|
|
37
|
+
require_relative "cswp"
|
|
38
|
+
type, = Cswp.lookup(ref)
|
|
39
|
+
return pubid if type.nil? || (type == :translation && pubid.to_s == ref)
|
|
40
|
+
|
|
41
|
+
nil
|
|
42
|
+
end
|
|
43
|
+
|
|
24
44
|
#
|
|
25
45
|
# Fetch all the documents from a source
|
|
26
46
|
#
|
data/lib/relaton/nist.rb
CHANGED
|
@@ -12,6 +12,7 @@ require_relative "nist/pubs_export"
|
|
|
12
12
|
require_relative "nist/hit"
|
|
13
13
|
require_relative "nist/hit_collection"
|
|
14
14
|
require_relative "nist/scraper"
|
|
15
|
+
require_relative "nist/cswp"
|
|
15
16
|
require_relative "nist/bibliography"
|
|
16
17
|
# require_relative "nist/tech_pubs_parser"
|
|
17
18
|
|
data/lib/relaton/sdo/store.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "yeptris/psych"
|
|
4
|
+
|
|
3
5
|
module Relaton
|
|
4
6
|
module Sdo
|
|
5
7
|
# Singleton registry of organizations, built lazily from the index manifest
|
|
@@ -35,7 +37,7 @@ module Relaton
|
|
|
35
37
|
|
|
36
38
|
def build_index
|
|
37
39
|
yaml = Fetcher.fetch_index
|
|
38
|
-
data = yaml.nil? || yaml.empty? ? {} :
|
|
40
|
+
data = yaml.nil? || yaml.empty? ? {} : Yeptris::Psych.safe_load(yaml)
|
|
39
41
|
# A corrupt/unexpected index (non-mapping YAML) degrades to "no orgs"
|
|
40
42
|
# rather than crashing every lookup with a TypeError.
|
|
41
43
|
organizations = data.is_a?(Hash) ? (data["organizations"] || {}) : {}
|
data/lib/relaton/version.rb
CHANGED
data/lib/relaton/xsf/hit.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "yeptris/psych"
|
|
1
2
|
module Relaton
|
|
2
3
|
module Xsf
|
|
3
4
|
class Hit < Relaton::Core::Hit
|
|
@@ -6,7 +7,7 @@ module Relaton
|
|
|
6
7
|
|
|
7
8
|
agent = Mechanize.new
|
|
8
9
|
resp = agent.get hit[:url]
|
|
9
|
-
hash =
|
|
10
|
+
hash = Yeptris::Psych.safe_load resp.body
|
|
10
11
|
hash["fetched"] = Date.today.to_s
|
|
11
12
|
@doc = Relaton::Xsf::Item.from_yaml hash.to_yaml
|
|
12
13
|
rescue StandardError => e
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: relaton
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: addressable
|
|
@@ -42,30 +42,30 @@ dependencies:
|
|
|
42
42
|
name: base64
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- - "
|
|
45
|
+
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
47
|
+
version: '0.3'
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- - "
|
|
52
|
+
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
54
|
+
version: '0.3'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: bibtex-ruby
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - "
|
|
59
|
+
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
61
|
+
version: '6.2'
|
|
62
62
|
type: :runtime
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- - "
|
|
66
|
+
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
68
|
+
version: '6.2'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: cnccs
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -98,16 +98,16 @@ dependencies:
|
|
|
98
98
|
name: sqlite3
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
|
-
- - "
|
|
101
|
+
- - "~>"
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '
|
|
103
|
+
version: '2.10'
|
|
104
104
|
type: :runtime
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
|
-
- - "
|
|
108
|
+
- - "~>"
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '
|
|
110
|
+
version: '2.10'
|
|
111
111
|
- !ruby/object:Gem::Dependency
|
|
112
112
|
name: csv
|
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -224,16 +224,16 @@ dependencies:
|
|
|
224
224
|
name: iso639
|
|
225
225
|
requirement: !ruby/object:Gem::Requirement
|
|
226
226
|
requirements:
|
|
227
|
-
- - "
|
|
227
|
+
- - "~>"
|
|
228
228
|
- !ruby/object:Gem::Version
|
|
229
|
-
version: '
|
|
229
|
+
version: '1.4'
|
|
230
230
|
type: :runtime
|
|
231
231
|
prerelease: false
|
|
232
232
|
version_requirements: !ruby/object:Gem::Requirement
|
|
233
233
|
requirements:
|
|
234
|
-
- - "
|
|
234
|
+
- - "~>"
|
|
235
235
|
- !ruby/object:Gem::Version
|
|
236
|
-
version: '
|
|
236
|
+
version: '1.4'
|
|
237
237
|
- !ruby/object:Gem::Dependency
|
|
238
238
|
name: isoics
|
|
239
239
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -282,20 +282,14 @@ dependencies:
|
|
|
282
282
|
requirements:
|
|
283
283
|
- - "~>"
|
|
284
284
|
- !ruby/object:Gem::Version
|
|
285
|
-
version:
|
|
286
|
-
- - ">="
|
|
287
|
-
- !ruby/object:Gem::Version
|
|
288
|
-
version: 0.2.5
|
|
285
|
+
version: 0.2.8
|
|
289
286
|
type: :runtime
|
|
290
287
|
prerelease: false
|
|
291
288
|
version_requirements: !ruby/object:Gem::Requirement
|
|
292
289
|
requirements:
|
|
293
290
|
- - "~>"
|
|
294
291
|
- !ruby/object:Gem::Version
|
|
295
|
-
version:
|
|
296
|
-
- - ">="
|
|
297
|
-
- !ruby/object:Gem::Version
|
|
298
|
-
version: 0.2.5
|
|
292
|
+
version: 0.2.8
|
|
299
293
|
- !ruby/object:Gem::Dependency
|
|
300
294
|
name: lutaml-model
|
|
301
295
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -370,30 +364,30 @@ dependencies:
|
|
|
370
364
|
name: moxml
|
|
371
365
|
requirement: !ruby/object:Gem::Requirement
|
|
372
366
|
requirements:
|
|
373
|
-
- - "
|
|
367
|
+
- - "~>"
|
|
374
368
|
- !ruby/object:Gem::Version
|
|
375
|
-
version:
|
|
369
|
+
version: 0.6.0
|
|
376
370
|
type: :runtime
|
|
377
371
|
prerelease: false
|
|
378
372
|
version_requirements: !ruby/object:Gem::Requirement
|
|
379
373
|
requirements:
|
|
380
|
-
- - "
|
|
374
|
+
- - "~>"
|
|
381
375
|
- !ruby/object:Gem::Version
|
|
382
|
-
version:
|
|
376
|
+
version: 0.6.0
|
|
383
377
|
- !ruby/object:Gem::Dependency
|
|
384
378
|
name: leptris
|
|
385
379
|
requirement: !ruby/object:Gem::Requirement
|
|
386
380
|
requirements:
|
|
387
|
-
- - "
|
|
381
|
+
- - "~>"
|
|
388
382
|
- !ruby/object:Gem::Version
|
|
389
|
-
version: 1.9.
|
|
383
|
+
version: 1.9.391
|
|
390
384
|
type: :runtime
|
|
391
385
|
prerelease: false
|
|
392
386
|
version_requirements: !ruby/object:Gem::Requirement
|
|
393
387
|
requirements:
|
|
394
|
-
- - "
|
|
388
|
+
- - "~>"
|
|
395
389
|
- !ruby/object:Gem::Version
|
|
396
|
-
version: 1.9.
|
|
390
|
+
version: 1.9.391
|
|
397
391
|
- !ruby/object:Gem::Dependency
|
|
398
392
|
name: parallel
|
|
399
393
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -410,92 +404,60 @@ dependencies:
|
|
|
410
404
|
version: '1.26'
|
|
411
405
|
- !ruby/object:Gem::Dependency
|
|
412
406
|
name: parsanol
|
|
413
|
-
requirement: !ruby/object:Gem::Requirement
|
|
414
|
-
requirements:
|
|
415
|
-
- - ">="
|
|
416
|
-
- !ruby/object:Gem::Version
|
|
417
|
-
version: 1.3.114
|
|
418
|
-
- - "<"
|
|
419
|
-
- !ruby/object:Gem::Version
|
|
420
|
-
version: '1.4'
|
|
421
|
-
type: :runtime
|
|
422
|
-
prerelease: false
|
|
423
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
424
|
-
requirements:
|
|
425
|
-
- - ">="
|
|
426
|
-
- !ruby/object:Gem::Version
|
|
427
|
-
version: 1.3.114
|
|
428
|
-
- - "<"
|
|
429
|
-
- !ruby/object:Gem::Version
|
|
430
|
-
version: '1.4'
|
|
431
|
-
- !ruby/object:Gem::Dependency
|
|
432
|
-
name: parslet
|
|
433
407
|
requirement: !ruby/object:Gem::Requirement
|
|
434
408
|
requirements:
|
|
435
409
|
- - "~>"
|
|
436
410
|
- !ruby/object:Gem::Version
|
|
437
|
-
version:
|
|
411
|
+
version: 1.3.114
|
|
438
412
|
type: :runtime
|
|
439
413
|
prerelease: false
|
|
440
414
|
version_requirements: !ruby/object:Gem::Requirement
|
|
441
415
|
requirements:
|
|
442
416
|
- - "~>"
|
|
443
417
|
- !ruby/object:Gem::Version
|
|
444
|
-
version:
|
|
418
|
+
version: 1.3.114
|
|
445
419
|
- !ruby/object:Gem::Dependency
|
|
446
420
|
name: pubid
|
|
447
421
|
requirement: !ruby/object:Gem::Requirement
|
|
448
422
|
requirements:
|
|
449
|
-
- - "
|
|
423
|
+
- - "~>"
|
|
450
424
|
- !ruby/object:Gem::Version
|
|
451
425
|
version: 2.0.1
|
|
452
|
-
- - "<"
|
|
453
|
-
- !ruby/object:Gem::Version
|
|
454
|
-
version: '2.1'
|
|
455
426
|
type: :runtime
|
|
456
427
|
prerelease: false
|
|
457
428
|
version_requirements: !ruby/object:Gem::Requirement
|
|
458
429
|
requirements:
|
|
459
|
-
- - "
|
|
430
|
+
- - "~>"
|
|
460
431
|
- !ruby/object:Gem::Version
|
|
461
432
|
version: 2.0.1
|
|
462
|
-
- - "<"
|
|
463
|
-
- !ruby/object:Gem::Version
|
|
464
|
-
version: '2.1'
|
|
465
433
|
- !ruby/object:Gem::Dependency
|
|
466
434
|
name: yeptris
|
|
467
435
|
requirement: !ruby/object:Gem::Requirement
|
|
468
436
|
requirements:
|
|
469
|
-
- - "
|
|
437
|
+
- - "~>"
|
|
470
438
|
- !ruby/object:Gem::Version
|
|
471
439
|
version: 0.6.51
|
|
472
440
|
type: :runtime
|
|
473
441
|
prerelease: false
|
|
474
442
|
version_requirements: !ruby/object:Gem::Requirement
|
|
475
443
|
requirements:
|
|
476
|
-
- - "
|
|
444
|
+
- - "~>"
|
|
477
445
|
- !ruby/object:Gem::Version
|
|
478
446
|
version: 0.6.51
|
|
479
447
|
- !ruby/object:Gem::Dependency
|
|
480
448
|
name: relaton-render
|
|
481
449
|
requirement: !ruby/object:Gem::Requirement
|
|
482
450
|
requirements:
|
|
483
|
-
- - "
|
|
484
|
-
- !ruby/object:Gem::Version
|
|
485
|
-
version: 3.0.0.pre.alpha.1
|
|
486
|
-
- - "<"
|
|
451
|
+
- - "~>"
|
|
487
452
|
- !ruby/object:Gem::Version
|
|
488
|
-
version: '
|
|
453
|
+
version: '3.0'
|
|
489
454
|
type: :runtime
|
|
490
455
|
prerelease: false
|
|
491
456
|
version_requirements: !ruby/object:Gem::Requirement
|
|
492
457
|
requirements:
|
|
493
|
-
- - "
|
|
494
|
-
- !ruby/object:Gem::Version
|
|
495
|
-
version: 3.0.0.pre.alpha.1
|
|
496
|
-
- - "<"
|
|
458
|
+
- - "~>"
|
|
497
459
|
- !ruby/object:Gem::Version
|
|
498
|
-
version: '
|
|
460
|
+
version: '3.0'
|
|
499
461
|
- !ruby/object:Gem::Dependency
|
|
500
462
|
name: rfcxml
|
|
501
463
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -1159,6 +1121,8 @@ files:
|
|
|
1159
1121
|
- lib/relaton/nist/bibitem.rb
|
|
1160
1122
|
- lib/relaton/nist/bibliography.rb
|
|
1161
1123
|
- lib/relaton/nist/comment_period.rb
|
|
1124
|
+
- lib/relaton/nist/cswp.rb
|
|
1125
|
+
- lib/relaton/nist/cswp.yaml
|
|
1162
1126
|
- lib/relaton/nist/data_fetcher.rb
|
|
1163
1127
|
- lib/relaton/nist/date.rb
|
|
1164
1128
|
- lib/relaton/nist/docidentifier.rb
|