[ADD] nominatim address calls

This commit is contained in:
Darío Lodeiros
2024-05-04 15:24:35 +02:00
parent 2e3b69ed74
commit e49e5059c1
5 changed files with 151 additions and 47 deletions

View File

@@ -7,7 +7,7 @@ OCR Klippa
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:0de4876412fe017db56d0ae207c4aa1f4f01394f57851ae281b5d94f5fc20c5f
!! source digest: sha256:2b9fc0252a9368c795df1d59126287bd5538d1a23498d99bfb72658e7a2a6eff
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png

View File

@@ -12,6 +12,7 @@
"depends": [
"pms_api_rest",
],
"external_dependencies": {"python": ["thefuzz", "geopy"]},
"data": [
"data/pms_ocr_klippa_data.xml",
"views/res_partner_id_category_views.xml",

View File

@@ -1,11 +1,16 @@
import logging
from datetime import date, datetime
import requests
from dateutil.relativedelta import relativedelta
from geopy.geocoders import Nominatim
from thefuzz import process
from odoo import _, fields, models
from odoo.exceptions import ValidationError
_logger = logging.getLogger(__name__)
class PmsProperty(models.Model):
_inherit = "pms.property"
@@ -21,7 +26,7 @@ class PmsProperty(models.Model):
self.env["ir.config_parameter"].sudo().get_param("ocr_klippa_api_key")
)
document = []
if image_base_64_back:
if image_base_64_front:
document.append(image_base_64_front)
if image_base_64_back:
document.append(image_base_64_back)
@@ -54,30 +59,7 @@ class PmsProperty(models.Model):
continue
# Residence Address --------------------------------------------------
if key == "address" and value:
if "street_name" in value:
mapped_data["residence_street"] = value["street_name"] + (
" " + value["house_number"] if "house_number" in value else ""
)
if "city" in value:
mapped_data["residence_city"] = value["city"]
if "postcode" in value:
mapped_data["zip"] = value["postcode"]
if "province" in value:
mapped_data["residence_state_id"] = (
self.env["res.country.state"]
.search(
[
("name", "ilike", value["province"]),
(
"country_id",
"=",
self._get_country_id(value.get("country", False)),
),
]
)
.id
or False
)
mapped_data = self._complete_residence_address(value, mapped_data)
# Document Data --------------------------------------------------
elif key == "issuing_country" and value:
@@ -87,7 +69,9 @@ class PmsProperty(models.Model):
elif key == "document_type" and value:
mapped_data["document_type"] = self._get_document_type(
klippa_type=value,
klippa_subtype=document_data.get("document_subtype", False),
country_id=self._get_country_id(
document_data.get("document_country_id", False)
),
)
elif key == "personal_number" and value:
mapped_data["document_number"] = value
@@ -100,10 +84,12 @@ class PmsProperty(models.Model):
and value
and not document_data.get("date_of_issue", False)
):
mapped_data["document_expiration_date"] = self._calc_expedition_date(
mapped_data["document_expedition_date"] = self._calc_expedition_date(
document_class_code=self._get_document_type(
klippa_type=document_data.get("document_class_code", False),
klippa_subtype=document_data.get("document_subtype", False),
country_id=self._get_country_id(
document_data.get("document_country_id", False)
),
),
date_of_expiry=value,
age=False,
@@ -135,29 +121,36 @@ class PmsProperty(models.Model):
mapped_data["nationality"] = self._get_country_id(value)
return mapped_data
def _calc_expedition_date(self, document_type, date_of_expiry, age, date_of_birth):
def _calc_expedition_date(
self, document_class_code, date_of_expiry, age, date_of_birth
):
result = False
person_age = False
if age and age.value != "":
person_age = int(age.value)
elif date_of_birth and date_of_birth.value != "":
if age:
person_age = age
elif date_of_birth and date_of_birth.get("value") != "":
date_of_birth = datetime.strptime(
date_of_birth.value.replace("-", "/"), "%Y-%m-%dT%H:%M:%S"
date_of_birth.get("value"), "%Y-%m-%dT%H:%M:%S"
).date()
person_age = relativedelta(date.today(), date_of_birth).years
if date_of_expiry and date_of_expiry.value != "" and person_age:
if date_of_expiry and date_of_expiry != "" and person_age:
date_of_expiry = datetime.strptime(
date_of_expiry.value.replace("-", "/"), "%Y-%m-%dT%H:%M:%S"
date_of_expiry, "%Y-%m-%dT%H:%M:%S"
).date()
if person_age < 30:
result = date_of_expiry - relativedelta(years=5)
elif person_age >= 30 and document_type and document_type.code == "P":
elif (
person_age >= 30
and document_class_code
and document_class_code.code == "P"
):
result = date_of_expiry - relativedelta(years=10)
elif 30 <= person_age < 70:
result = date_of_expiry - relativedelta(years=10)
return result.isoformat() if result else False
return result if result else False
def _get_document_type(self, klippa_type, klippa_subtype):
def _get_document_type(self, klippa_type, country_id):
document_type_id = False
document_type_ids = self.env["res.partner.id_category"].search(
[
("klippa_code", "=", klippa_type),
@@ -165,11 +158,15 @@ class PmsProperty(models.Model):
)
if not document_type_ids:
raise ValidationError(_(f"Document type not found: {klippa_type}"))
document_type_id = document_type_ids[0]
if len(document_type_ids) > 1:
document_type_id = document_type_ids.filtered(
lambda r: r.klippa_subtype_code == klippa_subtype
).id
lambda r: country_id in r.country_ids.ids
)
if not document_type_id:
document_type_id = document_type_ids.filtered(lambda r: not r.country_ids)[
0
]
return document_type_id
def _get_country_id(self, country_code):
@@ -185,3 +182,104 @@ class PmsProperty(models.Model):
return origin_surname.split(" ")
else:
return [origin_surname, ""]
def _complete_residence_address(self, value, mapped_data):
"""
This method tries to complete the residence address with the given data,
first we use the thefuzz library looking for acceptable matches
in the province and/or country name.
Once these data are completed, if the residence address has not been completed
we try to use the geopy library to complete the address with the data
"""
street_name = False
if "street_name" in value:
mapped_data["residence_street"] = value["street_name"] + (
" " + value["house_number"] if "house_number" in value else ""
)
street_name = value["street_name"]
if "city" in value:
mapped_data["residence_city"] = value["city"]
if "province" in value:
country_record = self._get_country_id(value.get("country", False))
domain = []
if country_record:
domain.append(("country_id", "=", country_record))
candidates = process.extractOne(
value["province"],
self.env["res.country.state"].search(domain).mapped("name"),
)
if candidates[1] >= 90:
country_state = self.env["res.country.state"].search(
domain + [("name", "=", candidates[0])]
)
mapped_data["residence_state_id"] = country_state.id
if not country_record:
mapped_data["country_id"] = country_state.country_id.id
else:
mapped_data["residence_state_id"] = None
if "country" in value and not mapped_data.get("country_id", False):
country_record = self._get_country_id(value["country"])
mapped_data["country_id"] = country_record
if "postcode" in value:
mapped_data["zip"] = value["postcode"]
address_data_dict = {
"zip": mapped_data.get("zip") or None,
"country_id": mapped_data.get("country_id") or None,
"countryState": mapped_data.get("country_state") or None,
"residence_city": mapped_data.get("residence_city") or None,
"residence_street": mapped_data.get("residence_street") or None,
}
# If we have one ore more values in address_data_dict, but not all,
# we try to complete the address
if any(address_data_dict.values()) and not all(address_data_dict.values()):
geolocator = Nominatim(user_agent="roomdoo_pms")
search_address_str = f"{street_name}, {mapped_data.get('residence_city', '')}, {mapped_data.get('zip', '')}, {mapped_data.get('country_id', '')}"
location = geolocator.geocode(
search_address_str,
addressdetails=True,
timeout=5,
language="en",
)
if not location:
search_address_str = f"{mapped_data.get('residence_city', '')}, {mapped_data.get('zip', '')}, {mapped_data.get('country_id', '')}"
location = geolocator.geocode(
search_address_str,
addressdetails=True,
timeout=5,
language="en",
)
if location:
if not mapped_data.get("zip", False):
mapped_data["zip"] = location.raw.get("address", {}).get(
"postcode", False
)
if not mapped_data.get("country_id", False):
country_match_name = process.extractOne(
location.raw.get("address", {}).get("country", False),
self.env["res.country"].search([]).mapped("name"),
)
if country_match_name[1] >= 90:
country_record = self.env["res.country"].search(
[("name", "=", country_match_name[0])]
)
mapped_data["country_id"] = country_record.id
if not mapped_data.get("country_state", False):
country_state_record = process.extractOne(
location.raw.get("address", {}).get("province", False),
self.env["res.country.state"].search([]).mapped("name"),
)
if country_state_record[1] >= 90:
country_state = self.env["res.country.state"].search(
[("name", "=", country_state_record[0])]
)
mapped_data["country_state"] = country_state.id
if not mapped_data.get("residence_city", False):
mapped_data["residence_city"] = location.raw.get("address", {}).get(
"city", False
)
if not mapped_data.get("residence_street", False):
mapped_data["residence_street"] = location.raw.get(
"address", {}
).get("road", False)
return mapped_data

View File

@@ -9,10 +9,11 @@
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
@@ -275,7 +276,7 @@ pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -301,7 +302,7 @@ span.option {
span.pre {
white-space: pre }
span.problematic {
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
@@ -367,7 +368,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:0de4876412fe017db56d0ae207c4aa1f4f01394f57851ae281b5d94f5fc20c5f
!! source digest: sha256:2b9fc0252a9368c795df1d59126287bd5538d1a23498d99bfb72658e7a2a6eff
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/pms/tree/14.0/pms_ocr_klippa"><img alt="OCA/pms" src="https://img.shields.io/badge/github-OCA%2Fpms-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/pms-14-0/pms-14-0-pms_ocr_klippa"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/pms&amp;target_branch=14.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>Module to connect the OCR Klippa with the pms</p>
@@ -413,7 +414,9 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>

View File

@@ -1,9 +1,11 @@
# generated from manifests external_dependencies
bs4
geopy
jose
jwt
marshmallow
pycountry
regula.documentreader.webclient
simplejson
thefuzz
xlrd