taxonifi 0.2.0 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +59 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +5 -17
  5. data/Gemfile.lock +22 -40
  6. data/README.md +192 -0
  7. data/Rakefile +35 -26
  8. data/lib/export/format/base.rb +1 -1
  9. data/lib/export/format/species_file.rb +154 -152
  10. data/lib/lumper/clump.rb +1 -1
  11. data/lib/lumper/lumper.rb +22 -18
  12. data/lib/lumper/lumps/parent_child_name_collection.rb +1 -2
  13. data/lib/lumper/name_index.rb +21 -0
  14. data/lib/{models → model}/author_year.rb +2 -2
  15. data/lib/{models → model}/base.rb +35 -5
  16. data/lib/{models → model}/collection.rb +8 -1
  17. data/lib/{models → model}/name.rb +128 -36
  18. data/lib/{models → model}/name_collection.rb +134 -33
  19. data/lib/{models → model}/person.rb +1 -1
  20. data/lib/{models → model}/ref.rb +4 -2
  21. data/lib/model/ref_collection.rb +171 -0
  22. data/lib/{models → model}/species_name.rb +24 -3
  23. data/lib/splitter/builder.rb +1 -1
  24. data/lib/splitter/parser.rb +5 -0
  25. data/lib/splitter/tokens.rb +54 -9
  26. data/lib/taxonifi/version.rb +3 -0
  27. data/lib/taxonifi.rb +5 -9
  28. data/taxonifi.gemspec +29 -99
  29. data/test/helper.rb +1 -1
  30. data/test/test_exporter.rb +1 -1
  31. data/test/test_lumper_names.rb +9 -9
  32. data/test/test_lumper_refs.rb +4 -4
  33. data/test/test_parser.rb +97 -26
  34. data/test/test_splitter_tokens.rb +25 -4
  35. data/test/test_taxonifi_base.rb +1 -1
  36. data/test/test_taxonifi_geog.rb +1 -1
  37. data/test/test_taxonifi_name.rb +13 -14
  38. data/test/test_taxonifi_name_collection.rb +11 -5
  39. data/test/test_taxonifi_ref.rb +1 -1
  40. data/test/test_taxonifi_ref_collection.rb +40 -3
  41. data/test/test_taxonifi_species_name.rb +51 -1
  42. data/travis/before_install.sh +2 -0
  43. metadata +96 -66
  44. data/README.rdoc +0 -154
  45. data/VERSION +0 -1
  46. data/lib/models/ref_collection.rb +0 -107
  47. /data/lib/{models → model}/generic_object.rb +0 -0
  48. /data/lib/{models → model}/geog.rb +0 -0
  49. /data/lib/{models → model}/geog_collection.rb +0 -0
  50. /data/lib/{models → model}/shared_class_methods.rb +0 -0
@@ -16,14 +16,14 @@ module Taxonifi
16
16
  # Optionly store the row this came from
17
17
  attr_accessor :row_number
18
18
 
19
- # A general purpose hash populable as needed for related metadata
20
- attr_accessor :related
19
+ # A general purpose Hash populable as needed for related metadata
20
+ attr_accessor :properties
21
21
 
22
22
  # TODO: Rethink this. See @@ATTRIBUTES in subclasses.
23
23
  ATTRIBUTES = [:row_number]
24
24
 
25
25
  def initialize(options = {})
26
- @related = {}
26
+ @properties = {}
27
27
  end
28
28
 
29
29
  # Assign on new() all attributes for the ATTRIBUTES
@@ -35,6 +35,38 @@ module Taxonifi
35
35
  end
36
36
  end
37
37
 
38
+ # Add a set of properties (doesn't check for key collisions)
39
+ def add_properties(hash)
40
+ @properties.merge!(hash)
41
+ end
42
+
43
+ # Add a key/value pair to @properties
44
+ def add_property(key, value)
45
+ if @properties[key]
46
+ return false
47
+ else
48
+ @properties.merge!(key => value)
49
+ end
50
+ end
51
+
52
+ # Replace an existing key/value pair in @properties
53
+ def replace_property(key,value)
54
+ if !@properties[key]
55
+ return false
56
+ else
57
+ @properties.merge!(key => value)
58
+ end
59
+ end
60
+
61
+ # Delete an existing key/value pair in @properties
62
+ def delete_property(key)
63
+ if !@properties[key]
64
+ @properties.delete(key)
65
+ else
66
+ @properties.merge!(key => value)
67
+ end
68
+ end
69
+
38
70
  def id=(id)
