Remove unfinished modules bemade_planning_travel and bemade_geo_routing

This commit is contained in:
Marc Durepos 2024-05-15 21:24:05 -04:00
parent d605932e02
commit e03d7a0669
10 changed files with 0 additions and 270 deletions

View file

@ -1 +0,0 @@
from . import models

View file

@ -1,35 +0,0 @@
#
# Bemade Inc.
#
# Copyright (C) July 2023 Bemade Inc. (<https://www.bemade.org>).
# Author: Marc Durepos (Contact : marc@bemade.org)
#
# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1)
# It is forbidden to publish, distribute, sublicense, or sell copies of the Software
# or modified copies of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
{
'name': 'Bemade Geo Distance',
'version': '17.0.1.0.0',
'summary': 'Utility module for calculating the distance driving between addresses.',
'description': """Module for calculating the driving distance between addresses using the Google Route API.
**IMPORTANT NOTE:** You must have an active Google Route API key configured in the Geo Localization
settings (under General Settings).""",
'category': 'Generic Modules/Others',
'author': 'Bemade Inc.',
'website': 'https://www.bemade.org',
'license': 'OPL-1',
'depends': ['base_geolocalize'],
'data': [],
'demo': [],
'installable': True,
'auto_install': False,
}

View file

@ -1 +0,0 @@
from . import bemade_geo_router

View file

@ -1,95 +0,0 @@
from odoo import models, fields, tools, _, api
from odoo.exceptions import UserError
import requests
import logging
import datetime
from typing import Literal
_logger = logging.getLogger(__name__)
class GeoRouter(models.AbstractModel):
""" Abstract class used to call Google Route API and convert responses
into useful distance and route data.
"""
_name = 'base.geo_router'
_description = "Geographic Router"
units = Literal['metric', 'imperial']
@api.model
def _get_api_key(self):
apikey = self.env['ir.config_parameter'].sudo().get_param('base_geolocalize.google_map_api_key')
if not apikey:
raise UserError(_(
"API key for GeoCoding (Places) required.\n"
"Visit https://developers.google.com/maps/documentation/geocoding/get-api-key for more information."
))
return apikey
@api.model
def get_driving_distance_time(self, origin, destination, departure_time: datetime.datetime = None,
arrival_time: datetime.datetime = None, units: units = 'metric',
avoid_tolls=False, avoid_highways=False, avoid_ferries=False) -> (float, float):
""" Calculates the route between two addresses and returns the distance it either in Kilometers or Miles and
the time in minutes.
:param origin: The origin address (res.partner) from which to drive (origin).
:param destination: The destination address (res.partner).
:param departure_time: The departure time to use when planning the route (traffic aware).
Cannot be set simultaneously with arrival_time.
:param arrival_time: The arrival time to use when planning the route (traffic aware).
Cannot be set simultaneously with departure_time.
:param units: Type of units to return: 'imperial' for Miles, 'metric' for Kilometers.
:param avoid_tolls: Whether Google should calculate a route avoiding tolls.
:param avoid_highways: Whether Google should calculate a route avoiding highways.
:param avoid_ferries: Whether Google should calculate a route avoiding ferries.
:returns: tuple(distance: float, time: float)
"""
if departure_time and arrival_time:
raise ValueError("Route cannot be calculated with both an arrival time and departure time.")
apikey = self._get_api_key()
origin_addr = self.env['base.geocoder'].geo_query_address(street=origin.street, zip=origin.zip,
city=origin.city, state=origin.state_id.name,
country=origin.country_id.name)
destination_addr = self.env['base.geocoder'].geo_query_address(street=destination.street, zip=destination.zip,
city=destination.city,
state=destination.state_id.name,
country=destination.country_id.name)
params = {
'origin': {
'address': origin_addr,
},
'destination': {
'address': destination_addr,
},
'travelMode': 'DRIVE',
'computeAlternativeRoutes': False,
'routeModifiers': {
'avoidTolls': avoid_tolls,
'avoidHighways': avoid_highways,
'avoidFerries': avoid_ferries,
},
'languageCode': 'en-US',
'units': units,
}
if departure_time or arrival_time:
params.update({
'routingPreference': 'TRAFFIC_AWARE',
'departureTime' if departure_time else 'arrivalTime': departure_time or arrival_time
})
url = "https://routes.googleapis.com/directions/v2:computeRoutes"
distance_field = 'distanceMeters' if units == 'metric' else 'distanceMiles'
result = requests.post(url,
json=params,
headers={'X-Goog-Api-Key': apikey,
'Content-Type': 'application/json',
'X-Goog-FieldMask': f"routes.duration,routes.{distance_field}"}).json()
if 'routes' not in result:
_logger.error(f"Failed to retrieve route between {origin_addr} and {destination_addr}. "
f"Google response: {result}.")
route = result['routes'][0]
distance = (route[distance_field] / 1000) if units == 'metric' else route[distance_field]
time = int(route['duration'].strip('s')) / 60
return distance, time

