relaton-3gpp 1.20.0 → 2.0.0.pre.alpha.1

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.
@@ -1,283 +0,0 @@
1
- module Relaton3gpp
2
- class DataFetcher
3
- CURRENT = "current.yaml".freeze
4
- #
5
- # Data fetcher initializer
6
- #
7
- # @param [String] output directory to save files
8
- # @param [String] format format of output files (xml, yaml, bibxml)
9
- #
10
- def initialize(output, format)
11
- require "fileutils"
12
- require "net/ftp"
13
- require "csv"
14
-
15
- @output = output
16
- @format = format
17
- @ext = format.sub(/^bib/, "")
18
- @files = []
19
- end
20
-
21
- def index
22
- @index ||= Relaton::Index.find_or_create "3gpp", file: "index-v1.yaml"
23
- end
24
-
25
- #
26
- # Initialize fetcher and run fetch
27
- #
28
- # @param [Strin] source source name
29
- # @param [Strin] output directory to save files, default: "data"
30
- # @param [Strin] format format of output files (xml, yaml, bibxml), default: yaml
31
- #
32
- def self.fetch(source, output: "data", format: "yaml")
33
- t1 = Time.now
34
- puts "Started at: #{t1}"
35
- FileUtils.mkdir_p output
36
- new(output, format).fetch(source == "status-smg-3GPP-force")
37
- t2 = Time.now
38
- puts "Stopped at: #{t2}"
39
- puts "Done in: #{(t2 - t1).round} sec."
40
- end
41
-
42
- #
43
- # Parse documents
44
- #
45
- # @param [Boolean] renewal force to update all documents
46
- #
47
- def fetch(renewal) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
48
- file = get_file renewal
49
- return unless file && File.exist?(file) && File.size(file) > 20_000_000
50
-
51
- if renewal
52
- FileUtils.rm_f File.join(@output, "/*") # if renewal && dbs["2001-04-25_schedule"].any?
53
- index.remove_all # if renewal
54
- end
55
- CSV.open(file, "r:bom|utf-8", headers: true).each do |row|
56
- save_doc Parser.parse(row)
57
- end
58
- File.write CURRENT, @current.to_yaml, encoding: "UTF-8"
59
- index.save
60
- end
61
-
62
- #
63
- # Get file from FTP. If file does not exist or changed, return nil
64
- #
65
- # @param [Boolean] renewal force to update all documents
66
- #
67
- # @return [String, nil] file name
68
- #
69
- def get_file(renewal) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
70
- @current = YAML.load_file CURRENT if File.exist? CURRENT
71
- @current ||= {}
72
- n = 0
73
- begin
74
- ftp = Net::FTP.new("www.3gpp.org")
75
- ftp.resume = true
76
- ftp.login
77
- ftp.chdir "/Information/Databases/"
78
- file_path = ftp.list("*.csv").first
79
- return unless file_path
80
-
81
- d, t, _, file = file_path.split
82
- dt = DateTime.strptime("#{d} #{t}", "%m-%d-%y %I:%M%p")
83
- if !renewal && file == @current["file"] && !@current["date"].empty? && dt == DateTime.parse(@current["date"])
84
- return
85
- end
86
-
87
- tmp_file = File.join Dir.tmpdir, "3gpp.csv"
88
- ftp.get(file, tmp_file)
89
- rescue Net::ReadTimeout => e
90
- n += 1
91
- retry if n < 5
92
- raise e
93
- end
94
- @current["file"] = file
95
- @current["date"] = dt.to_s
96
- tmp_file
97
- end
98
-
99
- #
100
- # Fetch document
101
- #
102
- # @param [Hash] row row from mdb
103
- # @param [Array<Hash>] specs specs
104
- # @param [Array<Hash>] specrels specrels
105
- # @param [Array<Hash>] releases releases
106
- # @param [Array<Hash>] tstatus tstatus
107
- #
108
- # @return [Relaton3gpp::BibliographicItem, nil] bibliographic item
109
- #
110
- # def fetch_doc(row, specs, specrels, releases, tstatus)
111
- # doc = Parser.parse row, specs, specrels, releases, tstatus
112
- # save_doc doc
113
- # rescue StandardError => e
114
- # warn "Error: #{e.message}"
115
- # warn "PubID: #{row[:spec]}:#{row[:release]}/#{row[:MAJOR_VERSION_NB]}."\
116
- # "#{row[:TECHNICAL_VERSION_NB]}.#{row[:EDITORIAL_VERSION_NB]}"
117
- # warn e.backtrace[0..5].join("\n")
118
- # end
119
-
120
- #
121
- # Save document to file
122
- #
123
- # @param [RelatonW3c::W3cBibliographicItem, nil] bib bibliographic item
124
- #
125
- def save_doc(bib) # rubocop:disable Metrics/MethodLength
126
- return unless bib
127
-
128
- bib1 = bib
129
- file = file_name(bib1)
130
- if @files.include? file
131
- bib1 = merge_duplication bib1, file
132
- Util.warn "File #{file} already exists. Document: #{bib.docnumber}" if bib1.nil?
133
- else
134
- @files << file
135
- index.add_or_update bib1.docnumber, file
136
- end
137
- File.write file, serialise(bib1), encoding: "UTF-8" unless bib1.nil?
138
- end
139
-
140
- #
141
- # Merge duplication
142
- #
143
- # @param [Relaton3gpp::BibliographicItem] bib new bibliographic item
144
- # @param [String] file file name of existing bibliographic item
145
- #
146
- # @return [Relaton3gpp::BibliographicItem, nil] merged bibliographic item or nil if no merge has been done
147
- #
148
- def merge_duplication(bib, file)
149
- hash = YAML.load_file file
150
- existed = BibliographicItem.from_hash hash
151
- changed = update_link bib, existed
152
- bib1, bib2, chng = transposed_relation bib, existed
153
- changed ||= chng
154
- chng = add_contributor(bib1, bib2)
155
- changed ||= chng
156
- bib1 if changed
157
- end
158
-
159
- #
160
- # Update link in case one of bibliographic items has no link
161
- #
162
- # @param [Relaton3gpp::BibliographicItem] bib1
163
- # @param [Relaton3gpp::BibliographicItem] bib2
164
- #
165
- # @return [Boolean] true if link has been updated
166
- #
167
- def update_link(bib1, bib2)
168
- if bib1.link.any? && bib2.link.empty?
169
- bib2.instance_variable_set(:@link, bib1.link)
170
- true
171
- elsif bib1.link.empty? && bib2.link.any?
172
- bib1.instance_variable_set(:@link, bib2.link)
173
- true
174
- else false
175
- end
176
- end
177
-
178
- #
179
- # If one of bibliographic items has date gereater than anotherm=, make it relation
180
- #
181
- # @param [Relaton3gpp::BibliographicItem] bib new bibliographic item
182
- # @param [Relaton3gpp::BibliographicItem] existed existing bibliographic item
183
- #
184
- # @return [Array<Relaton3gpp::BibliographicItem, Boolean>] main bibliographic item,
185
- # related bibliographic item, true if relation has been added
186
- #
187
- def transposed_relation(bib, existed) # rubocop:disable Metrics/CyclomaticComplexity
188
- return [bib, existed, false] if bib.date.none? && existed.date.none? ||
189
- bib.date.any? && existed.date.none?
190
- return [existed, bib, true] if bib.date.none? && existed.date.any?
191
-
192
- check_transposed_date bib, existed
193
- end
194
-
195
- #
196
- # Check if date of one bibliographic item is transposed to another
197
- #
198
- # @param [Relaton3gpp::BibliographicItem] bib new bibliographic item
199
- # @param [Relaton3gpp::BibliographicItem] existed existing bibliographic item
200
- #
201
- # @return [Array<Relaton3gpp::BibliographicItem, Boolean>] main bibliographic item,
202
- # related bibliographic item, true if relation has been added
203
- #
204
- def check_transposed_date(bib, existed)
205
- if bib.date[0].on < existed.date[0].on
206
- add_transposed_relation bib, existed
207
- [bib, existed, true]
208
- elsif bib.date[0].on > existed.date[0].on
209
- add_transposed_relation existed, bib
210
- [existed, bib, true]
211
- else [bib, existed, false]
212
- end
213
- end
214
-
215
- #
216
- # Add transposed relation
217
- #
218
- # @param [Relaton3gpp::BibliographicItem] bib1 the main bibliographic item
219
- # @param [Relaton3gpp::BibliographicItem] bib2 the transposed bibliographic item
220
- #
221
- # @return [Relaton3gpp::BibliographicItem]
222
- #
223
- def add_transposed_relation(bib1, bib2)
224
- bib2.relation.each { |r| bib1.relation << r }
225
- bib2.instance_variable_set :@relation, RelatonBib::DocRelationCollection.new([])
226
- dec = RelatonBib::FormattedString.new content: "equivalent"
227
- rel = RelatonBib::DocumentRelation.new(type: "adoptedAs", bibitem: bib2, description: dec)
228
- bib1.relation << rel
229
- end
230
-
231
- def add_contributor(bib1, bib2) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
232
- changed = false
233
-
234
- bib2.contributor.each do |bc|
235
- next if bc.entity.is_a? RelatonBib::Organization
236
-
237
- existed = bib1.contributor.find { |ic| ic.entity.name == bc.entity.name }
238
- if existed
239
- chng = add_affiliation existed, bc.entity.affiliation
240
- changed ||= chng
241
- else
242
- bib1.contributor << bc
243
- changed = true
244
- end
245
- end
246
-
247
- changed
248
- end
249
-
250
- def add_affiliation(contrib, affiliation)
251
- changed = false
252
-
253
- affiliation.each do |a|
254
- unless contrib.entity.affiliation.include? a
255
- contrib.entity.affiliation << a
256
- changed = true
257
- end
258
- end
259
-
260
- changed
261
- end
262
-
263
- def serialise(bib)
264
- case @format
265
- when "xml" then bib.to_xml(bibdata: true)
266
- when "yaml" then bib.to_hash.to_yaml
267
- else bib.send("to_#{@format}")
268
- end
269
- end
270
-
271
- #
272
- # Generate file name
273
- #
274
- # @param [RelatonW3c::W3cBibliographicItem] bib bibliographic item
275
- #
276
- # @return [String] file name
277
- #
278
- def file_name(bib)
279
- name = bib.docnumber.gsub(/[\s,:\/]/, "_").squeeze("_").upcase
280
- File.join @output, "#{name}.#{@ext}"
281
- end
282
- end
283
- end
@@ -1,18 +0,0 @@
1
- module Relaton3gpp
2
- class DocumentType < RelatonBib::DocumentType
3
- DOCTYPES = %w[TS TR].freeze
4
-
5
- def initialize(type:, abbreviation: nil)
6
- check type
7
- super
8
- end
9
-
10
- # @param type [String]
11
- def check(type)
12
- unless DOCTYPES.include? type
13
- Util.warn "Unknown doctype: `#{type}`"
14
- Util.warn "Possible doctypes: `#{DOCTYPES.join '`, `'}`"
15
- end
16
- end
17
- end
18
- end
@@ -1,31 +0,0 @@
1
- module Relaton3gpp
2
- module HashConverter
3
- include RelatonBib::HashConverter
4
- extend self
5
-
6
- def hash_to_bib(args)
7
- hash = super
8
- hash[:radiotechnology] = hash[:ext][:radiotechnology] if hash.dig(:ext, :radiotechnology)
9
- hash[:common_ims_spec] = hash[:ext][:"common-ims-spec"] if hash.dig(:ext, :"common-ims-spec")
10
- release_hash_to_bib(hash)
11
- hash
12
- end
13
-
14
- def release_hash_to_bib(hash)
15
- release = hash.dig(:ext, :release) || hash[:release] # @TODO remove hash[:release] after release is moved to ext
16
- return unless release
17
-
18
- hash[:release] = Release.new(**release)
19
- end
20
-
21
- # @param item_hash [Hash]
22
- # @return [Relaton3gpp::BibliographicItem]
23
- def bib_item(item_hash)
24
- BibliographicItem.new(**item_hash)
25
- end
26
-
27
- def create_doctype(**type)
28
- DocumentType.new(**type)
29
- end
30
- end
31
- end
@@ -1,273 +0,0 @@
1
- module Relaton3gpp
2
- class Parser
3
- DOCTYPES = { "TS" => "Technical Specification", "TR" => "Technical Report"}.freeze
4
-
5
- #
6
- # Document parser initalization
7
- #
8
- # @param [CSV::Row] row CSV row
9
- #
10
- def initialize(row)
11
- @row = row
12
- end
13
-
14
- #
15
- # Initialize document parser and run it
16
- #
17
- # @param [CSV:Row] row CSV row
18
- #
19
- # @return [RelatonBib:BibliographicItem, nil] bibliographic item
20
- #
21
- def self.parse(row)
22
- new(row).parse
23
- end
24
-
25
- #
26
- # Parse document
27
- #
28
- # @return [Relaton3gpp:BibliographicItem, nil] bibliographic item
29
- #
30
- def parse # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
31
- Relaton3gpp::BibliographicItem.new(
32
- type: "standard",
33
- language: ["en"],
34
- script: ["Latn"],
35
- title: parse_title,
36
- link: parse_link,
37
- # abstract: parse_abstract,
38
- docid: parse_docid,
39
- docnumber: number,
40
- date: parse_date,
41
- doctype: parse_doctype,
42
- editorialgroup: parse_editorialgroup,
43
- version: parse_version,
44
- # biblionote: parse_note,
45
- # docstatus: parse_status,
46
- radiotechnology: parse_radiotechnology,
47
- # common_ims_spec: @spec[:ComIMS] == "1",
48
- # internal: @spec[:"For publication"] == "0",
49
- release: parse_release,
50
- contributor: parse_contributor,
51
- place: ["Sophia Antipolis Cedex, France"],
52
- )
53
- end
54
-
55
- #
56
- # Parse title
57
- #
58
- # @return [RelatonBib::TypedTitleStringCollection] title
59
- #
60
- def parse_title
61
- t = RelatonBib::TypedTitleString.new content: @row["Title"], type: "main"
62
- RelatonBib::TypedTitleStringCollection.new [t]
63
- end
64
-
65
- #
66
- # Parse link
67
- #
68
- # @return [Array<RelatonBib::TypedUri>] link
69
- #
70
- def parse_link
71
- return [] unless @row["Link"]
72
-
73
- [RelatonBib::TypedUri.new(type: "src", content: @row["Link"])]
74
- end
75
-
76
- #
77
- # Parse abstract
78
- #
79
- # @return [Array<RelatonBib::FormattedString>]
80
- #
81
- # def parse_abstract
82
- # return [] unless @spec[:description]
83
-
84
- # [RelatonBib::FormattedString.new(content: @spec[:description])]
85
- # end
86
-
87
- #
88
- # Parse docidentifier
89
- #
90
- # @return [Arra<RelatonBib::DocumentIdentifier>] docidentifier
91
- #
92
- def parse_docid
93
- [RelatonBib::DocumentIdentifier.new(type: "3GPP", id: "3GPP #{number}", primary: true)]
94
- end
95
-
96
- #
97
- # Generate number
98
- #
99
- # @return [String] number
100
- #
101
- def number
102
- num = "#{doctype_abbr} #{@row[0]}"
103
- num += ":#{release}" if release
104
- "#{num}/#{version}"
105
- end
106
-
107
- def version
108
- @row["Version"]
109
- end
110
-
111
- def parse_version
112
- [RelatonBib::BibliographicItem::Version.new(nil, version)]
113
- end
114
-
115
- def doctype_abbr
116
- @row["Is TS"] == "1" ? "TS" : "TR"
117
- end
118
-
119
- def release
120
- @release ||= case @row["WPM Code 2G"]
121
- when /Release_(\d+)/ then "REL-#{$1}"
122
- when /PH(\d+)/ then "Ph#{$1}"
123
- else @row["Release"]
124
- end
125
- end
126
-
127
- #
128
- # Version
129
- #
130
- # @return [String] version
131
- #
132
- # def version
133
- # "#{@row[:MAJOR_VERSION_NB]}.#{@row[:TECHNICAL_VERSION_NB]}.#{@row[:EDITORIAL_VERSION_NB]}"
134
- # end
135
-
136
- #
137
- # Parse date
138
- #
139
- # @return [Array<RelatonBib::BibliographicDate>] date
140
- #
141
- def parse_date
142
- dates = []
143
- if @row["Date"]
144
- on = Date.parse(@row["Date"]).to_s
145
- dates << RelatonBib::BibliographicDate.new(type: "published", on: on)
146
- end
147
- dates
148
- end
149
-
150
- def parse_doctype
151
- # type = DOCTYPES[doctype_abbr]
152
- DocumentType.new(type: doctype_abbr)
153
- end
154
-
155
- #
156
- # Parse editorialgroup
157
- #
158
- # @return [RelatonBib::EditorialGroup] editorialgroups
159
- #
160
- def parse_editorialgroup # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
161
- eg = []
162
- prime = @row["Responsible Primary"]
163
- eg << create_workgroup(prime, "prime") if prime
164
-
165
- @row["Responsible Secondary"].strip.split(", ").each do |wg|
166
- eg << create_workgroup(wg, "other")
167
- end
168
- RelatonBib::EditorialGroup.new eg
169
- end
170
-
171
- def create_workgroup(name, type)
172
- wgf = RelatonBib::WorkGroup.new(name: name, type: type)
173
- RelatonBib::TechnicalCommittee.new(wgf)
174
- end
175
-
176
- #
177
- # Parse note
178
- #
179
- # @return [RelatonBib::BiblioNoteCollection] notes
180
- #
181
- # def parse_note
182
- # n = []
183
- # if @specrel && @specrel[:remarks] && @specrel[:remarks] != "."
184
- # n << RelatonBib::BiblioNote.new(type: "remark", content: @specrel[:remarks])
185
- # end
186
- # if @row[:comment] && @row[:comment] != "."
187
- # n << RelatonBib::BiblioNote.new(type: "comment", content: @row[:comment])
188
- # end
189
- # RelatonBib::BiblioNoteCollection.new n
190
- # end
191
-
192
- #
193
- # Prase status
194
- #
195
- # @return [RelatnoBib::DocumentStatus, nil] status
196
- #
197
- # def parse_status
198
- # if @specrel && @specrel[:withdrawn] == "1"
199
- # RelatonBib::DocumentStatus.new stage: "withdrawn"
200
- # elsif @spec[:"For publication"] == "1"
201
- # RelatonBib::DocumentStatus.new stage: "published"
202
- # end
203
- # end
204
-
205
- #
206
- # Parse radio technology
207
- #
208
- # @return [String] radio technology
209
- #
210
- def parse_radiotechnology
211
- case @row["WPM Code 3G"]
212
- when /5G/ then "5G"
213
- when /4G/ then "LTE"
214
- when /3G/ then "3G"
215
- else @row["WPM Code 2G"] && "2G"
216
- end
217
- end
218
-
219
- #
220
- # Parse release
221
- #
222
- # @return [Relaton3gpp::Release, nil] release
223
- #
224
- def parse_release # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
225
- project_start = Date.parse(@row["Project Start"]).to_s if @row["Project Start"]
226
- project_end = Date.parse(@row["Project End"]).to_s if @row["Project End"]
227
- Release.new(
228
- # version2g: @rel[:version_2g],
229
- # version3g: @rel[:version_3g],
230
- # defunct: @rel[:defunct] == "1",
231
- wpm_code_2g: @row["WPM Code 2G"],
232
- wpm_code_3g: @row["WPM Code 3G"],
233
- # freeze_meeting: @rel[:"freeze meeting"],
234
- freeze_stage1_meeting: @row["Stage 1 Freeze"],
235
- freeze_stage2_meeting: @row["Stage 2 Freeze"],
236
- freeze_stage3_meeting: @row["Stage 3 Freeze"],
237
- close_meeting: @row["Close Meeting"],
238
- project_start: project_start,
239
- project_end: project_end,
240
- )
241
- end
242
-
243
- #
244
- # Create contributors
245
- #
246
- # @return [Array<RelatonBib::ContributionInfo>] contributor
247
- #
248
- def parse_contributor # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
249
- address = RelatonBib::Address.new(
250
- street: ["c/o ETSI 650, route des Lucioles", "3GPP Mobile Competence Centre"],
251
- postcode: "06921", city: "Sophia Antipolis Cedex", country: "France"
252
- )
253
- org = RelatonBib::Organization.new(
254
- name: "3rd Generation Partnership Project", abbreviation: "3GPP", contact: [address],
255
- )
256
- roles = [{ type: "author" }, { type: "publisher" }]
257
- contribs = [RelatonBib::ContributionInfo.new(entity: org, role: roles)]
258
- return contribs unless @row["Last Name"] && @row["Last Name"] != "Vacant"
259
-
260
- aff = []
261
- if @row["Organisation"]
262
- org = RelatonBib::Organization.new(name: @row["Organisation"])
263
- aff << RelatonBib::Affiliation.new(organization: org)
264
- end
265
- surname = RelatonBib::LocalizedString.new @row["Last Name"], "en", "Latn"
266
- forename = RelatonBib::Forename.new content: @row["First Name"], language: ["en"], script: ["Latn"]
267
- name = RelatonBib::FullName.new(surname: surname, forename: [forename])
268
- person = RelatonBib::Person.new(name: name, affiliation: aff)
269
- role = { type: "author" }
270
- contribs << RelatonBib::ContributionInfo.new(entity: person, role: [role])
271
- end
272
- end
273
- end
@@ -1,61 +0,0 @@
1
- require "relaton/processor"
2
-
3
- module Relaton3gpp
4
- class Processor < Relaton::Processor
5
- attr_reader :idtype
6
-
7
- def initialize # rubocop:disable Lint/MissingSuper
8
- @short = :relaton_3gpp
9
- @prefix = "3GPP"
10
- @defaultprefix = %r{^3GPP\s}
11
- @idtype = "3GPP"
12
- @datasets = %w[status-smg-3GPP status-smg-3GPP-force]
13
- end
14
-
15
- # @param code [String]
16
- # @param date [String, NilClass] year
17
- # @param opts [Hash]
18
- # @return [RelatonBib::BibliographicItem]
19
- def get(code, date, opts)
20
- ::Relaton3gpp::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(source, **opts)
33
- end
34
-
35
- # @param xml [String]
36
- # @return [RelatonBib::BibliographicItem]
37
- def from_xml(xml)
38
- ::Relaton3gpp::XMLParser.from_xml xml
39
- end
40
-
41
- # @param hash [Hash]
42
- # @return [RelatonBib::BibliographicItem]
43
- def hash_to_bib(hash)
44
- item_hash = ::Relaton3gpp::HashConverter.hash_to_bib(hash)
45
- ::Relaton3gpp::BibliographicItem.new(**item_hash)
46
- end
47
-
48
- # Returns hash of XML grammar
49
- # @return [String]
50
- def grammar_hash
51
- @grammar_hash ||= ::Relaton3gpp.grammar_hash
52
- end
53
-
54
- #
55
- # Remove index file
56
- #
57
- def remove_index_file
58
- Relaton::Index.find_or_create("3GPP", url: true, file: Bibliography::INDEX_FILE).remove_file
59
- end
60
- end
61
- end