Class: Person

Inherits:
ApplicationRecord show all
Includes:
Housekeeping::Timestamps, Housekeeping::Users, Shared::AlternateValues, Shared::DataAttributes, Shared::Identifiable, Shared::IsData, Shared::Notable, Shared::SharedAcrossProjects
Defined in:
app/models/person.rb

Overview

A human. Data only, not users. There are two classes of people: vetted and unvetted.

A vetted person

  • Has two or more roles

  • Has one or more annotations

An unvetted person

  • Has no or 1 role

  • Has no annotations

A unvetted person becomes automatically vetted when they have > 1 roles or they have an annotation associated with them.

Direct Known Subclasses

Unvetted, Vetted

Defined Under Namespace

Classes: Unvetted, Vetted

Constant Summary

ALTERNATE_VALUES_FOR =

Class constants

[:last_name, :first_name]

Instance Attribute Summary (collapse)

Attributes included from Housekeeping::Users

#by

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods included from Housekeeping::Timestamps

#data_breakdown_for_chartkick_recent

Methods included from Housekeeping::Users

#set_created_by_id, #set_updated_by_id

Instance Attribute Details

- (String) cached

TODO:

Returns:

  • (String)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'app/models/person.rb', line 34

class Person < ApplicationRecord
  include Housekeeping::Users
  include Housekeeping::Timestamps
  include Shared::AlternateValues
  include Shared::DataAttributes
  include Shared::Identifiable
  include Shared::Notable
  include Shared::SharedAcrossProjects
  include Shared::IsData


  has_paper_trail :on => [:update]

  # Class constants
  ALTERNATE_VALUES_FOR = [:last_name, :first_name]

  validates_presence_of :last_name, :type
  before_validation :set_type_if_blank

  # after_save :update_bibtex_sources
  before_save :set_cached

  validates :type, inclusion: {in:      ['Person::Vetted', 'Person::Unvetted'],
                               message: "%{value} is not a validly_published type"}

  has_many :roles, dependent: :destroy, inverse_of: :person
  has_many :author_roles, class_name: 'SourceAuthor'
  has_many :editor_roles, class_name: 'SourceEditor'
  has_many :source_roles, class_name: 'SourceSource'
  has_many :collector_roles, class_name: 'Collector'
  has_many :determiner_roles, class_name: 'Determiner'
  has_many :taxon_name_author_roles, class_name: 'TaxonNameAuthor'
  has_many :type_designator_roles, class_name: 'TypeDesignator'
  has_many :georeferencer_roles, class_name: 'Georeferencer'

  # has_many :sources, through: :roles   # TODO: test and confirm dependent

  has_many :authored_sources, through: :author_roles, source: :role_object, source_type: 'Source::Bibtex'
  has_many :edited_sources, through: :editor_roles, source: :role_object, source_type: 'Source::Bibtex'
  has_many :human_sources, through: :source_roles, source: :role_object, source_type: 'Source::Human'
  has_many :collecting_events, through: :collector_roles, source: :role_object, source_type: 'CollectingEvent'
  has_many :taxon_determinations, through: :determiner_roles, source: :role_object, source_type: 'TaxonDetermination'
  has_many :taxon_name_authors, through: :taxon_name_author_roles, source: :role_object, source_type: 'TaxonName'
  has_many :type_material, through: :type_designator_roles, source: :role_object, source_type: 'TypeMaterial'
  has_many :georeferences, through: :georeferencer_roles, source: :role_object, source_type: 'Georeference'

  #scope :named, -> (name) {where(name: name)}
  #scope :named_smith, where(last_name: 'Smith')
  scope :named_smith, -> { where(last_name: 'Smith') }
  #scope :smith_start, -> {where(last_name: start_with?('Smith'))}  # have tried multiple ways to select records where last_name like 'Smith%' without success
  scope :created_before, -> (time) { where('created_at < ?', time) }
  scope :with_role, -> (role) { includes(:roles).where(roles: {type: role}) }
  scope :ordered_by_last_name, -> { order(:last_name) }

  def name
    [self.first_name, self.prefix, self.last_name, self.suffix].compact.join(' ').strip
  end

  # @return [String]
  #   The person's name in BibTeX format (von last, Jr, first)
  def bibtex_name
    out = ''
    out << self.prefix + ' ' unless self.prefix.blank?
    out << self.last_name unless self.last_name.blank?
    out << ', ' unless out.blank? || (self.first_name.blank? && self.suffix.blank?)
    out << self.suffix unless self.suffix.blank?
    out << ', ' unless out.end_with?(', ') || self.first_name.blank? || out.blank?
    out << self.first_name unless self.first_name.blank?
    out.strip
  end

  # @return [String]
  #   The person's full last name including prefix & suffix (von last Jr)
  def full_last_name
    out = ''
    out << self.prefix + ' ' unless self.prefix.blank?
    out << self.last_name unless self.last_name.blank?
    out << ' ' + self.suffix unless self.suffix.blank?
    out.strip
  end

  def is_author?
    self.author_roles.to_a.length > 0
  end

  def is_editor?
    self.editor_roles.to_a.length > 0
  end

  def is_source?
    self.source_roles.to_a.length > 0
  end

  def is_collector?
    self.collector_roles.to_a.length > 0
  end

  def is_determiner?
    self.determiner_roles.to_a.length > 0
  end

  def is_taxon_name_author?
    self.taxon_name_author_roles.to_a.length > 0
  end

  def is_type_designator?
    self.type_designator_roles.to_a.length > 0
  end

  def is_georeferencer?
    self.georeferencer_roles.to_a.length > 0
  end

  # TODO: TEST!
  def self.parser(name_string)
    BibTeX::Entry.new(type: :book, author: name_string).parse_names.to_citeproc['author']
  end

  # TODO: TEST!
  def self.parse_to_people(name_string)
    self.parser(name_string).collect { |n| Person::Unvetted.new(last_name: n['family'], first_name: n['given'], prefix: n['non-dropping-particle']) }
  end

  def self.generate_download(scope)
    CSV.generate do |csv|
      csv << column_names
      scope.order(id: :asc).find_each do |o|
        csv << o.attributes.values_at(*column_names).collect { |i|
          i.to_s.gsub(/\n/, '\n').gsub(/\t/, '\t')
        }
      end
    end
  end

  protected

  def set_type_if_blank
    self.type = 'Person::Unvetted' if self.type.blank?
  end

  def self.find_for_autocomplete(params)
    where('cached ILIKE ? OR cached ILIKE ? OR cached = ?', "#{params[:term]}%", "%#{params[:term]}%", params[:term])
  end

  def set_cached
    self.cached = self.bibtex_name if self.errors.empty?
  end

