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.
@@ -21,7 +21,9 @@
21
21
  <optional>
22
22
  <ref name="docsubtype"/>
23
23
  </optional>
24
- <ref name="editorialgroup"/>
24
+ <optional>
25
+ <ref name="editorialgroup"/>
26
+ </optional>
25
27
  <zeroOrMore>
26
28
  <ref name="ics"/>
27
29
  </zeroOrMore>
@@ -0,0 +1,8 @@
1
+ module Relaton
2
+ module ThreeGpp
3
+ # This class represents a bibliographic item as a bibdata.
4
+ class Bibdata < Item
5
+ include Bib::BibdataShared
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Relaton
2
+ module ThreeGpp
3
+ # This class represents a bibliographic item as a bibitem.
4
+ class Bibitem < Item
5
+ include Bib::BibitemShared
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Relaton
4
+ module ThreeGpp
5
+ # Methods for search IANA standards.
6
+ module Bibliography
7
+ # SOURCE = "http://xml2rfc.tools.ietf.org/public/rfc/bibxml-3gpp-new/"
8
+ SOURCE = "https://raw.githubusercontent.com/relaton/relaton-data-3gpp/refs/heads/data-v2/"
9
+ INDEX_FILE = "index-v1.yaml"
10
+
11
+ # @param text [String]
12
+ # @return [RelatonBib::BibliographicItem]
13
+ def search(text) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
14
+ index = Relaton::Index.find_or_create "3GPP", url: "#{SOURCE}index-v1.zip", file: INDEX_FILE
15
+ row = index.search(text.sub(/^3GPP\s/, "")).min_by { |r| r[:id] }
16
+ return unless row
17
+
18
+ # file = text.sub(/^3GPP\s/, "").gsub(/[\s,:\/]/, "_").squeeze("_").upcase
19
+ url = "#{SOURCE}#{row[:file]}"
20
+ resp = Net::HTTP.get_response URI(url)
21
+ return unless resp.code == "200"
22
+
23
+ item = Item.from_yaml(resp.body)
24
+ item.fetched = Date.today.to_s
25
+ item
26
+ rescue SocketError, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
27
+ EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
28
+ Net::ProtocolError, Errno::ETIMEDOUT => e
29
+ raise Relaton::RequestError, e.message
30
+ end
31
+
32
+ # @param ref [String] the W3C standard Code to look up
33
+ # @param year [String, NilClass] not used
34
+ # @param opts [Hash] options
35
+ # @return [RelatonBib::BibliographicItem]
36
+ def get(ref, _year = nil, _opts = {})
37
+ Util.info "Fetching from Relaton repository ...", key: ref
38
+ result = search(ref)
39
+ unless result
40
+ Util.info "Not found.", key: ref
41
+ return
42
+ end
43
+
44
+ Util.info "Found: `#{result.docidentifier[0].content}`", key: ref
45
+ result
46
+ end
47
+
48
+ extend Bibliography
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,225 @@
1
+ require "fileutils"
2
+ require "net/ftp"
3
+ require_relative "../3gpp"
4
+ require_relative "parser"
5
+
6
+ module Relaton
7
+ module ThreeGpp
8
+ class DataFetcher < Core::DataFetcher
9
+ CURRENT = "current.yaml".freeze
10
+
11
+ def index
12
+ @index ||= Relaton::Index.find_or_create "3gpp", file: Bibliography::INDEX_FILE
13
+ end
14
+
15
+ #
16
+ # Parse documents
17
+ #
18
+ # @param [String] source source of documents, status-smg-3gpp for updare or status-smg-3gpp-force for renewal
19
+ #
20
+ def fetch(source) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
21
+ renewal = source == "status-smg-3GPP-force"
22
+ file = get_file renewal
23
+ return unless file && File.exist?(file) && File.size(file) > 20_000_000
24
+
25
+ if renewal
26
+ FileUtils.rm_f Dir.glob(File.join(@output, "/*")) # if renewal && dbs["2001-04-25_schedule"].any?
27
+ index.remove_all # if renewal
28
+ end
29
+ CSV.open(file, "r:bom|utf-8", headers: true).each do |row|
30
+ save_doc Parser.parse(row)
31
+ end
32
+ File.write CURRENT, @current.to_yaml, encoding: "UTF-8"
33
+ index.save
34
+ end
35
+
36
+ #
37
+ # Get file from FTP. If file does not exist or changed, return nil
38
+ #
39
+ # @param [Boolean] renewal force to update all documents
40
+ #
41
+ # @return [String, nil] file name
42
+ #
43
+ def get_file(renewal) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
44
+ @current = YAML.load_file CURRENT if File.exist? CURRENT
45
+ @current ||= {}
46
+ n = 0
47
+ begin
48
+ ftp = Net::FTP.new("www.3gpp.org")
49
+ ftp.resume = true
50
+ ftp.login
51
+ ftp.chdir "/Information/Databases/"
52
+ file_path = ftp.list("*.csv").first
53
+ return unless file_path
54
+
55
+ d, t, _, file = file_path.split
56
+ dt = DateTime.strptime("#{d} #{t}", "%m-%d-%y %I:%M%p")
57
+ if !renewal && file == @current["file"] && !@current["date"].empty? && dt == DateTime.parse(@current["date"])
58
+ return
59
+ end
60
+
61
+ tmp_file = File.join Dir.tmpdir, "3gpp.csv"
62
+ ftp.get(file, tmp_file)
63
+ rescue Net::ReadTimeout => e
64
+ n += 1
65
+ retry if n < 5
66
+ raise e
67
+ end
68
+ @current["file"] = file
69
+ @current["date"] = dt.to_s
70
+ tmp_file
71
+ end
72
+
73
+ #
74
+ # Save document to file
75
+ #
76
+ # @param [RelatonW3c::W3cBibliographicItem, nil] bib bibliographic item
77
+ #
78
+ def save_doc(bib) # rubocop:disable Metrics/MethodLength
79
+ return unless bib
80
+
81
+ bib1 = bib
82
+ file = output_file(bib1.docnumber)
83
+ if @files.include? file
84
+ bib1 = merge_duplication bib1, file
85
+ Util.warn "File #{file} already exists. Document: #{bib.docnumber}" if bib1.nil?
86
+ else
87
+ @files << file
88
+ index.add_or_update bib1.docnumber, file
89
+ end
90
+ File.write file, serialize(bib1), encoding: "UTF-8" unless bib1.nil?
91
+ end
92
+
93
+ #
94
+ # Merge duplication
95
+ #
96
+ # @param [Relaton3gpp::BibliographicItem] bib new bibliographic item
97
+ # @param [String] file file name of existing bibliographic item
98
+ #
99
+ # @return [Relaton3gpp::BibliographicItem, nil] merged bibliographic item or nil if no merge has been done
100
+ #
101
+ def merge_duplication(bib, file)
102
+ hash = YAML.load_file file
103
+ existed = Item.from_hash hash
104
+ changed = update_source bib, existed
105
+ bib1, bib2, chng = transposed_relation bib, existed
106
+ changed ||= chng
107
+ chng = add_contributor(bib1, bib2)
108
+ changed ||= chng
109
+ bib1 if changed
110
+ end
111
+
112
+ #
113
+ # Update link in case one of bibliographic items has no link
114
+ #
115
+ # @param [Relaton3gpp::BibliographicItem] bib1
116
+ # @param [Relaton3gpp::BibliographicItem] bib2
117
+ #
118
+ # @return [Boolean] true if link has been updated
119
+ #
120
+ def update_source(bib1, bib2)
121
+ if bib1.source.any? && bib2.source.empty?
122
+ bib2.source = bib1.source
123
+ true
124
+ elsif bib1.source.empty? && bib2.source.any?
125
+ bib1.source = bib2.source
126
+ true
127
+ else false
128
+ end
129
+ end
130
+
131
+ #
132
+ # If one of bibliographic items has date gereater than anotherm=, make it relation
133
+ #
134
+ # @param [Relaton3gpp::BibliographicItem] bib new bibliographic item
135
+ # @param [Relaton3gpp::BibliographicItem] existed existing bibliographic item
136
+ #
137
+ # @return [Array<Relaton3gpp::BibliographicItem, Boolean>] main bibliographic item,
138
+ # related bibliographic item, true if relation has been added
139
+ #
140
+ def transposed_relation(bib, existed) # rubocop:disable Metrics/CyclomaticComplexity
141
+ return [bib, existed, false] if bib.date.none? && existed.date.none? ||
142
+ bib.date.any? && existed.date.none?
143
+ return [existed, bib, true] if bib.date.none? && existed.date.any?
144
+
145
+ check_transposed_date bib, existed
146
+ end
147
+
148
+ #
149
+ # Check if date of one bibliographic item is transposed to another
150
+ #
151
+ # @param [Relaton3gpp::BibliographicItem] bib new bibliographic item
152
+ # @param [Relaton3gpp::BibliographicItem] existed existing bibliographic item
153
+ #
154
+ # @return [Array<Relaton3gpp::BibliographicItem, Boolean>] main bibliographic item,
155
+ # related bibliographic item, true if relation has been added
156
+ #
157
+ def check_transposed_date(bib, existed)
158
+ if bib.date[0].at < existed.date[0].at
159
+ add_transposed_relation bib, existed
160
+ [bib, existed, true]
161
+ elsif bib.date[0].at > existed.date[0].at
162
+ add_transposed_relation existed, bib
163
+ [existed, bib, true]
164
+ else [bib, existed, false]
165
+ end
166
+ end
167
+
168
+ #
169
+ # Add transposed relation
170
+ #
171
+ # @param [Relaton3gpp::BibliographicItem] bib1 the main bibliographic item
172
+ # @param [Relaton3gpp::BibliographicItem] bib2 the transposed bibliographic item
173
+ #
174
+ # @return [Relaton3gpp::BibliographicItem]
175
+ #
176
+ def add_transposed_relation(bib1, bib2)
177
+ bib2.relation.each { |r| bib1.relation << r }
178
+ bib2.instance_variable_set :@relation, []
179
+ desc = Bib::LocalizedMarkedUpString.new content: "equivalent"
180
+ rel = Bib::Relation.new(type: "adoptedAs", bibitem: bib2, description: desc)
181
+ bib1.relation << rel
182
+ end
183
+
184
+ def add_contributor(bib1, bib2) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
185
+ changed = false
186
+
187
+ bib2.contributor.each do |bc|
188
+ next unless bc.person
189
+
190
+ existed = bib1.contributor.find { |ic| ic.person&.name == bc.person.name }
191
+ if existed
192
+ chng = add_affiliation existed, bc.person.affiliation
193
+ changed ||= chng
194
+ else
195
+ bib1.contributor << bc
196
+ changed = true
197
+ end
198
+ end
199
+
200
+ changed
201
+ end
202
+
203
+ def add_affiliation(contrib, affiliation)
204
+ changed = false
205
+
206
+ affiliation.each do |a|
207
+ unless contrib.person.affiliation.include? a
208
+ contrib.person.affiliation << a
209
+ changed = true
210
+ end
211
+ end
212
+
213
+ changed
214
+ end
215
+
216
+ def to_xml(bib)
217
+ Bibdata.to_xml(bib)
218
+ end
219
+
220
+ def to_yaml(bib)
221
+ Item.to_yaml(bib)
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,9 @@
1
+ module Relaton
2
+ module ThreeGpp
3
+ class Doctype < Relaton::Bib::Doctype
4
+ TYPES = %w[TR TS].freeze
5
+
6
+ attribute :content, :string, values: TYPES
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,34 @@
1
+ require_relative "doctype"
2
+ require_relative "release"
3
+
4
+ module Relaton
5
+ module ThreeGpp
6
+ class Ext < Lutaml::Model::Serializable
7
+ attribute :schema_version, method: :get_schema_version
8
+ attribute :doctype, Doctype
9
+ attribute :subdoctype, :string, values: %w[spec release]
10
+ attribute :editorialgroup, Relaton::Bib::EditorialGroup
11
+ attribute :ics, Relaton::Bib::ICS, collection: true
12
+ attribute :radiotechnology, :string, values: %w[2G 3G LTE 5G]
13
+ attribute :common_ims_spec, :boolean
14
+ attribute :internal, :boolean
15
+ attribute :release, Release
16
+
17
+ def get_schema_version
18
+ Relaton.schema_versions["relaton-model-3gpp"]
19
+ end
20
+
21
+ xml do
22
+ map_attribute "schema-version", to: :schema_version
23
+ map_element "doctype", to: :doctype
24
+ map_element "subdoctype", to: :subdoctype
25
+ map_element "editorialgroup", to: :editorialgroup
26
+ map_element "ics", to: :ics
27
+ map_element "radiotechnology", to: :radiotechnology
28
+ map_element "common-ims-spec", to: :common_ims_spec
29
+ map_element "internal", to: :internal
30
+ map_element "release", to: :release
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,12 @@
1
+ require_relative "item_data"
2
+ require_relative "ext"
3
+
4
+ module Relaton
5
+ module ThreeGpp
6
+ class Item < Relaton::Bib::Item
7
+ model ItemData
8
+
9
+ attribute :ext, Ext
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ module Relaton
2
+ module ThreeGpp
3
+ class ItemData < Relaton::Bib::ItemData
4
+ def to_xml(bibdata: false, **opts)
5
+ add_notes opts[:note] do
6
+ bibdata ? Bibdata.to_xml(self) : Bibitem.to_xml(self)
7
+ end
8
+ end
9
+
10
+ def to_yaml(**opts)
11
+ add_notes opts[:note] do
12
+ Item.to_yaml(self)
13
+ end
14
+ end
15
+
16
+ def to_json(**opts)
17
+ add_notes opts[:note] do
18
+ Item.to_json(self)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,304 @@
1
+ require "csv"
2
+
3
+ module Relaton
4
+ module ThreeGpp
5
+ class Parser
6
+ # DOCTYPES = { "TS" => "Technical Specification", "TR" => "Technical Report" }.freeze
7
+
8
+ #
9
+ # Document parser initalization
10
+ #
11
+ # @param [CSV::Row] row CSV row
12
+ #
13
+ def initialize(row)
14
+ @row = row
15
+ end
16
+
17
+ #
18
+ # Initialize document parser and run it
19
+ #
20
+ # @param [CSV:Row] row CSV row
21
+ #
22
+ # @return [RelatonBib:BibliographicItem, nil] bibliographic item
23
+ #
24
+ def self.parse(row)
25
+ new(row).parse
26
+ end
27
+
28
+ #
29
+ # Parse document
30
+ #
31
+ # @return [Relaton3gpp:BibliographicItem, nil] bibliographic item
32
+ #
33
+ def parse # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
34
+ ItemData.new(
35
+ id: parse_id,
36
+ type: "standard",
37
+ language: ["en"],
38
+ script: ["Latn"],
39
+ title: parse_title,
40
+ source: parse_source,
41
+ # abstract: parse_abstract,
42
+ docidentifier: parse_docid,
43
+ docnumber: number,
44
+ date: parse_date,
45
+ version: parse_version,
46
+ contributor: parse_contributor,
47
+ place: parse_place,
48
+ ext: parse_ext,
49
+ # biblionote: parse_note,
50
+ # docstatus: parse_status,
51
+ # common_ims_spec: @spec[:ComIMS] == "1",
52
+ # internal: @spec[:"For publication"] == "0",
53
+ )
54
+ end
55
+
56
+ def parse_id
57
+ number.gsub(/[\s:\/]/, "-").squeeze("-").upcase
58
+ end
59
+
60
+ #
61
+ # Parse title
62
+ #
63
+ # @return [RelatonBib::TypedTitleStringCollection] title
64
+ #
65
+ def parse_title
66
+ [Bib::Title.new(content: @row["Title"], type: "main")]
67
+ # RelatonBib::TypedTitleStringCollection.new [t]
68
+ end
69
+
70
+ #
71
+ # Parse link
72
+ #
73
+ # @return [Array<RelatonBib::TypedUri>] link
74
+ #
75
+ def parse_source
76
+ return [] unless @row["Link"]
77
+
78
+ [Bib::Uri.new(type: "src", content: @row["Link"])]
79
+ end
80
+
81
+ #
82
+ # Parse abstract
83
+ #
84
+ # @return [Array<RelatonBib::FormattedString>]
85
+ #
86
+ # def parse_abstract
87
+ # return [] unless @spec[:description]
88
+
89
+ # [RelatonBib::FormattedString.new(content: @spec[:description])]
90
+ # end
91
+
92
+ #
93
+ # Parse docidentifier
94
+ #
95
+ # @return [Arra<RelatonBib::DocumentIdentifier>] docidentifier
96
+ #
97
+ def parse_docid
98
+ [Bib::Docidentifier.new(type: "3GPP", content: "3GPP #{number}", primary: true)]
99
+ end
100
+
101
+ #
102
+ # Generate number
103
+ #
104
+ # @return [String] number
105
+ #
106
+ def number
107
+ num = "#{doctype_abbr} #{@row[0]}"
108
+ num += ":#{release}" if release
109
+ "#{num}/#{version}"
110
+ end
111
+
112
+ def version
113
+ @row["Version"]
114
+ end
115
+
116
+ def parse_version
117
+ [Bib::Version.new(draft: version)]
118
+ end
119
+
120
+ def doctype_abbr
121
+ @row["Is TS"] == "1" ? "TS" : "TR"
122
+ end
123
+
124
+ def release
125
+ @release ||= case @row["WPM Code 2G"]
126
+ when /Release_(\d+)/ then "REL-#{$1}"
127
+ when /PH(\d+)/ then "Ph#{$1}"
128
+ else @row["Release"]
129
+ end
130
+ end
131
+
132
+ #
133
+ # Version
134
+ #
135
+ # @return [String] version
136
+ #
137
+ # def version
138
+ # "#{@row[:MAJOR_VERSION_NB]}.#{@row[:TECHNICAL_VERSION_NB]}.#{@row[:EDITORIAL_VERSION_NB]}"
139
+ # end
140
+
141
+ #
142
+ # Parse date
143
+ #
144
+ # @return [Array<RelatonBib::BibliographicDate>] date
145
+ #
146
+ def parse_date
147
+ dates = []
148
+ if @row["Date"]
149
+ on = Date.parse(@row["Date"]).to_s
150
+ dates << Bib::Date.new(type: "published", at: on)
151
+ end
152
+ dates
153
+ end
154
+
155
+ #
156
+ # Parse note
157
+ #
158
+ # @return [RelatonBib::BiblioNoteCollection] notes
159
+ #
160
+ # def parse_note
161
+ # n = []
162
+ # if @specrel && @specrel[:remarks] && @specrel[:remarks] != "."
163
+ # n << RelatonBib::BiblioNote.new(type: "remark", content: @specrel[:remarks])
164
+ # end
165
+ # if @row[:comment] && @row[:comment] != "."
166
+ # n << RelatonBib::BiblioNote.new(type: "comment", content: @row[:comment])
167
+ # end
168
+ # RelatonBib::BiblioNoteCollection.new n
169
+ # end
170
+
171
+ #
172
+ # Prase status
173
+ #
174
+ # @return [RelatnoBib::DocumentStatus, nil] status
175
+ #
176
+ # def parse_status
177
+ # if @specrel && @specrel[:withdrawn] == "1"
178
+ # RelatonBib::DocumentStatus.new stage: "withdrawn"
179
+ # elsif @spec[:"For publication"] == "1"
180
+ # RelatonBib::DocumentStatus.new stage: "published"
181
+ # end
182
+ # end
183
+
184
+ #
185
+ # Create contributors
186
+ #
187
+ # @return [Array<RelatonBib::ContributionInfo>] contributor
188
+ #
189
+ def parse_contributor # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
190
+ name = Bib::TypedLocalizedString.new(content: "3rd Generation Partnership Project")
191
+ abbrev = Bib::LocalizedString.new content: "3GPP"
192
+ org = Bib::Organization.new(name: [name], abbreviation: abbrev, address: [address])
193
+ contribs = [Bib::Contributor.new(organization: org, role: contributor_role)]
194
+ return contribs unless @row["Last Name"] && @row["Last Name"] != "Vacant"
195
+
196
+ role = Bib::Contributor::Role.new type: "author"
197
+ contribs << Bib::Contributor.new(person: person, role: [role])
198
+ end
199
+
200
+ def address
201
+ Bib::Address.new(
202
+ street: ["c/o ETSI 650, route des Lucioles", "3GPP Mobile Competence Centre"],
203
+ postcode: "06921", city: "Sophia Antipolis Cedex", country: "France"
204
+ )
205
+ end
206
+
207
+ def contributor_role
208
+ [Bib::Contributor::Role.new(type: "author"), Bib::Contributor::Role.new(type: "publisher")]
209
+ end
210
+
211
+ def person
212
+ surname = Bib::LocalizedString.new content: @row["Last Name"], language: "en", script: "Latn"
213
+ forename = Bib::FullNameType::Forename.new content: @row["First Name"], language: "en", script: "Latn"
214
+ name = Bib::FullName.new(surname: surname, forename: [forename])
215
+ Bib::Person.new(name: name, affiliation: affiliation)
216
+ end
217
+
218
+ def affiliation
219
+ return [] if @row["Organisation"].nil? || @row["Organisation"].empty?
220
+
221
+ name = Bib::TypedLocalizedString.new(content: @row["Organisation"])
222
+ org = Bib::Organization.new(name: [name])
223
+ [Bib::Affiliation.new(organization: org)]
224
+ end
225
+
226
+ def parse_place
227
+ [Bib::Place.new(formatted_place: "Sophia Antipolis Cedex, France")]
228
+ end
229
+
230
+ def parse_ext
231
+ Ext.new(
232
+ doctype: parse_doctype,
233
+ editorialgroup: parse_editorialgroup,
234
+ radiotechnology: parse_radiotechnology,
235
+ release: parse_release,
236
+ )
237
+ end
238
+
239
+ def parse_doctype
240
+ # type = DOCTYPES[doctype_abbr]
241
+ # Doctype.new(abbreviation: doctype_abbr, content: type)
242
+ Doctype.new(content: doctype_abbr) # 3GPP grammar alloves only TS and TR content
243
+ end
244
+
245
+ #
246
+ # Parse editorialgroup
247
+ #
248
+ # @return [RelatonBib::EditorialGroup] editorialgroups
249
+ #
250
+ def parse_editorialgroup # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
251
+ eg = []
252
+ prime = @row["Responsible Primary"]
253
+ eg << create_workgroup(prime, "prime") unless prime.nil? || prime.empty?
254
+
255
+ @row["Responsible Secondary"].strip.split(", ").each do |wg|
256
+ eg << create_workgroup(wg, "other")
257
+ end
258
+ Bib::EditorialGroup.new technical_committee: eg
259
+ end
260
+
261
+ def create_workgroup(name, type)
262
+ Bib::WorkGroup.new(content: name, type: type)
263
+ end
264
+
265
+ #
266
+ # Parse radio technology
267
+ #
268
+ # @return [String] radio technology
269
+ #
270
+ def parse_radiotechnology
271
+ case @row["WPM Code 3G"]
272
+ when /5G/ then "5G"
273
+ when /4G/ then "LTE"
274
+ when /3G/ then "3G"
275
+ else @row["WPM Code 2G"] && "2G"
276
+ end
277
+ end
278
+
279
+ #
280
+ # Parse release
281
+ #
282
+ # @return [Relaton3gpp::Release, nil] release
283
+ #
284
+ def parse_release # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
285
+ project_start = Date.parse(@row["Project Start"]) if @row["Project Start"]
286
+ project_end = Date.parse(@row["Project End"]) if @row["Project End"]
287
+ Release.new(
288
+ # version2g: @rel[:version_2g],
289
+ # version3g: @rel[:version_3g],
290
+ # defunct: @rel[:defunct] == "1",
291
+ wpm_code_2g: @row["WPM Code 2G"],
292
+ wpm_code_3g: @row["WPM Code 3G"],
293
+ # freeze_meeting: @rel[:"freeze meeting"],
294
+ freeze_stage1_meeting: @row["Stage 1 Freeze"],
295
+ freeze_stage2_meeting: @row["Stage 2 Freeze"],
296
+ freeze_stage3_meeting: @row["Stage 3 Freeze"],
297
+ close_meeting: @row["Close Meeting"],
298
+ project_start: project_start,
299
+ project_end: project_end,
300
+ )
301
+ end
302
+ end
303
+ end
304
+ end