View file

@ -1 +0,0 @@
from . import test_geo_router

View file

@ -1,53 +0,0 @@
from odoo.tests import TransactionCase, tagged
import datetime
import pytz
@tagged('-at_install', 'post_install')
class TestGeoRouter(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.apikey = cls.env['base.geo_router']._get_api_key()
def test_distance_time_basic(self):
bemade, montreal = self._generate_partners()
distance, time = self.env['base.geo_router'].get_driving_distance_time(bemade, montreal)
self.assertTrue(20 < distance < 40)
self.assertTrue(12 < time < 60)
def test_time_traffic(self):
bemade, montreal = self._generate_partners()
est = pytz.timezone('US/Eastern')
today_naive = datetime.date.today()
today = datetime.datetime(today_naive.year, today_naive.month, today_naive.day, 8, 30, tzinfo=est)
arrival_time_local = today + datetime.timedelta(days=(2 - today.weekday()) % 7)
arrival_time_utc = arrival_time_local.astimezone(pytz.utc)
arrival_time = arrival_time_utc.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
print(arrival_time)
distance, time = self.env['base.geo_router'].get_driving_distance_time(bemade, montreal,
arrival_time=arrival_time)
self.assertTrue(25 < time < 90)
@classmethod
def _generate_partners(self):
bemade = self.env['res.partner'].create({
'name': 'Bemade Inc.',
'street': '255 North Montcalm Blvd.',
'city': 'Candiac',
'zip': 'J5R 3L6',
'state_id': self.env.ref('base.state_ca_qc').id,
'country_id': self.env.ref('base.ca').id,
})
montreal = self.env['res.partner'].create({
'name': 'City of Montreal',
'street': '220 East René-Lévesque Blvd.',
'city': 'Montreal',
'zip': 'H2X 3A7',
'state_id': self.env.ref('base.state_ca_qc').id,
'country_id': self.env.ref('base.ca').id,
})
return bemade, montreal

View file

@ -1 +0,0 @@
from . import models

View file

@ -1,40 +0,0 @@
#
# Bemade Inc.
#
# Copyright (C) July 2023 Bemade Inc. (<https://www.bemade.org>).
# Author: Marc Durepos (Contact : marc@bemade.org)
#
# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1)
# It is forbidden to publish, distribute, sublicense, or sell copies of the Software
# or modified copies of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
{
'name': 'Planning Travel Time',
'version': '15.0.1.0.0',
'summary': "Plan travel time for services planned via Odoo's Planning module.",
'description': """
Adds the ability to add travel planning records to existing planning records
linked to sale order lines. Travel planning records book time in the scheduled
resource's calendar. Travel distance is based on the resource's work address, if
available. Otherwise, travel is assumed to be between the client location and
the company's address.""",
'category': 'Services/Field Service',
'author': 'Bemade Inc.',
'website': 'https://www.bemade.org',
'license': 'OPL-1',
'depends': ['sale_planning',
'industry_fsm',
'bemade_geo_routing'],
'data': [''],
'demo': [''],
'installable': True,
'auto_install': False,
}

View file

@ -1 +0,0 @@
from . import planning_slot

View file

@ -1,42 +0,0 @@
from odoo import models, fields, api
class PlanningSlot(models.Model):
_inherit = "planning.slot"
inbound_travel_slot = fields.Many2one(comodel_name="planning.slot",
string="Inbound Travel Plan", )
outbound_travel_slot = fields.Many2one(comodel_name="planning.slot",
string="Outbound Travel Plan")
# Fields for travel slots
travel_origin_slot = fields.One2many(comodel_name="planning.slot",
inverse_name="outbound_travel_slot")
travel_destination_slot = fields.One2many(comodel_name="planning.slot",
inverse_name="inbound_travel_slot")
is_travel_slot = fields.Boolean()
def action_plan_travel(self):
# Get previous and next planning records for the same day, same resource
for rec in self:
prev_slot = rec._get_previous_same_day_same_resource_slot()
next_slot = rec._get_next_same_day_same_resource_slot()
def _get_previous_same_day_same_resource_slot(self):
start = self.start_datetime.replace(hour=0, minute=0, second=0,
microsecond=0)
domain = [('is_travel_slot', '=', False),
('start_datetime', '>=', start),
('end_datetime', '<=', self.start_datetime),
('resource_id', '=', self.resource_id.id)]
return self.env['planning.slot'].search(domain)
def _get_next_same_day_same_resource_slot(self):
end = self.end_datetime.replace(hour=23, minute=59, second=59,
microsecond=999999)
domain = [('is_travel_slot', '=', False),
('start_datetime', '>=', self.end_datetime),
('end_datetime', '<=', end),
('resource_id', '=', self.resource_id.id)]
return self.env['planning.slot'].search(domain)