def call_mad_api()

in src/olympia/scanners/tasks.py [0:0]


def call_mad_api(all_results, upload_pk):
    """
    Call the machine learning API (mad-server) for a given FileUpload.

    This task is the callback of the Celery chord in the validation chain. It
    receives all the results returned by all the tasks in this chord.

    - `all_results` are the results returned by all the tasks in the chord.
    - `upload_pk` is the FileUpload ID.
    """
    # In case of a validation error (linter or scanner), we do want to skip
    # this task. This is similar to the behavior of all other tasks decorated
    # with `@validation_task` but, because this task is the callback of a
    # Celery chord, we cannot use this decorator.
    for results in all_results:
        if results['errors'] > 0:
            return results

    # The first task registered in the chord is `forward_linter_results()`:
    results = all_results[0]

    if not waffle.switch_is_active('enable-mad'):
        log.info('Skipping scanner "mad" task, switch is off')
        return results

    request_id = uuid.uuid4().hex
    log.info(
        'Starting scanner "mad" task for FileUpload %s, request_id=%s.',
        upload_pk,
        request_id,
    )

    try:
        # TODO: retrieve all scanner results and pass each result to the API.
        customs_results = ScannerResult.objects.get(
            upload_id=upload_pk, scanner=CUSTOMS
        )

        scanMapKeys = customs_results.results.get('scanMap', {}).keys()
        if len(scanMapKeys) < 2:
            log.info(
                'Not calling scanner "mad" for FileUpload %s, scanMap is too small.',
                upload_pk,
            )
            statsd.incr('devhub.mad.skip')
            return results

        with statsd.timer('devhub.mad'):
            with requests.Session() as http:
                adapter = make_adapter_with_retry()
                http.mount('http://', adapter)
                http.mount('https://', adapter)

                json_payload = {'scanners': {'customs': customs_results.results}}
                response = http.post(
                    url=settings.MAD_API_URL,
                    json=json_payload,
                    timeout=settings.MAD_API_TIMEOUT,
                    headers={'x-request-id': request_id},
                )

        try:
            data = response.json()
        except ValueError as exc:
            # Log the response body when JSON decoding has failed.
            raise ValueError(response.text) from exc

        if response.status_code != 200:
            raise ValueError(data)

        default_score = -1
        ScannerResult.objects.create(
            upload_id=upload_pk,
            scanner=MAD,
            results=data,
            score=data.get('ensemble', default_score),
        )

        # Update the individual scanner results with some info from MAD.
        customs_data = data.get('scanners', {}).get('customs', {})
        customs_score = customs_data.get('score', default_score)
        customs_model_version = customs_data.get('model_version')
        customs_results.update(score=customs_score, model_version=customs_model_version)

        statsd.incr('devhub.mad.success')
        log.info('Ending scanner "mad" task for FileUpload %s.', upload_pk)
    except Exception:
        statsd.incr('devhub.mad.failure')
        # We log the exception but we do not raise to avoid perturbing the
        # submission flow.
        log.exception('Error in scanner "mad" task for FileUpload %s.', upload_pk)

    return results