This repository has been archived by the owner on Jun 11, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
person.rb
420 lines (361 loc) · 12.9 KB
/
person.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
require 'rubygems'
require 'open-uri'
require 'rdf/ntriples'
# This class represents a human who has been commemorated on a plaque.
# === Attributes
# * +name+ - The common full name of the person.
# * +wikipedia_url+ - An override link to the person's Wikipedia page (if they have one and it isn't linked to via their name).
# * +dbpedia_uri+ - A link to the DBpedia resource representing the person (if one exists).
# * +born_on+ - The date on which the person was born. Optional.
# * +died_on+ - The date on which the person died. Optional.
# * +born_on_is_circa+ - True or False. Whether the +born_on+ date is 'circa' or not. Optional.
# * +died_on_is_circa+ - True or False. Whether the +died_on+ date is 'circa' or not. Optional.
# === Associations
# * Plaques - plaques on which this person is commemorated.
# * Roles - roles associated with this person (eg 'carpenter').
# * Locations - locations associated with this person (via plaques).
# * Verbs - verbs associated with this person (via plaques).
# * Depiction - image showing the person (via photos)
class Person < ActiveRecord::Base
validates_presence_of :name
has_many :roles, :through => :personal_roles, :order => 'started_at asc'
has_many :personal_roles, :order => 'started_at asc'
has_many :relationships, :class_name => "PersonalRole", :conditions => ['related_person_id IS NOT NULL'], :order => 'started_at asc'
has_many :straight_roles, :class_name => "PersonalRole", :conditions => ['related_person_id IS NULL'], :order => 'started_at asc'
has_many :personal_connections, :order => 'started_at asc'
has_many :locations, :through => :personal_connections, :uniq => true
has_many :plaques, :through => :personal_connections, :uniq => true
has_one :birth_connection, :class_name => "PersonalConnection", :conditions => [ 'verb_id in (8,504)']
has_one :death_connection, :class_name => "PersonalConnection", :conditions => [ 'verb_id in (3,49,161,1108)']
has_one :main_photo, :class_name => "Photo"
attr_accessor :abstract, :comment # dbpedia fields
before_save :update_index
scope :no_role, :conditions => {:personal_roles_count => [nil,0]}
scope :no_dates, :conditions => ['born_on IS NULL and died_on IS NULL']
DATE_REGEX = /c?[\d]{4}/
DATE_RANGE_REGEX = /(?:\(#{DATE_REGEX}-#{DATE_REGEX}\)|#{DATE_REGEX}-#{DATE_REGEX})/
def areas
areas = []
locations.each do |location|
if location.area
areas << location.area unless areas.include?(location.area)
end
end
end
def self.find_or_create_by_name_and_dates(string)
name_and_dates_regex = /\A(.*)\s\(?(c?)([\d]{4})-(c?)([\d]{4})\)?\Z/
if string =~ name_and_dates_regex
person = find_or_create_by_name(string[name_and_dates_regex, 1])
unless person.born_on or person.died_on
person.born_on = Date.parse(string[name_and_dates_regex, 3] + "-01-01")
if string[name_and_dates_regex, 2] == "c"
person.born_on_is_circa = true
end
person.died_on = Date.parse(string[name_and_dates_regex, 5] + "-01-01")
if string[name_and_dates_regex, 4] == "c"
person.died_on_is_circa = true
end
person.save!
end
return Person.find(person.id)
else
find_or_create_by_name(string)
end
end
def self.find_or_create_by_name_and_dates_and_roles(string)
name_dates_roles_regex = /\A(.*\s#{DATE_RANGE_REGEX}),?\s?(.*)?\Z/
name_roles_re = /\A([A-Z][a-zA-Z\.,]+(\s(([A-Z][a-z]+|[A-Z]\.|de))|\,\sEarl\sof\s[A-Z][a-z]+)*)((\s|,\s)(.*?))?\Z/
if string =~ name_dates_roles_regex
person = find_or_create_by_name_and_dates(string[name_dates_roles_regex, 1])
person.find_or_create_roles(string[name_dates_roles_regex, 2])
elsif string =~ name_roles_re
person = find_or_create_by_name(string[name_roles_re, 1].strip)
person.find_or_create_roles(string[name_roles_re, 7])
else
person = find_or_create_by_name_and_dates(string)
end
return person
end
def find_or_create_roles(roles)
if roles
roles = roles.split(/,?\sand\s|,?\s\&\s|,\s/)
roles.each do |role|
role = Role.find_or_create_by_name_and_slug(role, role.downcase.gsub(" ", "_").gsub("-", "_").gsub("'", ""))
unless self.roles.exists?(role)
self.roles << role
end
end
end
return self
end
def person?
!(animal? or thing? or group? or place?)
end
def animal?
roles.any?{|role| role.animal?}
end
def thing?
roles.any?{|role| role.thing?}
end
def group?
roles.any?{|role| role.group?}
end
def place?
roles.any?{|role| role.place?}
end
def type
return "person" if person?
return "animal" if animal?
return "thing" if thing?
return "group" if group?
return "place" if place?
return "?"
end
def born_in
born_on.year if born_on
end
def died_in
died_on.year if died_on
end
def born_at
birth_connection.location if (birth_connection)
end
def died_at
death_connection.location if (death_connection)
end
def dead?
false
return true if died_in
return true if (person? || animal?) && born_in && born_in < 1900
end
def alive?
!dead?
end
def existence_word
return "is" if alive?
"was"
end
def age
"unknown"
return died_in - born_in if died_in && born_in
return Time.now.year - born_in if born_in && thing?
return Time.now.year - born_in if born_in && born_in > 1900
end
def age_in(year)
return year - born_in if born_in
end
# note that the Wikipedia url is constructed from the person's name
# unless it is overridden by data in the wikipedia_url field
# or the wikipedia_url field is set to blank to indicate that there
# is no Wikipedia record
def default_wikipedia_url
return wikipedia_url if wikipedia_url && wikipedia_url > ""
untitled_name = name.gsub("Canon ","").gsub("Captain ","").gsub("Cardinal ","").gsub("Dame ","").gsub("Dr ","").gsub("Lord ","").gsub("Sir ","").strip.gsub(/ /,"_")
"http://en.wikipedia.org/wiki/"+untitled_name
end
def default_dbpedia_uri
return default_wikipedia_url.gsub("http://en.wikipedia.org/wiki","http://dbpedia.org/resource")
end
def dbpedia_ntriples_uri
default_dbpedia_uri.gsub("resource","data") + ".ntriples"
end
def name_and_dates
name + dates
end
def dates
r = ""
r += " (" if born_on || died_on
r += born_on.year.to_s if born_on
r += "?-" if !born_on && died_on
r += "-" if born_on && died_on
r += died_on.year.to_s if died_on
r += ")" if born_on || died_on
return r
end
def surname
self.name[self.name.downcase.rindex(" " + self.surname_starts_with.downcase) ? self.name.downcase.rindex(" " + self.surname_starts_with.downcase) + 1: 0,self.name.size]
end
def default_thumbnail_url
return "/assets/NoPersonSqr.png"
end
def populate_from_dbpedia
begin
graph = RDF::Graph.load(self.dbpedia_ntriples_uri)
query = RDF::Query.new({
:person => {
RDF::URI("http://dbpedia.org/ontology/birthDate") => :birthDate,
RDF::URI("http://dbpedia.org/ontology/deathDate") => :deathDate,
RDF::URI("http://xmlns.com/foaf/0.1/depiction") => :depiction,
RDF::URI("http://dbpedia.org/ontology/abstract") => :abstract,
RDF::URI("http://www.w3.org/2000/01/rdf-schema#comment") => :comment,
}
})
query.execute(graph).filter { |solution| solution.comment.language == :en }.each do |solution|
self.depiction = solution.depiction
# need to filter abstract/comment with something like http://rdf.rubyforge.org/RDF/Query/Solutions.html
self.abstract = solution.abstract
self.comment = solution.comment
end
rescue
end
end
def title
title = ""
sir = ""
roles.each{|role|
sir = "Sir " if role.confers_honourific_title?
title += (role.abbreviated? ? role.abbreviation : role.name) + " " if role.used_as_a_prefix? and !title.include?(role.abbreviated? ? role.abbreviation : role.name)
}
# a clergyman or Commonwealth citizen does not get called 'Sir'
title += sir unless clergy?
title
end
def titled?
title != ""
end
def clergy?
return true if roles.any? { |role| role.type=="clergy"}
false
end
def letters
letters = ""
roles.each{|role|
letters += role.abbreviation + " " if role.used_as_a_suffix?
}
letters
end
def full_name
fullname = title + name
fullname += " " + letters if !letters.blank?
fullname
end
def parental_relationships
parents = []
relationships.each{|relationship|
parents << relationship if relationship.role.name=="son" or relationship.role.name=="daughter"
}
parents
end
def issue
issue = []
relationships.each{|relationship|
issue << relationship.related_person if relationship.role.name=="father" or relationship.role.name=="mother"
}
issue.sort! { |a,b| a.born_on||0 <=> b.born_on||0 }
end
def siblings
siblings = []
relationships.each{|relationship|
siblings << relationship.related_person if relationship.role.name=="brother" or relationship.role.name=="sister" or relationship.role.name=="half-brother" or relationship.role.name=="half-sister"
}
siblings.sort! { |a,b| a.born_on||0 <=> b.born_on||0 }
end
def spousal_relationships
spouses = []
relationships.each{|relationship|
spouses << relationship if relationship.role.name=="wife" or relationship.role.name=="husband"
}
spouses
end
def non_family_relationships
non_family = []
relationships.each{|relationship|
non_family << relationship if relationship.role.name!="husband" and relationship.role.name!="wife" and relationship.role.name!="brother" and relationship.role.name!="sister" and relationship.role.name!="half-brother" and relationship.role.name!="half-sister" and relationship.role.name!="father" and relationship.role.name!="mother" and relationship.role.name!="son" and relationship.role.name!="daughter"
}
non_family
end
def creation_word
return "from" if (self.thing?)
return "formed in" if (self.group?)
return "built in" if (self.place?)
"born in"
end
def destruction_word
return "until" if self.thing?
return "ended in" if self.group?
return "closed in" if self.place?
"died in"
end
def personal_pronoun
return "it" if (self.thing? || self.group? || self.place?)
return "he" if self.male?
return "she" if self.female?
"he/she"
end
def male?
!self.female?
end
def female?
return true if roles.any?{|role| role.female?}
return true if self.name.start_with?(
"Abigail","Adelaide","Ada","Agnes","Alice","Alison","Amelia","Anastasia","Anna","Anne","Annie","Antoinette",
"Beatriz",
"Caroline","Charlotte","Constance",
"Deborah","Diana","Dolly","Doris","Dorothea",
"Elizabeth","Ellen","Emma",
"Florence",
"Georgia","Georgina","Gladys",
"Hattie",
"Jane","Janet","Jacqueline","Jeanne","Julia",
"Kate","Kathleen",
"Letitia", "Lidia","Louisa",
"Mabel","Margery","Marianne","Mary","May","Mercy",
"Nancy, Nelly",
"Paloma","Priscilla",
"Rachel","Roberta","Rosa","Rose",
"Sally","Susanna",
"Ursula",
"Victoria","Violet","Virginia",
"Wilhelmina","Winifred")
false
end
def sex
return "female" if female?
return "object" if (self.thing? || self.group? || self.place?)
"male"
end
def possessive
return "its" if (self.thing? || self.group? || self.place?)
return "her" if self.female?
return "his" if self.male?
"his/her"
end
def uri
"http://openplaques.org" + Rails.application.routes.url_helpers.person_path(self, :format => :json)
end
def to_s
self.name
end
def as_json(options={})
# this example ignores the user's options
super(:only => [],
:include => {
:personal_roles => {
:only => [:started_at, :ended_at],
:include => {
:role => {:only => :name, :methods => :uri},
:related_person => {:only => [], :methods => [:uri, :full_name]}
},
:methods => [:uri, :from, :to]
},
:personal_connections => {
:only => [:started_at, :ended_at],
:include => {
:verb =>{:only => :name},
:location => {:only => :name, :include => {:area => {:only => :name, :methods => :uri, :include => {:country => {:only => [:name, :alpha2]}}}}},
:plaque =>{:only => [], :methods => :uri}
}
}
},
:methods => [:uri, :full_name, :surname, :born_in, :born_at, :died_in, :died_at, :type, :sex, :default_wikipedia_url, :default_dbpedia_uri]
)
end
private
def update_index
self.index = self.name[0,1].downcase
if self.surname_starts_with.blank?
self.surname_starts_with = self.name[self.name.rindex(" ") ? self.name.rindex(" ") + 1 : 0,1].downcase
end
self.surname_starts_with.downcase!
end
end