genealogy 1.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -13
- data/lib/genealogy.rb +4 -2
- data/lib/genealogy/alter_methods.rb +173 -112
- data/lib/genealogy/constants.rb +52 -21
- data/lib/genealogy/current_spouse_methods.rb +66 -0
- data/lib/genealogy/exceptions.rb +5 -8
- data/lib/genealogy/genealogy.rb +100 -63
- data/lib/genealogy/ineligible_methods.rb +116 -0
- data/lib/genealogy/query_methods.rb +194 -236
- data/lib/genealogy/util_methods.rb +180 -0
- data/lib/genealogy/version.rb +1 -1
- metadata +103 -30
- data/lib/genealogy/spouse_methods.rb +0 -53
data/lib/genealogy/constants.rb
CHANGED
|
@@ -1,24 +1,55 @@
|
|
|
1
1
|
module Genealogy
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
module Constants
|
|
3
|
+
# deafault options for has_parent method
|
|
4
|
+
DEFAULTS = {
|
|
5
|
+
column_names: {
|
|
6
|
+
sex: 'sex',
|
|
7
|
+
father_id: 'father_id',
|
|
8
|
+
mother_id: 'mother_id',
|
|
9
|
+
current_spouse_id: 'current_spouse_id',
|
|
10
|
+
birth_date: 'birth_date',
|
|
11
|
+
death_date: 'death_date'
|
|
12
|
+
},
|
|
13
|
+
perform_validation: true,
|
|
14
|
+
ineligibility: :pedigree,
|
|
15
|
+
current_spouse: false,
|
|
16
|
+
sex_values: ['M','F'],
|
|
17
|
+
limit_ages: {
|
|
18
|
+
min_male_procreation_age: 12,
|
|
19
|
+
max_male_procreation_age: 75,
|
|
20
|
+
min_female_procreation_age: 9,
|
|
21
|
+
max_female_procreation_age: 50,
|
|
22
|
+
max_male_life_expectancy: 110,
|
|
23
|
+
max_female_life_expectancy: 110
|
|
24
|
+
}
|
|
25
|
+
}
|
|
7
26
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
:
|
|
14
|
-
:
|
|
15
|
-
:
|
|
16
|
-
:
|
|
17
|
-
:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
# ineligibility levels
|
|
28
|
+
PEDIGREE = 1
|
|
29
|
+
PEDIGREE_AND_DATES = 2
|
|
30
|
+
OFF = 0
|
|
31
|
+
|
|
32
|
+
PARENT2LINEAGE = ActiveSupport::HashWithIndifferentAccess.new({ father: :paternal, mother: :maternal })
|
|
33
|
+
LINEAGE2PARENT = ActiveSupport::HashWithIndifferentAccess.new({ paternal: :father, maternal: :mother })
|
|
34
|
+
PARENT2SEX = ActiveSupport::HashWithIndifferentAccess.new({ father: :male, mother: :female })
|
|
35
|
+
SEX2PARENT = ActiveSupport::HashWithIndifferentAccess.new({ male: :father, female: :mother })
|
|
36
|
+
OPPOSITESEX = ActiveSupport::HashWithIndifferentAccess.new({male: :female, female: :male})
|
|
37
|
+
|
|
38
|
+
AKA = ActiveSupport::HashWithIndifferentAccess.new({
|
|
39
|
+
father: "F",
|
|
40
|
+
mother: "M",
|
|
41
|
+
paternal_grandfather: "PGF",
|
|
42
|
+
paternal_grandmother: "PGM",
|
|
43
|
+
maternal_grandfather: "MGF",
|
|
44
|
+
maternal_grandmother: "MGM",
|
|
45
|
+
children: "C",
|
|
46
|
+
siblings: "S",
|
|
47
|
+
half_siblings: "HS",
|
|
48
|
+
paternal_half_siblings: "PHS",
|
|
49
|
+
maternal_half_siblings: "MHS",
|
|
50
|
+
grandchildren: "GC",
|
|
51
|
+
uncles_and_aunts: "U&A",
|
|
52
|
+
nieces_and_nephews: "N&N"
|
|
53
|
+
})
|
|
54
|
+
end
|
|
24
55
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Genealogy
|
|
2
|
+
# Module CurrentSpouseMethods provides methods to manage and query current spouse. It's included by the genealogy enabled AR model
|
|
3
|
+
module CurrentSpouseMethods
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
include Constants
|
|
7
|
+
|
|
8
|
+
# add current spouse updating receiver and argument individuals foreign_key in a transaction
|
|
9
|
+
# @param [Object] spouse
|
|
10
|
+
# @return [Boolean]
|
|
11
|
+
def add_current_spouse(spouse)
|
|
12
|
+
|
|
13
|
+
raise_unless_current_spouse_enabled
|
|
14
|
+
check_incompatible_relationship(:current_spouse,spouse)
|
|
15
|
+
|
|
16
|
+
if gclass.perform_validation_enabled
|
|
17
|
+
self.current_spouse = spouse
|
|
18
|
+
spouse.current_spouse = self
|
|
19
|
+
transaction do
|
|
20
|
+
spouse.save!
|
|
21
|
+
save!
|
|
22
|
+
end
|
|
23
|
+
else
|
|
24
|
+
transaction do
|
|
25
|
+
self.update_attribute(:current_spouse,spouse)
|
|
26
|
+
spouse.update_attribute(:current_spouse,self)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# remove current spouse resetting receiver and argument individuals foreign_key in a transaction
|
|
33
|
+
# @return [Boolean]
|
|
34
|
+
def remove_current_spouse
|
|
35
|
+
raise_unless_current_spouse_enabled
|
|
36
|
+
if gclass.perform_validation_enabled
|
|
37
|
+
ex_current_spouse = current_spouse
|
|
38
|
+
current_spouse.current_spouse = nil
|
|
39
|
+
self.current_spouse = nil
|
|
40
|
+
transaction do
|
|
41
|
+
ex_current_spouse.save!
|
|
42
|
+
save!
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
transaction do
|
|
46
|
+
current_spouse.update_attribute(:current_spouse,nil)
|
|
47
|
+
self.update_attribute(:current_spouse,nil)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# list of individual who cannot be current spouse
|
|
53
|
+
# @return [Array]
|
|
54
|
+
def ineligible_current_spouses
|
|
55
|
+
raise_unless_current_spouse_enabled
|
|
56
|
+
self.gclass.send(ssex.to_s.pluralize)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def raise_unless_current_spouse_enabled
|
|
62
|
+
raise FeatureNotEnabled, "Spouse tracking not enabled. Enable it with option 'current_spouse_enabled: true' for has_parents method}" unless self.class.current_spouse_enabled
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/genealogy/exceptions.rb
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
module Genealogy
|
|
2
|
-
class
|
|
3
|
-
class
|
|
4
|
-
class
|
|
5
|
-
class
|
|
6
|
-
|
|
7
|
-
class WrongSexException < RuntimeError; end
|
|
8
|
-
class IncompatibleRelationshipException < RuntimeError
|
|
9
|
-
def initialize(msg = "Trying to create a relationship incopatible with the the existing ones")
|
|
2
|
+
class LineageGapException < StandardError; end
|
|
3
|
+
class FeatureNotEnabled < StandardError; end
|
|
4
|
+
class SexError < StandardError; end
|
|
5
|
+
class IncompatibleRelationshipException < StandardError
|
|
6
|
+
def initialize(msg = "Trying to create an incompatible relationship")
|
|
10
7
|
super(msg)
|
|
11
8
|
end
|
|
12
9
|
end
|
data/lib/genealogy/genealogy.rb
CHANGED
|
@@ -1,82 +1,119 @@
|
|
|
1
1
|
module Genealogy
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
module ClassMethods
|
|
6
|
+
|
|
7
|
+
include Genealogy::Constants
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
# gives to ActiveRecord model geneaogy capabilities. Modules UtilMethods QueryMethods IneligibleMethods AlterMethods and SpouseMethods are included
|
|
10
|
+
# @param [Hash] options
|
|
11
|
+
# @option options [Boolean] current_spouse (false) specifies whether to track or not individual's current spouse
|
|
12
|
+
# @option options [Boolean] perform_validation (true) specifies whether to perform validation or not while altering pedigree that is before updating relatives external keys
|
|
13
|
+
# @option options [Boolean, Symbol] ineligibility (:pedigree) specifies ineligibility setting. If `false` ineligibility checks will be disabled and you can assign, as a relative, any individuals you want.
|
|
14
|
+
# This can be dangerous because you can build nosense loop (in terms of pedigree). If pass one of symbols `:pedigree` or `:pedigree_and_dates` ineligibility checks will be enabled.
|
|
15
|
+
# More specifically with `:pedigree` (or `true`) checks will be based on pedigree topography, i.e., ineligible children will include ancestors. With `:pedigree_and_dates` check will also be based on
|
|
16
|
+
# procreation ages (min and max, male and female) and life expectancy (male and female), i.e. an individual born 200 years before is an ineligible mother
|
|
17
|
+
# @option options [Hash] limit_ages (min_male_procreation_age:12, max_male_procreation_age:75, min_female_procreation_age:9, max_female_procreation_age:50, max_male_life_expectancy:110, max_female_life_expectancy:110)
|
|
18
|
+
# specifies one or more limit ages different than defaults
|
|
19
|
+
# @option options [Hash] column_names (sex:'sex', father_id:'father_id', mother_id:'mother_id', current_spouse_id:'current_spouse_id', birth_date:'birth_date', death_date:'death_date') specifies column names to map database individual table
|
|
20
|
+
# @option options [Array] sex_values (['M','F']) specifies values used in database sex column
|
|
21
|
+
# @return [void]
|
|
22
|
+
def has_parents options = {}
|
|
23
|
+
|
|
24
|
+
check_options(options)
|
|
25
|
+
|
|
26
|
+
# keep track of the original extend class to prevent wrong scopes in query method in case of STI
|
|
27
|
+
class_attribute :gclass, instance_writer: false
|
|
28
|
+
self.gclass = self
|
|
29
|
+
|
|
30
|
+
class_attribute :ineligibility_level, instance_accessor: false
|
|
31
|
+
self.ineligibility_level = case options[:ineligibility]
|
|
32
|
+
when :pedigree
|
|
33
|
+
PEDIGREE
|
|
34
|
+
when true
|
|
35
|
+
PEDIGREE
|
|
36
|
+
when :pedigree_and_dates
|
|
37
|
+
PEDIGREE_AND_DATES
|
|
38
|
+
when false
|
|
39
|
+
OFF
|
|
40
|
+
when nil
|
|
41
|
+
PEDIGREE
|
|
42
|
+
else
|
|
43
|
+
raise ArgumentError, "ineligibility option must be one among :pedigree, :pedigree_and_dates or false"
|
|
11
44
|
end
|
|
12
|
-
|
|
13
|
-
|
|
45
|
+
|
|
46
|
+
## limit_ages
|
|
47
|
+
if ineligibility_level >= PEDIGREE_AND_DATES
|
|
48
|
+
DEFAULTS[:limit_ages].each do |age,v|
|
|
49
|
+
class_attribute age, instance_accessor: false
|
|
50
|
+
self.send("#{age}=", options[:limit_ages].try(:[],age) || v)
|
|
51
|
+
end
|
|
14
52
|
end
|
|
15
|
-
end
|
|
16
53
|
|
|
17
|
-
|
|
54
|
+
[:current_spouse, :perform_validation].each do |opt|
|
|
55
|
+
ca = "#{opt}_enabled"
|
|
56
|
+
class_attribute ca, instance_accessor: false
|
|
57
|
+
self.send "#{ca}=", options.key?(opt) ? options[opt] : DEFAULTS[opt]
|
|
58
|
+
end
|
|
18
59
|
|
|
19
|
-
def has_parents options = {}
|
|
20
60
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
61
|
+
# column names class attributes
|
|
62
|
+
DEFAULTS[:column_names].merge(options[:column_names]).each do |k,v|
|
|
63
|
+
class_attribute_name = "#{k}_column"
|
|
64
|
+
class_attribute class_attribute_name, instance_accessor: false
|
|
65
|
+
self.send("#{class_attribute_name}=", v)
|
|
66
|
+
alias_attribute k, v unless k == v.to_sym
|
|
25
67
|
end
|
|
26
|
-
end
|
|
27
68
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
## sex
|
|
38
|
-
# class attributes
|
|
39
|
-
class_attribute :sex_column, :sex_values, :sex_male_value, :sex_female_value, :birth_date_column, :death_date_column
|
|
40
|
-
self.sex_column = options[:sex_column] || 'sex'
|
|
41
|
-
self.sex_values = options[:sex_values] || ['M','F']
|
|
42
|
-
self.sex_male_value = self.sex_values.first
|
|
43
|
-
self.sex_female_value = self.sex_values.last
|
|
44
|
-
self.birth_date_column = options[:birth_date_column] || 'birth_date'
|
|
45
|
-
self.death_date_column = options[:death_date_column] || 'death_date'
|
|
46
|
-
# instance attribute
|
|
47
|
-
alias_attribute :sex, sex_column if self.sex_column != 'sex'
|
|
48
|
-
# validation
|
|
49
|
-
validates_presence_of sex_column
|
|
50
|
-
validates_format_of sex_column, :with => /[#{sex_values.join}]/
|
|
51
|
-
|
|
52
|
-
tracked_relatives.each do |key|
|
|
53
|
-
# class attribute where is stored the correspondig foreign_key column name
|
|
54
|
-
class_attribute_name = "#{key}_column"
|
|
55
|
-
foreign_key = "#{key}_id"
|
|
56
|
-
class_attribute class_attribute_name
|
|
57
|
-
self.send("#{class_attribute_name}=", options[class_attribute_name.to_sym] || foreign_key)
|
|
58
|
-
# self join associations
|
|
59
|
-
belongs_to key, class_name: self, foreign_key: foreign_key
|
|
60
|
-
end
|
|
69
|
+
## sex
|
|
70
|
+
class_attribute :sex_values, :sex_male_value, :sex_female_value, instance_accessor: false
|
|
71
|
+
self.sex_values = options[:sex_values] || DEFAULTS[:sex_values]
|
|
72
|
+
self.sex_male_value = self.sex_values.first
|
|
73
|
+
self.sex_female_value = self.sex_values.last
|
|
74
|
+
|
|
75
|
+
# validation
|
|
76
|
+
validates_presence_of :sex
|
|
77
|
+
validates_format_of :sex, with: /[#{sex_values.join}]/
|
|
61
78
|
|
|
62
|
-
|
|
63
|
-
|
|
79
|
+
tracked_relatives = [:father, :mother]
|
|
80
|
+
tracked_relatives << :current_spouse if current_spouse_enabled
|
|
81
|
+
tracked_relatives.each do |k|
|
|
82
|
+
belongs_to k, class_name: self, foreign_key: self.send("#{k}_id_column")
|
|
83
|
+
end
|
|
64
84
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
include Genealogy::AlterMethods
|
|
68
|
-
include Genealogy::SpouseMethods if current_spouse_enabled
|
|
85
|
+
has_many :children_as_father, class_name: self, foreign_key: self.father_id_column, dependent: :nullify
|
|
86
|
+
has_many :children_as_mother, class_name: self, foreign_key: self.mother_id_column, dependent: :nullify
|
|
69
87
|
|
|
70
|
-
|
|
88
|
+
include Genealogy::UtilMethods
|
|
89
|
+
include Genealogy::QueryMethods
|
|
90
|
+
include Genealogy::IneligibleMethods
|
|
91
|
+
include Genealogy::AlterMethods
|
|
92
|
+
include Genealogy::CurrentSpouseMethods
|
|
71
93
|
|
|
72
|
-
module MotherAssociationExtension
|
|
73
|
-
def with(father_id)
|
|
74
|
-
where(father_id: father_id)
|
|
75
94
|
end
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def check_options(options)
|
|
99
|
+
|
|
100
|
+
raise ArgumentError, "Hash expected, #{options.class} given." unless options.is_a? Hash
|
|
101
|
+
|
|
102
|
+
# column names
|
|
103
|
+
options[:column_names] ||= {}
|
|
104
|
+
raise ArgumentError, "Hash expected for :column_names option, #{options[:column_names].class} given." unless options[:column_names].is_a? Hash
|
|
105
|
+
|
|
106
|
+
# sex
|
|
107
|
+
if array = options[:sex_values]
|
|
108
|
+
raise ArgumentError, ":sex_values option must be an array of length 2: [:male_value, :female_value]" unless array.is_a?(Array) and array.size == 2
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# booleans
|
|
112
|
+
options.slice(:perform_validation, :current_spouse).each do |k,v|
|
|
113
|
+
raise ArgumentError, "Boolean expected for #{k} option, #{v.class} given." unless !!v == v
|
|
114
|
+
end
|
|
80
115
|
end
|
|
116
|
+
|
|
81
117
|
end
|
|
118
|
+
|
|
82
119
|
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
module Genealogy
|
|
2
|
+
# Module IneligibleMethods provides methods to run genealogy queries to retrive groups of individuals who cannot be relatives according to provided role.
|
|
3
|
+
# It's included by the genealogy enabled AR model
|
|
4
|
+
module IneligibleMethods
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
include Constants
|
|
8
|
+
|
|
9
|
+
# @!macro [attach] generate
|
|
10
|
+
# @method ineligible_$1s
|
|
11
|
+
# list of individual who cannot be $1
|
|
12
|
+
# @return [Array,NilClass] Return nil if $1 already assigned
|
|
13
|
+
def self.generate_method_ineligibles_parent(parent)
|
|
14
|
+
define_method "ineligible_#{parent}s" do
|
|
15
|
+
unless self.send(parent)
|
|
16
|
+
ineligibles = []
|
|
17
|
+
ineligibles |= descendants | [self] | gclass.send("#{OPPOSITESEX[PARENT2SEX[parent]]}s") if gclass.ineligibility_level >= PEDIGREE
|
|
18
|
+
if gclass.ineligibility_level >= PEDIGREE_AND_DATES and life_range
|
|
19
|
+
ineligibles |= (gclass.all - ineligibles).find_all do |indiv|
|
|
20
|
+
if indiv.life_range
|
|
21
|
+
if birth
|
|
22
|
+
!indiv.can_procreate_on?(birth)
|
|
23
|
+
else
|
|
24
|
+
!indiv.can_procreate_during?(life_range)
|
|
25
|
+
end
|
|
26
|
+
else
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
ineligibles
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
generate_method_ineligibles_parent(:father)
|
|
36
|
+
generate_method_ineligibles_parent(:mother)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# @!macro [attach] generate
|
|
40
|
+
# @method ineligible_$1_grand$2(grandparent)
|
|
41
|
+
# list of individual who cannot be $1 grand$2
|
|
42
|
+
# @return [Array,NilClass] Return nil if $$1 grand$2 already assigned
|
|
43
|
+
def self.generate_method_ineligible_grandparent(lineage,grandparent)
|
|
44
|
+
relationship = "#{lineage}_grand#{grandparent}"
|
|
45
|
+
define_method "ineligible_#{relationship}s" do
|
|
46
|
+
unless send(relationship)
|
|
47
|
+
parent = send(LINEAGE2PARENT[lineage])
|
|
48
|
+
ineligibles = []
|
|
49
|
+
ineligibles |= [parent].compact | descendants | siblings | send("#{lineage}_half_siblings") | [self] | gclass.send("#{OPPOSITESEX[PARENT2SEX[grandparent]]}s") if gclass.ineligibility_level >= PEDIGREE
|
|
50
|
+
if gclass.ineligibility_level >= PEDIGREE_AND_DATES
|
|
51
|
+
ineligibles |= if parent
|
|
52
|
+
parent.send("ineligible_#{grandparent}s")
|
|
53
|
+
else
|
|
54
|
+
(gclass.all - ineligibles).find_all do |indiv|
|
|
55
|
+
if indiv.life_range
|
|
56
|
+
!indiv.can_procreate_during?(send("#{LINEAGE2PARENT[lineage]}_birth_range"))
|
|
57
|
+
else
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
ineligibles
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
generate_method_ineligible_grandparent(:paternal,:father)
|
|
69
|
+
generate_method_ineligible_grandparent(:paternal,:mother)
|
|
70
|
+
generate_method_ineligible_grandparent(:maternal,:father)
|
|
71
|
+
generate_method_ineligible_grandparent(:maternal,:mother)
|
|
72
|
+
|
|
73
|
+
# list of individual who cannot be children: ancestors, children, full siblings, theirself and all individuals with father or mother according to self's sex
|
|
74
|
+
# @return [Array]
|
|
75
|
+
def ineligible_children
|
|
76
|
+
ineligibles = []
|
|
77
|
+
ineligibles |= ancestors | children | siblings | [self] | gclass.all_with(SEX2PARENT[ssex]) if gclass.ineligibility_level >= PEDIGREE
|
|
78
|
+
if gclass.ineligibility_level >= PEDIGREE_AND_DATES and fertility_range
|
|
79
|
+
ineligibles |= (gclass.all - ineligibles).find_all{ |indiv| !can_procreate_during?(indiv.birth_range)}
|
|
80
|
+
end
|
|
81
|
+
ineligibles
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# list of individual who cannot be full siblings: ancestors, descendants, siblings, theirself and all individuals with different father or mother
|
|
85
|
+
# @return [Array]
|
|
86
|
+
def ineligible_siblings
|
|
87
|
+
ineligibles = []
|
|
88
|
+
if gclass.ineligibility_level >= PEDIGREE
|
|
89
|
+
ineligibles |= ancestors | descendants | siblings | [self]
|
|
90
|
+
ineligibles |= (father ? gclass.all_with(:father).where.not(father_id: father) : [])
|
|
91
|
+
ineligibles |= (mother ? gclass.all_with(:mother).where.not(mother_id: mother) : [])
|
|
92
|
+
end
|
|
93
|
+
if gclass.ineligibility_level >= PEDIGREE_AND_DATES
|
|
94
|
+
# if a parent is present ineligible siblings are parent's ineligible children, otherwise try to estimate parent birth range.
|
|
95
|
+
# If it's possible ineligible siblings are all individuals whose life range overlaps parent birth range
|
|
96
|
+
[:father,:mother].each do |parent|
|
|
97
|
+
if p = send(parent)
|
|
98
|
+
ineligibles |= p.ineligible_children
|
|
99
|
+
elsif parent_fertility_range = send("#{parent}_fertility_range")
|
|
100
|
+
remainings = gclass.all - ineligibles
|
|
101
|
+
ineligibles |= remainings.find_all do |indiv|
|
|
102
|
+
if ibr = indiv.birth_range
|
|
103
|
+
!parent_fertility_range.overlaps? ibr
|
|
104
|
+
else
|
|
105
|
+
false
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
else
|
|
111
|
+
end
|
|
112
|
+
ineligibles
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
end
|