· 4 years ago · May 24, 2021, 10:30 AM
1# flake8: noqa
2# mypy: ignore-errors
3import re
4import json # noqa: I001
5import logging
6
7from datetime import timedelta
8from typing import (
9 Dict,
10 List,
11 SupportsInt,
12 AnyStr,
13 Optional,
14)
15
16from api_clients.base import BaseModelPO
17from api_clients.http import get_session
18from api_clients.platform import (
19 EquipmentPO,
20 SeriesPO
21)
22from requests_toolbelt.sessions import BaseUrlSession
23
24from celery_service.services.redis_client import RedisClient
25
26logger = logging.getLogger('console')
27
28
29class EquipmentService:
30 """Service for equipment connection, and update redis.
31
32 Attributes:
33 self.redis_cli:instance of redis cli (required).
34 self.platform_cfg: config for platform api (required).
35 self.loco_series_codes: mapping for ser_loc:name (required).
36 self.location_id: location od for future equipment (required).
37 self.files_paths: list of files paths (required).
38 self.reg_exp: regular expression for parse files (required).
39 self.platform_session: session for platform api.
40 self.eq_po: equipment PO for platform api.
41 self.series_po: series PO for platform api.
42 """
43
44 redis_cli: Optional[RedisClient] = None
45 platform_cfg: Dict[str, str] = {}
46 loco_series_codes: Dict[str, str] = {}
47 location_id: Optional[SupportsInt] = None
48 files_paths: List[AnyStr] = []
49 reg_exp: AnyStr = ''
50
51 _platform_session: Optional[BaseUrlSession] = None
52 _eq_po: Optional[EquipmentPO] = None
53 _series_po: Optional[SeriesPO] = None
54
55 def __init__(self, **kwargs):
56 # TODO: Make required fields checker
57 for key, value in kwargs.items():
58 if hasattr(self, key):
59 setattr(self, key, value)
60 else:
61 raise ValueError(
62 'The {key} is not the {cls_name} field'.format(key=key, cls_name=self.__class__.__name__)
63 )
64 self._platform_session = get_session(configuration=self.platform_cfg)
65 self._eq_po = EquipmentPO(self._platform_session)
66 self._series_po = SeriesPO(self._platform_session)
67 self._redis_key_expired = timedelta(hours=72)
68
69 @staticmethod
70 def check_platform_usage_types(po: EquipmentPO or SeriesPO, dictionary: Dict[str, str]):
71 """ Type checker method
72
73 :param po: BaseModelPO
74 :param dictionary: dict of data
75 """
76 if not isinstance(dictionary, dict):
77 raise TypeError(f"Expected type `dict` , got `{type(dictionary)}`instead")
78 if not isinstance(po, BaseModelPO) and not issubclass(type(po), BaseModelPO):
79 raise TypeError(f"Expected type `BaseModelPO` , got `{type(po)}`instead")
80
81 def get_data_from_platform(self, platform_po: EquipmentPO or SeriesPO, filters: Dict[str, str]) -> List[BaseModelPO]:
82 """ Making request to platform API using filters.
83
84 :param platform_po: PO for making requests to platform api
85 :param filters: dictionary of filtering values.
86 :return: list of resulting request.
87 """
88 self.check_platform_usage_types(platform_po, filters)
89 return platform_po.filter(**filters)
90
91 def post_data_to_platform(self, platform_po: EquipmentPO or SeriesPO, post_data: Dict[str, str]):
92 """ Making request to platform API using filters.
93
94 :param platform_po: PO for making requests to platform api
95 :param post_data: dictionary of post data values.
96 :return: list of resulting request.
97 """
98 self.check_platform_usage_types(platform_po, post_data)
99 return platform_po.create(**post_data)
100
101 def update_data_in_platform(self, platform_po: BaseModelPO, update_data: Dict[str, str]) -> BaseModelPO:
102 """ Making request to platform API for updating data
103
104 :param platform_po: PO for making requests to platform api
105 :param update_data: dictionary of updating data values.
106 :return: updated PO
107 """
108 self.check_platform_usage_types(platform_po, update_data)
109 return platform_po.update(partial=True, **update_data)
110
111 def update_equipment(self) -> bool:
112 # pylint: disable-msg=too-many-locals
113 """Checking if equipment_id exists in redis,
114 else get data from platform api (get or create) and store in Redis.
115 Storage example: 240_175_1, where 240: series code, 175 - loco_num, 1 - section.
116
117 :return boolean True if everything is ok, else False and log error
118 """
119 with self.redis_cli as cli:
120 for filename in self.files_paths:
121 reg = re.compile(self.reg_exp)
122 reg_result = reg.search(filename)
123 if reg_result:
124 loco_num = int(reg_result.group('loco_num'))
125 loco_section = int(reg_result.group('loco_section'))
126 series_code = int(reg_result.group('series_code'))
127 loco_series = self.loco_series_codes.get(str(series_code), '')
128 if not loco_series:
129 continue
130 else:
131 logger.error(f"Can't parse {filename} using `re` lib")
132 return False
133 equipment_name = f'{loco_series}. loco_num: {loco_num}. Section: {loco_section}'
134 redis_key = f'{series_code}_{loco_num}_{loco_section}'
135 if not cli.get(name=redis_key):
136 logger.info(f"{redis_key} isn't stored in Redis")
137 tags = {
138 '1': str(loco_num),
139 '2': str(loco_section)
140 }
141 tags_json = json.dumps(tags)
142 filters = {
143 'name': equipment_name,
144 'tags': tags_json
145 }
146 locos_data = self.get_data_from_platform(
147 self._eq_po,
148 filters
149 )
150 if locos_data:
151 redis_value = locos_data[0].json()['id']
152 else:
153 filters['name'] = loco_series
154 locos_data = self.get_data_from_platform(
155 self._eq_po,
156 filters
157 )
158 if locos_data:
159 locos_data = self.update_data_in_platform(locos_data[0], {'name': equipment_name})
160 redis_value = locos_data.json()['id']
161 else:
162 series = self.get_data_from_platform(
163 self._series_po,
164 {'name': loco_series}
165 )
166 series_list = [
167 po_obj.json() for po_obj in series
168 if po_obj.json()['name'] == loco_series
169 ]
170 if not series_list:
171 logger.warning(f'Series for name = `{loco_series}` not exists in equipment')
172 continue
173 series_id = series_list[0]['id']
174
175 data = {
176 'name': equipment_name,
177 'is_deleted': False,
178 'series': series_id,
179 'location': self.location_id,
180 'tags': tags,
181 'source_id': None,
182 }
183 response = self.post_data_to_platform(
184 self._eq_po,
185 data
186 )
187
188 redis_value = response.id
189
190 logger.info(f'Create new Equipment with id = {redis_value}')
191 cli.set(name=redis_key, value=redis_value, ex=self._redis_key_expired)
192 logger.info(f"{redis_key} now stored in Redis")
193 return True
194