relaton-etsi 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +12 -0
- data/LICENSE.txt +21 -0
- data/README.adoc +171 -0
- data/Rakefile +12 -0
- data/grammars/basicdoc.rng +1144 -0
- data/grammars/biblio-standoc.rng +164 -0
- data/grammars/biblio.rng +1483 -0
- data/grammars/relaton-etsi-compile.rng +11 -0
- data/grammars/relaton-etsi.rng +121 -0
- data/lib/relaton_etsi/bibliographic_item.rb +75 -0
- data/lib/relaton_etsi/bibliography.rb +48 -0
- data/lib/relaton_etsi/config.rb +10 -0
- data/lib/relaton_etsi/data_fetcher.rb +56 -0
- data/lib/relaton_etsi/data_parser.rb +88 -0
- data/lib/relaton_etsi/document_type.rb +46 -0
- data/lib/relaton_etsi/hash_converter.rb +14 -0
- data/lib/relaton_etsi/processor.rb +61 -0
- data/lib/relaton_etsi/pubid.rb +35 -0
- data/lib/relaton_etsi/util.rb +9 -0
- data/lib/relaton_etsi/version.rb +5 -0
- data/lib/relaton_etsi/xml_parser.rb +44 -0
- data/lib/relaton_etsi.rb +30 -0
- data/sig/relaton_etsi.rbs +4 -0
- metadata +98 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
3
|
+
<include href="basicdoc.rng"/>
|
4
|
+
<include href="relaton-etsi.rng"/>
|
5
|
+
<start>
|
6
|
+
<choice>
|
7
|
+
<ref name="bibitem"/>
|
8
|
+
<ref name="bibdata"/>
|
9
|
+
</choice>
|
10
|
+
</start>
|
11
|
+
</grammar>
|
@@ -0,0 +1,121 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
3
|
+
<include href="biblio-standoc.rng">
|
4
|
+
<define name="BibDataExtensionType">
|
5
|
+
<optional>
|
6
|
+
<attribute name="schema-version"/>
|
7
|
+
</optional>
|
8
|
+
<ref name="doctype"/>
|
9
|
+
<optional>
|
10
|
+
<ref name="docsubtype"/>
|
11
|
+
</optional>
|
12
|
+
<ref name="editorialgroup"/>
|
13
|
+
<ref name="marker"/>
|
14
|
+
<zeroOrMore>
|
15
|
+
<ref name="frequency"/>
|
16
|
+
</zeroOrMore>
|
17
|
+
<zeroOrMore>
|
18
|
+
<ref name="mandate"/>
|
19
|
+
</zeroOrMore>
|
20
|
+
<optional>
|
21
|
+
<ref name="custom-collection"/>
|
22
|
+
</optional>
|
23
|
+
</define>
|
24
|
+
<define name="status">
|
25
|
+
<element name="status">
|
26
|
+
<ref name="stage"/>
|
27
|
+
</element>
|
28
|
+
</define>
|
29
|
+
<define name="stage">
|
30
|
+
<element name="stage">
|
31
|
+
<ref name="StageType"/>
|
32
|
+
</element>
|
33
|
+
</define>
|
34
|
+
<define name="doctype">
|
35
|
+
<element name="doctype">
|
36
|
+
<optional>
|
37
|
+
<attribute name="abbreviation">
|
38
|
+
<ref name="DocumentTypeAbbreviation"/>
|
39
|
+
</attribute>
|
40
|
+
</optional>
|
41
|
+
<ref name="DocumentType"/>
|
42
|
+
</element>
|
43
|
+
</define>
|
44
|
+
<define name="DocumentType">
|
45
|
+
<choice>
|
46
|
+
<value>European Standard</value>
|
47
|
+
<value>ETSI Standard</value>
|
48
|
+
<value>ETSI Guide</value>
|
49
|
+
<value>Technical Specification</value>
|
50
|
+
<value>Group Specification</value>
|
51
|
+
<value>Group Report</value>
|
52
|
+
<value>Technical Report</value>
|
53
|
+
<value>ETSI Technical Report</value>
|
54
|
+
<value>GSM Technical Specification</value>
|
55
|
+
<value>Special Report</value>
|
56
|
+
<value>Technical Committee Reference Technical Report</value>
|
57
|
+
<value>Technical Basis for Regulation</value>
|
58
|
+
<value>European Telecommunication Standard</value>
|
59
|
+
<value>Interim European Telecommunication Standard</value>
|
60
|
+
<value>Norme Européenne de Télécommunication</value>
|
61
|
+
</choice>
|
62
|
+
</define>
|
63
|
+
</include>
|
64
|
+
<define name="StageType">
|
65
|
+
<choice>
|
66
|
+
<value>EN approval</value>
|
67
|
+
<value>SG approval</value>
|
68
|
+
<value>ES approval</value>
|
69
|
+
<value>Published</value>
|
70
|
+
<value>Withdrawn</value>
|
71
|
+
<value>Historical</value>
|
72
|
+
</choice>
|
73
|
+
</define>
|
74
|
+
<define name="DocumentTypeAbbreviation">
|
75
|
+
<choice>
|
76
|
+
<value>EN</value>
|
77
|
+
<value>ES</value>
|
78
|
+
<value>EG</value>
|
79
|
+
<value>TS</value>
|
80
|
+
<value>GS</value>
|
81
|
+
<value>GR</value>
|
82
|
+
<value>TR</value>
|
83
|
+
<value>ETR</value>
|
84
|
+
<value>GTS</value>
|
85
|
+
<value>SR</value>
|
86
|
+
<value>TCRTR</value>
|
87
|
+
<value>TBR</value>
|
88
|
+
<value>ETS</value>
|
89
|
+
<value>I-ETS</value>
|
90
|
+
<value>NET</value>
|
91
|
+
</choice>
|
92
|
+
</define>
|
93
|
+
<define name="marker">
|
94
|
+
<element name="marker">
|
95
|
+
<choice>
|
96
|
+
<value>Current</value>
|
97
|
+
<value>Superseded</value>
|
98
|
+
</choice>
|
99
|
+
</element>
|
100
|
+
</define>
|
101
|
+
<define name="frequency">
|
102
|
+
<element name="frequency">
|
103
|
+
<text/>
|
104
|
+
</element>
|
105
|
+
</define>
|
106
|
+
<define name="mandate">
|
107
|
+
<element name="mandate">
|
108
|
+
<text/>
|
109
|
+
</element>
|
110
|
+
</define>
|
111
|
+
<define name="custom-collection">
|
112
|
+
<element name="custom-collection">
|
113
|
+
<choice>
|
114
|
+
<value>HSs cited in OJ</value>
|
115
|
+
<value>HSs not yet cited in OJ</value>
|
116
|
+
<value>HSs RED cited in OJ</value>
|
117
|
+
<value>HSs EMC cited in OJ</value>
|
118
|
+
</choice>
|
119
|
+
</element>
|
120
|
+
</define>
|
121
|
+
</grammar>
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module RelatonEtsi
|
2
|
+
class BibliographicItem < RelatonBib::BibliographicItem
|
3
|
+
MAKER = %w[Current Superseded].freeze
|
4
|
+
CUSTOM_COLLECTION = ["HSs cited in OJ", "HSs not yet cited in OJ", "HSs RED cited in OJ", "HSs EMC cited in OJ"].freeze
|
5
|
+
|
6
|
+
attr_reader :marker, :frequency, :mandate, :custom_collection
|
7
|
+
|
8
|
+
#
|
9
|
+
# Constructor.
|
10
|
+
#
|
11
|
+
# @param [RelatonEtsi::DocumentType] doctype document type
|
12
|
+
# @param [String] marker current or superseded
|
13
|
+
# @param [Array<String>] frequency list of frequencies
|
14
|
+
# @param [Array<String>] mandate list of mandates
|
15
|
+
# @param [String, nil] custom_collection custom collection
|
16
|
+
#
|
17
|
+
def initialize(**args)
|
18
|
+
@marker = args.delete(:marker)
|
19
|
+
@frequency = args.delete(:frequency) || []
|
20
|
+
@mandate = args.delete(:mandate) || []
|
21
|
+
@custom_collection = args.delete(:custom_collection)
|
22
|
+
super(**args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.from_hash(hash)
|
26
|
+
item_hash = HashConverter.hash_to_bib(hash)
|
27
|
+
new(**item_hash)
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Fetch flavour schema version
|
32
|
+
#
|
33
|
+
# @return [String] flavour schema versio
|
34
|
+
#
|
35
|
+
def ext_schema
|
36
|
+
@ext_schema ||= schema_versions["relaton-model-etsi"]
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_xml(**opts) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
40
|
+
super(**opts) do |b|
|
41
|
+
if opts[:bibdata] && (doctype || subdoctype || editorialgroup || marker || frequency.any? || mandate.any? || custom_collection)
|
42
|
+
ext = b.ext do
|
43
|
+
doctype&.to_xml b
|
44
|
+
b.subdoctype subdoctype if subdoctype
|
45
|
+
editorialgroup&.to_xml b
|
46
|
+
b.marker marker if marker
|
47
|
+
frequency.each { |f| b.frequency f }
|
48
|
+
mandate.each { |m| b.mandate m }
|
49
|
+
b.send("custom-collection", custom_collection) if custom_collection
|
50
|
+
end
|
51
|
+
ext["schema-version"] = ext_schema unless opts[:embedded]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_hash # rubocop:disable Metrics/AbcSize
|
57
|
+
hash = super
|
58
|
+
hash["marker"] = marker if marker
|
59
|
+
hash["frequency"] = frequency if frequency.any?
|
60
|
+
hash["mandate"] = mandate if mandate.any?
|
61
|
+
hash["custom_collection"] = custom_collection if custom_collection
|
62
|
+
hash
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_asciibib(prefix = "") # rubocop:disable Metrics/AbcSize
|
66
|
+
out = super
|
67
|
+
pref = prefix.empty? ? prefix : "#{prefix}."
|
68
|
+
out += "#{pref}marker:: #{marker}\n" if marker
|
69
|
+
out += frequency.map { |f| "#{pref}frequency:: #{f}\n" }.join
|
70
|
+
out += mandate.map { |m| "#{pref}mandate:: #{m}\n" }.join
|
71
|
+
out += "#{pref}custom_collection:: #{custom_collection}" if custom_collection
|
72
|
+
out
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RelatonEtsi
|
4
|
+
# Methods for search IANA standards.
|
5
|
+
module Bibliography
|
6
|
+
SOURCE = "https://raw.githubusercontent.com/relaton/relaton-data-etsi/main/"
|
7
|
+
INDEX_FILE = "index-v1.yaml"
|
8
|
+
|
9
|
+
# @param text [String]
|
10
|
+
# @return [RelatonBib::BibliographicItem]
|
11
|
+
def search(text) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
12
|
+
index = Relaton::Index.find_or_create :etsi, url: "#{SOURCE}index-v1.zip", file: INDEX_FILE
|
13
|
+
row = index.search(text).min_by { |r| r[:id] }
|
14
|
+
return unless row
|
15
|
+
|
16
|
+
url = "#{SOURCE}#{row[:file]}"
|
17
|
+
resp = Net::HTTP.get_response URI(url)
|
18
|
+
return unless resp.code == "200"
|
19
|
+
|
20
|
+
hash = YAML.safe_load resp.body
|
21
|
+
bib_hash = HashConverter.hash_to_bib(hash)
|
22
|
+
bib_hash[:fetched] = Date.today.to_s
|
23
|
+
BibliographicItem.new(**bib_hash)
|
24
|
+
rescue SocketError, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
|
25
|
+
EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
|
26
|
+
Net::ProtocolError, Errno::ETIMEDOUT => e
|
27
|
+
raise RelatonBib::RequestError, e.message
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param ref [String] the ETSI standard Code to look up
|
31
|
+
# @param year [String, nil] year
|
32
|
+
# @param opts [Hash] options
|
33
|
+
# @return [RelatonEtsi::BibliographicItem]
|
34
|
+
def get(ref, _year = nil, _opts = {})
|
35
|
+
Util.warn "(#{ref}) Fetching from Relaton repository ..."
|
36
|
+
result = search(ref)
|
37
|
+
unless result
|
38
|
+
Util.warn "(#{ref}) Not found."
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
Util.warn "(#{ref}) Found: `#{result.docidentifier[0].id}`"
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
extend Bibliography
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module RelatonEtsi
|
2
|
+
class DataFetcher
|
3
|
+
#
|
4
|
+
# Initialize data fetcher.
|
5
|
+
#
|
6
|
+
# @param [String] output output directory
|
7
|
+
# @param [String] format output format (xml, bibxml, yaml)
|
8
|
+
#
|
9
|
+
def initialize(output, format)
|
10
|
+
@output = output
|
11
|
+
@format = format
|
12
|
+
@ext = format.sub(/^bib/, "")
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.fetch(output: "data", format: "yaml")
|
16
|
+
t1 = Time.now
|
17
|
+
puts "Started at: #{t1}"
|
18
|
+
FileUtils.mkdir_p output
|
19
|
+
new(output, format).fetch
|
20
|
+
t2 = Time.now
|
21
|
+
puts "Stopped at: #{t2}"
|
22
|
+
puts "Done in: #{(t2 - t1).round} sec."
|
23
|
+
end
|
24
|
+
|
25
|
+
def index1
|
26
|
+
@index1 ||= Relaton::Index.find_or_create :etsi, file: Bibliography::INDEX_FILE
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch
|
30
|
+
url = "https://www.etsi.org/?option=com_standardssearch&view=data&format=csv&includeScope=1&page=1&search=&" \
|
31
|
+
"title=1&etsiNumber=1&content=1&version=0&onApproval=1&published=1&withdrawn=1&historical=1&isCurrent=1&" \
|
32
|
+
"superseded=1&startDate=1988-01-15&endDate=2023-10-31&harmonized=0&keyword=&TB=&stdType=&frequency=&" \
|
33
|
+
"mandate=&collection=&sort=1&x=1698720135146"
|
34
|
+
csv = OpenURI.open_uri(url) { |f| f.readlines.join }
|
35
|
+
CSV.parse(csv, headers: true, col_sep: ';', skip_lines: /sep=;/).each do |row|
|
36
|
+
save DataParser.new(row).parse
|
37
|
+
end
|
38
|
+
index1.save
|
39
|
+
end
|
40
|
+
|
41
|
+
def save(bib)
|
42
|
+
filename = bib.docidentifier.first.id.gsub(/\//, "-").gsub(/\s|\./, "_").gsub(/\(|\)/, "")
|
43
|
+
file = File.join @output, "#{filename}.#{@ext}"
|
44
|
+
File.write file, content(bib), encoding: "UTF-8"
|
45
|
+
index1.add_or_update bib.docidentifier.first.id, file
|
46
|
+
end
|
47
|
+
|
48
|
+
def content(bib)
|
49
|
+
case @format
|
50
|
+
when "xml" then bib.to_xml bibdata: true
|
51
|
+
when "yaml" then bib.to_hash.to_yaml
|
52
|
+
else bib.send "to_#{@format}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module RelatonEtsi
|
2
|
+
class DataParser
|
3
|
+
ATTRS = %i[id title docnumber link date docid version status keyword editorialgroup
|
4
|
+
doctype abstract language script].freeze
|
5
|
+
|
6
|
+
def initialize(row)
|
7
|
+
@row = row
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse
|
11
|
+
args = ATTRS.each_with_object({}) do |attr, hash|
|
12
|
+
hash[attr] = send(attr)
|
13
|
+
end
|
14
|
+
BibliographicItem.new(**args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def pubid
|
18
|
+
@pubid ||= PubId.parse(@row["ETSI deliverable"])
|
19
|
+
end
|
20
|
+
|
21
|
+
def id
|
22
|
+
@row["ETSI deliverable"].gsub(/[\s\(\)]/, "")
|
23
|
+
end
|
24
|
+
|
25
|
+
def title
|
26
|
+
[RelatonBib::TypedTitleString.new(content: @row["title"], language: "en", script: "Latn")]
|
27
|
+
end
|
28
|
+
|
29
|
+
def docnumber
|
30
|
+
@row["ETSI deliverable"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def link
|
34
|
+
urls = []
|
35
|
+
urls << RelatonBib::TypedUri.new(content: @row["Details link"], type: "src")
|
36
|
+
urls << RelatonBib::TypedUri.new(content: @row["PDF link"], type: "pdf")
|
37
|
+
end
|
38
|
+
|
39
|
+
def date
|
40
|
+
return [] unless pubid.date
|
41
|
+
|
42
|
+
[RelatonBib::BibliographicDate.new(type: "published", on: pubid.date)]
|
43
|
+
end
|
44
|
+
|
45
|
+
def docid
|
46
|
+
[RelatonBib::DocumentIdentifier.new(id: @row["ETSI deliverable"], type: "ETSI", primary: true)]
|
47
|
+
end
|
48
|
+
|
49
|
+
def version
|
50
|
+
return [] unless pubid.version
|
51
|
+
|
52
|
+
[RelatonBib::BibliographicItem::Version.new(nil, pubid.version)]
|
53
|
+
end
|
54
|
+
|
55
|
+
def status
|
56
|
+
status = @row["Status"] == "On Approval" ? "#{pubid.type} approval" : @row["Status"]
|
57
|
+
RelatonBib::DocumentStatus.new(stage: status)
|
58
|
+
end
|
59
|
+
|
60
|
+
def keyword
|
61
|
+
@row["Keywords"].split(",")
|
62
|
+
end
|
63
|
+
|
64
|
+
def editorialgroup
|
65
|
+
wg = RelatonBib::WorkGroup.new name: @row["Technical body"]
|
66
|
+
tc = RelatonBib::TechnicalCommittee.new wg
|
67
|
+
RelatonBib::EditorialGroup.new [tc]
|
68
|
+
end
|
69
|
+
|
70
|
+
def doctype
|
71
|
+
DocumentType.create_from_abbreviation pubid.type
|
72
|
+
end
|
73
|
+
|
74
|
+
def abstract
|
75
|
+
return [] unless @row["Scope"]
|
76
|
+
|
77
|
+
[RelatonBib::FormattedString.new(content: @row["Scope"], language: "en", script: "Latn")]
|
78
|
+
end
|
79
|
+
|
80
|
+
def language
|
81
|
+
["en"]
|
82
|
+
end
|
83
|
+
|
84
|
+
def script
|
85
|
+
["Latn"]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module RelatonEtsi
|
2
|
+
class DocumentType < RelatonBib::DocumentType
|
3
|
+
DOCTYPES = {
|
4
|
+
"EN" => "European Standard",
|
5
|
+
"ES" => "ETSI Standard",
|
6
|
+
"EG" => "ETSI Guide",
|
7
|
+
"TS" => "Technical Specification",
|
8
|
+
"GS" => "Group Specification",
|
9
|
+
"GR" => "Group Report",
|
10
|
+
"TR" => "Technical Report",
|
11
|
+
"ETR" => "ETSI Technical Report",
|
12
|
+
"GTS" => "GSM Technical Specification",
|
13
|
+
"SR" => "Special Report",
|
14
|
+
"TCRTR" => "Technical Committee Reference Technical Report",
|
15
|
+
"TBR" => "Technical Basis for Regulation",
|
16
|
+
"ETS" => "European Telecommunication Standard",
|
17
|
+
"I-ETS" => "Interim European Telecommunication Standard",
|
18
|
+
"NET" => "Norme Européenne de Télécommunication",
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
def initialize(type:, abbreviation: nil)
|
22
|
+
check_type type
|
23
|
+
check_abbreviation abbreviation
|
24
|
+
abbreviation ||= DOCTYPES.key(type)
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.create_from_abbreviation(abbreviation)
|
29
|
+
new type: DOCTYPES[abbreviation], abbreviation: abbreviation
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def check_type(type)
|
35
|
+
return if DOCTYPES.value? type
|
36
|
+
|
37
|
+
Util.warn "WARNING: invalid doctype: `#{type}`"
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_abbreviation(abbreviation)
|
41
|
+
return if abbreviation.nil? || DOCTYPES.key?(abbreviation)
|
42
|
+
|
43
|
+
Util.warn "WARNING: invalid doctype abbreviation: `#{abbreviation}`"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RelatonEtsi
|
2
|
+
module HashConverter
|
3
|
+
include RelatonBib::HashConverter
|
4
|
+
extend self
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
# @param item_hash [Hash]
|
9
|
+
# @return [RelatonEtsi::BibliographicItem]
|
10
|
+
def bib_item(item_hash)
|
11
|
+
BibliographicItem.new(**item_hash)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "relaton/processor"
|
2
|
+
|
3
|
+
module RelatonEtsi
|
4
|
+
class Processor < Relaton::Processor
|
5
|
+
attr_reader :idtype
|
6
|
+
|
7
|
+
def initialize # rubocop:disable Lint/MissingSuper
|
8
|
+
@short = :relaton_etsi
|
9
|
+
@prefix = "ETSI"
|
10
|
+
@defaultprefix = %r{^ETSI\s}
|
11
|
+
@idtype = "ETSI"
|
12
|
+
@datasets = %w[etsi-csv]
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param code [String]
|
16
|
+
# @param date [String, nil] year
|
17
|
+
# @param opts [Hash]
|
18
|
+
# @return [RelatonEtsi::BibliographicItem]
|
19
|
+
def get(code, date, opts)
|
20
|
+
::RelatonEtsi::Bibliography.get(code, date, opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Fetch all the documents from http://xml2rfc.tools.ietf.org/public/rfc/bibxml-3gpp-new/
|
25
|
+
#
|
26
|
+
# @param [String] _source source name
|
27
|
+
# @param [Hash] opts
|
28
|
+
# @option opts [String] :output directory to output documents
|
29
|
+
# @option opts [String] :format
|
30
|
+
#
|
31
|
+
def fetch_data(_source, opts)
|
32
|
+
DataFetcher.fetch(**opts)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param xml [String]
|
36
|
+
# @return [RelatonEtsi::BibliographicItem]
|
37
|
+
def from_xml(xml)
|
38
|
+
::RelatonEtsi::XMLParser.from_xml xml
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param hash [Hash]
|
42
|
+
# @return [RelatonEtsi::BibliographicItem]
|
43
|
+
def hash_to_bib(hash)
|
44
|
+
item_hash = ::RelatonEtsi::HashConverter.hash_to_bib(hash)
|
45
|
+
::RelatonEtsi::BibliographicItem.new(**item_hash)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns hash of XML grammar
|
49
|
+
# @return [String]
|
50
|
+
def grammar_hash
|
51
|
+
@grammar_hash ||= ::RelatonEtsi.grammar_hash
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Remove index file
|
56
|
+
#
|
57
|
+
def remove_index_file
|
58
|
+
Relaton::Index.find_or_create(:etsi, url: true, file: Bibliography::INDEX_FILE).remove_file
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RelatonEtsi
|
2
|
+
class PubId
|
3
|
+
class Parser
|
4
|
+
def initialize(id)
|
5
|
+
@strscan = StringScanner.new id
|
6
|
+
end
|
7
|
+
|
8
|
+
def parse
|
9
|
+
@strscan.scan(/^ETSI\s+/)
|
10
|
+
type = @strscan.scan(/\S+/)
|
11
|
+
@strscan.scan(/\s+/)
|
12
|
+
docnumber = @strscan.scan_until(/(?=\s(V\d+\.\d+\.\d+)|ed\.\d+)/)
|
13
|
+
version = @strscan.scan(/\d+\.\d+\.\d+/) if @strscan.scan(/\sV(?=\d+\.\d+\.\d+)/)
|
14
|
+
edition = @strscan.scan(/\d+/) if @strscan.scan(/\sed\.(?=\d+)/)
|
15
|
+
date = @strscan.scan(/\d{4}-\d{2}/) if @strscan.scan(/\s\(/)
|
16
|
+
|
17
|
+
{ type: type, docnumber: docnumber, version: version, edition: edition, date: date }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :type, :docnumber, :version, :edition, :date
|
22
|
+
|
23
|
+
def initialize(type:, docnumber:, version:, edition:, date:)
|
24
|
+
@type = type
|
25
|
+
@docnumber = docnumber
|
26
|
+
@version = version
|
27
|
+
@edition = edition
|
28
|
+
@date = date
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse(id)
|
32
|
+
new(**Parser.new(id).parse)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RelatonEtsi
|
2
|
+
class XMLParser < RelatonBib::XMLParser
|
3
|
+
class << self
|
4
|
+
private
|
5
|
+
|
6
|
+
# @param intem [Nokogiri::XML::Document]
|
7
|
+
# @return [Hash]
|
8
|
+
def item_data(item) # rubocop:disable Metrics/AbcSize
|
9
|
+
data = super
|
10
|
+
ext = item.at "./ext"
|
11
|
+
return data unless ext
|
12
|
+
|
13
|
+
data[:marker] = ext.at("./marker")&.text
|
14
|
+
data[:frequency] = ext.xpath("./frequency").map(&:text)
|
15
|
+
data[:mandate] = ext.xpath("./mandate").map(&:text)
|
16
|
+
data[:custom_collection] = ext.at("./custom-collection")&.text
|
17
|
+
data
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param item_hash [Hash]
|
21
|
+
# @return [RelatonEtsi::BibliographicItem]
|
22
|
+
def bib_item(item_hash)
|
23
|
+
BibliographicItem.new(**item_hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
# def fetch_status(item)
|
27
|
+
# status = item.at "./status"
|
28
|
+
# return unless status
|
29
|
+
|
30
|
+
# DocumentStatus.new(
|
31
|
+
# stage: status.at("stage")&.text,
|
32
|
+
# substage: status.at("substage")&.text,
|
33
|
+
# iteration: status.at("iteration")&.text,
|
34
|
+
# )
|
35
|
+
# end
|
36
|
+
|
37
|
+
# # @param item [Nokogiri::XML::Element]
|
38
|
+
# # @return [Array<RelatonBib::DocumentRelation>]
|
39
|
+
# def fetch_relations(item)
|
40
|
+
# super item, DocumentRelation
|
41
|
+
# end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/relaton_etsi.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
require "open-uri"
|
5
|
+
require "csv"
|
6
|
+
require "relaton/index"
|
7
|
+
require "relaton_bib"
|
8
|
+
require_relative "relaton_etsi/version"
|
9
|
+
require_relative "relaton_etsi/config"
|
10
|
+
require_relative "relaton_etsi/util"
|
11
|
+
require_relative "relaton_etsi/pubid"
|
12
|
+
require_relative "relaton_etsi/document_type"
|
13
|
+
require_relative "relaton_etsi/bibliographic_item"
|
14
|
+
require_relative "relaton_etsi/xml_parser"
|
15
|
+
require_relative "relaton_etsi/hash_converter"
|
16
|
+
require_relative "relaton_etsi/bibliography"
|
17
|
+
require_relative "relaton_etsi/data_fetcher"
|
18
|
+
require_relative "relaton_etsi/data_parser"
|
19
|
+
|
20
|
+
module RelatonEtsi
|
21
|
+
class Error < StandardError; end
|
22
|
+
|
23
|
+
# Returns hash of gem versions used to generate data model.
|
24
|
+
# @return [String]
|
25
|
+
def grammar_hash
|
26
|
+
Digest::MD5.hexdigest RelatonEtsi::VERSION + RelatonBib::VERSION
|
27
|
+
end
|
28
|
+
|
29
|
+
extend self
|
30
|
+
end
|