gbifrb 0.1.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.
@@ -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