dencity 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 15c325015a579f5731901814dd2e70d850f436e0
4
+ data.tar.gz: aa12fc419b0af227e1991337bf9828165beb0420
5
+ SHA512:
6
+ metadata.gz: dcc65b80b293093023dab072673e5a925e3f997213f879f97d2c6a139966f2f0710f0f080110e72229041a6a38e4d2c3f8fe1dcd99e7e326337865e2272b206f
7
+ data.tar.gz: 88a940ed12da942654fb532e880b69dda1714ae15494a21898dd4c2673b5ad34f41a1f62d11889c2b771ab290be24e6a712bd56cce1fbf8f0ba5fd84427c77b0
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .idea
11
+ .DS_Store
12
+ dencity*.gem
13
+ /data/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,139 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'measures/**/*'
4
+ - 'spec/files/**/measures/**/*'
5
+ - '**/measure.rb'
6
+ - 'spec/files/mongoid/models/**/*'
7
+ - 'spec/files/pat_project/data_point_*/**/*'
8
+ - 'spec/files/pat_project/run/**/*'
9
+ - 'spec/files/mongo_pat1/run/**/*'
10
+
11
+ # ==================== Linters ====================
12
+ Lint/AmbiguousOperator:
13
+ Enabled: true
14
+
15
+ Lint/BlockAlignment:
16
+ Enabled: true
17
+
18
+ Lint/ParenthesesAsGroupedExpression:
19
+ Enabled: true
20
+
21
+ Lint/RequireParentheses:
22
+ Enabled: true
23
+
24
+ # Offense count: 1
25
+ Lint/UnreachableCode:
26
+ Enabled: false
27
+
28
+ # Offense count: 51
29
+ Lint/UselessAssignment:
30
+ Enabled: true
31
+
32
+ # ==================== Styles / Metrics====================
33
+ # Offense count: 4
34
+ Metrics/BlockNesting:
35
+ Max: 4
36
+
37
+ # Offense count: 18
38
+ # Configuration parameters: CountComments.
39
+ Metrics/ClassLength:
40
+ Max: 500
41
+
42
+ # Offense count: 27
43
+ Metrics/CyclomaticComplexity:
44
+ Max: 48
45
+
46
+ # Offense count: 974
47
+ Metrics/LineLength:
48
+ Max: 1200
49
+
50
+ # Offense count: 87
51
+ # Configuration parameters: CountComments.
52
+ Metrics/MethodLength:
53
+ Max: 350
54
+
55
+ # Offense count: 1
56
+ # Configuration parameters: CountKeywordArgs.
57
+ Metrics/ParameterLists:
58
+ Max: 8
59
+
60
+ # ==================== Disabled on Purpose ====================
61
+ # Offense count: 3
62
+ Style/AccessorMethodName:
63
+ Enabled: true
64
+
65
+ # Need to allow indented case statements
66
+ # Offense count: 9
67
+ # Configuration parameters: IndentWhenRelativeTo, SupportedStyles, IndentOneStep.
68
+ Style/CaseIndentation:
69
+ Enabled: false
70
+
71
+ # Offense count: 18
72
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
73
+ Style/ClassAndModuleChildren:
74
+ Enabled: false
75
+
76
+ # Offense count: 2
77
+ Style/ClassVars:
78
+ Enabled: false
79
+
80
+ # Offense count: 42
81
+ Style/Documentation:
82
+ Enabled: false
83
+
84
+ # Offense count: 1
85
+ Style/EachWithObject:
86
+ Enabled: false
87
+
88
+ # Offense count: 2
89
+ # Configuration parameters: AllowedVariables.
90
+ Style/GlobalVars:
91
+ Enabled: false
92
+
93
+ # Offense count: 7
94
+ # Configuration parameters: MinBodyLength.
95
+ Style/GuardClause:
96
+ Enabled: true
97
+
98
+ # Offense count: 17
99
+ # Configuration parameters: MaxLineLength.
100
+ Style/IfUnlessModifier:
101
+ Enabled: false
102
+
103
+ # Do NOT enable. For some reason this is catchy any next which I feel are okay.
104
+ # Offense count: 18
105
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
106
+ Style/Next:
107
+ Enabled: false
108
+
109
+ # Do NOT enable this because it appears $? is different than $CHILD_STATUS.
110
+ # Offense count: 7
111
+ # Cop supports --auto-correct.
112
+ Style/SpecialGlobalVars:
113
+ Enabled: false
114
+
115
+ # More Changes per DLM -- pulled from https://github.com/bbatsov/rubocop/blob/master/config/enabled.yml
116
+ Style/RedundantReturn:
117
+ Description: 'Do not use return where it is not required.'
118
+ StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return'
119
+ Enabled: false
120
+
121
+ Style/NumericLiterals:
122
+ Description: 'Add underscores to large numeric literals to improve readability.'
123
+ StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics'
124
+ Enabled: false
125
+
126
+ Lint/UnusedBlockArgument:
127
+ Description: 'Checks for unused block arguments.'
128
+ StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
129
+ Enabled: false
130
+
131
+ Lint/UnusedMethodArgument:
132
+ Description: 'Checks for unused method arguments.'
133
+ StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
134
+ Enabled: false
135
+
136
+ Lint/UselessAssignment:
137
+ Description: 'Checks for useless assignment to a local variable.'
138
+ StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
139
+ Enabled: false
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dencity.gemspec
4
+ gemspec
@@ -0,0 +1,164 @@
1
+ # Dencity Gem
2
+
3
+ This gem is an API client for DEnCity.org.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'dencity'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install dencity
20
+
21
+ ## Usage
22
+
23
+
24
+ ### Connect to DEnCity
25
+
26
+ Without authentication:
27
+
28
+ ```ruby
29
+ dencity_client = Dencity.connect
30
+ ```
31
+
32
+ On a different host (for development purposes):
33
+
34
+ ```ruby
35
+ dencity_client = Dencity.connect({host_name: <host_name>})
36
+ ```
37
+
38
+ With authentication:
39
+ ```ruby
40
+ dencity_client = Dencity.connect({username: <username>, password: <password>})
41
+ ```
42
+
43
+ To authenticate an existing connection:
44
+ ```ruby
45
+ dencity_client.login(<username>, <password>)
46
+ ```
47
+
48
+ To authenticate using stored credentials in ~/.dencity/config.yml, call the login method without arguments. The gem will generate the config.yml for you to fill out.
49
+ ```ruby
50
+ dencity_client.login
51
+ ```
52
+
53
+ ### Search
54
+ The search method accepts 3 optional parameters: *filters*, *return_only*, and *page*. *filters* is used to narrow the search results. The format is an array of hashes containing 3 keys: name, value, and operator. The list of operators is as follows:
55
+ - = - *Equal to* operator accepts string or number values.
56
+ - ne - *Not equal to* operator accepts string or number values.
57
+ - lt - *Less than* operator accepts number values.
58
+ - lte - *Less than or equal to* operator accepts number values.
59
+ - gt - *Greater than* operator accepts number values.
60
+ - gte - *Greater than or equal to* operator accepts number values.
61
+ - exists - *Exists* operator is used to return structure records where the specified metadatum exists in the structure record metadata. The value key can be left blank or nil for this operator.
62
+ - in - *In* operator accepts an array of values (strings or numbers).
63
+ - nin - * Not in* operator accepts an array of values (strings or numbers).
64
+
65
+ If *filters* is nil, all results will be returned.
66
+
67
+ The *return_only* parameter limits the data within each structure record to be returned. All fields specified in the filters will be returned as well as the fields listed in the *return_only* array. If *return_only* is nil, all fields will be returned.
68
+
69
+ Since only 100 results are return per call, the *page* parameter allows you to step through the result set. With each search call, the total number of results as well as total number of pages are returned. You can use that information to make additional calls. The *page* parameter is 0-based: set ```page=0``` to retrieve the first page of results. If *page* is nil, the first page of results will be returned by default.
70
+
71
+ Example:
72
+ ```ruby
73
+ filters = []
74
+ filters << { name: 'building_area', value: 2800, operator: 'lt' }
75
+ filters << { name: 'building_type', value: ['Community Center'], operator: 'in' }
76
+ return_only = ['related_files']
77
+ page = 3
78
+ results = dencity_client.search(filters, return_only, page)
79
+ ```
80
+
81
+ ### Retrieve an Analysis
82
+ There are two methods of retrieving an analysis: by ID, or by name + userID.
83
+
84
+ Retrieve an analysis by ID:
85
+ ```ruby
86
+ analysis = dencity_client.retrieve_analysis_by_id(<id>)
87
+ ```
88
+ Retrieve by analysis name and userID:
89
+ ```ruby
90
+ analysis = dencity_client.retrieve_analysis_by_name(<analysis_name>, <user_id>)
91
+ ```
92
+ You have access to your own user_id from the response object returned once you login to DEnCity, or from your account page on DEnCity.org.
93
+
94
+ ### Upload an Analysis
95
+ First load an analysis from a json file, then upload to DEnCity.org.
96
+ ```ruby
97
+ analysis = dencity_client.load_analysis(<json_file_path>)
98
+ analysis_response = analysis.push
99
+ ```
100
+ To catch the response and ensure that the analysis was uploaded to DEnCity.org correctly, you may want to use a begin-end block:
101
+ ```ruby
102
+ analysis = dencity_client.load_analysis(<json_file_path>)
103
+ begin
104
+ analysis_response = analysis.push
105
+ rescue StandardError => e
106
+ printf "%-40s %s\n", 'Upload Analysis', 'FAIL'
107
+ puts e
108
+ else
109
+ printf "%-40s %s\n", 'Upload Analysis', 'SUCCESS'
110
+ puts analysis_response
111
+ end
112
+ ```
113
+ When uploading an analysis, the combination of user_id and user_defined_id will be checked.
114
+ If an analysis with the same user_id and user_defined_id already exists on DEnCity.org, it will be updated; otherwise, a new analysis will be created.
115
+
116
+ ### Upload a Structure
117
+ To upload a structure, you will need an analysis_id. You can also specify a user_defined_id to identify your structure for future retrieving.
118
+ ```ruby
119
+ structure = dencity_client.load_structure(<analysis_id>, <user_defined_id> <json_file_path>)
120
+ structure_response = structure.push
121
+ ```
122
+ When uploading a structure, the combination of user_id and user_defined_id will be checked.
123
+ If a structure with the same user_id and user_defined_id already exists on DEnCity.org, it will be updated; otherwise, a new structure will be created.
124
+
125
+ ### Bulk Structures Upload
126
+ Each time a structure is loaded, it is appended to the structures array defined on the Dencity Client: ```dencity_client.structures```. You can bulk upload all structures to DenCity.org:
127
+ ```ruby
128
+ dencity_client.bulk_upload_structures
129
+ ```
130
+
131
+ ### Upload a file
132
+ Files can be attached to an uploaded structure. You will need to pass in the filepath as well as the desired filename on DEnCity.org. If no filename is specified, the file's current name will be used.
133
+
134
+ ```ruby
135
+ response = structure.upload_file(<file_path>, <file_name>)
136
+ ```
137
+
138
+ ### Delete an uploaded file
139
+ Uploaded files can also be deleted from DEncity.org:
140
+ ```ruby
141
+ response = structure.delete_file(<file_name>)
142
+ ```
143
+
144
+ ### Additional Examples
145
+
146
+ You can also do simple GETs using the dencity_get method.
147
+
148
+ Get all analyses:
149
+ ```ruby
150
+ response = dencity_client.dencity_get('analyses')
151
+ ```
152
+ Get structure by ID:
153
+ ```ruby
154
+ response = dencity_client.dencity_get('structures/<structure_id>')
155
+ ```
156
+
157
+ The file example_script.rb contains many usage examples.
158
+
159
+ ## Development
160
+
161
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
162
+
163
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
164
+
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'dencity'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dencity/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'dencity'
8
+ spec.version = Dencity::VERSION
9
+ spec.authors = %w(kflemin nlong)
10
+ spec.email = ['katherine.fleming@nrel.gov']
11
+
12
+ spec.summary = 'Client library for DEnCity.org.'
13
+ # spec.description = %q{}
14
+ spec.homepage = 'https://github.com/NREL/dencity-gem'
15
+
16
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
17
+ # delete this section to allow pushing this gem to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
20
+ else
21
+ fail 'RubyGems 2.0 or newer is required to protect against public '\
22
+ 'gem pushes.'
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_development_dependency 'bundler', '~> 1.10'
31
+ spec.add_development_dependency 'rake', '~> 10.0'
32
+ spec.add_development_dependency 'rspec'
33
+ spec.add_dependency('faraday')
34
+ spec.add_dependency('faraday_middleware')
35
+ spec.add_dependency('multi_json')
36
+ spec.add_dependency('parallel')
37
+ spec.add_dependency('hashie')
38
+ end
@@ -0,0 +1,152 @@
1
+ require 'dencity'
2
+
3
+ d = Dencity.connect(host_name: 'http://localhost:3000/')
4
+
5
+ # to login using stored credentials:
6
+ # begin
7
+ # login = d.login
8
+ # rescue StandardError => e
9
+ # printf "%-40s %s\n", 'Login', 'FAIL'
10
+ # puts e
11
+ # else
12
+ # printf "%-40s %s\n", 'Login', 'SUCCESS'
13
+ # puts login
14
+ # end
15
+
16
+ puts '****** SEARCH ********'
17
+ filters = []
18
+ filters << { name: 'building_area', value: 2737.26, operator: 'lt' }
19
+ filters << { name: 'building_type', value: ['Community Center'], operator: 'in' }
20
+ return_only = ['related_files']
21
+ page = 0 # pages are 0-based
22
+ begin
23
+ results = d.search(filters, return_only, page)
24
+ rescue StandardError => e
25
+ printf "%-40s %s\n", 'Search', 'FAIL'
26
+ puts e
27
+ else
28
+ printf "%-40s %s\n", 'Search', 'SUCCESS'
29
+ end
30
+ # puts "RESULTS: #{results}"
31
+ puts "Total number of pages in results set: #{results.total_pages}"
32
+ puts "Number of results: #{results.results.size}"
33
+
34
+ puts '******** GET ANALYSIS BY NAME & USER_ID *********'
35
+ begin
36
+ analysis = d.retrieve_analysis_by_name('test_analysis', '53a3656d986ffba2c5000001')
37
+ rescue StandardError => e
38
+ printf "%-40s %s\n", 'Retrieve Analysis', 'FAIL'
39
+ puts e
40
+ else
41
+ printf "%-40s %s\n", 'Retrieve Analysis', 'SUCCESS'
42
+ end
43
+
44
+ puts '******** GET ANALYSIS BY ID ************'
45
+ begin
46
+ analysis = d.retrieve_analysis_by_id(analysis.id)
47
+ rescue StandardError => e
48
+ printf "%-40s %s\n", 'Retrieve Analysis', 'FAIL'
49
+ puts e
50
+ else
51
+ printf "%-40s %s\n", 'Retrieve Analysis', 'SUCCESS'
52
+ end
53
+
54
+ puts '********* GET ANALYSES *************'
55
+ begin
56
+ analyses = d.dencity_get('analyses')
57
+ rescue StandardError => e
58
+ printf "%-40s %s\n", 'Get Analyses', 'FAIL'
59
+ puts e
60
+ else
61
+ printf "%-40s %s\n", 'Get Analyses', 'SUCCESS'
62
+ # puts analyses
63
+ end
64
+
65
+ puts '********* LOGIN ************'
66
+ begin
67
+ login = d.login('nicholas.long@nrel.gov', 'testing123')
68
+ rescue StandardError => e
69
+ printf "%-40s %s\n", 'Login', 'FAIL'
70
+ puts e
71
+ else
72
+ printf "%-40s %s\n", 'Login', 'SUCCESS'
73
+ puts login
74
+ end
75
+
76
+ puts '********* Upload ANALYSIS **********'
77
+
78
+ analysis = d.load_analysis('./spec/data/analysis.json')
79
+ begin
80
+ a_response = analysis.push
81
+ rescue StandardError => e
82
+ printf "%-40s %s\n", 'Upload Analysis', 'FAIL'
83
+ puts e
84
+ else
85
+ printf "%-40s %s\n", 'Upload Analysis', 'SUCCESS'
86
+ puts a_response
87
+ end
88
+
89
+ puts '********* Upload STRUCTURE *********'
90
+ structure = d.load_structure(analysis.analysis.id, 'testing!', './spec/data/structure.json')
91
+ puts "STRUCTURE: #{structure}"
92
+ begin
93
+ s_response = structure.push
94
+ rescue StandardError => e
95
+ printf "%-40s %s\n", 'Upload Structure', 'FAIL'
96
+ puts e
97
+ else
98
+ printf "%-40s %s\n", 'Upload Structure', 'SUCCESS'
99
+ puts s_response
100
+ end
101
+
102
+ puts '******* Upload RELATED FILE *******'
103
+ begin
104
+ response = structure.upload_file('./spec/data/related_file.txt', 'test-related-file.txt')
105
+ rescue StandardError => e
106
+ printf "%-40s %s\n", 'Upload RelatedFile', 'FAIL'
107
+ puts e
108
+ else
109
+ printf "%-40s %s\n", 'Upload RelatedFile', 'SUCCESS'
110
+ puts response
111
+ end
112
+
113
+ puts '********** GET STRUCTURE *************'
114
+ begin
115
+ new_structure = d.dencity_get("structures/#{structure.structure.id}")
116
+ rescue StandardError => e
117
+ printf "%-40s %s\n", 'Retrieve Structure', 'FAIL'
118
+ puts e
119
+ else
120
+ printf "%-40s %s\n", 'Retrieve Structure', 'SUCCESS'
121
+ end
122
+ puts new_structure
123
+
124
+ puts '******* Delete a RELATED FILE *******'
125
+ begin
126
+ response = structure.delete_file('test-related-file.txt')
127
+ rescue StandardError => e
128
+ printf "%-40s %s\n", 'Delete File', 'FAIL'
129
+ puts e
130
+ else
131
+ printf "%-40s %s\n", 'Delete File', 'SUCCESS'
132
+ puts response
133
+ end
134
+
135
+ puts '********** BULK UPLOAD ***********'
136
+ d.load_structure(analysis.analysis.id, 'testing_bulk_1', './spec/data/structure.json')
137
+ d.load_structure(analysis.analysis.id, 'testing_bulk_2', './spec/data/structure.json')
138
+ d.load_structure(analysis.analysis.id, 'testing_bulk_3', './spec/data/structure.json')
139
+
140
+ puts "structures: #{d.structures.size}"
141
+ begin
142
+ b_response = d.bulk_upload_structures
143
+ rescue StandardError => e
144
+ printf "%-40s %s\n", 'Upload Structure', 'FAIL'
145
+ puts e
146
+ else
147
+ printf "%-40s %s\n", 'Upload Structure', 'SUCCESS'
148
+ puts b_response
149
+ end
150
+
151
+ puts '******* LOGOUT *******'
152
+ d.logout
@@ -0,0 +1,18 @@
1
+ require 'dencity/version'
2
+ require_relative 'dencity/client'
3
+ require_relative 'dencity/response'
4
+ require_relative 'dencity/error'
5
+
6
+ require 'multi_json'
7
+ require 'hashie'
8
+ require 'faraday'
9
+ require 'faraday_middleware'
10
+ require 'parallel'
11
+
12
+ # Main module
13
+ module Dencity
14
+ # initialize / connect
15
+ def self.connect(options = {})
16
+ Dencity::Client.new(options)
17
+ end
18
+ end
@@ -0,0 +1,162 @@
1
+ require_relative 'request'
2
+ require_relative 'client/search'
3
+ require_relative 'client/analysis'
4
+ require_relative 'client/structure'
5
+ require_relative '../faraday/raise_http_exception'
6
+
7
+ require 'fileutils'
8
+ require 'yaml'
9
+
10
+ module Dencity
11
+ # API Client class
12
+ class Client
13
+ # connection options
14
+ attr_accessor :options
15
+ attr_accessor :analysis
16
+ attr_accessor :structures
17
+
18
+ include Request
19
+ include Dencity::Search
20
+
21
+ # connect to DEnCity (unauthenticated)
22
+ def initialize(options = {})
23
+ puts 'Initializing...'
24
+ defaults = {
25
+ username: nil,
26
+ password: nil,
27
+ host_name: 'https://dencity.org',
28
+ user_agent: "DEnCity Ruby Client #{Dencity::VERSION}".freeze,
29
+ logging: nil
30
+ }
31
+
32
+ @options = Hashie::Mash.new(defaults.merge(options))
33
+ # connection to site
34
+ @connection = connect
35
+
36
+ @default_number_threads = 4
37
+
38
+ # initialize analysis and structures
39
+ # array of structures
40
+ @structures = []
41
+ @analysis = nil
42
+ end
43
+
44
+ # for authenticated actions
45
+ def login(username = nil, password = nil)
46
+ # check config.yml
47
+ if !username.nil? && !password.nil?
48
+ @options.username = username
49
+ @options.password = password
50
+ else
51
+ # load login info from config file
52
+ config_path = File.expand_path('~') + '/.dencity'
53
+ config_name = 'config.yml'
54
+ if File.exist?(config_path + '/' + config_name)
55
+ puts "loading config settings from #{config_path + '/' + config_name}"
56
+
57
+ config = YAML.load_file(config_path + '/' + config_name)
58
+ puts "HEY! #{Hashie::Mash.new(config).inspect}"
59
+ puts "CONFIG: #{config.inspect}"
60
+ @options.username = config[:username]
61
+ @options.password = config[:password]
62
+ @options.host_name = config[:host_name]
63
+
64
+ else
65
+ # location of template file
66
+ FileUtils.mkdir_p(config_path)
67
+ puts default_yaml
68
+ File.open(config_path + '/' + config_name, 'w') do |file|
69
+ file << default_yaml.to_yaml
70
+ end
71
+ fail "******** Please fill in user credentials in #{config_path}/#{config_name} file. DO NOT COMMIT THIS FILE. **********"
72
+ end
73
+
74
+ end
75
+
76
+ @connection = connect
77
+ post('api/login')
78
+ end
79
+
80
+ def logout
81
+ # @cookie = nil
82
+ @options.username = nil
83
+ @options.password = nil
84
+ @connection = nil
85
+ end
86
+
87
+ def connected?
88
+ @connection != nil
89
+ end
90
+
91
+ # load structure
92
+ def load_structure(analysis_id = nil, user_defined_id = nil, path = nil)
93
+ @structures << Dencity::Structure.new(analysis_id, user_defined_id, path, @connection)
94
+ @structures.last
95
+ end
96
+
97
+ # bulk upload structures
98
+ def bulk_upload_structures(number_of_threads = @default_number_threads)
99
+ Parallel.each(@structures, number_of_threads: number_of_threads) do |structure|
100
+ structure.push
101
+ end
102
+ end
103
+
104
+ # load analysis
105
+ # this function is needed to pass @connection at least
106
+ def load_analysis(path = nil)
107
+ @analysis = Dencity::Analysis.new(path, @connection)
108
+ end
109
+
110
+ # retrieve analysis
111
+ # must pass in @connection
112
+ def retrieve_analysis_by_name(name, user_id)
113
+ @analysis = Dencity::Analysis.new(nil, @connection)
114
+ @analysis.retrieve_by_name(name, user_id)
115
+ end
116
+
117
+ # retrieve analysis by ID
118
+ # must pass in @connection
119
+ def retrieve_analysis_by_id(id)
120
+ @analysis = Dencity::Analysis.new(nil, @connection)
121
+ @analysis.retrieve_by_id(id)
122
+ end
123
+
124
+ # generic GET action to retrieve from DEnCity
125
+ # example paths: 'analyses', 'structures/<structure_id>'
126
+ def dencity_get(path)
127
+ get(path)
128
+ end
129
+
130
+ private
131
+
132
+ def connect(raw = false)
133
+ options = set_options
134
+ puts "CONNECTING TO: #{@options.host_name}, USER: #{@options.username}"
135
+ Faraday::Connection.new(options) do |c|
136
+ c.use FaradayMiddleware::Mashify unless raw
137
+ # basic auth
138
+ c.basic_auth(@options.username, @options.password) unless @options.username.nil? || @options.password.nil?
139
+ c.use FaradayMiddleware::RaiseHttpException
140
+ c.response :json, content_type: /\bjson$/
141
+ c.response :logger if @logging
142
+ c.adapter Faraday.default_adapter
143
+ end
144
+ end
145
+
146
+ def set_options
147
+ { headers: {
148
+ 'Accept' => 'application/json; charset=utf-8',
149
+ 'User-Agent' => @options.user_agent,
150
+ # 'Cookie' => @options.cookie,
151
+ # 'X-CSRF-Token' => @options.access_token
152
+ }.reject { |_k, v| v.nil? },
153
+ ssl: { verify: false },
154
+ url: @options.host_name
155
+ }
156
+ end
157
+
158
+ def default_yaml
159
+ settings = { host_name: 'http://localhost:3000', username: 'ENTER_DENCITY_USERNAME', password: 'ENTER_DENCITY_PASSWORD' }
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,91 @@
1
+ module Dencity
2
+ # Analysis methods
3
+ class Analysis
4
+ include Request
5
+
6
+ attr_accessor :analysis
7
+ attr_accessor :measure_definitions
8
+
9
+ # initialize
10
+ def initialize(path = nil, connection)
11
+ # for analysis, user_defined_id is created in @analysis (not a separate variable)
12
+ @analysis = Hashie::Mash.new
13
+ @measure_definitions = Hashie::Mash.new
14
+
15
+ @connection = connection
16
+
17
+ # initialize with JSON
18
+ unless path.nil?
19
+ load_from_file(path)
20
+ end
21
+
22
+ @upload_retries = nil
23
+ end
24
+
25
+ # get analysis by id
26
+ def retrieve_by_id(id)
27
+ response = get("api/analyses/#{id}")
28
+ @analysis = Hashie::Mash.new(response) unless response.error?
29
+ response
30
+ end
31
+
32
+ # get analysis by name and user_id
33
+ def retrieve_by_name(name, user_id)
34
+ data = { name: name, user_id: user_id }
35
+ response = get('api/retrieve_analysis', data)
36
+ @analysis = Hashie::Mash.new(response) unless response.error?
37
+ response
38
+ end
39
+
40
+ # load analysis from JSON file
41
+ def load_from_file(path)
42
+ return unless File.exist?(path)
43
+ json_data = File.read(path)
44
+ load_raw_json(json_data)
45
+ end
46
+
47
+ # load analysis from raw JSON
48
+ def load_raw_json(json_data)
49
+ temp = Hashie::Mash.new(MultiJson.load(json_data))
50
+ @analysis = temp.analysis ? temp.analysis : Hashie::Mash.new
51
+ @measure_definitions = temp.measure_definitions ? temp.measure_definitions : Hashie::Mash.new
52
+ end
53
+
54
+ def push
55
+ begin
56
+ @upload_retries ||= 0
57
+
58
+ response = post('api/analysis', format_analysis)
59
+ @analysis.id = response.analysis.id if (response.analysis && response.analysis.id)
60
+ return response if response
61
+ rescue StandardError => se
62
+ # Decide if we should fail based on number of retries
63
+ if @upload_retries < 3
64
+
65
+ raise 'could not upload'
66
+ else
67
+ # or here: @upload_retries = nil
68
+ return se
69
+ end
70
+ end
71
+ rescue => e
72
+ @upload_retries += 1
73
+ sleep 2
74
+ retry
75
+ ensure
76
+ # always do this
77
+ # verify that this is only called if the retry is not triggered
78
+ @upload_retries = nil
79
+ end
80
+
81
+ private
82
+
83
+ # format analysis for uploading
84
+ def format_analysis
85
+ data_hash = Hashie::Mash.new
86
+ data_hash.analysis = @analysis
87
+ data_hash.measure_definitions = @measure_definitions
88
+ MultiJson.dump(data_hash)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,20 @@
1
+ module Dencity
2
+ # Search methods
3
+ module Search
4
+ # filters: array of hashes with keys: name, value, operator
5
+ # return_only: array of metadata to return
6
+ # If blank, all metadata will be returned
7
+ # for iterating over results set (100 per page)
8
+ def search(filters = [{ name: 'building_type', value: 'Office', operator: '=' }], return_only = [], page = 0)
9
+ # must have 1 filter or search won't work
10
+ object = {}
11
+ object['filters'] = filters
12
+ object['return_only'] = return_only unless return_only.nil?
13
+ object['page'] = page
14
+
15
+ data = MultiJson.dump(object)
16
+ puts "DATA: #{data}"
17
+ post('api/search', data)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,158 @@
1
+ module Dencity
2
+ # Structure methods
3
+ class Structure
4
+ include Request
5
+
6
+ attr_accessor :analysis_id
7
+ attr_accessor :user_defined_id
8
+ attr_accessor :structure
9
+ attr_accessor :measure_instances
10
+
11
+ # initialize
12
+ def initialize(analysis_id = nil, user_defined_id = nil, path = nil, connection)
13
+ @analysis_id = analysis_id
14
+ @user_defined_id = user_defined_id
15
+ @structure = Hashie::Mash.new
16
+ @measure_instances = Hashie::Mash.new
17
+
18
+ @connection = connection
19
+
20
+ # initialize with json file
21
+ unless path.nil?
22
+ load_from_file(path)
23
+ end
24
+
25
+ @upload_retries = nil
26
+ end
27
+
28
+ def push
29
+ begin
30
+ @upload_retries ||= 0
31
+
32
+ response = post('api/structure', format_structure)
33
+ @structure.id = response['id'] if response['id']
34
+ return response if response
35
+ rescue StandardError => se
36
+ # Decide if we should fail based on number of retries
37
+ if @upload_retries < 3
38
+
39
+ raise 'could not upload'
40
+ else
41
+ # or here: @upload_retries = nil
42
+ return se
43
+ end
44
+ end
45
+ rescue => e
46
+ @upload_retries += 1
47
+ sleep 2
48
+ retry
49
+ ensure
50
+ # always do this
51
+ # verify that this is only called if the retry is not triggered
52
+ @upload_retries = nil
53
+ end
54
+
55
+ # load structure from json file into a mash
56
+ def load_from_file(path)
57
+ return unless File.exist?(path)
58
+ json_data = File.read(path)
59
+ load_raw_json(json_data)
60
+ end
61
+
62
+ # load structure from raw json
63
+ def load_raw_json(json_data)
64
+ temp = Hashie::Mash.new(MultiJson.load(json_data))
65
+ @structure = temp.structure ? temp.structure : Hashie::Mash.new
66
+ @measure_instances = temp.measure_instances ? temp.measure_instances : Hashie::Mash.new
67
+ # these could be set in the file
68
+ @analysis_id = temp.structure.analysis_id if temp.structure.analysis_id
69
+ @user_defined_id = temp.structure.user_defined_id if temp.structure.user_defined_id
70
+
71
+ return true
72
+ end
73
+
74
+ # upload file
75
+ def upload_file(path, file_name = nil)
76
+ fail 'No Structure ID defined for structure. Can\'t upload file' if @structure.id.nil?
77
+
78
+ file = File.open(path, 'rb')
79
+ the_file = Base64.strict_encode64(file.read)
80
+ file.close
81
+
82
+ # file_data param
83
+ file_data = {}
84
+ file_data['file_name'] = file_name.nil? ? File.basename(path) : file_name
85
+ file_data['file'] = the_file
86
+
87
+ data = Hashie::Mash.new
88
+ data.structure_id = @structure.id
89
+ data.file_data = file_data
90
+
91
+ push_file('api/related_file', MultiJson.dump(data))
92
+ end
93
+
94
+ # delete an uploaded file
95
+ # if structure_id is nil, will use @structure.id
96
+ def delete_file(file_name)
97
+ fail 'No Structure ID defined for structure. Can\'t delete file' if @structure.id.nil?
98
+
99
+ data = Hashie::Mash.new
100
+ data.structure_id = @structure.id
101
+ data.file_name = file_name
102
+
103
+ push_file('api/remove_file', MultiJson.dump(data))
104
+ end
105
+
106
+ # push file w/ retry
107
+ def push_file(path, data)
108
+ begin
109
+ @upload_retries ||= 0
110
+ response = post(path, data)
111
+ return response if response
112
+ rescue StandardError => se
113
+ # Decide if we should fail based on number of retries
114
+ if @upload_retries < 3
115
+ if path.include? 'remove'
116
+ raise 'could not delete file'
117
+ else
118
+ raise 'could not upload file'
119
+ end
120
+ else
121
+ return se
122
+ end
123
+ end
124
+ rescue => e
125
+ @upload_retries += 1
126
+ sleep 2
127
+ retry
128
+ ensure
129
+ # verify that this is only called if the retry is not triggered
130
+ @upload_retries = nil
131
+ end
132
+
133
+ private
134
+
135
+ # formats structure parameters for posting
136
+ def format_structure
137
+ # generate name/value pairs for structure metadata
138
+ formatted_meta = []
139
+ @structure.each do |k, v|
140
+ formatted_meta << { name: k, value: v } unless %w(id user_defined_id analysis_id).include?(k)
141
+ end
142
+ new_struct = Hashie::Mash.new
143
+ new_struct.metadata = formatted_meta
144
+
145
+ # TODO: what if it's already in the structure hash?
146
+ # add user_defined_id to structure
147
+ new_struct.user_defined_id = @user_defined_id
148
+ new_struct.analysis_id = @analysis_id
149
+
150
+ data_hash = Hashie::Mash.new
151
+ data_hash.structure = new_struct
152
+ data_hash.measure_instances = @measure_instances ? @measure_instances : []
153
+
154
+ # convert to json
155
+ MultiJson.dump(data_hash)
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,18 @@
1
+ module Dencity
2
+ class Error < StandardError; end
3
+
4
+ # For 400's
5
+ class BadRequest < Error; end
6
+
7
+ # For 401's
8
+ class Unauthorized < Error; end
9
+
10
+ # For 404's.
11
+ class NotFound < Error; end
12
+
13
+ # For 406's.
14
+ class NotAcceptable < Error; end
15
+
16
+ # For 500's
17
+ class InternalServerError < Error; end
18
+ end
@@ -0,0 +1,2 @@
1
+
2
+ #
@@ -0,0 +1,46 @@
1
+ module Dencity
2
+ # Request module
3
+ module Request
4
+ # Perform an HTTP GET request (options should not be in JSON)
5
+ def get(path, options = {}, raw = false,
6
+ unformatted = false, no_response_wrapper = false)
7
+ request(:get, path, options, raw, unformatted, no_response_wrapper)
8
+ end
9
+
10
+ # Perform an HTTP POST request (options should be in JSON already)
11
+ def post(path, options = {}, raw = false,
12
+ unformatted = false, no_response_wrapper = false)
13
+ request(:post, path, options, raw, unformatted, no_response_wrapper)
14
+ end
15
+
16
+ private
17
+
18
+ # Perform an HTTP request
19
+ def request(method, path, options, raw = false,
20
+ unformatted = false, no_response_wrapper = false)
21
+ response = @connection.send(method) do |request|
22
+ path = formatted_path(path) unless unformatted
23
+ puts "PATH: #{path}"
24
+
25
+ case method
26
+ when :get, :delete
27
+ request.url(path, options)
28
+ when :post, :put
29
+ request.path = path
30
+ request.body = options unless options.empty?
31
+ end
32
+
33
+ request.headers['Content-Type'] = 'application/json'
34
+
35
+ end
36
+
37
+ return response if raw
38
+ return response.body if no_response_wrapper
39
+ Response.create(response.body, response.status)
40
+ end
41
+
42
+ def formatted_path(path)
43
+ [path, 'json'].compact.join('.')
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ # Response Format
2
+
3
+ module Dencity
4
+ # Response module
5
+ module Response
6
+ def self.create(response_hash, status)
7
+ if response_hash.class.name == 'Array'
8
+ data = Hashie::Mash.new
9
+ data.data = response_hash
10
+ else
11
+ data = response_hash.dup rescue response_hash
12
+ end
13
+ data.extend(self)
14
+ data.status = status
15
+ data
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,4 @@
1
+ # Dencity version
2
+ module Dencity
3
+ VERSION = '0.1.0'
4
+ end
@@ -0,0 +1,45 @@
1
+ require 'faraday'
2
+
3
+ # Yeah, this is almost a carbon copy of the way the Instagram gem does it.
4
+ # But screw it. It's open source, and it works. :P
5
+
6
+ module FaradayMiddleware
7
+ class RaiseHttpException < Faraday::Middleware
8
+ def call(env)
9
+ @app.call(env).on_complete do |response|
10
+ case response[:status].to_i
11
+ when 400
12
+ fail Dencity::BadRequest, error_message_400(response)
13
+ when 401
14
+ fail Dencity::Unauthorized, error_message_400(response)
15
+ when 404
16
+ fail Dencity::NotFound, error_message_400(response)
17
+ when 406
18
+ fail Dencity::NotAcceptable, error_message_400(response)
19
+ when 500
20
+ fail Dencity::InternalServerError, error_message_500(response, 'Internal Server Error.')
21
+ end
22
+ end
23
+ end
24
+
25
+ def initialize(app)
26
+ super app
27
+ @parser = nil
28
+ end
29
+
30
+ private
31
+
32
+ def error_message_400(response)
33
+ "#{response[:method].to_s.upcase} #{response[:url]}: #{response[:status]} #{error_body(response[:body])}"
34
+ end
35
+
36
+ def error_body(body)
37
+ return unless !body.nil? && !body.empty? && body.is_a?(String)
38
+ body = ::MultiJson.load(body)
39
+ end
40
+
41
+ def error_message_500(response, body = nil)
42
+ "#{response[:method].to_s.upcase} #{response[:url]}: #{[response[:status].to_s + ':', body].compact.join(' ')}"
43
+ end
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,180 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dencity
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - kflemin
8
+ - nlong
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2016-01-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '1.10'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '1.10'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: faraday
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: faraday_middleware
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: multi_json
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: parallel
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: hashie
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :runtime
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description:
127
+ email:
128
+ - katherine.fleming@nrel.gov
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - .gitignore
134
+ - .rspec
135
+ - .rubocop.yml
136
+ - .travis.yml
137
+ - Gemfile
138
+ - README.md
139
+ - Rakefile
140
+ - bin/console
141
+ - bin/setup
142
+ - dencity.gemspec
143
+ - example_script.rb
144
+ - lib/dencity.rb
145
+ - lib/dencity/client.rb
146
+ - lib/dencity/client/analysis.rb
147
+ - lib/dencity/client/search.rb
148
+ - lib/dencity/client/structure.rb
149
+ - lib/dencity/error.rb
150
+ - lib/dencity/related_file.rb
151
+ - lib/dencity/request.rb
152
+ - lib/dencity/response.rb
153
+ - lib/dencity/version.rb
154
+ - lib/faraday/raise_http_exception.rb
155
+ homepage: https://github.com/NREL/dencity-gem
156
+ licenses: []
157
+ metadata:
158
+ allowed_push_host: 'TODO: Set to ''http://mygemserver.com'''
159
+ post_install_message:
160
+ rdoc_options: []
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - '>='
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - '>='
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubyforge_project:
175
+ rubygems_version: 2.0.3
176
+ signing_key:
177
+ specification_version: 4
178
+ summary: Client library for DEnCity.org.
179
+ test_files: []
180
+ has_rdoc: