def _maybe_restart_worker(self, paths, worker): """ Restart the worker if it has been started less than `self.max_worker_starts` times previously. Otherwise, log an error, and save the error image. :param paths: Paths object representing the image file. :type paths: src.io.TreeWalker.Paths :param worker: Worker to maybe restart :type worker: src.Workers.BaseWorker :return: True if worker was restarted, False otherwise :rtype: bool """ if worker.n_starts > self.max_worker_starts: LOGGER.error( __name__, f"{worker.__class__.__name__} failed for image: {paths.input_file}.", save=True, email=True, email_mode="error") return False else: worker.start() LOGGER.debug( __name__, f"Restarted {worker.__class__.__name__} for image: {paths.input_file}." ) return True
def main(): tree_walker, database_client = initialize() start_datetime = datetime.now() for i, paths in enumerate(tree_walker.walk()): count_str = f"{i + 1} of {tree_walker.n_valid_images}" LOGGER.info(__name__, LOG_SEP) LOGGER.info(__name__, f"Iteration: {count_str}.") LOGGER.info(__name__, f"Processing file {paths.input_file}") try: json_dict = load_json(paths) database_client.add_row(json_dict) except PROCESSING_EXCEPTIONS as err: LOGGER.error( __name__, f"Got error '{type(err).__name__}: {str(err)}' when writing JSON to Database. " f"File: {paths.input_file}") LOGGER.info(__name__, LOG_SEP) LOGGER.info(__name__, "Writing remaining files to Database") database_client.close() summary_str = get_summary(tree_walker, database_client, start_datetime) LOGGER.info(__name__, LOG_SEP) LOGGER.info(__name__, summary_str)
def send_mail(message_type, etype=None, ex=None, tb=None, msg=None): """ Send an email of type `message_type`. The sender, receiver(s) and smtp-server are configured in `email_config.py`. If `--log-folder` is specified to `src.main`, the log-file will be attached to the message. :param message_type: Type of message. This determines the subject and contents of the message. Must be one of - `critical`: This is suitable for critical errors which cause the program to exit abnormally. A critical message requires `etype`, `ex` and `tb` to be specified, and will include the exception type in the subject, and the traceback in the contents. - `error`: This message is suitable for processing errors which do not cause the program to exit. - `finished`: This message type should be used when the program exits normally. :type message_type: str :param etype: Exception type :type etype: type | None :param ex: Exception instance :type ex: BaseException | None :param tb: Traceback object :type tb: traceback.traceback | None :param msg: Message to include in the contents of the email. :type msg: str | None """ # Determine subject if message_type == "critical": msg = "".join(traceback.format_exception(etype, ex, tb)) subject = CRITICAL_SUBJECT.format(etype=etype.__name__, hostname=gethostname()) elif message_type == "error": subject = ERROR_SUBJECT.format(hostname=gethostname()) elif message_type == "finished": subject = FINISHED_SUBJECT.format(hostname=gethostname()) else: raise ValueError( f"Function `email.send_mail` got invalid message type: {message_type}" ) # Create the message message = create_base_message(subject, msg) # Try to send the email. If sending fails, log the message as an error, and continue. try: with smtplib.SMTP(email_config.smtp_host, email_config.port) as smtp: smtp.sendmail(from_addr=email_config.from_address, to_addrs=email_config.to_addresses, msg=message) except Exception as err: LOGGER.error( __name__, f"Got error '{str(err)}' when attempting to send e-mail.")
def main(): tree_walker = initialize() for i, paths in enumerate(tree_walker.walk()): count_str = f"{i + 1} of {tree_walker.n_valid_images}" LOGGER.info(__name__, LOG_SEP) LOGGER.info(__name__, f"Iteration: {count_str}.") LOGGER.info(__name__, f"Processing file {paths.input_file}") try: worker = EXIFWorker(None, paths, None) worker.get() except PROCESSING_EXCEPTIONS as err: LOGGER.error( f"Got error '{type(err).__name__}: {str(err)}' when creating JSON from image. " f"File: {paths.input_file}")
def main(): """Run the masking.""" # Initialize start_datetime = datetime.now() args, tree_walker, image_processor, dataset_iterator = initialize() n_imgs = "?" if config.lazy_paths else (tree_walker.n_valid_images + tree_walker.n_skipped_images) # Mask images time_at_iter_start = time.time() for i, paths in enumerate(tree_walker.walk()): count_str = f"{tree_walker.n_skipped_images + i + 1} of {n_imgs}" start_time = time.time() LOGGER.set_state(paths) LOGGER.info(__name__, LOG_SEP) LOGGER.info(__name__, f"Iteration: {count_str}.") # Catch potential exceptions raised while processing the image try: # Get the image img = next(dataset_iterator) # Do the processing image_processor.process_image(img, paths) except PROCESSING_EXCEPTIONS as err: error_msg = f"'{str(err)}'. File: {paths.input_file}" LOGGER.error(__name__, error_msg, save=True, email=True, email_mode="error") continue est_done = get_estimated_done(time_at_iter_start, n_imgs, i + 1) iter_time_delta = "{:.3f}".format(time.time() - start_time) LOGGER.info(__name__, f"Iteration finished in {iter_time_delta} s.") LOGGER.info(__name__, f"Estimated completion: {est_done}") # Close the image_processor. This will make sure that all exports are finished before we continue. LOGGER.info(__name__, LOG_SEP) LOGGER.info(__name__, f"Writing output files for the remaining images.") image_processor.close() # Summary summary_str = get_summary(tree_walker, image_processor, start_datetime) LOGGER.info(__name__, LOG_SEP) LOGGER.info(__name__, summary_str, email=True, email_mode="finished")
def handle_errors(self, errors, rows, action="writing to"): """ Log errors caused when running `cursor.executemany`. :param errors: Errors from `cursor.getbatcherrors` :type errors: list :param rows: Rows which caused the errors :type rows: list of dict :param action: Optional database action for the error message. :type action: str """ # Increment total error counter self.total_errors += len(errors) # Create an error message msg = f"Got {len(errors)} error(s) while {action} the database:\n" msg += "\n".join([err.message for err in errors]) # Log the error LOGGER.error(__name__, msg, save=False, email=True, email_mode="error")
def _handle_missing_files(paths, missing_files): """ Handle any missing files identified for a given image. This will log an error, which saves the error image, and sends an error-email, if email sending is enabled. :param paths: Paths object representing the input image :type paths: src.io.TreeWalker.Paths :param missing_files: List of missing files :type missing_files: list of str """ current_logger_state = LOGGER.get_state() LOGGER.set_state(paths) LOGGER.error( __name__, f"Missing output files {missing_files} for image: {paths.input_file}", save=True, email=True, email_mode="error") LOGGER.set_state(current_logger_state)
def handle_error(self, err): """ Handle an exception raised by an async worker. :param err: Exception raised by the worker :type err: BaseException """ # Get the current state of the logger current_logger_state = LOGGER.get_state() # Set the state of the logger to reflect the failed image. LOGGER.set_state(self.paths) # Log the error LOGGER.error(__name__, self.error_message.format( image_path=self.paths.input_file, err=str(err)), save=False, email=False) # Reset the state LOGGER.set_state(current_logger_state)