39
71
  raise Taxonifi::ModelError, "Base model objects must have Fixnum ids." if !id.nil? && id.class != Fixnum
40
72
  @id = id
@@ -70,9 +102,7 @@ module Taxonifi
70
102
  end
71
103
  ancestors
72
104
  end
73
-
74
105
  end
75
106
 
76
-
77
107
  end
78
108
  end
@@ -89,8 +89,15 @@ module Taxonifi
89
89
 
90
90
  # Returns an Array which respresents
91
91
  # all the "root" objects.
92
+ # TODO: test
92
93
  def objects_without_parents
93
- collection.select{|o| o.parent.nil?}
94
+ @collection.select{|o| o.parent.nil?}
95
+ end
96
+
97
+ # Returns an Array of immediate children
98
+ # TODO: test
99
+ def children_of_object(o)
100
+ @collection.select{|i| i.parent == o}
94
101
  end
95
102
 
96
103
  protected
@@ -10,10 +10,10 @@ module Taxonifi
10
10
  # String
11
11
  attr_accessor :rank
12
12
 
13
- # String
13
+ # String, authors as originally read
14
14
  attr_accessor :author
15
15
 
16
- # String, authors as originally read
16
+ # String, year as originally read
17
17
  attr_accessor :year
18
18
 
19
19
  # Boolean, true if parens present (i.e. _not_ in original combination)
@@ -22,16 +22,22 @@ module Taxonifi
22
22
  # A Taxonifi::Model::Name
23
23
  attr_accessor :parent
24
24
 
25
- # A Taxonifi::Model::Name General purpose relationship, typically used to indicate synonymy.
25
+ # A Taxonifi::Model::Name
26
+ # A general purpose relationship, typically used to indicate synonymy.
26
27
  attr_accessor :related_name
27
28
 
28
- # Array, contains properties assignable in Taxonifi::Model::Name#new()
29
- @@ATTRIBUTES = [:name, :rank, :year, :parens, :parent, :author, :related_name]
29
+ # A Taxonifi::Model::Reference
30
+ # The original description.
31
+ attr_accessor :original_description_reference
30
32
 
31
- # optionally parsed/index
33
+ # An Array, contains assignable properties in Taxonifi::Model::Name#new()
34
+ @@ATTRIBUTES = [:name, :rank, :year, :parens, :author, :related_name ]
35
+
36
+ # An Array of Taxonifi::Model::Person
37
+ # Optionally parsed/index
32
38
  attr_accessor :authors
33
39
 
34
- # optionally parsed/index
40
+ # Optionally parsed/index
35
41
  attr_accessor :author_year_index
36
42
 
37
43
  def initialize(options = {})
@@ -41,11 +47,16 @@ module Taxonifi
41
47
  }.merge!(options)
42
48
 
43
49
  @parent = nil
50
+ @authors ||= []
51
+
44
52
  build(@@ATTRIBUTES, opts)
45
- add_author_year(opts[:author_year]) if !opts[:author_year].nil? && opts[:author_year].size > 0
53
+ assign_author_year(opts)
54
+
55
+
56
+ @id = opts[:id]
46
57
  @parent = opts[:parent] if (!opts[:parent].nil? && opts[:parent].class == Taxonifi::Model::Name)
47
- @id = opts[:id] # if !opts[:id].nil? && opts[:id].size != 0
48
- @authors ||= []
58
+ @original_description_reference = opts[:original_description_reference] if (!opts[:original_description_reference].nil? && opts[:original_description_reference].class == Taxonifi::Model::Ref)
59
+
49
60
  true
50
61
  end
51
62
 
@@ -63,8 +74,8 @@ module Taxonifi
63
74
  end
64
75
 
65
76
  # Set the rank.
