brynq-sdk-task-scheduler 1.3.0__tar.gz → 2.0.1__tar.gz
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.
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/PKG-INFO +1 -1
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/brynq_sdk/task_scheduler/task_scheduler.py +57 -72
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/brynq_sdk_task_scheduler.egg-info/PKG-INFO +1 -1
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/setup.py +1 -1
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/brynq_sdk/task_scheduler/__init__.py +0 -0
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/brynq_sdk_task_scheduler.egg-info/SOURCES.txt +0 -0
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/brynq_sdk_task_scheduler.egg-info/dependency_links.txt +0 -0
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/brynq_sdk_task_scheduler.egg-info/not-zip-safe +0 -0
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/brynq_sdk_task_scheduler.egg-info/requires.txt +0 -0
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/brynq_sdk_task_scheduler.egg-info/top_level.txt +0 -0
- {brynq_sdk_task_scheduler-1.3.0 → brynq_sdk_task_scheduler-2.0.1}/setup.cfg +0 -0
|
@@ -35,7 +35,9 @@ class TaskScheduler(BrynQ):
|
|
|
35
35
|
self.email_after_errors = email_after_errors
|
|
36
36
|
self.customer_db = self.mysql.database
|
|
37
37
|
self.customer_id = self.mysql.raw_query(f'SELECT id FROM sc.customers WHERE dbname = \'{self.customer_db}\'')[0][0]
|
|
38
|
+
self.customer = os.getenv('BRYNQ_SUBDOMAIN').lower().replace(' ', '_')
|
|
38
39
|
self.partner_id = os.getenv('PARTNER_ID').lower().replace(' ', '_') if os.getenv('PARTNER_ID') else 'brynq'
|
|
40
|
+
self.write_logs_to_mysql = os.getenv('WRITE_LOGS_TO_MSYQL') if os.getenv('WRITE_LOGS_TO_MSYQL') else False
|
|
39
41
|
self.task_id = task_id
|
|
40
42
|
self.loglevel = loglevel
|
|
41
43
|
self.started_at = datetime.datetime.now()
|
|
@@ -173,6 +175,10 @@ class TaskScheduler(BrynQ):
|
|
|
173
175
|
if loglevel not in allowed_loglevels:
|
|
174
176
|
raise Exception('You\'ve entered a not allowed loglevel. Choose one of: {}'.format(allowed_loglevels))
|
|
175
177
|
|
|
178
|
+
# Count the errors for relevant log levels
|
|
179
|
+
if loglevel == 'ERROR' or loglevel == 'CRITICAL':
|
|
180
|
+
self.error_count += 1
|
|
181
|
+
|
|
176
182
|
# For Elastic, we need to have the data in JSON format. Handling different data types and preparing extra payload information based on the data type
|
|
177
183
|
# If the data is just a series, count rows, columns and cells
|
|
178
184
|
if isinstance(data, pd.Series):
|
|
@@ -245,7 +251,7 @@ class TaskScheduler(BrynQ):
|
|
|
245
251
|
'started_at': datetime.datetime.now().isoformat(),
|
|
246
252
|
'partner_id': self.partner_id,
|
|
247
253
|
'customer_id': self.customer_id,
|
|
248
|
-
'customer':
|
|
254
|
+
'customer': self.customer,
|
|
249
255
|
'file_name': file_name,
|
|
250
256
|
'function_name': function_name,
|
|
251
257
|
'line_number': line_number,
|
|
@@ -258,31 +264,13 @@ class TaskScheduler(BrynQ):
|
|
|
258
264
|
|
|
259
265
|
# Write the logline to the MYSQL database, depends on the chosen loglevel in the task
|
|
260
266
|
print('{} at line: {}'.format(message, line_number))
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
"INSERT INTO `task_execution_log` (reload_id, task_id, log_level, created_at, line_number, message) VALUES ({}, {}, '{}', '{}', {}, '{}')".format(self.run_id, self.task_id, loglevel, datetime.datetime.now(), line_number, message), insert=True)
|
|
269
|
-
return self.mysql.update(table='task_execution_log',
|
|
270
|
-
columns=['reload_id', 'task_id', 'log_level', 'created_at', 'line_number', 'message'],
|
|
271
|
-
values=[self.run_id, self.task_id, loglevel, datetime.datetime.now(), linenumber, message])
|
|
272
|
-
elif self.loglevel == 'INFO' and (loglevel == 'INFO' or loglevel == 'ERROR' or loglevel == 'CRITICAL'):
|
|
273
|
-
# Count the errors
|
|
274
|
-
if loglevel == 'ERROR' or loglevel == 'CRITICAL':
|
|
275
|
-
self.error_count += 1
|
|
276
|
-
return self.mysql.raw_query(
|
|
277
|
-
"INSERT INTO `task_execution_log` (reload_id, task_id, log_level, created_at, line_number, message) VALUES ({}, {}, '{}', '{}', {}, '{}')".format(self.run_id, self.task_id, loglevel, datetime.datetime.now(), line_number, message), insert=True)
|
|
278
|
-
elif self.loglevel == 'ERROR' and (loglevel == 'ERROR' or loglevel == 'CRITICAL'):
|
|
279
|
-
self.error_count += 1
|
|
280
|
-
return self.mysql.raw_query(
|
|
281
|
-
"INSERT INTO `task_execution_log` (reload_id, task_id, log_level, created_at, line_number, message) VALUES ({}, {}, '{}', '{}', {}, '{}')".format(self.run_id, self.task_id, loglevel, datetime.datetime.now(), line_number, message), insert=True)
|
|
282
|
-
elif self.loglevel == 'CRITICAL' and loglevel == 'CRITICAL':
|
|
283
|
-
self.error_count += 1
|
|
284
|
-
return self.mysql.raw_query(
|
|
285
|
-
"INSERT INTO `task_execution_log` (reload_id, task_id, log_level, created_at, line_number, message) VALUES ({}, {}, '{}', '{}', {}, '{}')".format(self.run_id, self.task_id, loglevel, datetime.datetime.now(), line_number, message), insert=True)
|
|
267
|
+
|
|
268
|
+
if self.write_logs_to_mysql:
|
|
269
|
+
# Remove quotes from message since these break the query
|
|
270
|
+
message = re.sub("[']", '', message)
|
|
271
|
+
query = "INSERT INTO `task_execution_log` (reload_id, task_id, log_level, created_at, line_number, message) VALUES ({}, {}, '{}', '{}', {}, '{}')".format(self.run_id, self.task_id, loglevel, datetime.datetime.now(), line_number, message)
|
|
272
|
+
if self.loglevel == 'DEBUG' or (self.loglevel == 'INFO' and loglevel != 'DEBUG') or (self.loglevel == 'ERROR' and loglevel in ['ERROR', 'CRITICAL']) or (self.loglevel == 'CRITICAL' and loglevel == 'CRITICAL'):
|
|
273
|
+
return self.mysql.raw_query(query, insert=True)
|
|
286
274
|
|
|
287
275
|
def update_execution_step(self, step_number: int):
|
|
288
276
|
"""
|
|
@@ -304,10 +292,6 @@ class TaskScheduler(BrynQ):
|
|
|
304
292
|
:param started_at: Give the time the task is started
|
|
305
293
|
:return: nothing
|
|
306
294
|
"""
|
|
307
|
-
# Format error to a somewhat readable format
|
|
308
|
-
exc_type, exc_obj, exc_tb = sys.exc_info()
|
|
309
|
-
error = str(e)[:400].replace('\'', '').replace('\"', '') + ' | Line: {}'.format(exc_tb.tb_lineno)
|
|
310
|
-
|
|
311
295
|
# Get the linenumber from where the logline is executed.
|
|
312
296
|
file_name, line_number, function_name = self.__get_caller_info()
|
|
313
297
|
|
|
@@ -318,7 +302,7 @@ class TaskScheduler(BrynQ):
|
|
|
318
302
|
'started_at': datetime.datetime.now().isoformat(),
|
|
319
303
|
'partner_id': self.partner_id,
|
|
320
304
|
'customer_id': self.customer_id,
|
|
321
|
-
'customer':
|
|
305
|
+
'customer': self.customer,
|
|
322
306
|
'file_name': file_name,
|
|
323
307
|
'function_name': function_name,
|
|
324
308
|
'line_number': line_number,
|
|
@@ -329,33 +313,34 @@ class TaskScheduler(BrynQ):
|
|
|
329
313
|
}
|
|
330
314
|
self.es.post_document(index_name=self.es_index, document=payload)
|
|
331
315
|
|
|
332
|
-
|
|
316
|
+
self.error_count += 1
|
|
333
317
|
# Get scheduler task details for logging
|
|
334
318
|
task_details = \
|
|
335
319
|
self.mysql.select('task_scheduler, data_interfaces', 'data_interfaces.docker_image, data_interfaces.runfile_path', 'WHERE task_scheduler.data_interface_id = data_interfaces.id AND task_scheduler.id = {}'.format(self.task_id))[0]
|
|
336
320
|
taskname = task_details[0]
|
|
337
321
|
customer = task_details[1].split('/')[-1].split('.')[0]
|
|
322
|
+
now = datetime.datetime.now()
|
|
338
323
|
|
|
324
|
+
# Format error to a somewhat readable format
|
|
325
|
+
exc_type, exc_obj, exc_tb = sys.exc_info()
|
|
326
|
+
error = str(e)[:400].replace('\'', '').replace('\"', '') + ' | Line: {}'.format(exc_tb.tb_lineno)
|
|
327
|
+
|
|
328
|
+
# Log to log table in the database
|
|
329
|
+
if self.write_logs_to_mysql:
|
|
330
|
+
query = "INSERT INTO `task_execution_log` (reload_id, task_id, log_level, created_at, line_number, message) VALUES ({}, {}, 'CRITICAL', '{}', {}, '{}')".format(self.run_id, self.task_id, now, exc_tb.tb_lineno, error)
|
|
331
|
+
self.mysql.raw_query(query, insert=True)
|
|
332
|
+
if send_to_teams:
|
|
333
|
+
Functions.send_error_to_teams(database=customer, task_number=self.task_id, task_title=taskname)
|
|
339
334
|
if breaking:
|
|
340
335
|
# Set scheduler status to failed
|
|
341
336
|
self.mysql.update('task_scheduler', ['status', 'last_reload', 'last_error_message', 'step_nr'],
|
|
342
|
-
['IDLE',
|
|
337
|
+
['IDLE', now, 'Failed', 0],
|
|
343
338
|
'WHERE `id` = {}'.format(self.task_id))
|
|
344
|
-
|
|
345
|
-
self.mysql.raw_query(
|
|
346
|
-
"INSERT INTO `task_execution_log` (reload_id, task_id, log_level, created_at, line_number, message) VALUES ({}, {}, 'CRITICAL', '{}', {}, '{}')".format(self.run_id,
|
|
347
|
-
self.task_id,
|
|
348
|
-
datetime.datetime.now(),
|
|
349
|
-
exc_tb.tb_lineno,
|
|
350
|
-
error),
|
|
351
|
-
insert=True)
|
|
339
|
+
|
|
352
340
|
self.mysql.update(table='task_scheduler_log',
|
|
353
341
|
columns=['reload_status', 'finished_at'],
|
|
354
|
-
values=['Failed', f'{
|
|
342
|
+
values=['Failed', f'{now}'],
|
|
355
343
|
filter=f'WHERE `reload_id` = {self.run_id}')
|
|
356
|
-
# Notify users on Teams and If the variable self.send_mail_after_errors is set to True, send an email with the message that the task is failed
|
|
357
|
-
if send_to_teams:
|
|
358
|
-
Functions.send_error_to_teams(database=customer, task_number=self.task_id, task_title=taskname)
|
|
359
344
|
if self.email_after_errors:
|
|
360
345
|
self.email_errors(failed=True)
|
|
361
346
|
# Remove the temp values from the variables table
|
|
@@ -365,22 +350,14 @@ class TaskScheduler(BrynQ):
|
|
|
365
350
|
self.start_chained_tasks(finished_task_status='FAILED')
|
|
366
351
|
|
|
367
352
|
raise Exception(error)
|
|
368
|
-
else:
|
|
369
|
-
self.mysql.raw_query(
|
|
370
|
-
"INSERT INTO `task_execution_log` (reload_id, task_id, log_level, created_at, line_number, message) VALUES ({}, {}, 'CRITICAL', '{}', {}, '{}')".format(self.run_id,
|
|
371
|
-
self.task_id,
|
|
372
|
-
datetime.datetime.now(),
|
|
373
|
-
exc_tb.tb_lineno,
|
|
374
|
-
error),
|
|
375
|
-
insert=True)
|
|
376
|
-
if send_to_teams:
|
|
377
|
-
Functions.send_error_to_teams(database=customer, task_number=self.task_id, task_title=taskname)
|
|
378
|
-
self.error_count += 1
|
|
379
353
|
|
|
380
354
|
def finish_task(self, reload_instant=False, log_limit: typing.Optional[int] = 10000, log_date_limit: datetime.date = None):
|
|
381
355
|
"""
|
|
382
356
|
At the end of the script, write the outcome to the database. Write if the task is finished with or without errors, Email to a contactperson if this variable is given in the
|
|
383
357
|
variables table. Also clean up the execution_log table when the number of lines is more than 1000
|
|
358
|
+
:param reload_instant: If the task should start again after it's finished
|
|
359
|
+
:param log_limit: The maximum number of logs to keep in the database. If the number of logs exceeds this limit, the oldest logs will be deleted.
|
|
360
|
+
:param log_date_limit: The date from which logs should be kept. If this is set, logs older than this date will be deleted.
|
|
384
361
|
:return:
|
|
385
362
|
"""
|
|
386
363
|
# If reload instant is true, this adds an extra field 'run_instant' to the update query, and sets the value to 1. This makes the task reload immediately after it's finished
|
|
@@ -414,19 +391,20 @@ class TaskScheduler(BrynQ):
|
|
|
414
391
|
# Start the new task if it there is a task which should start if this one is finished
|
|
415
392
|
self.start_chained_tasks(finished_task_status='SUCCESS')
|
|
416
393
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
394
|
+
if self.write_logs_to_mysql:
|
|
395
|
+
# Clean up execution log
|
|
396
|
+
# set this date filter above the actual delete filter because of the many uncooperative quotation marks involved in the whole filter
|
|
397
|
+
log_date_limit_filter = f"AND created_at >= \'{log_date_limit.strftime('%Y-%m-%d')}\'" if log_date_limit is not None else None
|
|
398
|
+
delete_filter = f"WHERE task_id = {self.task_id} " \
|
|
399
|
+
f"AND reload_id NOT IN (SELECT reload_id FROM (SELECT reload_id FROM `task_execution_log` WHERE task_id = {self.task_id} " \
|
|
400
|
+
f"AND log_level != 'CRITICAL' " \
|
|
401
|
+
f"AND log_level != 'ERROR' " \
|
|
402
|
+
f"{log_date_limit_filter if log_date_limit_filter is not None else ''} " \
|
|
403
|
+
f"ORDER BY created_at DESC {f' LIMIT {log_limit} ' if log_limit is not None else ''}) temp)"
|
|
404
|
+
|
|
405
|
+
resp = self.mysql.delete(table="task_execution_log",
|
|
406
|
+
filter=delete_filter)
|
|
407
|
+
print(resp)
|
|
430
408
|
|
|
431
409
|
def start_chained_tasks(self, finished_task_status: str):
|
|
432
410
|
filter = f'WHERE start_after_task_id = \'{self.task_id}\' AND start_after_preceding_task = \'{finished_task_status}\''
|
|
@@ -445,10 +423,17 @@ class TaskScheduler(BrynQ):
|
|
|
445
423
|
email_list = []
|
|
446
424
|
for i in email_to:
|
|
447
425
|
email_list.append({'name': 'BrynQ User', 'mail': i.strip()})
|
|
426
|
+
|
|
427
|
+
# Recieve the task name and the finished_at time from the task_scheduler table joined with the data_interfaces table
|
|
428
|
+
response = self.mysql.select(
|
|
429
|
+
table='task_scheduler LEFT JOIN data_interfaces ON task_scheduler.data_interface_id = data_interfaces.id ',
|
|
430
|
+
selection="title, last_reload",
|
|
431
|
+
filter=f'WHERE task_scheduler.id = {self.task_id}'
|
|
432
|
+
)
|
|
433
|
+
task = response[0][0]
|
|
434
|
+
finished_at = response[0][1]
|
|
435
|
+
|
|
448
436
|
# Set the content of the mail and all other stuff
|
|
449
|
-
task = self.mysql.select(table='data_interfaces', selection='title', filter=f'WHERE id = {self.task_id}')[0][0]
|
|
450
|
-
finished_at = \
|
|
451
|
-
self.mysql.select(table='task_scheduler', selection='last_reload', filter=f'WHERE data_interface_id = {self.task_id}')[0][0]
|
|
452
437
|
if failed:
|
|
453
438
|
subject = f'Task \'{task}\' has failed'
|
|
454
439
|
content = f'Task \'{task}\' with task ID \'{self.task_id}\' failed during its last run and was stopped at {finished_at}. ' \
|
|
@@ -3,7 +3,7 @@ from setuptools import setup
|
|
|
3
3
|
|
|
4
4
|
setup(
|
|
5
5
|
name='brynq_sdk_task_scheduler',
|
|
6
|
-
version='
|
|
6
|
+
version='2.0.1',
|
|
7
7
|
description='Code to execute tasks in BrynQ.com with the task scheduler',
|
|
8
8
|
long_description='Code to execute tasks in the BrynQ.com platform with the task scheduler',
|
|
9
9
|
author='BrynQ',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|