You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
6.0 KiB

  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # Base Location Geonames Import module for OpenERP
  5. # Copyright (C) 2014 Akretion (http://www.akretion.com)
  6. # @author Alexis de Lattre <alexis.delattre@akretion.com>
  7. # Copyright (C) 2014 Agile Business Group (http://www.agilebg.com)
  8. # @author Lorenzo Battistini <lorenzo.battistini@agilebg.com>
  9. #
  10. # This program is free software: you can redistribute it and/or modify
  11. # it under the terms of the GNU Affero General Public License as
  12. # published by the Free Software Foundation, either version 3 of the
  13. # License, or (at your option) any later version.
  14. #
  15. # This program is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU Affero General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU Affero General Public License
  21. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. #
  23. ##############################################################################
  24. from openerp import models, fields, api, _
  25. from openerp.exceptions import Warning
  26. import requests
  27. import tempfile
  28. import StringIO
  29. import zipfile
  30. import os
  31. import logging
  32. try:
  33. import unicodecsv
  34. except ImportError:
  35. unicodecsv = None
  36. logger = logging.getLogger(__name__)
  37. class BetterZipGeonamesImport(models.TransientModel):
  38. _name = 'better.zip.geonames.import'
  39. _description = 'Import Better Zip from Geonames'
  40. _rec_name = 'country_id'
  41. country_id = fields.Many2one('res.country', 'Country', required=True)
  42. title_case = fields.Boolean(
  43. string='Title Case',
  44. help='Converts retreived city and state names to Title Case.',
  45. )
  46. @api.model
  47. def transform_city_name(self, city, country):
  48. """Override it for transforming city name (if needed)
  49. :param city: Original city name
  50. :param country: Country record
  51. :return: Transformed city name
  52. """
  53. return city
  54. @api.model
  55. def _domain_search_better_zip(self, row, country):
  56. return [('name', '=', row[1]),
  57. ('city', '=', self.transform_city_name(row[2], country)),
  58. ('country_id', '=', country.id)]
  59. @api.model
  60. def _prepare_better_zip(self, row, country):
  61. state = self.select_or_create_state(row, country)
  62. vals = {
  63. 'name': row[1],
  64. 'city': self.transform_city_name(row[2], country),
  65. 'state_id': state.id,
  66. 'country_id': country.id,
  67. }
  68. return vals
  69. @api.model
  70. def create_better_zip(self, row, country):
  71. if row[0] != country.code:
  72. raise Warning(
  73. _("The country code inside the file (%s) doesn't "
  74. "correspond to the selected country (%s).")
  75. % (row[0], country.code))
  76. logger.debug('ZIP = %s - City = %s' % (row[1], row[2]))
  77. if (self.title_case):
  78. row[2] = row[2].title()
  79. row[3] = row[3].title()
  80. if row[1] and row[2]:
  81. zip_model = self.env['res.better.zip']
  82. zips = zip_model.search(self._domain_search_better_zip(
  83. row, country))
  84. if zips:
  85. return zips[0]
  86. else:
  87. vals = self._prepare_better_zip(row, country)
  88. if vals:
  89. return zip_model.create(vals)
  90. else:
  91. return False
  92. @api.model
  93. def select_or_create_state(
  94. self, row, country, code_row_index=4, name_row_index=3):
  95. states = self.env['res.country.state'].search([
  96. ('country_id', '=', country.id),
  97. ('code', '=', row[code_row_index]),
  98. ])
  99. if len(states) > 1:
  100. raise Warning(
  101. _("Too many states with code %s for country %s")
  102. % (row[code_row_index], country.code))
  103. if len(states) == 1:
  104. return states[0]
  105. else:
  106. return self.env['res.country.state'].create({
  107. 'name': row[name_row_index],
  108. 'code': row[code_row_index],
  109. 'country_id': country.id
  110. })
  111. @api.one
  112. def run_import(self):
  113. zip_model = self.env['res.better.zip']
  114. country_code = self.country_id.code
  115. config_url = self.env['ir.config_parameter'].get_param(
  116. 'geonames.url',
  117. default='http://download.geonames.org/export/zip/%s.zip')
  118. url = config_url % country_code
  119. logger.info('Starting to download %s' % url)
  120. res_request = requests.get(url)
  121. if res_request.status_code != requests.codes.ok:
  122. raise Warning(
  123. _('Got an error %d when trying to download the file %s.')
  124. % (res_request.status_code, url))
  125. # Store current record list
  126. zips_to_delete = zip_model.search(
  127. [('country_id', '=', self.country_id.id)])
  128. f_geonames = zipfile.ZipFile(StringIO.StringIO(res_request.content))
  129. tempdir = tempfile.mkdtemp(prefix='openerp')
  130. f_geonames.extract('%s.txt' % country_code, tempdir)
  131. logger.info('The geonames zipfile has been decompressed')
  132. data_file = open(os.path.join(tempdir, '%s.txt' % country_code), 'r')
  133. data_file.seek(0)
  134. logger.info('Starting to create the better zip entries')
  135. for row in unicodecsv.reader(
  136. data_file, encoding='utf-8', delimiter=' '):
  137. zip = self.create_better_zip(row, self.country_id)
  138. if zip in zips_to_delete:
  139. zips_to_delete -= zip
  140. data_file.close()
  141. if zips_to_delete:
  142. zips_to_delete.unlink()
  143. logger.info('%d better zip entries deleted for country %s' %
  144. (len(zips_to_delete), self.country_id.name))
  145. logger.info(
  146. 'The wizard to create better zip entries from geonames '
  147. 'has been successfully completed.')
  148. return True