Coverage for api/services/notifications/builder.py: 19%
116 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
1import copy
2import re
3from typing import Any, TypeAlias
5from errors import log_error
6from logging_config import logger
7from sotrans_models.models.services.notification_presets import (
8 NOTIFICATION_PRESETS_MAPPING,
9)
10from sotrans_models.models.services.notifications import (
11 CastType,
12 NotificationCreateBatchModel,
13 NotificationCreateModel,
14 NotificationCreateMulticastModel,
15 NotificationPresetModel,
16 NotificationUserModel,
17)
18from sotrans_models.models.users import SotransUserDBModel
21def _get_object_field(object_: dict | list, field_path: list[str]) -> str:
22 try:
23 field_value: Any = "(не найдено)"
24 if isinstance(object_, dict):
25 field_value = object_.get(field_path[0])
26 elif isinstance(object_, list):
27 index = int(field_path[0])
28 field_value = object_[index]
29 if isinstance(field_value, dict) or isinstance(field_value, list):
30 return _get_object_field(field_value, field_path[1:])
31 logger.info(object_)
32 logger.info(field_value)
33 if not field_value:
34 return "(не найдено)"
35 return str(field_value)
36 except (IndexError, ValueError):
37 logger.error(
38 "Invalid field_path {0} for object {1}".format(field_path, object_)
39 )
40 return "(не найдено)"
43UserType: TypeAlias = NotificationUserModel | dict | SotransUserDBModel
44UsersType: TypeAlias = list[UserType]
47def _fill_text_from_object(object_: dict, text: str) -> str:
48 matches = re.finditer(r"\*([-\w]+,?)+\*", text, re.MULTILINE)
49 for match in matches:
50 match_text = str(match.group())
51 field_path = match_text[1:-1].split(",")
52 object_field_value = _get_object_field(object_, field_path)
53 logger.info(object_field_value)
54 text = text.replace(
55 match_text, _get_object_field(object_, field_path), 1
56 )
57 return text
60def _get_user_dict(user: UserType):
61 if isinstance(user, NotificationUserModel) or isinstance(
62 user, SotransUserDBModel
63 ):
64 return user.model_dump(format_ids=False)
65 elif isinstance(user, dict):
66 return user
67 raise ValueError(
68 "User must be of type NotificationUserModel, SotransUserDBModel or dict"
69 )
72def _get_users_dict(users: UsersType):
73 return [_get_user_dict(user) for user in users]
76DOC_TRANSLATION_TABLE = {
77 "DriverDocumentType.drivers_license": "водительское удостоверение",
78 "DriverDocumentType.passport": "паспорт",
79 "TruckDocumentType.sts": "СТС тягача",
80 "TruckDocumentType.lease_contract": "договор аренды тягача",
81 "TrailerDocumentType.sts": "СТС прицепа",
82 "TrailerDocumentType.lease_contract": "договор аренды прицепа",
83 "OrganizationDocumentType.tax_authorities_certificate": "свидетельство о постановке на учет в налоговом органе",
84 "OrganizationDocumentType.registration_certificate": "свидетельство о государственной регистрации юридического лица",
85 "OrganizationDocumentType.director_or_proprietor_passport": "Копия паспорта генерального директора или индивидуального предпринимателя",
86 "OrganizationDocumentType.director_appointment": "приказ о назначении директора",
87 "OrganizationDocumentType.decision_protocol": "протокол решения общего собрания учредителей о создании юр. лица",
88 "OrganizationDocumentType.partner_card": "карта партнёра",
89 "RequestDocumentType.order_request": "запрос на заказ",
90 "RequestDocumentType.draft": "драфт",
91 "RequestDocumentType.order_request_draft": "драфт запроса на заказ",
92 "UserDocumentType.photo": "фото пользователя",
93}
96def _doc_verification_translation(payload_dict: dict):
97 if payload_dict.get("type") not in (
98 "success_document_verification",
99 "failed_document_verification",
100 ):
101 return
102 for k in DOC_TRANSLATION_TABLE:
103 if k in payload_dict["title"]:
104 old_title: str = payload_dict["title"]
105 new_title = old_title.replace(k, DOC_TRANSLATION_TABLE[k])
106 payload_dict["title"] = new_title
107 return
110# Inefficient due to object copies, but this is an experiment
111class NotificationBuilder:
112 # not scary until class copies itself
113 # payload_dict: dict = {}
114 preset: NotificationPresetModel
116 def __init__(self):
117 self.payload_dict = {}
119 def _copy(self):
120 return copy.deepcopy(self)
122 def _fill_payload_fields(self, object_: dict):
123 if self.payload_dict.get("title"):
124 self.payload_dict["title"] = _fill_text_from_object(
125 object_, self.payload_dict["title"]
126 )
127 if self.payload_dict.get("description"):
128 self.payload_dict["description"] = _fill_text_from_object(
129 object_, self.payload_dict["description"]
130 )
132 def with_preset(self, preset: NotificationPresetModel):
133 copy_self = self._copy()
134 copy_self.preset = preset
135 copy_self.payload_dict = {
136 **copy_self.payload_dict,
137 **copy_self.preset.default_info_model.model_dump(),
138 }
139 return copy_self
141 def with_preset_type(self, preset_type: str):
142 if preset_type not in NOTIFICATION_PRESETS_MAPPING:
143 raise ValueError(
144 f"preset_type {preset_type} is not in NOTIFICATION_PRESET_MAPPING"
145 )
146 return self.with_preset(NOTIFICATION_PRESETS_MAPPING[preset_type])
148 def with_object(self, object_: dict, fill_payload_fields: bool = True):
149 copy_self = self._copy()
150 copy_self.payload_dict["object"] = object_
151 if fill_payload_fields:
152 copy_self._fill_payload_fields(object_)
153 return copy_self
155 def with_user(self, user: UserType):
156 copy_self = self._copy()
157 copy_self.payload_dict["user"] = _get_user_dict(user)
158 print(copy_self.payload_dict)
159 return copy_self
161 def with_users(
162 self,
163 users: UsersType,
164 ):
165 copy_self = self._copy()
166 copy_self.payload_dict["users"] = [
167 _get_user_dict(user) for user in users
168 ]
169 return copy_self
171 def with_cast_one(self, cast_type: CastType, recipient: UserType | None):
172 if not recipient:
173 log_error(
174 f"No recipient provided for builder method with_cast_one, ${self.payload_dict}"
175 )
176 return self
177 copy_self = self._copy()
178 current_casts = copy_self.payload_dict.get("casts", [])
179 current_casts.append(
180 {"cast_type": cast_type, "recipient": _get_user_dict(recipient)}
181 )
182 copy_self.payload_dict["casts"] = current_casts
183 return copy_self
185 def with_cast_many(
186 self, cast_type: CastType, recipients: UsersType | None
187 ):
188 if not recipients:
189 # log_warning(
190 # f"No recipient provided for builder method with_cast_many, ${self.payload_dict}"
191 # )
192 return self
193 copy_self = self._copy()
194 current_casts = copy_self.payload_dict.get("casts", [])
195 current_casts.append(
196 {"cast_type": cast_type, "recipients": _get_users_dict(recipients)}
197 )
198 copy_self.payload_dict["casts"] = current_casts
199 return copy_self
201 def build(
202 self,
203 ) -> (
204 NotificationCreateBatchModel
205 | NotificationCreateModel
206 | NotificationCreateMulticastModel
207 | None
208 ):
209 _doc_verification_translation(self.payload_dict)
210 if "user" in self.payload_dict:
211 return NotificationCreateModel(**self.payload_dict)
212 elif "users" in self.payload_dict:
213 return NotificationCreateBatchModel(**self.payload_dict)
214 elif "casts" in self.payload_dict:
215 return NotificationCreateMulticastModel(**self.payload_dict)
216 return None
217 # raise ValueError("No user or users")
220# print(NotificationBuilder().with_user(SotransUserDBModel(id=UUID(int=0),)).with_preset_type("success_inn_verification").build())
221# print(NotificationBuilder().with_users([SotransUserDBModel(id=UUID(int=0),)]).with_preset_type("success_inn_verification").build())