relaton-etsi 1.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|