modelswrkpi 8.0.4__py3-none-any.whl → 8.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of modelswrkpi might be problematic. Click here for more details.

Files changed (24) hide show
  1. models/bro_clicks/initial_routes.cpython-310-x86_64-linux-gnu.so +0 -0
  2. models/bro_clicks/load_balancer/__init__.py +6 -0
  3. models/bro_clicks/load_balancer/compile.py +16 -0
  4. models/bro_clicks/load_balancer/v1.cpython-310-x86_64-linux-gnu.so +0 -0
  5. models/bro_clicks/load_balancer/v1.py +176 -0
  6. models/bro_clicks/load_balancer/v2.cpython-310-x86_64-linux-gnu.so +0 -0
  7. models/bro_clicks/load_balancer/v2.py +128 -0
  8. models/bro_clicks/load_balancer/v3.cpython-310-x86_64-linux-gnu.so +0 -0
  9. models/bro_clicks/load_balancer/v3.py +378 -0
  10. models/bro_clicks/load_balancer/v4.cpython-310-x86_64-linux-gnu.so +0 -0
  11. models/bro_clicks/load_balancer/v4.py +151 -0
  12. models/bro_clicks/load_balancer/v5.cpython-310-x86_64-linux-gnu.so +0 -0
  13. models/bro_clicks/load_balancer/v5.py +123 -0
  14. models/bro_clicks/load_balancer/v6.cpython-310-x86_64-linux-gnu.so +0 -0
  15. models/bro_clicks/load_balancer/v6.py +192 -0
  16. models/bro_clicks/load_balancer/v7.py +8 -0
  17. models/bro_clicks/load_balancer/v8.py +8 -0
  18. models/reports/table_reports/__init__.py +1 -1
  19. models/reports/table_reports/approval_report.cpython-310-x86_64-linux-gnu.so +0 -0
  20. {modelswrkpi-8.0.4.dist-info → modelswrkpi-8.1.0.dist-info}/METADATA +1 -1
  21. {modelswrkpi-8.0.4.dist-info → modelswrkpi-8.1.0.dist-info}/RECORD +23 -8
  22. models/bro_clicks/load_balancer.cpython-310-x86_64-linux-gnu.so +0 -0
  23. {modelswrkpi-8.0.4.dist-info → modelswrkpi-8.1.0.dist-info}/WHEEL +0 -0
  24. {modelswrkpi-8.0.4.dist-info → modelswrkpi-8.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,378 @@
1
+ import random
2
+ import pandas as pd
3
+ from models.bro_clicks.initial_routes import InitialRoutes
4
+ import datetime as dt
5
+ from threading import Thread, Lock
6
+ from copy import deepcopy
7
+ from models.bro_clicks.load_balancer.v2 import LoadBalancerV2
8
+
9
+
10
+ class LoadBalancerV3(LoadBalancerV2):
11
+ routes = None
12
+ _lock_static = Lock()
13
+ _lock_static_route_get = Lock()
14
+ _lock_model_replace = Lock()
15
+ _last_updated = False
16
+ _max_stale_seconds = random.randint(600, 1200)
17
+ _route_schema = 'initial_route'
18
+
19
+ def __init__(self, db, db_p, account_id='54407332', alert_call_back=False,
20
+ min_sample_count=500, optimise_pct=0.1,
21
+ randomise_pct=0.1, opt_type=4,
22
+ route_methods=['bank_conversion',
23
+ 'iin_conversion',
24
+ 'cc_type_conversion',
25
+ 'cc_type_mcc_conversion',
26
+ 'cc_type_cc_level_conversion',
27
+ ],
28
+ rewrite_route='initial_route' ,
29
+ iin_schema='bro_clicks',
30
+
31
+ **kwargs):
32
+ is_new_route = False
33
+ if rewrite_route:
34
+ is_new_route = LoadBalancerV3._route_schema != rewrite_route
35
+ LoadBalancerV3._route_schema = rewrite_route
36
+ self._iin_schema = iin_schema
37
+ self._model_class = InitialRoutes
38
+ LoadBalancerV2.__init__(self, db, db_p, account_id=account_id, alert_call_back=alert_call_back, **kwargs)
39
+ self._opt_arr = self._rand_arr(100, opt_type, {'key': 1, 'pct': randomise_pct}, {'key': 2, 'pct': optimise_pct})
40
+ self._route_methods = route_methods
41
+ self._opt_val = self.get_opt_type()
42
+ print('opt val', self._opt_val, 'opt_type', opt_type)
43
+ self._min_sample_count = min_sample_count
44
+ self._t_get_bin = False
45
+ self.iin_info = None
46
+ self.is_iin_data = False
47
+ self._t_get_route = False
48
+ self.sort_map = {
49
+ # Random
50
+ 1: {'by': ['fill_pct', 'initial_count'], 'ascending': [True, True]},
51
+ # Pure conversion rate
52
+ 2: {'by': ['conversion_rate', 'initial_count'], 'ascending': [False, True]},
53
+ # Hybrid optimization
54
+ 3: {'by': ['priority', 'conversion_rate', 'date_added', 'fill_pct', 'approval_rate', 'initial_count'],
55
+ 'ascending': [False, False, False, True, False, True]},
56
+ # No optimization
57
+ 4: {'by': ['priority', 'date_added', 'fill_pct', 'approval_rate', 'initial_count'],
58
+ 'ascending': [False, False, True, False, True]}
59
+ }
60
+ self._join_on_del = False
61
+ self.version = 3
62
+ self.init_static(db_p, route_methods=route_methods, is_rewrite=is_new_route)
63
+
64
+ def __del__(self):
65
+ self._joiner(self._t_get_bin)
66
+ # self._join_lb()
67
+
68
+ @staticmethod
69
+ def _rand_arr(length, default_val, *settings):
70
+ a = []
71
+ for s in settings:
72
+ a += [s['key'] for i in range(int(length * s['pct']))]
73
+ a += [default_val for i in range(length - len(a))]
74
+ return a
75
+
76
+ @staticmethod
77
+ def _rand_val(arr):
78
+ try:
79
+ return arr[random.randint(0, 100)]
80
+ except Exception as e:
81
+ print(str(e))
82
+ return 1
83
+
84
+ def get_opt_type(self):
85
+ return self._rand_val(self._opt_arr)
86
+
87
+ @staticmethod
88
+ def _joiner(*threads):
89
+ for t in threads:
90
+ try:
91
+ t.join()
92
+ except:
93
+ pass
94
+
95
+ def set_bin_info(self, cc_first_6):
96
+ def _exec():
97
+ nonlocal cc_first_6
98
+ self.iin_info = pd.read_sql(f"select * from {self._iin_schema}.iin_data where bin='{cc_first_6}'",
99
+ self.db_p.engine).astype(str).applymap(str.lower).replace({'none': None})
100
+ if len(self.iin_info):
101
+ self.is_iin_data = True
102
+ self.iin_info = self.iin_info.to_dict(orient='records')[0]
103
+ if self.iin_info['bank_map'] is not None:
104
+ self.iin_info['bank'] = self.iin_info['bank_map']
105
+ if 'level' not in self.iin_info:
106
+ self.iin_info['level'] = None
107
+ else:
108
+ self.iin_info = {}
109
+ self.iin_info['cc_first_6'] = cc_first_6
110
+
111
+ self._t_get_bin = Thread(target=_exec)
112
+ self._t_get_bin.start()
113
+
114
+ @staticmethod
115
+ def get_auto_routes(db, route_methods=[
116
+ 'bank_conversion',
117
+ 'iin_conversion',
118
+ 'cc_type_conversion',
119
+ 'cc_type_mcc_conversion',
120
+ 'cc_type_cc_level_conversion'
121
+ ]):
122
+ threads = []
123
+ print('LBV3 get auto routes')
124
+ if LoadBalancerV3._lock_static_route_get.acquire(timeout=0.001):
125
+ rts = pd.DataFrame()
126
+ _lock_rt = Lock()
127
+
128
+ def _getter(table, where=''):
129
+ nonlocal _lock_rt, rts
130
+ _rt = pd.read_sql(f"""select * from {LoadBalancerV3._route_schema}.{table} {where}""", db.engine)
131
+ _rt['mod_type'] = table
132
+ _lock_rt.acquire()
133
+ rts = rts.append(_rt)
134
+ _lock_rt.release()
135
+
136
+ for r in route_methods:
137
+ threads.append(Thread(target=_getter, args=(r,)))
138
+ threads[len(threads) - 1].start()
139
+ LoadBalancerV3._joiner(*threads)
140
+ LoadBalancerV3._lock_model_replace.acquire()
141
+ LoadBalancerV3.routes = rts.replace({'none': pd.np.nan})
142
+ LoadBalancerV3._lock_model_replace.release()
143
+ print('LBV3 get auto routes done')
144
+ LoadBalancerV3._lock_static_route_get.release()
145
+ else:
146
+ print('LBV3 get auto routes static lock already acquired')
147
+
148
+ def _join_lb(self):
149
+ print('join lb')
150
+ self._joiner(self._t_get_route)
151
+
152
+ @staticmethod
153
+ def async_del(lb):
154
+ print('async del')
155
+ lb._join_lb()
156
+
157
+ @staticmethod
158
+ def last_update_diff():
159
+ lst = LoadBalancerV3._last_updated
160
+ diff = (dt.datetime.now() - lst).total_seconds()
161
+ return 50000 if lst is None else (dt.datetime.now() - lst).total_seconds()
162
+
163
+ @staticmethod
164
+ def check_stale_data():
165
+ return LoadBalancerV3._max_stale_seconds < LoadBalancerV3.last_update_diff()
166
+
167
+ def init_static(self, db, route_methods=['bank_conversion',
168
+ 'iin_conversion',
169
+ 'cc_type_conversion',
170
+ 'cc_type_mcc_conversion',
171
+ 'cc_type_cc_level_conversion'],
172
+ is_rewrite=False,
173
+ **kwargs):
174
+
175
+ if LoadBalancerV3._lock_static.acquire(timeout=(0.001 if is_rewrite else 100)):
176
+
177
+ lb = LoadBalancerV3
178
+ try:
179
+ if not lb._last_updated or lb.routes is None or not len(lb.routes) or LoadBalancerV3.check_stale_data():
180
+ print('init_static')
181
+ LoadBalancerV3._last_updated = dt.datetime.now()
182
+
183
+ self._t_get_route = Thread(target=lb.get_auto_routes, args=(db, route_methods))
184
+ self._t_get_route.start()
185
+ else:
186
+ print('LBV3 cache is up to date')
187
+ except Exception as e:
188
+ print('LBV3 static init exception', str(e))
189
+
190
+ LoadBalancerV3._lock_static.release()
191
+ else:
192
+ print('LBV3 init static lock already aquired')
193
+ return LoadBalancerV3
194
+
195
+ @staticmethod
196
+ def update_static(cc_type, cc_first_6, processor, mcc, bank, level, mod_types=False):
197
+ try:
198
+ LoadBalancerV3._lock_static.acquire()
199
+ if not mod_types:
200
+ mod_types = list(LoadBalancerV3.routes.unique())
201
+ if 'iin_conversion' in mod_types:
202
+ pass
203
+ except Exception as e:
204
+ print(f'LB update static failed: {str(e)}')
205
+ LoadBalancerV3._lock_static.release()
206
+
207
+ def set_iin(self, **kwargs):
208
+
209
+ if self.iin_info is None or not len(self.iin_info):
210
+ self.iin_info = kwargs
211
+ else:
212
+ self.iin_info = {**self.iin_info, **kwargs}
213
+ if 'approved' in self.iin_info:
214
+ self.iin_info.pop('approved')
215
+ if 'declined' in self.iin_info:
216
+ self.iin_info.pop('declined')
217
+ if 'conversion_rate' in self.iin_info:
218
+ self.iin_info.pop('conversion_rate')
219
+ if 'level' not in self.iin_info:
220
+ self.iin_info['level'] = None
221
+
222
+ def next_gateway(self, crm_id, date, step, click_id='', processor=False, cc_type=None, cc_first_6=False, recurse=0,
223
+ decs=False, ignore_user_exclusions=None,
224
+ proc_excl=[], is_tds=None, **kwargs):
225
+ # opt vals 1 = random gateway constrained only by cap, 2 = optimised gateway constrained only by cap, 3 = Hybrid approach not ignoring settings, 4 = over cap (over-rides to that if needed)
226
+
227
+ if ignore_user_exclusions is None:
228
+ ignore_user_exclusions = self._opt_val < 2
229
+ if cc_first_6:
230
+ self.set_bin_info(cc_first_6)
231
+ try:
232
+ decs = self.exclude_list(crm_id, step, click_id, 'a.') if not decs else decs
233
+ except Exception as e:
234
+ return str(e)
235
+ self._joiner(self._t_get_bin)
236
+
237
+ try:
238
+ qry = self.gty_qry(crm_id, date, step, processor, cc_type, decs, proc_excl=proc_excl, is_tds=is_tds, **kwargs)
239
+ res = pd.read_sql(qry, self.engine)
240
+ cc_type = cc_type.lower()
241
+ if 'master' in cc_type:
242
+ cc_type = 'master'
243
+ if self._opt_val > 1 and self._opt_val < 4 and self.routes is not None:
244
+
245
+ self._lock_model_replace.acquire()
246
+ mod = self.routes if self.is_iin_data else self.routes.loc[
247
+ self.routes.mod_type.isin(['cc_type_conversion', 'cc_type_mcc_conversion'])]
248
+ self._lock_model_replace.release()
249
+ mod = mod.loc[(mod.approved + mod.declined >= self._min_sample_count)
250
+ & (mod.conversion_rate != 1) # take out dummy gateways
251
+ & ((mod.cc_first_6 == cc_first_6) | (mod.cc_first_6.isna()))
252
+ & (((mod.cc_type == cc_type) | mod.cc_type.isna()) if 'cc_type' in mod.columns else (
253
+ True))
254
+ & (((mod.cc_level == str(self.iin_info['level'])) | (
255
+ mod.cc_level.isna())) if self.is_iin_data and 'level' in self.iin_info else (True))
256
+ & (((mod.bank == str(self.iin_info['bank'])) | (
257
+ mod.bank.isna())) if self.is_iin_data and 'bank' in self.iin_info else (True))
258
+ ]
259
+
260
+ df_opt = mod.copy().sort_values('conversion_rate', ascending=False).reset_index(drop=True)
261
+ df_opt['r_rank'] = df_opt.index + 1
262
+
263
+ # Optimization Filters
264
+
265
+ res = res.merge(df_opt.loc[df_opt.mod_type.isin(
266
+ ['cc_type_mcc_conversion', 'bank_conversion', 'iin_conversion'])],
267
+ on=['processor', 'mcc'],
268
+ how='left').append(res.merge(
269
+ df_opt.loc[df_opt.mod_type.isin(['cc_type_cc_level_conversion', 'cc_type_conversion'])].drop('mcc',
270
+ axis=1),
271
+ on=['processor'],
272
+ how='left')).drop_duplicates()
273
+ # r_rank is Highest to lowest in terms of strength same as priority
274
+ res.mod_type = res.mod_type.fillna('undefined').replace({'nan': 'undefined', '': 'undefined'})
275
+ res.conversion_rate = res.conversion_rate.fillna(0)
276
+ else:
277
+ res['conversion_rate'] = 0
278
+ res = res.sort_values(**self.sort_map[self._opt_val]).drop_duplicates('gateway_id', keep='first')
279
+ res['cc_type'] = cc_type
280
+ res['cc_first_6'] = cc_first_6
281
+ self.set_iin(cc_first_6=cc_first_6, cc_type=cc_type)
282
+ except Exception as e:
283
+ print('LBV3 error', str(e))
284
+ raise e
285
+
286
+ if res is None or not len(res):
287
+ if not decs:
288
+ if not recurse and is_tds is not None:
289
+ return self.next_gateway(crm_id, date, step, click_id, processor, cc_type, recurse=recurse + 1,
290
+ is_tds=not is_tds)
291
+ elif recurse == 1:
292
+ self.init_date(date, crm_id)
293
+ return self.next_gateway(crm_id, date, step, click_id, processor, cc_type, recurse=recurse + 1,
294
+ is_tds=is_tds)
295
+ elif recurse == 2 and is_tds is not None:
296
+ return self.next_gateway(crm_id, date, step, click_id, processor, cc_type, recurse=recurse + 1,
297
+ is_tds=not is_tds)
298
+ return 'out of processing'
299
+ else:
300
+ return 'declined due to too many attempts'
301
+
302
+ r = res.loc[res.fill_pct < 1]
303
+ if 'conversion_rate' not in res:
304
+ res['conversion_rate'] = 0
305
+ # HARD CAP
306
+ if not len(r):
307
+
308
+ res = res.sort_values(['dly_initial_cap', 'conversion_rate'], ascending=[True, False]).sort_values(
309
+ ['fill_pct'])
310
+
311
+ def _get_aft_sc():
312
+ nonlocal res
313
+ if not len(res):
314
+ return 'out of processing'
315
+ r2 = res.to_dict(orient='records')[0]
316
+ if r2['initial_count_mtd'] >= r2['monthly_initial_cap']:
317
+ self.alert_cb('hard_cap_alert', crm_id=crm_id, gateway_id=r2['gateway_id'])
318
+ self.disable(crm_id=crm_id, gateway_id=r2['gateway_id'])
319
+ res = res.loc[res.gateway_id != r2['gateway_id']]
320
+ return _get_aft_sc()
321
+ self.set_iin(**r2)
322
+ r2['is_tds'] = is_tds
323
+ return r2
324
+
325
+ # SOFT CAP
326
+ if ~res.soft_cap_alerted.any():
327
+ cnt = self.engine.execute(
328
+ f"""select count(*) from {self.schema}.{self.table}
329
+ where date = '{date}'::date and crm_id = '{crm_id}'
330
+ and router_id = '{step if step == 1 else 2}'
331
+ and enabled and enable_initials and fill_pct<1
332
+ """).scalar()
333
+ if cnt == 0 or cnt is None:
334
+ self.alert_cb('soft_cap_alert', crm_id=crm_id)
335
+ self.set_soft_cap_alerted(crm_id)
336
+ return _get_aft_sc()
337
+ r = r.to_dict(orient='records')[0]
338
+ if cc_type:
339
+ r['cc_type'] = cc_type
340
+ if cc_first_6:
341
+ r['cc_first_6'] = cc_first_6
342
+
343
+ self.set_iin(**r)
344
+ r['is_tds'] = is_tds
345
+ return r
346
+
347
+ def update_models(self, approved, test=0, **kwargs):
348
+
349
+ if self.routes is None:
350
+ return
351
+
352
+ if not int(test):
353
+ self._lock_model_replace.acquire()
354
+ r = self.routes.mod_type.unique()
355
+ self._lock_model_replace.release()
356
+ for k in r:
357
+ _in = deepcopy(self.iin_info)
358
+ self.iin_info.pop('response_code',None)
359
+ getattr(self._model_class, k)(self.db_p).increment_conversion(approved, list(self.routes.columns),
360
+ **{**_in, **kwargs})
361
+
362
+ def add_test_result(self, crm_id, order_id, approved, optimised, test=0, **kwargs):
363
+ try:
364
+ self._model_class.optimised_orders(self.db_p).upsert(
365
+ pd.DataFrame([{**self.iin_info, **{'crm_id': crm_id, 'order_id': order_id, 'is_optimised': optimised,
366
+ 'is_test_cc': int(test), 'approved': int(approved), 'version':self.version}}]))
367
+ except Exception as e:
368
+ # raise e
369
+ print('LB ADD TEST RESULT ERROR', str(e))
370
+
371
+ def increment_conversion(self, date, gateway_id, crm_id, approved, order_id, **kwargs):
372
+ self.add_test_result(crm_id, order_id, approved, self._opt_val, **kwargs)
373
+ try:
374
+ self.update_models(approved, **kwargs)
375
+ except Exception as e:
376
+ pass
377
+
378
+ return self._increment_conversion(date, gateway_id, crm_id, approved, recurs_attempt=0, **kwargs)
@@ -0,0 +1,151 @@
1
+ import random
2
+ from models.db import Db, pd
3
+ from models.bro_clicks.initial_routes import ForeignInitialRoutes
4
+ from models.bro_clicks.load_balancer.v3 import LoadBalancerV3
5
+
6
+
7
+ class LoadBalancerV4(LoadBalancerV3):
8
+ def __init__(self, *args, rewrite_route='foreign_initial_route', **kw):
9
+ LoadBalancerV3.__init__(self, *args, rewrite_route=rewrite_route, **kw)
10
+ self._model_class = ForeignInitialRoutes
11
+ self._iin_schema = 'foreign_bins'
12
+ self.version = 4
13
+
14
+ def next_gateway(self, crm_id, date, step, click_id='', processor=False, cc_type=None, cc_first_6=False, recurse=0,
15
+ decs=False, ignore_user_exclusions=None,
16
+ proc_excl=[], is_tds=None, is_prepaid=True, **kwargs):
17
+ # opt vals 1 = random gateway constrained only by cap, 2 = optimised gateway constrained only by cap, 3 = Hybrid approach not ignoring settings, 4 = over cap (over-rides to that if needed)
18
+ if is_prepaid is None:
19
+ raise TypeError('is_prepaid value must be pass as a boolean got NoneType')
20
+
21
+ if is_prepaid:
22
+ pp_campaign_class = 'prepaid'
23
+ else:
24
+ pp_campaign_class = 'post_paid'
25
+ if ignore_user_exclusions is None:
26
+ ignore_user_exclusions = self._opt_val < 2
27
+ if cc_first_6:
28
+ self.set_bin_info(cc_first_6)
29
+ try:
30
+ decs = self.exclude_list(crm_id, step, click_id, 'a.') if not decs else decs
31
+ except Exception as e:
32
+ return str(e)
33
+ self._joiner(self._t_get_bin)
34
+
35
+ try:
36
+ qry = self.gty_qry(crm_id, date, step, processor, cc_type, decs, proc_excl=proc_excl, is_tds=is_tds,
37
+ is_prepaid=is_prepaid, **kwargs)
38
+ res = pd.read_sql(qry, self.engine)
39
+ cc_type = cc_type.lower()
40
+ if 'master' in cc_type:
41
+ cc_type = 'master'
42
+ if self._opt_val > 1 and self._opt_val < 4 and self.routes is not None:
43
+
44
+ self._lock_model_replace.acquire()
45
+ mod = self.routes if self.is_iin_data else self.routes.loc[
46
+ self.routes.mod_type.isin(['cc_type_conversion', 'cc_type_mcc_conversion'])]
47
+ self._lock_model_replace.release()
48
+ mod = mod.loc[(mod.approved + mod.declined >= self._min_sample_count)
49
+ # & (mod.conversion_rate != 1) # take out dummy gateways
50
+ & ((mod.cc_first_6 == cc_first_6) | (mod.cc_first_6.isna()))
51
+ & (mod.campaign_class == pp_campaign_class)
52
+ & (((mod.cc_type == cc_type) | mod.cc_type.isna()) if 'cc_type' in mod.columns else (
53
+ True))
54
+ & (((mod.cc_level == str(self.iin_info['level'])) | (
55
+ mod.cc_level.isna())) if self.is_iin_data and 'level' in self.iin_info else (True))
56
+ & (((mod.bank == str(self.iin_info['bank'])) | (
57
+ mod.bank.isna())) if self.is_iin_data and 'bank' in self.iin_info else (True))
58
+ ]
59
+ if len(mod):
60
+ df_opt = mod.copy().sort_values('conversion_rate', ascending=False).reset_index(drop=True)
61
+ df_opt['r_rank'] = df_opt.index + 1
62
+
63
+ # Optimization Filters
64
+
65
+ res = res.merge(df_opt.loc[df_opt.mod_type.isin(
66
+ ['cc_type_mcc_conversion', 'bank_conversion'])],
67
+ on=['processor', 'mcc'],
68
+ how='left').append(res.merge(
69
+ df_opt.loc[df_opt.mod_type.isin(['cc_type_cc_level_conversion', 'cc_type_conversion', 'iin_conversion'])].drop('mcc',
70
+ axis=1),
71
+ on=['processor'],
72
+ how='left')).sort_values('r_rank')
73
+ else:
74
+ res['mod_type'] = 'undefined'
75
+ res['conversion_rate'] = 0
76
+ # r_rank is Highest to lowest in terms of strength same as priority
77
+ res.mod_type = res.mod_type.fillna('undefined').replace({'nan': 'undefined', '': 'undefined'})
78
+ res.conversion_rate = res.conversion_rate.fillna(0)
79
+ else:
80
+ res['conversion_rate'] = 0
81
+ res['mod_type'] = 'undefined'
82
+ res = res.sort_values(**self.sort_map[self._opt_val]).drop_duplicates('gateway_id', keep='first')
83
+ res['cc_type'] = cc_type
84
+ res['cc_first_6'] = cc_first_6
85
+ self.set_iin(cc_first_6=cc_first_6, cc_type=cc_type)
86
+ except Exception as e:
87
+ print('LBV4 error', str(e))
88
+ raise e
89
+
90
+ if res is None or not len(res):
91
+ if not decs:
92
+ if not recurse and is_tds is not None:
93
+ return self.next_gateway(crm_id, date, step, click_id, processor, cc_type, recurse=recurse + 1,
94
+ is_tds=not is_tds, is_prepaid=is_prepaid)
95
+ elif recurse == 1:
96
+ self.init_date(date, crm_id)
97
+ return self.next_gateway(crm_id, date, step, click_id, processor, cc_type, recurse=recurse + 1,
98
+ is_tds=is_tds, is_prepaid=is_prepaid)
99
+ elif recurse == 2 and is_tds is not None:
100
+ return self.next_gateway(crm_id, date, step, click_id, processor, cc_type, recurse=recurse + 1,
101
+ is_tds=not is_tds, is_prepaid=is_prepaid)
102
+ return 'out of processing'
103
+ else:
104
+ # if len(decs) < 4:
105
+ # return 'out of processing'
106
+ return 'declined due to too many attempts'
107
+ r = res.loc[res.fill_pct < 1]
108
+ if 'conversion_rate' not in res:
109
+ res['conversion_rate'] = 0
110
+ # HARD CAP
111
+ if not len(r):
112
+
113
+ res = res.sort_values(['dly_initial_cap', 'conversion_rate'], ascending=[True, False]).sort_values(
114
+ ['fill_pct'])
115
+
116
+ def _get_aft_sc():
117
+ nonlocal res
118
+ if not len(res):
119
+ return 'out of processing'
120
+ r2 = res.to_dict(orient='records')[0]
121
+ if r2['initial_count_mtd'] >= r2['monthly_initial_cap']:
122
+ self.alert_cb('hard_cap_alert', crm_id=crm_id, gateway_id=r2['gateway_id'])
123
+ self.disable(crm_id=crm_id, gateway_id=r2['gateway_id'])
124
+ res = res.loc[res.gateway_id != r2['gateway_id']]
125
+ return _get_aft_sc()
126
+ self.set_iin(**r2)
127
+ r2['is_tds'] = is_tds
128
+ return r2
129
+
130
+ # SOFT CAP
131
+ if ~res.soft_cap_alerted.any():
132
+ cnt = self.engine.execute(
133
+ f"""select count(*) from {self.schema}.{self.table}
134
+ where date = '{date}'::date and crm_id = '{crm_id}'
135
+ and router_id = '{step if step == 1 else 2}'
136
+ and enabled and enable_initials and fill_pct<1
137
+ """).scalar()
138
+ if cnt == 0 or cnt is None:
139
+ self.alert_cb('soft_cap_alert', crm_id=crm_id)
140
+ self.set_soft_cap_alerted(crm_id)
141
+ return _get_aft_sc()
142
+ r = r.to_dict(orient='records')[0]
143
+ if cc_type:
144
+ r['cc_type'] = cc_type
145
+ if cc_first_6:
146
+ r['cc_first_6'] = cc_first_6
147
+
148
+ self.set_iin(**r)
149
+ r['is_tds'] = is_tds
150
+ return r
151
+
@@ -0,0 +1,123 @@
1
+ import pandas as pd
2
+ from models.bro_clicks.initial_routes import InitialRoutes, ForeignInitialRoutes
3
+ from models.bro_clicks.load_balancer.v4 import LoadBalancerV4
4
+
5
+
6
+ class LoadBalancerV5(LoadBalancerV4):
7
+ def __init__(self, *args, **kw):
8
+ LoadBalancerV4.__init__(self, *args, **kw)
9
+ self._model_class = ForeignInitialRoutes
10
+ self._iin_schema = 'foreign_bins'
11
+ self.version = 5
12
+
13
+ def init_date(self, date, crm_id, reset_cap_count=True):
14
+ sk = f'ui_{self._account_id}_clients'
15
+ real_cap_space = self.get_processing_for_month(crm_id) if reset_cap_count else None
16
+
17
+ qry = f"""
18
+ SELECT '{date}'::date as date, b.crm_id, a.mid_id, b.gateway_id, b.step, a.processor,
19
+ coalesce(e.approved, 0) approved, coalesce(e.approved,0) initial_count, c.dly_initial_cap, b.minimum_price, coalesce(e.declined, 0) declined,
20
+ d.approval_rate, c.dly_min_approval_rate, array_to_string(c.pr_exclude_cc_types, ',') exclude_cc_types ,
21
+ c.date_added, c.enable_tds, array_to_string(c.tds_exclude_cc_types, ',') tds_exclude_cc_types, c.enabled, c.enable_initials, c.monthly_initial_cap, c.priority, c.router_id, d.router_id as cur_router_id,
22
+ c.allow_prepaid, c.allow_non_prepaid,
23
+ d.soft_cap_alerted, d.initial_count_mtd as prev_mtd
24
+
25
+ FROM {sk}.mids a
26
+ LEFT JOIN {sk}.steps b on b.mid_id = a.mid_id
27
+ LEFT JOIN {sk}.gateway_settings c on c.gateway_id = b.gateway_id and c.crm_id = b.crm_id
28
+ LEFT JOIN {self.schema}.{self.table} d on c.gateway_id =d.gateway_id and c.crm_id = d.crm_id and b.step = d.step and '{date}'::date =d.date
29
+ LEFT JOIN (select crm_id, gateway_id, coalesce(sum(declined), 0) declined, coalesce(sum(approved), 0) approved
30
+ from {self.schema}.conversions where coalesce(test, 0) <> 1 and time_stamp::date = '{date}'::date group by crm_id, gateway_id
31
+ ) e on e.gateway_id =c.gateway_id and e.crm_id=c.crm_id
32
+ where (b.close_date is null or b.close_date >'{self.today()}')
33
+ and b.crm_id = '{crm_id}'
34
+ and b.gateway_id is not null
35
+ and a.processor not ilike '%%virtual%%'
36
+ and b.gateway_id::int <> 1
37
+ and a.processor != 'FlexCharge'
38
+
39
+ """
40
+
41
+ try:
42
+
43
+ # if crm_id != 'crm_ll_2':
44
+ #
45
+
46
+ # print(qry)
47
+ # print('break')
48
+ up = pd.read_sql(qry, self.engine)
49
+
50
+ up = up.sort_values('step').drop_duplicates(['gateway_id', 'cur_router_id'], keep='first')
51
+ up = up.loc[~up.router_id.isna()]
52
+ up = up.explode('router_id')
53
+
54
+ # delete changes to routers
55
+ del_gt_msk = (up.router_id != up.cur_router_id) & (
56
+ up.gateway_id.isin(up.loc[~up.cur_router_id.isna()].gateway_id.unique()))
57
+ del_gtys = up.loc[del_gt_msk].gateway_id.tolist()
58
+
59
+ up = up.loc[(~up.gateway_id.isin(del_gtys)) | (~up.cur_router_id.isna())]
60
+
61
+ # delete changes to routers
62
+ del_gt_msk = (up.router_id != up.cur_router_id)
63
+ del_gtys = up.loc[del_gt_msk].gateway_id.tolist()
64
+ self.engine.execute(
65
+ f"delete from {self.schema}.{self.table} where gateway_id::int = ANY(ARRAY{del_gtys}::int[]) and crm_id='{crm_id}'")
66
+ up = up.drop(columns='cur_router_id')
67
+ except Exception as e:
68
+ raise e
69
+ if reset_cap_count:
70
+ try:
71
+ up = up.merge(real_cap_space, on=['gateway_id'], how='left')
72
+ up.initial_count_mtd = up.initial_count_mtd.fillna(0)
73
+ up.initial_count_mtd += up.initial_count
74
+
75
+ except:
76
+ up['initial_count_mtd'] = up.prev_mtd.fillna(0)
77
+ up.initial_count_mtd = up.initial_count_mtd.fillna(0)
78
+
79
+ drim = float(self.get_drim())
80
+ up.dly_initial_cap = pd.np.floor((up.monthly_initial_cap - up.initial_count_mtd) / drim)
81
+ up.loc[up.dly_initial_cap < 0, 'dly_initial_cap'] = 0
82
+
83
+ up.dly_initial_cap = up.dly_initial_cap.fillna(11)
84
+ up.dly_min_approval_rate = up.dly_min_approval_rate.fillna(30)
85
+ up.declined = up.declined.fillna(0)
86
+ up.approval_rate = up.approval_rate.fillna(0)
87
+ up.soft_cap_alerted = up.soft_cap_alerted.fillna(False)
88
+ up.drop('prev_mtd', axis=1, errors='ignore', inplace=True)
89
+ up = up.drop_duplicates(['gateway_id', 'router_id'])
90
+ # self.engine.execute(f'truncate {self.schema}.{self.table}')
91
+ self.upsert(up.dropna())
92
+
93
+ def gty_qry(self, crm_id, date, step, processor, cc_type=False, decs='', proc_excl=[], is_tds=None,
94
+ is_prepaid=False, is_decline_salvage=False, **kw):
95
+ p_ex = ''
96
+ if proc_excl and len(proc_excl) and not processor:
97
+ p_ex = f"and a.processor not ilike all(ARRAY{[f'%%{p}%%' for p in proc_excl]}::text[])"
98
+
99
+ return f""" --LEFT HERE NEED TO GET MCC!
100
+ select a.gateway_id::int, a.fill_pct,a.dly_initial_cap,priority,initial_count, a.approval_rate, a.date_added, a.processor, a.mid_id, a.monthly_initial_cap, a.soft_cap_alerted, initial_count_mtd,b.mcc from {self.schema}.{self.table} a
101
+ inner join (select crm_id, gateway_id, mcc from ui_54407332_clients.steps ) b on b.crm_id = a.crm_id and b.gateway_id=a.gateway_id
102
+ {"inner join (select crm_id, gateway_id where enable_decline_salvage) ds on ds.crm_id = a.crm_id and ds.gateway_id::int = a.gateway_id::int" if is_decline_salvage else "" }
103
+ inner join processing.cap c on a.mid_id = c.mid_id and a.step=c.step and a.processor=c.processor and c.monthly_available > 200
104
+ {f"left join processing.cap_cc_type d on a.mid_id = d.mid_id and a.step= d.step and a.processor = d.processor and d.cc_type = '{cc_type}' " if cc_type else ''}
105
+
106
+ where date = '{date}'::date and a.crm_id = '{crm_id}' and router_id = '{step if step in [1, 11] else 2}' and enabled and enable_initials
107
+ {f"and a.processor = '{processor}'" if processor else ""}
108
+ {f"and (exclude_cc_types is null or exclude_cc_types::text not ilike '%%{cc_type.lower()}%%')" if cc_type else ''}
109
+ and (approval_rate > dly_min_approval_rate or(declined+initial_count<110))
110
+ {'and (d.available_tc is null or d.available_tc >50)' if cc_type else ''}
111
+ {decs}
112
+ {p_ex}
113
+ {f"and enable_tds = {bool(is_tds)}" if is_tds else ""}
114
+ {f"and (tds_exclude_cc_types is null or tds_exclude_cc_types not ilike '%%{cc_type}%%')" if cc_type and is_tds else ""}
115
+ {f"and allow_prepaid" if is_prepaid else ""}
116
+ {f"and allow_non_prepaid" if not is_prepaid else ""}
117
+ --and fill_pct < 1
118
+
119
+ --order by date_added desc, approval_rate desc, fill_pct asc limit 1
120
+ order by priority desc, date_added desc, fill_pct, approval_rate desc, initial_count
121
+
122
+ """
123
+