コード例 #1
0
ファイル: plates.py プロジェクト: sanger/lighthouse
def find_cherrytrack_plate_from_barcode() -> FlaskResponse:
    logger.info("Finding cherry track plate from barcode")
    try:
        barcode = request.args.get("barcode") or ""

        assert len(barcode) > 0, "Include a barcode in the request"

        plate_type = request.args.get(ARG_TYPE) or ""

        logger.debug(f"{plate_type} plate barcode to look for: {barcode}")

        if plate_type == ARG_TYPE_SOURCE:
            response = get_samples_from_source_plate_barcode_from_cherrytrack(
                barcode)
        elif plate_type == ARG_TYPE_DESTINATION:
            response = get_wells_from_destination_barcode_from_cherrytrack(
                barcode)
        else:
            raise AssertionError(
                f"Plate type needs to be either '{ARG_TYPE_SOURCE}' or '{ARG_TYPE_DESTINATION}'"
            )

        response_json = response.json()

        if response_json.get("errors") is not None:
            return internal_server_error(response_json.get("errors"))

        return ok(plate=response_json)
    except AssertionError as e:
        return bad_request(str(e))
    except Exception as e:
        logger.exception(e)
        return internal_server_error(f"Failed to lookup plate: {str(e)}")
コード例 #2
0
ファイル: reports.py プロジェクト: sanger/lighthouse
def get_reports() -> FlaskResponse:
    """Gets a list of all the available reports.

    Note: This is the existing implementation, currently used for the v1 endpoint.

    Returns:
        FlaskResponse: list of report details and an HTTP status code.
    """
    logger.info("Getting reports")
    try:
        reports = get_reports_details()

        return ok(reports=reports)
    except Exception as e:
        msg = f"{ERROR_UNEXPECTED} ({type(e).__name__})"
        logger.error(msg)
        logger.exception(e)

        return internal_server_error(msg)
コード例 #3
0
def get_failure_types() -> FlaskResponse:
    """Get a list of the Beckman failure types.

    Note: This is the existing implementation, currently used for the v1 endpoint.

    Returns:
        FlaskResponse: a list of failure types if found or a list of errors with the corresponding HTTP status code.
    """
    logger.info("Fetching Beckman failure type information")
    try:
        failure_types = Beckman.get_failure_types()

        return ok(errors=[], failure_types=failure_types)
    except Exception as e:
        logger.exception(e)

        return internal_server_error(
            f"{ERROR_UNEXPECTED} while fetching Beckman failure type information",
            failure_types=[])
コード例 #4
0
def get_robots() -> FlaskResponse:
    """Find information about the Beckman robots. Currently, this information lives in config.

    Note: This is the existing implementation, currently used for the v1 endpoint.

    Returns:
        FlaskResponse: the config for the robots configures with the corresponding HTTP status code.
    """
    logger.info("Fetching Beckman robot information")
    try:
        robots = Beckman.get_robots()

        return ok(errors=[], robots=robots)
    except Exception as e:
        logger.exception(e)

        return internal_server_error(
            f"{ERROR_UNEXPECTED} while fetching Beckman robot information",
            robots=[])
コード例 #5
0
ファイル: reports.py プロジェクト: sanger/lighthouse
def delete_reports() -> FlaskResponse:
    """A route which accepts a list of report filenames and then deletes them from the reports path.
    This endpoint should be JSON and the body should be in the format:

    `{"data":"filenames":["file1.xlsx","file2.xlsx", ...]}`

    This is a POST request but is a destructive action but this does not need to be delete as it is not a REST resource.

    Note: This is the existing implementation, currently used for the v1 endpoint.

    Returns:
        FlaskResponse: empty response body for OK; otherwise details of any errors and the corresponding HTTP status
        code.
    """
    logger.info("Attempting to delete report(s)")
    try:
        if (request_json := request.get_json()) is not None:
            if (data := request_json.get("data")) is not None:
                if (filenames := data.get("filenames")) is not None:
                    delete_reports_helper(filenames)

                    return ok()
