dwca_hunter 0.5.2 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.byebug_history +37 -0
  3. data/.gitignore +5 -0
  4. data/.rubocop.yml +3 -2
  5. data/.ruby-version +1 -1
  6. data/Gemfile.lock +59 -135
  7. data/LICENSE.txt +1 -1
  8. data/README.md +1 -1
  9. data/dwca_hunter.gemspec +7 -8
  10. data/exe/dwcahunter +1 -3
  11. data/lib/dwca_hunter.rb +39 -8
  12. data/lib/dwca_hunter/resource.rb +5 -0
  13. data/lib/dwca_hunter/resources/aos-birds.rb +143 -0
  14. data/lib/dwca_hunter/resources/arctos.rb +121 -145
  15. data/lib/dwca_hunter/resources/clements.rb +151 -0
  16. data/lib/dwca_hunter/resources/eol.rb +85 -0
  17. data/lib/dwca_hunter/resources/freebase.rb +51 -49
  18. data/lib/dwca_hunter/resources/how-moore-birds.rb +168 -0
  19. data/lib/dwca_hunter/resources/index-fungorum.rb +131 -0
  20. data/lib/dwca_hunter/resources/ioc_word_bird.rb +200 -0
  21. data/lib/dwca_hunter/resources/ion.rb +98 -0
  22. data/lib/dwca_hunter/resources/ipni.rb +3 -2
  23. data/lib/dwca_hunter/resources/itis.rb +99 -99
  24. data/lib/dwca_hunter/resources/mammal_divdb.rb +155 -0
  25. data/lib/dwca_hunter/resources/mammal_species.rb +9 -6
  26. data/lib/dwca_hunter/resources/mcz.rb +123 -0
  27. data/lib/dwca_hunter/resources/ncbi.rb +22 -23
  28. data/lib/dwca_hunter/resources/opentree.rb +5 -5
  29. data/lib/dwca_hunter/resources/paleobiodb.rb +193 -0
  30. data/lib/dwca_hunter/resources/paleodb_harvester.rb +140 -0
  31. data/lib/dwca_hunter/resources/sherborn.rb +91 -0
  32. data/lib/dwca_hunter/resources/wikispecies.rb +142 -129
  33. data/lib/dwca_hunter/version.rb +1 -1
  34. metadata +31 -40
  35. data/files/birdlife_7.csv +0 -11862
  36. data/files/fishbase_taxon_cache.tsv +0 -81000
  37. data/files/reptile_checklist_2014_12.csv +0 -15158
  38. data/files/species-black.txt +0 -251
  39. data/ipni.csv.gz +0 -0
  40. data/ipniWebName.csv.xz?dl=1 +0 -0
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DwcaHunter
4
+ class ResourceClements < DwcaHunter::Resource
5
+ def initialize(opts = {})
6
+ @command = "clements-ebird"
7
+ @title = "The eBird/Clements Checklist of Birds of the World"
8
+ @url = "https://uofi.box.com/shared/static/b4n8zqa99hq9rdga27skkh3870yhujgo.csv"
9
+ @UUID = "577c0b56-4a3c-4314-8724-14b304f601de"
10
+ @download_path = File.join(Dir.tmpdir,
11
+ "dwca_hunter",
12
+ "clements",
13
+ "data.csv")
14
+ @synonyms = []
15
+ @names = []
16
+ @vernaculars = []
17
+ @extensions = []
18
+ @synonyms_hash = {}
19
+ @vernaculars_hash = {}
20
+ super(opts)
21
+ end
22
+
23
+ def download
24
+ puts "Downloading cached and modified version of the file."
25
+ puts "Go to https://www.birds.cornell.edu/clementschecklist/download/ " \
26
+ "for updates."
27
+ `curl -s -L #{@url} -o #{@download_path}`
28
+ end
29
+
30
+ def unpack
31
+ end
32
+
33
+ def make_dwca
34
+ DwcaHunter.logger_write(object_id, "Extracting data")
35
+ get_names
36
+ generate_dwca
37
+ end
38
+
39
+ private
40
+
41
+ def get_names
42
+ Dir.chdir(@download_dir)
43
+ collect_names
44
+ end
45
+
46
+ def collect_names
47
+ @names_index = {}
48
+ file = CSV.open(File.join(@download_dir, "data.csv"),
49
+ headers: true)
50
+ file.each_with_index do |row, i|
51
+ name_string = row["scientific name"]
52
+ canonical = name_string
53
+ kingdom = "Animalia"
54
+ phylum = "Chordata"
55
+ klass = "Aves"
56
+ order = row["order"]
57
+ family = row["family"]
58
+ code = "ICZN"
59
+
60
+ taxon_id = "gn_#{i + 1}"
61
+ @names << { taxon_id: taxon_id,
62
+ name_string: name_string,
63
+ kingdom: kingdom,
64
+ phylum: phylum,
65
+ klass: klass,
66
+ order: order,
67
+ family: family,
68
+ code: code }
69
+
70
+ if row["English name"].to_s != ""
71
+ @vernaculars << {
72
+ taxon_id: taxon_id,
73
+ vern: row["English name"],
74
+ lang: "end"
75
+ }
76
+ end
77
+
78
+ puts "Processed %s names" % i if i % 10_000 == 0
79
+ end
80
+ end
81
+
82
+ def generate_dwca
83
+ DwcaHunter.logger_write(object_id,
84
+ "Creating DarwinCore Archive file")
85
+ @core = [["http://rs.tdwg.org/dwc/terms/taxonID",
86
+ "http://rs.tdwg.org/dwc/terms/scientificName",
87
+ "http://rs.tdwg.org/dwc/terms/kingdom",
88
+ "http://rs.tdwg.org/dwc/terms/phylum",
89
+ "http://rs.tdwg.org/dwc/terms/class",
90
+ "http://rs.tdwg.org/dwc/terms/order",
91
+ "http://rs.tdwg.org/dwc/terms/family",
92
+ "http://rs.tdwg.org/dwc/terms/nomenclaturalCode"]]
93
+ @names.each do |n|
94
+ @core << [n[:taxon_id], n[:name_string],
95
+ n[:kingdom], n[:phylum], n[:klass], n[:order], n[:family],
96
+ n[:code]]
97
+ end
98
+ @extensions << {
99
+ data: [[
100
+ "http://rs.tdwg.org/dwc/terms/taxonID",
101
+ "http://rs.tdwg.org/dwc/terms/vernacularName",
102
+ "http://purl.org/dc/terms/language"
103
+ ]],
104
+ file_name: "vernacular_names.txt",
105
+ row_type: "http://rs.gbif.org/terms/1.0/VernacularName"
106
+ }
107
+
108
+ @vernaculars.each do |v|
109
+ @extensions[-1][:data] << [v[:taxon_id], v[:vern], v[:lang]]
110
+ end
111
+
112
+ @eml = {
113
+ id: @uuid,
114
+ title: @title,
115
+ authors: [
116
+ { first_name: "G. F.",
117
+ last_name: "Clements"
118
+ },
119
+ { first_name: "T. S.",
120
+ last_name: "Schulenberg"
121
+ },
122
+ { first_name: "M. J.",
123
+ last_name: "Iliff"
124
+ },
125
+ { first_name: "S. M.",
126
+ last_name: "Billerman"
127
+ },
128
+ { first_name: "T. A.",
129
+ last_name: "Fredericks"
130
+ },
131
+ { first_name: "B. L.",
132
+ last_name: "Sullivan"
133
+ },
134
+ { first_name: "C. L.",
135
+ last_name: "Wood"
136
+ },
137
+ ],
138
+ metadata_providers: [
139
+ { first_name: "Dmitry",
140
+ last_name: "Mozzherin",
141
+ email: "dmozzherin@gmail.com" }
142
+ ],
143
+ abstract: "The eBird/Clements Checklist of Birds of the World" \
144
+ ": v2019. Downloaded from " \
145
+ "https://www.birds.cornell.edu/clementschecklist/download/",
146
+ url: @url
147
+ }
148
+ super
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,85 @@
1
+ require "xz"
2
+
3
+ module DwcaHunter
4
+ # Resource for FishBase
5
+ class ResourceEOL < DwcaHunter::Resource
6
+ attr_reader :title, :abbr
7
+ def initialize(opts = {}) #download: false, unpack: false})
8
+ @command = "eol"
9
+ @title = "Encyclopedia of Life"
10
+ @abbr = "EOL"
11
+ @url = "https://eol.org/data/provider_ids.csv.gz"
12
+ @uuid = "dba5f880-a40d-479b-a1ad-a646835edde4"
13
+ @download_dir = File.join(Dir.tmpdir, "dwca_hunter", "eol")
14
+ @download_path = File.join(@download_dir, "eol.csv.gz")
15
+ @extensions = []
16
+ super
17
+ end
18
+
19
+ def unpack
20
+ unpack_gzip
21
+ end
22
+
23
+ def make_dwca
24
+ organize_data
25
+ generate_dwca
26
+ end
27
+
28
+ private
29
+
30
+ def organize_data
31
+ DwcaHunter::logger_write(self.object_id,
32
+ "Organizing data")
33
+ # snp = ScientificNameParser.new
34
+ @data = CSV.open(@download_path[0...-3],
35
+ col_sep: ",", headers: true)
36
+ .each_with_object([]) do |row, data|
37
+ id = row['page_id'].strip
38
+ name = row['preferred_canonical_for_page'].strip
39
+ data << { taxon_id: id,
40
+ local_id: id,
41
+ scientific_name: name}
42
+ end
43
+ end
44
+
45
+ def generate_dwca
46
+ DwcaHunter::logger_write(self.object_id,
47
+ 'Creating DarwinCore Archive file')
48
+ core_init
49
+ eml_init
50
+ DwcaHunter::logger_write(self.object_id, 'Assembling Core Data')
51
+ count = 0
52
+ @data.each do |d|
53
+ count += 1
54
+ if count % 10000 == 0
55
+ DwcaHunter::logger_write(self.object_id, "Core row #{count}")
56
+ end
57
+ @core << [d[:taxon_id], d[:local_id],
58
+ d[:scientific_name]]
59
+ end
60
+ super
61
+ end
62
+
63
+ def eml_init
64
+ @eml = {
65
+ id: @uuid,
66
+ title: @title,
67
+ authors: [],
68
+ metadata_providers: [
69
+ { first_name: "Dmitry",
70
+ last_name: "Mozzherin",
71
+ }
72
+ ],
73
+ abstract: "Global access to knowledge about life on Earth",
74
+ url: "http://www.eol.org"
75
+ }
76
+ end
77
+
78
+ def core_init
79
+ @core = [["http://rs.tdwg.org/dwc/terms/taxonID",
80
+ "http://globalnames.org/terms/localID",
81
+ "http://rs.tdwg.org/dwc/terms/scientificName",
82
+ ]]
83
+ end
84
+ end
85
+ end
@@ -1,15 +1,15 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module DwcaHunter
4
4
  class ResourceFreebase < DwcaHunter::Resource
