migrate with countries_states_cities_db

This commit is contained in:
songtianlun 2025-02-08 17:42:50 +08:00
parent 6281ca73a2
commit 652107d0ee
27 changed files with 2033511 additions and 34 deletions

View File

@ -41,12 +41,12 @@ ActiveAdmin.register_page "Ahoy Dashboard" do
column do
panel "最冷门活跃城市" do
# table_for City.least_popular_active.limit(10) do
table_for City.least_popular_active do
table_for City.least_popular_active.limit(100) do
column("ID") { |city| city.id }
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
column("访问(DWMY)") { |city| "#{city.view_count(:day)}/#{city.view_count(:week)}/#{city.view_count(:month)}/#{city.view_count(:year)}" }
column("状态") { |city| status_tag(city.active? ? "活跃" : "停用") }
column("所属区域") { |city| city.country.name+"/"+city.country.region.name }
column("所属区域") { |city| city&.country&.name.to_s+"/"+city&.country&.region&.name.to_s }
column("图像个数") { |city| city.weather_arts.count }
column("最后更新时间") { |city| city.last_weather_fetch }
# column("状态") { |city| status_tag(city.active? ? "活跃" : "停用") }
@ -63,12 +63,12 @@ ActiveAdmin.register_page "Ahoy Dashboard" do
column do
panel "热门未活跃城市" do
# table_for City.most_popular_inactive.limit(10) do
table_for City.most_popular_inactive do
table_for City.most_popular_inactive(100) do
column("ID") { |city| city.id }
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
column("访问(DWMY)") { |city| "#{city.view_count(:day)}/#{city.view_count(:week)}/#{city.view_count(:month)}/#{city.view_count(:year)}" }
column("状态") { |city| status_tag(city.active? ? "活跃" : "停用") }
column("所属区域") { |city| city.country.name+"/"+city.country.region.name }
column("所属区域") { |city| city&.country&.name.to_s+"/"+city.country&.region&.name.to_s }
column("图像个数") { |city| city.weather_arts.count }
column("最后更新时间") { |city| city.last_weather_fetch }
column("操作") { |city|

View File

@ -16,6 +16,6 @@ class ArtsController < ApplicationController
@weather_arts.order(created_at: :desc)
end
@weather_arts = @weather_arts.page(params[:page]).per(10)
@weather_arts = @weather_arts.page(params[:page]).per(12)
end
end

View File

@ -5,7 +5,7 @@ class CitiesController < ApplicationController
if params[:region]
@current_region = Region.friendly.find(params[:region])
@cities = @cities.by_region(@current_region.id)
@cities = @cities.by_region(@current_region.id) if @current_region
end
if params[:country]
@ -13,7 +13,7 @@ class CitiesController < ApplicationController
@cities = @cities.by_country(@current_country.id)
end
@cities = @cities.page(params[:page]).per(10)
@cities = @cities.page(params[:page]).per(12)
set_meta_tags(
title: @current_region ? "Cities in #{@current_region.name}" : "Explore Cities",

View File

@ -2,6 +2,7 @@ class City < ApplicationRecord
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
belongs_to :country
belongs_to :state, optional: true
has_many :weather_arts, dependent: :destroy
@ -37,38 +38,38 @@ class City < ApplicationRecord
end
}
scope :least_popular_active, -> {
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")
.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")
.order("visit_count ASC, cities.name ASC").limit(limit)
end
}
scope :most_popular_inactive, -> {
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")
.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")
.order("COUNT(ahoy_events.id) DESC, cities.name ASC").limit(limit)
end
}

View File

@ -2,11 +2,14 @@ class Country < ApplicationRecord
extend FriendlyId
friendly_id :name, use: :slugged
belongs_to :region
belongs_to :region, optional: true
belongs_to :subregion, optional: true
has_many :cities, dependent: :restrict_with_error
has_many :states
validates :name, presence: true
validates :code, presence: true, uniqueness: true
validates :iso2, uniqueness: true, allow_blank: true
def to_s
name

View File

@ -4,8 +4,9 @@ class Region < ApplicationRecord
has_many :countries, dependent: :restrict_with_error
has_many :cities, through: :countries
has_many :subregions
validates :name, presence: true
validates :name, presence: true, uniqueness: true
validates :code, presence: true, uniqueness: true
def to_s

7
app/models/state.rb Normal file
View File

@ -0,0 +1,7 @@
class State < ApplicationRecord
belongs_to :country
has_many :cities
validates :name, presence: true
validates :country_code, presence: true
end

6
app/models/subregion.rb Normal file
View File

@ -0,0 +1,6 @@
class Subregion < ApplicationRecord
belongs_to :region
has_many :countries
validates :name, presence: true
end

View File

@ -32,7 +32,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
</svg>
<%= city.country.name %>, <%= city.region.name %>
<%= city&.country&.name %>, <%= city&.region&.name %>
</div>
</div>
</div>
@ -92,7 +92,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
</svg>
<%= city.country.name %>, <%= city.region.name %>
<%= city&.country&.name %>, <%= city&.region&.name %>
</div>
<div class="bg-base-200 rounded-lg p-4 mb-4">

View File

@ -51,7 +51,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<ul class="dropdown-content z-[1] menu p-2 shadow-lg bg-base-100 rounded-box w-52">
<ul class="dropdown-content z-[1] menu p-2 shadow-lg bg-base-100 rounded-box w-52 max-h-80 overflow-y-auto flex-nowrap">
<li>
<%= link_to cities_path,
class: "#{@current_region ? '' : 'active'}" do %>
@ -70,7 +70,7 @@
</div>
<% if @current_region %>
<div class="dropdown">
<div class="dropdown dropdown-bottom">
<button class="btn btn-ghost gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 21v-4m0 0V5a2 2 0 012-2h6.5l1 1H21l-3 6 3 6h-8.5l-1-1H5a2 2 0 00-2 2zm9-13.5V9" />
@ -80,7 +80,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<ul class="dropdown-content z-[1] menu p-2 shadow-lg bg-base-100 rounded-box w-52">
<ul class="dropdown-content z-[1] menu p-2 shadow-lg bg-base-100 rounded-box w-52 max-h-80 overflow-y-auto flex-nowrap">
<li>
<%= link_to "All in #{@current_region.name}",
cities_path(region: @current_region.slug),

View File

@ -12,7 +12,7 @@
<div class="join shadow-lg">
<!-- 首页 -->
<%= link_to url_for(page: 1, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn btn-xs #{collection.first_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
class: "join-item btn btn-sm #{collection.first_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
</svg>
@ -20,7 +20,7 @@
<!-- 上一页 -->
<%= link_to url_for(page: collection.prev_page || 1, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn btn-xs #{collection.first_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
class: "join-item btn btn-sm #{collection.first_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
@ -33,35 +33,35 @@
<% if start_page > 1 %>
<%= link_to 1, url_for(page: 1, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn btn-xs btn-ghost hover:bg-primary/5" %>
class: "join-item btn btn-sm btn-ghost hover:bg-primary/5" %>
<% if start_page > 2 %>
<button class="join-item btn btn-xs btn-ghost btn-disabled">...</button>
<button class="join-item btn btn-sm btn-ghost btn-disabled">...</button>
<% end %>
<% end %>
<% (start_page..end_page).each do |page| %>
<% if page == collection.current_page %>
<button class="join-item btn btn-xs btn-ghost bg-primary/10 font-medium">
<button class="join-item btn btn-sm btn-ghost bg-primary/10 font-medium">
<%= page %>
</button>
<% else %>
<%= link_to page, url_for(page: page, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn btn-xs btn-ghost hover:bg-primary/5" %>
class: "join-item btn btn-sm btn-ghost hover:bg-primary/5" %>
<% end %>
<% end %>
<% if end_page < collection.total_pages %>
<% if end_page < collection.total_pages - 1 %>
<button class="join-item btn btn-xs btn-ghost btn-disabled">...</button>
<button class="join-item btn btn-sm btn-ghost btn-disabled">...</button>
<% end %>
<%= link_to collection.total_pages,
url_for(page: collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn btn-xs btn-ghost hover:bg-primary/5" %>
class: "join-item btn btn-sm btn-ghost hover:bg-primary/5" %>
<% end %>
<!-- 下一页 -->
<%= link_to url_for(page: collection.next_page || collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn btn-xs #{collection.last_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
class: "join-item btn btn-sm #{collection.last_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
@ -69,7 +69,7 @@
<!-- 末页 -->
<%= link_to url_for(page: collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn btn-xs #{collection.last_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
class: "join-item btn btn-sm #{collection.last_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
</svg>

View File

@ -1,5 +1,5 @@
batch_generate_weather:
cron: '0 */1 * * *'
cron: '0 8,18 * * *'
class: BatchGenerateWeatherArtsWorker
description: "Generate weather arts every 2 hours"
enabled: true

View File

@ -0,0 +1,36 @@
class MigrateWithCountriesStatesCitiesDatabase < ActiveRecord::Migration[8.0]
def change
add_column :regions, :translations, :text, if_not_exists: true
add_column :regions, :flag, :boolean, default: true, if_not_exists: true
add_column :regions, :wiki_data_id, :string, if_not_exists: true
add_column :countries, :iso3, :string
add_column :countries, :numeric_code, :string
add_column :countries, :iso2, :string
add_column :countries, :phonecode, :string
add_column :countries, :capital, :string
add_column :countries, :currency, :string
add_column :countries, :currency_name, :string
add_column :countries, :currency_symbol, :string
add_column :countries, :tld, :string
add_column :countries, :native, :string
add_column :countries, :region, :string
add_column :countries, :subregion, :string
add_column :countries, :nationality, :string
add_column :countries, :timezones, :text
add_column :countries, :translations, :text
add_column :countries, :latitude, :decimal, precision: 10, scale: 8
add_column :countries, :longitude, :decimal, precision: 11, scale: 8
add_column :countries, :emoji, :string
add_column :countries, :emoji_u, :string
add_column :countries, :flag, :boolean, default: true
add_column :countries, :wiki_data_id, :string
add_reference :countries, :region, foreign_key: true
add_column :cities, :state_code, :string
add_column :cities, :country_code, :string
add_column :cities, :flag, :boolean, default: true
add_column :cities, :wiki_data_id, :string
add_column :cities, :active, :boolean, default: false
end
end

View File

@ -0,0 +1,22 @@
class CreateStates < ActiveRecord::Migration[8.0]
def change
create_table :states do |t|
t.string :name
t.string :code
t.references :country, foreign_key: true
t.string :country_code
t.string :fips_code
t.string :iso2
t.string :state_type
t.integer :level
t.integer :parent_id
t.decimal :latitude, precision: 10, scale: 8
t.decimal :longitude, precision: 11, scale: 8
t.boolean :flag
t.string :wiki_data_id
t.timestamps
end
add_reference :cities, :state, foreign_key: true
end
end

View File

@ -0,0 +1,15 @@
class CreateSubregions < ActiveRecord::Migration[8.0]
def change
create_table :subregions do |t|
t.string :name, null: false
t.text :translations
t.references :region, foreign_key: true, null: false
t.boolean :flag, default: true
t.string :wiki_data_id
t.timestamps
end
add_reference :countries, :subregion, foreign_key: true, null: true
end
end

84
db/schema.rb generated
View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_01_26_155239) do
ActiveRecord::Schema[8.0].define(version: 2025_02_08_052634) do
create_table "active_admin_comments", force: :cascade do |t|
t.string "namespace"
t.text "body"
@ -111,27 +111,57 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_26_155239) do
t.string "name"
t.float "latitude"
t.float "longitude"
t.boolean "active"
t.integer "priority"
t.string "timezone"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.integer "country_id", null: false
t.string "state_code"
t.string "country_code"
t.boolean "flag", default: true
t.string "wiki_data_id"
t.boolean "active", default: false
t.integer "state_id"
t.index ["country_id"], name: "index_cities_on_country_id"
t.index ["slug"], name: "index_cities_on_slug", unique: true
t.index ["state_id"], name: "index_cities_on_state_id"
end
create_table "countries", force: :cascade do |t|
t.string "name"
t.string "code"
t.string "slug"
t.integer "region_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "emojiU"
t.string "iso3"
t.string "numeric_code"
t.string "iso2"
t.string "phonecode"
t.string "capital"
t.string "currency"
t.string "currency_name"
t.string "currency_symbol"
t.string "tld"
t.string "native"
t.string "region"
t.string "subregion"
t.string "nationality"
t.text "timezones"
t.text "translations"
t.decimal "latitude", precision: 10, scale: 8
t.decimal "longitude", precision: 11, scale: 8
t.string "emoji"
t.string "emoji_u"
t.boolean "flag", default: true
t.string "wiki_data_id"
t.integer "region_id"
t.integer "subregion_id"
t.index ["code"], name: "index_countries_on_code", unique: true
t.index ["region_id"], name: "index_countries_on_region_id"
t.index ["slug"], name: "index_countries_on_slug", unique: true
t.index ["subregion_id"], name: "index_countries_on_subregion_id"
end
create_table "friendly_id_slugs", force: :cascade do |t|
@ -151,10 +181,54 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_26_155239) do
t.string "slug"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "translations"
t.boolean "flag", default: true
t.string "wiki_data_id"
t.index ["code"], name: "index_regions_on_code", unique: true
t.index ["slug"], name: "index_regions_on_slug", unique: true
end
create_table "states", force: :cascade do |t|
t.string "name"
t.string "code"
t.integer "country_id"
t.string "country_code"
t.string "fips_code"
t.string "iso2"
t.string "state_type"
t.integer "level"
t.integer "parent_id"
t.decimal "latitude", precision: 10, scale: 8
t.decimal "longitude", precision: 11, scale: 8
t.boolean "flag"
t.string "wiki_data_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["country_id"], name: "index_states_on_country_id"
end
create_table "subregions", force: :cascade do |t|
t.string "name", null: false
t.text "translations"
t.integer "region_id", null: false
t.boolean "flag", default: true
t.string "wiki_data_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["region_id"], name: "index_subregions_on_region_id"
end
create_table "votes", force: :cascade do |t|
t.string "votable_type"
t.integer "votable_id"
t.string "session_id"
t.integer "vote_type", default: 0
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["votable_id", "votable_type", "session_id", "vote_type"], name: "index_votes_on_votable_and_session_id_and_vote_type", unique: true
t.index ["votable_type", "votable_id"], name: "index_votes_on_votable"
end
create_table "weather_arts", force: :cascade do |t|
t.integer "city_id", null: false
t.date "weather_date"
@ -180,6 +254,10 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_26_155239) do
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "cities", "countries"
add_foreign_key "cities", "states"
add_foreign_key "countries", "regions"
add_foreign_key "countries", "subregions"
add_foreign_key "states", "countries"
add_foreign_key "subregions", "regions"
add_foreign_key "weather_arts", "cities"
end

1963314
lib/data/cities.json Normal file

File diff suppressed because it is too large Load Diff

13726
lib/data/countries.json Normal file

File diff suppressed because it is too large Load Diff

140
lib/data/regions.json Normal file
View File

@ -0,0 +1,140 @@
[
{
"id": 1,
"name": "Africa",
"translations": {
"ko": "아프리카",
"pt-BR": "África",
"pt": "África",
"nl": "Afrika",
"hr": "Afrika",
"fa": "آفریقا",
"de": "Afrika",
"es": "África",
"fr": "Afrique",
"ja": "アフリカ",
"it": "Africa",
"zh-CN": "非洲",
"tr": "Afrika",
"ru": "Африка",
"uk": "Африка",
"pl": "Afryka"
},
"wikiDataId": "Q15"
},
{
"id": 2,
"name": "Americas",
"translations": {
"ko": "아메리카",
"pt-BR": "América",
"pt": "América",
"nl": "Amerika",
"hr": "Amerika",
"fa": "قاره آمریکا",
"de": "Amerika",
"es": "América",
"fr": "Amérique",
"ja": "アメリカ州",
"it": "America",
"zh-CN": "美洲",
"tr": "Amerika",
"ru": "Америка",
"uk": "Америка",
"pl": "Ameryka"
},
"wikiDataId": "Q828"
},
{
"id": 3,
"name": "Asia",
"translations": {
"ko": "아시아",
"pt-BR": "Ásia",
"pt": "Ásia",
"nl": "Azië",
"hr": "Ázsia",
"fa": "آسیا",
"de": "Asien",
"es": "Asia",
"fr": "Asie",
"ja": "アジア",
"it": "Asia",
"zh-CN": "亚洲",
"tr": "Asya",
"ru": "Азия",
"uk": "Азія",
"pl": "Azja"
},
"wikiDataId": "Q48"
},
{
"id": 4,
"name": "Europe",
"translations": {
"ko": "유럽",
"pt-BR": "Europa",
"pt": "Europa",
"nl": "Europa",
"hr": "Európa",
"fa": "اروپا",
"de": "Europa",
"es": "Europa",
"fr": "Europe",
"ja": "ヨーロッパ",
"it": "Europa",
"zh-CN": "欧洲",
"tr": "Avrupa",
"ru": "Европа",
"uk": "Європа",
"pl": "Europa"
},
"wikiDataId": "Q46"
},
{
"id": 5,
"name": "Oceania",
"translations": {
"ko": "오세아니아",
"pt-BR": "Oceania",
"pt": "Oceania",
"nl": "Oceanië en Australië",
"hr": "Óceánia és Ausztrália",
"fa": "اقیانوسیه",
"de": "Ozeanien und Australien",
"es": "Oceanía",
"fr": "Océanie",
"ja": "オセアニア",
"it": "Oceania",
"zh-CN": "大洋洲",
"tr": "Okyanusya",
"ru": "Океания",
"uk": "Океанія",
"pl": "Oceania"
},
"wikiDataId": "Q55643"
},
{
"id": 6,
"name": "Polar",
"translations": {
"ko": "남극",
"pt-BR": "Antártida",
"pt": "Antártida",
"nl": "Antarctica",
"hr": "Antarktika",
"fa": "جنوبگان",
"de": "Antarktika",
"es": "Antártida",
"fr": "Antarctique",
"ja": "南極大陸",
"it": "Antartide",
"zh-CN": "南極洲",
"tr": "Antarktika",
"ru": "Антарктика",
"uk": "Антарктика",
"pl": "Antarktyka"
},
"wikiDataId": "Q51"
}
]

55420
lib/data/states.json Normal file

File diff suppressed because it is too large Load Diff

485
lib/data/subregions.json Normal file
View File

@ -0,0 +1,485 @@
[
{
"id": 19,
"name": "Australia and New Zealand",
"region_id": "5",
"translations": {
"ko": "오스트랄라시아",
"pt": "Australásia",
"nl": "Australazië",
"hr": "Australazija",
"fa": "استرالزی",
"de": "Australasien",
"es": "Australasia",
"fr": "Australasie",
"ja": "オーストララシア",
"it": "Australasia",
"zh-CN": "澳大拉西亞",
"ru": "Австралия и Новая Зеландия",
"uk": "Австралія та Нова Зеландія",
"pl": "Australia i Nowa Zelandia"
},
"wikiDataId": "Q45256"
},
{
"id": 7,
"name": "Caribbean",
"region_id": "2",
"translations": {
"ko": "카리브",
"pt": "Caraíbas",
"nl": "Caraïben",
"hr": "Karibi",
"fa": "کارائیب",
"de": "Karibik",
"es": "Caribe",
"fr": "Caraïbes",
"ja": "カリブ海地域",
"it": "Caraibi",
"zh-CN": "加勒比地区",
"ru": "Карибы",
"uk": "Кариби",
"pl": "Karaiby"
},
"wikiDataId": "Q664609"
},
{
"id": 9,
"name": "Central America",
"region_id": "2",
"translations": {
"ko": "중앙아메리카",
"pt": "América Central",
"nl": "Centraal-Amerika",
"hr": "Srednja Amerika",
"fa": "آمریکای مرکزی",
"de": "Zentralamerika",
"es": "América Central",
"fr": "Amérique centrale",
"ja": "中央アメリカ",
"it": "America centrale",
"zh-CN": "中美洲",
"ru": "Центральная Америка",
"uk": "Центральна Америка",
"pl": "Ameryka Środkowa"
},
"wikiDataId": "Q27611"
},
{
"id": 10,
"name": "Central Asia",
"region_id": "3",
"translations": {
"ko": "중앙아시아",
"pt": "Ásia Central",
"nl": "Centraal-Azië",
"hr": "Srednja Azija",
"fa": "آسیای میانه",
"de": "Zentralasien",
"es": "Asia Central",
"fr": "Asie centrale",
"ja": "中央アジア",
"it": "Asia centrale",
"zh-CN": "中亚",
"ru": "Центральная Азия",
"uk": "Центральна Азія",
"pl": "Azja Środkowa"
},
"wikiDataId": "Q27275"
},
{
"id": 4,
"name": "Eastern Africa",
"region_id": "1",
"translations": {
"ko": "동아프리카",
"pt": "África Oriental",
"nl": "Oost-Afrika",
"hr": "Istočna Afrika",
"fa": "شرق آفریقا",
"de": "Ostafrika",
"es": "África Oriental",
"fr": "Afrique de l'Est",
"ja": "東アフリカ",
"it": "Africa orientale",
"zh-CN": "东部非洲",
"ru": "Восточная Африка",
"uk": "Східна Африка",
"pl": "Afryka Wschodnia"
},
"wikiDataId": "Q27407"
},
{
"id": 12,
"name": "Eastern Asia",
"region_id": "3",
"translations": {
"ko": "동아시아",
"pt": "Ásia Oriental",
"nl": "Oost-Azië",
"hr": "Istočna Azija",
"fa": "شرق آسیا",
"de": "Ostasien",
"es": "Asia Oriental",
"fr": "Asie de l'Est",
"ja": "東アジア",
"it": "Asia orientale",
"zh-CN": "東亞",
"ru": "Восточная Азия",
"uk": "Східна Азія",
"pl": "Azja Wschodnia"
},
"wikiDataId": "Q27231"
},
{
"id": 15,
"name": "Eastern Europe",
"region_id": "4",
"translations": {
"ko": "동유럽",
"pt": "Europa de Leste",
"nl": "Oost-Europa",
"hr": "Istočna Europa",
"fa": "شرق اروپا",
"de": "Osteuropa",
"es": "Europa Oriental",
"fr": "Europe de l'Est",
"ja": "東ヨーロッパ",
"it": "Europa orientale",
"zh-CN": "东欧",
"ru": "Восточная Европа",
"uk": "Східна Європа",
"pl": "Europa Wschodnia"
},
"wikiDataId": "Q27468"
},
{
"id": 20,
"name": "Melanesia",
"region_id": "5",
"translations": {
"ko": "멜라네시아",
"pt": "Melanésia",
"nl": "Melanesië",
"hr": "Melanezija",
"fa": "ملانزی",
"de": "Melanesien",
"es": "Melanesia",
"fr": "Mélanésie",
"ja": "メラネシア",
"it": "Melanesia",
"zh-CN": "美拉尼西亚",
"ru": "Меланезия",
"uk": "Меланезія",
"pl": "Melanezja"
},
"wikiDataId": "Q37394"
},
{
"id": 21,
"name": "Micronesia",
"region_id": "5",
"translations": {
"ko": "미크로네시아",
"pt": "Micronésia",
"nl": "Micronesië",
"hr": "Mikronezija",
"fa": "میکرونزی",
"de": "Mikronesien",
"es": "Micronesia",
"fr": "Micronésie",
"ja": "ミクロネシア",
"it": "Micronesia",
"zh-CN": "密克罗尼西亚群岛",
"ru": "Микронезия",
"uk": "Мікронезія",
"pl": "Mikronezja"
},
"wikiDataId": "Q3359409"
},
{
"id": 2,
"name": "Middle Africa",
"region_id": "1",
"translations": {
"ko": "중앙아프리카",
"pt": "África Central",
"nl": "Centraal-Afrika",
"hr": "Srednja Afrika",
"fa": "مرکز آفریقا",
"de": "Zentralafrika",
"es": "África Central",
"fr": "Afrique centrale",
"ja": "中部アフリカ",
"it": "Africa centrale",
"zh-CN": "中部非洲",
"ru": "Средняя Африка",
"uk": "Середня Африка",
"pl": "Afryka Środkowa"
},
"wikiDataId": "Q27433"
},
{
"id": 1,
"name": "Northern Africa",
"region_id": "1",
"translations": {
"ko": "북아프리카",
"pt": "Norte de África",
"nl": "Noord-Afrika",
"hr": "Sjeverna Afrika",
"fa": "شمال آفریقا",
"de": "Nordafrika",
"es": "Norte de África",
"fr": "Afrique du Nord",
"ja": "北アフリカ",
"it": "Nordafrica",
"zh-CN": "北部非洲",
"ru": "Северная Африка",
"uk": "Північна Африка",
"pl": "Afryka Północna"
},
"wikiDataId": "Q27381"
},
{
"id": 6,
"name": "Northern America",
"region_id": "2",
"translations": {
"ko": "북미",
"pt": "América Setentrional",
"nl": "Noord-Amerika",
"fa": "شمال آمریکا",
"de": "Nordamerika",
"es": "América Norteña",
"fr": "Amérique septentrionale",
"ja": "北部アメリカ",
"it": "America settentrionale",
"zh-CN": "北美地區",
"ru": "Северная Америка",
"uk": "Північна Америка",
"pl": "Ameryka Północna"
},
"wikiDataId": "Q2017699"
},
{
"id": 18,
"name": "Northern Europe",
"region_id": "4",
"translations": {
"ko": "북유럽",
"pt": "Europa Setentrional",
"nl": "Noord-Europa",
"hr": "Sjeverna Europa",
"fa": "شمال اروپا",
"de": "Nordeuropa",
"es": "Europa del Norte",
"fr": "Europe du Nord",
"ja": "北ヨーロッパ",
"it": "Europa settentrionale",
"zh-CN": "北歐",
"ru": "Северная Европа",
"uk": "Північна Європа",
"pl": "Europa Północna"
},
"wikiDataId": "Q27479"
},
{
"id": 22,
"name": "Polynesia",
"region_id": "5",
"translations": {
"ko": "폴리네시아",
"pt": "Polinésia",
"nl": "Polynesië",
"hr": "Polinezija",
"fa": "پلی‌نزی",
"de": "Polynesien",
"es": "Polinesia",
"fr": "Polynésie",
"ja": "ポリネシア",
"it": "Polinesia",
"zh-CN": "玻里尼西亞",
"ru": "Полинезия",
"uk": "Полінезія",
"pl": "Polinezja"
},
"wikiDataId": "Q35942"
},
{
"id": 8,
"name": "South America",
"region_id": "2",
"translations": {
"ko": "남아메리카",
"pt": "América do Sul",
"nl": "Zuid-Amerika",
"hr": "Južna Amerika",
"fa": "آمریکای جنوبی",
"de": "Südamerika",
"es": "América del Sur",
"fr": "Amérique du Sud",
"ja": "南アメリカ",
"it": "America meridionale",
"zh-CN": "南美洲",
"ru": "Южная Америка",
"uk": "Південна Америка",
"pl": "Ameryka Południowa"
},
"wikiDataId": "Q18"
},
{
"id": 13,
"name": "South-Eastern Asia",
"region_id": "3",
"translations": {
"ko": "동남아시아",
"pt": "Sudeste Asiático",
"nl": "Zuidoost-Azië",
"hr": "Jugoistočna Azija",
"fa": "جنوب شرق آسیا",
"de": "Südostasien",
"es": "Sudeste Asiático",
"fr": "Asie du Sud-Est",
"ja": "東南アジア",
"it": "Sud-est asiatico",
"zh-CN": "东南亚",
"ru": "Юго-Восточная Азия",
"uk": "Південно-Східна Азія",
"pl": "Azja Południowo-Wschodnia"
},
"wikiDataId": "Q11708"
},
{
"id": 5,
"name": "Southern Africa",
"region_id": "1",
"translations": {
"ko": "남아프리카",
"pt": "África Austral",
"nl": "Zuidelijk Afrika",
"hr": "Južna Afrika",
"fa": "جنوب آفریقا",
"de": "Südafrika",
"es": "África austral",
"fr": "Afrique australe",
"ja": "南部アフリカ",
"it": "Africa australe",
"zh-CN": "南部非洲",
"ru": "Южная Африка",
"uk": "Південна Африка",
"pl": "Afryka Południowa"
},
"wikiDataId": "Q27394"
},
{
"id": 14,
"name": "Southern Asia",
"region_id": "3",
"translations": {
"ko": "남아시아",
"pt": "Ásia Meridional",
"nl": "Zuid-Azië",
"hr": "Južna Azija",
"fa": "جنوب آسیا",
"de": "Südasien",
"es": "Asia del Sur",
"fr": "Asie du Sud",
"ja": "南アジア",
"it": "Asia meridionale",
"zh-CN": "南亚",
"ru": "Южная Азия",
"uk": "Південна Азія",
"pl": "Azja Południowa"
},
"wikiDataId": "Q771405"
},
{
"id": 16,
"name": "Southern Europe",
"region_id": "4",
"translations": {
"ko": "남유럽",
"pt": "Europa meridional",
"nl": "Zuid-Europa",
"hr": "Južna Europa",
"fa": "جنوب اروپا",
"de": "Südeuropa",
"es": "Europa del Sur",
"fr": "Europe du Sud",
"ja": "南ヨーロッパ",
"it": "Europa meridionale",
"zh-CN": "南欧",
"ru": "Южная Европа",
"uk": "Південна Європа",
"pl": "Europa Południowa"
},
"wikiDataId": "Q27449"
},
{
"id": 3,
"name": "Western Africa",
"region_id": "1",
"translations": {
"ko": "서아프리카",
"pt": "África Ocidental",
"nl": "West-Afrika",
"hr": "Zapadna Afrika",
"fa": "غرب آفریقا",
"de": "Westafrika",
"es": "África Occidental",
"fr": "Afrique de l'Ouest",
"ja": "西アフリカ",
"it": "Africa occidentale",
"zh-CN": "西非",
"ru": "Западная Африка",
"uk": "Західна Африка",
"pl": "Afryka Zachodnia"
},
"wikiDataId": "Q4412"
},
{
"id": 11,
"name": "Western Asia",
"region_id": "3",
"translations": {
"ko": "서아시아",
"pt": "Sudoeste Asiático",
"nl": "Zuidwest-Azië",
"hr": "Jugozapadna Azija",
"fa": "غرب آسیا",
"de": "Vorderasien",
"es": "Asia Occidental",
"fr": "Asie de l'Ouest",
"ja": "西アジア",
"it": "Asia occidentale",
"zh-CN": "西亚",
"ru": "Западная Азия",
"uk": "Західна Азія",
"pl": "Azja Zachodnia"
},
"wikiDataId": "Q27293"
},
{
"id": 17,
"name": "Western Europe",
"region_id": "4",
"translations": {
"ko": "서유럽",
"pt": "Europa Ocidental",
"nl": "West-Europa",
"hr": "Zapadna Europa",
"fa": "غرب اروپا",
"de": "Westeuropa",
"es": "Europa Occidental",
"fr": "Europe de l'Ouest",
"ja": "西ヨーロッパ",
"it": "Europa occidentale",
"zh-CN": "西欧",
"ru": "Западная Европа",
"uk": "Західна Європа",
"pl": "Europa Zachodnia"
},
"wikiDataId": "Q27496"
}
]

View File

@ -0,0 +1,6 @@
desc "study rake about rails" # desc 是Rake定义的方法,表示对下面定义任务的描述.这个描述会在使用Rake --tasks(或者Rake -T)命令时输出在屏幕上.
task :study_rake do # cmd 命令行中执行 rake study_rake 开始执行脚本task是Rake最重要的方法.它的方法定义是:task(args, &block).任务体是一个block。
%w[a b c].each do |d|
puts d # 编写你所需的功能代码。
end
end

View File

@ -0,0 +1,181 @@
# 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
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_initialize_by(id: data["id"]) do |r|
r.name = data["name"]
end
puts "Sync Regions[#{count}/#{sum}]: [#{region.name}]"
count += 1
region.update(
translations: data["translations"],
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|
subregion = Subregion.find_or_initialize_by(id: data["id"]) do |s|
s.name = data["name"]
s.region_id = data["region_id"]
end
puts "Sync Subregions[#{count}/#{sum}]: [#{subregion.name}]"
count += 1
subregion.update(
translations: data["translations"],
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|
# puts "Syncing countries for #{data["name"]}"
# 处理 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_initialize_by(name: data["name"]) do |c|
end
# 更新 Country 属性
country.update(
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"],
translations: data["translations"],
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"]}] "
count += 1
state = State.find_or_initialize_by(name: data["name"]) do |s|
s.country_id = data["country_id"]
end
state.update(
country_code: data["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|
city = City.find_or_initialize_by(name: data["name"])
country = Country.find_by(name: data["country_name"])
state = State.find_by(name: data["state_name"])
puts "Syncing City[#{count}/#{sum}] [#{data["name"]}] Country:[#{country&.name}] "
count += 1
city.update(
latitude: data["latitude"],
longitude: data["longitude"],
flag: data["flag"] || true,
wiki_data_id: data["wikiDataId"]
)
if country != nil
city.update(
country: country,
country_code: country.code,
)
end
city.update(state: state) unless state == nil
city.update(state_code: state.code) unless state == nil
city.update(active: false) if city.active == nil
city.update(priority: 10) if city.priority == nil
end
end
end

11
test/fixtures/states.yml vendored Normal file
View File

@ -0,0 +1,11 @@
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
# This model initially had no columns defined. If you add columns to the
# model remove the "{}" from the fixture names and add the columns immediately
# below each fixture, per the syntax in the comments below
#
#one: {}
# column: value
#
#two: {}
# column: value

11
test/fixtures/subregions.yml vendored Normal file
View File

@ -0,0 +1,11 @@
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
# This model initially had no columns defined. If you add columns to the
# model remove the "{}" from the fixture names and add the columns immediately
# below each fixture, per the syntax in the comments below
#
#one: {}
# column: value
#
#two: {}
# column: value

View File

@ -0,0 +1,7 @@
require "test_helper"
class StateTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

View File

@ -0,0 +1,7 @@
require "test_helper"
class SubregionTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end