end

- (String) first name(name)

the first name, includes initials if the are provided

Returns:

  • (String)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'app/models/person.rb', line 34

class Person < ApplicationRecord
  include Housekeeping::Users
  include Housekeeping::Timestamps
  include Shared::AlternateValues
  include Shared::DataAttributes
  include Shared::Identifiable
  include Shared::Notable
  include Shared::SharedAcrossProjects
  include Shared::IsData


  has_paper_trail :on => [:update]

  # Class constants
  ALTERNATE_VALUES_FOR = [:last_name, :first_name]

  validates_presence_of :last_name, :type
  before_validation :set_type_if_blank

  # after_save :update_bibtex_sources
  before_save :set_cached

  validates :type, inclusion: {in:      ['Person::Vetted', 'Person::Unvetted'],
                               message: "%{value} is not a validly_published type"}

  has_many :roles, dependent: :destroy, inverse_of: :person
  has_many :author_roles, class_name: 'SourceAuthor'
  has_many :editor_roles, class_name: 'SourceEditor'
  has_many :source_roles, class_name: 'SourceSource'
  has_many :collector_roles, class_name: 'Collector'
  has_many :determiner_roles, class_name: 'Determiner'
  has_many :taxon_name_author_roles, class_name: 'TaxonNameAuthor'
  has_many :type_designator_roles, class_name: 'TypeDesignator'
  has_many :georeferencer_roles, class_name: 'Georeferencer'

  # has_many :sources, through: :roles   # TODO: test and confirm dependent

  has_many :authored_sources, through: :author_roles, source: :role_object, source_type: 'Source::Bibtex'
  has_many :edited_sources, through: :editor_roles, source: :role_object, source_type: 'Source::Bibtex'
  has_many :human_sources, through: :source_roles, source: :role_object, source_type: 'Source::Human'
  has_many :collecting_events, through: :collector_roles, source: :role_object, source_type: 'CollectingEvent'
  has_many :taxon_determinations, through: :determiner_roles, source: :role_object, source_type: 'TaxonDetermination'
  has_many :taxon_name_authors, through: :taxon_name_author_roles, source: :role_object, source_type: 'TaxonName'
  has_many :type_material, through: :type_designator_roles, source: :role_object, source_type: 'TypeMaterial'
  has_many :georeferences, through: :georeferencer_roles, source: :role_object, source_type: 'Georeference'

  #scope :named, -> (name) {where(name: name)}
  #scope :named_smith, where(last_name: 'Smith')
  scope :named_smith, -> { where(last_name: 'Smith') }
  #scope :smith_start, -> {where(last_name: start_with?('Smith'))}  # have tried multiple ways to select records where last_name like 'Smith%' without success
  scope :created_before, -> (time) { where('created_at < ?', time) }
  scope :with_role, -> (role) { includes(:roles).where(roles: {type: role}) }
  scope :ordered_by_last_name, -> { order(:last_name) }

  def name
    [self.first_name, self.prefix, self.last_name, self.suffix].compact.join(' ').strip
  end

  # @return [String]
  #   The person's name in BibTeX format (von last, Jr, first)
  def bibtex_name
    out = ''
    out << self.prefix + ' ' unless self.prefix.blank?
    out << self.last_name unless self.last_name.blank?
    out << ', ' unless out.blank? || (self.first_name.blank? && self.suffix.blank?)
    out << self.suffix unless self.suffix.blank?
    out << ', ' unless out.end_with?(', ') || self.first_name.blank? || out.blank?
    out << self.first_name unless self.first_name.blank?
    out.strip
  end

  # @return [String]
  #   The person's full last name including prefix & suffix (von last Jr)
  def full_last_name
    out = ''
    out << self.prefix + ' ' unless self.prefix.blank?
    out << self.last_name unless self.last_name.blank?
    out << ' ' + self.suffix unless self.suffix.blank?
    out.strip
  end

  def is_author?
    self.author_roles.to_a.length > 0
  end

  def is_editor?
    self.editor_roles.to_a.length > 0
  end

  def is_source?
    self.source_roles.to_a.length > 0
  end

  def is_collector?
    self.collector_roles.to_a.length > 0
  end

  def is_determiner?
    self.determiner_roles.to_a.length > 0
  end

  def is_taxon_name_author?
    self.taxon_name_author_roles.to_a.length > 0
  end

  def is_type_designator?
    self.type_designator_roles.to_a.length > 0
  end

  def is_georeferencer?
    self.georeferencer_roles.to_a.length > 0
  end

  # TODO: TEST!
  def self.parser(name_string)
    BibTeX::Entry.new(type: :book, author: name_string).parse_names.to_citeproc['author']
  end

  # TODO: TEST!
  def self.parse_to_people(name_string)
    self.parser(name_string).collect { |n| Person::Unvetted.new(last_name: n['family'], first_name: n['given'], prefix: n['non-dropping-particle']) }
  end

  def self.generate_download(scope)
    CSV.generate do |csv|
      csv << column_names
      scope.order(id: :asc).find_each do |o|
        csv << o.attributes.values_at(*column_names).collect { |i|
          i.to_s.gsub(/\n/, '\n').gsub(/\t/, '\t')
        }
      end
    end
  end

  protected

  def set_type_if_blank
    self.type = 'Person::Unvetted' if self.type.blank?
  end

  def self.find_for_autocomplete(params)
    where('cached ILIKE ? OR cached ILIKE ? OR cached = ?', "#{params[:term]}%", "%#{params[:term]}%", params[:term])
  end

  def set_cached
    self.cached = self.bibtex_name if self.errors.empty?
  end

