def setUp(self):
     dap_client_factory =Mock(DapClientFactory)
     self.land_cover_service = LandCoverService(dap_client_factory=dap_client_factory)
     self.model_run_service = ModelRunService()
     self.dataset_service = DatasetService()
     self.clean_database()
     self.user = self.login()
 def setUp(self):
     dap_client_factory = DapClientFactory()
     dap_client_factory.get_land_cover_dap_client = self._mock_get_land_cover_dap_client
     dap_client_factory.get_soil_properties_dap_client = self._mock_get_soil_properties_dap_client
     self.land_cover_service = LandCoverService(dap_client_factory=dap_client_factory)
     self.model_run_service = ModelRunService()
     self.dataset_service = DatasetService()
     self.parameter_service = ParameterService()
     self.clean_database()
     self.user = self.login()
     self.create_model_run_ready_for_submit()
    def submit_model_run(self, user):
        """
        Submit the model run which is being created to be run
        :param user: the logged in user
        :return:new status of the job
        """
        with self.readonly_scope() as session:
            model = self._get_model_run_being_created(session, user)
            parameters = self._get_parameters_for_creating_model(session, user)
            # 'Disconnect' parameters from session so you can edit them
            for parameter in parameters:
                session.expunge(parameter)
                make_transient(parameter)
                for parameter_value in parameter.parameter_values:
                    session.expunge(parameter_value)
                    make_transient(parameter_value)

        land_cover_service = LandCoverService()
        land_cover_actions = land_cover_service.get_land_cover_actions_for_model(model)

        status_name, message = self._job_runner_client.submit(model, parameters, land_cover_actions)

        with self.transaction_scope() as session:
            model = self._get_model_run_being_created(session, user)

            if status_name == constants.MODEL_RUN_STATUS_SUBMITTED:
                status = model.change_status(session, status_name)
            else:
                status = model.change_status(session, status_name, message)
                msg = FAILED_SUBMIT_SUPPORT_MESSAGE_TEMPLATE.format(
                    name=model.name,
                    description=model.description,
                    id=model.id,
                    error_message=message
                )
                self._email_service.send_email(
                    config['email.from_address'],
                    config['email.support_address'],
                    FAILED_SUBMIT_SUPPORT_SUBJECT_TEMPLATE,
                    msg)
            return status, message
    def test_GIVEN_thredds_not_working_WHEN_get_default_cover_THEN_zeros_returned(self):
        self.clean_database()
        self.user = self.login()
        self.create_model_run_with_user_uploaded_driving_data()

        def _get_broken_dap_client(url, key):
            return MockLandCoverDapClient("broken_url", key)

        dap_client_factory = DapClientFactory()
        dap_client_factory.get_land_cover_dap_client = _get_broken_dap_client
        self.land_cover_service = LandCoverService(dap_client_factory=dap_client_factory)

        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        fractional_vals = self.land_cover_service.get_default_fractional_cover(model_run, self.user)
        assert_that(fractional_vals, is_([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]))
