jbit better messages

This commit is contained in:
2026-06-06 13:59:49 +03:30
parent e22ce8b737
commit 08f2f475ed
4 changed files with 14424 additions and 50 deletions
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env python3
"""
Generate iran_cities_seed.sql from goldAli cities API.
Usage:
python generate_cities_sql.py
python generate_cities_sql.py --source /path/to/cities.json
"""
from __future__ import annotations
import json
import sys
import urllib.request
from pathlib import Path
SQL_DIR = Path(__file__).resolve().parent
DEFAULT_SOURCE = "https://goldaliii.com/server/api/city/all"
OUTPUT = SQL_DIR / "iran_cities_seed.sql"
ARABIC_TO_PERSIAN = str.maketrans(
{"ي": "ی", "ك": "ک", "ة": "ه", "ۀ": "ه", "ؤ": "و", "إ": "ا", "أ": "ا"}
)
TITLE_FIXES = {
"چهارمحال وبختیاری": "چهارمحال و بختیاری",
"سیستان وبلوچستان": "سیستان و بلوچستان",
"کهگیلویه وبویراحمد": "کهگیلویه و بویراحمد",
}
def normalize_title(title: str) -> str:
title = title.translate(ARABIC_TO_PERSIAN)
return TITLE_FIXES.get(title, title)
def sql_str(value: str | None) -> str:
if value is None:
return "NULL"
return "'" + str(value).replace("\\", "\\\\").replace("'", "''") + "'"
def sql_bool(value: bool | None) -> str:
return "1" if value else "0"
def load_rows(source: str) -> list[dict]:
if source.startswith("http://") or source.startswith("https://"):
with urllib.request.urlopen(source, timeout=60) as resp:
data = json.load(resp)
else:
data = json.loads(Path(source).read_text(encoding="utf-8"))
return [
row
for row in data
if row.get("id") != 1600 and row.get("title") != "تست"
]
def generate_sql(rows: list[dict]) -> str:
provinces = sorted(
[row for row in rows if row.get("parent_id") is None], key=lambda r: r["id"]
)
others = sorted(
[row for row in rows if row.get("parent_id") is not None], key=lambda r: r["id"]
)
ordered = provinces + others
lines = [
"-- =============================================================================",
"-- استان‌ها + شهرها + زیرمجموعه‌ها (جدول cities)",
"--",
"-- ساختار مطابق City model:",
"-- level=1 → استان (parent_id = NULL, has_children = 1)",
"-- level=2 → شهر/شهرستان (parent_id = id استان)",
"-- level=3 → محله/بخش (parent_id = id شهر level=2)",
"--",
f"-- تعداد: {len(ordered)} رکورد (31 استان + {len(ordered) - 31} زیرمجموعه)",
"-- =============================================================================",
"",
"START TRANSACTION;",
"",
"SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;",
"",
"CREATE TABLE IF NOT EXISTS `cities` (",
" `id` bigint unsigned NOT NULL AUTO_INCREMENT,",
" `title` varchar(255) NOT NULL,",
" `code` varchar(50) DEFAULT NULL,",
" `parent_id` bigint unsigned DEFAULT NULL,",
" `has_children` tinyint(1) NOT NULL DEFAULT 0,",
" `level` tinyint unsigned DEFAULT NULL,",
" PRIMARY KEY (`id`),",
" KEY `cities_parent_id_index` (`parent_id`)",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
"",
"ALTER TABLE `cities` MODIFY COLUMN `code` varchar(50) DEFAULT NULL;",
"",
"DELETE FROM `cities`;",
"",
]
batch_size = 50
for i in range(0, len(ordered), batch_size):
batch = ordered[i : i + batch_size]
lines.append(
"INSERT INTO `cities` (`id`, `title`, `code`, `parent_id`, `has_children`, `level`) VALUES"
)
values = []
for row in batch:
parent_id = row.get("parent_id")
level = row.get("level")
values.append(
f" ({row['id']}, {sql_str(normalize_title(row['title']))}, "
f"{sql_str(row.get('code'))}, "
f"{parent_id if parent_id is not None else 'NULL'}, "
f"{sql_bool(row.get('has_children'))}, "
f"{level if level is not None else 'NULL'})"
)
lines.append(",\n".join(values) + ";")
lines.append("")
max_id = max(row["id"] for row in ordered)
lines.extend([f"ALTER TABLE `cities` AUTO_INCREMENT = {max_id + 1};", "", "COMMIT;", ""])
return "\n".join(lines)
def main() -> None:
source = DEFAULT_SOURCE
if len(sys.argv) > 1 and sys.argv[1] == "--source" and len(sys.argv) > 2:
source = sys.argv[2]
rows = load_rows(source)
OUTPUT.write_text(generate_sql(rows), encoding="utf-8")
print(f"Wrote {OUTPUT} ({len(rows)} rows)")
if __name__ == "__main__":
main()