5
5
  def initialize(opts = {})
6
6
  @command = "freebase"
7
- @title = 'Freebase'
8
- @uuid = 'bacd21f0-44e0-43e2-914c-70929916f257'
7
+ @title = "Freebase"
8
+ @uuid = "bacd21f0-44e0-43e2-914c-70929916f257"
9
9
  @download_path = File.join(Dir.tmpdir,
10
- 'dwca_hunter',
11
- 'freebase',
12
- 'data.json')
10
+ "dwca_hunter",
11
+ "freebase",
12
+ "data.json")
13
13
  @data = []
14
14
  @all_taxa = {}
15
15
  @cleaned_taxa = {}
@@ -27,11 +27,11 @@ module DwcaHunter
27
27
  end
28
28
 
29
29
  def download
30
- DwcaHunter::logger_write(self.object_id,
31
- 'Querying freebase for species information...')
30
+ DwcaHunter.logger_write(object_id,
31
+ "Querying freebase for species information...")
32
32
  q = {
33
33
  query: [{
34
- type: '/biology/organism_classification',
34
+ type: "/biology/organism_classification",
35
35
  id: nil,
36
36
  guid: nil,
37
37
  name: nil,
@@ -41,16 +41,16 @@ module DwcaHunter
41
41
  id: nil,
42
42
  guid: nil,
43
43
  scientific_name: nil,
44
- optional: true,
45
- },
44
+ optional: true
45
+ }
46
46
  }],
