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>