end

- (String) last_name

the last/family name

Returns:

  • (String)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'app/models/person.rb', line 34

class Person < ApplicationRecord
  include Housekeeping::Users
  include Housekeeping::Timestamps
  include Shared::AlternateValues
  include Shared::DataAttributes
  include Shared::Identifiable
  include Shared::Notable
  include Shared::SharedAcrossProjects
  include Shared::IsData


  has_paper_trail :on => [:update]

  # Class constants
  ALTERNATE_VALUES_FOR = [:last_name, :first_name]

  validates_presence_of :last_name, :type
  before_validation :set_type_if_blank

  # after_save :update_bibtex_sources
  before_save :set_cached

  validates :type, inclusion: {in:      ['Person::Vetted', 'Person::Unvetted'],
                               message: "%{value} is not a validly_published type"}

  has_many :roles, dependent: :destroy, inverse_of: :person
  has_many :author_roles, class_name: 'SourceAuthor'
  has_many :editor_roles, class_name: 'SourceEditor'
  has_many :source_roles, class_name: 'SourceSource'
  has_many :collector_roles, class_name: 'Collector'
  has_many :determiner_roles, class_name: 'Determiner'
  has_many :taxon_name_author_roles, class_name: 'TaxonNameAuthor'
  has_many :type_designator_roles, class_name: 'TypeDesignator'
  has_many :georeferencer_roles, class_name: 'Georeferencer'

  # has_many :sources, through: :roles   # TODO: test and confirm dependent

  has_many :authored_sources, through: :author_roles, source: :role_object, source_type: 'Source::Bibtex'
  has_many :edited_sources, through: :editor_roles, source: :role_object, source_type: 'Source::Bibtex'
  has_many :human_sources, through: :source_roles, source: :role_object, source_type: 'Source::Human'
  has_many :collecting_events, through: :collector_roles, source: :role_object, source_type: 'CollectingEvent'
  has_many :taxon_determinations, through: :determiner_roles, source: :role_object, source_type: 'TaxonDetermination'
  has_many :taxon_name_authors, through: :taxon_name_author_roles, source: :role_object, source_type: 'TaxonName'
  has_many :type_material, through: :type_designator_roles, source: :role_object, source_type: 'TypeMaterial'
  has_many :georeferences, through: :georeferencer_roles, source: :role_object, source_type: 'Georeference'

  #scope :named, -> (name) {where(name: name)}
  #scope :named_smith, where(last_name: 'Smith')
  scope :named_smith, -> { where(last_name: 'Smith') }
  #scope :smith_start, -> {where(last_name: start_with?('Smith'))}  # have tried multiple ways to select records where last_name like 'Smith%' without success
  scope :created_before, -> (time) { where('created_at < ?', time) }
  scope :with_role, -> (role) { includes(:roles).where(roles: {type: role}) }
  scope :ordered_by_last_name, -> { order(:last_name) }

  def name
    [self.first_name, self.prefix, self.last_name, self.suffix].compact.join(' ').strip
  end

  # @return [String]
  #   The person's name in BibTeX format (von last, Jr, first)
  def bibtex_name
    out = ''
    out << self.prefix + ' ' unless self.prefix.blank?
    out << self.last_name unless self.last_name.blank?
    out << ', ' unless out.blank? || (self.first_name.blank? && self.suffix.blank?)
    out << self.suffix unless self.suffix.blank?
    out << ', ' unless out.end_with?(', ') || self.first_name.blank? || out.blank?
    out << self.first_name unless self.first_name.blank?
    out.strip
  end

  # @return [String]
  #   The person's full last name including prefix & suffix (von last Jr)
  def full_last_name
    out = ''
    out << self.prefix + ' ' unless self.prefix.blank?
    out << self.last_name unless self.last_name.blank?
    out << ' ' + self.suffix unless self.suffix.blank?
    out.strip
  end

  def is_author?
    self.author_roles.to_a.length > 0
  end

  def is_editor?
    self.editor_roles.to_a.length > 0
  end

  def is_source?
    self.source_roles.to_a.length > 0
  end

  def is_collector?
    self.collector_roles.to_a.length > 0
  end

  def is_determiner?
    self.determiner_roles.to_a.length > 0
  end

  def is_taxon_name_author?
    self.taxon_name_author_roles.to_a.length > 0
  end

  def is_type_designator?
    self.type_designator_roles.to_a.length > 0
  end

  def is_georeferencer?
    self.georeferencer_roles.to_a.length > 0
  end

  # TODO: TEST!
  def self.parser(name_string)
    BibTeX::Entry.new(type: :book, author: name_string).parse_names.to_citeproc['author']
  end

  # TODO: TEST!
  def self.parse_to_people(name_string)
    self.parser(name_string).collect { |n| Person::Unvetted.new(last_name: n['family'], first_name: n['given'], prefix: n['non-dropping-particle']) }
  end

  def self.generate_download(scope)
    CSV.generate do |csv|
      csv << column_names
      scope.order(id: :asc).find_each do |o|
        csv << o.attributes.values_at(*column_names).collect { |i|
          i.to_s.gsub(/\n/, '\n').gsub(/\t/, '\t')
        }
      end
    end
  end

  protected

  def set_type_if_blank
    self.type = 'Person::Unvetted' if self.type.blank?
  end

  def self.find_for_autocomplete(params)
    where('cached ILIKE ? OR cached ILIKE ? OR cached = ?', "#{params[:term]}%", "%#{params[:term]}%", params[:term])
  end

  def set_cached
    self.cached = self.bibtex_name if self.errors.empty?
  end

