indentlabs/notebook

View on GitHub
app/models/documents/document_entity.rb

Summary

Maintainability
A
0 mins
Test Coverage
class DocumentEntity < ApplicationRecord
  belongs_to :entity, polymorphic: true, optional: true
  belongs_to :document_analysis, optional: true

  after_create :match_notebook_page!, if: Proc.new { |de| de.entity_id.nil? }

  # TODO should this be some chain of method aliases maybe? or cached on object?
  def document_owner
    document_analysis.document.user
  end

  def match_notebook_page!
    matched_entity = document_owner.send(entity_relation).detect do |entity|
      entity_match?(entity)
    end

    # If we found a Notebook.ai entity to match to, hurrah!
    if matched_entity.present?
      update(
        entity_type: matched_entity.class.name,
        entity_id:   matched_entity.id
      )
    end

    # Return true/false for whether we found a match
    matched_entity.present?
  end

  # Analyze this entity within the context of an existing document analysis
  # AKA this entity was probably added manuallly post-analysis
  def analyze!
    DocumentEntityAnalysisJob.perform_later(id)
  end

  def entity_relation
    # 'Character' => 'characters'
    self.entity_type.downcase.pluralize
  end

  def entity_match?(entity)
    return unless entity.respond_to?(:name)

    # TODO: levenshtein distance threshold comparison?
    entity.name == self.text
  end

  def linked_name_if_possible
    entity.present? ? entity.name : text
  end

  def dominant_emotion
    return { unknown: 1 } if emotions.values.uniq == [0]

    emotions.sort_by { |emotion, score| score }.reverse
  end

  def recessive_emotion
    return { unknown: 1 } if emotions.values.uniq == [0]

    emotions.sort_by { |emotion, score| score }
  end

  def emotions
    {
      sadness: sadness_score,
      joy: joy_score,
      fear: fear_score,
      disgust: disgust_score,
      anger: anger_score
    }
  end
end