class TestLandCoverService(TestWithFullModelRun):
    def setUp(self):
        dap_client_factory = DapClientFactory()
        dap_client_factory.get_land_cover_dap_client = self._mock_get_land_cover_dap_client
        dap_client_factory.get_soil_properties_dap_client = self._mock_get_soil_properties_dap_client
        self.land_cover_service = LandCoverService(dap_client_factory=dap_client_factory)
        self.model_run_service = ModelRunService()
        self.dataset_service = DatasetService()
        self.parameter_service = ParameterService()
        self.clean_database()
        self.user = self.login()
        self.create_model_run_ready_for_submit()

    @staticmethod
    def _mock_get_land_cover_dap_client(url, key):
        return MockLandCoverDapClient(url, key)

    @staticmethod
    def _mock_get_soil_properties_dap_client(url):
        return MockSoilPropertiesDapClient(url)

    def test_GIVEN_land_cover_regions_exist_WHEN_get_land_cover_region_by_id_THEN_land_cover_region_returned(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)

        land_cover_region = self.add_land_cover_region(model_run)
        id = land_cover_region.id

        returned_region = self.land_cover_service.get_land_cover_region_by_id(id)
        assert_that(returned_region.id, is_(id))
        assert_that(returned_region.name, is_("Wales"))

    def test_GIVEN_land_cover_categories_WHEN_get_land_cover_region_THEN_returned_region_has_category_loaded(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)

        land_cover_region = self.add_land_cover_region(model_run)
        id = land_cover_region.id

        with session_scope() as session:
            land_cover_cat = LandCoverRegionCategory()
            land_cover_cat.name = "River Catchments"
            land_cover_cat.driving_dataset_id = model_run.driving_dataset_id
            session.add(land_cover_cat)

        returned_region = self.land_cover_service.get_land_cover_region_by_id(id)
        assert_that(returned_region.category.name, is_("Countries"))

    def test_GIVEN_land_cover_values_WHEN_get_land_cover_values_THEN_land_cover_values_returned(self):
        lc_values = self.land_cover_service.get_land_cover_values(None)
        assert_that(len(lc_values), is_(9))
        names = [lc_value.name for lc_value in lc_values]
        assert 'Urban' in names
        assert constants.FRACTIONAL_ICE_NAME in names

    def test_GIVEN_return_no_ice_WHEN_get_land_cover_values_THEN_land_cover_values_returned(self):
        lc_values = self.land_cover_service.get_land_cover_values(None, return_ice=False)
        assert_that(len(lc_values), is_(8))
        names = [lc_value.name for lc_value in lc_values]
        assert 'Urban' in names
        assert constants.FRACTIONAL_ICE_NAME not in names

    def test_GIVEN_multiple_land_cover_categories_WHEN_get_categories_THEN_correct_categories_returned(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        datasets = self.dataset_service.get_driving_datasets(self.user)

        with session_scope() as session:
            cat1 = LandCoverRegionCategory()
            cat1.name = "Countries"
            cat1.driving_dataset_id = model_run.driving_dataset_id

            region1 = LandCoverRegion()
            region1.mask_file = "filepath"
            region1.name = "Wales"
            region1.category = cat1

            cat2 = LandCoverRegionCategory()
            cat2.name = "Rivers"
            cat2.driving_dataset_id = datasets[1].id

            region2 = LandCoverRegion()
            region2.mask_file = "filepath2"
            region2.name = "Thames"
            region2.category = cat2

            session.add_all([region1, region2])

        categories = self.land_cover_service.get_land_cover_categories(model_run.driving_dataset_id)
        assert_that(len(categories), is_(1))
        assert_that(categories[0].name, is_("Countries"))

    def test_GIVEN_categories_have_land_cover_regions_WHEN_get_categories_THEN_category_has_regions_loaded(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)

        with session_scope() as session:
            cat1 = LandCoverRegionCategory()
            cat1.name = "Countries"
            cat1.driving_dataset_id = model_run.driving_dataset_id

            region1 = LandCoverRegion()
            region1.mask_file = "filepath"
            region1.name = "Wales"
            region1.category = cat1

            cat2 = LandCoverRegionCategory()
            cat2.name = "Rivers"
            cat2.driving_dataset_id = model_run.driving_dataset_id

            region2 = LandCoverRegion()
            region2.mask_file = "filepath2"
            region2.name = "Thames"
            region2.category = cat2

            session.add_all([region1, region2])

        categories = self.land_cover_service.get_land_cover_categories(model_run.driving_dataset_id)
        assert_that(len(categories[0].regions), is_(1))
        assert_that(categories[0].regions[0].name, is_("Wales"))

    def test_GIVEN_land_cover_actions_WHEN_save_land_cover_actions_THEN_land_cover_actions_saved(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)

        land_cover_region = self.add_land_cover_region(model_run)
        self.add_land_cover_actions(land_cover_region, model_run, [(1, 1), (2, 3)], self.land_cover_service)

        with session_scope() as session:
            model_run = self.model_run_service._get_model_run_being_created(session, self.user)
        actions = model_run.land_cover_actions
        assert_that(len(actions), is_(2))

    def test_GIVEN_existing_land_cover_actions_WHEN_save_land_cover_actions_THEN_land_cover_actions_overwritten(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)

        land_cover_region = self.add_land_cover_region(model_run)
        self.add_land_cover_actions(land_cover_region, model_run, [(1, 1), (2, 3)], self.land_cover_service)

        self.add_land_cover_actions(land_cover_region, model_run, [(2, 4)], self.land_cover_service)

        with session_scope() as session:
            model_run = self.model_run_service._get_model_run_being_created(session, self.user)
        actions = model_run.land_cover_actions
        assert_that(len(actions), is_(1))
        assert_that(actions[0].value_id, is_(2))
        assert_that(actions[0].order, is_(4))

    def test_GIVEN_no_land_cover_actions_WHEN_save_land_cover_actions_THEN_all_land_cover_actions_removed(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)

        land_cover_region = self.add_land_cover_region(model_run)
        self.add_land_cover_actions(land_cover_region, model_run, [(1, 1), (2, 3)], self.land_cover_service)

        self.add_land_cover_actions(land_cover_region, model_run, [], self.land_cover_service)

        with session_scope() as session:
            model_run = self.model_run_service._get_model_run_being_created(session, self.user)
        actions = model_run.land_cover_actions
        assert_that(len(actions), is_(0))

    def test_GIVEN_land_cover_actions_saved_on_model_run_WHEN_get_land_cover_actions_THEN_actions_returned(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)

        land_cover_region = self.add_land_cover_region(model_run)
        self.add_land_cover_actions(land_cover_region, model_run, [(1, 1), (2, 3)], self.land_cover_service)

        actions = self.land_cover_service.get_land_cover_actions_for_model(model_run)
        assert_that(len(actions), is_(2))
        assert_that(actions[0].value_id, is_(1))
        assert_that(actions[0].order, is_(1))
        assert_that(actions[1].value_id, is_(2))
        assert_that(actions[1].order, is_(3))

    def test_GIVEN_land_cover_actions_saved_WHEN_get_actions_THEN_actions_have_regions_and_categories_loaded(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)

        land_cover_region = self.add_land_cover_region(model_run)
        self.add_land_cover_actions(land_cover_region, model_run, [(1, 1)], self.land_cover_service)

        action = self.land_cover_service.get_land_cover_actions_for_model(model_run)[0]
        assert_that(action.region.name, is_("Wales"))
        assert_that(action.region.category.name, is_("Countries"))

    def test_GIVEN_land_cover_actions_saved_WHEN_get_actions_THEN_actions_have_values_loaded(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)

        land_cover_region = self.add_land_cover_region(model_run)
        self.add_land_cover_actions(land_cover_region, model_run, [(1, 1)], self.land_cover_service)

        action = self.land_cover_service.get_land_cover_actions_for_model(model_run)[0]
        assert_that(action.value.name, is_("Broad-leaved Tree"))

    def test_GIVEN_fractional_string_WHEN_save_fractional_land_cover_for_model_THEN_fractional_cover_saved(self):
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        assert_that(model_run.land_cover_frac, is_(None))

        fractional_string = "0\t0\t0\t0\t0\t0\t0\t0\t1"
        self.land_cover_service.save_fractional_land_cover_for_model(model_run, fractional_string)

        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        assert_that(model_run.land_cover_frac, is_(fractional_string))

    def test_GIVEN_user_uploaded_driving_data_WHEN_get_default_fractional_cover_THEN_fractional_cover_returned(self):
        self.clean_database()
        self.user = self.login()
        self.create_model_run_with_user_uploaded_driving_data()

        model_run = self.set_model_run_latlon(self.user, 70, 0)

        fractional_vals = self.land_cover_service.get_default_fractional_cover(model_run, self.user)
        assert_that(fractional_vals, is_([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]))

    def test_GIVEN_two_matching_datasets_WHEN_get_default_cover_THEN_higher_priority_fractional_cover_returned(self):
        self.clean_database()
        self.user = self.login()
        self.create_model_run_with_user_uploaded_driving_data()

        model_run = self.set_model_run_latlon(self.user, 0, 0)
        fractional_vals = self.land_cover_service.get_default_fractional_cover(model_run, self.user)
        assert_that(fractional_vals, is_([0.02, 0.11, 0.02, 0.05, 0.35, 0.19, 0.22, 0.04, 0.0]))

    def test_GIVEN_no_matching_datasets_WHEN_get_default_cover_THEN_zeros_returned(self):
        self.clean_database()
        self.user = self.login()
        self.create_model_run_with_user_uploaded_driving_data()

        model_run = self.set_model_run_latlon(self.user, 88, 0)
        fractional_vals = self.land_cover_service.get_default_fractional_cover(model_run, self.user)
        assert_that(fractional_vals, is_([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]))

    def test_GIVEN_thredds_not_working_WHEN_get_default_cover_THEN_zeros_returned(self):
        self.clean_database()
        self.user = self.login()
        self.create_model_run_with_user_uploaded_driving_data()

        def _get_broken_dap_client(url, key):
            return MockLandCoverDapClient("broken_url", key)

        dap_client_factory = DapClientFactory()
        dap_client_factory.get_land_cover_dap_client = _get_broken_dap_client
        self.land_cover_service = LandCoverService(dap_client_factory=dap_client_factory)

        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        fractional_vals = self.land_cover_service.get_default_fractional_cover(model_run, self.user)
        assert_that(fractional_vals, is_([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]))

    def test_GIVEN_using_provided_driving_data_WHEN_get_default_fractional_cover_THEN_fractional_cover_returned(self):
        self.clean_database()
        self.user = self.login()
        self.create_alternate_model_run()

        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        fractional_vals = self.land_cover_service.get_default_fractional_cover(model_run, self.user)
        assert_that(fractional_vals, is_([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0]))

    def test_GIVEN_multicell_model_run_WHEN_get_default_fractional_cover_THEN_ServiceException_raised(self):
        self.clean_database()
        self.user = self.login()
        self.create_model_run_ready_for_submit()

        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        with self.assertRaises(ServiceException):
            self.land_cover_service.get_default_fractional_cover(model_run, self.user)

    def test_GIVEN_user_uploaded_driving_data_WHEN_set_default_soil_properties_THEN_soil_cover_set(self):
        self.clean_database()
        self.user = self.login()
        self.create_model_run_with_user_uploaded_driving_data()

        model_run = self.set_model_run_latlon(self.user, 51, 0)

        self.land_cover_service.save_default_soil_properties(model_run, self.user)

        #self._land_cover_service.dap_client_factory.get_soil_properties_dap_client = _mock_get_soil_props_client
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        nvars = model_run.get_python_parameter_value(constants.JULES_PARAM_SOIL_PROPS_NVARS)
        var = model_run.get_python_parameter_value(constants.JULES_PARAM_SOIL_PROPS_VAR, is_list=True)
        use_file = model_run.get_python_parameter_value(constants.JULES_PARAM_SOIL_PROPS_USE_FILE, is_list=True)
        const_val = model_run.get_python_parameter_value(constants.JULES_PARAM_SOIL_PROPS_CONST_VAL, is_list=True)

        assert_that(nvars, is_(9))
        assert_that(var, is_(['b', 'sathh', 'satcon', 'sm_sat', 'sm_crit', 'sm_wilt', 'hcap', 'hcon', 'albsoil']))
        assert_that(use_file, is_(9 * [False]))
        assert_that(const_val, is_([0.9, 0.0, 0.0, 50.0, 275.0, 278.0, 10.0, 0.0, 0.5]))

    def test_GIVEN_no_appropriate_driving_data_WHEN_set_default_soil_properties_THEN_values_not_set(self):
        self.clean_database()
        self.user = self.login()
        self.create_model_run_with_user_uploaded_driving_data()

        model_run = self.set_model_run_latlon(self.user, 90, 0)

        self.land_cover_service.save_default_soil_properties(model_run, self.user)

        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        nvars = model_run.get_python_parameter_value(constants.JULES_PARAM_SOIL_PROPS_NVARS)
        var = model_run.get_python_parameter_value(constants.JULES_PARAM_SOIL_PROPS_VAR)
        use_file = model_run.get_python_parameter_value(constants.JULES_PARAM_SOIL_PROPS_USE_FILE, is_list=True)
        const_val = model_run.get_python_parameter_value(constants.JULES_PARAM_SOIL_PROPS_CONST_VAL, is_list=True)

        assert_that(nvars, is_(None))
        assert_that(var, is_(None))
        assert_that(use_file, is_(None))
        assert_that(const_val, is_(None))

    def set_model_run_latlon(self, user, lat, lon):
        params_to_save = [[constants.JULES_PARAM_POINTS_FILE, [lat, lon]]]
        params_to_del = constants.JULES_PARAM_POINTS_FILE
        self.parameter_service.save_new_parameters(params_to_save, params_to_del, user.id)

        return self.model_run_service.get_model_being_created_with_non_default_parameter_values(user)
    def add_summary_fields_to_context(self, model_run, context, user):
        """
        Add summary information to a template context, containing basic information
        for each of the pages in the workflow.
        :param model_run: Model run being created
        :param context: Context to summary fields to
        :param user: user
        :return:
        """
        # Create page
        context.model_run = model_run
        context.science_config = self._model_run_service.get_science_configuration_by_id(
            model_run.science_configuration_id)

        # Driving data page
        driving_data = model_run.driving_dataset
        context.driving_data_name = model_run.driving_dataset.name

        # Extents page
        extents_controller_helper = ExtentsControllerHelper()
        context.extents_values = extents_controller_helper.create_values_dict_from_database(model_run, driving_data)

        # Land cover page
        land_cover_service = LandCoverService()
        context.land_cover_actions = land_cover_service.get_land_cover_actions_for_model(model_run)
        land_cover_helper = LandCoverControllerHelper()
        try:
            land_cover_helper.add_fractional_land_cover_to_context(context, {}, model_run, user)
        except ServiceException:
            pass

        # Outputs page
        output_variables = self._model_run_service.get_output_variables()
        output_variable_dict = dict((x.name, x.description) for x in output_variables)
        selected_vars = model_run.get_parameter_values(constants.JULES_PARAM_OUTPUT_VAR)
        selected_output_periods = model_run.get_parameter_values(constants.JULES_PARAM_OUTPUT_PERIOD)
        outputs = {}
        # Each group contains one output variable and one output period
        for selected_var in selected_vars:
            var_name = selected_var.get_value_as_python()
            if var_name not in outputs:
                outputs[var_name] = []
            for output_period in selected_output_periods:
                if output_period.group_id == selected_var.group_id:
                    period = output_period.get_value_as_python()
                    if period == constants.JULES_YEARLY_PERIOD:
                        outputs[var_name].append('Yearly')
                    elif period == constants.JULES_MONTHLY_PERIOD:
                        outputs[var_name].append('Monthly')
                    elif period == constants.JULES_DAILY_PERIOD:
                        outputs[var_name].append('Daily')
                    else:
                        outputs[var_name].append('Hourly')
        context.outputs = []
        for output in outputs:
            context.outputs.append(output_variable_dict[output] + ' - ' + ', '.join(map(str, outputs[output])) + '')
        context.outputs.sort()

        if model_run.status.allow_visualise():
            # Downloads
            context.output_variable_dict = output_variable_dict
            context.output_variable_id_dict = dict((x.name, x.id) for x in output_variables)
            context.downloads = outputs
            context.download_formats = ["NetCDF"]
            if context.extents_values['site'] == 'single':
                context.download_formats.append('ASCII')
 def setUp(self):
     self.model_run_service = ModelRunService()
     self.land_cover_service = LandCoverService()
class TestModelRunLandCoverSingleCell(TestController):
    def setUp(self):
        self.model_run_service = ModelRunService()
        self.land_cover_service = LandCoverService()

    def set_up_single_cell_model_run(self):
        self.clean_database()
        self.user = self.login()
        self.create_two_driving_datasets()
        user_upload_id = DatasetService().get_id_for_user_upload_driving_dataset()
        with session_scope(Session) as session:
            self.model_run = ModelRun()
            self.model_run.name = "MR1"
            self.model_run.status = self._status(MODEL_RUN_STATUS_CREATED)
            self.model_run.driving_dataset_id = user_upload_id
            self.model_run.user = self.user
            self.model_run.driving_data_lat = 51.75
            self.model_run.driving_data_lon = -0.25
            self.model_run.driving_data_rows = 248

            param1 = self.model_run_service.get_parameter_by_constant(JULES_PARAM_DRIVE_INTERP)
            pv1 = ParameterValue()
            pv1.parameter_id = param1.id
            pv1.set_value_from_python(8 * ['nf'])

            param2 = self.model_run_service.get_parameter_by_constant(JULES_PARAM_DRIVE_DATA_PERIOD)
            pv2 = ParameterValue()
            pv2.parameter_id = param2.id
            pv2.set_value_from_python(60 * 60)

            param3 = self.model_run_service.get_parameter_by_constant(JULES_PARAM_DRIVE_DATA_START)
            pv3 = ParameterValue()
            pv3.parameter_id = param3.id
            pv3.value = "'1901-01-01 00:00:00'"

            param4 = self.model_run_service.get_parameter_by_constant(JULES_PARAM_DRIVE_DATA_END)
            pv4 = ParameterValue()
            pv4.parameter_id = param4.id
            pv4.value = "'1901-01-31 21:00:00'"

            param5 = self.model_run_service.get_parameter_by_constant(JULES_PARAM_LATLON_REGION)
            pv5 = ParameterValue()
            pv5.parameter_id = param5.id
            pv5.value = ".false."

            param6 = self.model_run_service.get_parameter_by_constant(JULES_PARAM_POINTS_FILE)
            pv6 = ParameterValue()
            pv6.parameter_id = param6.id
            pv6.value = "51.75 -0.25"

            self.model_run.parameter_values = [pv1, pv2, pv3, pv4, pv5, pv6]
            session.add(self.model_run)

    def test_GIVEN_single_cell_run_WHEN_page_get_THEN_fractional_cover_page_shown(self):
        self.set_up_single_cell_model_run()
        response = self.app.get(url(controller='model_run', action='land_cover'))
        assert_that(response.normal_body, contains_string("Fractional Land Cover"))

    def test_GIVEN_land_cover_values_WHEN_page_get_THEN_land_cover_type_names_rendered(self):
        self.set_up_single_cell_model_run()
        response = self.app.get(url(controller='model_run', action='land_cover'))
        lc_vals = self.land_cover_service.get_land_cover_values(None)
        del lc_vals[-1]  # Remove the ice
        names = [str(val.name) for val in lc_vals]
        assert_that(response.normal_body, string_contains_in_order(*names))

    def test_GIVEN_available_driving_datasets_WHEN_page_get_THEN_default_fractional_cover_rendered(self):
        self.set_up_single_cell_model_run()
        response = self.app.get(url(controller='model_run', action='land_cover'))
        lc_vals = self.land_cover_service.get_land_cover_values(None)
        del lc_vals[-1]  # Remove the ice
        string_names_values = []
        for val in lc_vals:
            string_names_values.append(str(val.name))
            string_names_values.append('12.5')
        assert_that(response.normal_body, string_contains_in_order(*string_names_values))

    def test_GIVEN_saved_land_cover_frac_WHEN_page_get_THEN_saved_values_rendered(self):
        self.set_up_single_cell_model_run()
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        frac_string = "0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.72 0.0"
        self.land_cover_service.save_fractional_land_cover_for_model(model_run, frac_string)

        response = self.app.get(url(controller='model_run', action='land_cover'))

        lc_vals = self.land_cover_service.get_land_cover_values(None)
        del lc_vals[-1]  # Remove the ice

        string_names_values = []
        for i in range(len(lc_vals)):
            string_names_values.append(str(lc_vals[i].name))
            string_names_values.append(str(100 * float(frac_string.split()[i])))
        assert_that(response.normal_body, string_contains_in_order(*string_names_values))

    def test_GIVEN_saved_land_cover_with_too_few_types_WHEN_page_get_THEN_available_values_rendered(self):
        self.set_up_single_cell_model_run()
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        frac_string = "0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.72"
        self.land_cover_service.save_fractional_land_cover_for_model(model_run, frac_string)

        response = self.app.get(url(controller='model_run', action='land_cover'))

        lc_vals = self.land_cover_service.get_land_cover_values(None)
        del lc_vals[-1]  # Remove the ice

        string_names_values = []
        for i in range(len(lc_vals)):
            string_names_values.append(str(lc_vals[i].name))
            string_names_values.append(str(100 * float(frac_string.split()[i])))
        assert_that(response.normal_body, string_contains_in_order(*string_names_values))

    def test_GIVEN_saved_land_cover_with_too_many_types_WHEN_page_get_THEN_some_values_rendered(self):
        self.set_up_single_cell_model_run()
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        frac_string = "0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.72 0.0 0.0"
        self.land_cover_service.save_fractional_land_cover_for_model(model_run, frac_string)

        response = self.app.get(url(controller='model_run', action='land_cover'))

        lc_vals = self.land_cover_service.get_land_cover_values(None)
        del lc_vals[-1]  # Remove the ice

        string_names_values = []
        for i in range(len(lc_vals)):
            string_names_values.append(str(lc_vals[i].name))
            string_names_values.append(str(100 * float(frac_string.split()[i])))
        assert_that(response.normal_body, string_contains_in_order(*string_names_values))

    def test_GIVEN_valid_fractional_cover_WHEN_post_THEN_values_saved(self):
        self.set_up_single_cell_model_run()
        self.app.post(
            url(controller='model_run', action='land_cover'),
            params={'submit': u'Next',
                    'fractional_cover': u'1',
                    'land_cover_value_1': u'20',
                    'land_cover_value_2': u'25',
                    'land_cover_value_3': u'5',
                    'land_cover_value_4': u'10',
                    'land_cover_value_5': u'10',
                    'land_cover_value_6': u'5',
                    'land_cover_value_7': u'10',
                    'land_cover_value_8': u'15'})
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        assert_that(model_run.land_cover_frac, is_("0.2\t0.25\t0.05\t0.1\t0.1\t0.05\t0.1\t0.15\t0"))

    def test_GIVEN_valid_fractional_cover_WHEN_post_THEN_default_soil_props_saved(self):
        self.set_up_single_cell_model_run()
        self.app.post(
            url(controller='model_run', action='land_cover'),
            params={'submit': u'Next',
                    'fractional_cover': u'1',
                    'land_cover_value_1': u'20',
                    'land_cover_value_2': u'25',
                    'land_cover_value_3': u'5',
                    'land_cover_value_4': u'10',
                    'land_cover_value_5': u'10',
                    'land_cover_value_6': u'5',
                    'land_cover_value_7': u'10',
                    'land_cover_value_8': u'15'})
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        soil_props = model_run.get_python_parameter_value(JULES_PARAM_SOIL_PROPS_CONST_VAL, is_list=True)
        assert_that(soil_props, is_([0.9, 0.0, 0.0, 50.0, 275.0, 300.0, 10.0, 0.0, 0.5]))

    def test_GIVEN_ice_fractional_cover_WHEN_post_THEN_values_saved(self):
        self.set_up_single_cell_model_run()
        self.app.post(
            url(controller='model_run', action='land_cover'),
            params={'submit': u'Next',
                    'fractional_cover': u'1',
                    'land_cover_ice': u'1'})
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        assert_that(model_run.land_cover_frac, is_("0\t0\t0\t0\t0\t0\t0\t0\t1"))

    def test_GIVEN_valid_fractional_cover_WHEN_post_THEN_moved_to_next_page(self):
        self.set_up_single_cell_model_run()
        response = self.app.post(
            url(controller='model_run', action='land_cover'),
            params={'submit': u'Next',
                    'fractional_cover': u'1',
                    'land_cover_value_1': u'20',
                    'land_cover_value_2': u'25',
                    'land_cover_value_3': u'5',
                    'land_cover_value_4': u'10',
                    'land_cover_value_5': u'10',
                    'land_cover_value_6': u'5',
                    'land_cover_value_7': u'10',
                    'land_cover_value_8': u'15'})
        assert_that(response.status_code, is_(302), "Response is redirect")
        assert_that(urlparse(response.response.location).path,
                    is_(url(controller='model_run', action='output')), "url")

    def test_GIVEN_fractional_cover_saved_WHEN_reset_THEN_cover_reset(self):
        self.set_up_single_cell_model_run()
        self.app.post(
            url(controller='model_run', action='land_cover'),
            params={'submit': u'Next',
                    'fractional_cover': u'1',
                    'land_cover_value_1': u'20',
                    'land_cover_value_2': u'25',
                    'land_cover_value_3': u'5',
                    'land_cover_value_4': u'10',
                    'land_cover_value_5': u'10',
                    'land_cover_value_6': u'5',
                    'land_cover_value_7': u'10',
                    'land_cover_value_8': u'15'})
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        initial_cover = model_run.land_cover_frac
        assert initial_cover is not None
        response = self.app.post(
            url(controller='model_run', action='land_cover'),
            params={'reset_fractional_cover': u'1'})
        assert_that(response.status_code, is_(302), "Response is redirect")
        assert_that(urlparse(response.response.location).path,
                    is_(url(controller='model_run', action='land_cover')), "url")
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        final_cover = model_run.land_cover_frac
        assert_that(final_cover, is_(None))

    def test_GIVEN_values_dont_add_up_WHEN_post_THEN_errors_returned_and_values_not_saved(self):
        self.set_up_single_cell_model_run()
        response = self.app.post(
            url(controller='model_run', action='land_cover'),
            params={'submit': u'Next',
                    'fractional_cover': u'1',
                    'land_cover_value_1': u'40',
                    'land_cover_value_2': u'25',
                    'land_cover_value_3': u'5',
                    'land_cover_value_4': u'10',
                    'land_cover_value_5': u'10',
                    'land_cover_value_6': u'5',
                    'land_cover_value_7': u'10',
                    'land_cover_value_8': u'15'})
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        assert_that(model_run.land_cover_frac, is_(None))
        assert_that(response.normal_body, contains_string("The sum of all the land cover fractions must be 100%"))
        assert_that(response.normal_body, contains_string("Fractional Land Cover"))

    def test_GIVEN_values_dont_add_up_WHEN_post_THEN_values_still_present_on_page(self):
        self.set_up_single_cell_model_run()
        response = self.app.post(
            url(controller='model_run', action='land_cover'),
            params={'submit': u'Next',
                    'fractional_cover': u'1',
                    'land_cover_value_1': u'40',
                    'land_cover_value_2': u'25',
                    'land_cover_value_3': u'5',
                    'land_cover_value_4': u'10',
                    'land_cover_value_5': u'10',
                    'land_cover_value_6': u'5',
                    'land_cover_value_7': u'10',
                    'land_cover_value_8': u'15'})

        lc_vals = self.land_cover_service.get_land_cover_values(None)
        del lc_vals[-1]  # Remove the ice
        frac_vals = ['40', '25', '5', '10', '10', '5', '10', '15']
        string_names_values = []
        for i in range(len(lc_vals)):
            string_names_values.append(str(lc_vals[i].name))
            string_names_values.append(str(frac_vals[i]))
        assert_that(response.normal_body, string_contains_in_order(*string_names_values))

    def test_GIVEN_no_extents_selected_WHEN_page_get_THEN_redirect(self):
        self.set_up_single_cell_model_run()
        param = self.model_run_service.get_parameter_by_constant(JULES_PARAM_POINTS_FILE)
        model_run = self.model_run_service.get_model_being_created_with_non_default_parameter_values(self.user)
        with session_scope() as session:
            session.query(ParameterValue) \
                .filter(ParameterValue.parameter_id == param.id) \
                .filter(ParameterValue.model_run_id == model_run.id) \
                .delete()

        response = self.app.get(url(controller='model_run', action='land_cover'))
        assert_that(response.status_code, is_(302), "Response is redirect")
        assert_that(urlparse(response.response.location).path,
                    is_(url(controller='model_run', action='extents')), "url")
class TestLandCoverServiceForCreateAndModifyCategoriesAndRegions(TestController):
    def setUp(self):
        dap_client_factory =Mock(DapClientFactory)
        self.land_cover_service = LandCoverService(dap_client_factory=dap_client_factory)
        self.model_run_service = ModelRunService()
        self.dataset_service = DatasetService()
        self.clean_database()
        self.user = self.login()


    def create_values(self, expected_categories, expected_names, expected_paths, expected_ids):
        values = []
        for name, category, path, id in zip(expected_names, expected_categories, expected_paths, expected_ids):
            values.append({
                'id': id,
                'name': name,
                'category': category,
                'path': path})
        return values

    def assert_regions_are_correct(self, driving_dataset, expected_categories, expected_names, expected_paths, values):
        with session_scope() as session:
            regions = LandCoverService().get_land_cover_regions(driving_dataset.id)
        assert_that(len(regions), is_(len(values)), "number of regions")
        for name, category, path, region in zip(expected_names, expected_categories, expected_paths, regions):
            assert_that(region.name, is_(name), "name")
            assert_that(region.category.name, is_(category), "category")
            assert_that(region.mask_file, is_(path), "path")
        return regions

    def test_GIVEN_no_values_WHEN_add_to_new_dataset_THEN_nothing_changes(self):
        with session_scope() as session:
            driving_dataset = self.create_driving_dataset(session)

            #WHEN
            self.land_cover_service.update_regions_and_categories_in_session(session, driving_dataset, [])

        with session_scope() as session:
            regions = LandCoverService().get_land_cover_regions(driving_dataset.id)
        assert_that(len(regions), is_(0), "number of regions")

    def test_GIVEN_one_values_WHEN_add_to_new_dataset_THEN_value_is_added(self):
        expected_names = ["name"]
        expected_categories = ["category"]
        expected_paths = ["expected_paths"]
        expected_ids = [""]
        values = self.create_values(expected_categories, expected_names, expected_paths, expected_ids)

        with session_scope() as session:
            driving_dataset = self.create_driving_dataset(session)

            #WHEN
            self.land_cover_service.update_regions_and_categories_in_session(session, driving_dataset, values)

        self.assert_regions_are_correct(driving_dataset, expected_categories, expected_names, expected_paths, values)

    def test_GIVEN_two_values_with_different_categories_WHEN_add_to_new_dataset_THEN_values_are_added(self):
        expected_names = ["name", "name2"]
        expected_categories = ["category", "category2"]
        expected_paths = ["expected_paths", "expected_path2"]
        expected_ids = [""]
        values = self.create_values(expected_categories, expected_names, expected_paths, expected_ids)

        with session_scope() as session:
            driving_dataset = self.create_driving_dataset(session)

            #WHEN
            self.land_cover_service.update_regions_and_categories_in_session(session, driving_dataset, values)

        self.assert_regions_are_correct(driving_dataset, expected_categories, expected_names, expected_paths, values)

    def test_GIVEN_two_values_with_same_categories_WHEN_add_to_new_dataset_THEN_values_are_added_only_one_category(self):
        expected_names = ["name", "name2"]
        expected_categories = ["category", "category"]
        expected_paths = ["expected_paths", "expected_path2"]
        expected_ids = ["", ""]
        values = self.create_values(expected_categories, expected_names, expected_paths, expected_ids)

        with session_scope() as session:
            driving_dataset = self.create_driving_dataset(session)

            #WHEN
            self.land_cover_service.update_regions_and_categories_in_session(session, driving_dataset, values)

        regions = self.assert_regions_are_correct(driving_dataset, expected_categories, expected_names, expected_paths, values)
        assert_that(regions[0].category, same_instance(regions[1].category), "categories are the same")

    def test_GIVEN_one_value_WHEN_add_to_existing_empty_dataset_THEN_value_is_added(self):
        expected_names = ["name"]
        expected_categories = ["category"]
        expected_paths = ["expected_paths"]
        expected_ids = [""]
        values = self.create_values(expected_categories, expected_names, expected_paths, expected_ids)

        with session_scope() as session:
            driving_dataset = self.create_driving_dataset(session)

        with session_scope() as session:
            driving_dataset = session.query(DrivingDataset).get(driving_dataset.id)
            self.land_cover_service.update_regions_and_categories_in_session(session, driving_dataset, values)

        self.assert_regions_are_correct(driving_dataset, expected_categories, expected_names, expected_paths, values)

    def test_GIVEN_one_value_WHEN_add_to_existing_dataset_with_mask_in_already_THEN_value_is_modified(self):

        with session_scope() as session:
            driving_dataset = DrivingDataset()
            original = self.create_values(["orig"], ["category"], ["orig"], [""])
            self.land_cover_service.update_regions_and_categories_in_session(session, driving_dataset, original)
            session.add(driving_dataset)
        regions = self.land_cover_service.get_land_cover_regions(driving_dataset.id)
        expected_ids = [regions[0].id]
        expected_names = ["name"]
        expected_categories = ["category"]
        expected_paths = ["expected_paths"]
        values = self.create_values(expected_categories, expected_names, expected_paths, expected_ids)

        with session_scope() as session:
            driving_dataset = session.query(DrivingDataset).get(driving_dataset.id)
            self.land_cover_service.update_regions_and_categories_in_session(session, driving_dataset, values)

        self.assert_regions_are_correct(driving_dataset, expected_categories, expected_names, expected_paths, values)

    def test_GIVEN_one_value_WHEN_add_to_existing_dataset_with_mask_in_already_in_different_category_THEN_value_is_modified_and_only_one_category(self):

        with session_scope() as session:
            driving_dataset = DrivingDataset()
            original = self.create_values(["orig"], ["orig"], ["orig"], [""])
            self.land_cover_service.update_regions_and_categories_in_session(session, driving_dataset, original)
            session.add(driving_dataset)
        regions = self.land_cover_service.get_land_cover_regions(driving_dataset.id)
        expected_ids = [regions[0].id]
        expected_names = ["name"]
        expected_categories = ["category"]
        expected_paths = ["expected_paths"]
        values = self.create_values(expected_categories, expected_names, expected_paths, expected_ids)

        with session_scope() as session:
            driving_dataset = session.query(DrivingDataset).get(driving_dataset.id)
            self.land_cover_service.update_regions_and_categories_in_session(session, driving_dataset, values)

        self.assert_regions_are_correct(driving_dataset, expected_categories, expected_names, expected_paths, values)

        categories = self.land_cover_service.get_land_cover_categories(driving_dataset.id)
        assert_that(len(categories), is_(1), "number of categories")