Mengekspor Rencana Penomoran Badan Komunikasi Federal ke Database Relasional

Badan Komunikasi Federal secara teratur memperbarui rencana penomoran yang tersedia untuk umum . Jika Anda menggunakan rencana ini untuk menentukan wilayah atau penyedia pelanggan dalam rencana Anda, maka kemungkinan besar Anda tertarik pada relevansi informasi ini. Pada pandangan pertama, tidak ada yang sulit dalam menulis aplikasi yang mengunduh, memproses, dan mengirim data ke basis data, namun, ketika Anda memulai implementasi, Anda pasti akan menemukan perangkap, yang akan saya bicarakan sekarang.

Paket panggilan terdiri dari empat file tabular dalam format csv:

rossvyaz.ru/data/ABC-3xx.csv
rossvyaz.ru/data/ABC-4xx.csv
rossvyaz.ru/data/ABC-8xx.csv
rossvyaz.ru/data/

URL DEF-9xx.csv terkadang berubah.

Struktur semua tabel adalah sama:

/ DEF;;;;;

Di bidang Wilayah , beberapa penyedia memiliki titik koma tambahan:

 ; | ; 

Untuk menghindari kesalahan saat membaca tabel oleh aplikasi Anda, Anda perlu menonaktifkan memeriksa jumlah bidang yang diharapkan atau mengganti catatan yang salah dengan yang benar.

Kami akan menulis data ke tabel dengan struktur berikut:

first BIGINT PRIMARY KEY NOT NULL  //  
last BIGINT UNIQUE NOT NULL        //  
provider TEXT                      // 
source_region TEXT                 //        
region INT NOT NULL                //   

Sebagian besar wilayah dalam tabel dapat diidentifikasi secara jelas dengan terjadinya substring. Misalnya, semua entri untuk wilayah Saratov berisi Sarat substring .

Untuk daerah-daerah yang tidak dapat ditentukan dengan jelas, logika tambahan diperlukan, tentang hal ini di bawah ini.

Contoh 1:

  • Yamal-Nenets Autonomous Okrug
  • Oknug otonom Nenets

Okrug Otonom Yamalo-Nenets adalah bagian dari Wilayah Tyumen, dan Okrug Otonom Nenets adalah bagian dari Arkhangelsk. Masalah dalam menentukan kode numerik wilayah adalah bahwa substring Nenets AO sepenuhnya termasuk dalam Yamalo-Nenets AO . Ini berarti bahwa dua wilayah akan sesuai dengan entri yang mengandung substring dari Daerah Otonomi Nenets .

Untuk mengatasi masalah ini, Anda perlu menambahkan tanda centang untuk tidak adanya substring (tidak termasuk substring). Dengan kata lain, Oksug Otonom Yamalo-Nenets akan ditentukan oleh masuknya Yamalo , dan Okrug Otonom Nenets dengan masuknya Nenets dan tidak adanya Yamalo .

Contoh 2:

  • Pergilah. Krasnogorsk
  • Pelayan
  • Partza

Contoh wilayah yang tidak dikenal. Ada banyak catatan seperti itu di tabel Rossvyaz. Finder membantu mengetahui GO itu Krasnogorsk - wilayah Moskow, Lyantor - Otonom Khanty-Mansi Okrug, dan pemukiman Partz - Republik Mordovia. Solusinya sederhana, substring yang diinginkan dikonversi ke array string, dan kami akan memeriksa kejadiannya dalam satu lingkaran. Substring eksklusif juga dikonversi ke array.

Contoh 3:

  • Distrik Federal Siberia, Distrik Federal Timur Jauh
  • Wilayah Krasnoyarsk, Republik Khakassia, Moskow, St. Petersburg
  • Federasi Rusia

Dalam contoh ini, tidak mungkin mengidentifikasi suatu wilayah secara unik. Anda dapat memilih yang paling banyak dari yang terdaftar, atau menetapkan kode khusus untuk catatan tersebut. Faktanya, semua catatan yang berhubungan dengan beberapa daerah adalah angka 8-80 [0-9], nomor federal yang tidak dikenakan biaya panggilan. Saya menetapkan kode untuk catatan semacam itu dalam kisaran 200 hingga 210. Saya tidak berpikir bahwa itu akan berguna bagi saya.

