mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[ADD] nominatim address calls
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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&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>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# generated from manifests external_dependencies
|
||||
bs4
|
||||
geopy
|
||||
jose
|
||||
jwt
|
||||
marshmallow
|
||||
pycountry
|
||||
regula.documentreader.webclient
|
||||
simplejson
|
||||
thefuzz
|
||||
xlrd
|
||||
|
||||
Reference in New Issue
Block a user