47
- cursor: true,
47
+ cursor: true
48
48
  }
49
49
 
50
50
  run_query(q)
51
51
 
52
52
  data = JSON.pretty_generate @data
53
- f = open(@download_path, 'w:utf-8')
53
+ f = open(@download_path, "w:utf-8")
54
54
  f.write(data)
55
55
  f.close
56
56
  end
@@ -60,31 +60,32 @@ module DwcaHunter
60
60
  def run_query(q)
61
61
  count = 0
62
62
  requests_num = 0
63
- while true
63
+ loop do
64
64
  freebase_url = "http://api.freebase.com/api/service/mqlread?query=%s" %
65
- URI.encode(q.to_json)
65
+ URI.encode(q.to_json)
66
66
  res = JSON.load RestClient.get(freebase_url)
67
67
  requests_num += 1
68
- break if res['result'] == nil || res['result'].empty?
68
+ break if res["result"].nil? || res["result"].empty?
69
+
69
70
  if requests_num % 10 == 0
70
- DwcaHunter::logger_write(self.object_id,
71
- "Received %s names" % count)
71
+ DwcaHunter.logger_write(object_id,
72
+ "Received %s names" % count)
72
73
  end
73
- count += res['result'].size
74
- res['result'].each { |d| @data << d }
75
- q[:cursor] = res['cursor']
74
+ count += res["result"].size
75
+ res["result"].each { |d| @data << d }
76
+ q[:cursor] = res["cursor"]
76
77
  end
