Class: DwcOccurrence

Inherits:
ApplicationRecord show all
Includes:
Housekeeping
Defined in:
app/models/dwc_occurrence.rb

Overview

A Darwin Core Record for the Occurence core. Field generated from Ruby dwc-meta, which references the same spec that is used in the IPT, and the Dwc Assistant. Each record references a specific CollectionObject or AssertedDistribution.

Important: This is a cache/index, data here are periodically (regenerated) from multiple tables in TW.

TODO: The basisOfRecord CVTs are not super informative.

We know collection object is definitely 1:1 with PreservedSpecimen, however
AssertedDistribution could be HumanObservation (if source is person), or ... what? if
its a published record.  Seems we need a 'PublishedAssertation', just like we model the data.

DWC attributes are camelCase to facilitate matching dwcClass is a replacement for the Rails reserved 'Class'

All DC attributes (attributes not in DwcOccurrence::TW_ATTRIBUTES) in this table are namespaced to dc (“purl.org/dc/terms/”, “rs.tdwg.org/dwc/terms/”)

Constant Summary collapse

DC_NAMESPACE =
'http://rs.tdwg.org/dwc/terms/'.freeze
TW_ATTRIBUTES =

Not yet implemented, but likely needed ? :id

[
  :id,
  :project_id,
  :created_at,
  :updated_at,
  :created_by_id,
  :updated_by_id,
  :dwc_occurrence_object_type,
  :dwc_occurence_object_id
].freeze
HEADER_CONVERTERS =
{
  'dwcClass' => 'class',
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Housekeeping

#has_polymorphic_relationship?

Methods inherited from ApplicationRecord

transaction_with_retry

Instance Attribute Details

#occurrence_identifierObject

Returns the value of attribute occurrence_identifier.



58
59
60
# File 'app/models/dwc_occurrence.rb', line 58

def occurrence_identifier
  @occurrence_identifier
end

Class Method Details

.by_collection_object_filter(filter_scope: nil, project_id: nil) ⇒ Object

Return scopes by a collection object filter



71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'app/models/dwc_occurrence.rb', line 71

def self.by_collection_object_filter(filter_scope: nil, project_id: nil)
  return DwcOccurrence.none if project_id.nil? || filter_scope.nil?

  c = ::CollectionObject.arel_table
  d = arel_table

  # TODO: hackish
  k = ::CollectionObject.select('coscope.id').from( '(' + filter_scope.to_sql + ') as coscope ' )

  a = self.collection_objects_join
    .where("dwc_occurrences.project_id = ?", project_id)
    .where(dwc_occurrence_object_id: k)
  a
end

.collection_objects_joinActiveRecord::Relation

TODO: will need similar join for AssertedDistribution, or any object that matches, consider moving to Shared

Returns:

  • (ActiveRecord::Relation)


63
64
65
66
67
68
# File 'app/models/dwc_occurrence.rb', line 63

def self.collection_objects_join
  a = arel_table
  b = ::CollectionObject.arel_table
  j = a.join(b).on(a[:dwc_occurrence_object_type].eq('CollectionObject').and(a[:dwc_occurrence_object_id].eq(b[:id])))
  joins(j.join_sources)
end

.computed_columnsScope

Returns the columns inferred to have data.

Returns:

  • (Scope)

    the columns inferred to have data



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

def self.computed_columns
  select(target_columns)
end

.empty_fieldsArray

Returns of column names as symbols that are blank in ALL projects (not just this one).

Returns:

  • (Array)

    of column names as symbols that are blank in ALL projects (not just this one)



88
89
90
91
92
93
94
95
96
97
98
99
# File 'app/models/dwc_occurrence.rb', line 88

def self.empty_fields
  empty_in_all_projects =  ActiveRecord::Base.connection.execute("select attname
  from pg_stats
  where tablename = 'dwc_occurrences'
  and most_common_vals is null
  and most_common_freqs is null
  and histogram_bounds is null
  and correlation is null
  and null_frac = 1;").pluck('attname').map(&:to_sym)

  empty_in_all_projects #  - target_columns
end

.excluded_columnsArray

Returns of symbols.

Returns:

  • (Array)

    of symbols



109
110
111
# File 'app/models/dwc_occurrence.rb', line 109

def self.excluded_columns
  ::DwcOccurrence.columns.collect{|c| c.name.to_sym} - self.target_columns
end

.target_columnsArray

Returns of symbols.

Returns:

  • (Array)

    of symbols



103
104
105
# File 'app/models/dwc_occurrence.rb', line 103

def self.target_columns
  [:id, :basisOfRecord] + CollectionObject::DwcExtensions::DWC_OCCURRENCE_MAP.keys
end

Instance Method Details

#basisObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/models/dwc_occurrence.rb', line 119

def basis
  case dwc_occurrence_object_type
  when 'CollectionObject'
    return 'PreservedSpecimen'
  when 'AssertedDistribution'
    case dwc_occurrence_object.source.try(:type)
    when 'Source::Bibtex'
      return 'Occurrence'
    when 'Source::Human'
      return 'HumanObservation'
    end
  end
  'Undefined'
end

#create_object_uuidObject (protected)



177
178
179
180
181
182
183
# File 'app/models/dwc_occurrence.rb', line 177

def create_object_uuid
  @occurrence_identifier = Identifier::Global::Uuid::TaxonworksDwcOccurrence.create!(
    identifier_object: dwc_occurrence_object,
    by: dwc_occurrence_object&.creator, # revisit, why required?
    project_id: dwc_occurrence_object&.project_id, # Current.project_id,  # revisit, why required?
    is_generated: true)
end

#generate_uuid_if_required(force = false) ⇒ Object

TODO: quick check if occurrenceID exists in table?! <-> locking sync !?

Parameters:

  • force (Boolean) (defaults to: false)

    true - only create identifier if identifier exists false - check if occurrenceID is present, if it is, assume identifier (still) exists



146
147
148
149
150
151
152
153
154
# File 'app/models/dwc_occurrence.rb', line 146

def generate_uuid_if_required(force = false)
  if force # really make sure there is an object to work with
    create_object_uuid if !occurrence_identifier && !dwc_occurrence_object.nil? # TODO: can be simplified when inverse_of/validation added to identifiers
  else # assume if occurrenceID is not blank identifier is present
    if occurrenceID.blank?
      create_object_uuid if !occurrence_identifier && !dwc_occurrence_object.nil? # TODO: can be simplified when inverse_of/validation added to identifiers
    end
  end
end

#is_stale?Boolean

!! A spot check, could be made more robust.

Returns:

  • (Boolean)

    By looking at the data, determine if a related record has been updated since this record ws updated at



160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'app/models/dwc_occurrence.rb', line 160

def is_stale?
  case dwc_occurrence_object_type
  when 'CollectionObject'
    t = [
      dwc_occurrence_object.updated_at,
      dwc_occurrence_object.collecting_event&.updated_at,
      dwc_occurrence_object&.taxon_determinations&.first&.updated_at
    ]
    t.compact!
    t.sort.first > (updated_at || Time.now)
  else # AssertedDistribution
    dwc_occurrence_object.updated_at > updated_at
  end
end

#set_metadata_attributesObject (protected)



186
187
188
189
# File 'app/models/dwc_occurrence.rb', line 186

def 
  write_attribute( :basisOfRecord, basis)
  write_attribute( :occurrenceID, occurrence_identifier&.identifier)
end

#uuid_identifier_scopeObject



134
135
136
# File 'app/models/dwc_occurrence.rb', line 134

def uuid_identifier_scope
  dwc_occurrence_object&.identifiers&.where('identifiers.type like ?', 'Identifier::Global::Uuid%')&.order(:position) # :created_at
end