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