66
- def rank=(rank)
67
- r = rank.to_s.downcase.strip
77
+ def rank=(value)
78
+ r = value.to_s.downcase.strip
68
79
  if !RANKS.include?(r)
69
80
  raise NameError, "#{r} is not a valid rank."
70
81
  end
@@ -76,7 +87,7 @@ module Taxonifi
76
87
  # TODO: Family group extension; ICZN specific
77
88
  def index_rank
78
89
  case rank
79
- when 'species', 'subspecies'
90
+ when 'species', 'subspecies', 'variety'
80
91
  'species_group'
81
92
  when 'genus', 'subgenus'
82
93
  'genus_group'
@@ -88,6 +99,8 @@ module Taxonifi
88
99
  end
89
100
 
90
101
  # Set the parent (a Taxonifi::Model::Name)
102
+ # Passing "true" allows you to bypass the parent level restriction, do so
103
+ # with great caution.
91
104
  def parent=(parent)
92
105
  if @rank.nil?
93
106
  raise Taxonifi::NameError, "Parent of name can not be set if rank of child is not set."
@@ -98,7 +111,8 @@ module Taxonifi
98
111
  raise NameError, "Parent is not a Taxonifi::Model::Name."
99
112
  end
100
113
 
101
- if RANKS.index(parent.rank) >= RANKS.index(self.rank)
114
+ if (RANKS.index(parent.rank) >= RANKS.index(self.rank))
115
+ # warn "WARNING:: Assigning parent to Name at same or lower rank than self (#{rank})."
102
116
  raise NameError, "Parent is same or lower rank than self (#{rank})."
103
117
  end
104
118
 
@@ -113,13 +127,70 @@ module Taxonifi
113
127
  (self.parens == true) ? "(#{au})" : au
114
128
  end
115
129
 
130
+
116
131
  # Return the author year string.
117
132
  def author_year_string
118
- au = [self.author, self.year].compact.join(", ")
133
+ # Build based on People
134
+
135
+ auth = nil
136
+ if authors.size > 0
137
+ case authors.size
138
+ when 1
139
+ auth = self.author
140
+ when 2
141
+ auth = authors.map(&:last_name).join(" & ")
142
+ when 2...100
143
+ auth = authors[0..-1].map(&:last_name).join(", ") + " & " + authors.last.last_name
144
+ end
145
+ # Build based on string
146
+ else
147
+ auth = self.author
148
+ end
149
+ au = [auth, self.year].compact.join(", ")
119
150
  return nil if au.size == 0
120
151
  au
121
152
  end
122
153
 
154
+ # Return a String, the human readable version of this name (genus, subgenus, species, subspecies, variety author, year)
155
+ def display_name
156
+ [nomenclator_name, author_year].compact.join(" ")
157
+ end
158
+
159
+ # Return a String, the human readable version of this name (genus, subgenus, species, subspecies)
160
+ def nomenclator_name
161
+ case @rank
162
+ # TODO: update for infrasubspecifics if we start tracking those
163
+ when 'species', 'subspecies', 'genus', 'subgenus', 'variety'
164
+ nomenclator_array.compact.join(" ")
165
+ else
166
+ @name
167
+ end
168
+ end
169
+
170
+ # Return a Boolean, True if @rank is one of 'genus', 'subgenus', 'species', 'subspecies'
171
+ # TODO: update for infrasubspecifics if we start tracking those
172
+ def nomenclator_name?
173
+ %w{genus subgenus species subspecies variety}.include?(@rank)
174
+ end
175
+
176
+ # Return an Array of lenght 4 of Names representing a Species or Genus group name
177
+ # [genus, subgenus, species, subspecies, infrasubspecific]
178
+ def nomenclator_array
179
+ case @rank
180
+ when 'variety'
181
+ return [parent_name_at_rank('genus'), (parent_name_at_rank('subgenus') ? "(#{parent_name_at_rank('subgenus')})" : nil), parent_name_at_rank('species'), parent_name_at_rank('subspecies'), "var. #{@name}"]
182
+ when 'species', 'subspecies'
183
+ return [parent_name_at_rank('genus'), (parent_name_at_rank('subgenus') ? "(#{parent_name_at_rank('subgenus')})" : nil), parent_name_at_rank('species'), parent_name_at_rank('subspecies'), nil]
184
+ when 'subgenus'
185
+ return [parent_name_at_rank('genus'), "(#{@name})", nil, nil, nil]
186
+ when 'genus'
187
+ return [@name, nil, nil, nil, nil]
188
+ else
189
+ return false
190
+ end
191
+
192
+ end
193
+
123
194
  # Return a Taxonifi::Model::Name representing the finest genus_group_parent.
