relaton-itu 1.14.1 → 1.14.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c4af93cb7f6ba7f9beff7222f442fb1d0b17ad382308fd492fadbbe7932cd69
4
- data.tar.gz: c19c46f3a457914f60cab5992a1443ccf22242ae9ba7c62d141f4de7bdd23fa2
3
+ metadata.gz: 1bdefbc155c0b9cf2d721888b2512f09913bac76831b421b44444f94dacb4d95
4
+ data.tar.gz: d5fa79047b6e08c91c8d320c8a95de3a63668a31eac6ae4d958aff3fcb5ce312
5
5
  SHA512:
6
- metadata.gz: addf42eb6f8631ed0f37fdcfc662edcab0b8877c9316c661b7168e6af64b9e584b23787bf9d5d59372da60f19c7468b378f896279ca6b39d6139c1791f023dc5
7
- data.tar.gz: 6789334b80124b7a9bbe33631379a952db3849cd34aa46c2bcbdb922f3f9393e65cb93120cd72b35d3590ab3e0f5b42595c3be815db9ca15042d1bb1e8a42332
6
+ metadata.gz: 0ae803ee1490114fd1ed254811aa46dc67ac2b9d273f3424018a3e5cf2a34a2a11876f7281184756a9b3ff4b2541bd9aba1eebdf2c39f530f9eacffe4137c0c6
7
+ data.tar.gz: a18d437f653e9955467dd9f26946cd853c97389835b271029ff721f9c86dfd11bf775bae72cab5ab5ed957467696c3dfbe0debf76670e0499ed6cc03eebcd9ae
@@ -5,6 +5,7 @@ name: rake
5
5
  on:
6
6
  push:
7
7
  branches: [ master, main ]
8
+ tags: [ v* ]
8
9
  pull_request:
9
10
 
10
11
  jobs:
@@ -10,8 +10,9 @@ on:
10
10
  Next release version. Possible values: x.y.z, major, minor, patch or pre|rc|etc
11
11
  required: true
12
12
  default: 'skip'
13
- push:
14
- tags: [ v* ]
13
+ repository_dispatch:
14
+ types: [ do-release ]
15
+
15
16
 
16
17
  jobs:
17
18
  release:
data/.gitignore CHANGED
@@ -7,6 +7,7 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  .vscode/
10
+ /vendor/
10
11
  .rubocop-https---raw-githubusercontent-com-riboseinc-oss-guides-master-ci-rubocop-yml
11
12
 
12
13
  # rspec failure tracking
data/.rubocop.yml CHANGED
@@ -7,6 +7,6 @@ require: rubocop-rails
7
7
  inherit_from:
8
8
  - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
9
9
  AllCops:
10
- TargetRubyVersion: 2.5
10
+ TargetRubyVersion: 2.7
11
11
  Rails:
12
12
  Enabled: false
data/Gemfile CHANGED
@@ -5,7 +5,10 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in relaton_itu.gemspec
6
6
  gemspec
7
7
 
8
+ gem "equivalent-xml", "~> 0.6"
8
9
  gem "pry-byebug"
10
+ gem "rake", "~> 13.0"
11
+ gem "rspec", "~> 3.0"
9
12
  gem "ruby-jing"
10
13
  gem "simplecov"
11
14
  gem "vcr"
data/README.adoc CHANGED
@@ -69,7 +69,7 @@ item.to_xml bibdata: true
69
69
  </bibdata>"
70
70
  ----
71
71
 
72
- === Get code, and year
72
+ === Get document by code and year
73
73
  [source,ruby]
74
74
  ----
75
75
  RelatonItu::ItuBibliography.get("ITU-T L.163", "2018", {})
@@ -123,7 +123,7 @@ item.link
123
123
  #<RelatonBib::TypedUri:0x00007f82d665f0f8 @content=#<Addressable::URI:0xc184 URI:https://www.itu.inthttp//handle.itu.int/11.1002/1000/13786-en?locatt=format:pdf&auth>, @type="obp">]
124
124
  ----
125
125
 
126
- === Create bibliographic item from YAML
126
+ === Create a bibliographic item from YAML
127
127
  [source,ruby]
128
128
  ----
129
129
  hash = YAML.load_file 'spec/examples/itu_bib_item.yml'
@@ -135,6 +135,25 @@ RelatonItu::ItuBibliographicItem.from_hash hash
135
135
  ...
136
136
  ----
137
137
 
