acan12
1/11/2018 - 3:54 AM

ajax_get_address

class Outlet < ActiveRecord::Base

  #### START ALGOLIA ####
  include AlgoliaSearch

  algoliasearch per_environment: true do
    attributes :name, :address1, :zipcode, :slug, :description, :phone1, :hype, :reviews_rating, :user_votes_count
    add_attribute :cuisine_array
    add_attribute :neighbourhood_name
    add_attribute :facility_array
    add_attribute :dish_array
    add_attribute :city_name
    add_attribute :display_photo_url

    # the attributesToIndex` setting defines the attributes
    # you want to search in: here `title`, `subtitle` & `description`.
    # You need to list them by order of importance. `description` is tagged as
    # `unordered` to avoid taking the position of a match into account in that attribute.
    attributesToIndex [
      'name',
      'address1',
      'zipcode',
      'city_name',
      'neighbourhood_name',
      'cuisine_array',
      'dish_array',
      'unordered(description)',
      'unordered(hype)',
      'facility_array'
    ]

    # the `customRanking` setting defines the ranking criteria use to compare two matching
    # records in case their text-relevance is equal. It should reflect your record popularity.
    customRanking ['desc(reviews_rating)', 'desc(user_votes_count)']
  end

  def cuisine_array; self.cuisines.map(&:label); end
  def facility_array; self.facilities.map(&:name); end
  def dish_array; self.dishes.map(&:name); end
  def city_name; self.city.try(:name); end
  def neighbourhood_name; self.neighbourhood.try(:name); end
  def display_photo_url; self.display_photo.try(:image).try(:url, :micro); end

  #### END ALGOLIA ####

  include Authority::Abilities, Slug, AdvanceSearch, Like, AbraCommon, Validations, AsyncRedis
  include ModelVersion, SiteModelConcerns, ClearActivityAfterDestroy

  extend FriendlyId
  friendly_id :slug_candidates, :use => :slugged
  async_redis! :name, :slug, :city_id, :site_id, :address1, :neighbourhood_id

  has_model_version :if_changed => [:name, :address1, :zipcode,
                                    :latitude, :longitude, :phone1,
                                    :fax, :description, :hype, :business_photos_link,
                                    :city_id, :site_id, :neighbourhood_id, :mall_id, :price_id ]


  translates :description, :additional_details, :hype, :quote #, :fallbacks_for_empty_translations => true  --- not necessary because it makes confusing from admin panel if after remove e.g.description stil show for the previous data.
  Globalize.fallbacks = {:en => [:en, :id], :id => [:id, :en]}

  # Authority
  self.authorizer_name = 'OutletAuthorizer'

  self.per_page = 10
  acts_as_followable

  acts_as_taggable

  THUMB_WIDTH = 100
  THUMB_HEIGHT = 100

  has_attached_file :cover,
    :path => "/images/cover/:style_:basename.:extension",
    :styles => {
      # '#' is to create square thumbnails
      # '>' is to resize if image is larger than specified
      # '<' is to resize if image is smaller than specified
      :thumb    => ["#{THUMB_WIDTH}x#{THUMB_HEIGHT}#"],

      android_small: { geometry: PPCLIP_APP_SMALL },

      android_medium: { geometry: PPCLIP_APP_MEDIUM },

      android_big: { geometry: PPCLIP_APP_BIG }

    },

    :default_url => "#{AppConfig::theme}/defaults/placeholder-picture.jpg",
    :processors => [:thumbnail]

  validates_attachment_content_type :cover, :content_type => %w(image/jpeg image/jpg image/png)

  attr_accessor :highlight_resto_picture
  scope :near_outlet, ->(outlet) { where("id != ? AND enabled = ?", outlet.id, true).near([outlet.latitude, outlet.longitude], 5) }
  scope :nearme, ->(latitude, longitude, range = 15) { where(enabled: true).near([latitude, longitude], range) }
  scope :by_letter, ->(letter) { where("outlets.name LIKE :letter", letter: "#{letter}%") }
  scope :by_neighbourhood, ->(neighbourhood_id) { where("neighbourhood_id = ?", neighbourhood_id) }
  scope :by_mall, ->(mall_id) { where("mall_id = ?", mall_id) }
  scope :by_price, ->(price_id) { where("price_id = ?", price_id) }
  scope :by_cuisine, ->(cuisine_id) { includes(:cuisines).where("cuisines.id = ?", cuisine_id).references(:cuisines) }
  scope :by_category, ->(category_id) { includes(:categories).where("categories.id = ?", category_id).references(:categories) }
  scope :by_city, ->(city_id) {where city_id: city_id}
  scope :in_city, ->(city_name) { includes(:city).where("cities.name = ?", city_name).references(:city) }

  scope :by_site, ->(site_id) {where site_id: site_id}
  scope :in_site, ->(site_name) { includes(:site).where("sites.name = ?", site_name).references(:site) }

  # Scopes used in outlets#browse for SEO browse pages
  scope :in_category, ->(category_slug) { includes(:categories).where("categories.slug = ?", category_slug).references(:categories) }
  scope :in_location, ->(location_slug) { includes([:neighbourhood, :mall]).where("neighbourhoods.slug = :slug OR malls.slug = :slug", slug: location_slug).references([:neighbourhoods, :malls]) }
  scope :in_neighbourhood, ->(neighbourhood_slug) { includes(:neighbourhood).where('neighbourhoods.slug = ?', neighbourhood_slug).references(:neighbourhoods) }
  scope :in_cuisine, ->(cuisine_slug) { includes(:cuisines).where('cuisines.slug = ?', cuisine_slug).references(:cuisines) }
  scope :in_price, ->(price_slug) { includes(:price).where("prices.slug = ?", price_slug).references(:prices) }
  scope :include_metadata, ->(metadata_filter = {}){includes(:categories, :facilities, :cuisines, :mall, :price, :photos, :neighbourhood, :translations).where(metadata_filter)}
  scope :enabled, -> { where(enabled: true) }

  scope :with_reviews_in_last_x_days, ->(number_of_days) { includes(:reviews).where("reviews.created_at > ? AND LENGTH(reviews.content) >= ?", (number_of_days.to_i).days.ago, AppConfig.minimum_review_length).order("(count(reviews.outlet_id) * 2) + (select count(*) from follows where followable_type = 'Outlet' AND followable_id = outlets.id AND follows.created_at > '#{(number_of_days.to_i).days.ago}') DESC").group('outlets.id').references(:reviews) }

  scope :with_reviews_this_calendar_month, -> { includes(:reviews).where("MONTH(reviews.created_at) = ? AND YEAR(reviews.created_at) = ? AND LENGTH(reviews.content) >= ?", DateTime.now.month, DateTime.now.year, AppConfig.minimum_review_length).order("(count(reviews.outlet_id) * 2) + (select count(*) from follows where followable_type = 'Outlet' AND followable_id = outlets.id AND MONTH(follows.created_at) = '#{DateTime.now.month}' AND YEAR(follows.created_at) = '#{DateTime.now.year}') DESC").group('outlets.id').references(:reviews) }

  scope :without_nr, -> { where("reviews_rating IS NOT NULL AND reviews_rating > 0") }

  # MC Jan-14. Exclude categories from popular - Cafe, Fast Food, Ice Cream & Frozen Yoghurt, Mall Food Court, Outdoor Food Court, Street Food, Bubble Tea, Candy and Snack.
  # Also exclude the cheaper price points.
  scope :in_excluded_categories, -> { includes(:price).includes(:categories).where("categories.id IN (?) OR prices.id IN (?)", [3,9,11,28, 6, 7, 8, 28, 59], [11, 12, 13]).references(:price).references(:categories) }
  scope :enabled_for_popular, -> { where(enabled_for_popular: true, enabled: true) } # basically a column which matches in_excluded_categories which is updated after_save

  scope :recomended, -> {order("outlets.reviews_count DESC , outlets.reviews_rating DESC")}
  scope :recomended_with_same_cuisine_mall, ->(cuisine_ids, mall_id, limit=10) {recomended.joins(:cuisines).where("cuisines.id in (?) AND mall_id = ? AND reviews_rating is not NULL", cuisine_ids, mall_id ).limit(limit)}
  scope :recomended_with_same_category_mall, ->(category_ids, mall_id, limit=10) {recomended.joins(:categories).where("categories.id in (?) AND mall_id = ? AND reviews_rating is not NULL", category_ids, mall_id ).limit(limit)}
  scope :recomended_with_same_neighbourhood, ->(neighbourhood_id, limit=10) {recomended.where("neighbourhood_id = ? AND reviews_rating is not NULL", neighbourhood_id ).limit(limit)}

  scope :paying, -> { joins(:managers) }

  has_many :managers
  has_many :users, :through => :managers

  has_many :hours, :dependent => :destroy

  has_many :photos, :dependent => :destroy
  has_many :last_five_photos, -> { limit(5) }, :class_name => "Photo", :foreign_key => "outlet_id"
  has_many :active_promotions, -> { active_now }, :class_name => "Promotion", :foreign_key => "outlet_id"
  has_many :promotions, :dependent => :destroy
  has_and_belongs_to_many :cuisines
  has_and_belongs_to_many :categories
  has_and_belongs_to_many :facilities
  has_many :features, :dependent => :destroy
  has_many :segments, :through => :features
  has_and_belongs_to_many :menus

  has_many :media_reviews, -> { where('reviews.review_type = "media"').order("reviews.created_at DESC") }, :class_name => "Review", :foreign_key => "outlet_id"
  has_many :active_reviews, -> { where("reviews.enabled = 1 AND LENGTH(reviews.content) >= #{AppConfig.minimum_review_length}").order("reviews.created_at DESC") }, class_name: "Review", foreign_key: "outlet_id"

  has_many :reviews, -> {is_review}
  has_many :reviews_from_ratebar, -> { is_review_rating_bar },  class_name: 'Review'
  has_one :last_review, -> { is_review.limit(1) },  class_name: 'Review'
  has_many :visitors, class_name: 'Review'

  has_many :ratings
  has_many :card_promos, :dependent => :destroy
  has_many :banks, :through => :card_promos

  has_many :menu_items, :dependent => :destroy
  has_many :dishes, :through => :menu_items

  has_many :lists_outlets, :dependent => :destroy
  has_many :lists, :through => :lists_outlets

  belongs_to :neighbourhood
  belongs_to :city
  belongs_to :site
  belongs_to :mall
  belongs_to :price
  belongs_to :suggester, :class_name => "User"
  belongs_to :outlet_chain

  has_one :outlet_keyword

  has_many :checkins
  has_many :checkin_users, through: :checkins, source: :user

  has_many :sales_data, :class_name => 'SalesData'

  has_many :outlet_searches, :class_name => 'OutletSearches'

  has_many :events

  attr_accessor :suggestion_mode, :city_name, :merge_into, :uniq_name

  # below validaton won't run if suggestion_mode is not present. it will not break existing transactions
  validates :suggester_email, :email => true, :allow_blank => true
  validates :name, presence: true, if: Proc.new { |a| a.suggestion_mode.present? }
  validates :address1, presence: true, if: Proc.new { |a| a.suggestion_mode.present? }
  validates :suggester_email, presence: true, if: Proc.new { |a| a.suggestion_mode.present? }
  validates :city, presence: true

  reverse_geocoded_by :latitude, :longitude do |obj,results|
    if geo = results.first
      obj.latitude  = geo.latitude
      obj.longitude = geo.longitude
    else
      obj.latitude = obj.city.latitude
      obj.longitude = obj.city.longitude
    end
  end

  geocoded_by :address do |obj,results|
    if geo = results.first
      obj.zipcode = geo.postal_code
      # --- not updated for location coordinate if change address ---
      # obj.latitude = geo.latitude
      # obj.longitude = geo.longitude
    end
  end

  before_validation :preset_city
  after_validation :geocode, :if => lambda { |obj| !Rails.env.test? && !Rails.env.indo_dev? && obj.suggestion_mode.blank? && (obj.address1_changed? or obj.new_record?) }
  after_validation :reverse_geocode, :if => lambda { |obj| obj.latitude.blank? or obj.longitude.blank? }

  before_create :assign_meta_description_wording_id
  after_save :update_blurrily, :update_excluded_from_popular, :track_slug_into_redirect, :update_meta

  OUTLET_RATING_LEVEL = [
      "outlet.ratings.bad",
      "outlet.ratings.average",
      "outlet.ratings.good",
      "outlet.ratings.verygood",
      "outlet.ratings.excellent"]

  # AL Jun-4. Ramadhan 2015 search distance limit in kilometers unit.
  RAMADHAN_SEARCH_DISTANCE_LIMIT = 2

  ################################### Class Method ##################################
  class << self
    include AbraCommon

    # :my_network_only_for_user_id, Integer, find outlet that reviewed by friends
    # :tags, String tag format, find outlet under specific tag
    # :keyword, String , Fuzzy string ordering by name, address, categories, cuisines, neig, price, facil match the string
    # :keyword_strict, Boolean, default true, make the Fuzzy string ordering more strict, with exclude unmatch result
    # :assoc_params, Hash, find outlet where categories, cuisines, neig, price, facil match the hash
    # :chain, String, find outlet under specific Chain
    # :order, :latitude, :longitude, Sorting the result
    # :page, :per_page,
    # :disable_group_by_chain, Boolean, by default result grouped by chain, give it false to ignore
    def search(options = {})
      options.symbolize_keys!

      # each outlet should belongs to a chain, because we trying to gruoping outlet by chain
      # outlet without chain will not appear on the results
      results = Outlet.joins(:outlet_chain, :outlet_keyword)

      #----search for ramadhan event
      if options[:event_signature].eql? 'ramadhan'
        results = results.near([options[:latitude], options[:longitude]], options[:distance_limit])
      end

      #----only show outlet that reviewed by friends
      if options[:my_network_only_for_user_id].present?
        results = results.filter_by_user(options[:my_network_only_for_user_id])
      end

      #----search by tags
      if options[:tags].present?
        results = results.tagged_with(options[:tags], any: true)

        #remove tags form keyword
        options[:tags].each{ |tag|  options[:keyword].gsub!(/##{tag}|!!#{tag}/, '') }
      end

      #----search by keyword
      if options[:keyword].present? && options[:keyword_strict].present?
        # fuzzy string ordering
        results = results.like_wildcard_order(['outlet_keywords.keyword'], options[:keyword])
      else options[:keyword].present?
        #strict false
        results = results.like_wildcard_order(['outlet_keywords.keyword'], options[:keyword])
      end

      #----search by association
      if options[:assoc_params].present?
        results = results.find_by_relations(options[:assoc_params])
      end

      #----search on chain
      if options[:chain].present?
        results = results.where(outlet_chains: {slug: options[:chain]})
      end

      #----group by chain
      if options[:disable_group_by_chain].blank? && options[:chain].blank?
        results = results.select('outlets.*, count(DISTINCT outlets.id) as total_outlet').group('outlet_chains.slug')
      else
        results = results.select('outlets.*, 1 as total_outlet')
      end

      #----order
      results = results.order_by_type(options[:order], options[:latitude], options[:longitude])

      #----pagination
      results = results.paginate(page: options[:page], per_page: options[:per_page])

      #return
      results
    end


    def find_by_relations(relations)

      relations = relations.symbolize_keys
      #--> { neighbourhood: {slug: ['kuningan']} }

      relation_names = relations.collect{|relation_name, parameters| relation_name}
      #--> [:neighbourhood]

      relation_parameters = Hash[relations.collect{|relation_name, parameters| [relation_name.to_s.pluralize.to_sym, parameters]}]
      #--> { neighbourhoods: {slug: ['kuningan']} }

      #return
      joins(relation_names).where(relation_parameters)
    end

    def find_most_resto
      limit_number = 5
      return Outlet.order("reviews_count DESC, user_followers_count DESC, wishlist_count_stat DESC, mark_count_stat DESC, pageview_count_stat DESC").limit(limit_number)
    end

    def order_by_type(type, latitude = nil, longitude = nil)

      if type == 'distance' and latitude.blank? or type == 'distance' and longitude.blank?
        raise ArgumentError, 'latitude & longitude is required'
      elsif type == 'distance'
        #https://github.com/alexreisner/geocoder/blob/8c000fdf7ee7a8bac9b9bb17429e94d2b371f4da/lib/geocoder/stores/active_record.rb#L155
        distance = distance_sql(latitude, longitude, order: "distance")
        near([latitude, longitude], 1000000, order: "(#{distance}) ASC")
      else
        # AL Jun-4. In order to avoid any other sort, especially geocoder than sorts by distance as default behaviour.
        reorder(sort_fields(type))
      end
    end

    def sort_fields(param_sort)

      if param_sort.present?
        case param_sort.to_sym
        when :alpha_low
          order_by = "outlets.name ASC"
        when :alpha_high
          order_by = "outlets.name DESC"
        when :location
          order_by = "neighbourhoods.name ASC"
        when :cuisine
          query += 'cuisines.id IS NOT NULL AND '
          order_by = "cuisines.label ASC"
        when :cheapest
          order_by = "outlets.price_id ASC"
        when :dearest
          order_by = "outlets.price_id DESC"
        when :top_rated
          order_by = "outlets.reviews_rating DESC"
        end
      else
        order_by = "outlets.name ASC"
      end
      return order_by
    end

    def my_network_only(user_id = false)
      users = User.where(id: user_id)
      return where(nil) if users.blank?
      my_network_only_ids = Review.where(user_id: users.first.my_network.map(&:id)).map(&:outlet_id)
      where(id: my_network_only_ids)
    end

    def popular(site_id = AppConfig::default_site_id, user = false)
      #TODO:  show restaurants based on my network of speficied user

      the_popular = Outlet.enabled_for_popular.without_nr.by_site(site_id).with_reviews_this_calendar_month
      expand_num_day = 30

      until the_popular.length >= 10 do
        the_popular = Outlet.enabled_for_popular.without_nr.by_site(site_id).with_reviews_in_last_x_days(expand_num_day)
        expand_num_day = expand_num_day + 30
        break if expand_num_day > 360
      end

      the_popular
    end

    def list_of_outlet_foursquare_venue
      Outlet.select("TRIM(foursquare_venue_id) as foursquare_venue_id").where("foursquare_venue_id IS NOT NULL and foursquare_venue_id <> ''").map(&:foursquare_venue_id)
    end

    def cuisine_list_by_resto(cuisine)
      outlet_ids = []
      outlet_cuisines = {}

      cuisine.each do |c|
        c[:content].each do |o|
          outlet_ids << o.id
        end
      end
    end

    def resto_groupby_cuisine(cuisine_category)
      cuisine = []
      # step-1
      # cuisine << {:key => "asian", :content => Cuisine.find(2).outlets.order("level DESC").limit(6)}
      # cuisine << {:key => "western", :content => Cuisine.find(41).outlets.order("level DESC").limit(6)}
      # cuisine << {:key => "european", :content => Cuisine.find(10).outlets.order("level DESC").limit(6)}
      # cuisine << {:key => "chinese", :content => Cuisine.find(7).outlets.order("level DESC").limit(6)}
      # cuisine << {:key => "japanese", :content => Cuisine.find(21).outlets.order("level DESC").limit(6)}
      # cuisine << {:key => "indonesian", :content => Cuisine.find(17).outlets.order("level DESC").limit(6)}

      cuisine_category.each do |cc|
        cuisine << {:key => cc.attributes.has_key?('label') ? cc.label.downcase : cc.name.downcase, :content => cc.outlets.order("level DESC").limit(6)}
      end

      return cuisine
    end

    def cuisine_list_by_resto(cuisine)
      outlet_ids = []
      outlet_cuisines = {}

      cuisine.each do |c|
        c[:content].each do |o|
          outlet_ids << o.id
        end
      end

      q = Cuisine.joins('INNER JOIN cuisines_outlets ON cuisines.id = cuisines_outlets.cuisine_id').where('cuisines_outlets.outlet_id IN (?)', outlet_ids).pluck(:label, :outlet_id)

      q.each do |cuisine_outlet|
        outlet_cuisines[cuisine_outlet.last] = [] unless outlet_cuisines.has_key?(cuisine_outlet.last)
        outlet_cuisines[cuisine_outlet.last] << cuisine_outlet.first
      end

      return outlet_cuisines
    end

    def to_js_array(user = false, site_id = AppConfig::default_site_id)
      authorize_label = "user"
      authorize_label = "admin" if user.present? && user.admin
      authorize_label = "manager" if user.present? && user.manager

      terms = []

      result = Rails.cache.fetch("list_outlets_on_#{authorize_label}_for_#{site_id}_cache", :expires_in => 1.hour) do

        # MC Jan-14. the city variable does not seem to be passed in to this cache.fetch, so we set it here.
        city_ids = City.where(site_id: site_id).map(&:id)
        outlets = self.where(city_id: city_ids)

        if user && user.admin
          outlets = outlets.order(:name).all

        elsif user && user.manager && !user.outlets.blank?
          outlets = outlets.order(:name).where(:enabled => true).all
          outlets.concat( user.outlets ).sort!{ |a,b| a.name <=> b.name }

        else
          outlets = outlets.order(:name).where(:enabled => true).load
        end

        terms.concat outlets.map(&:name)

        terms.concat Neighbourhood.where(city_id: city_ids).pluck(:name)

        terms.concat Cuisine.pluck(:label)

        terms
      end

      terms = result.uniq{|t| t}
      output = terms.to_json

      return output
    end

    def list_of_neighbourhoods
      return self.select("DISTINCT(neighbourhood)").reject!{|n| n.neighbourhood.blank? }
    end
  end
  #end of class << self

  ### testing foursquare venue
  # During still testing will do by staff user Abraresto,
  # we will set to 4square venue `https://foursquare.com/v/halimun--abraresto/5412e8cc498e4970199d8e1b`
  # def foursquare_venue_id
  #   "5412e8cc498e4970199d8e1b"
  # end

  def uniq_name
    return "#{self.name} - #{self.city.name}"
  end

  def address
    [address1, city.try(:name), zipcode, country].compact.join(', ')
  end

  def slug_candidates
    [
      :name,
      [:name, try(:mall).try(:name)],
      [:name, try(:neighbourhood).try(:name)],
      [:name, try(:neighbourhood).try(:name), :id]
    ]
  end

  def should_generate_new_friendly_id?
    name_changed? || mall_id_changed? || neighbourhood_id_changed? || super
  end

  def highlight_resto_picture

    return self.photos[0].image.url(:micro) if self.photos.present?
    return "#{AppConfig::theme}/defaults/placeholder-outlet.jpg"
  end

  def calculate_reviews_rating

    ratings_and_reviews = (self.reviews.without_nr + self.ratings + self.reviews_from_ratebar ).uniq

    case ratings_and_reviews.map(&:rating).sum == 0
    when true
      return 0
    else

      denominator = ratings_and_reviews.map(&:rating).sum
      quantifier  = ratings_and_reviews.size

      return (denominator/quantifier).round(1)
    end

  end

  def facility_listview
    return [] if self.facilities.blank?
    return self.facilities.pluck(:name)
  end

  def most_recent_review_date
    if !active_reviews.blank?
      active_reviews.first.created_at
    else
      Time.now - 10.years # set a long past date
    end

  end

  def featured?(segment)
    features.find_by_segment_id(segment)
  end

  def unfeature!(segment)
    features.find_by_segment_id(segment).destroy
  end

  def meta_cuisines
    content = ''
    cuisines.each do |c|
      content << "#{c.label} "
    end
    return content
  end

  def update_attributes(attrs)
    unless attrs[:cuisine].blank?
      cuisines.clear
      attrs[:cuisine].each do |c|
        cuisines << Cuisine.find(c)
      end
      attrs.delete(:cuisine)

    end

    unless attrs[:category].blank?
      categories.clear
      attrs[:category].each do |c|
        categories << Category.find(c)
      end
      attrs.delete(:category)

    end

    unless attrs[:facility].blank?
      facilities.clear
      attrs[:facility].each do |f|
        facilities << Facility.find(f)
      end
      attrs.delete(:facility)

    end

    unless attrs[:users].blank?
      users.clear
      attrs[:users].each do |u|
        users << User.friendly.find(u)
      end
      attrs.delete(:users)

    end

    unless attrs[:segment].blank?

      attrs[:segment].each_pair do |sid, feat|
        if feat[:description].blank? && feat[:file].blank?
          f = Feature.find_by_outlet_id_and_segment_id(id,sid)
          Feature.destroy(f) unless f.blank? || f.image.file?
        else

          f = Feature.find_or_create_by_outlet_id_and_segment_id(id, sid)
          f[:description] = feat[:description]
          f.image = feat[:file] unless feat[:file].blank?
          f.save

        end

      end
      attrs.delete(:segment)

    end

    unless attrs[:deletefeature].blank?
      attrs[:deletefeature].each do |f|
        feat = Feature.find(f)
        if feat.description.blank?
          Feature.destroy(f)
        else
          feat.image = nil
          feat.save
        end

      end
      attrs.delete(:deletefeature)
    end

    # Promotions
    unless attrs[:promotion].blank?
      promotions << Promotion.new(:upload => attrs[:promotion])
      attrs.delete(:promotion)
    end

    unless attrs[:deletepromo].blank?
      attrs[:deletepromo].each do |d|
        promo = Promotion.find(d)
        promotions.delete(promo)

      end
      attrs.delete(:deletepromo)
    end

    # Menus
    unless attrs[:menu].blank?
      if !attrs[:menu][:image].blank? || !attrs[:menu][:url].blank?
        menu_title = attrs[:menu][:title] || "Menu"
        menus << Menu.new(:image => attrs[:menu][:image], :title => menu_title, :url => attrs[:menu][:url])

      end
      attrs.delete(:menu)
    end

    unless attrs[:deletemenu].blank?
      attrs[:deletemenu].each do |d|
        menu = Menu.find(d)
        menus.delete(menu)

      end
      attrs.delete(:deletemenu)
    end

    super
  end

  def find_recomended_outlet
    # case-1
    if self.cuisines.present? && self.mall.present?
      result = Outlet.recomended_with_same_cuisine_mall(self.cuisines.map(&:id).join(','), self.mall_id, 2) - [self]
      return result unless result.blank?

    # case-2
    elsif self.categories.present? && self.mall.present?
      result = Outlet.recomended_with_same_category_mall(self.categories.map(&:id).join(','), self.mall_id, 2) - [self]
      return result unless result.blank?

    # case-3
    elsif self.neighbourhood.present?
      result = Outlet.recomended_with_same_neighbourhood(self.neighbourhood_id, 2) - [self]
      return result unless result.blank?
    end

    []
  end

  def outlet_category_average
    number_reviews = self.active_reviews

    avg = {}
    avg[:food]      = (number_reviews.map(&:food).compact.sum.to_f / number_reviews.size).round(1)
    avg[:ambiance]  = (number_reviews.map(&:ambiance).compact.sum.to_f / number_reviews.size).round(1)
    avg[:service]   = (number_reviews.map(&:service).compact.sum.to_f / number_reviews.size ).round(1)

    return avg
  end

  def top_tags
    # Exclude "new" tag from top tags
    self.tag_counts.where.not(name: "new").order('count DESC').limit(2)
  end

  def latest_reviews(current_user = nil)
    if current_user.present?
      mynetwork_ids = current_user.following_users.map(&:id)
      mynetwork_ids << current_user.id

      self.reviews.order("field(reviews.user_id, #{mynetwork_ids.join(',')})")
    else
      self.reviews
    end
  end

  def latest_visitors(current_user = nil)
    if current_user.present?
      mynetwork_ids = current_user.following_users.map(&:id)
      mynetwork_ids << current_user.id

      self.visitors.not_anon.where(user_id: mynetwork_ids).order("field(reviews.user_id, #{mynetwork_ids.join(',')})")
    else
      self.visitors.not_anon
    end
  end

  def this_user_checkedin?(user_id)
    checkin = Checkin.find_by_user_id_and_outlet_id( user_id, self.id )
    checkin.present? and checkin.updated_at >= 1.hour.ago
  end

  # def outlet_clear_cache
  #   # Clear cache for fetch data from outlets
  #   Rails.cache.delete_matched("list_outlets_on_*")
  # end

  def update_blurrily
    common_blurrily_client_update(self.name, self.id)
  end

  def update_excluded_from_popular
    #if Outlet.in_excluded_categories.pluck(:id).include?(self.id)
    # update for speed -R
    if Outlet.in_excluded_categories.where(id: self.id).present?
      self.update_column(:enabled_for_popular, false)
    else
      self.update_column(:enabled_for_popular, true)
    end

  end

  def track_slug_into_redirect
    if self.slug_was != self.slug
      Outlet.track_into_redirect_table(redirect_type: self.class.name, redirect_id: self.id, old_slug: self.slug_was, new_slug: self.slug)
    end

  end

  def preset_city

    # MC May-14. City_name is provided by Google Maps API. We try to match
    # because sometimes it returns "Jakarta Selatan", which is not a city in the Abraresto system.
    default_cities = City.where(site_id: AppConfig::default_site_id)

    if self.city.blank? && self.city_name.present? && !default_cities.map(&:name).include?(self.city_name.capitalize)
      city = City.where("name LIKE ?", self.city_name.first(6).downcase + '%').first
      self.city = city || City.find(AppConfig::default_city_id)
    end

    # In testing the AppConfig::default_city_id is not always a valid city, so just set to the first available city.
    self.city_id = ( City.exists?(AppConfig::default_city_id) ) ? AppConfig::default_city_id : City.first.id if self.city.blank?

  end


  def display_photo

    # Display a photo in order of either:
    # a) featurable admin
    # b) thumbable admin
    # c) featurable user
    # d) thumbable user
    # e) any admin photo.
    # f) any picture from the outlet cuisines

    display_photo = if self.photos.admin_submitted.featurable.present?
      self.photos.admin_submitted.featurable.order_by_rand.first

    elsif self.photos.admin_submitted.thumbable.present?
      self.photos.admin_submitted.thumbable.order_by_rand.first

    elsif self.photos.user_submitted.featurable.present?
      self.photos.enabled.user_submitted.featurable.order_by_rand.first  # admin must approve the photo for showing

    elsif self.photos.user_submitted.thumbable.present?
      self.photos.enabled.user_submitted.thumbable.order_by_rand.first   # admin must approve the photo for showing

    elsif self.photos.admin_submitted.present?
      self.photos.admin_submitted.order_by_rand.first

    elsif self.cuisines.present?
      self.cuisines.sample.pictures.sample

    else
      nil
    end

  end

  def assign_meta_description_wording_id
    self.meta_description_wording_id = rand(1..10)
  end

  def update_meta

    ActiveRecord::Base.connection.execute "DELETE FROM `outlet_keywords` WHERE outlet_id = #{self.class.sanitize(id)}"

    sql = "INSERT INTO outlet_keywords (outlet_id, keyword)
  SELECT outlets.id, CONCAT(COALESCE(outlets.name), ' ',COALESCE(outlets.address1), '',IF(categories.name IS NOT NULL, GROUP_CONCAT(DISTINCT categories.name SEPARATOR ' '), ''), ' ', IF(cuisines.label IS NOT NULL, GROUP_CONCAT(DISTINCT cuisines.label SEPARATOR ' '), ''),  ' ',IF(malls.name IS NOT NULL, GROUP_CONCAT(DISTINCT malls.name SEPARATOR ' '), ''),  ' ',IF(neighbourhoods.name IS NOT NULL, GROUP_CONCAT(DISTINCT neighbourhoods.name SEPARATOR ' '), ''),  ' ',IF(facilities.name IS NOT NULL, GROUP_CONCAT(DISTINCT facilities.name SEPARATOR ' '), ''),  ' ',IF(prices.range IS NOT NULL, GROUP_CONCAT(DISTINCT prices.range SEPARATOR ' '), '') ) as meta_search FROM `outlets` LEFT JOIN `categories_outlets` ON `categories_outlets`.`outlet_id` = `outlets`.`id` LEFT JOIN `categories` ON `categories`.`id` = `categories_outlets`.`category_id` LEFT JOIN `cuisines_outlets` ON `cuisines_outlets`.`outlet_id` = `outlets`.`id` LEFT JOIN `cuisines` ON `cuisines`.`id` = `cuisines_outlets`.`cuisine_id` LEFT JOIN `neighbourhoods` ON `neighbourhoods`.`id` = `outlets`.`neighbourhood_id` LEFT JOIN `malls` ON `malls`.`id` = `outlets`.`mall_id` LEFT JOIN `facilities_outlets` ON `facilities_outlets`.`outlet_id` = `outlets`.`id` LEFT JOIN `facilities` ON `facilities`.`id` = `facilities_outlets`.`facility_id` LEFT JOIN `prices` ON `prices`.`id` = `outlets`.`price_id` WHERE outlets.id = #{self.class.sanitize(id)} GROUP BY outlets.id"

      ActiveRecord::Base.connection.execute sql
  end
end
Geocoder.configure(

  # geocoding service (see below for supported options):
  #:lookup => :yandex,

  # to use an API key:
  #:api_key => "...",

  # geocoding service request timeout, in seconds (default 3):
  :timeout => 5,

  # set default units to kilometers:
  :units => :km,

  # caching (see below for details):
  #:cache => Redis.new,
  #:cache_prefix => "..."

)
  def ajax_get_address
    render text: Geocoder.address([params[:latitude], params[:longitude]])
  end