📜 ⬆️ ⬇️

We use postal codes in our application for the benefit

I think that on many sites the user will be asked for his physical address. Whether to deliver, to send out paper spam notifications. And, in general, it is a trifle. I hit the index, Moscow, region, district, village, street, house, apartment. It would seem that there is to simplify, everyone seems to remember his address, is it difficult to drive it? But the devil, as always, is in the details: the user is sealed in the address, the package goes in the wrong place, the rays of "good" go to your feedback and in general life is bad.

Look at the first part of the address - the index. In this set of six digits there is already a region, a district and a city / village. They can be substituted with a gun. This will kill two birds with one stone:
Things are easy: we need a database of postal codes.

And she is! Real, electronic and, most importantly, the official database of indices from Russian Post.

Meet: vinfo.russianpost.ru/database/ops.html
')
The database is available in a DBF format that is already outlandish for young developers and is regularly (twice a month) updated.

Of course, for details, this base is not enough up to FIAS , but you should pay tribute, it is much simpler (just one table!), Therefore, if you don’t need accuracy to the street and the house, but only the settlement is enough - you are here.

We fasten happiness to ... well, let's go to the site.


So, we happily swing the base and think how to cram it into what we are using, which is a modern SQL (or even NSQL).

We search in Google, search in Yandex, search in apt-cache, the latter is joyful to us and gives out:

envek@envek-work:~$ apt-cache search dbf pgdbf - converter of XBase / FoxPro tables to PostgreSQL dbf2mysql - xBase <--> MySQL 

Great how! I use Postgres and will convert to it. The base also uses DOS encoding, so call for help iconv. By the way, the latest versions of pgdbf (> = 0.6.2) themselves own shamanism and call iconv, but they have not reached the Ubunt repository yet.

 mv {PIndx08,post_indices}.dbf #  ,     pgdbf -u post_indices.dbf | iconv -f CP866 > post_indices.sql #  

Well, now we need to make it work.

I use Ruby on Rails, I will show it by her example. Who does not understand the rails, can scroll through.

We create a model that will be our information from the database and present in the application
 rails g model PostIndex 

In the migration, we thoughtfully copy the structure of the table from the original database, make the index the primary key:
 class CreatePostIndices < ActiveRecord::Migration def change create_table :post_indices, id: false do |t| t.string :index, limit: 6 t.string :ops_name, limit: 60 t.string :ops_type, limit: 50 t.string :ops_subm, limit: 6 t.string :region, limit: 60 t.string :autonom, limit: 60 t.string :area, limit: 60 t.string :city, limit: 60 t.string :city_1, limit: 60 t.date :act_date t.string :index_old, limit: 6 t.index :index_old end reversible do |to| to.up do execute 'ALTER TABLE post_indices ADD PRIMARY KEY (index);' end end end end 

Slightly customize the model:
 class PostIndex < ActiveRecord::Base self.primary_key = 'index' end 

We make a simple controller, which the zip code in json-format will give us:
 #  : rails generate controller PostIndices class PostIndicesController < ApplicationController def get @index = PostIndex.where(index: params[:index]).first @index = PostIndex.where(index_old: params[:index]).order(:index).first! unless @index respond_to do |format| format.json { render json: @index.to_json(only: [:index, :region, :area, :city]) } end end end 

We set the route in config / routes.rb, on which the application will give us the desired indices:
 get '/post_index/:index(.:format)', controller: :post_indices, action: :get 

And most importantly: html and javascript, which will make all the magic for the user.

HTML form:
 <form id='address_form'> <table> <tr> <td><label for='address_postcode'> </label></td> <td> <input class='postcode_field' id='address_postcode' name='address[postcode]'> <p class='description'>   ,  «», «»  «»  .</p> </td> </tr> <tr> <td><label for='address_region'>//</label></td> <td><input class='region_field' id='address_region' name='address[region]'></td> </tr> <tr> <td><label for='address_area'></label></td> <td><input class='area_field' id='address_area' name='address[area]'></td> </tr> <tr> <td><label for='address_city'>/</label></td> <td><input class='city_field' id='address_city' name='address[city]'></td> </tr> </table> </form> 