138
+ === Fetch data
139
+
140
+ This gem uses the (https://extranet.itu.int/brdocsearch) dataset as a data source. +
141
+
142
+ The method `RelatonItu::DataFetcher.fetch(output: "data", format: "yaml")` fetches all the documents from the dataset and saves them to the `./data` folder in YAML format.
143
+ Arguments:
144
+
145
+ - `output` - folder to save documents (default 'data').
146
+ - `format` - the format in which the documents are saved. Possible formats are: `yaml`, `xml`, `bibxxml` (default `yaml`).
147
+
148
+ [source,ruby]
149
+ ----
150
+ RelatonItu::DataFetcher.fetch output: "dir", format: "xml"
151
+ Started at: 2023-05-27 09:21:16 -0400
152
+ Stopped at: 2023-05-27 09:27:45 -0400
153
+ Done in: 390 sec.
154
+ => nil
155
+ ----
156
+
138
157
  == Development
139
158
 
140
159
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,147 @@
1
+ module RelatonItu
2
+ class DataFetcher
3
+ def initialize(output, format)
4
+ @output = output
5
+ @format = format
6
+ @ext = format.sub "bibxml", "xml"
7
+ end
8
+
9
+ def files
10
+ @files ||= []
11
+ end
12
+
13
+ def index
14
+ @index ||= Relaton::Index.find_or_create :itu, file: "index-v1.yaml"
15
+ end
16
+
17
+ # @return agent [Mechanize]
18
+ def agent
19
+ @agent ||= Mechanize.new
20
+ end
21
+
22
+ # @return workers [RelatonBib::WorkersPool]
23
+ def workers
24
+ return @workers if @workers
25
+
26
+ @workers = RelatonBib::WorkersPool.new 10
27
+ @workers.tap do |w|
28
+ w.worker { |row| parse_page(*row) }
29
+ end
30
+ end
31
+
32
+ # @param url [String]
33
+ # @param type [String]
34
+ def parse_page(url, type)
35
+ doc = agent.get url
36
+ bib = DataParserR.parse doc, url, type
37
+ write_file bib
38
+ rescue => e # rubocop:disable Style/RescueStandardError
39
+ warn e.message
40
+ warn e.backtrace
41
+ end
42
+
43
+ def self.fetch(output: "data", format: "yaml")
44
+ t1 = Time.now
45
+ puts "Started at: #{t1}"
46
+ FileUtils.mkdir_p output
47
+ new(output, format).fetch
48
+ t2 = Time.now
49
+ puts "Stopped at: #{t2}"
50
+ puts "Done in: #{(t2 - t1).round} sec."
51
+ end
52
+
53
+ def fetch
54
+ fetch_recommendation
55
+ fetch_question
56
+ fetch_report
57
+ fetch_handbook
58
+ fetch_resolution
59
+ workers.end
60
+ workers.result
61
+ index.save
62
+ end
63
+
64
+ def fetch_recommendation
65
+ url = "https://extranet.itu.int/brdocsearch/_layouts/15/inplview.aspx?" \
66
+ "List=%7B0661B581-2413-4E84-BAB2-77E6DB27AF7F%7D&" \
67
+ "View=%7BC81191DD-48C4-4881-9CB7-FB61C683FE98%7D&" \
68
+ "ViewCount=123&" \
69
+ "IsXslView=TRUE&" \
70
+ "IsCSR=TRUE&" \
71
+ "ListViewPageUrl=https%3A%2F%2Fextranet.itu.int%2Fbrdocsearch%2FR-REC%2FForms%2Ffolders_inforce.aspx&" \
72
+ "FolderCTID=0x012001"
73
+ json_index url, "recommendation"
74
+ end
75
+
76
+ def fetch_question
77
+ url = "https://extranet.itu.int/brdocsearch/R-QUE/Forms/folders_inforce.aspx"
78
+ html_index url, "question"
79
+ end
80
+
81
+ def fetch_report
82
+ url = "https://extranet.itu.int/brdocsearch/_layouts/15/inplview.aspx?" \
83
+ "List=%7B82E4A13D-C7F3-4844-9E8A-2463C4B7784F%7D&" \
84
+ "View=%7B94CC1561-E4AC-4317-B402-AA0AADD7F414%7D&" \
85
+ "ViewCount=407&" \
86
+ "IsXslView=TRUE&" \
87
+ "IsCSR=TRUE&" \
88
+ "ListViewPageUrl=https%3A%2F%2Fextranet.itu.int%2Fbrdocsearch%2FR-REP%2FForms%2FFolders%2520InForce.aspx&" \
89
+ "FolderCTID=0x012001"
90
+ json_index url, "technical-report"
91
+ end
92
+
93
+ def fetch_handbook
94
+ url = "https://extranet.itu.int/brdocsearch/R-HDB/Forms/Folders%20InForce.aspx"
95
+ html_index url, "handbook"
96
+ end
97
+
98
+ def fetch_resolution
99
+ url = "https://extranet.itu.int/brdocsearch/R-RES/Forms/Folders%20InForce.aspx"
100
+ html_index url, "resolution"
101
+ end
102
+
103
+ # #param url [String]
104
+ # @param type [String]
105
+ def json_index(url, type) # rubocop:disable Metrics/AbcSize
106
+ result = agent.post url
107
+ json = JSON.parse result.body
108
+ json["Row"].each { |row| workers << [row["serverurl.progid"].sub(/^1/, ""), type] }
109
+ return unless json["NextHref"]
110
+
111
+ nexturl = url.sub(/(Paged|FolderCTID)=.+/, json["NextHref"].match(/(?<=aspx\?).+/).to_s)
112
+ json_index nexturl, type
113
+ end
114
+
115
+ # #param url [String]
116
+ # @param type [String]
117
+ def html_index(url, type)
118
+ resp = agent.get url
119
+ result = Nokogiri::HTML resp.body
120
+ result.xpath("//table//table/tr[position() > 1]").each do |hit|
121
+ url = hit.at("td/a")[:onclick].match(%r{https://[^']+}).to_s
122
+ workers << [url, type]
123
+ end
124
+ end
125
+
126
+ # @param bib [RelatonItu::ItuBibliographicItem]
127
+ def write_file(bib) # rubocop:disable Metrics/AbcSize
128
+ id = bib.docidentifier[0].id.gsub(/[\s.]/, "_")
129
+ file = "#{@output}/#{id}.#{@ext}"
130
+ if files.include? file
131
+ warn "File #{file} exists."
132
+ else
133
+ files << file
134
+ end
135
+ index.add_or_update bib.docidentifier[0].id, file
136
+ File.write file, content(bib), encoding: "UTF-8"
137
+ end
138
+
139
+ def content(bib)
140
+ case @format
141
+ when "yaml" then bib.to_hash.to_yaml
142
+ when "xml" then bib.to_xml bibdata: true
143
+ when "bibxml" then bib.to_bibxml
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,94 @@
1
+ module RelatonItu
2
+ module DataParserR
3
+ extend self
4
+
5
+ #
6
+ # Parse ITU-R document.
7
+ #
8
+ # @param [Mechanize::Page] doc mechanize page
9
+ # @param [String] url document url
10
+ # @param [String] type document type
11
+ #
12
+ # @return [RelatonItu::ItuBibliographicItem] bibliographic item
13
+ #
14
+ def parse(doc, url, type)
15
+ RelatonItu::ItuBibliographicItem.new(
16
+ docid: fetch_docid(doc), title: fetch_title(doc),
17
+ abstract: fetch_abstract(doc), date: fetch_date(doc), language: ["en"],
18
+ link: fetch_link(url), script: ["Latn"], docstatus: fetch_status(doc),
19
+ type: "standard", doctype: type
20
+ )
21
+ end
22
+
23
+ # @param doc [Mechanize::Page]
24
+ # @return [Araay<RelatonBib::DocumentIdentifier>]
25
+ def fetch_docid(doc)
26
+ # id = doc.at('//h3[.="Number"]/parent::td/following-sibling::td[2]').text # .match(/^[^\s\(]+/).to_s
27
+ # %r{^(?<id1>[^\s\(\/]+(\/\d+)?)(\/(?<id2>\w+[^\s\(]+))?} =~ id
28
+ id = doc.at('//div[@id="idDocSetPropertiesWebPart"]/h2').text.match(/^R-\w+-([^-]+(?:-\d{1,3})?)/)[1]
29
+ [RelatonBib::DocumentIdentifier.new(type: "ITU", id: "ITU-R #{id}", primary: true)]
30
+ # docid << RelatonBib::DocumentIdentifier.new(type: 'ITU', id: id2) if id2
31
+ # docid
32
+ end
33
+
34
+ # @param doc [Mechanize::Page]
35
+ # @return [Araay<RelatonBib::TypedTitleString>]
36
+ def fetch_title(doc)
37
+ content = doc.at('//h3[.="Title"]/parent::td/following-sibling::td[2]').text
38
+ [RelatonBib::TypedTitleString.new(type: "main", content: content, language: "en", script: "Latn")]
39
+ end
40
+
41
+ # @param doc [Mechanize::Page]
42
+ # @return [Array<RelatonBib::FormattedString>]
43
+ def fetch_abstract(doc)
44
+ doc.xpath('//h3[.="Observation"]/parent::td/following-sibling::td[2]').map do |a|
45
+ c = a.text.strip
46
+ RelatonBib::FormattedString.new content: c, language: "en", script: "Latn" unless c.empty?
47
+ end.compact
48
+ end
49
+
50
+ # @param doc [Mechanize::Page]
51
+ # @return [Araay<RelatonBib::BibliographicDate>]
52
+ def fetch_date(doc)
53
+ dates = []
54
+ date = doc.at('//h3[.="Approval_Date"]/parent::td/following-sibling::td[2]',
55
+ '//h3[.="Approval date"]/parent::td/following-sibling::td[2]',
56
+ '//h3[.="Approval year"]/parent::td/following-sibling::td[2]')
57
+ dates << parse_date(date.text, "confirmed") if date
58
+
59
+ date = doc.at('//h3[.="Version year"]/parent::td/following-sibling::td[2]')
60
+ dates << parse_date(date.text, "updated") if date
61
+ date = doc.at('//div[@id="idDocSetPropertiesWebPart"]/h2').text.match(/(?<=-)(19|20)\d{2}/)
62
+ dates << parse_date(date.to_s, "published") if date
63
+ dates
64
+ end
65
+
66
+ # @param date [String]
67
+ # @param type [String]
68
+ # @return [RelatonBib::BibliographicDate]
69
+ def parse_date(date, type)
70
+ d = case date
71
+ # when /^\d{4}$/ then date
72
+ when /(\d{4})(\d{2})/ then "#{$1}-#{$2}"
73
+ when %r{(\d{1,2})/(\d{1,2})/(\d{4})} then "#{$3}-#{$1}-#{$2}"
74
+ else date
75
+ end
76
+ RelatonBib::BibliographicDate.new(type: type, on: d)
77
+ end
78
+
79
+ # @param url [String]
80
+ # @return [Array<RelatonBib::TypedUri>]
81
+ def fetch_link(url)
82
+ [RelatonBib::TypedUri.new(type: "src", content: url)]
83
+ end
84
+
85
+ # @param doc [Mechanize::Page]
86
+ # @return [RelatonBib::DocumentStatus, nil]
87
+ def fetch_status(doc)
88
+ s = doc.at('//h3[.="Status"]/parent::td/following-sibling::td[2]')
89
+ return unless s
90
+
91
+ RelatonBib::DocumentStatus.new stage: s.text
92
+ end
93
+ end
94
+ end
@@ -8,7 +8,7 @@ module RelatonItu
8
8
  # Parse page.
9
9
  # @return [RelatonItu::ItuBibliographicItem]
10
10
  def fetch
11
- @fetch ||= Scrapper.parse_page self, hit_collection.gi_imp
11
+ @fetch ||= Scrapper.parse_page self, imp: hit_collection.gi_imp
12
12
  end
13
13
  end
14
14
  end
@@ -8,6 +8,8 @@ module RelatonItu
8
8
  # Page of hit collection.
9
9
  class HitCollection < RelatonBib::HitCollection
10
10
  DOMAIN = "https://www.itu.int"
11
+ GH_ITU_R = "https://raw.githubusercontent.com/relaton/relaton-data-itu-r/master/"
12
+ INDEX_FILE = "index-v1.yaml"
11
13
 
12
14
  # @return [TrueClass, FalseClass]
13
15
  attr_reader :gi_imp
@@ -15,20 +17,22 @@ module RelatonItu
15
17
  # @return [Mechanize]
16
18
  attr_reader :agent
17
19
 
18
- # @param ref [String]
19
- # @param year [String]
20
- def initialize(ref, year = nil) # rubocop:todo Metrics/MethodLength
21
- text = ref.sub(/(?<=\.)Imp\s?(?=\d)/, "")
22
- super text, year
20
+ #
21
+ # @param refid [RelatonItu::Pubid] reference
22
+ #
23
+ def initialize(refid) # rubocop:todo Metrics/MethodLength
24
+ text = refid.to_ref.sub(/(?<=\.)Imp\s?(?=\d)/, "")
25
+ super text, refid.year
23
26
  @agent = Mechanize.new
24
27
  agent.user_agent_alias = "Mac Safari"
25
- @gi_imp = /\.Imp\d/.match?(ref)
28
+ @gi_imp = /\.Imp\d/.match?(refid.to_s)
29
+ @array = []
26
30
 
27
- case ref
31
+ case refid.to_ref
28
32
  when /^(ITU-T|ITU-R\sRR)/
29
33
  request_search
30
34
  when /^ITU-R\s/
31
- request_document(ref)
35
+ request_document(refid)
32
36
  end
33
37
  end
34
38
 
@@ -41,17 +45,17 @@ module RelatonItu
41
45
  @array = hits JSON.parse(resp.body)
42
46
  end
43
47
 
44
- # @param ref [String] a document ref
45
- def request_document(ref) # rubocop:todo Metrics/MethodLength
46
- uri = URI::HTTPS.build(
47
- host: "raw.githubusercontent.com",
48
- path: "/relaton/relaton-data-itu-r/master/data/#{ref.gsub(/[\s.]/, '_')}.yaml",
49
- )
48
+ #
49
+ # @param refid [RelatonItu::Pubid] a document reference
50
+ #
51
+ def request_document(refid) # rubocop:todo Metrics/MethodLength, Metrics/AbcSize
52
+ index = Relaton::Index.find_or_create :itu, url: "#{GH_ITU_R}index-v1.zip", file: INDEX_FILE
53
+ row = index.search(refid.to_ref).min_by { |i| i[:id] }
54
+ return unless row
55
+
56
+ uri = URI("#{GH_ITU_R}#{row[:file]}")
50
57
  resp = Net::HTTP.get_response(uri)
51
- if resp.code == "404"
52
- @array = []
53
- return
54
- end
58
+ return if resp.code == "404"
55
59
 
56
60
  hash = YAML.safe_load resp.body
57
61
  item_hash = HashConverter.hash_to_bib(hash)
@@ -14,134 +14,139 @@ require "date"
14
14
 
15
15
  module RelatonItu
16
16
  # Class methods for search ISO standards.
17
- class ItuBibliography
18
- class << self
19
- # @param text [String]
20
- # @return [RelatonItu::HitCollection]
21
- def search(text, year = nil)
22
- # code = text.sub(/(?<=ITU-T\s\w)\.(\w+\.)(?=\d+)/, ' \1')
23
- if text =~ /(ITU-T\s\w)\.(Suppl\.|Annex)\s?(\w?\d+)/
24
- correct_ref = "#{$~[1]} #{$~[2]} #{$~[3]}"
25
- warn "[relaton-itu] WARNING: Incorrect reference #{text}"
26
- warn "[relaton-itu] the reference should be #{correct_ref}"
27
- end
28
- HitCollection.new text, year
29
- rescue SocketError, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
30
- EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
31
- Net::ProtocolError, URI::InvalidURIError => e
32
- raise RelatonBib::RequestError, e.message
17
+ module ItuBibliography
18
+ extend self
19
+
20
+ # @param refid [RelatonItu::Pubid, String] a document reference
21
+ # @return [RelatonItu::HitCollection]
22
+ #
23
+ def search(refid)
24
+ refid = RelatonItu::Pubid.parse refid if refid.is_a? String
25
+ if refid.to_ref =~ /(ITU[\s-]T\s\w)\.(Suppl\.|Annex)\s?(\w?\d+)/
26
+ correct_ref = "#{$~[1]} #{$~[2]} #{$~[3]}"
27
+ warn "[relaton-itu] WARNING: Incorrect reference #{refid}"
28
+ warn "[relaton-itu] the reference should be #{correct_ref}"
33
29
  end
30
+ HitCollection.new refid
31
+ rescue SocketError, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse,
32
+ Net::HTTPHeaderSyntaxError, Net::ProtocolError, URI::InvalidURIError => e
33
+ raise RelatonBib::RequestError, e.message
34
+ end
34
35
 
35
- # @param code [String] the ISO standard Code to look up (e..g "ISO 9000")
36
- # @param year [String] the year the standard was published (optional)
37
- # @param opts [Hash] options; restricted to :all_parts if all-parts
38
- # reference is required
39
- # @return [String] Relaton XML serialisation of reference
40
- def get(code, year = nil, opts = {}) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
41
- if year.nil?
42
- /^(?<code1>[^\s]+\s[^\s]+)\s\((?:\d{2}\/)?(?<year1>\d+)\)$/ =~ code
43
- unless code1.nil?
44
- code = code1
45
- year = year1
46
- end
47
- end
36
+ # def transform_ref(ref)
37
+ # ref.sub(/^ITU[\s-](\w)[\s-](?:REC[\s-])?/, 'ITU-\1')
38
+ # end
48
39
 
49
- code += "-1" if opts[:all_parts]
50
- ret = itubib_get1(code, year, opts)
51
- return nil if ret.nil?
40
+ # @param code [String] the ISO standard Code to look up (e..g "ISO 9000")
41
+ # @param year [String] the year the standard was published (optional)
42
+ # @param opts [Hash] options; restricted to :all_parts if all-parts
43
+ # reference is required
44
+ # @return [String] Relaton XML serialisation of reference
45
+ def get(code, year = nil, opts = {}) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
46
+ refid = Pubid.parse code
47
+ refid.year ||= year
48
+ # if year.nil?
49
+ # /^(?<code1>[^\s]+\s[^\s]+)\s\((?:\d{2}\/)?(?<year1>\d+)\)$/ =~ code
50
+ # unless code1.nil?
51
+ # code = code1
52
+ # year = year1
53
+ # end
54
+ # end
52
55
 
53
- ret = ret.to_most_recent_reference unless year || opts[:keep_year]
54
- ret = ret.to_all_parts if opts[:all_parts]
55
- ret
56
- end
56
+ ret = itubib_get1(refid)
57
+ return nil if ret.nil?
57
58
 
58
- private
59
+ ret = ret.to_most_recent_reference unless refid.year || opts[:keep_year]
60
+ ret = ret.to_all_parts if opts[:all_parts]
61
+ ret
62
+ end
59
63
 
60
- def fetch_ref_err(code, year, missed_years) # rubocop:disable Metrics/MethodLength
61
- id = year ? "#{code}:#{year}" : code
62
- warn "[relaton-itu] WARNING: no match found online for #{id}. " \
63
- "The code must be exactly like it is on the standards website."
64
- unless missed_years.empty?
65
- warn "[relaton-itu] (There was no match for #{year}, though there " \
66
- "were matches found for #{missed_years.join(', ')}.)"
67
- end
68
- if /\d-\d/.match? code
69
- warn "[relaton-itu] The provided document part may not exist, or " \
70
- "the document may no longer be published in parts."
71
- else
72
- warn "[relaton-itu] If you wanted to cite all document parts for the reference, " \
73
- "use \"#{code} (all parts)\".\nIf the document is not a standard, " \
74
- "use its document type abbreviation (TS, TR, PAS, Guide)."
75
- end
76
- nil
64
+ private
65
+
66
+ def fetch_ref_err(refid, missed_years) # rubocop:disable Metrics/MethodLength
67
+ warn "[relaton-itu] WARNING: no match found online for #{refid}. " \
68
+ "The code must be exactly like it is on the standards website."
69
+ unless missed_years.empty?
70
+ warn "[relaton-itu] (There was no match for #{refid.year}, though there " \
71
+ "were matches found for #{missed_years.join(', ')}.)"
77
72
  end
73
+ # if /\d-\d/.match? refid.code
74
+ # warn "[relaton-itu] The provided document part may not exist, or " \
75
+ # "the document may no longer be published in parts."
76
+ # else
77
+ warn "[relaton-itu] If you wanted to cite all document parts for the reference, " \
78
+ "use \"#{refid} (all parts)\".\nIf the document is not a standard, " \
79
+ "use its document type abbreviation (TS, TR, PAS, Guide)."
80
+ # end
81
+ end
78
82
 
79
- def search_filter(code, year) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
80
- %r{
81
- ^(?<pref1>ITU)?(?:-(?<type1>\w))?\s?(?<code1>[^\s/]+(?:/\w[.\d]+)?)
82
- (?:\s\(?(?<ver1>v\d+)\)?)?
83
- (?:\s\((?:(?<month1>\d{2})/)?(?<year1>\d{4})\))?
84
- (?:\s-\s(?<buldate1>\d{2}\.\w{1,4}\.\d{4}))?
85
- (?:\s(?<corr1>(?:Amd|Cor|Amendment|Corrigendum)\.?\s?\d+))?
86
- (?:\s\((?:(?<cormonth1>\d{2})/)?(?<coryear1>\d{4})\))?
87
- }x =~ code
88
- year ||= year1
89
- warn "[relaton-itu] (\"#{code}\") fetching..."
90
- result = search(code)
91
- code1.sub!(/(?<=\.)Imp(?=\d)/, "") if result.gi_imp
92
- if corr1
93
- corr1.sub!(/[.\s]+/, " ").sub!("Amendment", "Amd")
94
- corr1.sub!("Corrigendum", "Corr")
95
- end
96
- result.select do |i|
97
- next true unless i.hit[:code]
83
+ def search_filter(refid) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
84
+ # %r{
85
+ # ^(?<pref1>ITU)?(?:-(?<type1>\w))?\s?(?<code1>[^\s/]+(?:/\w[.\d]+)?)
86
+ # (?:\s\(?(?<ver1>v\d+)\)?)?
87
+ # (?:\s\((?:(?<month1>\d{2})/)?(?<year1>\d{4})\))?
88
+ # (?:\s-\s(?<buldate1>\d{2}\.\w{1,4}\.\d{4}))?
89
+ # (?:\s(?<corr1>(?:Amd|Cor|Amendment|Corrigendum)\.?\s?\d+))?
90
+ # (?:\s\((?:(?<cormonth1>\d{2})/)?(?<coryear1>\d{4})\))?
91
+ # }x =~ code
92
+ # year ||= year1
93
+ warn "[relaton-itu] (\"#{refid}\") fetching..."
94
+ result = search(refid)
95
+ # refid.code.sub!(/(?<=\.)Imp(?=\d)/, "") if result.gi_imp
96
+ # if corr1
97
+ # corr1.sub!(/[.\s]+/, " ").sub!("Amendment", "Amd")
98
+ # corr1.sub!("Corrigendum", "Corr")
99
+ # end
100
+ result.select do |i|
101
+ next true unless i.hit[:code]
98
102
 
99
- %r{
100
- ^(?<pref2>ITU)?(?:-(?<type2>\w))?\s?(?<code2>\S+)
101
- (?:\s\(?(?<ver2>v\d+)\)?)?
102
- (?:\s\((?:(?<month2>\d{2})/)?(?<year2>\d{4})\))?
103
- (?:\s(?<corr2>(?:Amd|Cor)\.\s?\d+))?
104
- (?:\s\((?:(?<cormonth2>\d{2})/)?(?<coryear2>\d{4})\))?
105
- }x =~ i.hit[:code]
106
- /:[^(]+\((?<buldate2>\d{2}\.\w{1,4}\.\d{4})\)/ =~ i.hit[:title]
107
- corr2&.sub!(/\.\s?/, " ")
108
- pref1 == pref2 && (!type1 || type1 == type2) && code2.include?(code1) &&
109
- (!year || year == year2) && (!month1 || month1 == month2) &&
110
- corr1 == corr2 && (!coryear1 || coryear1 == coryear2) &&
111
- buldate1 == buldate2 && (!cormonth1 || cormonth1 == cormonth2) &&
112
- (!ver1 || ver1 == ver2)
113
- end
103
+ pubid = Pubid.parse i.hit[:code]
104
+ refid === pubid
105
+ # %r{
106
+ # ^(?<pref2>ITU)?(?:-(?<type2>\w))?\s?(?<code2>\S+)
107
+ # (?:\s\(?(?<ver2>v\d+)\)?)?
108
+ # (?:\s\((?:(?<month2>\d{2})/)?(?<year2>\d{4})\))?
109
+ # (?:\s(?<corr2>(?:Amd|Cor)\.\s?\d+))?
110
+ # (?:\s\((?:(?<cormonth2>\d{2})/)?(?<coryear2>\d{4})\))?
111
+ # }x =~ i.hit[:code]
112
+ # /:[^(]+\((?<buldate2>\d{2}\.\w{1,4}\.\d{4})\)/ =~ i.hit[:title]
113
+ # corr2&.sub!(/\.\s?/, " ")
114
+ # pref1 == pref2 && (!type1 || type1 == type2) && code2.include?(code1) &&
115
+ # (!year || year == year2) && (!month1 || month1 == month2) &&
116
+ # corr1 == corr2 && (!coryear1 || coryear1 == coryear2) &&
117
+ # buldate1 == buldate2 && (!cormonth1 || cormonth1 == cormonth2) &&
118
+ # (!ver1 || ver1 == ver2)
114
119
  end
120
+ end
115
121
 
116
- # Sort through the results from Isobib, fetching them three at a time,
117
- # and return the first result that matches the code,
118
- # matches the year (if provided), and which # has a title (amendments do not).
119
- # Only expects the first page of results to be populated.
120
- # Does not match corrigenda etc (e.g. ISO 3166-1:2006/Cor 1:2007)
121
- # If no match, returns any years which caused mismatch, for error reporting
122
- def isobib_results_filter(result, year)
123
- missed_years = []
124
- result.each do |r|
125
- /\((?:\d{2}\/)?(?<pyear>\d{4})\)/ =~ r.hit[:code]
126
- if !year || year == pyear
127
- ret = r.fetch
128
- return { ret: ret } if ret
129
- end
130
-
131
- missed_years << pyear
122
+ # Sort through the results from Isobib, fetching them three at a time,
123
+ # and return the first result that matches the code,
124
+ # matches the year (if provided), and which # has a title (amendments do not).
125
+ # Only expects the first page of results to be populated.
126
+ # Does not match corrigenda etc (e.g. ISO 3166-1:2006/Cor 1:2007)
127
+ # If no match, returns any years which caused mismatch, for error reporting
128
+ def isobib_results_filter(result, refid)
129
+ missed_years = []
130
+ result.each do |r|
131
+ /\((?:\d{2}\/)?(?<pyear>\d{4})\)/ =~ r.hit[:code]
132
+ if !refid.year || refid.year == pyear
133
+ ret = r.fetch
134
+ return { ret: ret } if ret
132
135
  end
133
- { years: missed_years }
136
+
137
+ missed_years << pyear
134
138
  end
139
+ { years: missed_years }
140
+ end
135
141
 
136
- def itubib_get1(code, year, _opts)
137
- result = search_filter(code, year) || return
138
- ret = isobib_results_filter(result, year)
139
- if ret[:ret]
140
- warn "[relaton-itu] (\"#{code}\") found #{ret[:ret].docidentifier.first&.id}"
141
- ret[:ret]
142
- else
143
- fetch_ref_err(code, year, ret[:years])
144
- end
142
+ def itubib_get1(refid)
143
+ result = search_filter(refid) || return
144
+ ret = isobib_results_filter(result, refid)
145
+ if ret[:ret]
146
+ warn "[relaton-itu] (\"#{refid}\") found #{ret[:ret].docidentifier.first&.id}"
147
+ ret[:ret]
148
+ else
149
+ fetch_ref_err(refid, ret[:years])
145
150
  end
146
151
  end
147
152
  end
@@ -7,6 +7,7 @@ module RelatonItu
7
7
  @prefix = "ITU"
8
8
  @defaultprefix = %r{^ITU\s}
9
9
  @idtype = "ITU"
10
+ @datasets = %w[itu-r]
10
11
  end
11
12
 
12
13
  # @param code [String]
@@ -17,6 +18,18 @@ module RelatonItu
17
18
  ::RelatonItu::ItuBibliography.get(code, date, opts)
18
19
  end
19
20
 
21
+ #
22
+ # Fetch all the documents from https://extranet.itu.int/brdocsearch/
23
+ #
24
+ # @param [String] source source name (itu-r)
25
+ # @param [Hash] opts
26
+ # @option opts [String] :output directory to output documents, default is data
27
+ # @option opts [String] :format output format, default is yaml
28
+ #
29
+ def fetch_data(_source, opts)
30
+ DataFetcher.fetch(**opts)
31
+ end
32
+
20
33
  # @param xml [String]
21
34
  # @return [RelatonItu::ItuBibliographicItem]
22
35
  def from_xml(xml)
@@ -34,5 +47,12 @@ module RelatonItu
34
47
  def grammar_hash
35
48
  @grammar_hash ||= ::RelatonItu.grammar_hash
36
49
  end
50
+
51
+ #
52
+ # Remove index file
53
+ #
54
+ def remove_index_file
55
+ Relaton::Index.find_or_create(:itu, url: true, file: HitCollection::INDEX_FILE).remove_file
56
+ end
37
57
  end
38
58
  end
@@ -0,0 +1,142 @@
1
+ module RelatonItu
2
+ class Pubid
3
+ class Parser < Parslet::Parser
4
+ rule(:dash) { str("-") }
5
+ rule(:dot) { str(".") }
6
+ rule(:dot?) { dot.maybe }
7
+ rule(:separator) { match['\s-'] }
8
+ rule(:space) { match("\s") }
9
+ rule(:num) { match["0-9"] }
10
+
11
+ rule(:prefix) { str("ITU").as(:prefix) }
12
+ rule(:sector) { separator >> match("[A-Z]").as(:sector) }
13
+ rule(:type) { separator >> str("REC").as(:type) }
14
+ rule(:type?) { type.maybe }
15
+ rule(:code) { separator >> (match["A-Z0-9"].repeat(1) >> match["[:alnum:]/.-"].repeat).as(:code) }
16
+ rule(:year) { (match["12"] >> num.repeat(3, 3)).as(:year) }
17
+
18
+ rule(:month1) { num.repeat(2, 2).as(:month) }
19
+ rule(:date1) { str(" (") >> (month1 >> str("/")).maybe >> year >> str(")") }
20
+ rule(:month2) { match["IVX"].repeat(1, 3).as(:month) }
21
+ rule(:date2) { str(" - ") >> num.repeat(2, 2) >> dot >> month2 >> dot >> year }
22
+ rule(:date) { date1 | date2 }
23
+ rule(:date?) { date.maybe }
24
+
25
+ rule(:amd) { space >> (str("Amd") | str("Amendment")) >> dot? >> space >> num.repeat(1, 2).as(:amd) }
26
+ rule(:amd?) { amd.maybe }
27
+
28
+ rule(:sup) { space >> str("Suppl") >> dot? >> space >> num.repeat(1, 2).as(:suppl) }
29
+ rule(:sup?) { sup.maybe }
30
+
31
+ rule(:annex) { space >> str("Annex") >> space >> match["[:alnum:]"].repeat(1, 2).as(:annex) }
32
+ rule(:annex?) { annex.maybe }
33
+
34
+ rule(:itu_pubid) { prefix >> sector >> type? >> code >> sup? >> annex? >> date? >> amd? >> any.repeat }
35
+ root(:itu_pubid)
36
+ end
37
+
38
+ attr_accessor :prefix, :sector, :type, :code, :suppl, :annex, :year, :month, :amd
39
+
40
+ #
41
+ # Create a new ITU publication identifier.
42
+ #
43
+ # @param [String] prefix
44
+ # @param [String] sector
45
+ # @param [String, nil] type
46
+ # @param [String] code
47
+ # @param [String, nil] suppl number
48
+ # @param [String, nil] year
49
+ # @param [String, nil] month
50
+ # @param [String, nil] amd amendment number
51
+ #
52
+ def initialize(prefix:, sector:, code:, **args)
53
+ @prefix = prefix
54
+ @sector = sector
55
+ @type = args[:type]
56
+ @code, year, month = date_from_code code
57
+ @suppl = args[:suppl]
58
+ @annex = args[:annex]
59
+ @year = args[:year] || year
60
+ @month = roman_to_2digit args[:month] || month
61
+ @amd = args[:amd]
62
+ end
63
+
64
+ def self.parse(id)
65
+ id_parts = Parser.new.parse(id).to_h.transform_values(&:to_s)
66
+ new(**id_parts)
67
+ rescue Parslet::ParseFailed => e
68
+ warn "[relaton-itu] WARNING: #{id} is invalid ITU publication identifier"
69
+ warn e.parse_failure_cause.ascii_tree
70
+ raise e
71
+ end
72
+
73
+ def to_h(with_type: true) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
74
+ hash = { prefix: prefix, sector: sector, code: code }
75
+ hash[:type] = type if type && with_type
76
+ hash[:suppl] = suppl if suppl
77
+ hash[:annex] = annex if annex
78
+ hash[:year] = year if year
79
+ hash[:month] = month if month
80
+ hash[:amd] = amd if amd
81
+ hash
82
+ end
83
+
84
+ def to_ref
85
+ to_s ref: true
86
+ end
87
+
88
+ def to_s(ref: false) # rubocop:disable Metrics/AbcSize
89
+ s = "#{prefix}-#{sector}"
90
+ s << " #{type}" if type && !ref
91
+ s << " #{code}"
92
+ s << " Suppl. #{suppl}" if suppl
93
+ s << " Annex #{annex}" if annex
94
+ s << date_to_s
95
+ s << " Amd #{amd}" if amd
96
+ s
97
+ end
98
+
99
+ def ===(other)
100
+ hash = to_h with_type: false
101
+ other_hash = other.to_h with_type: false
102
+ hash.delete(:month)
103
+ other_hash.delete(:month)
104
+ other_hash.delete(:year) unless hash[:year]
105
+ hash == other_hash
106
+ end
107
+
108
+ private
109
+
110
+ def date_from_code(code)
111
+ /(?<cod>.+?)-(?<date>\d{6})(?:-I|$)/ =~ code
112
+ return [code, nil, nil] unless cod && date
113
+
114
+ [cod, date[0..3], date[4..5]]
115
+ end
116
+
117
+ def roman_to_2digit(num) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
118
+ return unless num
119
+
120
+ roman_nums = { "I" => 1, "V" => 5, "X" => 10 }
121
+ last = roman_nums[num[-1]]
122
+ return num unless last
123
+
124
+ return roman_nums[num].to_s.rjust(2, "0") if num.size == 1
125
+
126
+ num.chars.each_cons(2).reduce(last) do |acc, (a, b)|
127
+ if roman_nums[a] < roman_nums[b]
128
+ acc - roman_nums[a]
129
+ else
130
+ acc + roman_nums[a]
131
+ end
132
+ end.to_s.rjust(2, "0")
133
+ end
134
+
135
+ def date_to_s
136
+ if month && year then " (#{month}/#{year})"
137
+ elsif year then " (#{year})"
138
+ else ""
139
+ end
140
+ end
141
+ end
142
+ end
@@ -27,7 +27,7 @@ module RelatonItu
27
27
  # Parse page.
28
28
  # @param hit [RelatonItu::Hit]
29
29
  # @return [Hash]
30
- def parse_page(hit, imp = false) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
30
+ def parse_page(hit, imp: false) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
31
31
  doc = get_page hit
32
32
  return unless doc.code == "200"
33
33
 
@@ -1,3 +1,3 @@
1
1
  module RelatonItu
2
- VERSION = "1.14.1".freeze
2
+ VERSION = "1.14.3".freeze
3
3
  end
data/lib/relaton_itu.rb CHANGED
@@ -1,7 +1,12 @@
1
1
  require "mechanize"
2
+ require "parslet"
3
+ require "digest/md5"
4
+ require "relaton/index"
2
5
  require "relaton_itu/version"
6
+ require "relaton_itu/pubid"
3
7
  require "relaton_itu/itu_bibliography"
4
- require "digest/md5"
8
+ require "relaton_itu/data_fetcher"
9
+ require "relaton_itu/data_parser_r"
5
10
 
6
11
  module RelatonItu
7
12
  class Error < StandardError; end
data/relaton-itu.gemspec CHANGED
@@ -1,5 +1,4 @@
1
-
2
- lib = File.expand_path("../lib", __FILE__)
1
+ lib = File.expand_path("lib", __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require "relaton_itu/version"
5
4
 
@@ -9,10 +8,10 @@ Gem::Specification.new do |spec|
9
8
  spec.authors = ["Ribose Inc."]
10
9
  spec.email = ["open.source@ribose.com"]
11
10
 
12
- spec.summary = "RelatonItu: retrieve ITU Standards for bibliographic use "\
13
- "using the BibliographicItem model"
14
- spec.description = "RelatonItu: retrieve ITU Standards for bibliographic use "\
15
- "using the BibliographicItem model"
11
+ spec.summary = "RelatonItu: retrieve ITU Standards for bibliographic " \
12
+ "use using the BibliographicItem model"
13
+ spec.description = "RelatonItu: retrieve ITU Standards for bibliographic " \
14
+ "use using the BibliographicItem model"
16
15
  spec.homepage = "https://github.com/metanorma/relaton-itu"
17
16
  spec.license = "MIT"
18
17
 
@@ -24,12 +23,10 @@ Gem::Specification.new do |spec|
24
23
  spec.bindir = "exe"
25
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
25
  spec.require_paths = ["lib"]
27
- spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
28
-
29
- spec.add_development_dependency "equivalent-xml", "~> 0.6"
30
- spec.add_development_dependency "rake", "~> 13.0"
31
- spec.add_development_dependency "rspec", "~> 3.0"
26
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
32
27
 
33
28
  spec.add_dependency "mechanize", "~> 2.8.0"
29
+ spec.add_dependency "parslet", "~> 2.0.0"
34
30
  spec.add_dependency "relaton-bib", "~> 1.14.0"
31
+ spec.add_dependency "relaton-index", "~> 0.2.0"
35
32
  end
metadata CHANGED
@@ -1,85 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relaton-itu
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.14.1
4
+ version: 1.14.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-04-09 00:00:00.000000000 Z
11
+ date: 2023-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: equivalent-xml
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '0.6'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '0.6'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
14
+ name: mechanize
29
15
  requirement: !ruby/object:Gem::Requirement
30
16
  requirements:
31
17
  - - "~>"
32
18
  - !ruby/object:Gem::Version
33
- version: '13.0'
34
- type: :development
19
+ version: 2.8.0
20
+ type: :runtime
35
21
  prerelease: false
36
22
  version_requirements: !ruby/object:Gem::Requirement
37
23
  requirements:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
- version: '13.0'
26
+ version: 2.8.0
41
27
  - !ruby/object:Gem::Dependency
42
- name: rspec
28
+ name: parslet
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - "~>"
46
32
  - !ruby/object:Gem::Version
47
- version: '3.0'
48
- type: :development
33
+ version: 2.0.0
34
+ type: :runtime
49
35
  prerelease: false
50
36
  version_requirements: !ruby/object:Gem::Requirement
51
37
  requirements:
52
38
  - - "~>"
53
39
  - !ruby/object:Gem::Version
54
- version: '3.0'
40
+ version: 2.0.0
55
41
  - !ruby/object:Gem::Dependency
56
- name: mechanize
42
+ name: relaton-bib
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
45
  - - "~>"
60
46
  - !ruby/object:Gem::Version
61
- version: 2.8.0
47
+ version: 1.14.0
62
48
  type: :runtime
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
52
  - - "~>"
67
53
  - !ruby/object:Gem::Version
68
- version: 2.8.0
54
+ version: 1.14.0
69
55
  - !ruby/object:Gem::Dependency
70
- name: relaton-bib
56
+ name: relaton-index
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
59
  - - "~>"
74
60
  - !ruby/object:Gem::Version
75
- version: 1.14.0
61
+ version: 0.2.0
76
62
  type: :runtime
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
66
  - - "~>"
81
67
  - !ruby/object:Gem::Version
82
- version: 1.14.0
68
+ version: 0.2.0
83
69
  description: 'RelatonItu: retrieve ITU Standards for bibliographic use using the BibliographicItem
84
70
  model'
85
71
  email:
@@ -106,6 +92,8 @@ files:
106
92
  - grammars/relaton-itu-compile.rng
107
93
  - grammars/relaton-itu.rng
108
94
  - lib/relaton_itu.rb
95
+ - lib/relaton_itu/data_fetcher.rb
96
+ - lib/relaton_itu/data_parser_r.rb
109
97
  - lib/relaton_itu/editorial_group.rb
110
98
  - lib/relaton_itu/hash_converter.rb
111
99
  - lib/relaton_itu/hit.rb
@@ -114,6 +102,7 @@ files:
114
102
  - lib/relaton_itu/itu_bibliography.rb
115
103
  - lib/relaton_itu/itu_group.rb
116
104
  - lib/relaton_itu/processor.rb
105
+ - lib/relaton_itu/pubid.rb
117
106
  - lib/relaton_itu/scrapper.rb
118
107
  - lib/relaton_itu/structured_identifier.rb
119
108
  - lib/relaton_itu/version.rb
@@ -131,14 +120,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
131
120
  requirements:
132
121
  - - ">="
133
122
  - !ruby/object:Gem::Version
134
- version: 2.5.0
123
+ version: 2.7.0
135
124
  required_rubygems_version: !ruby/object:Gem::Requirement
136
125
  requirements:
137
126
  - - ">="
138
127
  - !ruby/object:Gem::Version
139
128
  version: '0'
140
129
  requirements: []
141
- rubygems_version: 3.1.6
130
+ rubygems_version: 3.3.26
142
131
  signing_key:
143
132
  specification_version: 4
144
133
  summary: 'RelatonItu: retrieve ITU Standards for bibliographic use using the BibliographicItem