- Remove deprecated `image_with_watermark` attachment. - Introduce `webp_image`, `preview_image`, and `watermarked_image` methods in `WeatherArt` model for optimized image formats. - Update views to use new image variants, including webp and previews, improving loading times and visual quality. - Ensure images are processed with relevant attributes such as quality and dimensions for better performance and user experience. These changes enhance the image handling capabilities of the application by ensuring images are served in a more efficient format (WebP) and with improved resizing options, leading to better performance overall.
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
|