Javascript code (very detailed, with user notification, error trapping and index correction)
 jQuery(document).ready(function($){ $('.postcode_field').on('keyup change', function () { //    var postcode_field = $(this); var form = postcode_field.parents("form"); var region_field = $('.region_field', form); var area_field = $('.area_field', form); var city_field = $('.city_field', form); //    region_field.val(''); area_field.val(''); city_field.val(''); //     -     var postcode = this.value; if (postcode.length == 6) { jQuery.ajax({ dataType: "jsonp", url: 'http://postindexapi.ru/'+postcode+'.json?callback=?', beforeSend: function() { //  ,    $("td:last-child p.description.notice, td:last-child p.description.alert", postcode_field.parents("tr")).remove(); $('<p class="description notice loading"></p>').text("…").appendTo($("td:last-child", postcode_field.parents("tr"))) }, success: function(data){ postcode_field.val(data.index); region_field.val(data.region); area_field.val(data.area); city_field.val(data.city); if (data.index != postcode) { var message = "    : "+postcode+",   : "+data.index; $('<p class="description notice"></p>').text(message).appendTo($("td:last-child", postcode_field.parents("tr"))) } }, error: function (jqxhr, status, e) { var message = '       !'+e; if (e == 'Not Found') message = '      '; if (status == 'timeout') message = '     .   .'; $('<p class="description alert"></p>').text(message).appendTo($("td:last-child", postcode_field.parents("tr"))) console.debug(jqxhr, status, e); }, complete: function () { //   $("td:last-child p.description.loading", postcode_field.parents("tr")).remove(); } }); } }); }); 

And, voila, when we enter the index, we are automatically substituted for the region, city, and so on. At the same time, with a bonus, we can correct outdated indexes for actual ones (very often people have addresses of relatives with already hopelessly outdated indexes).



Another touch: to keep the base always fresh, create a rake-task, which will run on the crown, say, once every two weeks and do it all for us (in Gemfile you should have gem 'nokogiri' , you can with require: false ) :

 require 'open-uri' require 'fileutils' require 'nokogiri' namespace :post_index do desc 'Update used post indices database to latest' task update: :environment do # Get info about post indices database url_prefix = 'http://info.russianpost.ru/database' doc = Nokogiri::HTML(open("#{url_prefix}/ops.html")) file = doc.at_css('a[name=newdbdata]+table tr:last-child td:nth-child(4) a').attr :href FileUtils.mkdir_p "#{Rails.root}/tmp/post_indices" dir = Pathname.new("#{Rails.root}/tmp/post_indices") filepath = Pathname.new("#{dir}/#{file}") filepath_success = Pathname.new("#{dir}/#{file}.success") if filepath.exist? and filepath_success.exist? puts 'Already up-to-date.' else # Download, unzip, rename and convert post indices file sh "wget #{url_prefix}/#{file} -O #{filepath}" sh "unzip -o #{filepath} -d #{dir}" dbf_filename = filepath.to_s.gsub /\.zip$/, '.DBF' sh "cp -f #{dbf_filename} #{dir}/post_indices.dbf" sh "pgdbf -u #{dir}/post_indices.dbf | iconv -f CP866 > #{dir}/post_indices.sql" # Import in database config = Abitur::Application.config.database_configuration[::Rails.env] dbh, dbu, dbp, db = config['host'], config['username'], config['password'], config['database'] sh "PGPASSWORD=#{dbp} psql -U #{dbu} -w -h #{dbh} #{db} < #{dir}/post_indices.sql" # Clean up FileUtils.rm [dbf_filename, "#{dir}/post_indices.dbf", "#{dir}/post_indices.sql"], force: true FileUtils.touch filepath_success end end end 

Summary


Pros : ease of implementation, use and maintenance up to date, low weight
Minuses : low detail (only to the settlement), ALL CITIES CAPSOM, RUSSIAN POST, WHY?

And for the most lazy


Well, and finally. If you liked such a trifle, but you do not fiercely want to drag this information to your application, then I made a mini-service postindexapi.ru especially for you. Which does just what I described above - gives the information about the index in JSON. Take advantage of your health! Instructions are attached.

Pull requests and bug reports, as well as any tips and suggestions in the githab repository are accepted with gratitude: github.com/Envek/postindexapi.ru

Thanks for attention.

UPD : As of 2018, the postindexapi.ru extinct mini-service was maintained in working condition by alexkbs , for which many thanks to him. The API for its service is different from the one given in the article, so use separate JavaScript code .

Source: https://habr.com/ru/post/190122/


All Articles