124
195
  # TODO: ICZN specific(?)
125
196
  def genus_group_parent
@@ -164,24 +235,7 @@ module Taxonifi
164
235
  nil
165
236
  end
166
237
 
167
- # Return the human readable version of this name with author year (String)
168
- def display_name
169
- [nomenclator_name, author_year].compact.join(" ")
170
- end
171
-
172
- # Return the human readable version of this name, without author year (String)
173
- def nomenclator_name
174
- case @rank
175
- when 'species', 'subspecies'
176
- [parent_name_at_rank('genus'), (parent_name_at_rank('subgenus') ? "(#{parent_name_at_rank('subgenus')})" : nil), parent_name_at_rank('species'), parent_name_at_rank('subspecies')].compact.join(" ")
177
- when 'subgenus'
178
- [parent_name_at_rank('genus'), "(#{@name})"].compact.join(" ")
179
- else
180
- [@name].compact.join(" ")
181
- end
182
- end
183
-
184
- # Return a dashed "vector" of ids representing the ancestor parent closure, like:
238
+ # Return a dashed "vector" of ids representing the ancestor parent closure, like:
185
239
  # 0-1-14-29g-45s-99-100.
186
240
  # Postfixed g means "genus", postifed s means "subgenus. As per SpecieFile usage.
187
241
  # TODO: !! malformed because the valid name is not injected. Note that this can be generated internally post import.
@@ -197,26 +251,64 @@ module Taxonifi
197
251
  ids.push a.id.to_s
198
252
  end
199
253
  end
200
-
201
254
  ids.join("-")
202
255
  end
203
256
 
204
- # Return names indexed by author_year.
257
+ # Return a Taxonifi::Model::AuthorYear representing author/year
258
+ # !! Identical to method in Taxonifi::Model::Ref
259
+ # !! *Not* necessarily unique.
205
260
  def author_year_index
206
261
  @author_year_index ||= generate_author_year_index
207
262
  end
208
263
 
209
- # Generate/return the author year index.
264
+ # Generate and return (String) the author year index.
210
265
  def generate_author_year_index
211
266
  @author_year_index = Taxonifi::Model::AuthorYear.new(people: @authors, year: @year).compact_index
212
267
  end
213
268
 
269
+ # TODO: test
270
+ # Returne True of False based on @rank
271
+ def species_group?
272
+ true if @rank == 'species' || @rank == 'subspecies'
273
+ end
274
+
275
+ # TODO: test
276
+ # Returne True of False based on @rank
277
+ def genus_group?
278
+ true if @rank == 'genus' || @rank == 'subgenus'
279
+ end
280
+
214
281
  # Return a String of Prolog rules representing this Name
215
282
  def prologify
216
283
  "false"
284
+ end
217
285
 
286
+ protected
287
+
288
+ # Generate @authors = [People], @year = 1999 from incoming initialization options.
289
+ def assign_author_year(opts)
290
+ # If for some reason already set get out
291
+ raise NameError, "Name initialization error, @authors set prior to conversionf from @author_year." if @year && !@authors.nil? && @authors.size > 0
292
+ author_year = nil
293
+ if !opts[:author_year].nil? && (opts[:author_year].size > 0)
294
+ author_year = opts[:author_year]
295
+ elsif !opts[:author].nil? && !@year.nil? && (opts[:author].size > 0) && (@year.size > 0)
296
+ author_year = opts[:author] + ", " + @year.to_s
218
297
  end
219
298
 
