relaton-itu 2.1.1 → 2.1.2
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/CLAUDE.md +22 -0
- data/lib/relaton/itu/hit_collection.rb +89 -79
- data/lib/relaton/itu/radio_regulations_parser.rb +2 -1
- data/lib/relaton/itu/scraper.rb +14 -0
- data/lib/relaton/itu/version.rb +1 -1
- metadata +2 -7
- data/grammars/basicdoc.rng +0 -2140
- data/grammars/biblio-standoc.rng +0 -268
- data/grammars/biblio.rng +0 -2125
- data/grammars/relaton-itu-compile.rng +0 -11
- data/grammars/relaton-itu.rng +0 -239
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6934a71bcfd52917864d6cf3af90fd07333ce87cc9c5d8cd045217d86a423b28
|
|
4
|
+
data.tar.gz: 0a1daa40935cb00409c1d40186aa63d7c25bd70866356a635fa52dc4ef512d87
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 11d05f246d70e236f0995ec2a80abfef38c8085b602aa6bff5aa4c5033fdc5d4984cec8e9125348f9f31739903cf82e89d9d16819a4233e4855ece1488455366
|
|
7
|
+
data.tar.gz: 42cfd40edebcd128a3cc402a3c6e3c6fd4b2d7e1bad5a4a9d2403dd59b020cf729034eeaa864d915e69e1bf192b0bb7d2e90ce31465fa7db942dbaa608d3eb7f
|
data/CLAUDE.md
CHANGED
|
@@ -45,6 +45,28 @@ All model classes use `Lutaml::Model::Serializable` for XML/YAML serialization:
|
|
|
45
45
|
- **`DataParserR`** — module that parses ITU-R JSON search API results into `ItemData` instances (sets `flavor: "itu"` on all parsed documents)
|
|
46
46
|
- Sources: recommendations (JSON index), questions, reports, handbooks, resolutions (HTML indices)
|
|
47
47
|
|
|
48
|
+
### Runtime lookup (`HitCollection#search`)
|
|
49
|
+
|
|
50
|
+
`Bibliography.get`/`search` routes by reference in `HitCollection#search`:
|
|
51
|
+
|
|
52
|
+
- **ITU-T Recommendations** (`request_recommendation`) — discovery is a two-stage HTTP flow against
|
|
53
|
+
`www.itu.int`. The `RunSearch` Deep Search endpoint was removed/WAF-blocked (issue #88), so
|
|
54
|
+
discovery now GETs the public `ITU-T/recommendations/rec.aspx?rec={code}` page, extracts the
|
|
55
|
+
record handle (`11.1002/1000/{idrec}`), and calls the `mws/api/recommendations/getRecEditions`
|
|
56
|
+
API to build one `Hit` per edition (each `hit[:url]` is a `handle.itu.int/.../{idrec}-en` URL so
|
|
57
|
+
`Scraper#idrec` re-parses it). Year/edition filtering stays in `Bibliography#search_filter` /
|
|
58
|
+
`#isobib_results_filter`. Full metadata is fetched by `RecommendationParser` (the `mws/api/*`
|
|
59
|
+
endpoints), unchanged.
|
|
60
|
+
- **ITU-R RR & Operational Bulletins** (`request_publication`) — resolved to their predictable
|
|
61
|
+
`/pub/{pubid}` landing page (`R-REG-RR-{year}`, `T-SP-OB.{num}-{year}`) and scraped by
|
|
62
|
+
`RadioRegulationsParser`. A redirect to `notfound.aspx` (or a 404) yields no hit.
|
|
63
|
+
- **ITU-R (other)** (`request_document`) — reads the pre-built `relaton-data-itu-r` GitHub dataset
|
|
64
|
+
via `Relaton::Index`; does not hit `www.itu.int`.
|
|
65
|
+
|
|
66
|
+
Note: `DataFetcher` (the offline ITU-R harvester) still POSTs to the removed `RunSearch` endpoint,
|
|
67
|
+
so a dataset *refresh* is currently broken — a follow-up should migrate it (and, longer term, ITU-T)
|
|
68
|
+
to a relaton-data dataset.
|
|
69
|
+
|
|
48
70
|
### Processor
|
|
49
71
|
|
|
50
72
|
`Relaton::Itu::Processor` extends `Relaton::Core::Processor` and is the entry point for the Relaton plugin system. Provides `get`, `fetch_data`, `from_xml`, `hash_to_bib`, `grammar_hash`, and `remove_index_file`.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "cgi"
|
|
4
|
+
require "json"
|
|
3
5
|
require_relative "hit"
|
|
4
6
|
|
|
5
7
|
module Relaton
|
|
@@ -8,11 +10,16 @@ module Relaton
|
|
|
8
10
|
class HitCollection < Relaton::Core::HitCollection
|
|
9
11
|
DOMAIN = "https://www.itu.int"
|
|
10
12
|
GH_ITU_R = "https://raw.githubusercontent.com/relaton/relaton-data-itu-r/refs/heads/v2/"
|
|
13
|
+
REC_URL = "#{DOMAIN}/ITU-T/recommendations/rec.aspx?rec=%<rec>s&lang=en".freeze
|
|
14
|
+
RECEDITIONS_URL = "#{DOMAIN}/mws/api/recommendations/getRecEditions?idrec=%<idrec>s&lang=en".freeze
|
|
15
|
+
HANDLE_URL = "http://handle.itu.int/11.1002/1000/%<idrec>s-en".freeze
|
|
11
16
|
|
|
12
17
|
def search
|
|
13
18
|
case ref.to_ref
|
|
14
|
-
when
|
|
15
|
-
|
|
19
|
+
when %r{^ITU-R\sRR}, %r{\bOB\.|Operational Bulletin}
|
|
20
|
+
request_publication
|
|
21
|
+
when /^ITU-T/
|
|
22
|
+
request_recommendation
|
|
16
23
|
when /^ITU-R\s/
|
|
17
24
|
request_document
|
|
18
25
|
end
|
|
@@ -27,12 +34,87 @@ module Relaton
|
|
|
27
34
|
|
|
28
35
|
private
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
# Resolve an ITU-T Recommendation to its editions via the public rec.aspx
|
|
38
|
+
# page (which exposes the record's handle/idrec) and the getRecEditions API.
|
|
39
|
+
# One hit is built per edition, mirroring the multi-result shape the old
|
|
40
|
+
# RunSearch endpoint used to return, so year filtering keeps working.
|
|
41
|
+
def request_recommendation
|
|
31
42
|
Util.info "Fetching from www.itu.int ...", key: ref.to_s
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@array =
|
|
43
|
+
idrec = fetch_idrec
|
|
44
|
+
return @array = [] unless idrec
|
|
45
|
+
|
|
46
|
+
@array = editions(idrec).map { |ed| recommendation_hit(ed) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [String, nil] the record's idrec, or nil when the code is unknown
|
|
50
|
+
def fetch_idrec
|
|
51
|
+
url = format(REC_URL, rec: CGI.escape(rec_query))
|
|
52
|
+
agent.get(url).body[%r{11\.1002/1000/(\d+)}, 1]
|
|
53
|
+
rescue Mechanize::ResponseCodeError => e
|
|
54
|
+
raise unless e.response_code == "404" # unknown code => treat as not found
|
|
55
|
+
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @return [String] the `rec=` value for the rec.aspx lookup
|
|
60
|
+
def rec_query
|
|
61
|
+
ref.suppl ? "#{ref.code} Suppl. #{ref.suppl}" : ref.code
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param idrec [String]
|
|
65
|
+
# @return [Array<Hash>] editions of the recommendation
|
|
66
|
+
def editions(idrec)
|
|
67
|
+
JSON.parse agent.get(format(RECEDITIONS_URL, idrec: idrec)).body
|
|
68
|
+
rescue JSON::ParserError
|
|
69
|
+
[]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @param edition [Hash] a getRecEditions entry
|
|
73
|
+
# @return [Relaton::Itu::Hit]
|
|
74
|
+
def recommendation_hit(edition)
|
|
75
|
+
Hit.new({
|
|
76
|
+
code: "ITU-T #{edition['rec_name']}",
|
|
77
|
+
title: edition["title"],
|
|
78
|
+
url: format(HANDLE_URL, idrec: edition["idrec"]),
|
|
79
|
+
type: "recommendation",
|
|
80
|
+
}, self)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Resolve an ITU-R Radio Regulation or Operational Bulletin to its stable
|
|
84
|
+
# /pub landing page, whose id is derivable from the reference.
|
|
85
|
+
def request_publication
|
|
86
|
+
Util.info "Fetching from www.itu.int ...", key: ref.to_s
|
|
87
|
+
return @array = [] unless ref.year
|
|
88
|
+
|
|
89
|
+
url = "#{DOMAIN}/pub/#{publication_id}"
|
|
90
|
+
page = fetch_publication_page url
|
|
91
|
+
return @array = [] if page.nil? || page.uri.to_s.match?(/notfound/i)
|
|
92
|
+
|
|
93
|
+
hit = Hit.new({ code: publication_code, title: nil, url: url, type: "publication" }, self)
|
|
94
|
+
@array = [hit]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @return [Mechanize::Page, nil] nil when the publication does not exist
|
|
98
|
+
def fetch_publication_page(url)
|
|
99
|
+
agent.get url
|
|
100
|
+
rescue Mechanize::ResponseCodeError => e
|
|
101
|
+
raise unless e.response_code == "404" # unknown publication => not found
|
|
102
|
+
|
|
103
|
+
nil
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @return [String] the /pub identifier for RR or OB
|
|
107
|
+
def publication_id
|
|
108
|
+
if ref.code == "RR"
|
|
109
|
+
"R-REG-RR-#{ref.year}"
|
|
110
|
+
else # Operational Bulletin, e.g. OB.1096
|
|
111
|
+
"T-SP-OB.#{ref.code[/\d+/]}-#{ref.year}"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @return [String] the docidentifier-friendly code (year only, no month)
|
|
116
|
+
def publication_code
|
|
117
|
+
"#{ref.prefix}-#{ref.sector} #{ref.code} (#{ref.year})"
|
|
36
118
|
end
|
|
37
119
|
|
|
38
120
|
def request_document # rubocop:todo Metrics/MethodLength, Metrics/AbcSize
|
|
@@ -50,78 +132,6 @@ module Relaton
|
|
|
50
132
|
hit.item = item
|
|
51
133
|
@array = [hit]
|
|
52
134
|
end
|
|
53
|
-
|
|
54
|
-
# @return [String]
|
|
55
|
-
def group
|
|
56
|
-
@group ||= case ref.to_ref
|
|
57
|
-
when %r{OB|Operational Bulletin}, %r{^ITU-R\sRR}
|
|
58
|
-
"Publications"
|
|
59
|
-
when %r{^ITU-T} then "Recommendations"
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# @return [Hash]
|
|
64
|
-
def params # rubocop:disable Metrics/MethodLength
|
|
65
|
-
input = ref.dup
|
|
66
|
-
input.year = nil
|
|
67
|
-
{
|
|
68
|
-
"Input" => input.to_s,
|
|
69
|
-
"Start" => 0,
|
|
70
|
-
"Rows" => 20,
|
|
71
|
-
"SortBy" => "RELEVANCE",
|
|
72
|
-
"ExactPhrase" => false,
|
|
73
|
-
"CollectionName" => "General",
|
|
74
|
-
"CollectionGroup" => group,
|
|
75
|
-
"Sector" => ref.to_ref.match(/(?<=^ITU-)\w/).to_s.downcase,
|
|
76
|
-
"Criterias" => [{
|
|
77
|
-
"Name" => "Search in",
|
|
78
|
-
"Criterias" => [
|
|
79
|
-
{
|
|
80
|
-
"Selected" => false,
|
|
81
|
-
"Value" => "",
|
|
82
|
-
"Label" => "Name",
|
|
83
|
-
"Target" => "/name_s",
|
|
84
|
-
"TypeName" => "CHECKBOX",
|
|
85
|
-
"GetCriteriaType" => 0,
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
"Selected" => false,
|
|
89
|
-
"Value" => "",
|
|
90
|
-
"Label" => "Short description",
|
|
91
|
-
"Target" => "/short_description_s",
|
|
92
|
-
"TypeName" => "CHECKBOX",
|
|
93
|
-
"GetCriteriaType" => 0,
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
"Selected" => false,
|
|
97
|
-
"Value" => "",
|
|
98
|
-
"Label" => "File content",
|
|
99
|
-
"Target" => "/file",
|
|
100
|
-
"TypeName" => "CHECKBOX",
|
|
101
|
-
"GetCriteriaType" => 0,
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
"ShowCheckbox" => true,
|
|
105
|
-
"Selected" => false,
|
|
106
|
-
}],
|
|
107
|
-
"Topics" => "",
|
|
108
|
-
"ClientData" => {},
|
|
109
|
-
"Language" => "en",
|
|
110
|
-
"SearchType" => "All",
|
|
111
|
-
}
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# @param data [Hash]
|
|
115
|
-
# @return [Array<Relaton::Itu::Hit>]
|
|
116
|
-
def hits(data)
|
|
117
|
-
data["results"].map do |h|
|
|
118
|
-
code = h["Media"]["Name"]
|
|
119
|
-
title = h["Title"]
|
|
120
|
-
url = "#{DOMAIN}#{h['Redirection']}"
|
|
121
|
-
type = h["Collection"]["Group"].downcase[0...-1]
|
|
122
|
-
Hit.new({ code: code, title: title, url: url, type: type }, self)
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
135
|
end
|
|
126
136
|
end
|
|
127
137
|
end
|
data/lib/relaton/itu/scraper.rb
CHANGED
|
@@ -91,10 +91,24 @@ module Relaton
|
|
|
91
91
|
@docid ||= begin
|
|
92
92
|
docids = hit.hit[:code].to_s.split(" | ").map { |c| createdocid(c) }
|
|
93
93
|
docids << createdocid(doc["rec_name"]) if docids.empty?
|
|
94
|
+
iso = iso_docid
|
|
95
|
+
docids << iso if iso
|
|
94
96
|
docids
|
|
95
97
|
end
|
|
96
98
|
end
|
|
97
99
|
|
|
100
|
+
# The equivalent ISO/IEC identifier used to come from the search result's
|
|
101
|
+
# media name; it is now re-sourced from the recommendation header's
|
|
102
|
+
# `iso_number` field (e.g. "Equivalent standard: ISO/IEC 17788:2014 (Common)").
|
|
103
|
+
# @return [Relaton::Bib::Docidentifier, nil]
|
|
104
|
+
def iso_docid
|
|
105
|
+
return unless idrec # only recommendations carry an idrec / iso_number
|
|
106
|
+
|
|
107
|
+
num = parser.doc && parser.doc["iso_number"]
|
|
108
|
+
id = num && num[%r{ISO(?:/IEC)?(?:/IEEE)?\s+\d[\d-]*}]
|
|
109
|
+
createdocid(id) if id
|
|
110
|
+
end
|
|
111
|
+
|
|
98
112
|
# @param text [String]
|
|
99
113
|
# @return [Relaton::Bib::Docidentifier]
|
|
100
114
|
def createdocid(text) # rubocop:disable Metrics/MethodLength
|
data/lib/relaton/itu/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: relaton-itu
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.
|
|
4
|
+
version: 2.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: mechanize
|
|
@@ -102,11 +102,6 @@ files:
|
|
|
102
102
|
- bin/rspec
|
|
103
103
|
- bin/setup
|
|
104
104
|
- data.json
|
|
105
|
-
- grammars/basicdoc.rng
|
|
106
|
-
- grammars/biblio-standoc.rng
|
|
107
|
-
- grammars/biblio.rng
|
|
108
|
-
- grammars/relaton-itu-compile.rng
|
|
109
|
-
- grammars/relaton-itu.rng
|
|
110
105
|
- lib/relaton/itu.rb
|
|
111
106
|
- lib/relaton/itu/bibliography.rb
|
|
112
107
|
- lib/relaton/itu/data_fetcher.rb
|