gbifrb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,223 @@
1
+ require "faraday"
2
+ require 'faraday_middleware'
3
+ require "multi_json"
4
+ require "gbifrb/error"
5
+ require "gbifrb/request"
6
+ require "gbifrb/constants"
7
+ require 'gbifrb/helpers/configuration'
8
+ require 'gbifrb/faraday'
9
+ require 'gbifrb/utils'
10
+
11
+ ##
12
+ # Gbif::Species
13
+ #
14
+ # Class to perform HTTP requests to the GBIF API
15
+ # @!macro gbif_params
16
+ # @param offset [Fixnum] Number of record to start at, any non-negative integer. Default: 0
17
+ # @param limit [Fixnum] Number of results to return. Default: 100
18
+ # @param verbose [Boolean] Print request headers to stdout. Default: false
19
+ module Gbif
20
+ module Species
21
+ ##
22
+ # Search the GBIF taxonomic backbone
23
+ #
24
+ # @!macro gbif_params
25
+ # @!macro gbif_options
26
+ # @param name [String] Full scientific name potentially with authorship (required)
27
+ # @param rank [String] The rank given as our rank enum. (optional)
28
+ # @param kingdom [String] If provided default matching will also try to match against this
29
+ # if no direct match is found for the name alone. (optional)
30
+ # @param phylum [String] If provided default matching will also try to match against this
31
+ # if no direct match is found for the name alone. (optional)
32
+ # @param clazz [String] If provided default matching will also try to match against this
33
+ # if no direct match is found for the name alone. (optional)
34
+ # @param order [String] If provided default matching will also try to match against this
35
+ # if no direct match is found for the name alone. (optional)
36
+ # @param family [String] If provided default matching will also try to match against this
37
+ # if no direct match is found for the name alone. (optional)
38
+ # @param genus [String] If provided default matching will also try to match against this
39
+ # if no direct match is found for the name alone. (optional)
40
+ # @param strict [bool] If True it (fuzzy) matches only the given name, but never a
41
+ # taxon in the upper classification (optional)
42
+ # @return [Hash] A hash
43
+ #
44
+ # @example
45
+ # require 'gbifrb'
46
+ #
47
+ # species = Gbif::Species
48
+ # species.name_backbone(name: "Helianthus")
49
+ # species.name_backbone(name: "Poa")
50
+ def self.name_backbone(name:, rank: nil, kingdom: nil, phylum: nil,
51
+ clazz: nil, order: nil, family: nil, genus: nil, strict: nil,
52
+ offset: nil, limit: nil, verbose: nil, options: nil)
53
+
54
+ arguments = { name: name, rank: rank, kingdom: kingdom,
55
+ phylum: phylum, class: clazz, order: order,
56
+ family: family, genus: genus, strict: strict,
57
+ offset: offset, limit: limit }.tostrings
58
+ opts = arguments.delete_if { |k, v| v.nil? }
59
+ Request.new("species/match", opts, verbose, options).perform
60
+ end
61
+
62
+ ##
63
+ # Search the GBIF suggester
64
+ #
65
+ # @!macro gbif_params
66
+ # @!macro gbif_options
67
+ # @param q [String] Simple search parameter. The value for this parameter can be a
68
+ # simple word or a phrase. Wildcards can be added to the simple word parameters only,
69
+ # e.g. 'q=*puma*' (Required)
70
+ # @param datasetKey [String] Filters by the checklist dataset key (a uuid, see examples)
71
+ # @param rank [String] A taxonomic rank. One of 'class', 'cultivar', 'cultivar_group', 'domain', 'family',
72
+ # 'form', 'genus', 'informal', 'infrageneric_name', 'infraorder', 'infraspecific_name',
73
+ # 'infrasubspecific_name', 'kingdom', 'order', 'phylum', 'section', 'series', 'species', 'strain', 'subclass',
74
+ # 'subfamily', 'subform', 'subgenus', 'subkingdom', 'suborder', 'subphylum', 'subsection', 'subseries',
75
+ # 'subspecies', 'subtribe', 'subvariety', 'superclass', 'superfamily', 'superorder', 'superphylum',
76
+ # 'suprageneric_name', 'tribe', 'unranked', or 'variety'.
77
+ # @return [Hash] A hash
78
+ #
79
+ # @example
80
+ # require 'gbifrb'
81
+ #
82
+ # species = Gbif::Species
83
+ # species.name_suggest(q: "Helianthus")
84
+ # species.name_suggest(q: "Poa")
85
+ def self.name_suggest(q: nil, datasetKey: nil, rank: nil, limit: 100, offset: nil,
86
+ verbose: nil, options: nil)
87
+
88
+ arguments = { q: q, datasetKey: datasetKey, rank: rank,
89
+ limit: limit, offset: offset }.tostrings
90
+ opts = arguments.delete_if { |k, v| v.nil? }
91
+ Request.new("species/suggest", opts, verbose, options).perform
92
+ end
93
+
94
+ ##
95
+ # Search for GBIF name usages
96
+ #
97
+ # @!macro gbif_params
98
+ # @!macro gbif_options
99
+ # @param key [Fixnum] A GBIF key for a taxon
100
+ # @param name [String] Filters by a case insensitive, canonical namestring,
101
+ # e.g. 'Puma concolor'
102
+ # @param data [String] The type of data to get. Default: 'all'. Options: 'all',
103
+ # 'verbatim', 'name', 'parents', 'children', 'related', 'synonyms', 'descriptions',
104
+ # 'distributions', 'media', 'references', 'speciesProfiles', 'vernacularNames',
105
+ # 'typeSpecimens', 'root'
106
+ # @param language [String] Language, default is english
107
+ # @param datasetKey [String] Filters by the dataset's key (a uuid)
108
+ # @param uuid [String] A uuid for a dataset. Should give exact same results as datasetKey.
109
+ # @param sourceId [Fixnum] Filters by the source identifier.
110
+ # @param rank [String] Taxonomic rank. Filters by taxonomic rank as one of:
111
+ # 'CLASS', 'CULTIVAR', 'CULTIVAR_GROUP', 'DOMAIN', 'FAMILY', 'FORM', 'GENUS', 'INFORMAL',
112
+ # 'INFRAGENERIC_NAME', 'INFRAORDER', 'INFRASPECIFIC_NAME', 'INFRASUBSPECIFIC_NAME',
113
+ # 'KINGDOM', 'ORDER', 'PHYLUM', 'SECTION', 'SERIES', 'SPECIES', 'STRAIN', 'SUBCLASS', 'SUBFAMILY',
114
+ # 'SUBFORM', 'SUBGENUS', 'SUBKINGDOM', 'SUBORDER', 'SUBPHYLUM', 'SUBSECTION', 'SUBSERIES',
115
+ # 'SUBSPECIES', 'SUBTRIBE', 'SUBVARIETY', 'SUPERCLASS', 'SUPERFAMILY', 'SUPERORDER',
116
+ # 'SUPERPHYLUM', 'SUPRAGENERIC_NAME', 'TRIBE', 'UNRANKED', 'VARIETY'
117
+ # @param shortname [String] A short name..need more info on this?
118
+ # @return [Array] An array of hashes
119
+ #
120
+ # @example
121
+ # require 'gbifrb'
122
+ #
123
+ # speices = Gbif::Species
124
+ # speices.name_usage(name: "Helianthus")
125
+ # speices.name_usage(name: "Poa")
126
+ def self.name_usage(key: nil, name: nil, data: 'all', language: nil,
127
+ datasetKey: nil, uuid: nil, sourceId: nil, rank: nil, shortname: nil,
128
+ limit: 100, offset: nil, verbose: nil, options: nil)
129
+
130
+ arguments = { key: key, name: name, data: data,
131
+ language: language, datasetKey: datasetKey, uuid: uuid,
132
+ sourceId: sourceId, rank: rank, shortname: shortname,
133
+ limit: limit, offset: offset }.tostrings
134
+ opts = arguments.delete_if { |k, v| v.nil? }
135
+ Request.new("species/", opts, verbose, options).perform
136
+ end
137
+
138
+ ##
139
+ # Search the GBIF full text
140
+ #
141
+ # @param q [String] Query term(s) for full text search (optional)
142
+ # @param rank [String] 'CLASS', 'CULTIVAR', 'CULTIVAR_GROUP', 'DOMAIN', 'FAMILY',
143
+ # 'FORM', 'GENUS', 'INFORMAL', 'INFRAGENERIC_NAME', 'INFRAORDER', 'INFRASPECIFIC_NAME',
144
+ # 'INFRASUBSPECIFIC_NAME', 'KINGDOM', 'ORDER', 'PHYLUM', 'SECTION', 'SERIES', 'SPECIES', 'STRAIN', 'SUBCLASS',
145
+ # 'SUBFAMILY', 'SUBFORM', 'SUBGENUS', 'SUBKINGDOM', 'SUBORDER', 'SUBPHYLUM', 'SUBSECTION', 'SUBSERIES',
146
+ # 'SUBSPECIES', 'SUBTRIBE', 'SUBVARIETY', 'SUPERCLASS', 'SUPERFAMILY', 'SUPERORDER', 'SUPERPHYLUM',
147
+ # 'SUPRAGENERIC_NAME', 'TRIBE', 'UNRANKED', 'VARIETY' (optional)
148
+ # @param verbosity [bool] If True show alternative matches considered which had been rejected.
149
+ # @param higherTaxonKey [String] Filters by any of the higher Linnean rank keys. Note this
150
+ # is within the respective checklist and not searching nub keys across all checklists (optional)
151
+ # @param status [String] (optional) Filters by the taxonomic status as one of:
152
+ # * 'ACCEPTED'
153
+ # * 'DETERMINATION_SYNONYM' Used for unknown child taxa referred to via spec, ssp, ...
154
+ # * 'DOUBTFUL' Treated as accepted, but doubtful whether this is correct.
155
+ # * 'HETEROTYPIC_SYNONYM' More specific subclass of 'SYNONYM'.
156
+ # * 'HOMOTYPIC_SYNONYM' More specific subclass of 'SYNONYM'.
157
+ # * 'INTERMEDIATE_RANK_SYNONYM' Used in nub only.
158
+ # * 'MISAPPLIED' More specific subclass of 'SYNONYM'.
159
+ # * 'PROPARTE_SYNONYM' More specific subclass of 'SYNONYM'.
160
+ # * 'SYNONYM' A general synonym, the exact type is unknown.
161
+ # @param isExtinct [bool] Filters by extinction status (e.g. 'isExtinct=True')
162
+ # @param habitat [String] Filters by habitat. One of: 'marine', 'freshwater', or
163
+ # 'terrestrial' (optional)
164
+ # @param nameType [String] (optional) Filters by the name type as one of:
165
+ # * 'BLACKLISTED' surely not a scientific name.
166
+ # * 'CANDIDATUS' Candidatus is a component of the taxonomic name for a bacterium that cannot be maintained in a Bacteriology Culture Collection.
167
+ # * 'CULTIVAR' a cultivated plant name.
168
+ # * 'DOUBTFUL' doubtful whether this is a scientific name at all.
169
+ # * 'HYBRID' a hybrid formula (not a hybrid name).
170
+ # * 'INFORMAL' a scientific name with some informal addition like "cf." or indetermined like Abies spec.
171
+ # * 'SCINAME' a scientific name which is not well formed.
172
+ # * 'VIRUS' a virus name.
173
+ # * 'WELLFORMED' a well formed scientific name according to present nomenclatural rules.
174
+ # @param datasetKey [String] Filters by the dataset's key (a uuid) (optional)
175
+ # @param nomenclaturalStatus [String] Not yet implemented, but will eventually allow for
176
+ # filtering by a nomenclatural status enum
177
+ # @param facet [String] A list of facet names used to retrieve the 100 most frequent values
178
+ # for a field. Allowed facets are: 'datasetKey', 'higherTaxonKey', 'rank', 'status',
179
+ # 'isExtinct', 'habitat', and 'nameType'. Additionally 'threat' and 'nomenclaturalStatus'
180
+ # are legal values but not yet implemented, so data will not yet be returned for them. (optional)
181
+ # @param facetMincount [String] Used in combination with the facet parameter. Set
182
+ # 'facetMincount={#}' to exclude facets with a count less than {#}, e.g.
183
+ # http://bit.ly/1bMdByP only shows the type value 'ACCEPTED' because the other
184
+ # statuses have counts less than 7,000,000 (optional)
185
+ # @param facetMultiselect [bool] Used in combination with the facet parameter. Set
186
+ # 'facetMultiselect=True' to still return counts for values that are not currently
187
+ # filtered, e.g. http://bit.ly/19YLXPO still shows all status values even though
188
+ # status is being filtered by 'status=ACCEPTED' (optional)
189
+ # @param type [String] Type of name. One of 'occurrence', 'checklist', or 'metadata'. (optional)
190
+ # @param hl [bool] Set 'hl=True' to highlight terms matching the query when in fulltext
191
+ # search fields. The highlight will be an emphasis tag of class 'gbifH1' e.g.
192
+ # 'q='plant', hl=True'. Fulltext search fields include: 'title', 'keyword', 'country',
193
+ # 'publishing country', 'publishing organization title', 'hosting organization title', and
194
+ # 'description'. One additional full text field is searched which includes information from
195
+ # metadata documents, but the text of this field is not returned in the response. (optional)
196
+ # @!macro gbif_params
197
+ # @!macro gbif_options
198
+ # @return [Hash] A hash
199
+ #
200
+ # @example
201
+ # require 'gbifrb'
202
+ #
203
+ # speices = Gbif::Species
204
+ # speices.name_lookup(q: "Helianthus")
205
+ # speices.name_lookup(q: "Poa")
206
+ def self.name_lookup(q: nil, rank: nil, higherTaxonKey: nil, status: nil,
207
+ isExtinct: nil, habitat: nil, nameType: nil, datasetKey: nil,
208
+ nomenclaturalStatus: nil, limit: 100, offset: nil, facet: false,
209
+ facetMincount: nil, facetMultiselect: nil, type: nil, hl: false,
210
+ verbosity: false, verbose: nil, options: nil)
211
+
212
+ arguments = { q: q, rank: rank, higherTaxonKey: higherTaxonKey,
213
+ status: status, isExtinct: isExtinct, habitat: habitat, nameType: nameType,
214
+ datasetKey: datasetKey, nomenclaturalStatus: nomenclaturalStatus,
215
+ limit: limit, offset: offset, facet: facet, facetMincount: facetMincount,
216
+ facetMultiselect: facetMultiselect, type: type, hl: hl,
217
+ verbose: verbosity }.tostrings
218
+ opts = arguments.delete_if { |k, v| v.nil? }
219
+ Request.new("species/search", opts, verbose, options).perform
220
+ end
221
+
222
+ end
223
+ end
@@ -0,0 +1,36 @@
1
+ require 'net/http'
2
+
3
+ NETWORKABLE_EXCEPTIONS = [Faraday::Error::ClientError,
4
+ URI::InvalidURIError,
5
+ Encoding::UndefinedConversionError,
6
+ ArgumentError,
7
+ NoMethodError,
8
+ TypeError]
9
+
10
+ $cn_formats = ["rdf-xml", "turtle", "citeproc-json",
11
+ "citeproc-json-ish", "text", "ris", "bibtex",
12
+ "crossref-xml", "datacite-xml", "bibentry",
13
+ "crossref-tdm"]
14
+
15
+ $cn_format_headers = {"rdf-xml" => "application/rdf+xml",
16
+ "turtle" => "text/turtle",
17
+ "citeproc-json" => "transform/application/vnd.citationstyles.csl+json",
18
+ "text" => "text/x-bibliography",
19
+ "ris" => "application/x-research-info-systems",
20
+ "bibtex" => "application/x-bibtex",
21
+ "crossref-xml" => "application/vnd.crossref.unixref+xml",
22
+ "datacite-xml" => "application/vnd.datacite.datacite+xml",
23
+ "bibentry" => "application/x-bibtex",
24
+ "crossref-tdm" => "application/vnd.crossref.unixsd+xml"}
25
+
26
+ $cn_types = {"rdf-xml" => "text/xml",
27
+ "turtle" => "text/plain",
28
+ "citeproc-json" => "application/json",
29
+ "citeproc-json-ish" => "application/json",
30
+ "text" => "text/plain",
31
+ "ris" => "text/plain",
32
+ "bibtex" => "text/plain",
33
+ "crossref-xml" => "text/xml",
34
+ "datacite-xml" => "text/xml",
35
+ "bibentry" => "text/plain",
36
+ "crossref-tdm" => "text/xml"}
@@ -0,0 +1,22 @@
1
+ module Gbif
2
+ # Custom error class for rescuing from all Gbif errors
3
+ class Error < StandardError; end
4
+
5
+ # Raised when GBIF returns the HTTP status code 400
6
+ class BadRequest < Error; end
7
+
8
+ # Raised when GBIF returns the HTTP status code 404
9
+ class NotFound < Error; end
10
+
11
+ # Raised when GBIF returns the HTTP status code 500
12
+ class InternalServerError < Error; end
13
+
14
+ # Raised when GBIF returns the HTTP status code 502
15
+ class BadGateway < Error; end
16
+
17
+ # Raised when GBIF returns the HTTP status code 503
18
+ class ServiceUnavailable < Error; end
19
+
20
+ # Raised when GBIF returns the HTTP status code 504
21
+ class GatewayTimeout < Error; end
22
+ end
@@ -0,0 +1,71 @@
1
+ require 'faraday'
2
+ require 'multi_json'
3
+
4
+ # @private
5
+ module FaradayMiddleware
6
+ # @private
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
+ raise Gbif::BadRequest, error_message_400(response)
13
+ when 404
14
+ raise Gbif::NotFound, error_message_400(response)
15
+ when 500
16
+ raise Gbif::InternalServerError, error_message_500(response, "Something is technically wrong.")
17
+ when 502
18
+ raise Gbif::BadGateway, error_message_500(response, "The server returned an invalid or incomplete response.")
19
+ when 503
20
+ raise Gbif::ServiceUnavailable, error_message_500(response, "Crossref is rate limiting your requests.")
21
+ when 504
22
+ raise Gbif::GatewayTimeout, error_message_500(response, "504 Gateway Time-out")
23
+ end
24
+ end
25
+ end
26
+
27
+ def initialize(app)
28
+ super app
29
+ @parser = nil
30
+ end
31
+
32
+ private
33
+
34
+ def error_message_400(response)
35
+ "\n #{response[:method].to_s.upcase} #{response[:url].to_s}\n Status #{response[:status]}#{error_body(response[:body])}"
36
+ end
37
+
38
+ def error_body(body)
39
+ if not body.nil? and not body.empty? and body.kind_of?(String)
40
+ if is_json?(body)
41
+ body = ::MultiJson.load(body)
42
+ if body['message'].nil?
43
+ body = nil
44
+ elseif body['message'].length == 1
45
+ body = body['message']
46
+ else
47
+ body = body['message'].collect { |x| x['message'] }.join('; ')
48
+ end
49
+ end
50
+ end
51
+
52
+ if body.nil?
53
+ nil
54
+ else
55
+ ": #{body}"
56
+ end
57
+ end
58
+
59
+ def error_message_500(response, body=nil)
60
+ "#{response[:method].to_s.upcase} #{response[:url].to_s}: #{[response[:status].to_s + ':', body].compact.join(' ')}"
61
+ end
62
+
63
+ def is_json?(string)
64
+ MultiJson.load(string)
65
+ return true
66
+ rescue MultiJson::ParseError
67
+ return false
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,130 @@
1
+ require "gbifrb/request"
2
+ require "gbifrb/utils"
3
+
4
+ module Gbif
5
+ module Registry
6
+
7
+ def self.getdata_networks(x, uuid, opts, verbose, options) #:nodoc:
8
+ if x != 'all' and uuid.nil?
9
+ stop('You must specify a uuid if data does not equal "all"')
10
+ end
11
+ if uuid.nil?
12
+ url = 'network'
13
+ else
14
+ if x == 'all'
15
+ url = 'network/' + uuid
16
+ else
17
+ url = 'network/' + uuid + '/' + x
18
+ end
19
+ end
20
+ Request.new(url, opts, verbose, options).perform
21
+ # return { meta: get_meta(res), data: parse_results(res, uuid) }
22
+ end
23
+
24
+ def self.getdata_nodes(x, uuid, opts, isocode, verbose, options) #:nodoc:
25
+ if x != 'all' and uuid.nil?
26
+ stop('You must specify a uuid if data does not equal "all"')
27
+ end
28
+
29
+ if uuid.nil?
30
+ if x == 'all'
31
+ url = 'node'
32
+ else
33
+ if !isocode.nil? and x == 'country'
34
+ url = 'node/country/' + isocode
35
+ else
36
+ url = 'node/' + x
37
+ end
38
+ end
39
+ else
40
+ if x == 'all'
41
+ url = 'node/' + uuid
42
+ else
43
+ url = 'node/' + uuid + '/' + x
44
+ end
45
+ end
46
+
47
+ Request.new(url, opts, verbose, options).perform
48
+ # return {'meta': get_meta(res), 'data': parse_results(res, uuid)}
49
+ end
50
+
51
+ def self.getdata_orgs(x, uuid, opts, verbose, options) # :nodoc:
52
+ nouuid = ['all', 'deleted', 'pending', 'nonPublishing']
53
+ if !nouuid.include? x and uuid.nil?
54
+ stop('You must specify a uuid if data does not equal "all" and data does not equal one of ' + nouuid.join(', '))
55
+ end
56
+
57
+ if uuid.nil?
58
+ if x == 'all'
59
+ url = 'organization'
60
+ else
61
+ url = 'organization/' + x
62
+ end
63
+ else
64
+ if x == 'all'
65
+ url = 'organization/' + uuid
66
+ else
67
+ url = 'organization/' + uuid + '/' + x
68
+ end
69
+ end
70
+
71
+ Request.new(url, opts, verbose, options).perform
72
+ # return {'meta': get_meta(res), 'data': parse_results(res, uuid)}
73
+ end
74
+
75
+ def self.getdata_installations(x, uuid, opts, verbose, options) # :nodoc:
76
+ if !['all','deleted', 'nonPublishing'].include? x and uuid.nil?
77
+ stop('You must specify a uuid if data does not equal all and data does not equal one of deleted or nonPublishing')
78
+ end
79
+
80
+ if uuid.nil?
81
+ if x == 'all'
82
+ url = 'installation'
83
+ else
84
+ url = 'installation/' + x
85
+ end
86
+ else
87
+ if x == 'all'
88
+ url = 'installation/' + uuid
89
+ else
90
+ url = 'installation/' + uuid + '/' + x
91
+ end
92
+ end
93
+
94
+ Request.new(url, opts, verbose, options).perform
95
+ # return {'meta': get_meta(res), 'data': parse_results(res, uuid)}
96
+ end
97
+
98
+ def self.getdata_dataset_metrics(x, verbose, options) # :nodoc:
99
+ url = 'dataset/' + x + '/metrics'
100
+ Request.new(url, {}, verbose, options).perform
101
+ end
102
+
103
+ def self.datasets_fetch(x, uuid, opts, verbose, options) # :nodoc:
104
+ if !['all','deleted','duplicate','subDataset','withNoEndpoint'].include? x and uuid.nil?
105
+ stop('You must specify a uuid if data does not equal all and data does not equal of deleted, duplicate, subDataset, or withNoEndpoint')
106
+ end
107
+
108
+ if uuid.nil?
109
+ if x == 'all'
110
+ url = 'dataset'
111
+ else
112
+ if !id.nil? and x == 'metadata'
113
+ url = 'dataset/metadata/' + id + '/document'
114
+ else
115
+ url = 'dataset/' + x
116
+ end
117
+ end
118
+ else
119
+ if x == 'all'
120
+ url = 'dataset/' + uuid
121
+ else
122
+ url = 'dataset/' + uuid + '/' + x
123
+ end
124
+ end
125
+
126
+ Request.new(url, opts, verbose, options).perform
127
+ end
128
+
129
+ end
130
+ end