Kode aplikasi dapat ditemukan di sini .
File yang dapat dieksekusi untuk platform linux, macos dan windows terletak di direktori bin (saya tidak menguji aplikasi pada platform windows). File

konfigurasi config.ymlharus dalam direktori yang sama dengan file yang dapat dieksekusi. Jika mau, Anda dapat menerapkan dukungan bendera di aplikasi untuk menunjukkan jalur ke konfigurasi dalam argumen baris perintah, permintaan tarik dipersilakan.

File konfigurasi
data_source:
  - https://rossvyaz.ru/data/ABC-3xx.csv
  - https://rossvyaz.ru/data/ABC-4xx.csv
  - https://rossvyaz.ru/data/ABC-8xx.csv
  - https://rossvyaz.ru/data/DEF-9xx.csv
#  - ../service/testdata/ABC-3xx.csv
#  - ../service/testdata/ABC-4xx.csv
#  - ../service/testdata/ABC-8xx.csv
#  - ../service/testdata/DEF-9xx.csv
exceptions: exceptions.yml
regions: regions.yml
db:
  host: localhost
  name: asterisk
  table: codes
  user: asterisk
  password: asterisk


Bidang data_source berisi jalur ke file tabel Rossvyaz. Jika jalur dimulai dengan http , aplikasi akan memuat tabel menggunakan klien web bawaan, jika tidak, ia akan mencoba menemukan tabel di sistem file.

Perhatikan garis komentar. Ini adalah jalur ke file tabel Rossvyaz yang disimpan dalam repositori. Jika Anda tidak memiliki koneksi Internet tercepat, setiap peluncuran aplikasi akan mengunduh file-file ini lagi. Untuk aplikasi yang lebih cepat, batalkan komentar pada garis-garis ini dan beri komentar hyperlink. Anda mungkin perlu menjalankan aplikasi lebih sering daripada yang Anda pikirkan, nanti saya akan menjelaskan mengapa menggunakan contoh.

Bidang pengecualian dan daerahberisi jalur ke file dengan koreksi dan deskripsi wilayah. Tentang mereka beberapa saat kemudian.

Bagian db menjelaskan parameter untuk menghubungkan ke DBMS MySQL. Untuk menggunakan DBMS lain, Anda perlu mengganti driver dalam kode dan templat string koneksi di konstruktor struktur Layanan

db, err := dbr.Open("mysql",
		fmt.Sprintf("%s:%s@tcp(%s)/%s", c.DB.User, c.DB.Password, c.DB.Host, c.DB.Name),
		nil)

Namun, tidak ada yang rumit dalam menambahkan bidang tipe DBMS ke konfigurasi dan menambahkan dukungan untuk tipe-tipe ini ke aplikasi, permintaan tarik dipersilakan lagi.

File exceptionions.yml berisi daftar perbaikan. Semua kecocokan substring dalam exception.yml sebelum titik dua, ditemukan dalam file tabel Rossvyaz, akan diganti dengan substring mengikuti titik dua.

File region.yml berisi daftar kode numerik wilayah dengan susunan kemunculan dan tidak termasuk substring yang digunakan untuk menentukan wilayah tertentu. Jika tidak ada catatan yang ditemukan untuk catatan apa pun atau ada dua pertandingan atau lebih, maka catatan ini akan pergi ke database, tetapi bidang wilayah akan berisi nilai 0 (wilayah tidak ditentukan).

Contoh deskripsi wilayah
78:
  name: -
  contain:
    - 
    - 
    - .. 
  not_contain:
    - 


Sebelum memulai aplikasi, buat database dengan nama yang ditentukan dalam db.name dan pastikan bahwa pengguna yang ditentukan dalam db.user telah membaca dan menulis izin.

Jadi, kami sudah menyiapkan semuanya, saatnya untuk meluncurkan aplikasi.

Harap dicatat bahwa selama startup, tabel yang ditentukan dalam bidang db.table dari file konfigurasi akan dihapus, berhati-hatilah.

./def2sql

Jika tidak ada kesalahan dan peringatan, Anda akan melihat output yang sama
correct records amount: 372324
inserted 372324 records


