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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: brynq_sdk_task_scheduler
3
- Version: 1.3.0
3
+ Version: 2.0.1
4
4
  Summary: Code to execute tasks in BrynQ.com with the task scheduler
5
5
  Home-page: UNKNOWN
6
6
  Author: BrynQ
@@ -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': os.getenv('BRYNQ_SUBDOMAIN').lower().replace(' ', '_'),
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
- # Remove quotes from message since these break the query
262
- message = re.sub("[']", '', message)
263
- if self.loglevel == 'DEBUG':
264
- # Count the errors
265
- if loglevel == 'ERROR' or loglevel == 'CRITICAL':
266
- self.error_count += 1
267
- return self.mysql.raw_query(
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': os.getenv('BRYNQ_SUBDOMAIN').lower().replace(' ', '_'),
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', datetime.datetime.now(), 'Failed', 0],
337
+ ['IDLE', now, 'Failed', 0],
343
338
  'WHERE `id` = {}'.format(self.task_id))
344
- # Log to database
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'{datetime.datetime.now()}'],
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
- # Clean up execution log
418
- # set this date filter above the actual delete filter because of the many uncooperative quotation marks involved in the whole filter
419
- log_date_limit_filter = f"AND created_at >= \'{log_date_limit.strftime('%Y-%m-%d')}\'" if log_date_limit is not None else None
420
- delete_filter = f"WHERE task_id = {self.task_id} " \
421
- f"AND reload_id NOT IN (SELECT reload_id FROM (SELECT reload_id FROM `task_execution_log` WHERE task_id = {self.task_id} " \
422
- f"AND log_level != 'CRITICAL' " \
423
- f"AND log_level != 'ERROR' " \
424
- f"{log_date_limit_filter if log_date_limit_filter is not None else ''} " \
425
- f"ORDER BY created_at DESC {f' LIMIT {log_limit} ' if log_limit is not None else ''}) temp)"
426
-
427
- resp = self.mysql.delete(table="task_execution_log",
428
- filter=delete_filter)
429
- print(resp)
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}. ' \
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: brynq-sdk-task-scheduler
3
- Version: 1.3.0
3
+ Version: 2.0.1
4
4
  Summary: Code to execute tasks in BrynQ.com with the task scheduler
5
5
  Home-page: UNKNOWN
6
6
  Author: BrynQ
@@ -3,7 +3,7 @@ from setuptools import setup
3
3
 
4
4
  setup(
5
5
  name='brynq_sdk_task_scheduler',
6
- version='1.3.0',
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',