- Change `by_popularity` method in `City` model to use `start_time` for filtering views. - Update `by_popularity` in `WeatherArt` model to limit results and incorporate the correct popularity calculation. - Adjust `HomeController` to define `@popular_shuffle_arts` and `@latest_arts` with appropriate scopes. These changes improve the accuracy of popularity retrieval for both cities and weather arts, simplifying the logic in the controller and leveraging updated model methods for performance.
190 lines
6.1 KiB
Ruby
190 lines
6.1 KiB
Ruby
class City < ApplicationRecord
|
|
extend FriendlyId
|
|
friendly_id :slug_candidates, use: :slugged
|
|
belongs_to :country, optional: true
|
|
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) }
|
|
|
|
# 在 City 模型中
|
|
scope :by_popularity, ->(period = :year, limit = 100) {
|
|
# 根据时间周期确定时间范围
|
|
start_time =
|
|
case period.to_sym
|
|
when :day
|
|
1.day.ago
|
|
when :week
|
|
1.week.ago
|
|
when :month
|
|
1.month.ago
|
|
when :year
|
|
1.year.ago
|
|
else
|
|
1.year.ago
|
|
end
|
|
|
|
# 根据数据库类型构建不同的查询
|
|
base_query = if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite"
|
|
joins(<<-SQL.squish)
|
|
LEFT JOIN ahoy_events ON
|
|
json_extract(ahoy_events.properties, '$.city_id') = cities.id
|
|
AND json_extract(ahoy_events.properties, '$.event_type') = 'city_view'
|
|
AND ahoy_events.time > '#{start_time}'
|
|
SQL
|
|
else
|
|
joins(<<-SQL.squish)
|
|
LEFT JOIN ahoy_events ON
|
|
(ahoy_events.properties::jsonb->>'city_id')::integer = cities.id
|
|
AND ahoy_events.properties::jsonb->>'event_type' = 'city_view'
|
|
AND ahoy_events.time > '#{start_time}'
|
|
SQL
|
|
end
|
|
|
|
base_query
|
|
.group("cities.id")
|
|
.select("cities.*, COUNT(ahoy_events.id) as visit_count")
|
|
.order("visit_count DESC")
|
|
.limit(limit)
|
|
}
|
|
|
|
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
|
|
}
|
|
scope :search_by_name, ->(query) {
|
|
return all if query.blank?
|
|
|
|
decoded_query = URI.decode_www_form_component(query).downcase
|
|
|
|
where(
|
|
"LOWER(cities.name) LIKE :query", query: "%#{decoded_query}%"
|
|
)
|
|
}
|
|
|
|
|
|
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
|