Jika tidak, Anda akan melihat laporan dengan kesalahan dan peringatan.
correct records amount: 372324
inserted 372324 records
{
    "unknown_regions": [
        {
            "First": 3424333950,
            "Last": 3424333999,
            "Range": 50,
            "Provider": " \"\"",
            "SourceRegion": ".. ",
            "Region": 0
        },
        {
            "First": 3424820000,
            "Last": 3424820049,
            "Range": 50,
            "Provider": " \"\"",
            "SourceRegion": ".. ",
            "Region": 0
        },
        {
            "First": 3425425000,
            "Last": 3425425049,
            "Range": 50,
            "Provider": " \"\"",
            "SourceRegion": ".. ",
            "Region": 0
        },
        {
            "First": 3425620000,
            "Last": 3425620049,
            "Range": 50,
            "Provider": " \"\"",
            "SourceRegion": ".. ",
            "Region": 0
        },
        {
            "First": 3426050000,
            "Last": 3426050050,
            "Range": 51,
            "Provider": " \"\"",
            "SourceRegion": ".. ",
            "Region": 0
        },
        {
            "First": 3427399950,
            "Last": 3427399999,
            "Range": 50,
            "Provider": " \"\"",
            "SourceRegion": ".. ",
            "Region": 0
        },
        {
            "First": 4217523500,
            "Last": 4217523999,
            "Range": 500,
            "Provider": " \" \"\"",
            "SourceRegion": " ",
            "Region": 0
        },
        {
            "First": 4217526000,
            "Last": 4217526999,
            "Range": 1000,
            "Provider": " \" \"\"",
            "SourceRegion": " ",
            "Region": 0
        },
        {
            "First": 8003550000,
            "Last": 8003559999,
            "Range": 10000,
            "Provider": " \" \" ( 2460087999)",
            "SourceRegion": "  *   * .  * . -",
            "Region": 0
        },
        {
            "First": 8003810000,
            "Last": 8003819999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8013810000,
            "Last": 8013819999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8023810000,
            "Last": 8023819999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8031010000,
            "Last": 8031019999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8033550000,
            "Last": 8033559999,
            "Range": 10000,
            "Provider": " \" \" ( 2460087999)",
            "SourceRegion": "  *   * .  * . -",
            "Region": 0
        },
        {
            "First": 8033810000,
            "Last": 8033819999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8041010000,
            "Last": 8041019999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8043810000,
            "Last": 8043819999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8051010000,
            "Last": 8051019999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8053810000,
            "Last": 8053819999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8063810000,
            "Last": 8063819999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8073810000,
            "Last": 8073819999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8083810000,
            "Last": 8083819999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8091010000,
            "Last": 8091019999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 8093550000,
            "Last": 8093559999,
            "Range": 10000,
            "Provider": " \" \" ( 2460087999)",
            "SourceRegion": "  *   * .  * . -",
            "Region": 0
        },
        {
            "First": 8093810000,
            "Last": 8093819999,
            "Range": 10000,
            "Provider": " \"\"",
            "SourceRegion": "   *   ",
            "Region": 0
        },
        {
            "First": 9512780000,
            "Last": 9512789999,
            "Range": 10000,
            "Provider": " \"2 \"",
            "SourceRegion": "  *  |  *  ",
            "Region": 0
        },
        {
            "First": 9963000000,
            "Last": 9963029999,
            "Range": 30000,
            "Provider": " \"2 \"",
            "SourceRegion": "  *  |  *  ",
            "Region": 0
        }
    ],
    "warnings": [
        "couldn't find region for record (3424333950-3424333999;  \"\", .. )",
        "couldn't find region for record (3424820000-3424820049;  \"\", .. )",
        "couldn't find region for record (3425425000-3425425049;  \"\", .. )",
        "couldn't find region for record (3425620000-3425620049;  \"\", .. )",
        "couldn't find region for record (3426050000-3426050050;  \"\", .. )",
        "couldn't find region for record (3427399950-3427399999;  \"\", .. )",
        "couldn't find region for record (4217523500-4217523999;  \" \"\",  )",
        "couldn't find region for record (4217526000-4217526999;  \" \"\",  )",
        "couldn't find region for record (8003550000-8003559999;  \" \" ( 2460087999),   *   * .  * . -)",
initial commit
        "couldn't find region for record (8003810000-8003819999;  \"\",    *   )",
        "couldn't find region for record (8013810000-8013819999;  \"\",    *   )",
        "couldn't find region for record (8023810000-8023819999;  \"\",    *   )",
        "couldn't find region for record (8031010000-8031019999;  \"\",    *   )",
        "couldn't find region for record (8033550000-8033559999;  \" \" ( 2460087999),   *   * .  * . -)",
        "couldn't find region for record (8033810000-8033819999;  \"\",    *   )",
        "couldn't find region for record (8041010000-8041019999;  \"\",    *   )",
        "couldn't find region for record (8043810000-8043819999;  \"\",    *   )",
        "couldn't find region for record (8051010000-8051019999;  \"\",    *   )",
        "couldn't find region for record (8053810000-8053819999;  \"\",    *   )",
        "couldn't find region for record (8063810000-8063819999;  \"\",    *   )",
        "couldn't find region for record (8073810000-8073819999;  \"\",    *   )",
        "couldn't find region for record (8083810000-8083819999;  \"\",    *   )",
        "couldn't find region for record (8091010000-8091019999;  \"\",    *   )",
        "couldn't find region for record (8093550000-8093559999;  \" \" ( 2460087999),   *   * .  * . -)",
        "couldn't find region for record (8093810000-8093819999;  \"\",    *   )",
        "couldn't find region for record (9512780000-9512789999;  \"2 \",   *  |  *  )",
        "couldn't find region for record (9963000000-9963029999;  \"2 \",   *  |  *  )"
    ]
}