77
78
  end
78
79
 
79
80
  def organize_data
80
- @data = JSON.load(open(@download_path, 'r:utf-8').read)
81
+ @data = JSON.load(open(@download_path, "r:utf-8").read)
81
82
  @data.each do |d|
82
- scientific_name = d['scientific_name'].to_s
83
+ scientific_name = d["scientific_name"].to_s
83
84
  id = d["id"]
84
- parent_id = d['higher_classification'] ?
85
- d['higher_classification']["id"] :
85
+ parent_id = d["higher_classification"] ?
86
+ d["higher_classification"]["id"] :
86
87
  nil
87
- synonyms = d['synonym_scientific_name']
88
+ synonyms = d["synonym_scientific_name"]
88
89
  @all_taxa[id] = { id: id,
89
90
  parent_id: parent_id,
90
91
  scientific_name: scientific_name,
@@ -93,6 +94,7 @@ module DwcaHunter
93
94
 
94
95
  @all_taxa.each do |k, v|
95
96
  next unless v[:scientific_name] && v[:scientific_name].strip != ""
97
+
96
98
  parent_id = v[:parent_id]
97
99
  until (@all_taxa[parent_id] &&
98
100
  @all_taxa[parent_id][:scientific_name]) || parent_id.nil?
@@ -103,29 +105,28 @@ module DwcaHunter
103
105
  v[:parent_id] = parent_id
104
106
  @cleaned_taxa[k] = v
105
107
  end
106
-
107
108
  end
108
109
 
109
110
  def generate_dwca
110
- DwcaHunter::logger_write(self.object_id,
111
- 'Creating DarwinCore Archive file')
112
- @core = [['http://rs.tdwg.org/dwc/terms/taxonID',
113
- 'http://rs.tdwg.org/dwc/terms/scientificName',
114
- 'http://rs.tdwg.org/dwc/terms/parentNameUsageID']]
111
+ DwcaHunter.logger_write(object_id,
112
+ "Creating DarwinCore Archive file")
113
+ @core = [["http://rs.tdwg.org/dwc/terms/taxonID",
114
+ "http://rs.tdwg.org/dwc/terms/scientificName",
115
+ "http://rs.tdwg.org/dwc/terms/parentNameUsageID"]]
115
116
 
116
117
  @extensions << { data: [[
117
- 'http://rs.tdwg.org/dwc/terms/TaxonID',
118
- 'http://rs.tdwg.org/dwc/terms/scientificName',
119
- ]], file_name: 'synonyms.txt' }
120
- DwcaHunter::logger_write(self.object_id,
121
- 'Creating synonyms extension for DarwinCore Archive file')
118
+ "http://rs.tdwg.org/dwc/terms/TaxonID",
119
+ "http://rs.tdwg.org/dwc/terms/scientificName"
120
+ ]], file_name: "synonyms.txt" }
121
+ DwcaHunter.logger_write(object_id,
122
+ "Creating synonyms extension for DarwinCore Archive file")
122
123
  count = 0
