Class: TypeMaterial
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- TypeMaterial
- Includes:
- Housekeeping, Shared::Citations, Shared::Confidences, Shared::DataAttributes, Shared::DwcOccurrenceHooks, Shared::IsData, Shared::Notes, Shared::Tags, SoftValidation
- Defined in:
- app/models/type_material.rb
Overview
TypeMaterial links CollectionObjects to Protonyms. It is the single direct relationship between nomenclature (TaxonName) and CollectionObject in TaxonWorks. Other name-collection object relationships coming through TaxonDeterminations, i.e. linking an OTU to a object. TypeMaterial is used to encode specific rules of nomenclature, therefor it only includes those types (e.g. “holotype”) that are specifically goverened, for example “topotype” is not allowed.
Constant Summary collapse
- ICZN_TYPES =
Keys are valid values for type_type, values are required Class for BiologicalCollectionObject
{ 'holotype' => Specimen, 'paratype' => Specimen, 'paralectotype' => Specimen, 'neotype' => Specimen, 'lectotype' => Specimen, 'syntype' => Specimen, 'paratypes' => Lot, 'syntypes' => Lot, 'paralectotypes' => Lot }.freeze
- ICN_TYPES =
{ 'holotype' => Specimen, 'paratype' => Specimen, 'lectotype' => Specimen, 'neotype' => Specimen, 'epitype' => Specimen, 'isotype' => Specimen, 'syntype' => Specimen, 'isosyntype' => Specimen, 'syntypes' => Lot, 'isotypes' => Lot, 'isosyntypes' => Lot }.freeze
Constants included from SoftValidation
SoftValidation::ANCESTORS_WITH_SOFT_VALIDATIONS
Instance Attribute Summary collapse
-
#collection_object_id ⇒ Integer
The CollectionObject.
-
#position ⇒ Integer
Sort column.
-
#project_id ⇒ Integer
the project ID.
-
#protonym_id ⇒ Integer
The protonym in question.
-
#type_type ⇒ String
The type of Type relationship (e.g. holotype).
Class Method Summary collapse
Instance Method Summary collapse
- #check_protonym_rank ⇒ Object protected
- #check_type_type ⇒ Object protected
- #dwc_occurrences ⇒ Object
- #sv_single_primary_type ⇒ Object protected
- #sv_type_source ⇒ Object protected
-
#type_source ⇒ Object
TODO: really should be validating uniqueness at this point, it’s type material, not garbage records.
Methods included from SoftValidation
#clear_soft_validations, #fix_for, #fix_soft_validations, #soft_fixed?, #soft_valid?, #soft_validate, #soft_validated?, #soft_validations, #soft_validators
Methods included from Shared::IsData
#errors_excepting, #full_error_messages_excepting, #identical, #is_community?, #is_destroyable?, #is_editable?, #is_in_use?, #is_in_users_projects?, #metamorphosize, #similar
Methods included from Shared::Confidences
Methods included from Shared::Tags
#reject_tags, #tag_with, #tagged?, #tagged_with?
Methods included from Shared::Notes
#concatenated_notes_string, #reject_notes
Methods included from Shared::DataAttributes
#import_attributes, #internal_attributes, #keyword_value_hash, #reject_data_attributes
Methods included from Shared::Citations
#cited?, #mark_citations_for_destruction, #nomenclature_date, #origin_citation_source_id, #reject_citations, #requires_citation?, #sources_by_topic_id
Methods included from Housekeeping
#has_polymorphic_relationship?
Methods inherited from ApplicationRecord
Instance Attribute Details
#collection_object_id ⇒ Integer
Returns the CollectionObject.
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 |
# File 'app/models/type_material.rb', line 24 class TypeMaterial < ApplicationRecord include Housekeeping include Shared::Citations include Shared::DataAttributes include Shared::Notes include Shared::Tags include Shared::Confidences include Shared::DwcOccurrenceHooks include Shared::IsData include SoftValidation # Keys are valid values for type_type, values are # required Class for BiologicalCollectionObject ICZN_TYPES = { 'holotype' => Specimen, 'paratype' => Specimen, 'paralectotype' => Specimen, 'neotype' => Specimen, 'lectotype' => Specimen, 'syntype' => Specimen, 'paratypes' => Lot, 'syntypes' => Lot, 'paralectotypes' => Lot }.freeze ICN_TYPES = { 'holotype' => Specimen, 'paratype' => Specimen, 'lectotype' => Specimen, 'neotype' => Specimen, 'epitype' => Specimen, 'isotype' => Specimen, 'syntype' => Specimen, 'isosyntype' => Specimen, 'syntypes' => Lot, 'isotypes' => Lot, 'isosyntypes' => Lot }.freeze belongs_to :collection_object, class_name: 'CollectionObject', inverse_of: :type_materials belongs_to :protonym, inverse_of: :type_materials has_many :otus, through: :protonym, inverse_of: :type_materials scope :where_protonym, -> (taxon_name) { where(protonym_id: taxon_name) } scope :with_type_string, -> (base_string) { where('type_type LIKE ?', "#{base_string}" ) } scope :with_type_array, -> (base_array) { where('type_type IN (?)', base_array ) } scope :primary, -> {where(type_type: %w{neotype lectotype holotype}).order('collection_object_id')} scope :syntypes, -> {where(type_type: %w{syntype syntypes}).order('collection_object_id')} scope :primary_with_protonym_array, -> (base_array) {select('type_type, collection_object_id').group('type_type, collection_object_id').where("type_materials.type_type IN ('neotype', 'lectotype', 'holotype', 'syntype', 'syntypes') AND type_materials.protonym_id IN (?)", base_array ) } validate :check_type_type validate :check_protonym_rank validates_uniqueness_of :type_type, scope: [:protonym_id, :collection_object_id] soft_validate(:sv_single_primary_type, set: :single_primary_type) soft_validate(:sv_type_source, set: :type_source) accepts_nested_attributes_for :collection_object, allow_destroy: true validates_presence_of :type_type, :protonym, :collection_object # TODO: really should be validating uniqueness at this point, it's type material, not garbage records def type_source [source, protonym.try(:source), nil].compact.first end def self.legal_type_type(code, type_type) case code when :iczn ICZN_TYPES.keys.include?(type_type) when :icn ICN_TYPES.keys.include?(type_type) else false end end def dwc_occurrences return DwcOccurrence.none unless collection_object.present? DwcOccurrence.where( dwc_occurrence_object_type: 'CollectionObject', dwc_occurrence_object_id: id ).distinct end protected def check_type_type if protonym code = protonym.rank_class.nomenclatural_code errors.add(:type_type, 'Not a legal type for the nomenclatural code provided') unless TypeMaterial::legal_type_type(code, type_type) end end def check_protonym_rank errors.add(:protonym_id, 'Type cannot be designated, name is not a species group name') if protonym && !protonym.is_species_rank? end def sv_single_primary_type primary_types = TypeMaterial.with_type_array(['holotype', 'neotype', 'lectotype']).where_protonym(protonym).not_self(self) syntypes = TypeMaterial.with_type_array(['syntype', 'syntypes']).where_protonym(protonym) if type_type =~ /syntype/ soft_validations.add(:type_type, 'Other primary types selected for the taxon are conflicting with the syntypes') unless primary_types.empty? end if ['holotype', 'neotype', 'lectotype'].include?(type_type) soft_validations.add(:type_type, 'More than one primary type associated with the taxon') if !primary_types.empty? || !syntypes.empty? end end def sv_type_source soft_validations.add(:base, 'Source is not selected neither for type nor for taxon') unless type_source if %w(paralectotype neotype lectotype paralectotypes).include?(type_type) if source.nil? soft_validations.add(:base, "Source for #{type_type} designation is not selected ") if source.nil? elsif !protonym.try(:source).nil? && source.cached_nomenclature_date && protonym.cached_nomenclature_date soft_validations.add(:base, "#{type_type.capitalize} could not be designated in the original publication") if source == protonym.source soft_validations.add(:base, "#{type_type.capitalize} could not be designated before taxon description") if source.cached_nomenclature_date&.to_date < protonym.cached_nomenclature_date end end end end |
#position ⇒ Integer
Returns sort column.
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 |
# File 'app/models/type_material.rb', line 24 class TypeMaterial < ApplicationRecord include Housekeeping include Shared::Citations include Shared::DataAttributes include Shared::Notes include Shared::Tags include Shared::Confidences include Shared::DwcOccurrenceHooks include Shared::IsData include SoftValidation # Keys are valid values for type_type, values are # required Class for BiologicalCollectionObject ICZN_TYPES = { 'holotype' => Specimen, 'paratype' => Specimen, 'paralectotype' => Specimen, 'neotype' => Specimen, 'lectotype' => Specimen, 'syntype' => Specimen, 'paratypes' => Lot, 'syntypes' => Lot, 'paralectotypes' => Lot }.freeze ICN_TYPES = { 'holotype' => Specimen, 'paratype' => Specimen, 'lectotype' => Specimen, 'neotype' => Specimen, 'epitype' => Specimen, 'isotype' => Specimen, 'syntype' => Specimen, 'isosyntype' => Specimen, 'syntypes' => Lot, 'isotypes' => Lot, 'isosyntypes' => Lot }.freeze belongs_to :collection_object, class_name: 'CollectionObject', inverse_of: :type_materials belongs_to :protonym, inverse_of: :type_materials has_many :otus, through: :protonym, inverse_of: :type_materials scope :where_protonym, -> (taxon_name) { where(protonym_id: taxon_name) } scope :with_type_string, -> (base_string) { where('type_type LIKE ?', "#{base_string}" ) } scope :with_type_array, -> (base_array) { where('type_type IN (?)', base_array ) } scope :primary, -> {where(type_type: %w{neotype lectotype holotype}).order('collection_object_id')} scope :syntypes, -> {where(type_type: %w{syntype syntypes}).order('collection_object_id')} scope :primary_with_protonym_array, -> (base_array) {select('type_type, collection_object_id').group('type_type, collection_object_id').where("type_materials.type_type IN ('neotype', 'lectotype', 'holotype', 'syntype', 'syntypes') AND type_materials.protonym_id IN (?)", base_array ) } validate :check_type_type validate :check_protonym_rank validates_uniqueness_of :type_type, scope: [:protonym_id, :collection_object_id] soft_validate(:sv_single_primary_type, set: :single_primary_type) soft_validate(:sv_type_source, set: :type_source) accepts_nested_attributes_for :collection_object, allow_destroy: true validates_presence_of :type_type, :protonym, :collection_object # TODO: really should be validating uniqueness at this point, it's type material, not garbage records def type_source [source, protonym.try(:source), nil].compact.first end def self.legal_type_type(code, type_type) case code when :iczn ICZN_TYPES.keys.include?(type_type) when :icn ICN_TYPES.keys.include?(type_type) else false end end def dwc_occurrences return DwcOccurrence.none unless collection_object.present? DwcOccurrence.where( dwc_occurrence_object_type: 'CollectionObject', dwc_occurrence_object_id: id ).distinct end protected def check_type_type if protonym code = protonym.rank_class.nomenclatural_code errors.add(:type_type, 'Not a legal type for the nomenclatural code provided') unless TypeMaterial::legal_type_type(code, type_type) end end def check_protonym_rank errors.add(:protonym_id, 'Type cannot be designated, name is not a species group name') if protonym && !protonym.is_species_rank? end def sv_single_primary_type primary_types = TypeMaterial.with_type_array(['holotype', 'neotype', 'lectotype']).where_protonym(protonym).not_self(self) syntypes = TypeMaterial.with_type_array(['syntype', 'syntypes']).where_protonym(protonym) if type_type =~ /syntype/ soft_validations.add(:type_type, 'Other primary types selected for the taxon are conflicting with the syntypes') unless primary_types.empty? end if ['holotype', 'neotype', 'lectotype'].include?(type_type) soft_validations.add(:type_type, 'More than one primary type associated with the taxon') if !primary_types.empty? || !syntypes.empty? end end def sv_type_source soft_validations.add(:base, 'Source is not selected neither for type nor for taxon') unless type_source if %w(paralectotype neotype lectotype paralectotypes).include?(type_type) if source.nil? soft_validations.add(:base, "Source for #{type_type} designation is not selected ") if source.nil? elsif !protonym.try(:source).nil? && source.cached_nomenclature_date && protonym.cached_nomenclature_date soft_validations.add(:base, "#{type_type.capitalize} could not be designated in the original publication") if source == protonym.source soft_validations.add(:base, "#{type_type.capitalize} could not be designated before taxon description") if source.cached_nomenclature_date&.to_date < protonym.cached_nomenclature_date end end end end |
#project_id ⇒ Integer
the project ID
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 |
# File 'app/models/type_material.rb', line 24 class TypeMaterial < ApplicationRecord include Housekeeping include Shared::Citations include Shared::DataAttributes include Shared::Notes include Shared::Tags include Shared::Confidences include Shared::DwcOccurrenceHooks include Shared::IsData include SoftValidation # Keys are valid values for type_type, values are # required Class for BiologicalCollectionObject ICZN_TYPES = { 'holotype' => Specimen, 'paratype' => Specimen, 'paralectotype' => Specimen, 'neotype' => Specimen, 'lectotype' => Specimen, 'syntype' => Specimen, 'paratypes' => Lot, 'syntypes' => Lot, 'paralectotypes' => Lot }.freeze ICN_TYPES = { 'holotype' => Specimen, 'paratype' => Specimen, 'lectotype' => Specimen, 'neotype' => Specimen, 'epitype' => Specimen, 'isotype' => Specimen, 'syntype' => Specimen, 'isosyntype' => Specimen, 'syntypes' => Lot, 'isotypes' => Lot, 'isosyntypes' => Lot }.freeze belongs_to :collection_object, class_name: 'CollectionObject', inverse_of: :type_materials belongs_to :protonym, inverse_of: :type_materials has_many :otus, through: :protonym, inverse_of: :type_materials scope :where_protonym, -> (taxon_name) { where(protonym_id: taxon_name) } scope :with_type_string, -> (base_string) { where('type_type LIKE ?', "#{base_string}" ) } scope :with_type_array, -> (base_array) { where('type_type IN (?)', base_array ) } scope :primary, -> {where(type_type: %w{neotype lectotype holotype}).order('collection_object_id')} scope :syntypes, -> {where(type_type: %w{syntype syntypes}).order('collection_object_id')} scope :primary_with_protonym_array, -> (base_array) {select('type_type, collection_object_id').group('type_type, collection_object_id').where("type_materials.type_type IN ('neotype', 'lectotype', 'holotype', 'syntype', 'syntypes') AND type_materials.protonym_id IN (?)", base_array ) } validate :check_type_type validate :check_protonym_rank validates_uniqueness_of :type_type, scope: [:protonym_id, :collection_object_id] soft_validate(:sv_single_primary_type, set: :single_primary_type) soft_validate(:sv_type_source, set: :type_source) accepts_nested_attributes_for :collection_object, allow_destroy: true validates_presence_of :type_type, :protonym, :collection_object # TODO: really should be validating uniqueness at this point, it's type material, not garbage records def type_source [source, protonym.try(:source), nil].compact.first end def self.legal_type_type(code, type_type) case code when :iczn ICZN_TYPES.keys.include?(type_type) when :icn ICN_TYPES.keys.include?(type_type) else false end end def dwc_occurrences return DwcOccurrence.none unless collection_object.present? DwcOccurrence.where( dwc_occurrence_object_type: 'CollectionObject', dwc_occurrence_object_id: id ).distinct end protected def check_type_type if protonym code = protonym.rank_class.nomenclatural_code errors.add(:type_type, 'Not a legal type for the nomenclatural code provided') unless TypeMaterial::legal_type_type(code, type_type) end end def check_protonym_rank errors.add(:protonym_id, 'Type cannot be designated, name is not a species group name') if protonym && !protonym.is_species_rank? end def sv_single_primary_type primary_types = TypeMaterial.with_type_array(['holotype', 'neotype', 'lectotype']).where_protonym(protonym).not_self(self) syntypes = TypeMaterial.with_type_array(['syntype', 'syntypes']).where_protonym(protonym) if type_type =~ /syntype/ soft_validations.add(:type_type, 'Other primary types selected for the taxon are conflicting with the syntypes') unless primary_types.empty? end if ['holotype', 'neotype', 'lectotype'].include?(type_type) soft_validations.add(:type_type, 'More than one primary type associated with the taxon') if !primary_types.empty? || !syntypes.empty? end end def sv_type_source soft_validations.add(:base, 'Source is not selected neither for type nor for taxon') unless type_source if %w(paralectotype neotype lectotype paralectotypes).include?(type_type) if source.nil? soft_validations.add(:base, "Source for #{type_type} designation is not selected ") if source.nil? elsif !protonym.try(:source).nil? && source.cached_nomenclature_date && protonym.cached_nomenclature_date soft_validations.add(:base, "#{type_type.capitalize} could not be designated in the original publication") if source == protonym.source soft_validations.add(:base, "#{type_type.capitalize} could not be designated before taxon description") if source.cached_nomenclature_date&.to_date < protonym.cached_nomenclature_date end end end end |
#protonym_id ⇒ Integer
Returns the protonym in question.
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 |
# File 'app/models/type_material.rb', line 24 class TypeMaterial < ApplicationRecord include Housekeeping include Shared::Citations include Shared::DataAttributes include Shared::Notes include Shared::Tags include Shared::Confidences include Shared::DwcOccurrenceHooks include Shared::IsData include SoftValidation # Keys are valid values for type_type, values are # required Class for BiologicalCollectionObject ICZN_TYPES = { 'holotype' => Specimen, 'paratype' => Specimen, 'paralectotype' => Specimen, 'neotype' => Specimen, 'lectotype' => Specimen, 'syntype' => Specimen, 'paratypes' => Lot, 'syntypes' => Lot, 'paralectotypes' => Lot }.freeze ICN_TYPES = { 'holotype' => Specimen, 'paratype' => Specimen, 'lectotype' => Specimen, 'neotype' => Specimen, 'epitype' => Specimen, 'isotype' => Specimen, 'syntype' => Specimen, 'isosyntype' => Specimen, 'syntypes' => Lot, 'isotypes' => Lot, 'isosyntypes' => Lot }.freeze belongs_to :collection_object, class_name: 'CollectionObject', inverse_of: :type_materials belongs_to :protonym, inverse_of: :type_materials has_many :otus, through: :protonym, inverse_of: :type_materials scope :where_protonym, -> (taxon_name) { where(protonym_id: taxon_name) } scope :with_type_string, -> (base_string) { where('type_type LIKE ?', "#{base_string}" ) } scope :with_type_array, -> (base_array) { where('type_type IN (?)', base_array ) } scope :primary, -> {where(type_type: %w{neotype lectotype holotype}).order('collection_object_id')} scope :syntypes, -> {where(type_type: %w{syntype syntypes}).order('collection_object_id')} scope :primary_with_protonym_array, -> (base_array) {select('type_type, collection_object_id').group('type_type, collection_object_id').where("type_materials.type_type IN ('neotype', 'lectotype', 'holotype', 'syntype', 'syntypes') AND type_materials.protonym_id IN (?)", base_array ) } validate :check_type_type validate :check_protonym_rank validates_uniqueness_of :type_type, scope: [:protonym_id, :collection_object_id] soft_validate(:sv_single_primary_type, set: :single_primary_type) soft_validate(:sv_type_source, set: :type_source) accepts_nested_attributes_for :collection_object, allow_destroy: true validates_presence_of :type_type, :protonym, :collection_object # TODO: really should be validating uniqueness at this point, it's type material, not garbage records def type_source [source, protonym.try(:source), nil].compact.first end def self.legal_type_type(code, type_type) case code when :iczn ICZN_TYPES.keys.include?(type_type) when :icn ICN_TYPES.keys.include?(type_type) else false end end def dwc_occurrences return DwcOccurrence.none unless collection_object.present? DwcOccurrence.where( dwc_occurrence_object_type: 'CollectionObject', dwc_occurrence_object_id: id ).distinct end protected def check_type_type if protonym code = protonym.rank_class.nomenclatural_code errors.add(:type_type, 'Not a legal type for the nomenclatural code provided') unless TypeMaterial::legal_type_type(code, type_type) end end def check_protonym_rank errors.add(:protonym_id, 'Type cannot be designated, name is not a species group name') if protonym && !protonym.is_species_rank? end def sv_single_primary_type primary_types = TypeMaterial.with_type_array(['holotype', 'neotype', 'lectotype']).where_protonym(protonym).not_self(self) syntypes = TypeMaterial.with_type_array(['syntype', 'syntypes']).where_protonym(protonym) if type_type =~ /syntype/ soft_validations.add(:type_type, 'Other primary types selected for the taxon are conflicting with the syntypes') unless primary_types.empty? end if ['holotype', 'neotype', 'lectotype'].include?(type_type) soft_validations.add(:type_type, 'More than one primary type associated with the taxon') if !primary_types.empty? || !syntypes.empty? end end def sv_type_source soft_validations.add(:base, 'Source is not selected neither for type nor for taxon') unless type_source if %w(paralectotype neotype lectotype paralectotypes).include?(type_type) if source.nil? soft_validations.add(:base, "Source for #{type_type} designation is not selected ") if source.nil? elsif !protonym.try(:source).nil? && source.cached_nomenclature_date && protonym.cached_nomenclature_date soft_validations.add(:base, "#{type_type.capitalize} could not be designated in the original publication") if source == protonym.source soft_validations.add(:base, "#{type_type.capitalize} could not be designated before taxon description") if source.cached_nomenclature_date&.to_date < protonym.cached_nomenclature_date end end end end |
#type_type ⇒ String
Returns the type of Type relationship (e.g. holotype).
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 |
# File 'app/models/type_material.rb', line 24 class TypeMaterial < ApplicationRecord include Housekeeping include Shared::Citations include Shared::DataAttributes include Shared::Notes include Shared::Tags include Shared::Confidences include Shared::DwcOccurrenceHooks include Shared::IsData include SoftValidation # Keys are valid values for type_type, values are # required Class for BiologicalCollectionObject ICZN_TYPES = { 'holotype' => Specimen, 'paratype' => Specimen, 'paralectotype' => Specimen, 'neotype' => Specimen, 'lectotype' => Specimen, 'syntype' => Specimen, 'paratypes' => Lot, 'syntypes' => Lot, 'paralectotypes' => Lot }.freeze ICN_TYPES = { 'holotype' => Specimen, 'paratype' => Specimen, 'lectotype' => Specimen, 'neotype' => Specimen, 'epitype' => Specimen, 'isotype' => Specimen, 'syntype' => Specimen, 'isosyntype' => Specimen, 'syntypes' => Lot, 'isotypes' => Lot, 'isosyntypes' => Lot }.freeze belongs_to :collection_object, class_name: 'CollectionObject', inverse_of: :type_materials belongs_to :protonym, inverse_of: :type_materials has_many :otus, through: :protonym, inverse_of: :type_materials scope :where_protonym, -> (taxon_name) { where(protonym_id: taxon_name) } scope :with_type_string, -> (base_string) { where('type_type LIKE ?', "#{base_string}" ) } scope :with_type_array, -> (base_array) { where('type_type IN (?)', base_array ) } scope :primary, -> {where(type_type: %w{neotype lectotype holotype}).order('collection_object_id')} scope :syntypes, -> {where(type_type: %w{syntype syntypes}).order('collection_object_id')} scope :primary_with_protonym_array, -> (base_array) {select('type_type, collection_object_id').group('type_type, collection_object_id').where("type_materials.type_type IN ('neotype', 'lectotype', 'holotype', 'syntype', 'syntypes') AND type_materials.protonym_id IN (?)", base_array ) } validate :check_type_type validate :check_protonym_rank validates_uniqueness_of :type_type, scope: [:protonym_id, :collection_object_id] soft_validate(:sv_single_primary_type, set: :single_primary_type) soft_validate(:sv_type_source, set: :type_source) accepts_nested_attributes_for :collection_object, allow_destroy: true validates_presence_of :type_type, :protonym, :collection_object # TODO: really should be validating uniqueness at this point, it's type material, not garbage records def type_source [source, protonym.try(:source), nil].compact.first end def self.legal_type_type(code, type_type) case code when :iczn ICZN_TYPES.keys.include?(type_type) when :icn ICN_TYPES.keys.include?(type_type) else false end end def dwc_occurrences return DwcOccurrence.none unless collection_object.present? DwcOccurrence.where( dwc_occurrence_object_type: 'CollectionObject', dwc_occurrence_object_id: id ).distinct end protected def check_type_type if protonym code = protonym.rank_class.nomenclatural_code errors.add(:type_type, 'Not a legal type for the nomenclatural code provided') unless TypeMaterial::legal_type_type(code, type_type) end end def check_protonym_rank errors.add(:protonym_id, 'Type cannot be designated, name is not a species group name') if protonym && !protonym.is_species_rank? end def sv_single_primary_type primary_types = TypeMaterial.with_type_array(['holotype', 'neotype', 'lectotype']).where_protonym(protonym).not_self(self) syntypes = TypeMaterial.with_type_array(['syntype', 'syntypes']).where_protonym(protonym) if type_type =~ /syntype/ soft_validations.add(:type_type, 'Other primary types selected for the taxon are conflicting with the syntypes') unless primary_types.empty? end if ['holotype', 'neotype', 'lectotype'].include?(type_type) soft_validations.add(:type_type, 'More than one primary type associated with the taxon') if !primary_types.empty? || !syntypes.empty? end end def sv_type_source soft_validations.add(:base, 'Source is not selected neither for type nor for taxon') unless type_source if %w(paralectotype neotype lectotype paralectotypes).include?(type_type) if source.nil? soft_validations.add(:base, "Source for #{type_type} designation is not selected ") if source.nil? elsif !protonym.try(:source).nil? && source.cached_nomenclature_date && protonym.cached_nomenclature_date soft_validations.add(:base, "#{type_type.capitalize} could not be designated in the original publication") if source == protonym.source soft_validations.add(:base, "#{type_type.capitalize} could not be designated before taxon description") if source.cached_nomenclature_date&.to_date < protonym.cached_nomenclature_date end end end end |
Class Method Details
.legal_type_type(code, type_type) ⇒ Object
93 94 95 96 97 98 99 100 101 102 |
# File 'app/models/type_material.rb', line 93 def self.legal_type_type(code, type_type) case code when :iczn ICZN_TYPES.keys.include?(type_type) when :icn ICN_TYPES.keys.include?(type_type) else false end end |
Instance Method Details
#check_protonym_rank ⇒ Object (protected)
121 122 123 |
# File 'app/models/type_material.rb', line 121 def check_protonym_rank errors.add(:protonym_id, 'Type cannot be designated, name is not a species group name') if protonym && !protonym.is_species_rank? end |
#check_type_type ⇒ Object (protected)
114 115 116 117 118 119 |
# File 'app/models/type_material.rb', line 114 def check_type_type if protonym code = protonym.rank_class.nomenclatural_code errors.add(:type_type, 'Not a legal type for the nomenclatural code provided') unless TypeMaterial::legal_type_type(code, type_type) end end |
#dwc_occurrences ⇒ Object
104 105 106 107 108 109 110 |
# File 'app/models/type_material.rb', line 104 def dwc_occurrences return DwcOccurrence.none unless collection_object.present? DwcOccurrence.where( dwc_occurrence_object_type: 'CollectionObject', dwc_occurrence_object_id: id ).distinct end |
#sv_single_primary_type ⇒ Object (protected)
125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'app/models/type_material.rb', line 125 def sv_single_primary_type primary_types = TypeMaterial.with_type_array(['holotype', 'neotype', 'lectotype']).where_protonym(protonym).not_self(self) syntypes = TypeMaterial.with_type_array(['syntype', 'syntypes']).where_protonym(protonym) if type_type =~ /syntype/ soft_validations.add(:type_type, 'Other primary types selected for the taxon are conflicting with the syntypes') unless primary_types.empty? end if ['holotype', 'neotype', 'lectotype'].include?(type_type) soft_validations.add(:type_type, 'More than one primary type associated with the taxon') if !primary_types.empty? || !syntypes.empty? end end |
#sv_type_source ⇒ Object (protected)
138 139 140 141 142 143 144 145 146 147 148 |
# File 'app/models/type_material.rb', line 138 def sv_type_source soft_validations.add(:base, 'Source is not selected neither for type nor for taxon') unless type_source if %w(paralectotype neotype lectotype paralectotypes).include?(type_type) if source.nil? soft_validations.add(:base, "Source for #{type_type} designation is not selected ") if source.nil? elsif !protonym.try(:source).nil? && source.cached_nomenclature_date && protonym.cached_nomenclature_date soft_validations.add(:base, "#{type_type.capitalize} could not be designated in the original publication") if source == protonym.source soft_validations.add(:base, "#{type_type.capitalize} could not be designated before taxon description") if source.cached_nomenclature_date&.to_date < protonym.cached_nomenclature_date end end end |
#type_source ⇒ Object
TODO: really should be validating uniqueness at this point, it’s type material, not garbage records
89 90 91 |
# File 'app/models/type_material.rb', line 89 def type_source [source, protonym.try(:source), nil].compact.first end |