Compare commits
20 Commits
a895216bda
...
62bfe8888e
Author | SHA1 | Date | |
---|---|---|---|
62bfe8888e | |||
138d610c3a | |||
c332230709 | |||
f0f94de528 | |||
069b6d4a4f | |||
2a0226eb68 | |||
8cd4c50024 | |||
4020f89271 | |||
29de36f5fb | |||
2e438166ee | |||
bf2ff282bb | |||
ce5d09b621 | |||
c68fecf3fa | |||
16ee512b0c | |||
111fd85ebb | |||
59a3f792c6 | |||
9e666310cf | |||
cc74145033 | |||
3e713a9b26 | |||
263c85486c |
@ -1,4 +1,5 @@
|
|||||||
ActiveAdmin.register AdminUser do
|
ActiveAdmin.register AdminUser do
|
||||||
|
menu label: "AdminUser Manager", parent: "系统管理"
|
||||||
permit_params :email, :password, :password_confirmation
|
permit_params :email, :password, :password_confirmation
|
||||||
|
|
||||||
index do
|
index do
|
||||||
|
83
app/admin/ahoy_dashboard.rb
Normal file
83
app/admin/ahoy_dashboard.rb
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
ActiveAdmin.register_page "Ahoy Dashboard" do
|
||||||
|
menu label: "总览", parent: "数据统计", priority: 1
|
||||||
|
page_action :toggle_city_status, method: :post do
|
||||||
|
city = City.find(params[:city_id])
|
||||||
|
city.update(active: !city.active)
|
||||||
|
redirect_back(fallback_location: admin_dashboard_path, notice: "城市状态已更新")
|
||||||
|
end
|
||||||
|
|
||||||
|
content title: "总览" do
|
||||||
|
columns do
|
||||||
|
column do
|
||||||
|
panel "访问统计" do
|
||||||
|
para "总访问量: #{Ahoy::Visit.count}"
|
||||||
|
para "总事件数: #{Ahoy::Event.count}"
|
||||||
|
para "独立访客数: #{Ahoy::Visit.distinct.count(:visitor_token)}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
column do
|
||||||
|
panel "热门城市" do
|
||||||
|
table_for City.by_popularity.limit(10) do
|
||||||
|
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
|
||||||
|
column("访问量") { |city| city.view_count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
column do
|
||||||
|
panel "热门天气艺术" do
|
||||||
|
table_for WeatherArt.by_popularity.limit(10) do
|
||||||
|
column("作品") { |art| link_to(art.to_s, admin_weather_art_path(art)) }
|
||||||
|
column("访问量") { |art| art.view_count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
columns do
|
||||||
|
column do
|
||||||
|
panel "最冷门活跃城市" do
|
||||||
|
table_for City.least_popular_active.limit(10) do
|
||||||
|
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
|
||||||
|
column("访问量") { |city| city.view_count }
|
||||||
|
column("状态") { |city| status_tag(city.active? ? "活跃" : "停用") }
|
||||||
|
# column("状态") { |city| status_tag(city.active? ? "活跃" : "停用") }
|
||||||
|
column("操作") { |city|
|
||||||
|
button_to "停用",
|
||||||
|
admin_ahoy_dashboard_toggle_city_status_path(city_id: city.id),
|
||||||
|
method: :post,
|
||||||
|
data: { confirm: "确定要停用 #{city.name} 吗?" }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
column do
|
||||||
|
panel "热门未活跃城市" do
|
||||||
|
table_for City.most_popular_inactive.limit(10) do
|
||||||
|
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
|
||||||
|
column("访问量") { |city| city.view_count }
|
||||||
|
column("状态") { |city| status_tag(city.active? ? "活跃" : "停用") }
|
||||||
|
column("所属区域") { |city| city.country.region.name }
|
||||||
|
column("操作") { |city|
|
||||||
|
button_to "激活",
|
||||||
|
admin_ahoy_dashboard_toggle_city_status_path(city_id: city.id),
|
||||||
|
method: :post,
|
||||||
|
data: { confirm: "确定要激活 #{city.name} 吗?" }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# 添加一个事件列表面板
|
||||||
|
panel "最近事件" do
|
||||||
|
table_for Ahoy::Event.order(time: :desc).limit(10) do
|
||||||
|
column :time
|
||||||
|
column :name
|
||||||
|
column :properties
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,4 +1,5 @@
|
|||||||
ActiveAdmin.register Ahoy::Event do
|
ActiveAdmin.register Ahoy::Event do
|
||||||
|
menu label: "事件统计", parent: "数据统计"
|
||||||
# See permitted parameters documentation:
|
# See permitted parameters documentation:
|
||||||
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
|
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
|
||||||
#
|
#
|
||||||
@ -13,7 +14,7 @@ ActiveAdmin.register Ahoy::Event do
|
|||||||
# permitted << :other if params[:action] == 'create' && current_user.admin?
|
# permitted << :other if params[:action] == 'create' && current_user.admin?
|
||||||
# permitted
|
# permitted
|
||||||
# end
|
# end
|
||||||
menu priority: 101, label: "事件统计"
|
# menu priority: 101, label: "事件统计"
|
||||||
|
|
||||||
actions :index
|
actions :index
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
ActiveAdmin.register Ahoy::Visit do
|
ActiveAdmin.register Ahoy::Visit do
|
||||||
|
menu label: "访客统计", parent: "数据统计"
|
||||||
# See permitted parameters documentation:
|
# See permitted parameters documentation:
|
||||||
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
|
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
|
||||||
#
|
#
|
||||||
@ -14,7 +15,7 @@ ActiveAdmin.register Ahoy::Visit do
|
|||||||
# permitted
|
# permitted
|
||||||
# end
|
# end
|
||||||
|
|
||||||
menu priority: 100, label: "访问统计"
|
# menu priority: 100, label: "访问统计"
|
||||||
|
|
||||||
actions :index
|
actions :index
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
ActiveAdmin.register City do
|
ActiveAdmin.register City do
|
||||||
|
menu label: "City Manager", parent: "系统管理"
|
||||||
controller do
|
controller do
|
||||||
def find_resource
|
def find_resource
|
||||||
scoped_collection.friendly.find(params[:id])
|
scoped_collection.friendly.find(params[:id])
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
ActiveAdmin.register Country do
|
ActiveAdmin.register Country do
|
||||||
|
menu label: "Country Manager", parent: "系统管理"
|
||||||
controller do
|
controller do
|
||||||
def find_resource
|
def find_resource
|
||||||
scoped_collection.friendly.find(params[:id])
|
scoped_collection.friendly.find(params[:id])
|
||||||
|
@ -37,9 +37,11 @@ ActiveAdmin.register_page "Dashboard" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
columns do
|
||||||
column do
|
column do
|
||||||
panel "冷门活跃城市" do
|
panel "最冷门活跃城市" do
|
||||||
table_for City.least_popular_active.limit(10) do
|
table_for City.least_popular_active.limit(10) do
|
||||||
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
|
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
|
||||||
column("访问量") { |city| city.view_count }
|
column("访问量") { |city| city.view_count }
|
||||||
@ -47,6 +49,17 @@ ActiveAdmin.register_page "Dashboard" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
column do
|
||||||
|
panel "热门未活跃城市" do
|
||||||
|
table_for City.most_popular_inactive.limit(10) do
|
||||||
|
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
|
||||||
|
column("访问量") { |city| city.view_count }
|
||||||
|
column("状态") { |city| status_tag(city.active? ? "活跃" : "停用") }
|
||||||
|
column("所属区域") { |city| city.country.region.name }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# 添加一个事件列表面板
|
# 添加一个事件列表面板
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
ActiveAdmin.register Region do
|
ActiveAdmin.register Region do
|
||||||
|
menu label: "Region Manager", parent: "系统管理"
|
||||||
# See permitted parameters documentation:
|
# See permitted parameters documentation:
|
||||||
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
|
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
|
||||||
#
|
#
|
||||||
|
98
app/admin/sidekiq_jobs.rb
Normal file
98
app/admin/sidekiq_jobs.rb
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# app/admin/sidekiq_tasks.rb
|
||||||
|
ActiveAdmin.register_page "Sidekiq Tasks" do
|
||||||
|
# menu label: "Sidekiq Tasks", priority: 99
|
||||||
|
menu label: "Sidekiq Tasks Manager", parent: "系统管理"
|
||||||
|
|
||||||
|
content title: "Sidekiq Tasks Management" do
|
||||||
|
div class: "sidekiq-tasks" do
|
||||||
|
panel "Manual Task Execution" do
|
||||||
|
div class: "task-buttons" do
|
||||||
|
div class: "task-button" do
|
||||||
|
h3 "Generate Weather Arts"
|
||||||
|
form action: admin_sidekiq_tasks_run_task_path, method: :post do
|
||||||
|
input type: "hidden", name: "authenticity_token", value: form_authenticity_token
|
||||||
|
input type: "hidden", name: "task", value: "GenerateWeatherArtsWorker"
|
||||||
|
select name: "city_id" do
|
||||||
|
City.all.map do |city|
|
||||||
|
option city.name, value: city.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
input type: "submit", value: "Run Task", class: "button"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
div class: "task-button" do
|
||||||
|
h3 "Batch Generate Weather Arts"
|
||||||
|
form action: admin_sidekiq_tasks_run_task_path, method: :post do
|
||||||
|
input type: "hidden", name: "authenticity_token", value: form_authenticity_token
|
||||||
|
input type: "hidden", name: "task", value: "BatchGenerateWeatherArts"
|
||||||
|
input type: "submit", value: "Run Task", class: "button"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
div class: "task-button" do
|
||||||
|
h3 "Refresh Sitemap"
|
||||||
|
form action: admin_sidekiq_tasks_run_task_path, method: :post do
|
||||||
|
input type: "hidden", name: "authenticity_token", value: form_authenticity_token
|
||||||
|
input type: "hidden", name: "task", value: "RefreshSitemapWorker"
|
||||||
|
input type: "submit", value: "Run Task", class: "button"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
div class: "task-button" do
|
||||||
|
h3 "Clean Ahoy Data"
|
||||||
|
form action: admin_sidekiq_tasks_run_task_path, method: :post do
|
||||||
|
input type: "hidden", name: "authenticity_token", value: form_authenticity_token
|
||||||
|
input type: "hidden", name: "task", value: "CleanAhoyDataWorker"
|
||||||
|
input type: "submit", value: "Run Task", class: "button"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
panel "Sidekiq Statistics" do
|
||||||
|
stats = Sidekiq::Stats.new
|
||||||
|
table class: "sidekiq-stats" do
|
||||||
|
tr do
|
||||||
|
th "Processed Jobs"
|
||||||
|
td stats.processed
|
||||||
|
end
|
||||||
|
tr do
|
||||||
|
th "Failed Jobs"
|
||||||
|
td stats.failed
|
||||||
|
end
|
||||||
|
tr do
|
||||||
|
th "Enqueued Jobs"
|
||||||
|
td stats.enqueued
|
||||||
|
end
|
||||||
|
tr do
|
||||||
|
th "Scheduled Jobs"
|
||||||
|
td stats.scheduled_size
|
||||||
|
end
|
||||||
|
tr do
|
||||||
|
th "Retry Set Size"
|
||||||
|
td stats.retry_size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
page_action :run_task, method: :post do
|
||||||
|
task_name = params[:task]
|
||||||
|
city_id = params[:city_id]
|
||||||
|
|
||||||
|
case task_name
|
||||||
|
when "BatchGenerateWeatherArts"
|
||||||
|
BatchGenerateWeatherArtsWorker.perform_async
|
||||||
|
when "GenerateWeatherArtsWorker"
|
||||||
|
GenerateWeatherArtWorker.perform_async(city_id)
|
||||||
|
when "RefreshSitemapWorker"
|
||||||
|
RefreshSitemapWorker.perform_async
|
||||||
|
when "CleanAhoyDataWorker"
|
||||||
|
CleanAhoyDataWorker.perform_async
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_sidekiq_tasks_path, notice: "Task #{task_name} has been queued"
|
||||||
|
end
|
||||||
|
end
|
@ -1,4 +1,5 @@
|
|||||||
ActiveAdmin.register WeatherArt do
|
ActiveAdmin.register WeatherArt do
|
||||||
|
menu label: "WeatherArt Manager", parent: "系统管理"
|
||||||
controller do
|
controller do
|
||||||
def find_resource
|
def find_resource
|
||||||
scoped_collection.friendly.find(params[:id])
|
scoped_collection.friendly.find(params[:id])
|
||||||
|
@ -1,46 +1,65 @@
|
|||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
include SeoConcern
|
include SeoConcern
|
||||||
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
||||||
|
before_action :log_browser_info
|
||||||
# allow_browser versions: :modern
|
# allow_browser versions: :modern
|
||||||
allow_browser versions: :modern do |config|
|
# allow_browser versions: :modern,
|
||||||
# 通用移动浏览器
|
# patterns: [
|
||||||
config.allow(/Mobile Safari/) # iOS Safari
|
# # 鸿蒙系统相关
|
||||||
config.allow(/Chrome\/[\d.]+/) # Chrome 内核浏览器
|
# /OpenHarmony/, # 鸿蒙系统标识
|
||||||
|
# /ArkWeb\/[\d.]+/, # 鸿蒙浏览器内核
|
||||||
# 国内主流浏览器
|
# /Mobile HuaweiBrowser/, # 华为浏览器(新格式)
|
||||||
config.allow(/Quark\/[\d.]+/) # 夸克浏览器
|
# /HuaweiBrowser\/[\d.]+/, # 华为浏览器(旧格式)
|
||||||
config.allow(/HuaweiBrowser\/[\d.]+/) # 华为浏览器
|
#
|
||||||
config.allow(/MiuiBrowser\/[\d.]+/) # 小米浏览器
|
# # 夸克浏览器(更宽松的匹配)
|
||||||
config.allow(/VivoBrowser\/[\d.]+/) # vivo浏览器
|
# /Quark[\s\/][\d.]+/, # 匹配 "Quark/7.4.6.681" 或 "Quark 7.4.6.681"
|
||||||
config.allow(/OppoBrowser\/[\d.]+/) # OPPO浏览器
|
#
|
||||||
config.allow(/UCBrowser\/[\d.]+/) # UC浏览器
|
# /Mobile Safari/,
|
||||||
config.allow(/QQBrowser\/[\d.]+/) # QQ浏览器
|
# /Chrome\/[\d.]+/,
|
||||||
config.allow(/MicroMessenger\/[\d.]+/) # 微信内置浏览器
|
# /Quark\/[\d.]+/,
|
||||||
config.allow(/Alipay/) # 支付宝内置浏览器
|
# /HuaweiBrowser\/[\d.]+/,
|
||||||
config.allow(/BaiduBoxApp/) # 百度 App
|
# /MiuiBrowser\/[\d.]+/,
|
||||||
config.allow(/baiduboxapp/i) # 百度 App (小写)
|
# /VivoBrowser\/[\d.]+/,
|
||||||
config.allow(/SogouMobile/) # 搜狗移动浏览器
|
# /OppoBrowser\/[\d.]+/,
|
||||||
config.allow(/Weibo/) # 微博内置浏览器
|
# /UCBrowser\/[\d.]+/,
|
||||||
config.allow(/DingTalk/) # 钉钉内置浏览器
|
# /QQBrowser\/[\d.]+/,
|
||||||
config.allow(/ToutiaoMicroApp/) # 今日头条小程序
|
# /MicroMessenger\/[\d.]+/,
|
||||||
config.allow(/BytedanceWebview/) # 字节跳动系浏览器
|
# /Alipay/,
|
||||||
config.allow(/ArkWeb/) # 鸿蒙浏览器
|
# /BaiduBoxApp/,
|
||||||
|
# /baiduboxapp/i,
|
||||||
# 调试日志(开发环境)
|
# /SogouMobile/,
|
||||||
if Rails.env.development?
|
# /Weibo/,
|
||||||
config.on_failure do |browser|
|
# /DingTalk/,
|
||||||
Rails.logger.info <<~INFO
|
# /ToutiaoMicroApp/,
|
||||||
Browser blocked:
|
# /BytedanceWebview/,
|
||||||
Name: #{browser.name}
|
# /ArkWeb/
|
||||||
Version: #{browser.version}
|
# ],
|
||||||
User Agent: #{browser.ua}
|
# on_failure: ->(browser) {
|
||||||
INFO
|
# Rails.logger.warn <<~BROWSER_INFO
|
||||||
end
|
# Browser Blocked:
|
||||||
end
|
# User Agent: #{browser.ua}
|
||||||
end
|
# Name: #{browser.name}
|
||||||
|
# Version: #{browser.version}
|
||||||
|
# Platform: #{browser.platform.name}
|
||||||
|
# Device: #{browser.device.name}
|
||||||
|
# Mobile: #{browser.mobile?}
|
||||||
|
# Modern: #{browser.modern?}
|
||||||
|
# Bot: #{browser.bot?}
|
||||||
|
# BROWSER_INFO
|
||||||
|
# }
|
||||||
before_action :set_locale
|
before_action :set_locale
|
||||||
after_action :track_action
|
after_action :track_action
|
||||||
|
|
||||||
|
def log_browser_info
|
||||||
|
# 构建详细的浏览器信息
|
||||||
|
Rails.logger.debug "User Agent: #{request.user_agent}"
|
||||||
|
|
||||||
|
# 如果是被拦截的浏览器,记录额外信息
|
||||||
|
# unless browser_allowed?
|
||||||
|
# Rails.logger.info "User Agent: #{request.user_agent}"
|
||||||
|
# end
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def track_action
|
def track_action
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
class ApplicationJob < ActiveJob::Base
|
|
||||||
# Automatically retry jobs that encountered a deadlock
|
|
||||||
# retry_on ActiveRecord::Deadlocked
|
|
||||||
|
|
||||||
# Most jobs are safe to ignore if the underlying records are no longer available
|
|
||||||
# discard_on ActiveJob::DeserializationError
|
|
||||||
end
|
|
@ -1,33 +0,0 @@
|
|||||||
class BatchGenerateWeatherArtsJob < ApplicationJob
|
|
||||||
queue_as :default
|
|
||||||
|
|
||||||
def perform(*args)
|
|
||||||
start_time = Time.current
|
|
||||||
max_duration = 50.minutes
|
|
||||||
|
|
||||||
cities_to_process = get_eligible_cities
|
|
||||||
|
|
||||||
cities_to_process.each do |city|
|
|
||||||
break if Time.current - start_time > max_duration
|
|
||||||
|
|
||||||
GenerateWeatherArtJob.perform_now(city)
|
|
||||||
sleep 1.minute # 确保不超过API限制
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def get_eligible_cities
|
|
||||||
City.active
|
|
||||||
.where(active: true)
|
|
||||||
.where("last_weather_fetch IS NULL OR last_weather_fetch < ?", Date.today)
|
|
||||||
# .select { |city| early_morning_in_timezone?(city.timezone) }
|
|
||||||
end
|
|
||||||
|
|
||||||
# def early_morning_in_timezone?(timezone)
|
|
||||||
# return false if timezone.blank?
|
|
||||||
|
|
||||||
# time = Time.current.in_time_zone(timezone)
|
|
||||||
# time.hour == 2
|
|
||||||
# end
|
|
||||||
end
|
|
@ -1,45 +0,0 @@
|
|||||||
class GenerateWeatherArtJob < ApplicationJob
|
|
||||||
queue_as :default
|
|
||||||
|
|
||||||
def perform(*args)
|
|
||||||
city = args[0]
|
|
||||||
return if city.last_weather_fetch&.today?
|
|
||||||
|
|
||||||
weather_service = WeatherService.new
|
|
||||||
ai_service = AiService.new
|
|
||||||
|
|
||||||
# 获取天气数据
|
|
||||||
weather_data = weather_service.get_weather(city.latitude, city.longitude)
|
|
||||||
return unless weather_data
|
|
||||||
|
|
||||||
# 生成提示词
|
|
||||||
prompt = ai_service.generate_prompt(city, weather_data)
|
|
||||||
return unless prompt
|
|
||||||
|
|
||||||
# 生成图像
|
|
||||||
image_url = ai_service.generate_image(prompt)
|
|
||||||
return unless image_url
|
|
||||||
|
|
||||||
# 创建天气艺术记录
|
|
||||||
weather_art = city.weather_arts.create!(
|
|
||||||
weather_date: Date.today,
|
|
||||||
**weather_data,
|
|
||||||
prompt: prompt
|
|
||||||
)
|
|
||||||
|
|
||||||
# 下载并附加图像
|
|
||||||
tempfile = Down.download(image_url)
|
|
||||||
weather_art.image.attach(
|
|
||||||
io: tempfile,
|
|
||||||
filename: "#{city.country.name}-#{city.name.parameterize}-#{Time.current.strftime('%Y%m%d-%H%M%S')}.png"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 更新城市状态
|
|
||||||
city.update!(
|
|
||||||
last_weather_fetch: Time.current,
|
|
||||||
last_image_generation: Time.current
|
|
||||||
)
|
|
||||||
rescue => e
|
|
||||||
Rails.logger.error "Error generating weather art for #{city.name}: #{e.message}"
|
|
||||||
end
|
|
||||||
end
|
|
@ -53,6 +53,24 @@ class City < ApplicationRecord
|
|||||||
.order("visit_count ASC, cities.name ASC")
|
.order("visit_count ASC, cities.name ASC")
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
scope :most_popular_inactive, -> {
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
name
|
name
|
||||||
@ -66,7 +84,7 @@ class City < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def localized_name
|
def localized_name
|
||||||
I18n.t("cities.#{name.parameterize.underscore}")
|
I18n.t("cities.#{name.parameterize.underscore}", default: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def full_name
|
def full_name
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
<%= @city.country.name %>, <%= @city.region %>
|
<%= @city.country.name %>, <%= @city.region %>
|
||||||
</div>
|
</div>
|
||||||
<div class="badge badge-lg badge-secondary gap-2">
|
<div class="badge badge-lg badge-secondary gap-2">
|
||||||
<%= Time.current.in_time_zone(@city.timezone).strftime("%Y-%m-%d %H:%M") %>
|
<%= @city.timezone.present? ? Time.current.in_time_zone(@city.timezone).strftime("%Y-%m-%d %H:%M") : "Timezone undefined" %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user