today_ai_weather/lib/tasks/sync_geo_data.rake
songtianlun 070a3dfc1c fix: handle nil state in city initialization
- Update city initialization to safely access state ID
- Use safe navigation operator to prevent potential nil errors

This change ensures that if the state is nil, the code will not raise
an error when trying to access its ID. It improves the robustness of
the city data synchronization process.
2025-02-26 14:44:07 +08:00

204 lines
5.9 KiB
Ruby

# lib/tasks/sync_geo_data.rake
require "json"
namespace :geo do
desc "Sync geographical data from JSON"
task sync: :environment do
# 定义 JSON 文件路径
base_path = Rails.root.join("lib", "data")
# 同步顺序很重要:先同步 Regions -> Subregions -> Countries -> States -> Cities
sync_regions(base_path)
sync_subregions(base_path)
sync_countries(base_path)
sync_states(base_path)
sync_cities(base_path)
end
task sync_city: :environment do
# 定义 JSON 文件路径
base_path = Rails.root.join("lib", "data")
sync_cities(base_path)
end
def sync_regions(base_path)
file_path = base_path.join("regions.json")
regions = JSON.parse(File.read(file_path))
sum = regions.count
count = 1
regions.each do |data|
region = Region.find_or_create_by!(id: data["id"]) do |r|
r.name = data["name"]
r.code = data["name"]
end
puts "Sync Regions[#{count}/#{sum}]: [#{region.name}]"
count += 1
region.update!(
name: data["name"],
code: data["name"],
translations: data["translations"].to_json.to_s,
flag: data["flag"] || true,
wiki_data_id: data["wikiDataId"]
)
end
end
def sync_subregions(base_path)
file_path = base_path.join("subregions.json")
subregions = JSON.parse(File.read(file_path))
sum = subregions.count
count = 1
subregions.each do |data|
region = Region.find_by!(id: data["region_id"])
subregion = Subregion.find_or_create_by!(id: data["id"]) do |s|
s.name = data["name"]
s.region_id = region.id
end
puts "Sync Subregions[#{count}/#{sum}]: [#{subregion.name}] region:[#{region.name}]"
count += 1
subregion.update!(
translations: data["translations"].to_json,
flag: data["flag"] || true,
wiki_data_id: data["wikiDataId"]
)
end
end
def sync_countries(base_path)
file_path = base_path.join("countries.json")
countries = JSON.parse(File.read(file_path))
sum = countries.count
count = 1
countries.each do |data|
# 处理 Region
region = if data["region_id"]
Region.find_by(id: data["region_id"])
elsif data["region"]
Region.find_by(name: data["region"])
end
# 处理 Subregion
subregion = nil
if data["subregion_id"].present?
subregion = Subregion.find_by(id: data["subregion_id"])
elsif data["subregion"].present?
subregion = Subregion.find_by(name: data["subregion"])
end
puts "Syncing Country[#{count}/#{sum}] [#{data["name"]}] region: [#{region&.name}] subregion: [#{data["subregion"]}]"
count += 1
# 查找或初始化 Country
country = Country.find_or_create_by!(code: data["iso2"]) do |c|
c.name = data["name"]
end
# 更新 Country 属性
country.update!(
name: data["name"],
iso3: data["iso3"],
numeric_code: data["numeric_code"],
iso2: data["iso2"],
code: data["iso2"],
phonecode: data["phonecode"],
capital: data["capital"],
currency: data["currency"],
currency_name: data["currency_name"],
currency_symbol: data["currency_symbol"],
tld: data["tld"],
native: data["native"],
nationality: data["nationality"],
timezones: data["timezones"].to_json,
translations: data["translations"].to_json,
latitude: data["latitude"],
longitude: data["longitude"],
emoji: data["emoji"],
emoji_u: data["emojiU"],
flag: data["flag"] || true,
wiki_data_id: data["wikiDataId"]
)
country.update!(region: region) if region != nil
country.update!(subregion: subregion) if subregion != nil
end
end
def sync_states(base_path)
file_path = base_path.join("states.json")
states = JSON.parse(File.read(file_path))
sum = states.count
count = 1
states.each do |data|
puts "Syncing State[#{count}/#{sum}] [#{data["name"]}] country:[#{data["country_name"]}]"
count += 1
country = Country.find_by!(code: data["country_code"])
state = State.find_or_create_by!(name: data["name"]) do |s|
s.country_id = country&.id
s.country_code = country&.code
end
state.update!(
country_id: country&.id,
country_code: country&.code,
fips_code: data["fips_code"],
iso2: data["iso2"],
code: data["state_code"],
state_type: data["type"],
level: data["level"],
parent_id: data["parent_id"],
latitude: data["latitude"],
longitude: data["longitude"],
flag: data["flag"] || true,
wiki_data_id: data["wikiDataId"]
)
end
end
def sync_cities(base_path)
file_path = base_path.join("cities.json")
cities = JSON.parse(File.read(file_path))
sum = cities.count
count = 1
cities.each do |data|
country = Country.find_by!(code: data["country_code"])
state = State.find_by(code: data["state_code"])
# 使用多个属性来确保唯一性
city = City.find_or_initialize_by(
name: data["name"],
country_id: country.id,
state_id: state&.id
)
# 更新或设置城市属性
city.assign_attributes(
country_code: country.code,
latitude: data["latitude"],
longitude: data["longitude"],
state_id: state&.id,
state_code: state&.code,
flag: data["flag"] || true,
wiki_data_id: data["wikiDataId"],
active: city.active || false,
priority: city.priority || 10
)
# 保存城市
if city.save
puts "Syncing City[#{count}/#{sum}] [#{data["name"]}] Country:[#{country.name}] State: [#{state&.name}] Lat:[#{data["latitude"]}] Lon:[#{data["longitude"]}]"
else
puts "Error saving city: #{city.errors.full_messages.join(", ")}"
end
count += 1
end
end
end