コード例 #6
0
ファイル: plates.py プロジェクト: sanger/lighthouse
def find_plate_from_barcode() -> FlaskResponse:
    """A route which returns information about a list of comma separated plates as specified
    in the 'barcodes' parameters. Default fields can be excluded from the response using the url
    param '_exclude'.

    Note: This is the existing implementation, currently used for the v1 endpoint.

    ### Source plate example
    To fetch data for the source plates with barcodes '123' and '456' and exclude field 'picked_samples' from the
    output:

    #### Query:
    ```
    GET /plates?barcodes=123,456&_exclude=picked_samples
    ```

    #### Response:
    ```json
    {"plates":
        [
            {
                "plate_barcode": "123",
                "has_plate_map": true,
                "count_must_sequence": 0,
                "count_preferentially_sequence": 0,
                "count_filtered_positive": 2,
                "count_fit_to_pick_samples": 2,
            },
            {
                "plate_barcode": "456",
                "has_plate_map": false,
                "count_must_sequence": 0,
                "count_preferentially_sequence": 0,
                "count_filtered_positive": 4,
                "count_fit_to_pick_samples": 4,
            },
        ]
    }
    ```

    ### Destination plate example
    To fetch data for the destination plates with barcodes 'destination_123' and 'destination_456':

    #### Query:
    ```
    GET /plates?barcodes=destination_123,destination_456&_type=destination
    ```

    #### Response:
    ```json
    {"plates":
        [
            {
                "plate_barcode": "destination_123",
                "plate_exists": true,
            },
            {
                "plate_barcode": "destination_456",
                "plate_exists": false,
            },
        ]
    }
    ```

    Returns:
        FlaskResponse: the response body and HTTP status code
    """
    logger.info("Finding plate from barcode")
    try:
        barcodes_arg = request.args.get("barcodes")
        barcodes_list = barcodes_arg.split(",") if barcodes_arg else []

        assert len(
            barcodes_list
        ) > 0, "Include a list of barcodes separated by commas (,) in the request"

        plate_type = request.args.get(ARG_TYPE, ARG_TYPE_SOURCE)
        assert plate_type in (
            ARG_TYPE_SOURCE,
            ARG_TYPE_DESTINATION,
        ), f"Plate type needs to be either '{ARG_TYPE_SOURCE}' or '{ARG_TYPE_DESTINATION}'"

        exclude_props_arg = request.args.get(ARG_EXCLUDE)
        exclude_props = exclude_props_arg.split(
            ",") if exclude_props_arg else []

        logger.debug(
            f"{plate_type} plate(s) barcodes to look for: {barcodes_arg}")

        plates = [
            format_plate(barcode,
                         exclude_props=exclude_props,
                         plate_type=plate_type) for barcode in barcodes_list
        ]

        pretty(logger, plates)

        return ok(plates=plates)
    except AssertionError as e:
        return bad_request(str(e))
    except Exception as e:
        logger.exception(e)
        # We don't use str(e) here to fetch the exception summary, because the exceptions we're most likely to see here
        #   aren't end-user-friendly
        return internal_server_error(
            f"Failed to lookup plates: {type(e).__name__}")
