SpeciesFileGroup/taxonworks

View on GitHub
app/helpers/otus_helper.rb

Summary

Maintainability
A
0 mins
Test Coverage
module OtusHelper

  def otu_tag(otu)
    return nil if otu.nil?
    a = otu_tag_elements(otu)
    a.push taxon_name_type_short_tag(otu.taxon_name)
    content_tag(:span, a.compact.join(' ').html_safe, class: :otu_tag)
  end

  def label_for_otu(otu)
    return nil if otu.nil?
    [otu.name,
     label_for_taxon_name(otu.taxon_name)
    ].compact.join(': ')
  end

  def otu_tag_elements(otu)
    return nil if otu.nil?
    [
      ( otu.name ? content_tag(:span, otu.name, class: :otu_tag_otu_name, title: otu.id) : nil ),
      ( otu.taxon_name ? content_tag(:span, full_taxon_name_tag(otu.taxon_name).html_safe, class: :otu_tag_taxon_name, title: otu.taxon_name.id) : nil)
    ].compact
  end

  # Used exclusively in /api/v1/otus/autocomplete
  def otu_extended_autocomplete_tag(target)
    if target.kind_of?(Otu)
      otu_tag(target)
    else # TaxonName
      a = [ tag.span( full_taxon_name_tag(target).html_safe, class: :otu_tag_taxon_name, title: target.id) ]
      a.push taxon_name_type_short_tag(target)
      tag.span( a.compact.join(' ').html_safe, class: :otu_tag )
    end
  end

  # @return [String]
  #    no HTML inside <input>
  def otu_autocomplete_selected_tag(otu)
    return nil if otu.nil? || (otu.new_record? && !otu.changed?)
    [otu.name,
     Utilities::Strings.nil_wrap('[',taxon_name_autocomplete_selected_tag(otu.taxon_name), ']')&.html_safe
    ].compact.join(' ')
  end

  def otu_link(otu)
    return nil if otu.nil?
    link_to(otu_tag_elements(otu).join(' ').html_safe, otu)
  end

  def otus_search_form
    render('/otus/quick_search_form')
  end

  def otus_link_list_tag(otus)
    otus.collect { |o| link_to(o.name, o) }.join(',')
  end

  # Stub a smart link to browse OTUs
  # @param object [an instance of TaxonName or Otu]
  #   if TaxonName is provided JS UI will disambiguate if more options are possible
  def browse_otu_link(object)
    return nil if object.nil?
    otu = object.metamorphosize
    content_tag(:div, '', 'data-taxon-name' => object_tag(otu), 'data-redirect' => 'true', 'data-id' => otu.id, 'data-klass' => object.class.base_class.name.to_s, 'data-otu-button' => 'true')
  end

  def otus_radial_disambiguate(object)
    otu = object.metamorphosize
    content_tag(:div, '', 'data-taxon-name' => object_tag(otu), 'data-redirect' => 'false', 'data-id' => otu.id, 'data-klass' => object.class.base_class.name.to_s, 'data-otu-button' => 'true')
  end

  def otus_radial(object)
    content_tag(:div, '', 'data-global-id' => object.to_global_id.to_s, 'data-otu-radial' => 'true')
  end

  # @return [Array]
  #   of OTUs
  def next_otus(otu)
    if otu.taxon_name_id
      o = []
      t = otu.taxon_name.next_sibling
      unless t.nil?
        while o.empty?
          o = t&.otus.to_a
          break if t.nil?
          t = t.next_sibling
        end
      end
      o
    else
      Otu.where(project_id: otu.id).where('id > ?', otu.id).all
    end
  end

  # @return [Array]
  #   of OTUs
  # Some OTUs don't have TaxonName, skip along
  # until we hit one.
  def previous_otus(otu)
    if otu.taxon_name_id
      o = []
      t = otu.taxon_name.previous_sibling
      unless t.nil?
        while o.empty?
          o = t&.otus.to_a
          break if t.nil?
          t = t.previous_sibling
        end
      end
      o
    else
      Otu.where(project_id: otu.id).where('id < ?', otu.id).all
    end
  end

  def parent_otus(otu)
    otu.taxon_name&.parent&.otus&.all || []
  end

  # See also otus#ancestor_otu_ids ?
  def parents_by_nomenclature(otu)
    above = [ ]
    if otu.taxon_name_id
      TaxonName.ancestors_of(otu.taxon_name)
        .select('taxon_names.*, taxon_name_hierarchies.generations')
        .that_is_valid.joins(:otus)
        .distinct
        .reorder('taxon_name_hierarchies.generations DESC, taxon_names.cached_valid_taxon_name_id').each do |t|
          above.push [t.cached, t.otus.to_a] # TODO: to_a vs. pluck
        end
    end
    above
  end

  # @return Hash
  #   A GeoJSON collection of distribution data in x parts
  #     need a `to_geo_json` for each object
  #
  #      :asserted_distributions
  #         with shape
  #         without shapes
  #      :collection_objects
  #           with georeferences
  #           with GeographicAreas
  #      :type material
  #           with georeferences
  #           with GeographicAreas
  # TODO:
  #
  #  * make properties universal
  #    type: 'Model',
  #    id: id,
  #    label: <label>
  # * merge origin_otu_id: id to reference coordinate OTUs
  #
  #  target:
  #    type: Otu
  #    label:
  #    id
  #
  #  base:   # one level above target (or one level below shape?!)
  #    type:
  #    id:
  #
  #  shape: # Either GeographicArea or Georeference
  #     type
  #     id
  #
  def otu_distribution(otu, children = true, cutoff = 200)
    return {} if otu.nil?
    otus = if children
             otu.coordinate_otus_with_children
           else
             Otu.coordinate_otus(otu.id)
           end

    h = {
      'type' => 'FeatureCollection',
      'features' => [],
      'properties' => {
        'target' => { # Top level target
          'id' => otu.id,
          'label' => label_for_otu(otu),
          'type' => 'Otu'
        }
      }
    }

    if otu.taxon_name && otu.taxon_name.is_protonym? && !otu.taxon_name.is_species_rank?
      add_aggregate_geo_json(otu, h)
    else
      otus.each do |o|
        add_distribution_geo_json(o, h)
      end
    end

    h
  end

  def add_aggregate_geo_json(otu, target)
    h = target

    if g = aggregate_geo_json(otu, h)
      t = {
        'id' => otu.id,
        'type' => 'Otu',
        'label' => label_for_otu(otu)
      }

      g['properties'] = {'aggregate': true}

      g['properties']['target'] = t
      h['features'].push g
    end

    h
  end

  # NOT USED
  # Caching the cached map
  def otu_cached_map(otu, target, cached_map_type = 'CachedMapItem::WebLevel1', cache = true, force = false)
    r = nil
    if force
      r = aggregate_geo_json(otu, target, cached_map_type)
    else
      # Check for map

      # TODO: extend with synced check
      if a = CachedMap.where(project_id: sessions_current_project_id).where(otu_id: otu.id, cached_map_type:  )
      end
    end
  end

  # TODO: cleanup
  def aggregate_geo_json(otu, target, cached_map_type = 'CachedMapItem::WebLevel1')
    h = target

    if gj = otu.cached_map_geo_json(cached_map_type)

     i =
       {
         **gj,
        # 'type' => gj['type'],  # 'Feature',

        'properties' => {
          'base' => {
            'type' => 'Otu',
            'id' => otu.id,
            'label' => label_for_otu(otu) },
    #     'shape' => {
    #       'type' => cached_map_type,
    #       'id' => 99999 }, # was nil
         'updated_at' => 'foo' # last updated at on CachedMapItem scope, possibly
        }
      }

     if gj.keys.include?('coordinates')
       i['coordinates'] = gj['coordinates'] # was 'coordinates' TODO: might not work
     elsif gj.keys.include?('geometries')
       i['geometries'] = gj['geometries'] # was 'coordinates' TODO: might not work
     end

      i

    else
      nil
    end

  end

  def add_distribution_geo_json(otu, target)
    h = target
    o = otu

    # internal target
    t = {
      'id' => o.id,
      'type' => 'Otu',
      'label' => label_for_otu(otu)
    }

    o.current_collection_objects.each do |c|
      if g = collection_object_to_geo_json_feature(c)
        g['properties']['target'] = t
        h['features'].push g
      end
    end

    o.asserted_distributions.each do |a|
      if g = asserted_distribution_to_geo_json_feature(a)
        g['properties']['target'] = t
        h['features'].push g
      end
    end

    o.type_materials.each do |e|
      if g = type_material_to_geo_json_feature(e)
        g['properties']['target'] = t
        h['features'].push g
      end
    end
    h
  end

end