relaton 3.0.0.pre.alpha.4 → 3.0.0.pre.alpha.6
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/bibliography.rb +19 -6
- data/lib/relaton/3gpp/processor.rb +6 -0
- data/lib/relaton/adobe/processor.rb +9 -0
- data/lib/relaton/bib/converter/csl.rb +110 -0
- data/lib/relaton/bib/converter/ris.rb +104 -0
- data/lib/relaton/bib/converter/titles.rb +24 -0
- data/lib/relaton/bib/item_data.rb +23 -0
- data/lib/relaton/bib/model/item.rb +6 -6
- data/lib/relaton/bib/sanitizer.rb +71 -30
- data/lib/relaton/bib.rb +10 -0
- data/lib/relaton/bipm/bibliography.rb +24 -21
- data/lib/relaton/bipm/processor.rb +1 -0
- data/lib/relaton/bipm/rawdata_bipm_metrologia/affiliations.rb +6 -6
- data/lib/relaton/bipm/si_brochure_parser.rb +2 -2
- data/lib/relaton/bsi/bibliography.rb +26 -23
- data/lib/relaton/bsi/processor.rb +5 -0
- data/lib/relaton/calconnect/bibliography.rb +6 -6
- data/lib/relaton/calconnect/hit_collection.rb +4 -1
- data/lib/relaton/ccsds/bibliography.rb +16 -10
- data/lib/relaton/ccsds/hit_collection.rb +1 -1
- data/lib/relaton/ccsds/processor.rb +13 -0
- data/lib/relaton/cen/bibliography.rb +27 -24
- data/lib/relaton/cen/hit_collection.rb +1 -1
- data/lib/relaton/cen/processor.rb +5 -0
- data/lib/relaton/cen/scraper.rb +9 -9
- data/lib/relaton/cie/bibliography.rb +11 -9
- data/lib/relaton/cie/data_fetcher.rb +18 -18
- data/lib/relaton/cie/processor.rb +1 -0
- data/lib/relaton/cie/scrapper.rb +15 -8
- data/lib/relaton/cie.rb +1 -1
- data/lib/relaton/cloud.rb +127 -0
- data/lib/relaton/core/hit_collection.rb +6 -10
- data/lib/relaton/core/processor.rb +121 -0
- data/lib/relaton/core/request_error.rb +21 -3
- data/lib/relaton/db/cache.rb +474 -148
- data/lib/relaton/db/cache_entry.rb +42 -0
- data/lib/relaton/db/registry.rb +152 -0
- data/lib/relaton/db.rb +275 -102
- data/lib/relaton/doi/crossref.rb +23 -6
- data/lib/relaton/doi/processor.rb +1 -0
- data/lib/relaton/easc/processor.rb +1 -0
- data/lib/relaton/ecma/bibliography.rb +18 -13
- data/lib/relaton/ecma/data_fetcher.rb +1 -1
- data/lib/relaton/ecma/data_parser.rb +1 -1
- data/lib/relaton/ecma/edition_parser.rb +2 -2
- data/lib/relaton/ecma/memento_parser.rb +4 -4
- data/lib/relaton/ecma/standard_parser.rb +6 -6
- data/lib/relaton/etsi/bibliography.rb +8 -7
- data/lib/relaton/etsi/processor.rb +1 -0
- data/lib/relaton/gb/bibliography.rb +20 -12
- data/lib/relaton/gb/gb_scraper.rb +5 -5
- data/lib/relaton/gb/scraper.rb +16 -16
- data/lib/relaton/gb/sec_scraper.rb +8 -8
- data/lib/relaton/gb/t_scraper.rb +5 -5
- data/lib/relaton/gost/processor.rb +1 -0
- data/lib/relaton/iala/bibliography.rb +15 -12
- data/lib/relaton/iala/processor.rb +1 -0
- data/lib/relaton/iana/bibliography.rb +12 -12
- data/lib/relaton/iana/data_fetcher.rb +3 -3
- data/lib/relaton/iana/parser.rb +12 -5
- data/lib/relaton/iana/processor.rb +9 -0
- data/lib/relaton/iec/bibliography.rb +18 -7
- data/lib/relaton/iec/data_parser.rb +24 -9
- data/lib/relaton/iec/processor.rb +11 -0
- data/lib/relaton/iec.rb +1 -1
- data/lib/relaton/ieee/bibliography.rb +19 -13
- data/lib/relaton/ieee/data_fetcher.rb +1 -1
- data/lib/relaton/ieee/processor.rb +24 -0
- data/lib/relaton/ieee/rawbib_id_parser.rb +2 -2
- data/lib/relaton/ietf/bibliography.rb +11 -9
- data/lib/relaton/ietf/data_fetcher.rb +1 -1
- data/lib/relaton/ietf/processor.rb +1 -0
- data/lib/relaton/ietf/rfc/entry.rb +15 -19
- data/lib/relaton/ietf/scraper.rb +5 -4
- data/lib/relaton/iho/processor.rb +1 -0
- data/lib/relaton/index/file_io.rb +2 -2
- data/lib/relaton/index/pool.rb +4 -3
- data/lib/relaton/index/shard_source.rb +1 -1
- data/lib/relaton/index/type.rb +2 -5
- data/lib/relaton/isbn/open_library.rb +11 -7
- data/lib/relaton/isbn/processor.rb +10 -0
- data/lib/relaton/iso/bibliography.rb +11 -9
- data/lib/relaton/iso/data_parser.rb +2 -2
- data/lib/relaton/iso/processor.rb +5 -0
- data/lib/relaton/iso/scraper.rb +20 -20
- data/lib/relaton/itu/bibliography.rb +107 -16
- data/lib/relaton/itu/data_crawler_r.rb +2 -2
- data/lib/relaton/itu/hit_collection.rb +64 -52
- data/lib/relaton/itu/processor.rb +1 -0
- data/lib/relaton/itu/scraper.rb +13 -3
- data/lib/relaton/itu.rb +0 -2
- data/lib/relaton/jis/bibliography.rb +22 -11
- data/lib/relaton/jis/data_fetcher.rb +4 -4
- data/lib/relaton/jis/processor.rb +1 -0
- data/lib/relaton/jis/scraper.rb +8 -8
- data/lib/relaton/nist/bibliography.rb +25 -21
- data/lib/relaton/oasis/bibliography.rb +16 -13
- data/lib/relaton/oasis/browser_agent.rb +2 -2
- data/lib/relaton/oasis/data_parser.rb +5 -5
- data/lib/relaton/oasis/data_parser_utils.rb +2 -2
- data/lib/relaton/oasis/data_part_parser.rb +7 -7
- data/lib/relaton/ogc/bibliography.rb +15 -14
- data/lib/relaton/ogc/hit_collection.rb +9 -7
- data/lib/relaton/ogc/processor.rb +13 -0
- data/lib/relaton/oiml/processor.rb +1 -0
- data/lib/relaton/omg/bibliography.rb +9 -9
- data/lib/relaton/omg/scraper.rb +14 -13
- data/lib/relaton/omg.rb +1 -1
- data/lib/relaton/plateau/bibliography.rb +9 -7
- data/lib/relaton/plateau/hit_collection.rb +2 -1
- data/lib/relaton/plateau/processor.rb +1 -0
- data/lib/relaton/un/bibliography.rb +21 -10
- data/lib/relaton/un/processor.rb +1 -0
- data/lib/relaton/version.rb +1 -1
- data/lib/relaton/w3c/processor.rb +1 -0
- data/lib/relaton/xsf/bibliography.rb +11 -6
- data/lib/relaton.rb +13 -0
- metadata +60 -14
- data/lib/relaton/itu/pubid.rb +0 -199
|
@@ -24,21 +24,21 @@ module Relaton
|
|
|
24
24
|
RECEDITIONS_URL = "#{DOMAIN}/mws/api/recommendations/getRecEditions?idrec=%<idrec>s&lang=en".freeze
|
|
25
25
|
HANDLE_URL = "http://handle.itu.int/11.1002/1000/%<idrec>s-en".freeze
|
|
26
26
|
|
|
27
|
+
# `ref` is a Pubid::Itu identifier; its class and sector pick the route.
|
|
27
28
|
def search
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
when /^ITU-R\s/, /^Report\sITU-R\s/ then request_document
|
|
29
|
+
if publication?(ref) then request_publication
|
|
30
|
+
# an annex to a bulletin has no landing page, and it is not a
|
|
31
|
+
# recommendation either
|
|
32
|
+
elsif publication?(ref.root) then @array = []
|
|
33
|
+
else
|
|
34
|
+
case sector
|
|
35
|
+
when "T" then request_recommendation
|
|
36
|
+
when "R" then request_document
|
|
37
|
+
end
|
|
38
38
|
end
|
|
39
39
|
rescue Mechanize::ResponseCodeError, SocketError, Timeout::Error, Errno::ECONNRESET,
|
|
40
40
|
EOFError, Net::ProtocolError, OpenSSL::SSL::SSLError => e
|
|
41
|
-
raise Relaton::RequestError, "Could not access #{ref
|
|
41
|
+
raise Relaton::RequestError, "Could not access #{ref}: #{e.message}"
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def agent
|
|
@@ -70,6 +70,21 @@ module Relaton
|
|
|
70
70
|
|
|
71
71
|
private
|
|
72
72
|
|
|
73
|
+
# Radio Regulations and Operational Bulletins live on www.itu.int/pub,
|
|
74
|
+
# not in the dataset.
|
|
75
|
+
def publication?(id)
|
|
76
|
+
id.is_a?(::Pubid::Itu::Identifiers::RadioRegulations) ||
|
|
77
|
+
id.is_a?(::Pubid::Itu::Identifiers::SpecialPublication)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# An annex or an appendix carries its sector on the recommendation it
|
|
81
|
+
# belongs to.
|
|
82
|
+
#
|
|
83
|
+
# @return [String, nil] "T" or "R"
|
|
84
|
+
def sector
|
|
85
|
+
(ref.sector || ref.root.sector)&.to_s
|
|
86
|
+
end
|
|
87
|
+
|
|
73
88
|
# ITU-T Recommendations and supplements. The index is preferred (offline,
|
|
74
89
|
# every edition, no scraping); the live path covers what the dataset does
|
|
75
90
|
# not have — Implementers' Guides, the `(V##)`/`Annex` forms the data
|
|
@@ -86,9 +101,8 @@ module Relaton
|
|
|
86
101
|
|
|
87
102
|
# @return [Array<Relaton::Itu::Hit>] newest edition first
|
|
88
103
|
def index_hits
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
# Every edition is a candidate; Bibliography selects the year.
|
|
105
|
+
pubid = ref.exclude(:year, :month, :day)
|
|
92
106
|
Util.info "Fetching from Relaton repository ...", key: ref.to_s
|
|
93
107
|
index.search(pubid) { |i| index_match?(pubid, i[:id]) }
|
|
94
108
|
.map { |r| index_hit r }.sort_by { |h| edition_key h.hit[:code] }.reverse
|
|
@@ -112,7 +126,10 @@ module Relaton
|
|
|
112
126
|
def year_satisfied?(hits)
|
|
113
127
|
return false if hits.empty?
|
|
114
128
|
|
|
115
|
-
|
|
129
|
+
year = Bibliography.edition_year(ref)
|
|
130
|
+
return true if year.nil?
|
|
131
|
+
|
|
132
|
+
hits.any? { |h| edition_key(h.hit[:code]).first == year.to_i }
|
|
116
133
|
end
|
|
117
134
|
|
|
118
135
|
# The edition date of a rendered docidentifier, as `[year, month]`. Uses
|
|
@@ -135,14 +152,13 @@ module Relaton
|
|
|
135
152
|
|
|
136
153
|
def request_document # rubocop:todo Metrics/MethodLength, Metrics/AbcSize
|
|
137
154
|
Util.info "Fetching from Relaton repository ...", key: ref.to_s
|
|
138
|
-
# Pass the reference
|
|
155
|
+
# Pass the reference as a pubid (not a String) so Relaton::Index can
|
|
139
156
|
# narrow candidates by document number before the block; `index_match?`
|
|
140
157
|
# then matches every edition when the reference omits the part and the
|
|
141
158
|
# exact edition when it names one. Rows are Pubid::Itu objects (not
|
|
142
159
|
# Comparable), so rank by the numeric edition in `code.parts` (`["3"]` → 3)
|
|
143
160
|
# to return the latest — index order isn't by edition.
|
|
144
|
-
|
|
145
|
-
row = index.search(pubid) { |i| index_match?(pubid, i[:id]) }
|
|
161
|
+
row = index.search(ref) { |i| index_match?(ref, i[:id]) }
|
|
146
162
|
.max_by { |i| i[:id].code&.parts&.last.to_i }
|
|
147
163
|
return unless row
|
|
148
164
|
|
|
@@ -155,30 +171,6 @@ module Relaton
|
|
|
155
171
|
@array = [hit]
|
|
156
172
|
end
|
|
157
173
|
|
|
158
|
-
# Parse the reference into a Pubid::Itu identifier for the index lookup. A
|
|
159
|
-
# ref pubid can't parse raises (a `Pubid`/`Parslet` error) and propagates to
|
|
160
|
-
# the caller (relaton-cli / API callers rescue it), mirroring the ETSI
|
|
161
|
-
# flavor — the consumer no longer degrades to a raw-string substring search.
|
|
162
|
-
#
|
|
163
|
-
# @return [::Pubid::Itu::Identifier]
|
|
164
|
-
def pubid_ref
|
|
165
|
-
::Pubid::Itu.parse ref.to_ref
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# The reference as a pubid for an ITU-T index lookup, stripped of its
|
|
169
|
-
# edition date (every edition is a candidate; Bibliography selects the
|
|
170
|
-
# year). Unlike `#pubid_ref` this **rescues** an unparseable reference: the
|
|
171
|
-
# ITU-T path has a live fallback to degrade to, and a malformed reference
|
|
172
|
-
# (e.g. `ITU-T G.Suppl.47`) should warn and report "Not found", not raise.
|
|
173
|
-
#
|
|
174
|
-
# @return [::Pubid::Itu::Identifier, nil]
|
|
175
|
-
def index_pubid_ref
|
|
176
|
-
undated = ref.dup.tap { |r| r.year = r.month = r.day = nil }
|
|
177
|
-
::Pubid::Itu.parse undated.to_ref
|
|
178
|
-
rescue StandardError
|
|
179
|
-
nil
|
|
180
|
-
end
|
|
181
|
-
|
|
182
174
|
# Does an index row's id match the reference? When the reference omits the
|
|
183
175
|
# part/edition (a bare `ITU-R P.838`), every edition of that exact document
|
|
184
176
|
# matches — "search all parts"; when it names a part (`ITU-R P.838-2`), only
|
|
@@ -227,9 +219,15 @@ module Relaton
|
|
|
227
219
|
nil
|
|
228
220
|
end
|
|
229
221
|
|
|
230
|
-
#
|
|
222
|
+
# The `rec=` value for the rec.aspx lookup: the document without its
|
|
223
|
+
# sector, edition date or version — "H.264", "A Suppl. 2",
|
|
224
|
+
# "G.989 Suppl. 1". An amendment or an annex is looked up by the
|
|
225
|
+
# recommendation it belongs to.
|
|
226
|
+
#
|
|
227
|
+
# @return [String]
|
|
231
228
|
def rec_query
|
|
232
|
-
|
|
229
|
+
Bibliography.document(ref).exclude(:year, :month, :day, :version)
|
|
230
|
+
.to_s.delete_prefix("ITU-T ")
|
|
233
231
|
end
|
|
234
232
|
|
|
235
233
|
# @param idrec [String]
|
|
@@ -244,10 +242,14 @@ module Relaton
|
|
|
244
242
|
# @param edition [Hash] a getRecEditions entry
|
|
245
243
|
# @return [Relaton::Itu::Hit]
|
|
246
244
|
def recommendation_hit(edition)
|
|
245
|
+
# The crawler's rec_name canonicalisation ("G 231" -> "G.231"): the
|
|
246
|
+
# code is parsed with Pubid::Itu, which rejects the space spelling.
|
|
247
|
+
require_relative "data_parser_t"
|
|
248
|
+
name = DataParserT.normalize_rec_name edition["rec_name"]
|
|
247
249
|
# No `:ref` key: it drives Hit#gi_imp, which would route an Implementers'
|
|
248
250
|
# Guide reference onto the getImplGuides endpoints.
|
|
249
251
|
Hit.new({
|
|
250
|
-
code: "ITU-T #{
|
|
252
|
+
code: "ITU-T #{name}",
|
|
251
253
|
title: edition["title"],
|
|
252
254
|
url: format(HANDLE_URL, idrec: edition["idrec"]),
|
|
253
255
|
type: "recommendation",
|
|
@@ -279,21 +281,31 @@ module Relaton
|
|
|
279
281
|
end
|
|
280
282
|
|
|
281
283
|
# The /pub identifier for a Radio Regulation or an Operational Bulletin
|
|
282
|
-
# (`OB.1096` → `T-SP-OB.1096-2016`). nil — no hit —
|
|
283
|
-
#
|
|
284
|
+
# (`OB.1096` → `T-SP-OB.1096-2016`). nil — no hit — for any other special
|
|
285
|
+
# publication, which has no derivable landing page.
|
|
284
286
|
#
|
|
285
287
|
# @return [String, nil]
|
|
286
288
|
def publication_id
|
|
287
|
-
return "R-REG-RR-#{ref.year}" if
|
|
289
|
+
return "R-REG-RR-#{ref.year}" if radio_regulations?
|
|
288
290
|
|
|
289
|
-
|
|
290
|
-
"T-SP-OB.#{num}-#{ref.year}" if num
|
|
291
|
+
"T-SP-OB.#{ref.number}-#{ref.year}" if bulletin?
|
|
291
292
|
end
|
|
292
293
|
|
|
293
|
-
#
|
|
294
|
+
# The docidentifier (year only, no month or day). A bulletin keeps its
|
|
295
|
+
# sector even when the reference is the sector-less `ITU OB No. 1096`.
|
|
296
|
+
#
|
|
297
|
+
# @return [String]
|
|
294
298
|
def publication_code
|
|
295
|
-
"
|
|
299
|
+
return "ITU-R RR (#{ref.year})" if radio_regulations?
|
|
300
|
+
|
|
301
|
+
"ITU-T OB.#{ref.number} (#{ref.year})"
|
|
296
302
|
end
|
|
303
|
+
|
|
304
|
+
def radio_regulations?
|
|
305
|
+
ref.is_a? ::Pubid::Itu::Identifiers::RadioRegulations
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def bulletin? = ref.series.to_s == "OB"
|
|
297
309
|
end
|
|
298
310
|
end
|
|
299
311
|
end
|
data/lib/relaton/itu/scraper.rb
CHANGED
|
@@ -112,13 +112,23 @@ module Relaton
|
|
|
112
112
|
type = "ISO"
|
|
113
113
|
text.match(/[^(]+/).to_s.strip.squeeze(" ")
|
|
114
114
|
else
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
pubid.to_s
|
|
115
|
+
type = "ITU"
|
|
116
|
+
canonical_id text
|
|
118
117
|
end => id
|
|
119
118
|
Docidentifier.new(type: type, content: id, primary: true)
|
|
120
119
|
end
|
|
121
120
|
|
|
121
|
+
# The pubid spelling of an ITU code. The code is data from ITU, so a
|
|
122
|
+
# form pubid cannot identify is kept as ITU wrote it.
|
|
123
|
+
#
|
|
124
|
+
# @param text [String]
|
|
125
|
+
# @return [String]
|
|
126
|
+
def canonical_id(text)
|
|
127
|
+
::Pubid::Itu.parse(text).to_s
|
|
128
|
+
rescue ::Pubid::Errors::ParseError
|
|
129
|
+
text
|
|
130
|
+
end
|
|
131
|
+
|
|
122
132
|
# @return [Array<Relaton::Bib::Contributor>]
|
|
123
133
|
def fetch_contributors
|
|
124
134
|
contribs = fetch_publisher_contributors
|
data/lib/relaton/itu.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require "mechanize"
|
|
2
|
-
require "parslet"
|
|
3
2
|
require "pubid"
|
|
4
3
|
require "digest/md5"
|
|
5
4
|
require "relaton/index"
|
|
@@ -11,7 +10,6 @@ require_relative "itu/item_data"
|
|
|
11
10
|
require_relative "itu/model/item"
|
|
12
11
|
require_relative "itu/model/bibitem"
|
|
13
12
|
require_relative "itu/model/bibdata"
|
|
14
|
-
require_relative "itu/pubid"
|
|
15
13
|
require_relative "itu/scraper"
|
|
16
14
|
require_relative "itu/hit_collection"
|
|
17
15
|
require_relative "itu/bibliography"
|
|
@@ -13,7 +13,8 @@ module Relaton
|
|
|
13
13
|
# the reference's series and number (a supplement is filed under its base
|
|
14
14
|
# number), so an edition and its amendments are returned together.
|
|
15
15
|
#
|
|
16
|
-
# @param [String]
|
|
16
|
+
# @param [String, Pubid::Jis::Identifier] ref JIS document reference; a
|
|
17
|
+
# pubid is never mutated
|
|
17
18
|
# @param [String, nil] year JIS document year
|
|
18
19
|
#
|
|
19
20
|
# @return [Relaton::Jis::HitCollection] search result
|
|
@@ -21,16 +22,21 @@ module Relaton
|
|
|
21
22
|
# @raise [Pubid::Errors::ParseError] when the reference is not a JIS
|
|
22
23
|
# identifier, so a malformed reference is not reported as "not found"
|
|
23
24
|
#
|
|
24
|
-
def search(
|
|
25
|
-
pubid = ::Pubid::Jis::Identifier.parse
|
|
26
|
-
|
|
25
|
+
def search(ref, year = nil)
|
|
26
|
+
pubid = ref.is_a?(String) ? ::Pubid::Jis::Identifier.parse(ref) : ref
|
|
27
|
+
if year && pubid.year.nil?
|
|
28
|
+
# Copy first: a caller's pubid is also its Db cache key.
|
|
29
|
+
pubid = pubid.class.from_hash(pubid.to_hash)
|
|
30
|
+
pubid.year = year.to_i
|
|
31
|
+
end
|
|
27
32
|
HitCollection.new pubid
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
#
|
|
31
36
|
# Get JIS document by reference
|
|
32
37
|
#
|
|
33
|
-
# @param [String] ref JIS document reference
|
|
38
|
+
# @param [String, Pubid::Jis::Identifier] ref JIS document reference, or
|
|
39
|
+
# the parse that Relaton::Db routed with (relaton#205)
|
|
34
40
|
# @param [String, nil] year JIS document year
|
|
35
41
|
# @param [Hash] opts options
|
|
36
42
|
# @option opts [Boolean] :all_parts return all parts of document
|
|
@@ -41,16 +47,21 @@ module Relaton
|
|
|
41
47
|
# identifier
|
|
42
48
|
#
|
|
43
49
|
def get(ref, year = nil, opts = {}) # rubocop:disable Metrics/AbcSize
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
if ref.is_a?(String)
|
|
51
|
+
query = ref.sub(/\s\((all parts|規格群)\)/, "")
|
|
52
|
+
opts[:all_parts] ||= !$1.nil?
|
|
53
|
+
else
|
|
54
|
+
opts[:all_parts] ||= ref.all_parts?
|
|
55
|
+
query = ref.all_parts? ? ref.identifiers.first : ref
|
|
56
|
+
end
|
|
57
|
+
Util.info "Fetching from webdesk.jsa.or.jp ...", key: ref.to_s
|
|
58
|
+
hits = search(query, year)
|
|
48
59
|
result = opts[:all_parts] ? hits.find_all_parts : hits.find
|
|
49
60
|
if result.is_a? Bib::ItemData
|
|
50
|
-
Util.info "Found: `#{result.docidentifier[0].content}`", key: ref
|
|
61
|
+
Util.info "Found: `#{result.docidentifier[0].content}`", key: ref.to_s
|
|
51
62
|
return result
|
|
52
63
|
end
|
|
53
|
-
hint result, ref, year
|
|
64
|
+
hint result, ref.to_s, year
|
|
54
65
|
end
|
|
55
66
|
|
|
56
67
|
#
|
|
@@ -114,12 +114,12 @@ module Relaton
|
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
def parse_offset(resp) # rubocop:disable Metrics/AbcSize
|
|
117
|
-
if resp.
|
|
117
|
+
if resp.at_xpath('//*[@id="btnPaging"]') # first page
|
|
118
118
|
xpath = '//script[contains(.,"var count =")]'
|
|
119
|
-
@count = resp.
|
|
120
|
-
resp.
|
|
119
|
+
@count = resp.at_xpath(xpath).text.match(/var count = (\d+);/)[1]
|
|
120
|
+
resp.at_xpath("//*[@id='offset']")[:value].to_i
|
|
121
121
|
else
|
|
122
|
-
script = resp.
|
|
122
|
+
script = resp.at_xpath("//script").text
|
|
123
123
|
script.match(/\("offset"\)\.value = '(\d+)'/)[1].to_i
|
|
124
124
|
end
|
|
125
125
|
end
|
data/lib/relaton/jis/scraper.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Relaton
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def fetch # rubocop:disable Metrics/MethodLength
|
|
25
|
-
@doc = @agent.get(@url).
|
|
25
|
+
@doc = @agent.get(@url).at_xpath "//div[@id='main']/section"
|
|
26
26
|
contributors = fetch_contributor
|
|
27
27
|
eg_contributor = fetch_editorialgroup_contributor
|
|
28
28
|
contributors << eg_contributor if eg_contributor
|
|
@@ -33,7 +33,7 @@ module Relaton
|
|
|
33
33
|
|
|
34
34
|
def fetch_title
|
|
35
35
|
result = { "ja" => "Jpan", "en" => "Latn" }.map.with_index do |(lang, script), i|
|
|
36
|
-
content = @doc.
|
|
36
|
+
content = @doc.at_xpath("./h2/text()[#{i + 2}]").text.strip
|
|
37
37
|
Bib::Title.new content: content, language: lang, script: script
|
|
38
38
|
end
|
|
39
39
|
@errors[:title] &&= result.empty?
|
|
@@ -84,12 +84,12 @@ module Relaton
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def document_id
|
|
87
|
-
@document_id ||= @doc.
|
|
87
|
+
@document_id ||= @doc.at_xpath("./h2/text()[1]")&.text&.strip
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def fetch_date
|
|
91
91
|
result = DATETYPES.each_with_object([]) do |(key, type), a|
|
|
92
|
-
node = @doc.
|
|
92
|
+
node = @doc.at_xpath("./div/div/div/p/text()[contains(.,'#{key}')]")
|
|
93
93
|
next unless node
|
|
94
94
|
|
|
95
95
|
at = node.text.match(/\d{4}-\d{2}-\d{2}/).to_s
|
|
@@ -116,7 +116,7 @@ module Relaton
|
|
|
116
116
|
def langs_scripts # rubocop:disable Metrics/MethodLength
|
|
117
117
|
@langs_scripts ||= begin
|
|
118
118
|
result = LANGS.each_with_object([]) do |(key, lang), a|
|
|
119
|
-
l = @doc.
|
|
119
|
+
l = @doc.at_xpath(
|
|
120
120
|
"./div/div/div[@class='blockContentFile']/div/div/p[1]" \
|
|
121
121
|
"/span[contains(.,'#{key}')]/following-sibling::span",
|
|
122
122
|
)
|
|
@@ -132,7 +132,7 @@ module Relaton
|
|
|
132
132
|
def fetch_status
|
|
133
133
|
xpath = "./div/div/div/p/text()[contains(.,'状態')]" \
|
|
134
134
|
"/following-sibling::span"
|
|
135
|
-
st = @doc.
|
|
135
|
+
st = @doc.at_xpath(xpath)
|
|
136
136
|
status_val = STATUSES[st&.text&.strip]
|
|
137
137
|
@errors[:status] &&= status_val.nil?
|
|
138
138
|
return unless status_val
|
|
@@ -155,7 +155,7 @@ module Relaton
|
|
|
155
155
|
end
|
|
156
156
|
|
|
157
157
|
def fetch_ics
|
|
158
|
-
td = @doc.
|
|
158
|
+
td = @doc.at_xpath("./table/tr[th[.='ICS']]/td")
|
|
159
159
|
@errors[:ics] &&= td.nil?
|
|
160
160
|
return [] unless td
|
|
161
161
|
|
|
@@ -194,7 +194,7 @@ module Relaton
|
|
|
194
194
|
end
|
|
195
195
|
|
|
196
196
|
def fetch_editorialgroup_contributor # rubocop:disable Metrics/MethodLength
|
|
197
|
-
node = @doc.
|
|
197
|
+
node = @doc.at_xpath("./table/tr[th[.='原案作成団体']]/td")
|
|
198
198
|
@errors[:editorialgroup] &&= node.nil?
|
|
199
199
|
return unless node
|
|
200
200
|
|
|
@@ -7,18 +7,18 @@ module Relaton
|
|
|
7
7
|
#
|
|
8
8
|
# Search NIST documents by reference
|
|
9
9
|
#
|
|
10
|
-
# @param
|
|
10
|
+
# @param ref [String] reference
|
|
11
11
|
#
|
|
12
12
|
# @return [Relaton::Nist::HitCollection] search result
|
|
13
13
|
#
|
|
14
|
-
def search(
|
|
15
|
-
|
|
14
|
+
def search(ref, year = nil, opts = {})
|
|
15
|
+
query = ref.sub(/^NISTIR/, "NIST IR").sub(/\/Add/, " Add")
|
|
16
16
|
# pubid 2.x only recognizes the addendum marker with a trailing
|
|
17
17
|
# period, and only splits an uppercase part letter — canonicalize
|
|
18
18
|
# both ("800-38a Add" -> "800-38A Add.") so @reference parses to the
|
|
19
19
|
# same pubid the index/CSRC carry.
|
|
20
|
-
|
|
21
|
-
HitCollection.search
|
|
20
|
+
query = query.sub(/\bAdd\b\.?/i, "Add.").sub(/([0-9])([a-z])(?=\s+Add\.)/) { "#{$1}#{$2.upcase}" }
|
|
21
|
+
HitCollection.search query, year, opts
|
|
22
22
|
rescue OpenURI::HTTPError, SocketError, OpenSSL::SSL::SSLError => e
|
|
23
23
|
raise Relaton::RequestError, e.message
|
|
24
24
|
end
|
|
@@ -26,34 +26,38 @@ module Relaton
|
|
|
26
26
|
#
|
|
27
27
|
# Get NIST document by reference
|
|
28
28
|
#
|
|
29
|
-
# @param
|
|
29
|
+
# @param ref [String, Pubid::Nist::Identifier] the NIST standard Code
|
|
30
|
+
# to look up, or its parse from Relaton::Db (relaton#205)
|
|
30
31
|
# @param year [String] the year the standard was published (optional)
|
|
31
32
|
# @param opts [Hash] options
|
|
32
33
|
# @option opts [Boolean] :all_parts restricted to all parts
|
|
33
34
|
#
|
|
34
35
|
# @return [Relaton::Nist::ItemData, nil] bibliographic item
|
|
35
36
|
#
|
|
36
|
-
def get(
|
|
37
|
-
|
|
37
|
+
def get(ref, year = nil, opts = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
38
|
+
# The lookup below works on text, so a pubid from Relaton::Db is
|
|
39
|
+
# read back as its printed form; the pubid itself is not changed.
|
|
40
|
+
ref = ref.to_s unless ref.is_a?(String)
|
|
41
|
+
return fetch_ref_err(ref, year, []) if ref.match?(/\sEP$/)
|
|
38
42
|
|
|
39
|
-
/^(?<code2>[^(]+)(?:\((?<date2>\w+\s(?:\d{2},\s)?\d{4})\))?\s?\(?(?:(?<=\()(?<stage>(?:I|F|\d)PD))?/ =~
|
|
40
|
-
stage ||= /(?<=\.)PD-\w+(?=\.)/.match(
|
|
43
|
+
/^(?<code2>[^(]+)(?:\((?<date2>\w+\s(?:\d{2},\s)?\d{4})\))?\s?\(?(?:(?<=\()(?<stage>(?:I|F|\d)PD))?/ =~ ref
|
|
44
|
+
stage ||= /(?<=\.)PD-\w+(?=\.)/.match(ref)&.to_s
|
|
41
45
|
if code2
|
|
42
|
-
|
|
46
|
+
ref = code2.strip
|
|
43
47
|
opts[:date] = parse_date(date2, str: false) if date2
|
|
44
48
|
opts[:stage] = stage if stage
|
|
45
49
|
end
|
|
46
50
|
|
|
47
51
|
if year.nil?
|
|
48
|
-
/^(?<code1>[^:]+):(?<year1>[^:]+)$/ =~
|
|
52
|
+
/^(?<code1>[^:]+):(?<year1>[^:]+)$/ =~ ref
|
|
49
53
|
unless code1.nil?
|
|
50
|
-
|
|
54
|
+
ref = code1
|
|
51
55
|
year = year1
|
|
52
56
|
end
|
|
53
57
|
end
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
nistbib_get(
|
|
59
|
+
ref += "-1" if opts[:all_parts]
|
|
60
|
+
nistbib_get(ref, year, opts)
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
private
|
|
@@ -61,14 +65,14 @@ module Relaton
|
|
|
61
65
|
#
|
|
62
66
|
# Get NIST document by reference
|
|
63
67
|
#
|
|
64
|
-
# @param [String]
|
|
68
|
+
# @param [String] ref reference
|
|
65
69
|
# @param [String] year year
|
|
66
70
|
# @param [Hash] opts options
|
|
67
71
|
#
|
|
68
72
|
# @return [Relaton::Nist::ItemData, nil] bibliographic item
|
|
69
73
|
#
|
|
70
|
-
def nistbib_get(
|
|
71
|
-
result = nistbib_search_filter(
|
|
74
|
+
def nistbib_get(ref, year, opts)
|
|
75
|
+
result = nistbib_search_filter(ref, year, opts) || (return nil)
|
|
72
76
|
ret = nistbib_results_filter(result, year, opts)
|
|
73
77
|
if ret[:ret]
|
|
74
78
|
Util.info "Found: `#{ret[:ret].docidentifier.first.content}`", key: result.reference
|
|
@@ -153,14 +157,14 @@ module Relaton
|
|
|
153
157
|
#
|
|
154
158
|
# Get search results and filter them by code and year
|
|
155
159
|
#
|
|
156
|
-
# @param
|
|
160
|
+
# @param ref [String] reference
|
|
157
161
|
# @param year [String, nil] year
|
|
158
162
|
# @param opts [Hash] options
|
|
159
163
|
#
|
|
160
164
|
# @return [Relaton::Nist::HitCollection] hits collection
|
|
161
165
|
#
|
|
162
|
-
def nistbib_search_filter(
|
|
163
|
-
result = search(
|
|
166
|
+
def nistbib_search_filter(ref, year, opts)
|
|
167
|
+
result = search(ref, year, opts)
|
|
164
168
|
result.search_filter
|
|
165
169
|
end
|
|
166
170
|
|
|
@@ -16,16 +16,16 @@ module Relaton
|
|
|
16
16
|
OPTIONAL = %i[version stage part label].freeze
|
|
17
17
|
|
|
18
18
|
class << self
|
|
19
|
-
def search(
|
|
20
|
-
Util.info "Fetching from Relaton repository ...", key:
|
|
21
|
-
row = find_index_entry(
|
|
19
|
+
def search(ref, _year = nil, _opts = {}) # rubocop:disable Metrics/MethodLength
|
|
20
|
+
Util.info "Fetching from Relaton repository ...", key: ref.to_s
|
|
21
|
+
row = find_index_entry(ref)
|
|
22
22
|
unless row
|
|
23
|
-
Util.info "Not found.", key:
|
|
23
|
+
Util.info "Not found.", key: ref.to_s
|
|
24
24
|
return
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
uri = URI("#{ENDPOINT}#{row[:file]}")
|
|
28
|
-
parse_item(fetch_yaml(uri),
|
|
28
|
+
parse_item(fetch_yaml(uri), ref)
|
|
29
29
|
rescue SocketError, Errno::EINVAL, Errno::ECONNRESET, EOFError,
|
|
30
30
|
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
|
|
31
31
|
Net::ProtocolError, Net::ReadTimeout,
|
|
@@ -78,11 +78,11 @@ module Relaton
|
|
|
78
78
|
# by substring, so `OASIS amqp` resolved to some `amqp-core` record;
|
|
79
79
|
# v2 matches identifiers, so a partial name no longer resolves.
|
|
80
80
|
#
|
|
81
|
-
# @param
|
|
81
|
+
# @param ref [String] the reference as the caller wrote it
|
|
82
82
|
# @return [Hash, nil] the index row, or nil
|
|
83
83
|
#
|
|
84
|
-
def find_index_entry(
|
|
85
|
-
pubid = parse_ref
|
|
84
|
+
def find_index_entry(ref)
|
|
85
|
+
pubid = parse_ref ref
|
|
86
86
|
return unless pubid
|
|
87
87
|
|
|
88
88
|
rows = index.search(pubid)
|
|
@@ -111,12 +111,15 @@ module Relaton
|
|
|
111
111
|
# Rescuing here would collapse "this identifier is malformed" into "no
|
|
112
112
|
# such document", leaving a caller unable to tell them apart.
|
|
113
113
|
#
|
|
114
|
-
# @param
|
|
114
|
+
# @param ref [String, Pubid::Oasis::Identifier]
|
|
115
115
|
# @return [Pubid::Oasis::Identifier]
|
|
116
116
|
# @raise [Pubid::Errors::ParseError]
|
|
117
117
|
#
|
|
118
|
-
def parse_ref(
|
|
119
|
-
|
|
118
|
+
def parse_ref(ref)
|
|
119
|
+
# A parsed pubid comes from Relaton::Db (relaton#205); it is used as it is.
|
|
120
|
+
return ref unless ref.is_a?(String)
|
|
121
|
+
|
|
122
|
+
slug = ref.to_s.strip.sub(/\A#{Regexp.escape PREFIX}/i, "")
|
|
120
123
|
::Pubid::Oasis::Identifier.parse "#{PREFIX}#{slug}"
|
|
121
124
|
end
|
|
122
125
|
|
|
@@ -190,9 +193,9 @@ module Relaton
|
|
|
190
193
|
resp.body
|
|
191
194
|
end
|
|
192
195
|
|
|
193
|
-
def parse_item(yaml,
|
|
196
|
+
def parse_item(yaml, ref)
|
|
194
197
|
item = Item.from_yaml yaml
|
|
195
|
-
Util.info "Found: `#{item.docidentifier.first.content}`", key:
|
|
198
|
+
Util.info "Found: `#{item.docidentifier.first.content}`", key: ref.to_s
|
|
196
199
|
item.tap { |i| i.fetched = Date.today.to_s }
|
|
197
200
|
end
|
|
198
201
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require "ferrum"
|
|
2
|
-
require "
|
|
2
|
+
require "moxml"
|
|
3
3
|
|
|
4
4
|
module Relaton
|
|
5
5
|
module Oasis
|
|
@@ -42,7 +42,7 @@ module Relaton
|
|
|
42
42
|
def get(url)
|
|
43
43
|
@browser.go_to(url)
|
|
44
44
|
wait_for_challenge
|
|
45
|
-
|
|
45
|
+
Moxml.new.parse_html(@browser.body)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
# HTTP status code of the most recent navigation's main resource,
|
|
@@ -7,7 +7,7 @@ module Relaton
|
|
|
7
7
|
#
|
|
8
8
|
# Initialize parser.
|
|
9
9
|
#
|
|
10
|
-
# @param [
|
|
10
|
+
# @param [Moxml::Element] node document node
|
|
11
11
|
#
|
|
12
12
|
def initialize(node, errors = {}, agent: nil)
|
|
13
13
|
@node = node
|
|
@@ -16,14 +16,14 @@ module Relaton
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def title
|
|
19
|
-
@title ||= @node.
|
|
19
|
+
@title ||= @node.at_xpath("./summary/div/h2").text
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def text
|
|
23
23
|
xpath = "./div/div/div[contains(@class, " \
|
|
24
24
|
"'standard__grid--cite-as')]" \
|
|
25
25
|
"/p[em or i or a or span]"
|
|
26
|
-
@text ||= @node.
|
|
26
|
+
@text ||= @node.at_xpath(xpath)&.text&.strip
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
#
|
|
@@ -144,7 +144,7 @@ module Relaton
|
|
|
144
144
|
xpath = "./div/div/div[contains(@class, " \
|
|
145
145
|
"'standard__grid--cite-as')]" \
|
|
146
146
|
"/p[strong or span/strong]/a"
|
|
147
|
-
@link_node ||= @node.
|
|
147
|
+
@link_node ||= @node.at_xpath(xpath)
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
#
|
|
@@ -220,7 +220,7 @@ module Relaton
|
|
|
220
220
|
parts = document_part_refs
|
|
221
221
|
result = case parts.size
|
|
222
222
|
when 0
|
|
223
|
-
txt = @node.
|
|
223
|
+
txt = @node.at_xpath("./summary/div/h2").text
|
|
224
224
|
parse_spec title_to_docid(txt)
|
|
225
225
|
when 1 then parse_part parse_spec(parts[0])
|
|
226
226
|
else parts_to_docid parts
|