import responses

from tests.utils import request_body_to_json
from datarobot.errors import InvalidUsageError


@responses.activate
def test_set_target(datetime_partition_spec, holdout_start_date,
                    project_with_target_json, project_without_target,
                    unittest_endpoint, project_id):
    project_url = '{}/projects/{}/'.format(unittest_endpoint, project_id)
    aim_url = project_url + 'aim/'
    status_url = '{}/status/some-status/'.format(unittest_endpoint)

    responses.add(responses.PATCH,
                  aim_url,
                  body='',
                  status=202,
                  adding_headers={'Location': status_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  status_url,
                  body='',
                  status=303,
                  adding_headers={'Location': project_url},
                  content_type='application/json')
    responses.add(responses.GET,
                  project_url,
                  body=project_with_target_json,
                  status=200,
                  content_type='application/json')

    expected_backtests = [{
        'index': bt.index, 'gapDuration': bt.gap_duration,
        'validationStartDate': bt.validation_start_date.isoformat(),
        'validationDuration': bt.validation_duration
        } for bt in datetime_partition_spec.backtests]
    expected_payload = {
        'datetimePartitionColumn': datetime_partition_spec.datetime_partition_column,
        'useTimeSeries': datetime_partition_spec.use_time_series,
        'defaultToKnownInAdvance': datetime_partition_spec.default_to_known_in_advance,
        'defaultToDoNotDerive': datetime_partition_spec.default_to_do_not_derive,
        'autopilotDataSelectionMethod': datetime_partition_spec.autopilot_data_selection_method,
        'validationDuration': datetime_partition_spec.validation_duration,
        'holdoutStartDate': holdout_start_date.isoformat(),
        'holdoutDuration': datetime_partition_spec.holdout_duration,
        'gapDuration': datetime_partition_spec.gap_duration,
        'numberOfBacktests': datetime_partition_spec.number_of_backtests,
        'backtests': expected_backtests,
        'cvMethod': 'datetime',
        'target': 'target',
        'mode': 'auto',
        'quickrun': False,
        'modelSplits': 2
    }
    project_without_target.set_target('target', partitioning_method=datetime_partition_spec)
    assert responses.calls[0].request.method == 'PATCH'
    actual_request = request_body_to_json(responses.calls[0].request)

    assert set(actual_request.keys()) == set(expected_payload.keys())
    assert actual_request == expected_payload


@responses.activate
def test_time_series_set_target(time_series_partition_spec, holdout_start_date,
                                project_with_target_json, project_without_target,
                                unittest_endpoint, project_id):
    project_url = '{}/projects/{}/'.format(unittest_endpoint, project_id)
    aim_url = project_url + 'aim/'
    status_url = '{}/status/some-status/'.format(unittest_endpoint)

    time_series_partition_spec.calendar_id = project_id

    responses.add(responses.PATCH,
                  aim_url,
                  body='',
                  status=202,
                  adding_headers={'Location': status_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  status_url,
                  body='',
                  status=303,
                  adding_headers={'Location': project_url},
                  content_type='application/json')
    responses.add(responses.GET,
                  project_url,
                  body=project_with_target_json,
                  status=200,
                  content_type='application/json')

    expected_backtests = [{
        'index': bt.index, 'gapDuration': bt.gap_duration,
        'validationStartDate': bt.validation_start_date.isoformat(),
        'validationDuration': bt.validation_duration
        } for bt in time_series_partition_spec.backtests]
    expected_payload = {
        'datetimePartitionColumn': time_series_partition_spec.datetime_partition_column,
        'useTimeSeries': time_series_partition_spec.use_time_series,
        'defaultToKnownInAdvance': time_series_partition_spec.default_to_known_in_advance,
        'defaultToDoNotDerive': time_series_partition_spec.default_to_do_not_derive,
        'featureDerivationWindowStart': time_series_partition_spec.feature_derivation_window_start,
        'featureDerivationWindowEnd': time_series_partition_spec.feature_derivation_window_end,
        'forecastWindowStart': time_series_partition_spec.forecast_window_start,
        'forecastWindowEnd': time_series_partition_spec.forecast_window_end,
        'autopilotDataSelectionMethod': time_series_partition_spec.autopilot_data_selection_method,
        'validationDuration': time_series_partition_spec.validation_duration,
        'holdoutStartDate': holdout_start_date.isoformat(),
        'holdoutDuration': time_series_partition_spec.holdout_duration,
        'gapDuration': time_series_partition_spec.gap_duration,
        'numberOfBacktests': time_series_partition_spec.number_of_backtests,
        'backtests': expected_backtests,
        'cvMethod': 'datetime',
        'target': 'target',
        'mode': 'auto',
        'quickrun': False,
        'calendarId': time_series_partition_spec.calendar_id
    }
    project_without_target.set_target('target', partitioning_method=time_series_partition_spec)

    assert responses.calls[0].request.method == 'PATCH'
    actual_request = request_body_to_json(responses.calls[0].request)
    assert set(actual_request.keys()) == set(expected_payload.keys())
    assert actual_request == expected_payload


@responses.activate
def test_irregular_time_series_set_target(irregular_time_series_partition_spec,
                                          holdout_start_date,
                                          project_without_target,
                                          project_with_target_json,
                                          unittest_endpoint,
                                          project_id):
    project_url = '{}/projects/{}/'.format(unittest_endpoint, project_id)
    aim_url = project_url + 'aim/'
    status_url = '{}/status/some-status/'.format(unittest_endpoint)
    spec = irregular_time_series_partition_spec

    responses.add(responses.PATCH,
                  aim_url,
                  body='',
                  status=202,
                  adding_headers={'Location': status_url},
                  content_type='application/json')
    responses.add(responses.GET,
                  status_url,
                  body='',
                  status=303,
                  adding_headers={'Location': project_url},
                  content_type='application/json')
    responses.add(responses.GET,
                  project_url,
                  body=project_with_target_json,
                  status=200,
                  content_type='application/json')
    expected_backtests = [{
        'index': bt.index, 'gapDuration': bt.gap_duration,
        'validationStartDate': bt.validation_start_date.isoformat(),
        'validationDuration': bt.validation_duration
        } for bt in irregular_time_series_partition_spec.backtests]
    expected_payload = {
        'datetimePartitionColumn': spec.datetime_partition_column,
        'useTimeSeries': spec.use_time_series,
        'defaultToKnownInAdvance': spec.default_to_known_in_advance,
        'defaultToDoNotDerive': spec.default_to_do_not_derive,
        'featureDerivationWindowStart': spec.feature_derivation_window_start,
        'featureDerivationWindowEnd': spec.feature_derivation_window_end,
        'forecastWindowStart': spec.forecast_window_start,
        'forecastWindowEnd': spec.forecast_window_end,
        'autopilotDataSelectionMethod': spec.autopilot_data_selection_method,
        'holdoutStartDate': holdout_start_date.isoformat(),
        'holdoutDuration': spec.holdout_duration,
        'gapDuration': spec.gap_duration,
        'numberOfBacktests': spec.number_of_backtests,
        'backtests': expected_backtests,
        'cvMethod': 'datetime',
        'target': 'target',
        'mode': 'auto',
        'quickrun': False,
    }
    project_without_target.set_target('target', partitioning_method=spec)

    assert responses.calls[0].request.method == 'PATCH'
    actual_request = request_body_to_json(responses.calls[0].request)
    assert set(actual_request.keys()) == set(expected_payload.keys())
    assert actual_request == expected_payload


@responses.activate
def test_multiseries_set_target(time_series_partition_spec, holdout_start_date,
                                project_with_target_json, project_without_target,
                                unittest_endpoint, project_id):
    time_series_partition_spec.multiseries_id_columns = ['series_id']
    project_url = '{}/projects/{}/'.format(unittest_endpoint, project_id)
    aim_url = project_url + 'aim/'
    status_url = '{}/status/some-status/'.format(unittest_endpoint)
    multi_prop_url = '{}features/{}/multiseriesProperties/'.format(
        project_url, time_series_partition_spec.datetime_partition_column)
    multi_prop_json = {
        'datetimePartitionColumn': time_series_partition_spec.datetime_partition_column,
        'detectedMultiseriesIdColumns': [
            {'multiseriesIdColumns': ['series_id'], 'timeStep': 1, 'timeUnit': 'DAY'}
        ]
    }
    responses.add(responses.GET, multi_prop_url,
                  json=multi_prop_json)
    date_feat_json = {
        'projectId': project_id, 'name': time_series_partition_spec.datetime_partition_column,
        'id': 2, 'timeSeriesEligible': False, 'featureType': 'date',
        'timeSeriesEligibilityReason': 'notUnique', 'lowInformation': False, 'uniqueCount': 500
    }
    responses.add(responses.GET,
                  '{}features/{}/'.format(project_url,
                                          time_series_partition_spec.datetime_partition_column),
                  json=date_feat_json)

    responses.add(responses.PATCH,
                  aim_url,
                  body='',
                  status=202,
                  adding_headers={'Location': status_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  status_url,
                  body='',
                  status=303,
                  adding_headers={'Location': project_url},
                  content_type='application/json')
    responses.add(responses.GET,
                  project_url,
                  body=project_with_target_json,
                  status=200,
                  content_type='application/json')

    expected_backtests = [{
        'index': bt.index, 'gapDuration': bt.gap_duration,
        'validationStartDate': bt.validation_start_date.isoformat(),
        'validationDuration': bt.validation_duration
        } for bt in time_series_partition_spec.backtests]
    expected_payload = {
        'datetimePartitionColumn': time_series_partition_spec.datetime_partition_column,
        'multiseriesIdColumns': time_series_partition_spec.multiseries_id_columns,
        'useTimeSeries': time_series_partition_spec.use_time_series,
        'defaultToKnownInAdvance': time_series_partition_spec.default_to_known_in_advance,
        'defaultToDoNotDerive': time_series_partition_spec.default_to_do_not_derive,
        'featureDerivationWindowStart': time_series_partition_spec.feature_derivation_window_start,
        'featureDerivationWindowEnd': time_series_partition_spec.feature_derivation_window_end,
        'forecastWindowStart': time_series_partition_spec.forecast_window_start,
        'forecastWindowEnd': time_series_partition_spec.forecast_window_end,
        'autopilotDataSelectionMethod': time_series_partition_spec.autopilot_data_selection_method,
        'validationDuration': time_series_partition_spec.validation_duration,
        'holdoutStartDate': holdout_start_date.isoformat(),
        'holdoutDuration': time_series_partition_spec.holdout_duration,
        'gapDuration': time_series_partition_spec.gap_duration,
        'numberOfBacktests': time_series_partition_spec.number_of_backtests,
        'backtests': expected_backtests,
        'cvMethod': 'datetime',
        'target': 'target',
        'mode': 'auto',
        'quickrun': False,
    }
    project_without_target.set_target('target', partitioning_method=time_series_partition_spec)

    assert responses.calls[2].request.method == 'PATCH'
    actual_request = request_body_to_json(responses.calls[2].request)
    assert set(actual_request.keys()) == set(expected_payload.keys())
    assert actual_request == expected_payload


@responses.activate
def test_use_cross_series_features(time_series_partition_spec, holdout_start_date,
                                   project_with_target_json, project_without_target,
                                   unittest_endpoint, project_id):
    time_series_partition_spec.multiseries_id_columns = ['series_id']
    time_series_partition_spec.use_cross_series_features = True
    time_series_partition_spec.aggregation_type = 'total'

    project_url = '{}/projects/{}/'.format(unittest_endpoint, project_id)
    aim_url = project_url + 'aim/'
    status_url = '{}/status/some-status/'.format(unittest_endpoint)
    multi_prop_url = '{}features/{}/multiseriesProperties/'.format(
        project_url, time_series_partition_spec.datetime_partition_column)
    multi_prop_json = {
        'datetimePartitionColumn': time_series_partition_spec.datetime_partition_column,
        'detectedMultiseriesIdColumns': [
            {'multiseriesIdColumns': ['series_id'], 'timeStep': 1, 'timeUnit': 'DAY'}
        ]
    }
    responses.add(responses.GET, multi_prop_url,
                  json=multi_prop_json)
    date_feat_json = {
        'projectId': project_id, 'name': time_series_partition_spec.datetime_partition_column,
        'id': 2, 'timeSeriesEligible': False, 'featureType': 'date',
        'timeSeriesEligibilityReason': 'notUnique', 'lowInformation': False, 'uniqueCount': 500
    }
    responses.add(responses.GET,
                  '{}features/{}/'.format(project_url,
                                          time_series_partition_spec.datetime_partition_column),
                  json=date_feat_json)

    responses.add(responses.PATCH,
                  aim_url,
                  body='',
                  status=202,
                  adding_headers={'Location': status_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  status_url,
                  body='',
                  status=303,
                  adding_headers={'Location': project_url},
                  content_type='application/json')
    responses.add(responses.GET,
                  project_url,
                  body=project_with_target_json,
                  status=200,
                  content_type='application/json')

    expected_backtests = [{
        'index': bt.index, 'gapDuration': bt.gap_duration,
        'validationStartDate': bt.validation_start_date.isoformat(),
        'validationDuration': bt.validation_duration
    } for bt in time_series_partition_spec.backtests]
    expected_payload = {
        'datetimePartitionColumn': time_series_partition_spec.datetime_partition_column,
        'multiseriesIdColumns': time_series_partition_spec.multiseries_id_columns,
        'useTimeSeries': time_series_partition_spec.use_time_series,
        'defaultToKnownInAdvance': time_series_partition_spec.default_to_known_in_advance,
        'defaultToDoNotDerive': time_series_partition_spec.default_to_do_not_derive,
        'featureDerivationWindowStart': time_series_partition_spec.feature_derivation_window_start,
        'featureDerivationWindowEnd': time_series_partition_spec.feature_derivation_window_end,
        'forecastWindowStart': time_series_partition_spec.forecast_window_start,
        'forecastWindowEnd': time_series_partition_spec.forecast_window_end,
        'autopilotDataSelectionMethod': time_series_partition_spec.autopilot_data_selection_method,
        'validationDuration': time_series_partition_spec.validation_duration,
        'holdoutStartDate': holdout_start_date.isoformat(),
        'holdoutDuration': time_series_partition_spec.holdout_duration,
        'gapDuration': time_series_partition_spec.gap_duration,
        'numberOfBacktests': time_series_partition_spec.number_of_backtests,
        'backtests': expected_backtests,
        'cvMethod': 'datetime',
        'target': 'target',
        'mode': 'auto',
        'quickrun': False,
        'useCrossSeriesFeatures': True,
        'aggregationType': 'total'
    }
    project_without_target.set_target('target', partitioning_method=time_series_partition_spec)

    assert responses.calls[2].request.method == 'PATCH'
    actual_request = request_body_to_json(responses.calls[2].request)
    assert set(actual_request.keys()) == set(expected_payload.keys())
    assert actual_request == expected_payload


@responses.activate
def test_use_cross_series_group_by_with_columns_validated(
        time_series_partition_spec, holdout_start_date, project_with_target_json,
        project_without_target, unittest_endpoint, project_id):

    time_series_partition_spec.multiseries_id_columns = ['series_id']
    time_series_partition_spec.use_cross_series_features = True
    time_series_partition_spec.aggregation_type = 'total'
    time_series_partition_spec.cross_series_group_by_columns = ['correct_location']

    project_url = '{}/projects/{}/'.format(unittest_endpoint, project_id)
    aim_url = project_url + 'aim/'
    status_url = '{}/status/some-status/'.format(unittest_endpoint)
    multi_prop_url = '{}features/{}/multiseriesProperties/'.format(
        project_url, time_series_partition_spec.datetime_partition_column)
    multi_prop_json = {
        'datetimePartitionColumn': time_series_partition_spec.datetime_partition_column,
        'detectedMultiseriesIdColumns': [
            {'multiseriesIdColumns': ['series_id'], 'timeStep': 1, 'timeUnit': 'DAY'}
        ]
    }
    responses.add(responses.GET, multi_prop_url, json=multi_prop_json)

    responses.add(responses.GET,
                  '{}features/{}/'.format(project_url,
                                          time_series_partition_spec.datetime_partition_column),
                  json={
                      'projectId': project_id,
                      'name': time_series_partition_spec.datetime_partition_column,
                      'id': 2,
                      'timeSeriesEligible': False,
                      'featureType': 'date',
                      'timeSeriesEligibilityReason': 'notUnique',
                      'lowInformation': False, 'uniqueCount': 500
                  })

    responses.add(responses.PATCH,
                  aim_url,
                  body='',
                  status=202,
                  adding_headers={'Location': status_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  status_url,
                  body='',
                  status=303,
                  adding_headers={'Location': project_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  project_url,
                  body=project_with_target_json,
                  status=200,
                  content_type='application/json')

    responses.add(responses.GET,
                  '{}features/{}/'.format(project_url,
                                          time_series_partition_spec.multiseries_id_columns[0]),
                  json={
                      'projectId': project_id,
                      'name': time_series_partition_spec.multiseries_id_columns[0],
                      'id': 3,
                      'timeSeriesEligible': True,
                      'featureType': 'category',
                      'timeSeriesEligibilityReason': 'suitable',
                      'lowInformation': False,
                      'uniqueCount': 100
                  })

    responses.add(responses.GET, ('{}multiseriesIds/{}/crossSeriesProperties/?'
                                  'crossSeriesGroupByColumns={}').format(
        project_url, time_series_partition_spec.multiseries_id_columns[0],
        time_series_partition_spec.cross_series_group_by_columns[0]),
                  json={
                      'multiseriesId': time_series_partition_spec.multiseries_id_columns[0],
                      'crossSeriesGroupByColumns': [
                          {
                              'name': time_series_partition_spec.cross_series_group_by_columns[0],
                              'eligibility': 'suitable',
                              'isEligible': True
                          }
                      ]
                  })

    expected_backtests = [{
        'index': bt.index, 'gapDuration': bt.gap_duration,
        'validationStartDate': bt.validation_start_date.isoformat(),
        'validationDuration': bt.validation_duration
    } for bt in time_series_partition_spec.backtests]
    expected_payload = {
        'datetimePartitionColumn': time_series_partition_spec.datetime_partition_column,
        'multiseriesIdColumns': time_series_partition_spec.multiseries_id_columns,
        'useTimeSeries': time_series_partition_spec.use_time_series,
        'defaultToKnownInAdvance': time_series_partition_spec.default_to_known_in_advance,
        'defaultToDoNotDerive': time_series_partition_spec.default_to_do_not_derive,
        'featureDerivationWindowStart': time_series_partition_spec.feature_derivation_window_start,
        'featureDerivationWindowEnd': time_series_partition_spec.feature_derivation_window_end,
        'forecastWindowStart': time_series_partition_spec.forecast_window_start,
        'forecastWindowEnd': time_series_partition_spec.forecast_window_end,
        'autopilotDataSelectionMethod': time_series_partition_spec.autopilot_data_selection_method,
        'validationDuration': time_series_partition_spec.validation_duration,
        'holdoutStartDate': holdout_start_date.isoformat(),
        'holdoutDuration': time_series_partition_spec.holdout_duration,
        'gapDuration': time_series_partition_spec.gap_duration,
        'numberOfBacktests': time_series_partition_spec.number_of_backtests,
        'backtests': expected_backtests,
        'cvMethod': 'datetime',
        'target': 'target',
        'mode': 'auto',
        'quickrun': False,
        'useCrossSeriesFeatures': True,
        'aggregationType': 'total',
        'crossSeriesGroupByColumns': time_series_partition_spec.cross_series_group_by_columns
    }
    project_without_target.set_target('target', partitioning_method=time_series_partition_spec)

    assert responses.calls[4].request.method == 'PATCH'
    actual_request = request_body_to_json(responses.calls[4].request)
    assert set(actual_request.keys()) == set(expected_payload.keys())
    assert actual_request == expected_payload


@responses.activate
def test_use_cross_series_group_by_with_columns_not_validated(
        time_series_partition_spec, project_with_target_json, project_without_target,
        holdout_start_date, unittest_endpoint, project_id):
    time_series_partition_spec.multiseries_id_columns = ['series_id']
    time_series_partition_spec.use_cross_series_features = True
    time_series_partition_spec.aggregation_type = 'total'
    time_series_partition_spec.cross_series_group_by_columns = ['location']

    project_url = '{}/projects/{}/'.format(unittest_endpoint, project_id)
    aim_url = project_url + 'aim/'
    status_url = '{}/status/some-status/'.format(unittest_endpoint)
    multi_prop_url = '{}features/{}/multiseriesProperties/'.format(
        project_url, time_series_partition_spec.datetime_partition_column)
    cross_series_url = '{}/projects/{}/crossSeriesProperties/'.format(unittest_endpoint, project_id)

    multi_prop_json = {
        'datetimePartitionColumn': time_series_partition_spec.datetime_partition_column,
        'detectedMultiseriesIdColumns': [
            {'multiseriesIdColumns': ['series_id'], 'timeStep': 1, 'timeUnit': 'DAY'}
        ]
    }

    responses.add(responses.GET, multi_prop_url,
                  json=multi_prop_json)

    responses.add(responses.GET,
                  '{}features/{}/'.format(project_url,
                                          time_series_partition_spec.datetime_partition_column),
                  json={
                      'projectId': project_id,
                      'name': time_series_partition_spec.datetime_partition_column,
                      'id': 2,
                      'timeSeriesEligible': False,
                      'featureType': 'date',
                      'timeSeriesEligibilityReason': 'notUnique',
                      'lowInformation': False, 'uniqueCount': 500
                  })

    responses.add(responses.PATCH,
                  aim_url,
                  body='',
                  status=202,
                  adding_headers={'Location': status_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  status_url,
                  body='',
                  status=303,
                  adding_headers={'Location': project_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  project_url,
                  body=project_with_target_json,
                  status=200,
                  content_type='application/json')

    responses.add(responses.GET,
                  '{}features/{}/'.format(project_url,
                                          time_series_partition_spec.multiseries_id_columns[0]),
                  json={
                      'projectId': project_id,
                      'name': time_series_partition_spec.multiseries_id_columns[0],
                      'id': 3,
                      'timeSeriesEligible': True,
                      'featureType': 'category',
                      'timeSeriesEligibilityReason': 'suitable',
                      'lowInformation': False,
                      'uniqueCount': 100
                  })

    responses.add(responses.GET, ('{}multiseriesIds/{}/crossSeriesProperties/?'
                                  'crossSeriesGroupByColumns={}').format(
        project_url, time_series_partition_spec.multiseries_id_columns[0],
        time_series_partition_spec.cross_series_group_by_columns[0]),
                  json={
                      'multiseriesId': time_series_partition_spec.multiseries_id_columns[0],
                      'crossSeriesGroupByColumns': [
                          {
                              'name': time_series_partition_spec.cross_series_group_by_columns[0],
                              'eligibility': 'notAnalyzed',
                              'isEligible': False
                          }
                      ]
                  })

    responses.add(responses.POST,
                  cross_series_url,
                  body='',
                  status=202,
                  adding_headers={'Location': status_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  status_url,
                  body='',
                  status=303,
                  adding_headers={'Location': project_url},
                  content_type='application/json')

    responses.add(responses.GET, ('{}multiseriesIds/{}/crossSeriesProperties/?'
                                  'crossSeriesGroupByColumns={}').format(
        project_url, time_series_partition_spec.multiseries_id_columns[0],
        time_series_partition_spec.cross_series_group_by_columns[0]),
                  json={
                      'multiseriesId': time_series_partition_spec.multiseries_id_columns[0],
                      'crossSeriesGroupByColumns': [
                          {
                              'name': time_series_partition_spec.cross_series_group_by_columns[0],
                              'eligibility': 'suitable',
                              'isEligible': True
                          }
                      ]
                  })

    expected_backtests = [{
        'index': bt.index, 'gapDuration': bt.gap_duration,
        'validationStartDate': bt.validation_start_date.isoformat(),
        'validationDuration': bt.validation_duration
    } for bt in time_series_partition_spec.backtests]
    expected_payload = {
        'datetimePartitionColumn': time_series_partition_spec.datetime_partition_column,
        'multiseriesIdColumns': time_series_partition_spec.multiseries_id_columns,
        'useTimeSeries': time_series_partition_spec.use_time_series,
        'defaultToKnownInAdvance': time_series_partition_spec.default_to_known_in_advance,
        'defaultToDoNotDerive': time_series_partition_spec.default_to_do_not_derive,
        'featureDerivationWindowStart': time_series_partition_spec.feature_derivation_window_start,
        'featureDerivationWindowEnd': time_series_partition_spec.feature_derivation_window_end,
        'forecastWindowStart': time_series_partition_spec.forecast_window_start,
        'forecastWindowEnd': time_series_partition_spec.forecast_window_end,
        'autopilotDataSelectionMethod': time_series_partition_spec.autopilot_data_selection_method,
        'validationDuration': time_series_partition_spec.validation_duration,
        'holdoutStartDate': holdout_start_date.isoformat(),
        'holdoutDuration': time_series_partition_spec.holdout_duration,
        'gapDuration': time_series_partition_spec.gap_duration,
        'numberOfBacktests': time_series_partition_spec.number_of_backtests,
        'backtests': expected_backtests,
        'cvMethod': 'datetime',
        'target': 'target',
        'mode': 'auto',
        'quickrun': False,
        'useCrossSeriesFeatures': True,
        'aggregationType': 'total',
        'crossSeriesGroupByColumns': time_series_partition_spec.cross_series_group_by_columns
    }

    project_without_target.set_target('target', partitioning_method=time_series_partition_spec)

    assert responses.calls[7].request.method == 'PATCH'
    actual_request = request_body_to_json(responses.calls[7].request)
    assert set(actual_request.keys()) == set(expected_payload.keys())
    assert actual_request == expected_payload


@responses.activate
def test_use_cross_series_group_by_with_invalid_columns(
        time_series_partition_spec, project_with_target_json,
        project_without_target, unittest_endpoint, project_id):

    time_series_partition_spec.multiseries_id_columns = ['series_id']
    time_series_partition_spec.use_cross_series_features = True
    time_series_partition_spec.aggregation_type = 'total'
    time_series_partition_spec.cross_series_group_by_columns = ['location']

    project_url = '{}/projects/{}/'.format(unittest_endpoint, project_id)
    aim_url = project_url + 'aim/'
    status_url = '{}/status/some-status/'.format(unittest_endpoint)
    multi_prop_url = '{}features/{}/multiseriesProperties/'.format(
        project_url, time_series_partition_spec.datetime_partition_column)
    multi_prop_json = {
        'datetimePartitionColumn': time_series_partition_spec.datetime_partition_column,
        'detectedMultiseriesIdColumns': [
            {'multiseriesIdColumns': ['series_id'], 'timeStep': 1, 'timeUnit': 'DAY'}
        ]
    }
    responses.add(responses.GET, multi_prop_url,
                  json=multi_prop_json)

    responses.add(responses.GET,
                  '{}features/{}/'.format(project_url,
                                          time_series_partition_spec.datetime_partition_column),
                  json={
                      'projectId': project_id,
                      'name': time_series_partition_spec.datetime_partition_column,
                      'id': 2,
                      'timeSeriesEligible': False,
                      'featureType': 'date',
                      'timeSeriesEligibilityReason': 'notUnique',
                      'lowInformation': False, 'uniqueCount': 500
                  })

    responses.add(responses.PATCH,
                  aim_url,
                  body='',
                  status=202,
                  adding_headers={'Location': status_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  status_url,
                  body='',
                  status=303,
                  adding_headers={'Location': project_url},
                  content_type='application/json')

    responses.add(responses.GET,
                  project_url,
                  body=project_with_target_json,
                  status=200,
                  content_type='application/json')

    responses.add(responses.GET,
                  '{}features/{}/'.format(project_url,
                                          time_series_partition_spec.multiseries_id_columns[0]),
                  json={
                      'projectId': project_id,
                      'name': time_series_partition_spec.multiseries_id_columns[0],
                      'id': 3,
                      'timeSeriesEligible': True,
                      'featureType': 'category',
                      'timeSeriesEligibilityReason': 'suitable',
                      'lowInformation': False,
                      'uniqueCount': 100
                  })

    responses.add(responses.GET, ('{}multiseriesIds/{}/crossSeriesProperties/?'
                                  'crossSeriesGroupByColumns={}').format(
        project_url, time_series_partition_spec.multiseries_id_columns[0],
        time_series_partition_spec.cross_series_group_by_columns[0]),
                  json={
                      'multiseriesId': time_series_partition_spec.multiseries_id_columns[0],
                      'crossSeriesGroupByColumns': [
                          {
                              'name': time_series_partition_spec.cross_series_group_by_columns[0],
                              'eligibility': 'splitSeries',
                              'isEligible': False
                          }
                      ]
                  })

    try:
        project_without_target.set_target('target', partitioning_method=time_series_partition_spec)
        assert False, 'Should fail with exception as column \'{}\' is not eligible.'.format(
            time_series_partition_spec.cross_series_group_by_columns[0]
        )
    except InvalidUsageError:
        assert True
