brynq-sdk-task-scheduler 1.2.1__tar.gz → 2.0.0__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.2.1
3
+ Version: 2.0.0
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()
@@ -58,7 +60,7 @@ class TaskScheduler(BrynQ):
58
60
  self.task_manual_started = self.check_if_task_manual_started()
59
61
 
60
62
  # Creates Elasticsearch index and data view if not exists
61
- self.es_index = f"task_execution_log_{self.customer_db}_{self.started_at.strftime('%Y_%m')}"
63
+ self.es_index = f"task_execution_log_{self.customer_db}"
62
64
  self.es.create_index(index_name=self.es_index)
63
65
  self.es.create_data_view(space_name='interfaces', view_name=f'task_execution_log_{self.customer_db}', name=f'Task execution log {self.customer_db}', time_field='started_at')
64
66
 
@@ -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()
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)
338
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,15 @@ 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
353
+
379
354
 
380
355
  def finish_task(self, reload_instant=False, log_limit: typing.Optional[int] = 10000, log_date_limit: datetime.date = None):
381
356
  """
382
357
  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
358
  variables table. Also clean up the execution_log table when the number of lines is more than 1000
359
+ :param reload_instant: If the task should start again after it's finished
360
+ :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.
361
+ :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
362
  :return:
385
363
  """
386
364
  # 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 +392,20 @@ class TaskScheduler(BrynQ):
414
392
  # Start the new task if it there is a task which should start if this one is finished
415
393
  self.start_chained_tasks(finished_task_status='SUCCESS')
416
394
 
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)
395
+ if self.write_logs_to_mysql:
396
+ # Clean up execution log
397
+ # set this date filter above the actual delete filter because of the many uncooperative quotation marks involved in the whole filter
398
+ log_date_limit_filter = f"AND created_at >= \'{log_date_limit.strftime('%Y-%m-%d')}\'" if log_date_limit is not None else None
399
+ delete_filter = f"WHERE task_id = {self.task_id} " \
400
+ f"AND reload_id NOT IN (SELECT reload_id FROM (SELECT reload_id FROM `task_execution_log` WHERE task_id = {self.task_id} " \
401
+ f"AND log_level != 'CRITICAL' " \
402
+ f"AND log_level != 'ERROR' " \
403
+ f"{log_date_limit_filter if log_date_limit_filter is not None else ''} " \
404
+ f"ORDER BY created_at DESC {f' LIMIT {log_limit} ' if log_limit is not None else ''}) temp)"
405
+
406
+ resp = self.mysql.delete(table="task_execution_log",
407
+ filter=delete_filter)
408
+ print(resp)
430
409
 
431
410
  def start_chained_tasks(self, finished_task_status: str):
432
411
  filter = f'WHERE start_after_task_id = \'{self.task_id}\' AND start_after_preceding_task = \'{finished_task_status}\''
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: brynq-sdk-task-scheduler
3
- Version: 1.2.1
3
+ Version: 2.0.0
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.2.1',
6
+ version='2.0.0',
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',