Source code for route4me.sdk.models

# -*- coding: utf-8 -*-

import pydash

# reimport enums for convenience:
from ..enums import AlgorithmTypeEnum
from ..enums import OptimizationStateEnum
from ..enums import OptimizationQualityEnum
from ..enums import OptimizationFactorEnum
from ..enums import RouteMetricEnum
from ..enums import TravelModeEnum
from ..enums import DeviceTypeEnum

from ..enums import AddressStopTypeEnum

from route4me.sdk._internals.decorators import dict_property
from route4me.sdk._internals.decorators import dict_enum_property
from route4me.sdk._internals import timestamp_and_seconds2datetime
from route4me.sdk._internals import datetime2timestamp_and_seconds


class BaseModel(dict):
	def __init__(self, raw=None):
		super(BaseModel, self).__init__(raw)

	@property
	def raw(self):
		"""
		Provides access to raw model data, as it would be sent to Route4Me API

		:getter: Get, property is readonly
		:rtype: dict
		"""
		return self


class TiedListWrapper(list):
	def __init__(self, parent, key, anytype):
		self._parent = parent
		self._key = key
		self._t = anytype

		r = self._raw
		if r is None:
			r = []

		super(TiedListWrapper, self).__init__(r)

	@property
	def _raw(self):
		"""
		Mostly - internal field.

		Raw - is a low-level content. It is a :class:`list` instance stored
		in the parent object. Raw - because it is a low-level presentation,
		which will be used to send data to R4M

		:returns: A raw object from parent
		:rtype: list
		"""
		return pydash.get(self._parent, self._key)

	def link(self):
		"""
		Put SELF instance to the parent object

		If parent contains None = put self
		If parent already contains SELF - do nothing
		If parent contains smth. - load content to self and connect
		"""
		r = self._raw

		if r is not None and r != self:
			super(TiedListWrapper, self).__init__(r)

		self._parent.raw[self._key] = self

	def unlink(self):
		self._parent.raw[self._key] = None

	def unset(self):
		self._parent.pop(self._key)

	def __getitem__(self, index):
		r = super(TiedListWrapper, self).__getitem__(index)
		return self._t(r)

	def __setitem__(self, index, value):
		self.link()
		r = value
		if isinstance(r, BaseModel):
			r = r.raw
		res = super(TiedListWrapper, self).__setitem__(index, r)
		return res

	def __iter__(self):
		for r in list.__iter__(self):
			yield self._t(r)

	def append(self, item):
		r = item
		if isinstance(r, BaseModel):
			r = r.raw

		self.link()
		res = super(TiedListWrapper, self).append(r)
		return res