end

- (String) suffix

string following the last/family name

Returns:

  • (String)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'app/models/person.rb', line 34

class Person < ApplicationRecord
  include Housekeeping::Users
  include Housekeeping::Timestamps
  include Shared::AlternateValues
  include Shared::DataAttributes
  include Shared::Identifiable
  include Shared::Notable
  include Shared::SharedAcrossProjects
  include Shared::IsData


  has_paper_trail :on => [:update]

  # Class constants
  ALTERNATE_VALUES_FOR = [:last_name, :first_name]

  validates_presence_of :last_name, :type
  before_validation :set_type_if_blank

  # after_save :update_bibtex_sources
  before_save :set_cached

  validates :type, inclusion: {in:      ['Person::Vetted', 'Person::Unvetted'],
                               message: "%{value} is not a validly_published type"}

  has_many :roles, dependent: :destroy, inverse_of: :person
  has_many :author_roles, class_name: 'SourceAuthor'
  has_many :editor_roles, class_name: 'SourceEditor'
  has_many :source_roles, class_name: 'SourceSource'
  has_many :collector_roles, class_name: 'Collector'
  has_many :determiner_roles, class_name: 'Determiner'
  has_many :taxon_name_author_roles, class_name: 'TaxonNameAuthor'
  has_many :type_designator_roles, class_name: 'TypeDesignator'
  has_many :georeferencer_roles, class_name: 'Georeferencer'

  # has_many :sources, through: :roles   # TODO: test and confirm dependent

  has_many :authored_sources, through: :author_roles, source: :role_object, source_type: 'Source::Bibtex'
  has_many :edited_sources, through: :editor_roles, source: :role_object, source_type: 'Source::Bibtex'
  has_many :human_sources, through: :source_roles, source: :role_object, source_type: 'Source::Human'
  has_many :collecting_events, through: :collector_roles, source: :role_object, source_type: 'CollectingEvent'
  has_many :taxon_determinations, through: :determiner_roles, source: :role_object, source_type: 'TaxonDetermination'
  has_many :taxon_name_authors, through: :taxon_name_author_roles, source: :role_object, source_type: 'TaxonName'
  has_many :type_material, through: :type_designator_roles, source: :role_object, source_type: 'TypeMaterial'
  has_many :georeferences, through: :georeferencer_roles, source: :role_object, source_type: 'Georeference'

  #scope :named, -> (name) {where(name: name)}
  #scope :named_smith, where(last_name: 'Smith')
  scope :named_smith, -> { where(last_name: 'Smith') }
  #scope :smith_start, -> {where(last_name: start_with?('Smith'))}  # have tried multiple ways to select records where last_name like 'Smith%' without success
  scope :created_before, -> (time) { where('created_at < ?', time) }
  scope :with_role, -> (role) { includes(:roles).where(roles: {type: role}) }
  scope :ordered_by_last_name, -> { order(:last_name) }

  def name
    [self.first_name, self.prefix, self.last_name, self.suffix].compact.join(' ').strip
  end

  # @return [String]
  #   The person's name in BibTeX format (von last, Jr, first)
  def bibtex_name
    out = ''
    out << self.prefix + ' ' unless self.prefix.blank?
    out << self.last_name unless self.last_name.blank?
    out << ', ' unless out.blank? || (self.first_name.blank? && self.suffix.blank?)
    out << self.suffix unless self.suffix.blank?
    out << ', ' unless out.end_with?(', ') || self.first_name.blank? || out.blank?
    out << self.first_name unless self.first_name.blank?
    out.strip
  end

  # @return [String]
  #   The person's full last name including prefix & suffix (von last Jr)
  def full_last_name
    out = ''
    out << self.prefix + ' ' unless self.prefix.blank?
    out << self.last_name unless self.last_name.blank?
    out << ' ' + self.suffix unless self.suffix.blank?
    out.strip
  end

  def is_author?
    self.author_roles.to_a.length > 0
  end

  def is_editor?
    self.editor_roles.to_a.length > 0
  end

  def is_source?
    self.source_roles.to_a.length > 0
  end

  def is_collector?
    self.collector_roles.to_a.length > 0
  end

  def is_determiner?
    self.determiner_roles.to_a.length > 0
  end

  def is_taxon_name_author?
    self.taxon_name_author_roles.to_a.length > 0
  end

  def is_type_designator?
    self.type_designator_roles.to_a.length > 0
  end

  def is_georeferencer?
    self.georeferencer_roles.to_a.length > 0
  end

  # TODO: TEST!
  def self.parser(name_string)
    BibTeX::Entry.new(type: :book, author: name_string).parse_names.to_citeproc['author']
  end

  # TODO: TEST!
  def self.parse_to_people(name_string)
    self.parser(name_string).collect { |n| Person::Unvetted.new(last_name: n['family'], first_name: n['given'], prefix: n['non-dropping-particle']) }
  end

  def self.generate_download(scope)
    CSV.generate do |csv|
      csv << column_names
      scope.order(id: :asc).find_each do |o|
        csv << o.attributes.values_at(*column_names).collect { |i|
          i.to_s.gsub(/\n/, '\n').gsub(/\t/, '\t')
        }
      end
    end
  end

  protected

  def set_type_if_blank
    self.type = 'Person::Unvetted' if self.type.blank?
  end

  def self.find_for_autocomplete(params)
    where('cached ILIKE ? OR cached ILIKE ? OR cached = ?', "#{params[:term]}%", "%#{params[:term]}%", params[:term])
  end

  def set_cached
    self.cached = self.bibtex_name if self.errors.empty?
  end

