Coverage for api/services/recommendations.py: 21%
72 statements
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-10 03:02 +0300
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-10 03:02 +0300
1from typing import Any
3import aiohttp
4import config
5from bson import ObjectId
6from exceptions import StopsLimitedInfo
7from services.microservice_connector import RecommendationsConnector
8from sotrans_models.models.orders.order import (
9 OrderDBModel,
10 OrderUpdateModel,
11 RecommendationMatch,
12 RecommendationServiceOrderModel,
13 RecommendationServiceUpdateOrderModel,
14 StopModel,
15)
18def check_location(stop: StopModel) -> bool:
19 if not stop.address.location:
20 return False
21 if not (
22 stop.address.location.latitude and stop.address.location.longitude
23 ):
24 return False
25 return True
28def normalize_stops(order: OrderDBModel | OrderUpdateModel):
29 order.stops = list(filter(lambda stop: stop.datetime, order.stops))
30 if len(order.stops) < 2:
31 raise StopsLimitedInfo
32 order.stops.sort(key=lambda stop: stop.datetime)
33 if not (
34 check_location(order.stops[0]) and check_location(order.stops[-1])
35 ):
36 raise StopsLimitedInfo
39def normalize_datetime_to_msc(order_data: dict[str, Any]):
40 for date in ("finish_date", "start_date"):
41 date_value: str | None = order_data.get(date)
42 if date_value:
43 date_value = date_value.rstrip("Z")
44 if len(date_value) > 5 and date_value[-6] not in ("-", "+"):
45 order_data[date] = f"{date_value}+03:00"
48class APIQuerier:
49 API_URL = config.RecommendationAPIConfig.RECOMMENDATION_API_URL
51 def __init__(self, connector: RecommendationsConnector):
52 self.connector = connector
54 def _form_rec_domain(
55 self, order_resource: OrderDBModel, *, update: bool
56 ) -> dict[str, Any]:
57 model = (
58 RecommendationServiceUpdateOrderModel
59 if update
60 else RecommendationServiceOrderModel
61 )
62 create_entity = model(
63 id=order_resource.id,
64 start_date=order_resource.stops[0].datetime,
65 finish_date=order_resource.stops[-1].datetime,
66 start_location=order_resource.stops[0].address.location,
67 finish_location=order_resource.stops[-1].address.location,
68 volume=order_resource.truck_body.volume,
69 weight=order_resource.truck_body.weight,
70 body=order_resource.truck_body.body_type,
71 loading=order_resource.truck_body.loading_type,
72 )
73 json = create_entity.model_dump(mode="json")
74 normalize_datetime_to_msc(json)
75 return json
77 async def create_target_and_recommendation_order(
78 self, order: OrderDBModel, *, target: bool = True
79 ):
80 if not order.stops:
81 return
82 if not order.truck_body.loading_type:
83 return
85 order_resource = order.model_copy(deep=True)
86 try:
87 normalize_stops(order_resource)
88 except StopsLimitedInfo:
89 return
91 create_entity = self._form_rec_domain(order_resource, update=False)
92 await self.connector.make_request(
93 self.API_URL,
94 "/target-orders" if target else "/recommendation-orders",
95 "POST",
96 json=create_entity,
97 )
99 async def update_order(
100 self, oid: ObjectId, order: OrderDBModel, *, target: bool
101 ):
102 order_copy = order.model_copy(deep=True)
103 if order.stops:
104 try:
105 normalize_stops(order_copy)
106 except StopsLimitedInfo:
107 return
108 data = self._form_rec_domain(order_copy, update=True)
109 api_req_url = (
110 f"/target-orders/{oid}"
111 if target
112 else f"/recommendation-orders/{oid}"
113 )
114 await self.connector.make_request(
115 self.API_URL, api_req_url, "PATCH", json=data
116 )
118 async def remove_order(self, oid: ObjectId, *, target: bool):
119 api_del_url = (
120 f"/target-orders/{oid}"
121 if target
122 else f"/recommendation-orders/{oid}"
123 )
124 await self.connector.make_request(self.API_URL, api_del_url, "DELETE")
126 async def get_recommendations_for_target(
127 self, oid: ObjectId
128 ) -> list[RecommendationMatch]:
129 api_get_url = f"/target-orders/{oid}/recommendations"
130 try:
131 recommendations = await self.connector.make_request(
132 self.API_URL, api_get_url, "GET"
133 )
134 except aiohttp.ClientError:
135 return []
136 if not recommendations:
137 return []
138 return [
139 RecommendationMatch(**recommendation)
140 for recommendation in recommendations
141 ]
144suggestion_api = APIQuerier(RecommendationsConnector())