class City < ApplicationRecord extend FriendlyId friendly_id :slug_candidates, use: :slugged belongs_to :country belongs_to :state, optional: true has_many :weather_arts, dependent: :destroy has_many :visits, class_name: "Ahoy::Visit", foreign_key: :city_id has_many :events, class_name: "Ahoy::Event", foreign_key: :city_id delegate :region, to: :country validates :name, presence: true validates :latitude, presence: true validates :longitude, presence: true delegate :region, to: :country scope :by_region, ->(region_id) { joins(:country).where(countries: { region_id: region_id }) } scope :by_country, ->(country_id) { where(country_id: country_id) } scope :active, -> { where(active: true) } scope :inactive, -> { where(active: false) } scope :by_popularity, -> { if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite" joins("LEFT JOIN ahoy_events ON json_extract(ahoy_events.properties, '$.city_id') = cities.id AND json_extract(ahoy_events.properties, '$.event_type') = 'city_view'") .group("cities.id") .select("cities.*, COUNT(ahoy_events.id) as visit_count") .order("visit_count DESC") else joins("LEFT JOIN ahoy_events ON (ahoy_events.properties::jsonb->>'city_id')::integer = cities.id AND ahoy_events.properties::jsonb->>'event_type' = 'city_view'") .group("cities.id") .select("cities.*, COUNT(ahoy_events.id) as visit_count") .order("visit_count DESC") end } scope :least_popular_active, ->(limit = 100) { if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite" active .joins("LEFT JOIN ahoy_events ON json_extract(ahoy_events.properties, '$.city_id') = cities.id AND json_extract(ahoy_events.properties, '$.event_type') = 'city_view'") .group("cities.id") .select("cities.*, COUNT(ahoy_events.id) as visit_count") .order("visit_count ASC, cities.name ASC").limit(limit) else active .joins("LEFT JOIN ahoy_events ON (ahoy_events.properties::jsonb->>'city_id')::integer = cities.id AND ahoy_events.properties::jsonb->>'event_type' = 'city_view'") .group("cities.id") .select("cities.*, COUNT(ahoy_events.id) as visit_count") .order("visit_count ASC, cities.name ASC").limit(limit) end } scope :most_popular_inactive, ->(limit = 100) { if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite" where(active: false) .joins("LEFT JOIN ahoy_events ON json_extract(ahoy_events.properties, '$.city_id') = cities.id AND json_extract(ahoy_events.properties, '$.event_type') = 'city_view'") .group("cities.id") .select("cities.*, COUNT(ahoy_events.id) as visit_count") .order("COUNT(ahoy_events.id) DESC, cities.name ASC").limit(limit) else where(active: false) .joins("LEFT JOIN ahoy_events ON (ahoy_events.properties::jsonb->>'city_id')::integer = cities.id AND ahoy_events.properties::jsonb->>'event_type' = 'city_view'") .group("cities.id") .select("cities.*, COUNT(ahoy_events.id) as visit_count") .order("COUNT(ahoy_events.id) DESC, cities.name ASC").limit(limit) end } def to_s name end def slug_candidates [ :name, [ :country, :name ] ] end def localized_name I18n.t("cities.#{name.parameterize.underscore}", default: name) end def full_name "#{name}, #{country}" end def should_generate_new_friendly_id? name_changed? || super end def self.ransackable_associations(auth_object = nil) [ "weather_arts" ] end def self.ransackable_attributes(auth_object = nil) [ "active", "country_id", "created_at", "id", "id_value", "last_image_generation", "last_weather_fetch", "latitude", "longitude", "name", "priority", "region", "slug", "timezone", "updated_at" ] end def last_weather_fetch # latest_weather_art&.created_at Rails.cache.fetch("city/#{id}/last_weather_fetch", expires_in: 1.hour) do latest_weather_art&.created_at end end def last_image_generation # latest_weather_art&.image&.created_at Rails.cache.fetch("city/#{id}/last_image_generation", expires_in: 1.hour) do latest_weather_art&.image&.created_at end end def latest_weather_art weather_arts.order(weather_date: :desc).first end def view_count(period = :day) start_time = case period when :day 1.day.ago when :week 7.days.ago when :month 1.month.ago when :year 1.year.ago else 1.year.ago end if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite" # Ahoy::Event.where("json_extract(properties, '$.event_type') = 'city_view' AND json_extract(properties, '$.city_id') = ?", self.id).count Ahoy::Event .where("time >= ?", start_time) .where("json_extract(properties, '$.event_type') = 'city_view' AND json_extract(properties, '$.city_id') = ?", self.id) .count else # Ahoy::Event.where("properties::jsonb->>'event_type' = 'city_view' AND (properties::jsonb->>'city_id')::integer = ?", self.id).count Ahoy::Event .where("time >= ?", start_time) .where("properties::jsonb->>'event_type' = 'city_view' AND (properties::jsonb->>'city_id')::integer = ?", self.id) .count end end end