feat: справочник городов КАТО и поле выбора города
Города и посёлки РК (178) из официального КАТО stat.gov.kz — на русском и казахском, с кодом и областью. Справочник генерируется скриптом scripts/Build-KatoCities.ps1 и лежит на фронте: внешних API и ключей не нужно. CityPicker ищет по обоим языкам с учётом раскладки (Құлсары = Кулсары) и оставляет свой вариант для сёл и заграницы. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Собирает справочник городов и посёлков РК для выпадающих списков портала из
|
||||
официального КАТО (Бюро национальной статистики, stat.gov.kz) и пишет его
|
||||
TS-модулем src/webparts/requestPortal/shared/dictionary/kato/katoCities.ts.
|
||||
|
||||
.DESCRIPTION
|
||||
Берём только города («г.») и посёлки («п.»): ~180 записей, несколько KB в бандле.
|
||||
Сёла (~10 тыс.) не берём — тяжело для фронта; их вводят вручную в то же поле.
|
||||
Административные единицы («Г.А.», «п.а.», «г.а.» — администрации, не населённые
|
||||
пункты) отбрасываются. КАТО обновляется редко: перезапустить скрипт со ссылкой
|
||||
на свежий файл со страницы https://stat.gov.kz/ru/classifiers/statistical/21/
|
||||
и закоммитить сгенерированный модуль.
|
||||
|
||||
.EXAMPLE
|
||||
./scripts/Build-KatoCities.ps1
|
||||
./scripts/Build-KatoCities.ps1 -XlsxUrl "https://stat.gov.kz/upload/iblock/.../КАТО_ДД.ММ.ГГГГ.xlsx"
|
||||
#>
|
||||
param(
|
||||
[string]$XlsxUrl = 'https://stat.gov.kz/upload/iblock/a7d/eqogm92xc0udumlzfzffhcrutk0fjhb4/КАТО_18.09.2026.xlsx',
|
||||
[string]$OutFile = (Join-Path $PSScriptRoot '..\src\webparts\requestPortal\shared\dictionary\kato\katoCities.ts')
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$work = Join-Path ([System.IO.Path]::GetTempPath()) "kato-$([guid]::NewGuid())"
|
||||
New-Item -ItemType Directory -Path $work | Out-Null
|
||||
try {
|
||||
# xlsx — это zip: читаем XML листа напрямую, без Excel и сторонних модулей.
|
||||
$zip = Join-Path $work 'kato.zip'
|
||||
Invoke-WebRequest -Uri $XlsxUrl -OutFile $zip -UseBasicParsing
|
||||
Expand-Archive -Path $zip -DestinationPath $work
|
||||
|
||||
[xml]$sharedXml = Get-Content -Raw -Encoding UTF8 (Join-Path $work 'xl\sharedStrings.xml')
|
||||
$shared = @($sharedXml.sst.si | ForEach-Object { $_.InnerText })
|
||||
|
||||
[xml]$sheetXml = Get-Content -Raw -Encoding UTF8 (Join-Path $work 'xl\worksheets\sheet1.xml')
|
||||
|
||||
# Колонки листа: A — код КАТО, B — код области (ab), C/D/E — нижние уровни, G/H — название на казахском/русском.
|
||||
$rows = foreach ($row in $sheetXml.worksheet.sheetData.row) {
|
||||
$cells = @{}
|
||||
foreach ($c in $row.c) {
|
||||
$column = $c.r -replace '\d', ''
|
||||
$value = if ($c.t -eq 's') { $shared[[int]$c.v] } elseif ($c.t -eq 'inlineStr') { $c.is.InnerText } else { $c.v }
|
||||
$cells[$column] = "$value".Trim()
|
||||
}
|
||||
[pscustomobject]@{ Kato = $cells['A']; Region = $cells['B']; Level2 = $cells['C']; Level3 = $cells['D']; Level4 = $cells['E']; Name = $cells['H']; NameKz = $cells['G'] }
|
||||
}
|
||||
|
||||
# Области — строки без нижних уровней. Города республиканского значения (Астана,
|
||||
# Алматы, Шымкент) сами себе «область» — для них регион не показываем.
|
||||
$regions = @{}
|
||||
$rows | Where-Object { $_.Level2 -eq '00' -and $_.Level3 -eq '00' -and $_.Level4 -eq '000' } | ForEach-Object {
|
||||
$regions[$_.Region] = if ($_.Name -match '^г\.') { '' } else { $_.Name }
|
||||
}
|
||||
|
||||
# «г.Атырау», «п. Асыката» — населённые пункты; «п.а.», «г.а.» — администрации.
|
||||
$cities = $rows |
|
||||
Where-Object { $_.Name -match '^(г|п)\.\s*[^\s.]' -and $_.Name -notmatch '^(г|п)\.а\.' } |
|
||||
ForEach-Object {
|
||||
[pscustomobject]@{
|
||||
Kato = $_.Kato
|
||||
Name = ($_.Name -replace '^(г|п)\.\s*', '')
|
||||
# «Құлсары қ.» / «Шаған к.» — тип пункта последним словом с точкой (в источнике
|
||||
# встречается и опечатка «ќ.»), поэтому отрезаем любое такое слово.
|
||||
NameKz = ($_.NameKz -replace '\s+\S+\.$', '')
|
||||
Kind = if ($_.Name -match '^г\.') { 'city' } else { 'settlement' }
|
||||
Region = $regions[$_.Region]
|
||||
}
|
||||
} |
|
||||
Sort-Object Name, Region
|
||||
|
||||
$esc = { param($s) $s -replace '\\', '\\' -replace "'", "\'" }
|
||||
$lines = $cities | ForEach-Object {
|
||||
" { kato: '$($_.Kato)', name: '$(& $esc $_.Name)', nameKz: '$(& $esc $_.NameKz)', kind: '$($_.Kind)', region: '$(& $esc $_.Region)' },"
|
||||
}
|
||||
|
||||
$source = [System.Uri]::UnescapeDataString(($XlsxUrl -split '/')[-1])
|
||||
$content = @"
|
||||
// СГЕНЕРИРОВАНО scripts/Build-KatoCities.ps1 — руками не править, перезапустить скрипт.
|
||||
// Источник: КАТО, Бюро национальной статистики РК (stat.gov.kz), файл «$source».
|
||||
import { IKatoCity } from './IKatoCity';
|
||||
|
||||
export const KATO_CITIES: ReadonlyArray<IKatoCity> = [
|
||||
$($lines -join "`n")
|
||||
];
|
||||
"@
|
||||
|
||||
$outPath = [System.IO.Path]::GetFullPath($OutFile)
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path $outPath) | Out-Null
|
||||
# Без BOM и с LF — как остальные исходники репозитория.
|
||||
[System.IO.File]::WriteAllText($outPath, $content.Replace("`r`n", "`n") + "`n", (New-Object System.Text.UTF8Encoding $false))
|
||||
|
||||
$cityCount = @($cities | Where-Object Kind -eq 'city').Count
|
||||
Write-Host "Готово: $($cities.Count) записей (городов $cityCount, посёлков $($cities.Count - $cityCount)) → $OutFile"
|
||||
}
|
||||
finally {
|
||||
Remove-Item -Recurse -Force $work
|
||||
}
|
||||
Reference in New Issue
Block a user