end

- (String) type

Person::Vetted or Person::Unvetted

Returns:

  • (String)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'app/models/person.rb', line 34

class Person < ApplicationRecord
  include Housekeeping::Users
  include Housekeeping::Timestamps
  include Shared::AlternateValues
  include Shared::DataAttributes
  include Shared::Identifiable
  include Shared::Notable
  include Shared::SharedAcrossProjects
  include Shared::IsData


  has_paper_trail :on => [:update]

  # Class constants
  ALTERNATE_VALUES_FOR = [:last_name, :first_name]

  validates_presence_of :last_name, :type
  before_validation :set_type_if_blank

  # after_save :update_bibtex_sources
  before_save :set_cached

  validates :type, inclusion: {in:      ['Person::Vetted', 'Person::Unvetted'],
                               message: "%{value} is not a validly_published type"}

  has_many :roles, dependent: :destroy, inverse_of: :person
  has_many :author_roles, class_name: 'SourceAuthor'
  has_many :editor_roles, class_name: 'SourceEditor'
  has_many :source_roles, class_name: 'SourceSource'
  has_many :collector_roles, class_name: 'Collector'
  has_many :determiner_roles, class_name: 'Determiner'
  has_many :taxon_name_author_roles, class_name: 'TaxonNameAuthor'
  has_many :type_designator_roles, class_name: 'TypeDesignator'
  has_many :georeferencer_roles, class_name: 'Georeferencer'

  # has_many :sources, through: :roles   # TODO: test and confirm dependent

  has_many :authored_sources, through: :author_roles, source: :role_object, source_type: 'Source::Bibtex'
  has_many :edited_sources, through: :editor_roles, source: :role_object, source_type: 'Source::Bibtex'
  has_many :human_sources, through: :source_roles, source: :role_object, source_type: 'Source::Human'
  has_many :collecting_events, through: :collector_roles, source: :role_object, source_type: 'CollectingEvent'
  has_many :taxon_determinations, through: :determiner_roles, source: :role_object, source_type: 'TaxonDetermination'
  has_many :taxon_name_authors, through: :taxon_name_author_roles, source: :role_object, source_type: 'TaxonName'
  has_many :type_material, through: :type_designator_roles, source: :role_object, source_type: 'TypeMaterial'
  has_many :georeferences, through: :georeferencer_roles, source: :role_object, source_type: 'Georeference'

  #scope :named, -> (name) {where(name: name)}
  #scope :named_smith, where(last_name: 'Smith')
  scope :named_smith, -> { where(last_name: 'Smith') }
  #scope :smith_start, -> {where(last_name: start_with?('Smith'))}  # have tried multiple ways to select records where last_name like 'Smith%' without success
  scope :created_before, -> (time) { where('created_at < ?', time) }
  scope :with_role, -> (role) { includes(:roles).where(roles: {type: role}) }
  scope :ordered_by_last_name, -> { order(:last_name) }

  def name
    [self.first_name, self.prefix, self.last_name, self.suffix].compact.join(' ').strip
  end

  # @return [String]
  #   The person's name in BibTeX format (von last, Jr, first)
  def bibtex_name
    out = ''
    out << self.prefix + ' ' unless self.prefix.blank?
    out << self.last_name unless self.last_name.blank?
    out << ', ' unless out.blank? || (self.first_name.blank? && self.suffix.blank?)
    out << self.suffix unless self.suffix.blank?
    out << ', ' unless out.end_with?(', ') || self.first_name.blank? || out.blank?
    out << self.first_name unless self.first_name.blank?
    out.strip
  end

  # @return [String]
  #   The person's full last name including prefix & suffix (von last Jr)
  def full_last_name
    out = ''
    out << self.prefix + ' ' unless self.prefix.blank?
    out << self.last_name unless self.last_name.blank?
    out << ' ' + self.suffix unless self.suffix.blank?
    out.strip
  end

  def is_author?
    self.author_roles.to_a.length > 0
  end

  def is_editor?
    self.editor_roles.to_a.length > 0
  end

  def is_source?
    self.source_roles.to_a.length > 0
  end

  def is_collector?
    self.collector_roles.to_a.length > 0
  end

  def is_determiner?
    self.determiner_roles.to_a.length > 0
  end

  def is_taxon_name_author?
    self.taxon_name_author_roles.to_a.length > 0
  end

  def is_type_designator?
    self.type_designator_roles.to_a.length > 0
  end

  def is_georeferencer?
    self.georeferencer_roles.to_a.length > 0
  end

  # TODO: TEST!
  def self.parser(name_string)
    BibTeX::Entry.new(type: :book, author: name_string).parse_names.to_citeproc['author']
  end

  # TODO: TEST!
  def self.parse_to_people(name_string)
    self.parser(name_string).collect { |n| Person::Unvetted.new(last_name: n['family'], first_name: n['given'], prefix: n['non-dropping-particle']) }
  end

  def self.generate_download(scope)
    CSV.generate do |csv|
      csv << column_names
      scope.order(id: :asc).find_each do |o|
        csv << o.attributes.values_at(*column_names).collect { |i|
          i.to_s.gsub(/\n/, '\n').gsub(/\t/, '\t')
        }
      end
    end
  end

  protected

  def set_type_if_blank
    self.type = 'Person::Unvetted' if self.type.blank?
  end

  def self.find_for_autocomplete(params)
    where('cached ILIKE ? OR cached ILIKE ? OR cached = ?', "#{params[:term]}%", "%#{params[:term]}%", params[:term])
  end

  def set_cached
    self.cached = self.bibtex_name if self.errors.empty?
  end

