Class: Person

Inherits:
ActiveRecord::Base
  • Object
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
# File 'app/models/person.rb', line 34

class Person < ActiveRecord::Base
  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

  # 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 :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'

  #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

  # 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
# File 'app/models/person.rb', line 34

class Person < ActiveRecord::Base
  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

  # 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 :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'

  #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

  # 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
# File 'app/models/person.rb', line 34

class Person < ActiveRecord::Base
  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

  # 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 :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'

  #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

  # 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
# File 'app/models/person.rb', line 34

class Person < ActiveRecord::Base
  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

  # 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 :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'

  #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

  # 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
# File 'app/models/person.rb', line 34

class Person < ActiveRecord::Base
  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

  # 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 :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'

  #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

  # 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)



167
168
169
# File 'app/models/person.rb', line 167

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)



150
151
152
153
154
155
156
157
158
159
# File 'app/models/person.rb', line 150

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!



146
147
148
# File 'app/models/person.rb', line 146

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!



141
142
143
# File 'app/models/person.rb', line 141

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)



91
92
93
94
95
96
97
98
99
100
# File 'app/models/person.rb', line 91

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)



104
105
106
107
108
109
110
# File 'app/models/person.rb', line 104

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)


112
113
114
# File 'app/models/person.rb', line 112

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

- (Boolean) is_collector?

Returns:

  • (Boolean)


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

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

- (Boolean) is_determiner?

Returns:

  • (Boolean)


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

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

- (Boolean) is_editor?

Returns:

  • (Boolean)


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

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

- (Boolean) is_source?

Returns:

  • (Boolean)


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

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

- (Boolean) is_taxon_name_author?

Returns:

  • (Boolean)


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

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

- (Boolean) is_type_designator?

Returns:

  • (Boolean)


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

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

- (Object) name



85
86
87
# File 'app/models/person.rb', line 85

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

- (Object) set_cached (protected)



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

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

- (Object) set_type_if_blank (protected)



163
164
165
# File 'app/models/person.rb', line 163

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