Example #1
0
    def test_training_job_migration_on_algorithm_version_change(self):
        self.post_blob(
            '/ml/trainedclassifierhandler',
            self.payload_proto.SerializeToString(), expected_status_int=200)

        params = {
            'exploration_id': self.exp_id,
            'exploration_version': self.exploration.version,
            'state_name': 'Home',
        }
        interaction_classifier_mapping = {
            'TextInput': {
                'algorithm_id': 'TextClassifier',
                'algorithm_version': 2
            },
        }

        with self.swap(
            feconf, 'INTERACTION_CLASSIFIER_MAPPING',
            interaction_classifier_mapping):
            self.get_json(
                '/ml/trainedclassifierhandler', params=params,
                expected_status_int=404)

        state_training_jobs_mapping = (
            classifier_services.get_state_training_jobs_mapping(
                self.exp_id, self.exploration.version, 'Home'))
        self.assertIn(
            'TextClassifier',
            state_training_jobs_mapping.algorithm_ids_to_job_ids)

        with self.swap(
            feconf, 'INTERACTION_CLASSIFIER_MAPPING',
            interaction_classifier_mapping):
            json_response = self.post_json(
                '/ml/nextjobhandler',
                self.payload_for_fetching_next_job_request,
                expected_status_int=200)

        self.assertEqual(
            state_training_jobs_mapping.algorithm_ids_to_job_ids[
                'TextClassifier'],
            json_response['job_id']
        )
        self.assertEqual(json_response['algorithm_id'], 'TextClassifier')
        self.assertEqual(json_response['algorithm_version'], 2)
Example #2
0
    def get(self):
        """Handles GET requests.

        Retrieves the name of the file on GCS storing the trained model
        parameters and transfers it to the frontend.
        """
        exploration_id = self.normalized_request.get('exploration_id')
        state_name = self.normalized_request.get('state_name')

        try:
            exp_version = int(self.normalized_request.get(
                'exploration_version'))
            exploration = exp_fetchers.get_exploration_by_id(
                exploration_id, version=exp_version)
            interaction_id = exploration.states[state_name].interaction.id
        except:
            raise self.InvalidInputException(
                'Entity for exploration with id %s, version %s and state %s '
                'not found.' % (
                    exploration_id, self.normalized_request.get(
                        'exploration_version'), state_name))

        if interaction_id not in feconf.INTERACTION_CLASSIFIER_MAPPING:
            raise self.PageNotFoundException(
                'No classifier algorithm found for %s interaction' % (
                    interaction_id))

        algorithm_id = feconf.INTERACTION_CLASSIFIER_MAPPING[
            interaction_id]['algorithm_id']
        algorithm_version = feconf.INTERACTION_CLASSIFIER_MAPPING[
            interaction_id]['algorithm_version']

        state_training_jobs_mapping = (
            classifier_services.get_state_training_jobs_mapping(
                exploration_id, exp_version, state_name))
        if state_training_jobs_mapping is None:
            raise self.InvalidInputException(
                'No training jobs exist for given exploration state')

        if not (
                algorithm_id in state_training_jobs_mapping.
                algorithm_ids_to_job_ids):
            classifier_services.migrate_state_training_jobs(
                state_training_jobs_mapping)
            # Since the required training job doesn't exist and old job has to
            # be migrated, a PageNotFound exception is raised.
            # Once jobs are migrated and trained they can be sent to the client
            # upon further requests. This exception should be gracefully
            # handled in the client code and shouldn't break UX.
            raise self.PageNotFoundException(
                'No valid classifier exists for the given exploration state')

        training_job = classifier_services.get_classifier_training_job_by_id(
            state_training_jobs_mapping.algorithm_ids_to_job_ids[algorithm_id])

        if training_job is None or (
                training_job.status != feconf.TRAINING_JOB_STATUS_COMPLETE):
            raise self.PageNotFoundException(
                'No valid classifier exists for the given exploration state')

        if training_job.algorithm_version != algorithm_version:
            classifier_services.migrate_state_training_jobs(
                state_training_jobs_mapping)
            # Since the required training job doesn't exist and old job has to
            # be migrated, a PageNotFound exception is raised.
            # Once jobs are migrated and trained they can be sent to the client
            # upon further requests. This exception should be gracefully
            # handled in the client code and shouldn't break UX.
            raise self.PageNotFoundException(
                'No valid classifier exists for the given exploration state')

        return self.render_json({
            'algorithm_id': algorithm_id,
            'algorithm_version': algorithm_version,
            'gcs_filename': training_job.classifier_data_filename
        })