<commit_before>class BusquedasController < ApplicationController

  skip_before_filter :set_locale, only: [:cat_tax_asociadas]
  layout false, :only => [:cat_tax_asociadas]

  def basica
  end

  def avanzada
  end

  def resultados
    # Por si no coincidio nada
    @taxones = Especie.none

    if params[:busqueda] == 'basica'
      arbol = params[:arbol].present? && params[:arbol].to_i == 1

      pagina = params[:pagina].present? ? params[:pagina].to_i : 1
      por_pagina = params[:por_pagina].present? ? params[:por_pagina].to_i : Busqueda::POR_PAGINA_PREDETERMINADO

      if params[:solo_categoria].present?
        query = Especie.busqueda_basica(params[:nombre], {vista_general: true, pagina: pagina,
                                                          por_pagina: por_pagina, solo_categoria: params[:solo_categoria]})
        @taxones = Especie.find_by_sql(query)
        #@paginacion = paginacion(totales, pagina, por_pagina)

        @taxones.each do |t|
          t.cual_nombre_comun_coincidio(params[:nombre])
        end

      else
        totales = Especie.find_by_sql(Especie.count_busqueda_basica(params[:nombre], {vista_general: true, solo_categoria: params[:solo_categoria]}))[0].totales

        if totales > 0
          query = Especie.busqueda_basica(params[:nombre], {vista_general: true, pagina: pagina, por_pagina: por_pagina})
          query_por_categoria = Especie.por_categoria_busqueda_basica(params[:nombre].limpia_sql, true)
          @por_categoria = Especie.find_by_sql(query_por_categoria)

          @taxones = Especie.find_by_sql(query)
          @paginacion = paginacion(totales, pagina, por_pagina)

          @taxones.each do |t|
            t.cual_nombre_comun_coincidio(params[:nombre])
          end

        else
          ids_comun = FUZZY_NOM_COM.find(params[:nombre], limit=CONFIG.limit_fuzzy)
          ids_cientifico = FUZZY_NOM_CIEN.find(params[:nombre], limit=CONFIG.limit_fuzzy)

          if ids_comun.any? || ids_cientifico.any?
            sql = "Especie.datos_basicos(['nombre_comun']).nombres_comunes_join"

            # Parte del estatus
            if I18n.locale.to_s == 'es'
              sql << ".where('estatus=2')"
            end

            if ids_comun.any? && ids_cientifico.any?
              sql << ".where(\"nombres_comunes.id IN (#{ids_comun.join(',')}) OR especies.id IN (#{ids_cientifico.join(',')})\")"
            elsif ids_comun.any?
              sql << ".caso_rango_valores('nombres_comunes.id', \"#{ids_comun.join(',')}\")"
            elsif ids_cientifico.any?
              sql << ".caso_rango_valores('especies.id', \"#{ids_cientifico.join(',')}\")"
            end

            query = eval(sql).distinct.to_sql
            consulta = Bases.distinct_limpio(query) << " ORDER BY nombre_cientifico ASC OFFSET #{(pagina-1)*por_pagina} ROWS FETCH NEXT #{por_pagina} ROWS ONLY"
            taxones = Especie.find_by_sql(consulta)

            ids_totales = []

            taxones.each do |taxon|
              # Para evitar que se repitan los taxones con los joins
              next if ids_totales.include?(taxon.id)
              ids_totales << taxon.id

              # Si la distancia entre palabras es menor a 3 que muestre la sugerencia
              if taxon.nombre_comun.present?
                distancia = Levenshtein.distance(params[:nombre].downcase, taxon.nombre_comun.downcase)
                @taxones <<= taxon if distancia < 3
              end

              distancia = Levenshtein.distance(params[:nombre].downcase, taxon.nombre_cientifico.limpiar.downcase)
              @taxones <<= taxon if distancia < 3
            end
          end

          # Para que saga el total tambien con el fuzzy match
          if @taxones.any?
            @taxones.each do |t|
              t.cual_nombre_comun_coincidio(params[:nombre], true)
            end

            @fuzzy_match = '¿Quizás quiso decir algunos de los siguientes taxones?'.html_safe
          end

          @paginacion = paginacion(@taxones.length, pagina, por_pagina)
        end
      end

      if @taxones.any? && arbol
        # Po si requieren que se genere el checklis
        @arboles = []

        @taxones.each do | taxon|
          # Primero hallo nombre comunes, mape unicamente el campo nombre_comun, le pego el nombre común principal (si tiene), saco los únicos y los ordeno alfabéticamente non-case
          nombres_comunes = (taxon.nombres_comunes.map(&:nombre_comun) << taxon.adicional.nombre_comun_principal).uniq.sort_by{|w| [I18n.transliterate(w.downcase), w] unless !(w.present?)}
          # Jalo unicamente los ancestros del taxón en cuestión
          arbolito = Especie.datos_arbol_para_json.where("especies.id = (#{taxon.id})")[0].arbol.split('/').join(',')

          # Género el árbol para cada uno de los ancestros recién obtenidos en la linea anterior de código ^
          @arboles << (Especie.datos_arbol_para_json_2.where("especies.id in (#{arbolito})" ).order('arbol') << {"distancia" => taxon.try("distancia") || 0} << {"nombres_comunes" => nombres_comunes.compact} )
        end
        render 'busquedas/arbol.json.erb'
      elsif params[:solo_categoria].present?
        # Para desplegar solo una categoria de resultados, o el paginado con el scrolling
        if params[:pagina].present? && params[:pagina].to_i > 1 && @taxones.any?
          render :partial => 'busquedas/_resultados'
        elsif params[:pagina].to_i > 1 && @taxones.empty?
          # Quiere decir que el paginado acabo en algun TAB que no es el default
          render text: ''
        else
          # Despliega el inicio de un TAB que no sea el default
          render :partial => 'busquedas/resultados'
        end
      elsif params[:pagina].present? && params[:pagina].to_i > 1 && @taxones.any?
        # Despliega el paginado del TAB que tiene todos
        render :partial => 'busquedas/_resultados'
      elsif @taxones.empty? && params[:pagina].present? && params[:pagina].to_i > 1
        # Quiere decir que el paginado acabo en algun TAB
        render text: ''
      elsif @taxones.empty?
        # La busqueda no dio ningun resultado
        redirect_to  '/inicio/error', :notice => 'Tu búsqueda no dio ningún resultado.'
      end
      # Ojo si no entro a ningun condicional desplegará el render normal (resultados.html.erb).


    elsif params[:busqueda] == 'avanzada'

      # Es necesario hacer un index con estos campos para aumentar la velocidad
      condiciones = []
      joins = []
      busqueda = 'Especie.datos_basicos'

      conID = params[:id]

      # Para hacer la condicion con el nombre_comun
      if conID.blank? && params[:nombre].present?
        condiciones << ".caso_nombre_comun_y_cientifico(\"#{params[:nombre].limpia_sql}\")"
        joins << '.nombres_comunes_join'
      end

      # Parte de la categoria taxonomica
      if conID.present? && params[:cat].present? && params[:nivel].present?
        taxon = Especie.find(conID)

        if taxon.is_root?
          condiciones << ".where(\"ancestry_ascendente_directo LIKE '#{taxon.id}%' OR especies.id=#{taxon.id}\")"
        else
          ancestros = taxon.ancestry_ascendente_directo
          condiciones << ".where(\"ancestry_ascendente_directo LIKE '#{ancestros}/#{taxon.id}%' OR especies.id IN (#{taxon.path_ids.join(',')})\")"
        end

        # Se limita la busqueda al rango de categorias taxonomicas de acuerdo al taxon que escogio
        condiciones << ".where(\"CONCAT(categorias_taxonomicas.nivel1,categorias_taxonomicas.nivel2,categorias_taxonomicas.nivel3,categorias_taxonomicas.nivel4) #{params[:nivel]} '#{params[:cat]}'\")"
      end

      # Parte del estatus
      if I18n.locale.to_s == 'es-cientifico'
        # Si escogio uno lo pone, si escogio los dos es como no poner esta condicion
        if params[:estatus].present? && params[:estatus].length == 1
          condiciones << ".where('estatus=#{params[:estatus].first}')"
        end
      else  # En la busqueda general solo el valido
        condiciones << ".where('estatus=2')"
      end

      # Parte del tipo de ditribucion
      if params[:dist].present?
        #######################  Quitar cuando se arregle en la base
        if params[:dist].include?('Invasora') && params[:dist].length == 1  # Solo selecciono invasora
          condiciones << ".where('especies.invasora IS NOT NULL')"
        elsif params[:dist].include?('Invasora')  # No solo selecciono invasora, caso complejo
          params[:dist].delete('Invasora')  # Para quitar invasora y no lo ponga en el join
          joins << '.tipo_distribucion_join'
          condiciones << ".where(\"tipos_distribuciones.descripcion IN ('#{params[:dist].join("','")}') OR especies.invasora IS NOT NULL\")"
        else  # Selecciono cualquiera menos invasora
          joins << '.tipo_distribucion_join'
          condiciones << ".caso_rango_valores('tipos_distribuciones.descripcion', \"'#{params[:dist].join("','")}'\")"
        end
        #######################
      end

      # Parte del edo. de conservacion
      if params[:edo_cons].present?
        joins << '.catalogos_join'
        condiciones << ".caso_rango_valores('catalogos.descripcion', \"'#{params[:edo_cons].join("','")}'\")"
      end

      # Para las especies prioritarias
      if params[:prior].present?
        joins << '.catalogos_join'
        condiciones << ".caso_rango_valores('catalogos.descripcion', \"'#{params[:prior].join("','")}'\")"
      end

      # Parte de consultar solo un TAB (categoria taxonomica), se tuvo que hacer con nombre_categoria taxonomica,
      # ya que los catalogos no tienen estandarizados los niveles en la tabla categorias_taxonomicas  >.>
      if params[:solo_categoria]
        condiciones << ".caso_sensitivo('nombre_categoria_taxonomica', '#{params[:solo_categoria]}')"
      end

      # Quita las condiciones y los joins repetidos
      condiciones_unicas = condiciones.uniq.join('')
      joins_unicos = joins.uniq.join('')
      busqueda << joins_unicos << condiciones_unicas      #pone el query basico armado

      # Para sacar los resultados por categoria
      @por_categoria = Busqueda.por_categoria(busqueda) if params[:solo_categoria].blank?

      pagina = params[:pagina].present? ? params[:pagina].to_i : 1
      por_pagina = params[:por_pagina].present? ? params[:por_pagina].to_i : Busqueda::POR_PAGINA_PREDETERMINADO

      totales = eval(busqueda.gsub('datos_basicos','datos_count'))[0].totales

      if totales > 0
        @paginacion = paginacion(totales, pagina, por_pagina)

        if params[:checklist] == '1' # Reviso si me pidieron una url que contien parametro checklist (Busqueda CON FILTROS)
          @taxones = Busqueda.por_arbol(busqueda)
          checklist
        else
          query = eval(busqueda).distinct.to_sql
          consulta = Bases.distinct_limpio(query) << " ORDER BY nombre_cientifico ASC OFFSET #{(pagina-1)*por_pagina} ROWS FETCH NEXT #{por_pagina} ROWS ONLY"
          @taxones = Especie.find_by_sql(consulta)
        end
      end

      # Para desplegar solo una categoria de resultados, o el paginado con el scrolling
      if params[:solo_categoria].present?
        if params[:pagina].present? && params[:pagina].to_i > 1 && !@taxones.empty?
          render :partial => 'busquedas/_resultados'<commit_msg>el full text index ahora es insensitivo a acentos<commit_after>class BusquedasController < ApplicationController

  skip_before_filter :set_locale, only: [:cat_tax_asociadas]
  layout false, :only => [:cat_tax_asociadas]

  def basica
  end

  def avanzada
  end

  def resultados
    # Por si no coincidio nada
    @taxones = Especie.none

    if params[:busqueda] == 'basica'
      arbol = params[:arbol].present? && params[:arbol].to_i == 1
      vista_general = I18n.locale.to_s == 'es' ? true : false

      pagina = params[:pagina].present? ? params[:pagina].to_i : 1
      por_pagina = params[:por_pagina].present? ? params[:por_pagina].to_i : Busqueda::POR_PAGINA_PREDETERMINADO

      if params[:solo_categoria].present?
        query = Especie.busqueda_basica(params[:nombre], {vista_general: vista_general, pagina: pagina, por_pagina: por_pagina,
                                                          solo_categoria: params[:solo_categoria]})
        @taxones = Especie.find_by_sql(query)

        @taxones.each do |t|
          t.cual_nombre_comun_coincidio(params[:nombre])
        end

      else
        totales = Especie.find_by_sql(Especie.count_busqueda_basica(params[:nombre], {vista_general: vista_general, solo_categoria: params[:solo_categoria]}))[0].totales

        if totales > 0
          query = Especie.busqueda_basica(params[:nombre], {vista_general: vista_general, pagina: pagina, por_pagina: por_pagina})
          query_por_categoria = Especie.por_categoria_busqueda_basica(params[:nombre].limpia_sql, true)
          @por_categoria = Especie.find_by_sql(query_por_categoria)

          @taxones = Especie.find_by_sql(query)
          @paginacion = paginacion(totales, pagina, por_pagina)

          @taxones.each do |t|
            t.cual_nombre_comun_coincidio(params[:nombre])
          end

        else
          ids_comun = FUZZY_NOM_COM.find(params[:nombre], limit=CONFIG.limit_fuzzy)
          ids_cientifico = FUZZY_NOM_CIEN.find(params[:nombre], limit=CONFIG.limit_fuzzy)

          if ids_comun.any? || ids_cientifico.any?
            sql = "Especie.datos_basicos(['nombre_comun']).nombres_comunes_join"

            # Parte del estatus
            if vista_general
              sql << ".where('estatus=2')"
            end

            if ids_comun.any? && ids_cientifico.any?
              sql << ".where(\"nombres_comunes.id IN (#{ids_comun.join(',')}) OR especies.id IN (#{ids_cientifico.join(',')})\")"
            elsif ids_comun.any?
              sql << ".caso_rango_valores('nombres_comunes.id', \"#{ids_comun.join(',')}\")"
            elsif ids_cientifico.any?
              sql << ".caso_rango_valores('especies.id', \"#{ids_cientifico.join(',')}\")"
            end

            query = eval(sql).distinct.to_sql
            consulta = Bases.distinct_limpio(query) << " ORDER BY nombre_cientifico ASC OFFSET #{(pagina-1)*por_pagina} ROWS FETCH NEXT #{por_pagina} ROWS ONLY"
            taxones = Especie.find_by_sql(consulta)

            ids_totales = []

            taxones.each do |taxon|
              # Para evitar que se repitan los taxones con los joins
              next if ids_totales.include?(taxon.id)
              ids_totales << taxon.id

              # Si la distancia entre palabras es menor a 3 que muestre la sugerencia
              if taxon.nombre_comun.present?
                distancia = Levenshtein.distance(params[:nombre].downcase, taxon.nombre_comun.downcase)
                @taxones <<= taxon if distancia < 3
              end

              distancia = Levenshtein.distance(params[:nombre].downcase, taxon.nombre_cientifico.limpiar.downcase)
              @taxones <<= taxon if distancia < 3
            end
          end

          # Para que saga el total tambien con el fuzzy match
          if @taxones.any?
            @taxones.each do |t|
              t.cual_nombre_comun_coincidio(params[:nombre], true)
            end

            @fuzzy_match = '¿Quizás quiso decir algunos de los siguientes taxones?'.html_safe
          end

          @paginacion = paginacion(@taxones.length, pagina, por_pagina)
        end
      end

      if @taxones.any? && arbol
        # Po si requieren que se genere el checklis
        @arboles = []

        @taxones.each do | taxon|
          # Primero hallo nombre comunes, mape unicamente el campo nombre_comun, le pego el nombre común principal (si tiene), saco los únicos y los ordeno alfabéticamente non-case
          nombres_comunes = (taxon.nombres_comunes.map(&:nombre_comun) << taxon.adicional.nombre_comun_principal).uniq.sort_by{|w| [I18n.transliterate(w.downcase), w] unless !(w.present?)}
          # Jalo unicamente los ancestros del taxón en cuestión
          arbolito = Especie.datos_arbol_para_json.where("especies.id = (#{taxon.id})")[0].arbol.split('/').join(',')

          # Género el árbol para cada uno de los ancestros recién obtenidos en la linea anterior de código ^
          @arboles << (Especie.datos_arbol_para_json_2.where("especies.id in (#{arbolito})" ).order('arbol') << {"distancia" => taxon.try("distancia") || 0} << {"nombres_comunes" => nombres_comunes.compact} )
        end
        render 'busquedas/arbol.json.erb'
      elsif params[:solo_categoria].present?
        # Para desplegar solo una categoria de resultados, o el paginado con el scrolling
        if params[:pagina].present? && params[:pagina].to_i > 1 && @taxones.any?
          render :partial => 'busquedas/_resultados'
        elsif params[:pagina].to_i > 1 && @taxones.empty?
          # Quiere decir que el paginado acabo en algun TAB que no es el default
          render text: ''
        else
          # Despliega el inicio de un TAB que no sea el default
          render :partial => 'busquedas/resultados'
        end
      elsif params[:pagina].present? && params[:pagina].to_i > 1 && @taxones.any?
        # Despliega el paginado del TAB que tiene todos
        render :partial => 'busquedas/_resultados'
      elsif @taxones.empty? && params[:pagina].present? && params[:pagina].to_i > 1
        # Quiere decir que el paginado acabo en algun TAB
        render text: ''
      elsif @taxones.empty?
        # La busqueda no dio ningun resultado
        redirect_to  '/inicio/error', :notice => 'Tu búsqueda no dio ningún resultado.'
      end
      # Ojo si no entro a ningun condicional desplegará el render normal (resultados.html.erb).


    elsif params[:busqueda] == 'avanzada'

      # Es necesario hacer un index con estos campos para aumentar la velocidad
      condiciones = []
      joins = []
      busqueda = 'Especie.datos_basicos'

      conID = params[:id]

      # Para hacer la condicion con el nombre_comun
      if conID.blank? && params[:nombre].present?
        condiciones << ".caso_nombre_comun_y_cientifico(\"#{params[:nombre].limpia_sql}\")"
        joins << '.nombres_comunes_join'
      end

      # Parte de la categoria taxonomica
      if conID.present? && params[:cat].present? && params[:nivel].present?
        taxon = Especie.find(conID)

        if taxon.is_root?
          condiciones << ".where(\"ancestry_ascendente_directo LIKE '#{taxon.id}%' OR especies.id=#{taxon.id}\")"
        else
          ancestros = taxon.ancestry_ascendente_directo
          condiciones << ".where(\"ancestry_ascendente_directo LIKE '#{ancestros}/#{taxon.id}%' OR especies.id IN (#{taxon.path_ids.join(',')})\")"
        end

        # Se limita la busqueda al rango de categorias taxonomicas de acuerdo al taxon que escogio
        condiciones << ".where(\"CONCAT(categorias_taxonomicas.nivel1,categorias_taxonomicas.nivel2,categorias_taxonomicas.nivel3,categorias_taxonomicas.nivel4) #{params[:nivel]} '#{params[:cat]}'\")"
      end

      # Parte del estatus
      if I18n.locale.to_s == 'es-cientifico'
        # Si escogio uno lo pone, si escogio los dos es como no poner esta condicion
        if params[:estatus].present? && params[:estatus].length == 1
          condiciones << ".where('estatus=#{params[:estatus].first}')"
        end
      else  # En la busqueda general solo el valido
        condiciones << ".where('estatus=2')"
      end

      # Parte del tipo de ditribucion
      if params[:dist].present?
        #######################  Quitar cuando se arregle en la base
        if params[:dist].include?('Invasora') && params[:dist].length == 1  # Solo selecciono invasora
          condiciones << ".where('especies.invasora IS NOT NULL')"
        elsif params[:dist].include?('Invasora')  # No solo selecciono invasora, caso complejo
          params[:dist].delete('Invasora')  # Para quitar invasora y no lo ponga en el join
          joins << '.tipo_distribucion_join'
          condiciones << ".where(\"tipos_distribuciones.descripcion IN ('#{params[:dist].join("','")}') OR especies.invasora IS NOT NULL\")"
        else  # Selecciono cualquiera menos invasora
          joins << '.tipo_distribucion_join'
          condiciones << ".caso_rango_valores('tipos_distribuciones.descripcion', \"'#{params[:dist].join("','")}'\")"
        end
        #######################
      end

      # Parte del edo. de conservacion
      if params[:edo_cons].present?
        joins << '.catalogos_join'
        condiciones << ".caso_rango_valores('catalogos.descripcion', \"'#{params[:edo_cons].join("','")}'\")"
      end

      # Para las especies prioritarias
      if params[:prior].present?
        joins << '.catalogos_join'
        condiciones << ".caso_rango_valores('catalogos.descripcion', \"'#{params[:prior].join("','")}'\")"
      end

      # Parte de consultar solo un TAB (categoria taxonomica), se tuvo que hacer con nombre_categoria taxonomica,
      # ya que los catalogos no tienen estandarizados los niveles en la tabla categorias_taxonomicas  >.>
      if params[:solo_categoria]
        condiciones << ".caso_sensitivo('nombre_categoria_taxonomica', '#{params[:solo_categoria]}')"
      end

      # Quita las condiciones y los joins repetidos
      condiciones_unicas = condiciones.uniq.join('')
      joins_unicos = joins.uniq.join('')
      busqueda << joins_unicos << condiciones_unicas      #pone el query basico armado

      # Para sacar los resultados por categoria
      @por_categoria = Busqueda.por_categoria(busqueda) if params[:solo_categoria].blank?

      pagina = params[:pagina].present? ? params[:pagina].to_i : 1
      por_pagina = params[:por_pagina].present? ? params[:por_pagina].to_i : Busqueda::POR_PAGINA_PREDETERMINADO

      totales = eval(busqueda.gsub('datos_basicos','datos_count'))[0].totales

      if totales > 0
        @paginacion = paginacion(totales, pagina, por_pagina)

        if params[:checklist] == '1' # Reviso si me pidieron una url que contien parametro checklist (Busqueda CON FILTROS)
          @taxones = Busqueda.por_arbol(busqueda)

          @taxones.each do |t|
            t.cual_nombre_comun_coincidio(params[:nombre])
          end

          checklist
        else
          query = eval(busqueda).distinct.to_sql
          consulta = Bases.distinct_limpio(query) << " ORDER BY nombre_cientifico ASC OFFSET #{(pagina-1)*por_pagina} ROWS FETCH NEXT #{por_pagina} ROWS ONLY"
          @taxones = Especie.find_by_sql(consulta)

          @taxones.each do |t|
            t.cual_nombre_comun_coincidio(params[:nombre])
          end
        end
      end

      # Para desplegar solo una categoria de resultados, o el paginado con el scrolling
      if params[:solo_categoria].present?
        if params[:pagina].present? && params[:pagina].to_i > 1 && !@taxones.empty?
          render :partial => 'busquedas/_resultados'
{'fraction_non_alphanumeric': 0.11965065502183406, 'fraction_numerical': 0.003056768558951965, 'mean_word_length': 2.779042904290429, 'pattern_counts': {'":': 0, '<': 137, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '917373', 'n_tokens_mistral': 7777, 'n_tokens_neox': 7627, 'n_words': 1651}