refactor: improve weather art generation process

- Update city selection to consider countries.
- Use daytime instead of sunrise for time checks.
- Add per-run generation limit

This commit refactors the weather art generation process to
increase the efficiency. The city selection has been updated to
consider country. Daytime check has been updated to determine
if the city is within the correct local time. A limit is added
to restrict the number of cities processed in a single run.
This commit is contained in:
songtianlun 2025-02-16 22:59:53 +08:00
parent afc871deb1
commit fa1fc7c21a

View File

@ -5,29 +5,31 @@ class BatchGenerateWeatherArtsWorker
MAX_DURATION = 5.minutes
SLEEP_DURATION = 10.seconds
DAILY_GENERATION_LIMIT = 50 # 每日生成图片上限
PER_RUN_GENERATION_LIMIT = 5 # 每次运行生成图片上限
def perform(*args)
start_time = Time.current
remaining_slots = calculate_remaining_slots
return if remaining_slots <= 0
cities_to_process = select_cities(remaining_slots)&.shuffle
countries_to_process = select_countries
cities_to_process = select_cities(countries_to_process, remaining_slots)&.shuffle
print_cities_list(cities_to_process, start_time)
skipped_cities = []
processed_cities = []
cities_to_process.each do |city|
if within_sunrise_time?(city)
if within_daytime?(city)
Rails.logger.info "Generating weather art for #{city.name} (time: [#{city.formatted_current_time(:all)}])"
GenerateWeatherArtWorker.perform_async(city.id)
processed_cities << city.name
else
Rails.logger.info "Skipping #{city.name} (time: [#{city.formatted_current_time(:all)}]) due to local time not being within sunrise hours."
Rails.logger.info "Skipping #{city.name} (time: [#{city.formatted_current_time(:all)}]) due to local time not being within daytime hours."
skipped_cities << city.name
end
sleep SLEEP_DURATION
break if Time.current - start_time > MAX_DURATION
break if Time.current - start_time > MAX_DURATION || processed_cities.size >= PER_RUN_GENERATION_LIMIT
end
print_summary(processed_cities, skipped_cities)
@ -45,6 +47,45 @@ class BatchGenerateWeatherArtsWorker
[ DAILY_GENERATION_LIMIT - today_generations, 0 ].max
end
def select_countries
cutoff_time = Time.current - GENERATION_INTERVAL
Country.all.select do |country|
timezone_info = country.timezones.present? ? JSON.parse(country.timezones).first : { "zoneName" => "UTC" }
local_time = Time.current.in_time_zone(ActiveSupport::TimeZone[timezone_info["zoneName"]])
local_time.hour >= 6 && local_time.hour <= 18
end
end
def select_cities(countries, limit)
active_cities = City.where(active: true, country_id: countries.map(&:id)).to_a
remaining_slots = limit - active_cities.size
if remaining_slots > 0
other_cities = City
.where.not(id: active_cities.map(&:id))
.where(country_id: countries.map(&:id))
.order("RANDOM()").limit(remaining_slots).to_a
active_cities + other_cities
else
active_cities
end
end
def within_daytime?(city)
local_time = get_local_time(city)
local_time.hour >= 6 && local_time.hour <= 18
end
def get_local_time(city)
return Time.current unless city
timezone_info = city.country&.timezones.present? ? JSON.parse(city.country.timezones).first : { "zoneName" => "UTC" }
timezone = ActiveSupport::TimeZone[timezone_info["zoneName"]] || ActiveSupport::TimeZone["UTC"]
Time.current.in_time_zone(timezone)
end
def print_cities_list(cities, start_time)
Rails.logger.info "Generate city task start at: #{start_time}"
Rails.logger.info "Generate city list: "
@ -55,60 +96,8 @@ class BatchGenerateWeatherArtsWorker
end
end
def within_sunrise_time?(city)
local_time = get_local_time(city)
Rails.logger.debug "#{city.name} now hour: #{local_time.hour}"
local_time.hour >= 8 && local_time.hour <= 18
end
def get_local_time(city)
return Time.current unless city
timezone_info =
city.country&.timezones.present? ?
JSON.parse(city.country.timezones)&.first :
{ "zoneName": "UTC" }
return Time.current unless timezone_info
timezone = ActiveSupport::TimeZone[timezone_info["zoneName"]] ||
ActiveSupport::TimeZone["UTC"]
Time.current.in_time_zone(timezone)
end
def print_summary(processed_cities, skipped_cities)
Rails.logger.info "Processed cities: #{processed_cities.join(', ')}"
Rails.logger.info "Skipped cities: #{skipped_cities.join(', ')}"
end
def select_cities(limit)
cutoff_time = Time.current - GENERATION_INTERVAL
base_query = City
.joins("LEFT JOIN (
SELECT city_id, MAX(created_at) as last_generation_time
FROM weather_arts
GROUP BY city_id
) latest_arts ON cities.id = latest_arts.city_id")
.where("latest_arts.last_generation_time IS NULL OR latest_arts.last_generation_time < ?", cutoff_time)
active_cities = base_query
.where(active: true)
.order(:priority)
.limit(limit)
.to_a
remaining_slots = limit - active_cities.size
if remaining_slots > 0
other_cities = base_query
.where.not(id: active_cities.map(&:id))
.order("RANDOM()")
.limit(remaining_slots)
.to_a
active_cities + other_cities
else
active_cities
end
end
end