end

Class Method Details

+ (Object) find_for_autocomplete(params) (protected)



174
175
176
# File 'app/models/person.rb', line 174

def self.find_for_autocomplete(params)
  where('cached ILIKE ? OR cached ILIKE ? OR cached = ?', "#{params[:term]}%", "%#{params[:term]}%", params[:term])
end

+ (Object) generate_download(scope)



157
158
159
160
161
162
163
164
165
166
# File 'app/models/person.rb', line 157

def self.generate_download(scope)
  CSV.generate do |csv|
    csv << column_names
    scope.order(id: :asc).find_each do |o|
      csv << o.attributes.values_at(*column_names).collect { |i|
        i.to_s.gsub(/\n/, '\n').gsub(/\t/, '\t')
      }
    end
  end
end

+ (Object) parse_to_people(name_string)

TODO: TEST!



153
154
155
# File 'app/models/person.rb', line 153

def self.parse_to_people(name_string)
  self.parser(name_string).collect { |n| Person::Unvetted.new(last_name: n['family'], first_name: n['given'], prefix: n['non-dropping-particle']) }
end

+ (Object) parser(name_string)

TODO: TEST!



148
149
150
# File 'app/models/person.rb', line 148

def self.parser(name_string)
  BibTeX::Entry.new(type: :book, author: name_string).parse_names.to_citeproc['author']