299
+ if !author_year.nil?
300
+ if ay = Taxonifi::Splitter::Builder.build_author_year(author_year)
301
+ @year = ay.year
302
+ @authors = ay.people
303
+ true
304
+ else
305
+ false
306
+ end
307
+ end
308
+ end
309
+
310
+
311
+
220
312
  end
221
313
 
222
314
  # ICZN specific sublassing of a taxonomic name.
@@ -5,28 +5,61 @@ module Taxonifi
5
5
  # A collection of taxonomic names.
6
6
  class NameCollection < Taxonifi::Model::Collection
7
7
 
8
- # A by-name (string index)
8
+ # A Hash. Keys are the name (String), values are ids of Names in the collection.
9
+ # { rank => { name => [ids] }}
10
+ # There are two special ranks "genus_group" and "species_group".
11
+ # E.g.: {'species_group' => {'bar' => [1,2,93]}})
9
12
  attr_accessor :by_name_index
10
-
13
+
11
14
  # A Taxonifi::Model::RefCollection, optionally generated from Author/Year strings
12
15
  attr_accessor :ref_collection
13
16
 
14
17
  # An optional collection of existing combinations of species names, as represented by
15
18
  # individual arrays of Taxonifi::Model::Names. Note you can not use a Taxonifi::Model::SpeciesName
16
19
  # for this purpose because getting/setting names therin will affect other combinations
20
+ # TODO: DEPRECATE? !?!
17
21
  attr_accessor :combinations
18
22
 
23
+ # A Hash. Contains metadata when non-unique names are found in input parsing
24
+ # {unique row identifier => { redundant row identifier => {properties hash}}}
25
+ # TODO: reconsider, move to superclass or down to record base
26
+ # TODO: Test
27
+ attr_accessor :duplicate_entry_buffer
28
+
29
+ # A Hash. Index (keys) created by the collection, values are an Array of Strings
30
+ # representing the genus, subgenus, species, and subspecies name.
31
+ # Automatically populated when .add_object is used.
32
+ # Alternately populated with .add_nomenclator(Name)
33
+ # Nomenclator values are not repeated.
34
+ # Index is used in @citations.
35
+ # { @nomenclator_index => [genus_string, subgenus_string, species_string, subspecies_string, infrasubspecific_string (e.g. variety)], ... }
36
+ # !! The names represented in the Array of strings does *NOT* have to be represented in the NameCollection.
37
+ attr_accessor :nomenclators
38
+
39
+ # An Integer used for indexing nomenclator records in @nomenclators. Contains the next available index value.
40
+ attr_accessor :nomenclator_index
41
+
42
+ # TaxonNameID => [[ nomenclator_index, Ref ], ... []]
43
+ attr_accessor :citations
44
+
19
45
  def initialize(options = {})
20
46
  super
21
- @by_name_index = {'genus_group' => {}, 'species_group' => {} } # "foo => [1,2,3]"
22
- Taxonifi::RANKS[0..-5].inject(@by_name_index){|hsh, v| hsh.merge!(v => {})} # Lumping species and genus group names
23
-
24
- @by_name_index['unknown'] = {} # unranked names get dumped in here
25
- @ref_collection = nil
26
- @combinations = []
47
+ @by_name_index = {'genus_group' => {}, 'species_group' => {}, 'unknown' => {}} # Lumping species and genus group names, unranked named are in 'unknown'
48
+ Taxonifi::RANKS[0..-5].inject(@by_name_index){|hsh, v| hsh.merge!(v => {})} # TODO: Still needed?!
49
+ @ref_collection = options[:ref_collection]
50
+ @citations = {}
51
+ @combinations = []
52
+ @nomenclators = {}
53
+ @nomenclator_index = options[:initial_nomenclator_index]
54
+ @nomenclator_index ||= 1
55
+ @duplicate_entry_buffer = {} # Move to Collection?!
27
56
  true
28
57
  end
29
58
 
59
+ def ref_collection=(ref_collection)
60
+ @ref_collection ||= ref_collection
61
+ end
62
+
30
63
  def object_class
31
64
  Taxonifi::Model::Name
32
65
  end
@@ -70,7 +103,7 @@ module Taxonifi
70
103
  by_name_index[rank][name.name_author_year_string].each do |id|