コード例 #7
0
ファイル: beckman_events.py プロジェクト: sanger/lighthouse
def create_plate_event() -> FlaskResponse:
    """/v3/plate-events/create beckman endpoint to publish a plate event message to the RabbitMQ broker.

    Returns:
        FlaskResponse: if successful, return an empty list of errors and an OK status; otherwise, a list of errors and
        the corresponding HTTP status code.
    """
    required_params = (FIELD_EVENT_TYPE, FIELD_EVENT_ROBOT,
                       FIELD_EVENT_USER_ID)

    write_exception_error = False
    plate_event = None
    try:
        event = create_event_dict(request, required_params)
        automation_system = AutomationSystem.AutomationSystemEnum.BECKMAN
        # Assume we only receive one event

        beckman = Beckman()
        event_type = event.get(FIELD_EVENT_TYPE)

        if event_type is None or not isinstance(event_type,
                                                str) or not event_type:
            raise Exception("Cannot determine event type in hook")

        logger.info(
            f"Attempting to process a '{event_type}' plate event message received from {automation_system.name}"
        )

        plate_event = beckman.get_plate_event(event_type)
        plate_event.initialize_event(event)
        write_exception_error = True

        if plate_event.is_valid():
            plate_event.process_event()

        plate_event.process_errors()
        if len(plate_event.errors) > 0:
            write_exception_error = False
            raise Exception("The processing of the event failed.")

        return ok(errors=[])

    except Exception as e:
        if write_exception_error:
            if plate_event is not None:
                plate_event.process_exception(e)

        message = str(e)
        issues: Any = None

        if plate_event is not None and hasattr(plate_event, "errors"):
            issues = plate_event.errors
        else:
            issues = [message]

        abort(
            make_response(
                jsonify({
                    "_status": "ERR",
                    "_issues": issues,
                    "_error": {
                        "code": HTTPStatus.INTERNAL_SERVER_ERROR,
                        "message": message,
                    },
                }),
                HTTPStatus.INTERNAL_SERVER_ERROR,
            ))
コード例 #8
0
def fail_plate_from_barcode() -> FlaskResponse:
    """This endpoints attempts to publish an event to the event warehouse when a failure occurs when a destination plate
    is not created successfully.

    Note: This is the existing implementation, currently used for the v1 endpoint.

    Returns:
        FlaskResponse: If the message is published successfully return with an OK otherwise return the error messages
        and the corresponding HTTP status code.
    """
    logger.info(
        f"Attempting to publish a '{PE_BECKMAN_DESTINATION_FAILED}' message")
    try:
        required_args = (ARG_USER_ID, ARG_BARCODE, ARG_ROBOT_SERIAL,
                         ARG_FAILURE_TYPE)
        user_id, barcode, robot_serial_number, failure_type = get_required_params(
            request, required_args)
    except Exception as e:
        logger.error(
            f"{ERROR_CHERRYPICKED_FAILURE_RECORD} {ERROR_MISSING_PARAMETERS}")
        logger.exception(e)

        return bad_request(str(e))
    try:
        if failure_type not in app.config["ROBOT_FAILURE_TYPES"]:
            logger.error(
                f"{ERROR_CHERRYPICKED_FAILURE_RECORD} unknown failure type")

            return bad_request(
                f"'{failure_type}' is not a known cherrypicked plate failure type"
            )

        errors, message = construct_cherrypicking_plate_failed_message(
            barcode, user_id, robot_serial_number, failure_type)

        if message is None:
            logger.error(
                f"{ERROR_CHERRYPICKED_FAILURE_RECORD} error(s) constructing event message: {errors}"
            )

            return internal_server_error(errors)

        routing_key = get_routing_key(PE_BECKMAN_DESTINATION_FAILED)

        logger.info(
            "Attempting to publish the destination failed event message")
        broker = Broker()
        # To use the broker as a context manager we don't need these methods to be public but we still need to refactor
        #   these calls to use a context manager
        broker._connect()
        try:
            broker.publish(message, routing_key)
            broker._close_connection()
            logger.info(
                f"Successfully published a '{PE_BECKMAN_DESTINATION_FAILED}' message"
            )

            return ok(errors=errors)

        except Exception:
            broker._close_connection()
            raise
    except Exception as e:
        msg = f"{ERROR_UNEXPECTED_CHERRYPICKING_FAILURE} ({type(e).__name__})"
        logger.error(msg)
        logger.exception(e)

        return internal_server_error(msg)