end

Instance Method Details

- (String) bibtex_name

Returns The person's name in BibTeX format (von last, Jr, first)

Returns:

  • (String)

    The person's name in BibTeX format (von last, Jr, first)



94
95
96
97
98
99
100
101
102
103
# File 'app/models/person.rb', line 94

def bibtex_name
  out = ''
  out << self.prefix + ' ' unless self.prefix.blank?
  out << self.last_name unless self.last_name.blank?
  out << ', ' unless out.blank? || (self.first_name.blank? && self.suffix.blank?)
  out << self.suffix unless self.suffix.blank?
  out << ', ' unless out.end_with?(', ') || self.first_name.blank? || out.blank?
  out << self.first_name unless self.first_name.blank?
  out.strip
end

- (String) full_last_name

Returns The person's full last name including prefix & suffix (von last Jr)

Returns:

  • (String)

    The person's full last name including prefix & suffix (von last Jr)



107
108
109
110
111
112
113
# File 'app/models/person.rb', line 107

def full_last_name
  out = ''
  out << self.prefix + ' ' unless self.prefix.blank?
  out << self.last_name unless self.last_name.blank?
  out << ' ' + self.suffix unless self.suffix.blank?
  out.strip
end

- (Boolean) is_author?

Returns:

  • (Boolean)