Laporan tersebut berisi beberapa bagian.

unknown_regions - daftar catatan yang tidak sesuai dengan wilayah, atau jumlahnya lebih dari satu;
wrong_records - daftar entri yang tidak valid di tabel Rossvyaz;
peringatan - daftar peringatan yang terjadi saat aplikasi sedang berjalan;

Laporan di bawah spoiler terakhir yang saya terima selama peluncuran aplikasi sekitar satu bulan setelah edisi terbaru dari region.yml .

Versi usang dari region.yml
1:
  name:   ()
  contain:
    - 
2:
  name:  
  contain:
    - 
    -   
3:
  name:  
  contain:
    - 
4:
  name:  
  contain:
    -  
5:
  name:  
  contain:
    - 
6:
  name:  
  contain:
    - 
7:
  name: - 
  contain:
    - -
8:
  name:  
  contain:
    - 
9:
  name: - 
  contain:
    - 
10:
  name:  
  contain:
    - 
    - .. 
11:
  name:  
  contain:
    - 
12:
  name:   
  contain:
    - 
13:
  name:  
  contain:
    - 
    - . 
14:
  name:   ()
  contain:
    - 
    - 
15:
  name:    - 
  contain:
    - 
16:
  name:   ()
  contain:
    - 
    - 
    - 
17:
  name:  
  contain:
    - 
18:
  name:  
  contain:
    - 
    - . 
19:
  name:  
  contain:
    - 
  not_contain:
    - 
20:
  name:  
  contain:
    -  
    - 
    -  
21:
  name:   - 
  contain:
    -  
    - 
22:
  name:  
  contain:
    -  
23:
  name:  
  contain:
    - 
24:
  name:  
  contain:
    -  
  not_contain:
    - 
25:
  name:  
  contain:
    -  
26:
  name:  
  contain:
    -  
27:
  name:  
  contain:
    - 
28:
  name:  
  contain:
    - 
29:
  name:  
  contain:
    - 
    - 
  not_contain:
    - 
    - 
30:
  name:  
  contain:
    - 
31:
  name:  
  contain:
    - 
32:
  name:  
  contain:
    - 
33:
  name:  
  contain:
    - 
34:
  name:  
  contain:
    - 
35:
  name:  
  contain:
    - 
36:
  name:  
  contain:
    - 
    - .. 
37:
  name:  
  contain:
    - 
    - .. 
    - .. 
38:
  name:  
  contain:
    - 
39:
  name:  
  contain:
    - 
40:
  name:  
  contain:
    - 
41:
  name:  
  contain:
    - 
42:
  name:  
  contain:
    - 
43:
  name:  
  contain:
    - 
44:
  name:  
  contain:
    - 
45:
  name:  
  contain:
    - 
46:
  name:  
  contain:
    - 
#47:
#  name:  
#  contain:
#    - 
48:
  name:  
  contain:
    - 
49:
  name:  
  contain:
    - 
#50:
#  name:  
#  contain:
#    - 
51:
  name:  
  contain:
    - 
52:
  name:  
  contain:
    - 
53:
  name:  
  contain:
    - 