71
104
  full_parent_vector = parent_id_vector(name.parent.id)
72
105
  return id if full_parent_vector == parent_id_vector(id) # this hits species/genus group names
73
-
106
+
74
107
  vector = parent_id_vector(id)
75
108
  next if vector.last != name.parent.id # can stop looking at this possiblity
76
109
  vector.pop # compare just parents
@@ -83,20 +116,60 @@ module Taxonifi
83
116
  false
84
117
  end
85
118
 
86
- # Add an individaul name object, indexing it.
119
+ # Add an individual Name instance, indexing it.
87
120
  def add_object(obj)
88
121
  super
89
122
  index_by_name(obj)
123
+ derive_nomenclator(obj) if obj.nomenclator_name?
90
124
  obj
91
125
  end
92
126
 
93
- # Add an individaul name object, without indexing it.
127
+ # Add an individaul Name instance, without indexing it.
94
128
  def add_object_pre_indexed(obj)
95
129
  super
96
130
  index_by_name(obj)
97
131
  obj
98
132
  end
99
133
 
134
+ # TODO: Test
135
+ def derive_nomenclator(obj)
136
+ if obj.nomenclator_name? && !obj.authors.nil? && !obj.year.nil?
137
+ add_nomenclator(obj.nomenclator_array)
138
+ end
139
+ end
140
+
141
+ # Add a Nomenclator (Array) to the index.
142
+ # Returns the index of the Nomenclator added.
143
+ # TODO: Test
144
+ def add_nomenclator(nomenclator_array)
145
+ raise if (!nomenclator_array.class == Array) || (nomenclator_array.length != 5)
146
+ if @nomenclators.has_value?(nomenclator_array)
147
+ return @nomenclators.key(nomenclator_array)
148
+ else
149
+ @nomenclators.merge!(@nomenclator_index => nomenclator_array)
150
+ @nomenclator_index += 1
151
+ return @nomenclator_index - 1
152
+ end
153
+ end
154
+
155
+ # Return the Integer (index) for a name
156
+ def nomenclator_id_for_name(name)
157
+ @nomenclators.key(name.nomenclator_array)
158
+ end
159
+
160
+ # Add a Citation
161
+ # Returns the index of the Nomenclator added.
162
+ # TODO: Test/Validate
163
+ def add_citation(name, ref, nomenclator_index, properties)
164
+ if @citations[name.id]
165
+ return false if @citations[name.id].collect{|i| [i[0],i[1]] }.include?([ref.id, nomenclator_index])
166
+ @citations[name.id].push([ref.id, nomenclator_index, properties])
167
+ else
168
+ @citations[name.id] = [[ref.id, nomenclator_index, properties]]
169
+ end
170
+ true
171
+ end
172
+
100
173
  # Add a Taxonifi::Model::SpeciesName object
101
174
  # as individual objects.
102
175
  def add_species_name(sn)
@@ -126,24 +199,17 @@ module Taxonifi
126
199
  end
127
200
  end
128
201
 
129
- # Return an array of the names in the collection
130
- def name_string_array
131
- collection.collect{|n| n.display_name}
132
- end
133
-
134
-
135
- # Take the author/years of these names and generate a reference collection.
202
+ # Take the author/years of these names and generate a RefCollection.
136
203
  # Start the ids assigned to the references with initial_id.
137
204
  def generate_ref_collection(initial_id = 0)
138
205
  rc = Taxonifi::Model::RefCollection.new(:initial_id => initial_id)
139
- if collection.size > 0
140
- uniques = collection.inject({}){|hsh, n| hsh.merge!(n.author_year_string => nil)}.keys.compact
141
- if uniques.size > 0
142
- uniques.sort.each_with_index do |r, i|
143
- next if r.size == 0
144
- ref = Taxonifi::Model::Ref.new(:author_year => r)
145
- rc.add_object(ref)
146
- end
206
+ temp_index = {}
207
+ @collection.each do |n|
208
+ index = n.author_year_index
209
+ if !temp_index[index] && index.size > 0
210
+ temp_index.merge!(index => nil)
211
+ ref = Taxonifi::Model::Ref.new(authors: n.authors, year: n.year)
212
+ rc.add_object(ref)
147
213
  end
