fbo 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +2 -2
- data/README.md +23 -0
- data/lib/fbo.rb +17 -0
- data/lib/fbo/notices/archive.rb +8 -0
- data/lib/fbo/notices/fair_opportunity.rb +15 -0
- data/lib/fbo/notices/foreign_standard.rb +13 -0
- data/lib/fbo/notices/intent_to_bundle.rb +13 -0
- data/lib/fbo/notices/justification_approval.rb +18 -0
- data/lib/fbo/notices/sale_of_surplus.rb +13 -0
- data/lib/fbo/notices/special_notice.rb +13 -0
- data/lib/fbo/notices/unarchive.rb +9 -0
- data/lib/fbo/parser.rb +2 -15
- data/lib/fbo/parser/archive_handler.rb +41 -0
- data/lib/fbo/parser/fair_opportunity_handler.rb +61 -0
- data/lib/fbo/parser/foreign_standard_handler.rb +57 -0
- data/lib/fbo/parser/handler_selector.rb +37 -0
- data/lib/fbo/parser/intent_to_bundle_handler.rb +56 -0
- data/lib/fbo/parser/justification_approval_handler.rb +60 -0
- data/lib/fbo/parser/parser_helper.rb +40 -0
- data/lib/fbo/parser/sale_of_surplus_handler.rb +57 -0
- data/lib/fbo/parser/special_notice_handler.rb +57 -0
- data/lib/fbo/parser/unarchive_handler.rb +42 -0
- data/lib/fbo/version.rb +1 -1
- data/spec/fbo/parser/amendment_handler_spec.rb +28 -23
- data/spec/fbo/parser/archive_handler_spec.rb +36 -0
- data/spec/fbo/parser/award_handler_spec.rb +5 -0
- data/spec/fbo/parser/combined_solicitation_handler_spec.rb +4 -0
- data/spec/fbo/parser/fair_opportunity_handler_spec.rb +56 -0
- data/spec/fbo/parser/foreign_standard_handler_spec.rb +51 -0
- data/spec/fbo/parser/handler_selector_spec.rb +40 -0
- data/spec/fbo/parser/intent_to_bundle_handler_spec.rb +52 -0
- data/spec/fbo/parser/justification_approval_handler_spec.rb +53 -0
- data/spec/fbo/parser/modification_handler_spec.rb +5 -0
- data/spec/fbo/parser/presolicitation_handler_spec.rb +5 -0
- data/spec/fbo/parser/sale_of_surplus_handler_spec.rb +53 -0
- data/spec/fbo/parser/sources_sought_handler_spec.rb +27 -22
- data/spec/fbo/parser/special_notice_handler_spec.rb +53 -0
- data/spec/fbo/parser/unarchive_handler_spec.rb +37 -0
- data/spec/fbo/parser/unknown_handler_spec.rb +5 -0
- data/spec/fbo/parser_spec.rb +18 -49
- data/spec/fixtures/notices/archive +10 -0
- data/spec/fixtures/notices/fairopp +29 -0
- data/spec/fixtures/notices/fstd +24 -0
- data/spec/fixtures/notices/itb +19 -0
- data/spec/fixtures/notices/ja +30 -0
- data/spec/fixtures/notices/notanotice +7 -0
- data/spec/fixtures/notices/ssale +24 -0
- data/spec/fixtures/notices/unarchive +10 -0
- metadata +53 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0c4e53ddeb9ea9df943ef8af9ca33a4e1f99bbc
|
4
|
+
data.tar.gz: bc42c21126580a453a02996fa1799375d7057339
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02ea8f7e4d11ba748b01abbe212952c958cb8e20f95b690dfd4b1da657e7bbdb6f744728395b8c2a3c523b8208d5f2560ff57811e3cc92dd2f27b2654d6dada7
|
7
|
+
data.tar.gz: 02ec6b226bbf7c58391fb0073c28bedb6635ad96dc73676f4c353511e585d6554cf27f3226a05c7e6cf6ac7f7c748c8e70703a4728e4e03d4b9fe3c30600e468
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -3,6 +3,29 @@
|
|
3
3
|
Fetch a dated dump file from [FedBizOpps](https://www.fbo.gov) and parse its
|
4
4
|
contents into structures suitable for use by other programs.
|
5
5
|
|
6
|
+
The gem in its current state handles most of the notice types described in the
|
7
|
+
[FBO documentation](https://www.fbo.gov/?&static=interface) for the data format.
|
8
|
+
* Presolicitation (PRESOL)
|
9
|
+
* Combined Synopsis/Solicitation (COMBINE)
|
10
|
+
* Amendment to a Previous Combined Solicitation (AMDCSS)
|
11
|
+
* Modification to a Previous Base (MOD)
|
12
|
+
* Award (AWARD)
|
13
|
+
* Justification and Approval (JA)
|
14
|
+
* Intent to Bundle Requirements (ITB)
|
15
|
+
* Fair Opportunity / Limited Sources Justification (FAIROPP)
|
16
|
+
* Sources Sought (SRCSGT)
|
17
|
+
* Foreign Government Standard (FSTD)
|
18
|
+
* Special Notice (SNOTE)
|
19
|
+
* Sale of Surplus Property (SSALE)
|
20
|
+
* Document Archival (ARCHIVE)
|
21
|
+
* Document Unarchival (UNARCHIVE)
|
22
|
+
|
23
|
+
Two notice types have not yet been implemented as no record of their ever having
|
24
|
+
been used could be found in any of the FBO dump files dating back as far as 2001.
|
25
|
+
These are:
|
26
|
+
* Document Upload (EPSUPLOAD)
|
27
|
+
* Document Deleting (DELETE)
|
28
|
+
|
6
29
|
|
7
30
|
## Installation
|
8
31
|
|
data/lib/fbo.rb
CHANGED
@@ -4,21 +4,38 @@ require_relative "fbo/file"
|
|
4
4
|
require_relative "fbo/remote_file"
|
5
5
|
require_relative "fbo/notice"
|
6
6
|
require_relative "fbo/notices/amendment"
|
7
|
+
require_relative "fbo/notices/archive"
|
7
8
|
require_relative "fbo/notices/award"
|
8
9
|
require_relative "fbo/notices/combined_solicitation"
|
10
|
+
require_relative "fbo/notices/fair_opportunity"
|
11
|
+
require_relative "fbo/notices/foreign_standard"
|
12
|
+
require_relative "fbo/notices/intent_to_bundle"
|
13
|
+
require_relative "fbo/notices/justification_approval"
|
9
14
|
require_relative "fbo/notices/modification"
|
10
15
|
require_relative "fbo/notices/presolicitation"
|
16
|
+
require_relative "fbo/notices/sale_of_surplus"
|
11
17
|
require_relative "fbo/notices/sources_sought"
|
18
|
+
require_relative "fbo/notices/special_notice"
|
19
|
+
require_relative "fbo/notices/unarchive"
|
12
20
|
require_relative "fbo/notices/unknown"
|
13
21
|
require_relative "fbo/parser"
|
22
|
+
require_relative "fbo/parser/handler_selector"
|
14
23
|
require_relative "fbo/parser/notice_handler"
|
15
24
|
require_relative "fbo/parser/parser_helper"
|
16
25
|
require_relative "fbo/parser/amendment_handler"
|
26
|
+
require_relative "fbo/parser/archive_handler"
|
17
27
|
require_relative "fbo/parser/award_handler"
|
18
28
|
require_relative "fbo/parser/combined_solicitation_handler"
|
29
|
+
require_relative "fbo/parser/fair_opportunity_handler"
|
30
|
+
require_relative "fbo/parser/foreign_standard_handler"
|
31
|
+
require_relative "fbo/parser/intent_to_bundle_handler"
|
32
|
+
require_relative "fbo/parser/justification_approval_handler"
|
19
33
|
require_relative "fbo/parser/modification_handler"
|
20
34
|
require_relative "fbo/parser/presolicitation_handler"
|
35
|
+
require_relative "fbo/parser/sale_of_surplus_handler"
|
21
36
|
require_relative "fbo/parser/sources_sought_handler"
|
37
|
+
require_relative "fbo/parser/special_notice_handler"
|
38
|
+
require_relative "fbo/parser/unarchive_handler"
|
22
39
|
require_relative "fbo/parser/unknown_handler"
|
23
40
|
|
24
41
|
module FBO
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module FBO
|
2
|
+
module Notices
|
3
|
+
class FairOpportunity < Struct.new(:date, :year, :zip, :class_code, :naics_code,
|
4
|
+
:agency, :office, :location, :office_address,
|
5
|
+
:subject, :solicitation_number, :notice_type,
|
6
|
+
:justification_authority, :award_number,
|
7
|
+
:delivery_order_number, :modification_number,
|
8
|
+
:award_date, :archive_date, :description,
|
9
|
+
:contact_info, :link_url, :link_description,
|
10
|
+
:email_address, :email_description, :file_list,
|
11
|
+
:correction)
|
12
|
+
include FBO::Notice
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module FBO
|
2
|
+
module Notices
|
3
|
+
class ForeignStandard < Struct.new(:date, :year, :zip, :class_code, :naics_code,
|
4
|
+
:agency, :office, :location, :office_address,
|
5
|
+
:subject, :solicitation_number, :archive_date,
|
6
|
+
:response_date, :contact_info, :description,
|
7
|
+
:link_url, :link_description, :email_address,
|
8
|
+
:email_description, :setaside, :pop_address,
|
9
|
+
:pop_zip, :pop_country)
|
10
|
+
include FBO::Notice
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module FBO
|
2
|
+
module Notices
|
3
|
+
class IntentToBundle < Struct.new(:date, :year, :zip, :class_code, :naics_code,
|
4
|
+
:agency, :office, :location, :office_address,
|
5
|
+
:subject, :solicitation_number, :notice_type,
|
6
|
+
:archive_date, :description, :contact_info,
|
7
|
+
:award_number, :delivery_order_number,
|
8
|
+
:link_url, :link_description, :email_address,
|
9
|
+
:email_description, :correction)
|
10
|
+
include FBO::Notice
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FBO
|
2
|
+
module Notices
|
3
|
+
class JustificationApproval < Struct.new(:date, :year, :zip, :class_code,
|
4
|
+
:naics_code, :agency, :office,
|
5
|
+
:location, :office_address, :subject,
|
6
|
+
:solicitation_number, :notice_type,
|
7
|
+
:description, :contact_info,
|
8
|
+
:statutory_authority, :award_number,
|
9
|
+
:modification_number, :award_date,
|
10
|
+
:archive_date, :link_url,
|
11
|
+
:link_description, :email_address,
|
12
|
+
:email_description, :file_list,
|
13
|
+
:correction)
|
14
|
+
include FBO::Notice
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module FBO
|
2
|
+
module Notices
|
3
|
+
class SaleOfSurplus < Struct.new(:date, :year, :zip, :class_code, :naics_code,
|
4
|
+
:agency, :office, :location, :office_address,
|
5
|
+
:subject, :solicitation_number, :response_date,
|
6
|
+
:archive_date, :description, :contact_info,
|
7
|
+
:link_url, :link_description, :email_address,
|
8
|
+
:email_description, :setaside, :pop_country,
|
9
|
+
:pop_zip, :pop_address)
|
10
|
+
include FBO::Notice
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module FBO
|
2
|
+
module Notices
|
3
|
+
class SpecialNotice < Struct.new(:date, :year, :zip, :class_code, :naics_code,
|
4
|
+
:agency, :office, :location, :office_address,
|
5
|
+
:subject, :solicitation_number, :response_date,
|
6
|
+
:archive_date, :description, :contact_info,
|
7
|
+
:link_url, :link_description, :email_address,
|
8
|
+
:email_description, :setaside, :pop_country,
|
9
|
+
:pop_zip, :pop_address)
|
10
|
+
include FBO::Notice
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/fbo/parser.rb
CHANGED
@@ -46,21 +46,8 @@ module FBO
|
|
46
46
|
def parse_notices(text_notices)
|
47
47
|
@notices = []
|
48
48
|
text_notices.each do |text|
|
49
|
-
|
50
|
-
|
51
|
-
elsif FBO::Parser::CombinedSolicitationHandler.is_combined_solicitation?(text)
|
52
|
-
@notices << FBO::Parser::CombinedSolicitationHandler.parse(text)
|
53
|
-
elsif FBO::Parser::SourcesSoughtHandler.is_sources_sought?(text)
|
54
|
-
@notices << FBO::Parser::SourcesSoughtHandler.parse(text)
|
55
|
-
elsif FBO::Parser::AmendmentHandler.is_amendment?(text)
|
56
|
-
@notices << FBO::Parser::AmendmentHandler.parse(text)
|
57
|
-
elsif FBO::Parser::ModificationHandler.is_modification?(text)
|
58
|
-
@notices << FBO::Parser::ModificationHandler.parse(text)
|
59
|
-
elsif FBO::Parser::AwardHandler.is_award?(text)
|
60
|
-
@notices << FBO::Parser::AwardHandler.parse(text)
|
61
|
-
else
|
62
|
-
@notices << FBO::Parser::UnknownHandler.parse(text)
|
63
|
-
end
|
49
|
+
handler = FBO::Parser::HandlerSelector.select(text)
|
50
|
+
@notices << handler.parse(text)
|
64
51
|
end
|
65
52
|
end
|
66
53
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module FBO::Parser::ArchiveHandler
|
2
|
+
TAGS = %w( ARCHIVE DATE YEAR CBAC PASSWORD AGENCY OFFICE LOCATION NTYPE ) +
|
3
|
+
%w( SOLNBR ARCHDATE \/ARCHIVE )
|
4
|
+
|
5
|
+
ANY_ARCHIVE_TAG = TAGS.join("|")
|
6
|
+
ARCHIVE_PATTERN = /^<ARCHIVE>/
|
7
|
+
|
8
|
+
|
9
|
+
def is_archive?(text)
|
10
|
+
text =~ ARCHIVE_PATTERN
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse(text)
|
14
|
+
params = {
|
15
|
+
date: date(text),
|
16
|
+
year: year(text),
|
17
|
+
agency: agency(text),
|
18
|
+
office: office(text),
|
19
|
+
location: location(text),
|
20
|
+
solicitation_number: solicitation_number(text),
|
21
|
+
notice_type: notice_type(text),
|
22
|
+
archive_date: archive_date(text)
|
23
|
+
}
|
24
|
+
FBO::Notices::Archive.new(params)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
# Returns a concatenated list of all tags for the notice
|
31
|
+
#
|
32
|
+
# @return [String]
|
33
|
+
def any_notice_tag
|
34
|
+
ANY_ARCHIVE_TAG
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
extend FBO::Parser::NoticeHandler
|
39
|
+
extend FBO::Parser::ParserHelper
|
40
|
+
extend self
|
41
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module FBO::Parser::FairOpportunityHandler
|
2
|
+
TAGS = %w( FAIROPP DATE YEAR CBAC PASSWORD ZIP CLASSCOD NAICS OFFADD AGENCY ) +
|
3
|
+
%w( OFFICE LOCATION SUBJECT SOLNBR NTYPE FOJA AWDNBR DONBR MODNBR AWDDATE ) +
|
4
|
+
%w( ARCHDATE DESC CONTACT LINK URL EMAIL ADDRESS FILELIST FILE MIMETYPE ) +
|
5
|
+
%w( /FILE /FILELIST CORRECTION /FAIROPP )
|
6
|
+
|
7
|
+
ANY_FAIROPP_TAG = TAGS.join("|")
|
8
|
+
FAIR_OPPORTUNITY_PATTERN = /^<FAIROPP>/
|
9
|
+
|
10
|
+
|
11
|
+
def is_fair_opportunity?(text)
|
12
|
+
text =~ FAIR_OPPORTUNITY_PATTERN
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse(text)
|
16
|
+
params = {
|
17
|
+
date: date(text),
|
18
|
+
year: year(text),
|
19
|
+
zip: zip(text),
|
20
|
+
class_code: classification_code(text),
|
21
|
+
naics_code: naics_code(text),
|
22
|
+
agency: agency(text),
|
23
|
+
office: office(text),
|
24
|
+
location: location(text),
|
25
|
+
office_address: office_address(text),
|
26
|
+
subject: subject(text),
|
27
|
+
solicitation_number: solicitation_number(text),
|
28
|
+
notice_type: notice_type(text),
|
29
|
+
justification_authority: justification_authority(text),
|
30
|
+
award_number: award_number(text),
|
31
|
+
delivery_order_number: delivery_order_number(text),
|
32
|
+
modification_number: modification_number(text),
|
33
|
+
award_date: award_date(text),
|
34
|
+
archive_date: archive_date(text),
|
35
|
+
description: description(text),
|
36
|
+
contact_info: contact(text),
|
37
|
+
link_url: link_url(text),
|
38
|
+
link_description: link_description(text),
|
39
|
+
email_address: email_address(text),
|
40
|
+
email_description: email_description(text),
|
41
|
+
file_list: file_list(text),
|
42
|
+
correction: correction?(text)
|
43
|
+
}
|
44
|
+
FBO::Notices::FairOpportunity.new(params)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
# Returns a concatenated list of all tags for the notice
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
def any_notice_tag
|
54
|
+
ANY_FAIROPP_TAG
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
extend FBO::Parser::NoticeHandler
|
59
|
+
extend FBO::Parser::ParserHelper
|
60
|
+
extend self
|
61
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module FBO::Parser::ForeignStandardHandler
|
2
|
+
TAGS = %w( FSTD DATE YEAR CBAC PASSWORD ZIP CLASSCOD NAICS OFFADD AGENCY ) +
|
3
|
+
%w( OFFICE LOCATION SUBJECT SOLNBR RESPDATE ARCHDATE CONTACT DESC LINK ) +
|
4
|
+
%w( URL EMAIL ADDRESS SETASIDE POPCOUNTRY POPADDRESS POPZIP \/FSTD )
|
5
|
+
|
6
|
+
ANY_FSTD_TAG = TAGS.join("|")
|
7
|
+
FOREIGN_STANDARD_PATTERN = /^<FSTD>/
|
8
|
+
|
9
|
+
|
10
|
+
def is_foreign_standard?(text)
|
11
|
+
text =~ FOREIGN_STANDARD_PATTERN
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse(text)
|
15
|
+
params = {
|
16
|
+
date: date(text),
|
17
|
+
year: year(text),
|
18
|
+
zip: zip(text),
|
19
|
+
class_code: classification_code(text),
|
20
|
+
naics_code: naics_code(text),
|
21
|
+
agency: agency(text),
|
22
|
+
office: office(text),
|
23
|
+
location: location(text),
|
24
|
+
office_address: office_address(text),
|
25
|
+
subject: subject(text),
|
26
|
+
solicitation_number: solicitation_number(text),
|
27
|
+
response_date: response_date(text),
|
28
|
+
archive_date: archive_date(text),
|
29
|
+
contact_info: contact(text),
|
30
|
+
description: description(text),
|
31
|
+
link_url: link_url(text),
|
32
|
+
link_description: link_description(text),
|
33
|
+
email_address: email_address(text),
|
34
|
+
email_description: email_description(text),
|
35
|
+
setaside: set_aside(text),
|
36
|
+
pop_address: pop_address(text),
|
37
|
+
pop_zip: pop_zip_code(text),
|
38
|
+
pop_country: pop_country(text)
|
39
|
+
}
|
40
|
+
FBO::Notices::ForeignStandard.new(params)
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
# Returns a concatenated list of all tags for the notice
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
def any_notice_tag
|
50
|
+
ANY_FSTD_TAG
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
extend FBO::Parser::NoticeHandler
|
55
|
+
extend FBO::Parser::ParserHelper
|
56
|
+
extend self
|
57
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class FBO::Parser::HandlerSelector
|
2
|
+
|
3
|
+
def self.select(text)
|
4
|
+
if FBO::Parser::PresolicitationHandler.is_presolicitation?(text)
|
5
|
+
FBO::Parser::PresolicitationHandler
|
6
|
+
elsif FBO::Parser::CombinedSolicitationHandler.is_combined_solicitation?(text)
|
7
|
+
FBO::Parser::CombinedSolicitationHandler
|
8
|
+
elsif FBO::Parser::SourcesSoughtHandler.is_sources_sought?(text)
|
9
|
+
FBO::Parser::SourcesSoughtHandler
|
10
|
+
elsif FBO::Parser::AmendmentHandler.is_amendment?(text)
|
11
|
+
FBO::Parser::AmendmentHandler
|
12
|
+
elsif FBO::Parser::ModificationHandler.is_modification?(text)
|
13
|
+
FBO::Parser::ModificationHandler
|
14
|
+
elsif FBO::Parser::AwardHandler.is_award?(text)
|
15
|
+
FBO::Parser::AwardHandler
|
16
|
+
elsif FBO::Parser::FairOpportunityHandler.is_fair_opportunity?(text)
|
17
|
+
FBO::Parser::FairOpportunityHandler
|
18
|
+
elsif FBO::Parser::ForeignStandardHandler.is_foreign_standard?(text)
|
19
|
+
FBO::Parser::ForeignStandardHandler
|
20
|
+
elsif FBO::Parser::ArchiveHandler.is_archive?(text)
|
21
|
+
FBO::Parser::ArchiveHandler
|
22
|
+
elsif FBO::Parser::IntentToBundleHandler.is_intent_to_bundle?(text)
|
23
|
+
FBO::Parser::IntentToBundleHandler
|
24
|
+
elsif FBO::Parser::JustificationApprovalHandler.is_justification_approval?(text)
|
25
|
+
FBO::Parser::JustificationApprovalHandler
|
26
|
+
elsif FBO::Parser::SaleOfSurplusHandler.is_sale_of_surplus?(text)
|
27
|
+
FBO::Parser::SaleOfSurplusHandler
|
28
|
+
elsif FBO::Parser::SpecialNoticeHandler.is_special_notice?(text)
|
29
|
+
FBO::Parser::SpecialNoticeHandler
|
30
|
+
elsif FBO::Parser::UnarchiveHandler.is_unarchive?(text)
|
31
|
+
FBO::Parser::UnarchiveHandler
|
32
|
+
else
|
33
|
+
FBO::Parser::UnknownHandler
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|