115
116
117
# File 'app/models/person.rb', line 115

def is_author?
  self.author_roles.to_a.length > 0
end

- (Boolean) is_collector?

Returns:

  • (Boolean)


127
128
129
# File 'app/models/person.rb', line 127

def is_collector?
  self.collector_roles.to_a.length > 0
end

- (Boolean) is_determiner?

Returns:

  • (Boolean)


131
132
133
# File 'app/models/person.rb', line 131

def is_determiner?
  self.determiner_roles.to_a.length > 0
end

- (Boolean) is_editor?

Returns:

  • (Boolean)


119
120
121
# File 'app/models/person.rb', line 119

def is_editor?
  self.editor_roles.to_a.length > 0
end

- (Boolean) is_georeferencer?

Returns:

  • (Boolean)


143
144
145
# File 'app/models/person.rb', line 143

def is_georeferencer?
  self.georeferencer_roles.to_a.length > 0
end

- (Boolean) is_source?

Returns:

  • (Boolean)


123
124
125
# File 'app/models/person.rb', line 123

def is_source?
  self.source_roles.to_a.length > 0
end

- (Boolean) is_taxon_name_author?

Returns:

  • (Boolean)


135
136
137
# File 'app/models/person.rb', line 135

def is_taxon_name_author?
  self.taxon_name_author_roles.to_a.length > 0
end

- (Boolean) is_type_designator?

Returns:

  • (Boolean)


139
140
141
# File 'app/models/person.rb', line 139

def is_type_designator?
  self.type_designator_roles.to_a.length > 0
end

- (Object) name



88
89
90
# File 'app/models/person.rb', line 88

def name
  [self.first_name, self.prefix, self.last_name, self.suffix].compact.join(' ').strip
end

- (Object) set_cached (protected)



178
179
180
# File 'app/models/person.rb', line 178

def set_cached
  self.cached = self.bibtex_name if self.errors.empty?
end

- (Object) set_type_if_blank (protected)



170
171
172
# File 'app/models/person.rb', line 170

def set_type_if_blank
  self.type = 'Person::Unvetted' if self.type.blank?
end