duke-libraries/ddr-models

View on GitHub
lib/ddr/models/solr_document.rb

Summary

Maintainability
B
4 hrs
Test Coverage
require 'json'

module Ddr::Models
  module SolrDocument
    extend ActiveSupport::Concern
    extend Deprecation

    self.deprecation_horizon = 'ddr-models v3.0'

    included do
      alias_method :pid, :id
    end

    class NotFound < Error; end

    module ClassMethods
      def find(pid_or_uri)
        query = Ddr::Index::Query.new { id pid_or_uri.sub(/\Ainfo:fedora\//, "") }
        if doc = query.docs.first
          return doc
        end
        raise NotFound, "SolrDocument not found for \"#{pid_or_uri}\"."
      end

      def find_by_permanent_id(ark)
        query = Ddr::Index::Query.new { where permanent_id: ark }
        if doc = query.docs.first
          return doc
        end
        raise NotFound, "SolrDocument not found for permanent id \"#{ark}\"."
      end
    end

    def inspect
      "#<#{self.class.name} id=#{id.inspect}>"
    end

    def method_missing(name, *args, &block)
      if args.empty? && !block
        begin
          field = Ddr::Index::Fields.get(name)
        rescue NameError
        # pass
        else
          # Preserves the default behavior of the deprecated method
          # Blacklight::Solr::Document#get, which this procedure
          # effectively replaces.
          val = self[field]
          return val.is_a?(Array) ? val.join(", ") : val
        end
      end
      super
    end

    def to_partial_path
      'document'
    end

    def safe_id
      id.sub(/:/, "-")
    end

    def object_profile
      @object_profile ||= get_json(Ddr::Index::Fields::OBJECT_PROFILE)
    end

    def object_state
      object_profile["objState"]
    end

    def object_create_date
      parse_date(object_profile["objCreateDate"])
    end

    def object_modified_date
      parse_date(object_profile["objLastModDate"])
    end

    def last_fixity_check_on
      get_date(Ddr::Index::Fields::LAST_FIXITY_CHECK_ON)
    end

    def last_virus_check_on
      get_date(Ddr::Index::Fields::LAST_VIRUS_CHECK_ON)
    end

    def datastreams
      object_profile["datastreams"]
    end

    def has_datastream?(dsID)
      datastreams[dsID].present?
    end

    def has_admin_policy?
      admin_policy_uri.present?
    end

    def admin_policy_uri
      is_governed_by
    end

    def admin_policy_pid
      uri = admin_policy_uri
      uri &&= ActiveFedora::Base.pid_from_uri(uri)
    end
    alias_method :admin_policy_id, :admin_policy_pid

    def admin_policy
      if has_admin_policy?
        self.class.find(admin_policy_uri)
      end
    end

    def has_children?
      ActiveFedora::SolrService.class_from_solr_document(self).reflect_on_association(:children).present?
    end

    def label
      object_profile["objLabel"]
    end

    def title_display
      title
    end

    def identifier
      # We want the multivalued version here
      self[ActiveFedora::SolrService.solr_name(:identifier, :stored_searchable, type: :text)]
    end

    def source
      self[ActiveFedora::SolrService.solr_name(:source, :stored_searchable, type: :text)]
    end

    def has_thumbnail?
      has_datastream?(Ddr::Datastreams::THUMBNAIL)
    end

    def has_content?
      has_datastream?(Ddr::Datastreams::CONTENT)
    end

    def has_intermediate_file?
      has_datastream?(Ddr::Datastreams::INTERMEDIATE_FILE)
    end

    def has_extracted_text?
      has_datastream?(Ddr::Datastreams::EXTRACTED_TEXT)
    end

    def content_ds
      datastreams[Ddr::Datastreams::CONTENT]
    end

    def content_mime_type
      content_ds["dsMIME"] rescue nil
    end
    # For duck-typing with Ddr::Models::HasContent
    alias_method :content_type, :content_mime_type

    def content_checksum
      content_ds["dsChecksum"] rescue nil
    end

    def targets
      @targets ||= ActiveFedora::SolrService.query(targets_query)
    end

    def targets_count
      @targets_count ||= ActiveFedora::SolrService.count(targets_query)
    end

    def has_target?
      targets_count > 0
    end

    def association(name)
      get_pid(ActiveFedora::SolrService.solr_name(name, :symbol))
    end

    def controller_name
      active_fedora_model.tableize
    end

    def rights_statement
      @rights_statement ||= RightsStatement.call(self)
    end
    alias_method :effective_license, :rights_statement
    deprecation_deprecate :effective_license

    def roles
      @roles ||= Ddr::Auth::Roles::DetachedRoleSet.from_json(access_role)
    end

    def structure
      JSON.parse(fetch(Ddr::Index::Fields::STRUCTURE))
    rescue
      nil
    end

    def effective_permissions(agents)
      Ddr::Auth::EffectivePermissions.call(self, agents)
    end

    def research_help
      research_help_contact = self[Ddr::Index::Fields::RESEARCH_HELP_CONTACT] || inherited_research_help_contact
      Ddr::Models::Contact.call(research_help_contact) if research_help_contact
    end

    def parent_uri
      is_part_of || is_member_of_collection
    end

    def has_parent?
      parent_uri.present?
    end

    def parent
      if has_parent?
        self.class.find(parent_uri)
      end
    end

    def multires_image_file_paths
      if structure
        structure_docs.map { |doc| doc.multires_image_file_path }.compact
      else
        []
      end
    end

    # DRY HasAdminMetadata
    def finding_aid
      if ead_id
        FindingAid.new(ead_id)
      end
    end

    def intermediate_type
      if has_intermediate_file?
        datastreams[Ddr::Datastreams::INTERMEDIATE_FILE]["dsMIME"]
      end
    end

    def intermediate_path
      if has_intermediate_file?
        Ddr::Utils.path_from_uri(datastreams[Ddr::Datastreams::INTERMEDIATE_FILE]["dsLocation"])
      end
    end

    def intermediate_extension
      if has_intermediate_file?
        extensions = Ddr::Models.preferred_file_extensions
        if extensions.include? intermediate_type
          extensions[intermediate_type]
        else
          intermediate_extension_default
        end
      end
    end

    def captionable?
      has_datastream?(Ddr::Datastreams::CAPTION)
    end

    def caption_type
      if captionable?
        datastreams[Ddr::Datastreams::CAPTION]["dsMIME"]
      end
    end

    def caption_extension
      if captionable?
        extensions = Ddr::Models.preferred_file_extensions
        if extensions.include? caption_type
          extensions[caption_type]
        else
          caption_extension_default
        end
      end
    end

    def caption_path
      if captionable?
        Ddr::Utils.path_from_uri(datastreams[Ddr::Datastreams::CAPTION]["dsLocation"])
      end
    end

    def streamable?
      has_datastream?(Ddr::Datastreams::STREAMABLE_MEDIA)
    end

    def streamable_media_extension
      if streamable?
        extensions = Ddr::Models.preferred_file_extensions
        if extensions.include? streamable_media_type
          extensions[streamable_media_type]
        else
          streamable_media_extension_default
        end
      end
    end

    def streamable_media_type
      if streamable?
        datastreams[Ddr::Datastreams::STREAMABLE_MEDIA]["dsMIME"]
      end
    end

    def streamable_media_path
      if streamable?
        Ddr::Utils.path_from_uri(datastreams[Ddr::Datastreams::STREAMABLE_MEDIA]["dsLocation"])
      end
    end

    # FIXME - Probably need a more general solution mapping object reader methods to index field names.
    def rights
      self["rights_tesim"]
    end

    def children
      children_query.docs rescue []
    end

    private

    def targets_query
      "#{Ddr::Index::Fields::IS_EXTERNAL_TARGET_FOR}:#{internal_uri_for_query}"
    end

    def internal_uri_for_query
      ActiveFedora::SolrService.escape_uri_for_query(internal_uri)
    end

    def get_date(field)
      parse_date(self[field])
    end

    def get_json(field)
      JSON.parse Array(self[field]).first
    end

    def parse_date(date)
      Time.parse(date).localtime if date
    end

    def get_pid(field)
      ActiveFedora::Base.pid_from_uri(self[field]) rescue nil
    end

    def inherited_research_help_contact
      if doc = admin_policy
        doc.research_help_contact
      end
    end

    def structure_docs
      structure_repo_ids.map { |repo_id| self.class.find(repo_id) }.compact
    end

    # For simplicity, initial implementation returns repo ID's only from top-level
    # (i.e., not nested) contents.  This is done since we have not clarified what
    # an _ordered_ list of repo ID's should look like if structure contains nested
    # contents.
    def structure_repo_ids
      default_struct_map['contents'].map { |content| content['contents'].map { |content| content['repo_id'] } }.flatten
    end

    def default_struct_map
      structure['default'] || structure.values.first
    end

    def intermediate_extension_default
      datastreams[Ddr::Datastreams::INTERMEDIATE_FILE].default_file_extension
    end

    def caption_extension_default
      datastreams[Ddr::Datastreams::CAPTION].default_file_extension
    end

    def streamable_media_extension_default
      datastreams[Ddr::Datastreams::STREAMABLE_MEDIA].default_file_extension
    end

    def children_query
      case self[Ddr::Index::Fields::ACTIVE_FEDORA_MODEL]
        when 'Collection'
          Ddr::Index::Query.build(self) do |parent|
            is_member_of_collection parent.id
          end
        when 'Item'
          Ddr::Index::Query.build(self) do |parent|
            is_part_of parent.id
          end
      end
    end
  end
end