예제 #1
0
def dispatch(args):
    """ Main entry function. """
    if helper.is_terminated():
        return

    helper.g_log("events.run", 1)

    try:
        config.read_config()
    except Exception:
        logger.exception("Unable to read configuration. Skipping processing.")
        monitor.send_event(
            monitor.h_events.CONFIG_UPDATE,
            monitor.severity.WARNING,
            "Unable to read configuration (possibly locked)",
        )
        return

    success_folder = Path(config.mercure[mercure_folders.SUCCESS])
    error_folder = Path(config.mercure[mercure_folders.ERROR])
    retry_max = config.mercure["retry_max"]
    retry_delay = config.mercure["retry_delay"]

    # TODO: Sort list so that the oldest DICOMs get dispatched first
    with os.scandir(config.mercure[mercure_folders.OUTGOING]) as it:
        for entry in it:
            if entry.is_dir() and not has_been_send(entry.path) and is_ready_for_sending(entry.path):
                logger.info(f"Sending folder {entry.path}")
                execute(Path(entry.path), success_folder, error_folder, retry_max, retry_delay)

            # If termination is requested, stop processing series after the
            # active one has been completed
            if helper.is_terminated():
                break
예제 #2
0
def execute(
    source_folder: Path,
    success_folder: Path,
    error_folder: Path,
    retry_max,
    retry_delay,
):
    """
    Execute the dcmsend command. It will create a .sending file to indicate that
    the folder is being sent. This is to prevent double sending. If there
    happens any error the .lock file is deleted and an .error file is created.
    Folder with .error files are _not_ ready for sending.
    """
    target_info = is_ready_for_sending(source_folder)
    delay = target_info.get("next_retry_at", 0)

    if target_info and time.time() >= delay:
        logger.info(f"Folder {source_folder} is ready for sending")

        series_uid = target_info.get("series_uid", "series_uid-missing")
        target_name = target_info.get("target_name", "target_name-missing")

        if (series_uid == "series_uid-missing") or (target_name
                                                    == "target_name-missing"):
            send_event(h_events.PROCESSING, severity.WARNING,
                       f"Missing information for folder {source_folder}")

        # Create a .sending file to indicate that this folder is being sent,
        # otherwise the dispatcher would pick it up again if the transfer is
        # still going on
        lock_file = Path(source_folder) / mercure_names.PROCESSING
        try:
            lock_file.touch()
        except:
            send_event(h_events.PROCESSING, severity.ERROR,
                       f"Error sending {series_uid} to {target_name}")
            send_series_event(s_events.ERROR, series_uid, 0, target_name,
                              "Unable to create lock file")
            logger.exception(f"Unable to create lock file {lock_file.name}")
            return

        command = _create_command(target_info, source_folder)
        logger.debug(f"Running command {command}")
        try:
            run(split(command), check=True)
            logger.info(
                f"Folder {source_folder} successfully sent, moving to {success_folder}"
            )
            # Send bookkeeper notification
            file_count = len(
                list(Path(source_folder).glob(mercure_names.DCMFILTER)))
            send_series_event(
                s_events.DISPATCH,
                target_info.get("series_uid", "series_uid-missing"),
                file_count,
                target_info.get("target_name", "target_name-missing"),
                "",
            )
            _move_sent_directory(source_folder, success_folder)
            send_series_event(s_events.MOVE, series_uid, 0, success_folder, "")
        except CalledProcessError as e:
            dcmsend_error_message = DCMSEND_ERROR_CODES.get(e.returncode, None)
            logger.exception(
                f"Failed command:\n {command} \nbecause of {dcmsend_error_message}"
            )
            send_event(h_events.PROCESSING, severity.ERROR,
                       f"Error sending {series_uid} to {target_name}")
            send_series_event(s_events.ERROR, series_uid, 0, target_name,
                              dcmsend_error_message)
            retry_increased = increase_retry(source_folder, retry_max,
                                             retry_delay)
            if retry_increased:
                lock_file.unlink()
            else:
                logger.info(f"Max retries reached, moving to {error_folder}")
                send_series_event(s_events.SUSPEND, series_uid, 0, target_name,
                                  "Max retries reached")
                _move_sent_directory(source_folder, error_folder)
                send_series_event(s_events.MOVE, series_uid, 0, error_folder,
                                  "")
                send_event(h_events.PROCESSING, severity.ERROR,
                           f"Series suspended after reaching max retries")
    else:
        pass
예제 #3
0
def test_is_not_read_for_sending_for_empty_dir(fs):
    fs.create_dir("/var/data/")
    assert not is_ready_for_sending("/var/data")
예제 #4
0
def test_is_read_for_sending(fs):
    fs.create_dir("/var/data/")
    fs.create_file("/var/data/a.dcm")
    target = {"dispatch": {"target_ip": "0.0.0.0", "target_port": 104, "target_aet_target": "ANY"}}
    fs.create_file("/var/data/task.json", contents=json.dumps(target))
    assert is_ready_for_sending("/var/data")
예제 #5
0
def test_is_not_read_for_sending_while_sending(fs):
    fs.create_dir("/var/data/")
    fs.create_file("/var/data/" + mercure_names.PROCESSING)
    assert not is_ready_for_sending("/var/data")
예제 #6
0
def test_is_not_read_for_sending_while_locked(fs):
    fs.create_dir("/var/data/")
    fs.create_file("/var/data/" + mercure_names.LOCK)
    assert not is_ready_for_sending("/var/data")