148
214
  end
149
215
  @ref_collection = rc
@@ -152,10 +218,10 @@ module Taxonifi
152
218
  # Assign a reference collection to this name collection.
153
219
  # !! Overwrites existing reference collection, including ones built
154
220
  # using generate_ref_collection.
155
- def ref_collection=(ref_collection)
221
+ def ref_collection=(ref_collection = Taxonifi::Model::RefCollection)
156
222
  @ref_collection = ref_collection if ref_collection.class == Taxonifi::Model::RefCollection
157
223
  end
158
-
224
+
159
225
  # Return an Array of "homonyms" within the rank
160
226
  # provided. Useful for finding missmatched upper heirarchies,
161
227
  # if nc is a name_collection:
@@ -169,7 +235,7 @@ module Taxonifi
169
235
  # end
170
236
  #
171
237
  def homonyms_at_rank(rank)
172
- raise if !RANKS.include?(rank)
238
+ raise if !RANKS.include?(rank)
173
239
  uniques = {}
174
240
  names_at_rank(rank).each do |n|
175
241
  uniques.merge!(n.name => []) if !uniques[n.name]
@@ -179,12 +245,47 @@ module Taxonifi
179
245
  uniques
180
246
  end
181
247
 
248
+ # Add a record to @duplicate_entry_buffer
249
+ def add_duplicate_entry_metadata(name_id, row_identifier, data_hash)
250
+ @duplicate_entry_buffer[name_id] = Hash.new if !@duplicate_entry_buffer[name_id]
251
+ @duplicate_entry_buffer[name_id].merge!(row_identifier => data_hash)
252
+ end
253
+
254
+ # For all species group names, assigns to property 'original_genus_id' the id of the parent genus group name if parens are not set.
255
+ # Returns an Array of ids for those names for which the property has *NOT* been set.
256
+ def add_original_genus_id_property
257
+ not_added = []
258
+ by_name_index['species_group'].values.flatten.uniq.each do |id|
259
+ name = object_by_id(id)
260
+ if name.parens != true
261
+ name.add_property('original_genus_id', name.genus_group_parent.id)
262
+ else
263
+ not_added.push(name.id)
264
+ end
265
+ end
266
+ not_added
267
+ end
268
+
269
+ # Return an Array of Strings
270
+ def genus_group_name_strings
271
+ by_name_index['genus_group'].keys
272
+ end
273
+
274
+ # Return an Array of Strings
275
+ def species_group_name_strings
276
+ by_name_index['species_group'].keys
277
+ end
278
+
279
+ # Return an array of the names in the collection
280
+ # DEPRECATE for name_strings
281
+ def name_string_array
282
+ collection.collect{|n| n.display_name}
283
+ end
284
+
182
285
  protected
183
286
 
184
- # Index the object by name into the
185
- # @by_name_index variable (this looks like:
186
- # {"Foo bar" => [1,2,93]})
187
- # Pass a Taxonifi::Name
287
+ # Index the object by name into the @by_name_index variable
288
+ # Pass a Taxonifi::Name
188
289
  def index_by_name(name)
189
290
  rank = name.rank
190
291
  rank = 'species_group' if %w{species subspecies variety}.include?(rank)
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "../models/base.rb"))
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "../model/base.rb"))
2
2
 
3
3
  module Taxonifi
4
4
  module Model
@@ -71,12 +71,14 @@ module Taxonifi
71
71
  s
72
72
  end
73
73
 
74
- # Return a by author_year index.
74
+ # Return a Taxonifi::Model::AuthorYear representing author/year
75
+ # !! Identical to method in Taxonifi::Model::Name
76
+ # !! *Not* necessarily unique.
75
77
  def author_year_index
76
78
  @author_year_index ||= generate_author_year_index
77
79
  end
78
80
 
79
- # (re-) generate the author year index.
81
+ # (Re-) generate the author year index.
80
82
  def generate_author_year_index
81
83
  @author_year_index = Taxonifi::Model::AuthorYear.new(people: @authors, year: @year).compact_index
82
84
  end