123
- @cleaned_taxa.each do |key, taxon|
124
+ @cleaned_taxa.each do |_key, taxon|
124
125
  count += 1
125
126
  @core << [taxon[:id], taxon[:scientific_name], taxon[:parent_id]]
126
127
  if count % BATCH_SIZE == 0
127
- DwcaHunter::logger_write(self.object_id,
128
- "Traversing %s extension data record" % count)
128
+ DwcaHunter.logger_write(object_id,
129
+ "Traversing %s extension data record" % count)
129
130
  end
130
131
  taxon[:synonyms].each do |name|
131
132
  @extensions[-1][:data] << [taxon[:id], name]
@@ -134,19 +135,20 @@ module DwcaHunter
134
135
  @eml = {
135
136
  id: @uuid,
136
137
  title: @title,
137
- license: 'http://creativecommons.org/licenses/by-sa/3.0/',
138
+ license: "http://creativecommons.org/licenses/by-sa/3.0/",
138
139
  authors: [
139
- { url: 'http://www.freebase.com/home' }],
140
- abstract: 'An entity graph of people, places and things, ' +
141
- 'built by a community that loves open data.',
140
+ { url: "http://www.freebase.com/home" }
141
+ ],
142
+ abstract: "An entity graph of people, places and things, " \
143
+ "built by a community that loves open data.",
142
144
  metadata_providers: [
143
- { first_name: 'Dmitry',
144
- last_name: 'Mozzherin',
145
- email: 'dmozzherin@mbl.edu' }],
146
- url: 'http://www.freebase.com/home'
145
+ { first_name: "Dmitry",
146
+ last_name: "Mozzherin",
147
+ email: "dmozzherin@mbl.edu" }
148
+ ],
149
+ url: "http://www.freebase.com/home"
147
150
  }
148
151
  super
149
152
  end
150
-
151
153
  end
152
154
  end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DwcaHunter
