sec_query 1.3.1 → 1.4.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 +4 -4
- data/.travis.yml +11 -0
- data/README.md +23 -1
- data/Rakefile +9 -1
- data/lib/sec_query.rb +1 -0
- data/lib/sec_query/filing.rb +9 -0
- data/lib/sec_query/filing_detail.rb +67 -0
- data/lib/sec_query/version.rb +1 -1
- data/sec_query.gemspec +4 -2
- data/spec/sec_query/filing_spec.rb +56 -0
- metadata +36 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85944bfa167ef27d49aee0beaeed632b44a44fa0
|
4
|
+
data.tar.gz: 536db81b013206c45038b8f10d697ffc4158621c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: baccb44f714eb163c13aa2f5313d8e6fd34794ee4cdb92bd386dc1c0e2c9e666475983d8662de49c8f99d42daf44a7bfbd429bfd00dd585140424770e3dc8295
|
7
|
+
data.tar.gz: 26a9c698c541f28f980c7937921f761665c721ffb4c86021b42b89bfcf0e911d1adf7c925099e09919060b982a26bde7ac541b5f54343b7ffe013f9d9415b02c
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -88,7 +88,7 @@ Middle initial or name is optional, but helps when there are multiple results fo
|
|
88
88
|
|
89
89
|
Returns a list of Sec::Filing instances for an Sec::Entity
|
90
90
|
|
91
|
-
###
|
91
|
+
### SecQuery::Filing
|
92
92
|
|
93
93
|
SecQuery::Filing instance may contains the following attributes:
|
94
94
|
|
@@ -99,6 +99,7 @@ SecQuery::Filing instance may contains the following attributes:
|
|
99
99
|
* term
|
100
100
|
* date
|
101
101
|
* file_id
|
102
|
+
* detail
|
102
103
|
|
103
104
|
#### Class Methods
|
104
105
|
|
@@ -115,6 +116,27 @@ end
|
|
115
116
|
|
116
117
|
Requires a block. Returns the most recent filings. Use start, count and limit to iterate through recent filings.
|
117
118
|
|
119
|
+
### SecQuery::FilingDetail
|
120
|
+
Represents the detail page for a given filing.
|
121
|
+
Ex: [Filing Detail page](https://www.sec.gov/Archives/edgar/data/320193/000032019317000070/0000320193-17-000070-index.htm) of Apple's Annual Report from 2017
|
122
|
+
|
123
|
+
#### Instance Methods
|
124
|
+
* link
|
125
|
+
* filing_date
|
126
|
+
* accepted_date
|
127
|
+
* period_of_report
|
128
|
+
* sec_access_number
|
129
|
+
* document_count
|
130
|
+
* format_files
|
131
|
+
* data_files
|
132
|
+
|
133
|
+
#### Class Methods
|
134
|
+
##### .fetch
|
135
|
+
```
|
136
|
+
appl_10k_details_url = 'https://www.sec.gov/Archives/edgar/data/320193/000032019317000070/0000320193-17-000070-index.htm'
|
137
|
+
filing_detail = SecQuery::FilingDetail.fetch(appl_10k_details_url)
|
138
|
+
```
|
139
|
+
|
118
140
|
## To Whom It May Concern at the SEC
|
119
141
|
|
120
142
|
Over the last decade, I have gotten to know Edgar quite extensively and I have grown quite fond of it and the information it contains. So it is with my upmost respect that I make the following suggestions:
|
data/Rakefile
CHANGED
data/lib/sec_query.rb
CHANGED
data/lib/sec_query/filing.rb
CHANGED
@@ -14,6 +14,10 @@ module SecQuery
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
def detail
|
18
|
+
@detail ||= FilingDetail.fetch(@link)
|
19
|
+
end
|
20
|
+
|
17
21
|
def self.fetch(uri, &blk)
|
18
22
|
open(uri) do |rss|
|
19
23
|
parse_rss(rss, &blk)
|
@@ -138,6 +142,11 @@ module SecQuery
|
|
138
142
|
parse(cik, document)
|
139
143
|
end
|
140
144
|
|
145
|
+
def self.last(cik, args = {})
|
146
|
+
filings = find(cik, 0, 1, args)
|
147
|
+
filings.is_a?(Array) ? filings.first : nil
|
148
|
+
end
|
149
|
+
|
141
150
|
def self.parse(cik, document)
|
142
151
|
filings = []
|
143
152
|
if document.xpath('//content').to_s.length > 0
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module SecQuery
|
4
|
+
# => SecQuery::FilingDetail
|
5
|
+
# SecQuery::FilingDetail requests and parses Filing Detail for any given SecQuery::Filing
|
6
|
+
class FilingDetail
|
7
|
+
COLUMNS = [:link, :filing_date, :accepted_date, :period_of_report, :sec_access_number, :document_count, :format_files, :data_files]
|
8
|
+
|
9
|
+
attr_accessor(*COLUMNS)
|
10
|
+
|
11
|
+
def initialize(filing_detail)
|
12
|
+
COLUMNS.each do |column|
|
13
|
+
instance_variable_set("@#{ column }", filing_detail[column])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.fetch(uri)
|
18
|
+
document = Nokogiri::HTML(open(uri.gsub('http:', 'https:')))
|
19
|
+
filing_date = document.xpath('//*[@id="formDiv"]/div[2]/div[1]/div[2]').text
|
20
|
+
accepted_date = document.xpath('//*[@id="formDiv"]/div[2]/div[1]/div[4]').text
|
21
|
+
period_of_report = document.xpath('//*[@id="formDiv"]/div[2]/div[2]/div[2]').text
|
22
|
+
sec_access_number = document.xpath('//*[@id="secNum"]/text()').text.strip
|
23
|
+
document_count = document.xpath('//*[@id="formDiv"]/div[2]/div[1]/div[6]').text.to_i
|
24
|
+
format_files_table = document.xpath("//table[@summary='Document Format Files']")
|
25
|
+
data_files_table = document.xpath("//table[@summary='Data Files']")
|
26
|
+
|
27
|
+
format_files = (parsed = parse_files(format_files_table)) && (parsed || [])
|
28
|
+
data_files = (parsed = parse_files(data_files_table)) && (parsed || [])
|
29
|
+
|
30
|
+
new({uri: uri,
|
31
|
+
filing_date: filing_date,
|
32
|
+
accepted_date: accepted_date,
|
33
|
+
period_of_report: period_of_report,
|
34
|
+
sec_access_number: sec_access_number,
|
35
|
+
document_count: document_count,
|
36
|
+
format_files: format_files,
|
37
|
+
data_files: data_files})
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.parse_files(format_files_table)
|
41
|
+
# get table headers
|
42
|
+
headers = []
|
43
|
+
format_files_table.xpath('//th').each do |th|
|
44
|
+
headers << th.text
|
45
|
+
end
|
46
|
+
|
47
|
+
# get table rows
|
48
|
+
rows = []
|
49
|
+
format_files_table.xpath('//tr').each_with_index do |row, i|
|
50
|
+
rows[i] = {}
|
51
|
+
row.xpath('td').each_with_index do |td, j|
|
52
|
+
if td.children.first && td.children.first.name == 'a'
|
53
|
+
relative_url = td.children.first.attributes.first[1].value
|
54
|
+
rows[i][headers[j]] = {
|
55
|
+
'link' => "https://www.sec.gov#{relative_url}",
|
56
|
+
'text' => td.text.gsub(/\A\p{Space}*/, '')
|
57
|
+
}
|
58
|
+
else
|
59
|
+
rows[i][headers[j]] = td.text.gsub(/\A\p{Space}*/, '')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
rows.reject(&:empty?)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/sec_query/version.rb
CHANGED
data/sec_query.gemspec
CHANGED
@@ -19,6 +19,8 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ['lib']
|
21
21
|
|
22
|
+
s.add_development_dependency 'bundler', '~> 2.0.1'
|
23
|
+
s.add_development_dependency 'rake'
|
22
24
|
s.add_development_dependency 'rspec', '~> 3.5'
|
23
25
|
s.add_development_dependency 'vcr', '~> 3.0'
|
24
26
|
s.add_development_dependency 'webmock', '~> 2.3'
|
@@ -26,6 +28,6 @@ Gem::Specification.new do |s|
|
|
26
28
|
s.add_development_dependency 'byebug', '~> 9.0'
|
27
29
|
s.add_runtime_dependency 'rest-client', '~> 2.0'
|
28
30
|
s.add_runtime_dependency 'addressable', '~> 2.5'
|
29
|
-
s.add_runtime_dependency 'nokogiri', '
|
31
|
+
s.add_runtime_dependency 'nokogiri', '>= 1.8.5'
|
30
32
|
s.add_runtime_dependency 'activesupport', '>= 0'
|
31
|
-
end
|
33
|
+
end
|
@@ -101,5 +101,61 @@ describe SecQuery::Filing do
|
|
101
101
|
expect(f.content).to match(/^(<SEC-DOCUMENT>)/)
|
102
102
|
end
|
103
103
|
end
|
104
|
+
|
105
|
+
describe "::last", vcr: { cassette_name: "Steve Jobs"} do
|
106
|
+
let(:cik) { "0000320193" }
|
107
|
+
|
108
|
+
context 'when querying by cik' do
|
109
|
+
let(:filing) { SecQuery::Filing.last(cik) }
|
110
|
+
|
111
|
+
it 'returns the first filing' do
|
112
|
+
expect(filing).to be_kind_of(SecQuery::Filing)
|
113
|
+
is_valid_filing?(filing)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when querying cik and by type param' do
|
118
|
+
let(:filing) { SecQuery::Filing.last(cik,{ type: "10-K" }) }
|
119
|
+
|
120
|
+
describe "Filings", vcr: { cassette_name: "Steve Jobs"} do
|
121
|
+
it "should return filing of type 10-K" do
|
122
|
+
expect(filing).to be_kind_of(SecQuery::Filing)
|
123
|
+
expect(filing.term).to eq "10-K"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe '#detail', vcr: { cassette_name: 'Steve Jobs'} do
|
131
|
+
let(:cik) { '0000320193' }
|
132
|
+
let(:filing) { SecQuery::Filing.find(cik, 0, 1, {type: type}).first }
|
133
|
+
subject(:filing_detail) { filing.detail }
|
134
|
+
|
135
|
+
shared_examples 'Valid SecQuery::FilingDetail' do |filing_type|
|
136
|
+
it 'valid filing detail' do
|
137
|
+
expect(filing_detail).to be_a SecQuery::FilingDetail
|
138
|
+
expect((Date.strptime(subject.filing_date, '%Y-%m-%d') rescue false)).to be_a Date
|
139
|
+
expect((DateTime.strptime(subject.accepted_date, '%Y-%m-%d %H:%M:%S') rescue false)).to be_a DateTime
|
140
|
+
expect((Date.strptime(subject.period_of_report, '%Y-%m-%d') rescue false)).to be_a Date
|
141
|
+
expect(filing_detail.sec_access_number).to match /^[0-9]{10}-[0-9]{2}-[0-9]{6}$/ # ex: 0000320193-18-000100
|
142
|
+
expect(filing_detail.document_count).to be > 0
|
143
|
+
|
144
|
+
|
145
|
+
expect(filing_detail.data_files).not_to be_empty if filing_type == '10-K'
|
146
|
+
expect(filing_detail.data_files).to be_empty if filing_type == '4'
|
147
|
+
expect(filing_detail.format_files).not_to be_empty
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context '10-K' do
|
152
|
+
let(:type) { '10-K' }
|
153
|
+
it_behaves_like 'Valid SecQuery::FilingDetail', '10-K'
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'Form 4' do
|
157
|
+
let(:type) { '4' }
|
158
|
+
it_behaves_like 'Valid SecQuery::FilingDetail', '4'
|
159
|
+
end
|
104
160
|
end
|
105
161
|
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sec_query
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ty Rauber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.0.1
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.0.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: rspec
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,16 +140,16 @@ dependencies:
|
|
112
140
|
name: nokogiri
|
113
141
|
requirement: !ruby/object:Gem::Requirement
|
114
142
|
requirements:
|
115
|
-
- - "
|
143
|
+
- - ">="
|
116
144
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
145
|
+
version: 1.8.5
|
118
146
|
type: :runtime
|
119
147
|
prerelease: false
|
120
148
|
version_requirements: !ruby/object:Gem::Requirement
|
121
149
|
requirements:
|
122
|
-
- - "
|
150
|
+
- - ">="
|
123
151
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
152
|
+
version: 1.8.5
|
125
153
|
- !ruby/object:Gem::Dependency
|
126
154
|
name: activesupport
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -145,13 +173,14 @@ extensions: []
|
|
145
173
|
extra_rdoc_files: []
|
146
174
|
files:
|
147
175
|
- ".gitignore"
|
176
|
+
- ".travis.yml"
|
148
177
|
- Gemfile
|
149
|
-
- Gemfile.lock
|
150
178
|
- README.md
|
151
179
|
- Rakefile
|
152
180
|
- lib/sec_query.rb
|
153
181
|
- lib/sec_query/entity.rb
|
154
182
|
- lib/sec_query/filing.rb
|
183
|
+
- lib/sec_query/filing_detail.rb
|
155
184
|
- lib/sec_query/sec_uri.rb
|
156
185
|
- lib/sec_query/version.rb
|
157
186
|
- sec_query.gemspec
|