quandl 0.2.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +16 -0
- data/.travis.yml +20 -0
- data/Gemfile +14 -0
- data/Guardfile +8 -0
- data/LICENSE +7 -0
- data/README.md +316 -0
- data/Rakefile +39 -0
- data/UPGRADE.md +143 -0
- data/bin/quandl +26 -0
- data/dist/resources/pkg/Distribution.erb +15 -0
- data/dist/resources/pkg/PackageInfo.erb +6 -0
- data/dist/resources/pkg/postinstall +45 -0
- data/dist/resources/pkg/quandl +25 -0
- data/dist/resources/ruby/PackageInfo +3 -0
- data/lib/commander/command/quandl_ext.rb +21 -0
- data/lib/quandl/command.rb +45 -0
- data/lib/quandl/command/client_ext.rb +1 -0
- data/lib/quandl/command/client_ext/user.rb +11 -0
- data/lib/quandl/command/compatibility_check.rb +11 -0
- data/lib/quandl/command/qconfig.rb +76 -0
- data/lib/quandl/command/tasks.rb +15 -0
- data/lib/quandl/command/tasks/base.rb +263 -0
- data/lib/quandl/command/tasks/delete.rb +58 -0
- data/lib/quandl/command/tasks/download.rb +111 -0
- data/lib/quandl/command/tasks/info.rb +46 -0
- data/lib/quandl/command/tasks/list.rb +40 -0
- data/lib/quandl/command/tasks/login.rb +46 -0
- data/lib/quandl/command/tasks/update.rb +205 -0
- data/lib/quandl/command/tasks/upload.rb +69 -0
- data/lib/quandl/command/version.rb +5 -0
- data/lib/quandl/utility.rb +2 -0
- data/lib/quandl/utility/config.rb +43 -0
- data/lib/quandl/utility/ruby_version.rb +143 -0
- data/quandl.gemspec +39 -0
- data/scripts/compile_ruby_pkg.sh +34 -0
- data/scripts/install.sh +51 -0
- data/scripts/win/quandl_toolbelt.iss +82 -0
- data/spec/lib/quandl/command/delete_spec.rb +31 -0
- data/spec/lib/quandl/command/download_spec.rb +42 -0
- data/spec/lib/quandl/command/upload_spec.rb +39 -0
- data/spec/lib/quandl/command_spec.rb +46 -0
- data/spec/spec_helper.rb +20 -0
- data/tasks/toolbelt.rake +133 -0
- data/tasks/toolbelt.rb +92 -0
- data/tasks/toolbelt/build.rb +2 -0
- data/tasks/toolbelt/build/darwin.rb +71 -0
- data/tasks/toolbelt/build/ruby.rb +105 -0
- data/tasks/toolbelt/build/tarball.rb +73 -0
- data/tasks/toolbelt/build/windows.rb +25 -0
- data/tasks/toolbelt/push.rb +15 -0
- data/tasks/toolbelt/storage.rb +28 -0
- data/tasks/toolbelt/tar.rb +21 -0
- metadata +354 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
class Quandl::Command::Tasks::Info < Quandl::Command::Tasks::Base
|
2
|
+
|
3
|
+
description "Display information about the Quandl Toolbelt."
|
4
|
+
|
5
|
+
def execute
|
6
|
+
info title "Quandl Toolbelt"
|
7
|
+
info current_user.info
|
8
|
+
info toolbelt_info
|
9
|
+
|
10
|
+
debug title "Package Versions"
|
11
|
+
debug package_info
|
12
|
+
end
|
13
|
+
|
14
|
+
def title(name)
|
15
|
+
name = name.to_s
|
16
|
+
"\n#{name}\n" + ( name.length.times.collect{'='}.join )
|
17
|
+
end
|
18
|
+
|
19
|
+
def user_info
|
20
|
+
[
|
21
|
+
"username: #{current_user.username}",
|
22
|
+
"email: #{current_user.email}",
|
23
|
+
].join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
def toolbelt_info
|
27
|
+
[
|
28
|
+
"host: #{quandl_url}",
|
29
|
+
"token: #{auth_token}",
|
30
|
+
"version: #{Quandl::Command::VERSION}",
|
31
|
+
].join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
def package_info
|
35
|
+
quandl_package_versions.sort_by{|p| p[:package].to_s }.collect{|p| "#{ p[:package] } ( #{ p[:version] } )" }.join("\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
def quandl_package_versions
|
39
|
+
Quandl.constants.collect do |c|
|
40
|
+
package = "Quandl::#{c}".constantize
|
41
|
+
version = "Quandl::#{c}::VERSION".constantize rescue nil
|
42
|
+
{ package: package, version: version } unless version.nil?
|
43
|
+
end.compact
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Quandl::Command::Tasks::List < Quandl::Command::Tasks::Base
|
2
|
+
|
3
|
+
description "List datasets matching conditions."
|
4
|
+
options({
|
5
|
+
String => {
|
6
|
+
match: "Only return datasets matching: [match]",
|
7
|
+
source_code: "Return datasets matching: [SOURCE_CODE]"
|
8
|
+
},
|
9
|
+
Integer => {
|
10
|
+
limit: "How datasets to return per page.",
|
11
|
+
page: "Return datasets starting from: [page]",
|
12
|
+
}
|
13
|
+
})
|
14
|
+
|
15
|
+
authenticated_users_only!
|
16
|
+
|
17
|
+
def execute
|
18
|
+
# find dataset
|
19
|
+
debug "search_params: #{search_params}"
|
20
|
+
dataset = Quandl::Client::Dataset.where( search_params ).fetch
|
21
|
+
codes = dataset.collect(&:full_code)
|
22
|
+
# fail on errors
|
23
|
+
info codes.join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
def search_params
|
27
|
+
search_params = {}
|
28
|
+
search_params[:query] = options.match if options.match.present?
|
29
|
+
search_params[:source_code] = options.source_code.to_s.upcase if options.source_code.present?
|
30
|
+
search_params[:per_page] = options.limit if options.limit.present?
|
31
|
+
search_params[:page] = page
|
32
|
+
search_params[:self_search] = search_params[:source_code].present? ? false : true
|
33
|
+
search_params
|
34
|
+
end
|
35
|
+
|
36
|
+
def page
|
37
|
+
options.page.to_i || 1
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Quandl::Command::Tasks::Login < Quandl::Command::Tasks::Base
|
2
|
+
|
3
|
+
description "Login to quandl with username and email."
|
4
|
+
|
5
|
+
options String => { method: 'Login method? ( password | token )' }
|
6
|
+
|
7
|
+
def execute
|
8
|
+
authenticate!
|
9
|
+
reload_session!
|
10
|
+
if current_user.present?
|
11
|
+
info "You have successfully authenticated!"
|
12
|
+
info current_user.info
|
13
|
+
else
|
14
|
+
error "Failed to authenticate!"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def authenticate!
|
19
|
+
return execute_password_login if options.method == 'password'
|
20
|
+
execute_token_login
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute_token_login
|
24
|
+
info("Obtain your Auth Token from this page: http://www.quandl.com/users/info")
|
25
|
+
token = ask("Auth Token: "){ |q| q.echo = "*" }
|
26
|
+
write_auth_token(token)
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute_password_login
|
30
|
+
# request login and password
|
31
|
+
login = ask("Username or Email: ")
|
32
|
+
password = ask("Password: "){ |q| q.echo = "*" }
|
33
|
+
# attempt to login
|
34
|
+
user = Quandl::Client::User.login( login: login, password: password )
|
35
|
+
# fail on errors
|
36
|
+
return error( user.human_status ) unless user.auth_token.present?
|
37
|
+
# output qdf
|
38
|
+
write_auth_token(user.auth_token)
|
39
|
+
info "You've successfully authorized #{login}!"
|
40
|
+
end
|
41
|
+
|
42
|
+
def write_auth_token(token)
|
43
|
+
QConfig.configuration.token = token
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
class Quandl::Command::Tasks::Update < Quandl::Command::Tasks::Base
|
2
|
+
|
3
|
+
# curl -s https://s3.amazonaws.com/quandl-command/install.sh | bash
|
4
|
+
|
5
|
+
description "Update Quandl Toolbelt to the latest version."
|
6
|
+
syntax %Q{quandl update [revision]
|
7
|
+
|
8
|
+
Examples:
|
9
|
+
|
10
|
+
$ quandl update
|
11
|
+
Updating from 0.2.18 ...
|
12
|
+
You are up to date! ( 0.3.1 )
|
13
|
+
|
14
|
+
$ quandl update beta1
|
15
|
+
Updating from 0.3.1 ...
|
16
|
+
You are up to date! ( 0.3.1-beta1 )}
|
17
|
+
|
18
|
+
PACKAGE_URL = 'http://s3.amazonaws.com/quandl-command/'
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def package_url(revision=nil)
|
22
|
+
# are we on windows?
|
23
|
+
platform = Quandl::Utility::Config.windows? ? 'windows' : nil
|
24
|
+
# build filename
|
25
|
+
filename = ['quandl-command', platform, revision, 'tar.gz'].compact.join(".")
|
26
|
+
# append s3 url
|
27
|
+
File.join(PACKAGE_URL, filename)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def execute
|
32
|
+
# load libraries needed by this action
|
33
|
+
require_dependencies
|
34
|
+
return unless assert_update_requirements!
|
35
|
+
info "Updating from #{Quandl::Command::VERSION} ... "
|
36
|
+
# wipe update/ and backup/ folders
|
37
|
+
prepare_for_update
|
38
|
+
download_tarball
|
39
|
+
# ensure package was downloaded
|
40
|
+
return error("'#{package_url}' not found") unless File.exists?(tarball_path)
|
41
|
+
# install
|
42
|
+
extract_tarball
|
43
|
+
copy_windows_specific_files if Quandl::Utility::Config.windows?
|
44
|
+
install_update
|
45
|
+
configure_update
|
46
|
+
ensure_correct_permissions
|
47
|
+
# success
|
48
|
+
version = %x{quandl -v}.to_s.strip.rstrip
|
49
|
+
info "You are up to date! ( #{version} )"
|
50
|
+
|
51
|
+
rescue => err
|
52
|
+
# log error
|
53
|
+
error(err)
|
54
|
+
debug(err.backtrace.join("\n"))
|
55
|
+
# report failure and rollback
|
56
|
+
info("----\nAn error has occured! Rolling back to previous version ... ")
|
57
|
+
info("If the problem persists reinstall with: #{installer_url}")
|
58
|
+
rollback_update
|
59
|
+
end
|
60
|
+
|
61
|
+
def installer_url
|
62
|
+
return File.join(PACKAGE_URL, "Quandl Setup.exe") if Quandl::Utility::Config.windows?
|
63
|
+
return File.join(PACKAGE_URL, "quandl-toolbelt.pkg") if Quandl::Utility::Config.macosx?
|
64
|
+
end
|
65
|
+
|
66
|
+
def package_path
|
67
|
+
@package_path ||= File.join(update_path, "quandl-command")
|
68
|
+
end
|
69
|
+
|
70
|
+
def tarball_path
|
71
|
+
@tarball_path ||= File.join(update_path, "update.tar.gz")
|
72
|
+
end
|
73
|
+
|
74
|
+
def package_url
|
75
|
+
@package_url ||= self.class.package_url(args.first)
|
76
|
+
end
|
77
|
+
|
78
|
+
def update_path
|
79
|
+
@update_path ||= File.join( root_path, 'update' )
|
80
|
+
end
|
81
|
+
|
82
|
+
def backup_path
|
83
|
+
@backup_path ||= File.join( root_path, 'backup' )
|
84
|
+
end
|
85
|
+
|
86
|
+
def root_path
|
87
|
+
Quandl::Command::Tasks.root
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def require_dependencies
|
94
|
+
require 'quandl/utility/config'
|
95
|
+
require 'zlib'
|
96
|
+
require 'archive/tar/minitar'
|
97
|
+
end
|
98
|
+
|
99
|
+
def assert_update_requirements!
|
100
|
+
return fatal("update is destructive and disabled in development!") if Dir.exists?(File.join(root_path, ".git"))
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
104
|
+
def prepare_for_update
|
105
|
+
[backup_path, update_path].each do |path|
|
106
|
+
# remove previous directory if present
|
107
|
+
rm_rf(path) if Dir.exists?(path)
|
108
|
+
# create new directory
|
109
|
+
mkdir_p(path) unless Dir.exists?(path)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def download_tarball
|
114
|
+
debug "Downloading '#{package_url}' to '#{tarball_path}'"
|
115
|
+
# download new package
|
116
|
+
uri = URI( package_url )
|
117
|
+
# open connection to storage host
|
118
|
+
Net::HTTP.start( uri.host, uri.port ) do |http|
|
119
|
+
# download file
|
120
|
+
resp = http.get( uri.path )
|
121
|
+
# write tar file if it was found
|
122
|
+
open( tarball_path, "wb"){|f| f.write(resp.body) } unless resp.code == '404'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def extract_tarball
|
127
|
+
debug "Archive::Tar::Minitar.unpack( '#{tarball_path}', '#{update_path}' )"
|
128
|
+
# extract into update_path
|
129
|
+
Archive::Tar::Minitar.unpack( Zlib::GzipReader.open(tarball_path), update_path )
|
130
|
+
end
|
131
|
+
|
132
|
+
def copy_windows_specific_files
|
133
|
+
files = ['bin/quandl.bat']
|
134
|
+
files.each do |file|
|
135
|
+
source_path = File.join(root_path, file)
|
136
|
+
cp source_path, File.join(package_path, file) if File.exists?(source_path)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def install_update
|
141
|
+
debug "Installing '#{update_path}' to '#{root_path}'"
|
142
|
+
# install each folder into the live directory
|
143
|
+
Dir.glob(File.join(package_path, "/*")) do |update_dir|
|
144
|
+
# current folder
|
145
|
+
folder_name = File.basename(update_dir)
|
146
|
+
# get live dir
|
147
|
+
live_dir = File.join(root_path, folder_name )
|
148
|
+
# move live to backup (skip this step if it doesn't exist since it might be a new folder)
|
149
|
+
mv live_dir, File.join(backup_path, folder_name) if File.exists?(live_dir)
|
150
|
+
# move update to live
|
151
|
+
mv update_dir, live_dir
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def configure_update
|
156
|
+
if Dir.exists?("#{root_path}/ruby")
|
157
|
+
bin_file = File.read("#{root_path}/bin/quandl")
|
158
|
+
bin_file.gsub!("#!/usr/bin/env ruby", "#!/#{root_path}/ruby/bin/ruby")
|
159
|
+
File.write("#{root_path}/bin/quandl", bin_file)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def rollback_update
|
164
|
+
Dir.glob(File.join(backup_path, "/*")) do |backup_dir|
|
165
|
+
# current folder
|
166
|
+
folder_name = File.basename(backup_dir)
|
167
|
+
# get live dir
|
168
|
+
live_dir = File.join(root_path, folder_name )
|
169
|
+
# move live to update
|
170
|
+
mv live_dir, File.join(package_path, folder_name)
|
171
|
+
# move backup to live
|
172
|
+
mv backup_dir, live_dir
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def ensure_correct_permissions
|
177
|
+
chmod("+x", File.join(root_path, 'bin/quandl'))
|
178
|
+
end
|
179
|
+
|
180
|
+
def chmod(*args)
|
181
|
+
debug("FileUtils.chmod #{args.to_a.join(' ')}")
|
182
|
+
FileUtils.chmod(*args)
|
183
|
+
end
|
184
|
+
|
185
|
+
def cp(old_path, new_path)
|
186
|
+
debug("FileUtils.cp #{old_path} #{new_path}")
|
187
|
+
FileUtils.cp( old_path, new_path )
|
188
|
+
end
|
189
|
+
|
190
|
+
def mv(old_path, new_path)
|
191
|
+
debug("FileUtils.mv #{old_path} #{new_path}")
|
192
|
+
FileUtils.mv( old_path, new_path )
|
193
|
+
end
|
194
|
+
|
195
|
+
def rm_rf(*args)
|
196
|
+
debug( "FileUtils.rm_rf #{args.to_a.join(" ")}")
|
197
|
+
FileUtils.rm_rf(*args)
|
198
|
+
end
|
199
|
+
|
200
|
+
def mkdir_p(*args)
|
201
|
+
debug( "FileUtils.mkdir_p #{args.to_a.join(" ")}")
|
202
|
+
FileUtils.mkdir_p(*args)
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Quandl::Command::Tasks::Upload < Quandl::Command::Tasks::Base
|
2
|
+
|
3
|
+
description "Upload a dataset using its quandl code."
|
4
|
+
syntax %{quandl upload file.qdf
|
5
|
+
|
6
|
+
Examples:
|
7
|
+
|
8
|
+
$ quandl upload file.qcsv
|
9
|
+
OK | 98ms | http://quandl.com/USERNAME/CODE_1
|
10
|
+
OK | 72ms | http://quandl.com/USERNAME/CODE_2
|
11
|
+
|
12
|
+
$ ruby code.rb | quandl upload
|
13
|
+
OK | 98ms | http://quandl.com/USERNAME/CODE_1
|
14
|
+
OK | 72ms | http://quandl.com/USERNAME/CODE_2
|
15
|
+
|
16
|
+
Quandl CSV Format:
|
17
|
+
|
18
|
+
code: YOUR_QUANDL_CODE
|
19
|
+
name: Dataset Title
|
20
|
+
description: Dataset description.
|
21
|
+
private: false
|
22
|
+
-
|
23
|
+
Date, First, Second, Third
|
24
|
+
2013-11-22,1252.0,454.95,448.2
|
25
|
+
2013-11-21,452.25,457.75,449.1
|
26
|
+
}
|
27
|
+
options({
|
28
|
+
Integer => {
|
29
|
+
threads: "How many workers to use during download.",
|
30
|
+
}
|
31
|
+
})
|
32
|
+
|
33
|
+
authenticated_users_only!
|
34
|
+
|
35
|
+
def execute
|
36
|
+
# datasets from file_path if given
|
37
|
+
interface = file_path.present? ? File.open(file_path, "r") : $stdin
|
38
|
+
# for each dataset streamed from interface
|
39
|
+
Quandl::Format::Dataset.each_line(interface) do |dataset|
|
40
|
+
pool.process{ upload( dataset ) }
|
41
|
+
end
|
42
|
+
pool.shutdown
|
43
|
+
end
|
44
|
+
|
45
|
+
def upload(dataset)
|
46
|
+
# display debug info when verbose
|
47
|
+
debug dataset.attributes.to_s
|
48
|
+
# upload
|
49
|
+
dataset.upload if dataset.valid?
|
50
|
+
# output report to $stdout or $stderr
|
51
|
+
report(dataset)
|
52
|
+
end
|
53
|
+
|
54
|
+
def report(dataset)
|
55
|
+
if [200,201].include?( dataset.client.status )
|
56
|
+
info table dataset.client.human_status,
|
57
|
+
dataset.client.elapsed_request_time_ms,
|
58
|
+
dataset.client.full_url
|
59
|
+
else
|
60
|
+
error(dataset.human_errors)
|
61
|
+
end
|
62
|
+
debug "---"
|
63
|
+
end
|
64
|
+
|
65
|
+
def file_path
|
66
|
+
args.first
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
module Quandl::Utility
|
4
|
+
module Config
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def windows?
|
8
|
+
host_os == :windows
|
9
|
+
end
|
10
|
+
|
11
|
+
def macosx?
|
12
|
+
host_os == :macosx
|
13
|
+
end
|
14
|
+
|
15
|
+
def linux?
|
16
|
+
host_os == :linux
|
17
|
+
end
|
18
|
+
|
19
|
+
def unix?
|
20
|
+
host_os == :unix
|
21
|
+
end
|
22
|
+
|
23
|
+
def host_os
|
24
|
+
@os ||= (
|
25
|
+
host_os = RbConfig::CONFIG['host_os']
|
26
|
+
case host_os
|
27
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
28
|
+
:windows
|
29
|
+
when /darwin|mac os/
|
30
|
+
:macosx
|
31
|
+
when /linux/
|
32
|
+
:linux
|
33
|
+
when /solaris|bsd/
|
34
|
+
:unix
|
35
|
+
else
|
36
|
+
raise Error::WebDriverError, "unknown os: #{host_os.inspect}"
|
37
|
+
end
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|