4
+ class ResourceHowardMoore < DwcaHunter::Resource
5
+ def initialize(opts = {})
6
+ @command = "how-moore-birds"
7
+ @title = "Howard and Moore Complete Checklist of the Birds of the World"
8
+ @url = "https://uofi.box.com/shared/static/m71m541dr5unc41xzg4y51d92b7wiy2k.csv"
9
+ @UUID = "85023fe5-bf2a-486b-bdae-3e61cefd41fd"
10
+ @download_path = File.join(Dir.tmpdir,
11
+ "dwca_hunter",
12
+ "how-moore-birds",
13
+ "data.csv")
14
+ @synonyms = []
15
+ @names = []
16
+ @vernaculars = []
17
+ @extensions = []
18
+ @synonyms_hash = {}
19
+ @vernaculars_hash = {}
20
+ super(opts)
21
+ end
22
+
23
+ def download
24
+ puts "Downloading cached verion of the file."
25
+ puts "Check https://www.howardandmoore.org/howard-and-moore-database/"
26
+ puts "If there is a more recent edition"
27
+ `curl -s -L #{@url} -o #{@download_path}`
28
+ end
29
+
30
+ def unpack; end
31
+
32
+ def make_dwca
33
+ DwcaHunter.logger_write(object_id, "Extracting data")
34
+ get_names
35
+ generate_dwca
36
+ end
37
+
38
+ private
39
+
40
+ def get_names
41
+ Dir.chdir(@download_dir)
42
+ collect_names
43
+ end
44
+
45
+ def collect_names
46
+ file = CSV.open(File.join(@download_dir, "data.csv"),
47
+ headers: true)
48
+ file.each_with_index do |row, i|
49
+ kingdom = "Animalia"
50
+ phylum = "Chordata"
51
+ klass = "Aves"
52
+ family = row["FAMILY_NAME"].capitalize
53
+ genus = row["GENERA_NAME"].capitalize
54
+ species = row["SPECIES_NAME"]
55
+ species_au =
56
+ "#{row['species_author']} #{row['species_rec_year']}".strip
57
+ subspecies = row["SUB_SPECIES_NAME"]
58
+ subspecies_au =
59
+ "#{row['subspecies_author']} #{row['subspecies_rec_year']}".strip
60
+ code = "ICZN"
61
+
62
+ taxon_id = "gn_#{i + 1}"
63
+ name_string = species
64
+ name_string = if subspecies.to_s == "" ||
65
+ name_string.include?(subspecies)
66
+ "#{name_string} #{species_au}".strip
67
+ else
68
+ "#{name_string} #{subspecies} #{subspecies_au}".strip
69
+ end
70
+
71
+ @names << { taxon_id: taxon_id,
72
+ name_string: name_string,
73
+ kingdom: kingdom,
74
+ phylum: phylum,
75
+ klass: klass,
76
+ family: family,
77
+ genus: genus,
78
+ code: code }
79
+
80
+ if row["species_english_name"].to_s != ""
81
+ @vernaculars << {
82
+ taxon_id: taxon_id,
83
+ vern: row["species_english_name"],
84
+ lang: "en"
85
+ }
86
+ end
87
+ if row["species_english_name2"].to_s != ""
88
+ @vernaculars << {
89
+ taxon_id: taxon_id,
90
+ vern: row["species_english_name2"],
91
+ lang: "en"
92
+ }
93
+ end
94
+
95
+ puts "Processed %s names" % i if i % 10_000 == 0
96
+ end
97
+ end
98
+
99
+ def update_vernacular(taxon_id, canonical)
100
+ return unless @vernaculars_hash.key?(canonical)
101
+
102
+ @vernaculars_hash[canonical].each do |vern|
103
+ @vernaculars << { taxon_id: taxon_id, vern: vern }
104
+ end
105
+ end
106
+
107
+ def update_synonym(taxon_id, canonical)
108
+ return unless @synonyms_hash.key?(canonical)
109
+
110
+ @synonyms_hash[canonical].each do |syn|
111
+ @synonyms << { taxon_id: taxon_id, name_string: syn[:name_string],
112
+ status: syn[:status] }
113
+ end
114
+ end
115
+
116
+ def generate_dwca
117
+ DwcaHunter.logger_write(object_id,
118
+ "Creating DarwinCore Archive file")
119
+ @core = [["http://rs.tdwg.org/dwc/terms/taxonID",
120
+ "http://rs.tdwg.org/dwc/terms/scientificName",
121
+ "http://rs.tdwg.org/dwc/terms/kingdom",
122
+ "http://rs.tdwg.org/dwc/terms/phylum",
123
+ "http://rs.tdwg.org/dwc/terms/class",
124
+ "http://rs.tdwg.org/dwc/terms/family",
125
+ "http://rs.tdwg.org/dwc/terms/genus",
126
+ "http://rs.tdwg.org/dwc/terms/nomenclaturalCode"]]
127
+ @names.each do |n|
128
+ @core << [n[:taxon_id], n[:name_string],
129
+ n[:kingdom], n[:phylum], n[:klass], n[:family],
130
+ n[:genus], n[:code]]
131
+ end
132
+ @extensions << {
133
+ data: [[
134
+ "http://rs.tdwg.org/dwc/terms/taxonID",
135
+ "http://rs.tdwg.org/dwc/terms/vernacularName",
136
+ "http://purl.org/dc/terms/language"
137
+ ]],
138
+ file_name: "vernacular_names.txt",
139
+ row_type: "http://rs.gbif.org/terms/1.0/VernacularName"
140
+ }
141
+
142
+ @vernaculars.each do |v|
143
+ @extensions[-1][:data] << [v[:taxon_id], v[:vern], v[:lang]]
144
+ end
145
+
146
+ @eml = {
147
+ id: @uuid,
148
+ title: @title,
149
+ authors: [
150
+ {
151
+ last_name: "Christidis"
152
+ }
153
+ ],
154
+ metadata_providers: [
155
+ { first_name: "Dmitry",
156
+ last_name: "Mozzherin",
157
+ email: "dmozzherin@gmail.com" }
158
+ ],
159
+ abstract: "Christidis et al. 2018. The Howard and Moore Complete " \
160
+ "Checklist of the Birds of the World, version 4.1 " \
161
+ "(Downloadable checklist). " \
162
+ "Accessed from https://www.howardandmoore.org.",
163
+ url: @url
164
+ }
165
+ super
166
+ end
167
+ end
168
+ end