changeset d5def6606352 in modules/country:default details: https://hg.tryton.org/modules/country?cmd=changeset&node=d5def6606352 description: Rename zip into postal code
issue9853 review340411002 diffstat: CHANGELOG | 2 + __init__.py | 2 +- country.py | 37 +++++++---- country.xml | 35 +++++----- doc/design.rst | 12 +- doc/setup.rst | 7 +- doc/usage.rst | 4 +- scripts/import_postal_codes.py | 122 ++++++++++++++++++++++++++++++++++++++ scripts/import_zip.py | 122 -------------------------------------- setup.py | 2 +- tests/scenario_country_import.rst | 6 +- view/postal_code_form.xml | 13 ++++ view/postal_code_list.xml | 9 ++ view/zip_form.xml | 13 ---- view/zip_list.xml | 9 -- 15 files changed, 204 insertions(+), 191 deletions(-) diffs (550 lines): diff -r 7eabfccb0d32 -r d5def6606352 CHANGELOG --- a/CHANGELOG Sat Feb 27 18:16:28 2021 +0100 +++ b/CHANGELOG Wed Mar 31 23:59:33 2021 +0200 @@ -1,3 +1,5 @@ +* Rename zip into postal code + Version 5.8.0 - 2020-11-02 * Bug fixes (see mercurial logs for details) * Remove support for Python 3.5 diff -r 7eabfccb0d32 -r d5def6606352 __init__.py --- a/__init__.py Sat Feb 27 18:16:28 2021 +0100 +++ b/__init__.py Wed Mar 31 23:59:33 2021 +0200 @@ -10,5 +10,5 @@ Pool.register( country.Country, country.Subdivision, - country.Zip, + country.PostalCode, module='country', type_='model') diff -r 7eabfccb0d32 -r d5def6606352 country.py --- a/country.py Sat Feb 27 18:16:28 2021 +0100 +++ b/country.py Wed Mar 31 23:59:33 2021 +0200 @@ -1,5 +1,6 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. +from trytond import backend from trytond.model import ModelView, ModelSQL, DeactivableMixin, fields from trytond.pool import Pool from trytond.pyson import Eval @@ -250,31 +251,41 @@ super(Subdivision, cls).write(*args) -class Zip(ModelSQL, ModelView): - "Zip" - __name__ = 'country.zip' +class PostalCode(ModelSQL, ModelView): + "Postal Code" + __name__ = 'country.postal_code' country = fields.Many2One('country.country', 'Country', required=True, select=True, ondelete='CASCADE', - help="The country where the zip is.") + help="The country that contains the postal code.") subdivision = fields.Many2One('country.subdivision', 'Subdivision', select=True, ondelete='CASCADE', domain=[('country', '=', Eval('country', -1))], depends=['country'], - help="The subdivision where the zip is.") - zip = fields.Char('Zip') - city = fields.Char('City', help="The name of the city for the zip.") + help="The subdivision where the postal code is.") + postal_code = fields.Char('Postal Code') + city = fields.Char( + "City", help="The city associated with the postal code.") @classmethod def __setup__(cls): - super(Zip, cls).__setup__() + super().__setup__() cls._order.insert(0, ('country', 'ASC')) - cls._order.insert(0, ('zip', 'ASC')) + cls._order.insert(0, ('postal_code', 'ASC')) + + @classmethod + def __register__(cls, module): + # Migration from 5.8: rename zip to postal_code + backend.TableHandler.table_rename('country_zip', cls._table) + table_h = cls.__table_handler__(module) + table_h.column_rename('zip', 'postal_code') + + super().__register__(module) def get_rec_name(self, name): - if self.city and self.zip: - return '%s (%s)' % (self.city, self.zip) + if self.city and self.postal_code: + return '%s (%s)' % (self.city, self.postal_code) else: - return (self.zip or self.city or str(self.id)) + return (self.postal_code or self.city or str(self.id)) @classmethod def search_rec_name(cls, name, clause): @@ -283,6 +294,6 @@ else: bool_op = 'OR' return [bool_op, - ('zip',) + tuple(clause[1:]), + ('postal_code',) + tuple(clause[1:]), ('city',) + tuple(clause[1:]), ] diff -r 7eabfccb0d32 -r d5def6606352 country.xml --- a/country.xml Sat Feb 27 18:16:28 2021 +0100 +++ b/country.xml Wed Mar 31 23:59:33 2021 +0200 @@ -55,38 +55,37 @@ <field name="name">subdivision_tree</field> </record> - <record model="ir.ui.view" id="zip_view_form"> - <field name="model">country.zip</field> + <record model="ir.ui.view" id="postal_code_view_form"> + <field name="model">country.postal_code</field> <field name="type">form</field> - <field name="name">zip_form</field> + <field name="name">postal_code_form</field> </record> - <record model="ir.ui.view" id="zip_view_list"> - <field name="model">country.zip</field> + <record model="ir.ui.view" id="postal_code_view_list"> + <field name="model">country.postal_code</field> <field name="type">tree</field> - <field name="name">zip_list</field> + <field name="name">postal_code_list</field> </record> - <record model="ir.action.act_window" id="act_zip_form"> - <field name="name">Zip</field> - <field name="res_model">country.zip</field> + <record model="ir.action.act_window" id="act_postal_code_form"> + <field name="name">Postal Codes</field> + <field name="res_model">country.postal_code</field> <field name="domain" eval="[('country', 'in', Eval('active_ids'))]" pyson="1"/> </record> - <record model="ir.action.act_window.view" id="act_zip_form_view1"> + <record model="ir.action.act_window.view" id="act_postal_code_form_view1"> <field name="sequence" eval="10"/> - <field name="view" ref="zip_view_list"/> - <field name="act_window" ref="act_zip_form"/> + <field name="view" ref="postal_code_view_list"/> + <field name="act_window" ref="act_postal_code_form"/> </record> - <record model="ir.action.act_window.view" id="act_zip_form_view2"> + <record model="ir.action.act_window.view" id="act_postal_code_form_view2"> <field name="sequence" eval="20"/> - <field name="view" ref="zip_view_form"/> - <field name="act_window" ref="act_zip_form"/> + <field name="view" ref="postal_code_view_form"/> + <field name="act_window" ref="act_postal_code_form"/> </record> - <record model="ir.action.keyword" - id="act_zip_form_keyword1"> + <record model="ir.action.keyword" id="act_postal_code_form_keyword1"> <field name="keyword">form_relate</field> <field name="model">country.country,-1</field> - <field name="action" ref="act_zip_form"/> + <field name="action" ref="act_postal_code_form"/> </record> </data> </tryton> diff -r 7eabfccb0d32 -r d5def6606352 doc/design.rst --- a/doc/design.rst Sat Feb 27 18:16:28 2021 +0100 +++ b/doc/design.rst Wed Mar 31 23:59:33 2021 +0200 @@ -38,14 +38,14 @@ The ISO 3166-2 standard defines codes and names for country subdivisions. These are automatically loaded and updated along with the countries. -.. _model-country.zip: +.. _model-country.postal_code: -Zip -=== +Postal Code +=========== -The *Zip* concept is used to store postal codes, and their relationship to -`Countries <model-country.country>`, `Subdivisions <model-country.subdivision>` -and cities. +The *Postal Code* concept is used to store postal codes, and their relationship +to `Countries <model-country.country>`, `Subdivisions +<model-country.subdivision>` and cities. Depending on the country they relate to these codes are known locally as either postcodes, post codes, :abbr:`PIN (Postal Index Number)` or :abbr:`ZIP (Zone Improvement Plan)` codes. diff -r 7eabfccb0d32 -r d5def6606352 doc/setup.rst --- a/doc/setup.rst Sat Feb 27 18:16:28 2021 +0100 +++ b/doc/setup.rst Wed Mar 31 23:59:33 2021 +0200 @@ -28,12 +28,13 @@ Loading and updating postal codes ================================= -You can use the :command:`trytond_import_zip` script to load and update the -`Postal Codes <model-country.zip>` in Tryton from the `GeoNames Database`_. +You can use the :command:`trytond_import_postal_codes` script to load and update +the `Postal Codes <model-country.postal_code>` in Tryton from the `GeoNames +Database`_. It is run with: .. code-block:: bash - trytond_import_zip -c trytond.conf -d <database> <two_letter_country_code> + trytond_import_postal_codes -c trytond.conf -d <database> <two_letter_country_code> .. _GeoNames Database: https://www.geonames.org/ diff -r 7eabfccb0d32 -r d5def6606352 doc/usage.rst --- a/doc/usage.rst Sat Feb 27 18:16:28 2021 +0100 +++ b/doc/usage.rst Wed Mar 31 23:59:33 2021 +0200 @@ -9,5 +9,5 @@ .. tip:: From a country you can :guilabel:`Open related records` and select - :guilabel:`Zip` to find a list of all the `Postal Codes <model-country.zip>` - for that country. + :guilabel:`Postal Codes` to find a list of all the `Postal Codes + <model-country.postal_code>` for that country. diff -r 7eabfccb0d32 -r d5def6606352 scripts/import_postal_codes.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/import_postal_codes.py Wed Mar 31 23:59:33 2021 +0200 @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. +from __future__ import print_function +import csv +import os +import sys +try: + from urllib.request import urlopen +except ImportError: + from urllib2 import urlopen +import zipfile + +from argparse import ArgumentParser +from io import BytesIO, StringIO + +try: + from progressbar import ProgressBar, Bar, ETA, SimpleProgress +except ImportError: + ProgressBar = None + +try: + from proteus import Model, config +except ImportError: + prog = os.path.basename(sys.argv[0]) + sys.exit("proteus must be installed to use %s" % prog) + + +def clean(code): + sys.stderr.write('Cleaning') + PostalCode = Model.get('country.postal_code') + PostalCode._proxy.delete( + [c.id for c in PostalCode.find([('country.code', '=', code)])], {}) + print('.', file=sys.stderr) + + +def fetch(code): + sys.stderr.write('Fetching') + url = 'https://download.geonames.org/export/zip/%s.zip' % code + responce = urlopen(url) + data = responce.read() + with zipfile.ZipFile(BytesIO(data)) as zf: + data = zf.read('%s.txt' % code) + print('.', file=sys.stderr) + return data + + +def import_(data): + PostalCode = Model.get('country.postal_code') + Country = Model.get('country.country') + Subdivision = Model.get('country.subdivision') + print('Importing', file=sys.stderr) + + def get_country(code): + country = countries.get(code) + if not country: + country, = Country.find([('code', '=', code)]) + countries[code] = country + return country + countries = {} + + def get_subdivision(country, code): + code = '%s-%s' % (country, code) + subdivision = subdivisions.get(code) + if not subdivision: + try: + subdivision, = Subdivision.find([('code', '=', code)]) + except ValueError: + return + subdivisions[code] = subdivision + return subdivision + subdivisions = {} + + if ProgressBar: + pbar = ProgressBar( + widgets=[SimpleProgress(), Bar(), ETA()]) + else: + pbar = iter + f = StringIO(data.decode('utf-8')) + codes = [] + for row in pbar(list(csv.DictReader( + f, fieldnames=_fieldnames, delimiter='\t'))): + country = get_country(row['country']) + for code in ['code1', 'code2', 'code3']: + subdivision = get_subdivision(row['country'], row[code]) + if code == 'code1' or subdivision: + codes.append( + PostalCode(country=country, subdivision=subdivision, + postal_code=row['postal'], city=row['place'])) + PostalCode.save(codes) + + +_fieldnames = ['country', 'postal', 'place', 'name1', 'code1', + 'name2', 'code2', 'name3', 'code3', 'latitude', 'longitude', 'accuracy'] + + +def main(database, codes, config_file=None): + config.set_trytond(database, config_file=config_file) + do_import(codes) + + +def do_import(codes): + for code in codes: + print(code, file=sys.stderr) + code = code.upper() + clean(code) + import_(fetch(code)) + + +def run(): + parser = ArgumentParser() + parser.add_argument('-d', '--database', dest='database', required=True) + parser.add_argument('-c', '--config', dest='config_file', + help='the trytond config file') + parser.add_argument('codes', nargs='+') + + args = parser.parse_args() + main(args.database, args.codes, args.config_file) + + +if __name__ == '__main__': + run() diff -r 7eabfccb0d32 -r d5def6606352 scripts/import_zip.py --- a/scripts/import_zip.py Sat Feb 27 18:16:28 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -#!/usr/bin/env python -# This file is part of Tryton. The COPYRIGHT file at the top level of -# this repository contains the full copyright notices and license terms. -from __future__ import print_function -import csv -import os -import sys -try: - from urllib.request import urlopen -except ImportError: - from urllib2 import urlopen -import zipfile - -from argparse import ArgumentParser -from io import BytesIO, StringIO - -try: - from progressbar import ProgressBar, Bar, ETA, SimpleProgress -except ImportError: - ProgressBar = None - -try: - from proteus import Model, config -except ImportError: - prog = os.path.basename(sys.argv[0]) - sys.exit("proteus must be installed to use %s" % prog) - - -def clean(code): - sys.stderr.write('Cleaning') - Zip = Model.get('country.zip') - Zip._proxy.delete( - [z.id for z in Zip.find([('country.code', '=', code)])], {}) - print('.', file=sys.stderr) - - -def fetch(code): - sys.stderr.write('Fetching') - url = 'https://download.geonames.org/export/zip/%s.zip' % code - responce = urlopen(url) - data = responce.read() - with zipfile.ZipFile(BytesIO(data)) as zf: - data = zf.read('%s.txt' % code) - print('.', file=sys.stderr) - return data - - -def import_(data): - Zip = Model.get('country.zip') - Country = Model.get('country.country') - Subdivision = Model.get('country.subdivision') - print('Importing', file=sys.stderr) - - def get_country(code): - country = countries.get(code) - if not country: - country, = Country.find([('code', '=', code)]) - countries[code] = country - return country - countries = {} - - def get_subdivision(country, code): - code = '%s-%s' % (country, code) - subdivision = subdivisions.get(code) - if not subdivision: - try: - subdivision, = Subdivision.find([('code', '=', code)]) - except ValueError: - return - subdivisions[code] = subdivision - return subdivision - subdivisions = {} - - if ProgressBar: - pbar = ProgressBar( - widgets=[SimpleProgress(), Bar(), ETA()]) - else: - pbar = iter - f = StringIO(data.decode('utf-8')) - zips = [] - for row in pbar(list(csv.DictReader( - f, fieldnames=_fieldnames, delimiter='\t'))): - country = get_country(row['country']) - for code in ['code1', 'code2', 'code3']: - subdivision = get_subdivision(row['country'], row[code]) - if code == 'code1' or subdivision: - zips.append( - Zip(country=country, subdivision=subdivision, - zip=row['postal'], city=row['place'])) - Zip.save(zips) - - -_fieldnames = ['country', 'postal', 'place', 'name1', 'code1', - 'name2', 'code2', 'name3', 'code3', 'latitude', 'longitude', 'accuracy'] - - -def main(database, codes, config_file=None): - config.set_trytond(database, config_file=config_file) - do_import(codes) - - -def do_import(codes): - for code in codes: - print(code, file=sys.stderr) - code = code.upper() - clean(code) - import_(fetch(code)) - - -def run(): - parser = ArgumentParser() - parser.add_argument('-d', '--database', dest='database', required=True) - parser.add_argument('-c', '--config', dest='config_file', - help='the trytond config file') - parser.add_argument('codes', nargs='+') - - args = parser.parse_args() - main(args.database, args.codes, args.config_file) - - -if __name__ == '__main__': - run() diff -r 7eabfccb0d32 -r d5def6606352 setup.py --- a/setup.py Sat Feb 27 18:16:28 2021 +0100 +++ b/setup.py Wed Mar 31 23:59:33 2021 +0200 @@ -151,7 +151,7 @@ country = trytond.modules.country [console_scripts] trytond_import_countries = trytond.modules.country.scripts.import_countries:run [data] - trytond_import_zip = trytond.modules.country.scripts.import_zip:run [GeoNames] + trytond_import_postal_code = trytond.modules.country.scripts.import_postal_codes:run [GeoNames] """, # noqa: E501 test_suite='tests', test_loader='trytond.test_loader:Loader', diff -r 7eabfccb0d32 -r d5def6606352 tests/scenario_country_import.rst --- a/tests/scenario_country_import.rst Sat Feb 27 18:16:28 2021 +0100 +++ b/tests/scenario_country_import.rst Wed Mar 31 23:59:33 2021 +0200 @@ -6,7 +6,7 @@ >>> from proteus import Model >>> from trytond.tests.tools import activate_modules - >>> from trytond.modules.country.scripts import import_countries, import_zip + >>> from trytond.modules.country.scripts import import_countries, import_postal_codes Activate modules:: @@ -20,6 +20,6 @@ >>> import_countries.do_import() -Import ZIP:: +Import postal codes:: - >>> import_zip.do_import(['us']) + >>> import_postal_codes.do_import(['us']) diff -r 7eabfccb0d32 -r d5def6606352 view/postal_code_form.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/view/postal_code_form.xml Wed Mar 31 23:59:33 2021 +0200 @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<!-- This file is part of Tryton. The COPYRIGHT file at the top level of +this repository contains the full copyright notices and license terms. --> +<form> + <label name="country"/> + <field name="country"/> + <label name="subdivision"/> + <field name="subdivision"/> + <label name="postal_code"/> + <field name="postal_code"/> + <label name="city"/> + <field name="city"/> +</form> diff -r 7eabfccb0d32 -r d5def6606352 view/postal_code_list.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/view/postal_code_list.xml Wed Mar 31 23:59:33 2021 +0200 @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<!-- This file is part of Tryton. The COPYRIGHT file at the top level of +this repository contains the full copyright notices and license terms. --> +<tree> + <field name="country" expand="1"/> + <field name="subdivision" expand="1"/> + <field name="postal_code" expand="1"/> + <field name="city" expand="1"/> +</tree> diff -r 7eabfccb0d32 -r d5def6606352 view/zip_form.xml --- a/view/zip_form.xml Sat Feb 27 18:16:28 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -<?xml version="1.0"?> -<!-- This file is part of Tryton. The COPYRIGHT file at the top level of -this repository contains the full copyright notices and license terms. --> -<form> - <label name="country"/> - <field name="country"/> - <label name="subdivision"/> - <field name="subdivision"/> - <label name="zip"/> - <field name="zip"/> - <label name="city"/> - <field name="city"/> -</form> diff -r 7eabfccb0d32 -r d5def6606352 view/zip_list.xml --- a/view/zip_list.xml Sat Feb 27 18:16:28 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -<?xml version="1.0"?> -<!-- This file is part of Tryton. The COPYRIGHT file at the top level of -this repository contains the full copyright notices and license terms. --> -<tree> - <field name="country" expand="1"/> - <field name="subdivision" expand="1"/> - <field name="zip" expand="1"/> - <field name="city" expand="1"/> -</tree>