54:
  name:  
  contain:
    - 
    - . 
55:
  name:  
  contain:
    - 
    - . 
56:
  name:  
  contain:
    - 
57:
  name:  
  contain:
    - 
58:
  name:  
  contain:
    - 
59:
  name:  
  contain:
    - 
    - .. 
    -  -
    - - 
60:
  name:  
  contain:
    - 
61:
  name:  
  contain:
    - 
62:
  name:  
  contain:
    - 
63:
  name:  
  contain:
    - 
64:
  name:  
  contain:
    - 
65:
  name:  
  contain:
    - 
66:
  name:  
  contain:
    - 
67:
  name:  
  contain:
    - 
68:
  name:  
  contain:
    - 
69:
  name:  
  contain:
    - 
70:
  name:  
  contain:
    - 
71:
  name:  
  contain:
    - 
72:
  name:  
  contain:
    - 
73:
  name:  
  contain:
    - 
74:
  name:  
  contain:
    - 
    - .. 
    - .. 
75:
  name:  
  contain:
    - 
76:
  name:  
  contain:
    - 
77:
  name: . 
  contain:
    - 
    - 
    - 
    - 
    - .. 
    - .. 
    - .. 
    - .. 
    - .. 
    -    
    - .. 
    - .. 
    - .. 
    -  . 
    - . 
  not_contain:
    - 
78:
  name: -
  contain:
    - 
    - 
    - .. 
  not_contain:
    - 
79:
  name:   
  contain:
    - 
#83:
#  name:   
#  contain:
#    - 
86:
  name: -   - 
  contain:
    - 
    - 
    - 
    - 
    - 
    - . 
  not_contain:
    - 
87:
  name:   
  contain:
    - 
89:
  name: -  
  contain:
    - -
91:
  name:  
  contain:
    -  
    - 
    - 
#92:
#  name: 
#  contain:
#    - 
99:
  name:  ,     
  contain:
    - 

200:
  name:  
  contain:
    - 

201:
  name:   
  contain:
    -   
  not_contain:
    -   
202:
  name:   
  contain:
    -   
  not_contain:
    -   
203:
  name:   
  contain:
    -   
204:
  name:   
  contain:
    -   
  not_contain:
    -   
205:
  name:   
  contain:
    -   
  not_contain:
    -   
206:
  name: -  
  contain:
    - -  
207:
  name:   ,   
  contain:
    -   ,   
208:
  name:   ,   
  contain:
    -   ,   
209:
  name:  ,  , . , . -
  contain:
    -  ,  , . , . -


Untuk menghilangkan peringatan dan mendapatkan kode wilayah untuk semua catatan, Anda perlu mengedit region.yml .

versi diff dari region.yml
10d9
<     -  
245d243
<     -  
265,270d262
<     - .. 
<     - .. 
<     - .. 
<     - .. 
<     - .. 
<     - .. 
452d443
<     -    *   
457d447
<     -    *   
461,462c451
<     -  ,  , . , . -
<     -   *   * .  * . -
\ No newline at end of file
---
>     -  ,  , . , . -
\ No newline at end of file


Voila
correct records amount: 372324
inserted 372324 records


Sebagai ceri pada kue, saya menawarkan makro dialplan Asterisk untuk menentukan wilayah dengan nomor pelanggan:

[macro-get-region]
        exten => s,1,MYSQL(Connect conn localhost user password dbname)
        exten => s,n,MYSQL(Query result ${conn} SELECT region FROM codes WHERE ${ARG1} BETWEEN first AND last LIMIT 1)
        exten => s,n,MYSQL(Fetch region ${result} region_num)
        exten => s,n,MYSQL(Clear ${result})
        exten => s,n,MYSQL(Disconnect ${conn})

Untuk tujuan yang sama, Anda dapat menggunakan skrip Lua:
local driver = require("luasql.mysql")
local env = assert (driver.mysql())
local con = assert (env:connect("dbname", "user", "password"))
local cur = assert (con:execute(string.format("select region from codes where %s between first and last limit 1", arg[1])))

row = cur:fetch ({}, "a")
if row ~= nil then
    print(row.region)
else
    print(0)
end

cur:close()
con:close()
env:close(

Namun, alat apa pun yang mendukung permintaan SELECT ke MySQL cocok.

Nikmati administrasi Anda.

All Articles