[docs]class Address(BaseModel): """ Single *Address*, also known as *Route Destination* .. seealso:: - **api doc**: https://route4me.io/docs/#addresses - **schema**: https://github.com/route4me/route4me-json-schemas/blob/master/Address.dtd """
[docs] def __init__(self, raw=None): """ Create instance **LOCALLY**. Use :meth:`~route4me.sdk.endpoints.optimizations.Optimizations.add_address` or :meth:`~route4me.sdk.endpoints.routes.Routes.add_address` to create new Address in the Route4Me API :param raw: Raw values for new address, example: \ `add address to optimization \ <https://route4me.io/docs/#insert-an-address-into-an-optimization>`_, \ defaults to :data:`None` :type raw: dict, optional """ if raw is None: raw = { # 'manifest': {}, # 'path_to_next': [], # 'directions': [], } super(Address, self).__init__(raw=raw)
@property def ID(self): """ Route Destination ID Internal unique address identifier :getter: Gets value :setter: Sets value :rtype: str """ return self.raw.get('route_destination_id') @dict_property('alias', str) def name(self, value): """ Address Alias / Address Name .. note:: In Route4Me API this field is known as ``alias`` <AUTO> """ return value # ========================================================================== @dict_enum_property('address_stop_type', AddressStopTypeEnum) def address_stop_type(self, value): """ Address stop type <AUTO> """ return value # ========================================================================== @dict_property('address', str) def address(self, value): """ The route's Address Line .. note:: In Route4Me API this field is known as ``address`` <AUTO> """ return value @dict_property('lat', float) def latitude(self, value): """ Latitude Shoud be -90.0 ≤ lat ≤ 90 .. note:: In Route4Me API this field is known as ``lat`` <AUTO> """ return value @dict_property('lng', float) def longitude(self, value): """ Longitude Shoud be -180.0 ≤ lng ≤ 180 .. note:: In Route4Me API this field is known as ``lng`` <AUTO> """ return value @dict_property('route_id', str) def route_id(self, value): """ Route ID Parent route <AUTO> """ return value @dict_property('sequence_no', int) def sequence_no(self, value): """ The sequence number for the address <AUTO> """ return value @dict_property('time', int) def service_time_sec(self, value): """ Service time (seconds) .. note:: In Route4Me API this field is known as ``time`` <AUTO> """ return value @dict_property('is_depot', bool) def is_depot(self, value): """ Indicates that this address is a depot <AUTO> """ return value @dict_property('geocoded', bool) def geocoded(self, value): """ :data:`True` means the :attr:`address_string` field was successfully geocoded <AUTO> """ return value @dict_property('failed_geocoding', bool) def failed_geocoding(self, value): """ :data:`True` means there was a geocoding attempt which failed. \ :data:`False` means success or no geocoding <AUTO> """ return value
[docs]class Optimization(BaseModel): """ Optimization problem (or simple *Optimization*) .. seealso:: - **schema**: https://github.com/route4me/route4me-json-schemas/blob/master/Optimization_response.dtd """
[docs] def __init__(self, raw=None): """ Create instance **LOCALLY**. Use :meth:`~route4me.sdk.endpoints.optimizations.Optimizations.create` to create new Optimization Problem in the Route4Me API :param raw: Raw values for new optimization, example: \ `create optimization <https://route4me.io/docs/#create-an-optimization>`_, \ defaults to None :type raw: dict, optional """ if raw is None: raw = { # 'parameters': { # 'store_route': True, # 'route_max_duration': 24 * 60 * 60, # 'route_time': 0, # 'route_date': unix_timestamp_today() # }, # 'user_errors': [], # 'optimization_errors': [], # 'links': {}, # 'addresses': [], # 'routes': [], } super(Optimization, self).__init__(raw=raw) self._addresses = TiedListWrapper( parent=self, key='addresses', anytype=Address )
# ========================================================================== @property def ID(self): return self.raw.get('optimization_problem_id') @dict_property('parameters.route_name', str) def name(self, value): """ The name of this optimization problem. This name will be accessible in the search API, and also will be displayed on the mobile device of a user <AUTO> """ return value # ========================================================================== @dict_enum_property('parameters.algorithm_type', AlgorithmTypeEnum) def algorithm_type(self, value): """ The algorithm type to be used <AUTO> """ return value @dict_enum_property('state', OptimizationStateEnum) def state(self, value): """ The current state of the optimization <AUTO> """ return value @dict_enum_property('parameters.optimization_quality', OptimizationQualityEnum) def quality(self, value): """ Optimization quality There are 3 types of optimization qualities that are optimizations goals, see :class:`~route4me.sdk.enums.OptimizationQualityEnum` <AUTO> """ return value @dict_enum_property('parameters.metric', RouteMetricEnum) def metric(self, value): """ Metric <AUTO> """ return value @dict_enum_property('parameters.travel_mode', TravelModeEnum) def travel_mode(self, value): """ Travel mode The mode of travel that the directions should be optimized for <AUTO> """ return value @dict_enum_property('parameters.device_type', DeviceTypeEnum) def device_type(self, value): """ Device type The type of the device that is creating this Optimization Problem <AUTO> """ return value @dict_enum_property('parameters.optimize', OptimizationFactorEnum) def optimization_factor(self, value): """ The driving directions can be generated biased for this selection. This has no impact on route sequencing. .. note:: In Route4Me API this enum also known as ``optimize`` <AUTO> """ return value # ========================================================================== # @dict_property('parameters.store_route', bool) # def store_route(self, value): # """ # Store Route # <AUTO> # """ # return value @dict_property('parameters.parts', int) def parts(self, value): """ Legacy feature which permits a user to request an example number of optimized routes <AUTO> """ return value @dict_property('parameters.disable_optimization', bool) def disable_optimization(self, value): """ By disabling optimization, the route optimization engine will not resequence stops in your optimization problem <AUTO> """ return value @dict_property('parameters.route_max_duration', int) def route_max_duration_sec(self, value): """ Route maximum duration How many seconds a route can last at most. Default is 86400 seconds = 24 hours <AUTO> """ return value @dict_property('parameters.rt', bool) def round_trip(self, value): """ Round Trip The tour type of this route. The optimization engine changes its behavior for round trip routes .. note:: In Route4Me API this parameter is also known as ``parameters.rt`` <AUTO> """ return value @dict_property('parameters.member_id', int) def member_id(self, value): """ Member ID User ID who is assigned to this Optimization Problem <AUTO> """ return value @dict_property('parameters.device_id', str) def device_id(self, value): """ Device ID 32 Character MD5 String ID of the device that was used to plan this route <AUTO> """ return value @dict_property('parameters.vehicle_id', str) def vehicle_id(self, value): """ Vehicle ID The unique internal id of a vehicle <AUTO> """ return value # ========================================================================== @property def route_datetime(self): """ Route DateTime .. note:: In Route4Me API this parameter is broken into 2 parts: fields ``parameters.route_date`` and ``parameters.route_time`` :getter: returns a :class:`~datetime.datetime` combining values from \ two fields :setter: sets ``parameters.route_date`` and ``parameters.route_time`` \ in the raw data :rtype: ~datetime.datetime """ # from JSON Schema: # "route_date": { "type": ["integer", "null"], # // ... # "description": "The route start date in UTC, unix timestamp seconds. # Used to show users when the route will begin, also used for # reporting and analytics" # }, # "route_time": { "type": "integer", # // ... # "description": "Time when the route starts (relative to route_date) # (Seconds). UTC timezone as well" # # So, we have UNIX timestamp (seconds) and seconds from day start. Lets # create date d = pydash.get(self.raw, 'parameters.route_date') t = pydash.get(self.raw, 'parameters.route_time') return timestamp_and_seconds2datetime(d, t) @route_datetime.setter def route_datetime(self, value): d, t = datetime2timestamp_and_seconds(value) pydash.set_(self.raw, 'parameters.route_date', d) pydash.set_(self.raw, 'parameters.route_time', t) # ========================================================================== @property def addresses(self): """ Addresses included to this Optimization Problem <AUTO> """ return self._addresses @property def links(self): """ Links to the GET operations for the optimization problem :getter: Get :setter: Set :deleter: Del :rtype: dict or None """ return pydash.get(self.raw, 'links') @links.deleter def links(self, value): return self.raw.pop('links')