Example #1
0
    def get(self, exploration_id, asset_type, encoded_filename):
        """Returns an asset file.

        Args:
            exploration_id: str. The id of the exploration.
            asset_type: str. Type of the asset, either image or audio.
            encoded_filename: str. The asset filename. This
              string is encoded in the frontend using encodeURIComponent().
        """
        if not constants.DEV_MODE:
            raise self.PageNotFoundException
        if asset_type not in self._SUPPORTED_TYPES:
            raise Exception('%s is not a supported asset type.' % asset_type)
        try:
            filename = urllib.unquote(encoded_filename)
            file_format = filename[(filename.rfind('.') + 1):]

            # If the following is not cast to str, an error occurs in the wsgi
            # library because unicode gets used.
            self.response.headers['Content-Type'] = str(
                '%s/%s' % (asset_type, file_format))

            fs = fs_domain.AbstractFileSystem(
                fs_domain.ExplorationFileSystem('exploration/%s' %
                                                exploration_id))
            raw = fs.get('%s/%s' % (asset_type, filename))

            self.response.cache_control.no_cache = None
            self.response.cache_control.public = True
            self.response.cache_control.max_age = 600
            self.response.write(raw)
        except:
            raise self.PageNotFoundException
Example #2
0
    def get(self, exploration_id):
        """Handles GET requests."""
        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem(exploration_id))
        dir_list = fs.listdir('')

        self.render_json({'filepaths': dir_list})
Example #3
0
def export_to_zip_file(exploration_id, version=None):
    """Returns a ZIP archive of the exploration."""
    exploration = get_exploration_by_id(exploration_id, version=version)
    yaml_repr = exploration.to_yaml()

    o = StringIO.StringIO()
    with zipfile.ZipFile(o, mode='w', compression=zipfile.ZIP_DEFLATED) as zf:
        zf.writestr('%s.yaml' % exploration.title, yaml_repr)

        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem(exploration_id))
        dir_list = fs.listdir('')
        for filepath in dir_list:
            # Currently, the version number of all files is 1, since they are
            # not modifiable post-upload.
            # TODO(sll): When allowing editing of files, implement versioning
            # for them.
            file_contents = fs.get(filepath, version=1)

            str_filepath = 'assets/%s' % filepath
            assert isinstance(str_filepath, str)
            unicode_filepath = str_filepath.decode('utf-8')
            zf.writestr(unicode_filepath, file_contents)

    return o.getvalue()
Example #4
0
    def get(self, exploration_id, filename):
        """Returns an audio file.

        Args:
            encoded_filepath: a string representing the audio filepath. This
              string is encoded in the frontend using encodeURIComponent().
        """
        file_format = filename[(filename.rfind('.') + 1):]
        # If the following is not cast to str, an error occurs in the wsgi
        # library because unicode gets used.
        self.response.headers['Content-Type'] = str(
            'audio/%s' % file_format)

        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem(exploration_id))

        try:
            raw = fs.get('%s/%s' % (self._AUDIO_PATH_PREFIX, filename))
        except:
            raise self.PageNotFoundException

        self.response.cache_control.no_cache = None
        self.response.cache_control.public = True
        self.response.cache_control.max_age = 600
        self.response.write(raw)
Example #5
0
    def get(self, exploration_id, encoded_filepath):
        """Returns an image.

        Args:
            exploration_id: the id of the exploration.
            encoded_filepath: a string representing the image filepath. This
              string is encoded in the frontend using encodeURIComponent().
        """
        try:
            filepath = urllib.unquote(encoded_filepath)
            file_format = filepath[(filepath.rfind('.') + 1):]
            # If the following is not cast to str, an error occurs in the wsgi
            # library because unicode gets used.
            self.response.headers['Content-Type'] = str(
                'image/%s' % file_format)

            fs = fs_domain.AbstractFileSystem(
                fs_domain.ExplorationFileSystem(exploration_id))
            raw = fs.get(filepath)

            self.response.cache_control.no_cache = None
            self.response.cache_control.public = True
            self.response.cache_control.max_age = 600
            self.response.write(raw)
        except:
            raise self.PageNotFoundException
Example #6
0
    def test_independence_of_file_systems(self):
        self.fs.commit(self.user_id, 'abc.png', 'file_contents')
        self.assertEqual(self.fs.get('abc.png'), 'file_contents')

        fs2 = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem('eid2'))
        with self.assertRaisesRegexp(IOError, r'File abc\.png .* not found'):
            fs2.get('abc.png')
Example #7
0
def classify(exp_id, state, answer, params):
    """Normalize the answer and select among the answer groups the group in
    which the answer best belongs. The best group is decided by finding the
    first rule best satisfied by the answer. Returns a dict with the following
    keys:
        'outcome': A dict representing the outcome of the answer group matched.
        'answer_group_index': An index into the answer groups list indicating
            which one was selected as the group which this answer belongs to.
            This is equal to the number of answer groups if the default outcome
            was matched.
        'classification_certainty': A normalized value within the range of
            [0, 1] representing at which confidence level the answer belongs in
            the chosen answer group. A certainty of 1 means it is the best
            possible match. A certainty of 0 means it is matched to the default
            outcome.
        'rule_spec_index': An index into the rule specs list of the matched
            answer group which was selected that indicates which rule spec was
            matched. This is equal to 0 if the default outcome is selected.
    When the default rule is matched, outcome is the default_outcome of the
    state's interaction.
    """
    interaction_instance = interaction_registry.Registry.get_interaction_by_id(
        state.interaction.id)
    normalized_answer = interaction_instance.normalize_answer(answer)

    fs = fs_domain.AbstractFileSystem(fs_domain.ExplorationFileSystem(exp_id))
    input_type = interaction_instance.answer_type

    response = classify_hard_rule(state, params, input_type, normalized_answer,
                                  fs)
    if response is None:
        response = classify_soft_rule(state, params, input_type,
                                      normalized_answer, fs)
    if (interaction_instance.is_string_classifier_trainable
            and feconf.ENABLE_STRING_CLASSIFIER and response is None):
        response = classify_string_classifier_rule(state, normalized_answer)

    # The best matched group must match above a certain threshold. If no group
    # meets this requirement, then the default 'group' automatically matches
    # resulting in the outcome of the answer being the default outcome of the
    # state.
    if (response is not None and response['classification_certainty'] >=
            feconf.DEFAULT_ANSWER_GROUP_CLASSIFICATION_THRESHOLD):
        return response
    elif state.interaction.default_outcome is not None:
        return {
            'outcome': state.interaction.default_outcome.to_dict(),
            'answer_group_index': len(state.interaction.answer_groups),
            'classification_certainty': 0.0,
            'rule_spec_index': 0
        }

    raise Exception(
        'Something has seriously gone wrong with the exploration. Oppia does '
        'not know what to do with this answer. Please contact the '
        'exploration owner.')
    def test_listdir(self):
        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem('exploration/eid'))
        fs.commit(self.user_id, 'abc.png', 'file_contents')
        fs.commit(self.user_id, 'abcd.png', 'file_contents_2')
        fs.commit(self.user_id, 'abc/abcd.png', 'file_contents_3')
        fs.commit(self.user_id, 'bcd/bcde.png', 'file_contents_4')

        self.assertEqual(fs.listdir(''), [
            'abc.png', 'abc/abcd.png', 'abcd.png', 'bcd/bcde.png'])

        self.assertEqual(fs.listdir('abc'), ['abc/abcd.png'])

        with self.assertRaisesRegexp(IOError, 'Invalid filepath'):
            fs.listdir('/abc')

        self.assertEqual(fs.listdir('fake_dir'), [])

        new_fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem('exploration/eid2'))
        self.assertEqual(new_fs.listdir('assets'), [])
Example #9
0
    def test_delete(self):
        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem('eid'))
        self.assertFalse(fs.isfile('abc.png'))
        fs.commit(self.user_id, 'abc.png', 'file_contents')
        self.assertTrue(fs.isfile('abc.png'))

        fs.delete(self.user_id, 'abc.png')
        self.assertFalse(fs.isfile('abc.png'))
        with self.assertRaisesRegexp(IOError, r'File abc\.png .* not found'):
            fs.get('abc.png')

        # Nothing happens when one tries to delete a file that does not exist.
        fs.delete(self.user_id, 'fake_file.png')
Example #10
0
    def post(self, exploration_id):
        """Saves an image uploaded by a content creator."""

        raw = self.request.get('image')
        filename = self.payload.get('filename')
        if not raw:
            raise self.InvalidInputException('No image supplied')

        allowed_formats = ', '.join(
            feconf.ACCEPTED_IMAGE_FORMATS_AND_EXTENSIONS.keys())

        # Verify that the data is recognized as an image.
        file_format = imghdr.what(None, h=raw)
        if file_format not in feconf.ACCEPTED_IMAGE_FORMATS_AND_EXTENSIONS:
            raise self.InvalidInputException('Image not recognized')

        # Verify that the file type matches the supplied extension.
        if not filename:
            raise self.InvalidInputException('No filename supplied')
        if '/' in filename or '..' in filename:
            raise self.InvalidInputException(
                'Filenames should not include slashes (/) or consecutive dot '
                'characters.')
        if '.' not in filename:
            raise self.InvalidInputException(
                'Image filename with no extension: it should have '
                'one of the following extensions: %s.' % allowed_formats)

        dot_index = filename.rfind('.')
        extension = filename[dot_index + 1:].lower()
        if (extension not in
                feconf.ACCEPTED_IMAGE_FORMATS_AND_EXTENSIONS[file_format]):
            raise self.InvalidInputException(
                'Expected a filename ending in .%s, received %s' %
                (file_format, filename))

        # Save the file.
        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem(exploration_id))
        if fs.isfile(filename):
            raise self.InvalidInputException(
                'A file with the name %s already exists. Please choose a '
                'different name.' % filename)
        fs.commit(self.user_id, filename, raw)

        self.render_json({'filepath': filename})
Example #11
0
    def post(self, exploration_id):
        """Saves an image uploaded by a content creator."""

        raw = self.request.get('image')
        filename = self.payload.get('filename')
        if not raw:
            raise self.InvalidInputException('No image supplied')

        file_format = imghdr.what(None, h=raw)
        if file_format not in feconf.ACCEPTED_IMAGE_FORMATS_AND_EXTENSIONS:
            allowed_formats = ', '.join(
                feconf.ACCEPTED_IMAGE_FORMATS_AND_EXTENSIONS.keys())
            raise Exception('Image file not recognized: it should be in '
                            'one of the following formats: %s.' %
                            allowed_formats)

        if not filename:
            raise self.InvalidInputException('No filename supplied')
        if '/' in filename or '..' in filename:
            raise self.InvalidInputException(
                'Filenames should not include slashes (/) or consecutive dot '
                'characters.')
        if '.' in filename:
            dot_index = filename.rfind('.')
            primary_name = filename[:dot_index]
            extension = filename[dot_index + 1:]
            if (extension not in
                    feconf.ACCEPTED_IMAGE_FORMATS_AND_EXTENSIONS[file_format]):
                raise self.InvalidInputException(
                    'Expected a filename ending in .%s; received %s' %
                    (file_format, filename))
        else:
            primary_name = filename

        filepath = '%s.%s' % (primary_name, file_format)

        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem(exploration_id))
        if fs.isfile(filepath):
            raise self.InvalidInputException(
                'A file with the name %s already exists. Please choose a '
                'different name.' % filepath)
        fs.commit(self.user_id, filepath, raw)

        self.render_json({'filepath': filepath})
Example #12
0
def classify(exp_id, state, answer, params):
    """Normalize the answer and select among the answer groups the group in
    which the answer best belongs. The best group is decided by finding the
    first rule best satisfied by the answer. Returns a dict with the following
    keys:
        'outcome': A dict representing the outcome of the answer group matched.
        'rule_spec_string': A descriptive string representation of the rule
            matched.
    When the default rule is matched, outcome is the default_outcome of the
    state's interaction and the rule_spec_string is just 'Default.'
    """
    interaction_instance = interaction_registry.Registry.get_interaction_by_id(
        state.interaction.id)
    normalized_answer = interaction_instance.normalize_answer(answer)

    # Find the first group which best matches the given answer.
    for answer_group in state.interaction.answer_groups:
        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem(exp_id))
        input_type = interaction_instance.answer_type
        matched_rule_spec = None
        for rule_spec in answer_group.rule_specs:
            if rule_domain.evaluate_rule(rule_spec, input_type, params,
                                         normalized_answer, fs):
                matched_rule_spec = rule_spec
                break
        if matched_rule_spec is not None:
            return {
                'outcome': answer_group.outcome.to_dict(),
                'rule_spec_string':
                (matched_rule_spec.stringify_classified_rule())
            }

    # If no other groups match, then the default group automatically matches
    # (if there is one present).
    if state.interaction.default_outcome is not None:
        return {
            'outcome': state.interaction.default_outcome.to_dict(),
            'rule_spec_string': exp_domain.DEFAULT_RULESPEC_STR
        }

    raise Exception(
        'Something has seriously gone wrong with the exploration. '
        'Oppia does not know what to do with this answer. Please contact the '
        'exploration owner.')
Example #13
0
    def test_versioning(self):
        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem('eid'))
        fs.commit(self.user_id, 'abc.png', 'file_contents')
        self.assertEqual(fs.get('abc.png'), 'file_contents')
        file_stream = fs.open('abc.png')
        self.assertEqual(file_stream.version, 1)
        self.assertEqual(file_stream.metadata.size, len('file_contents'))

        fs.commit(self.user_id, 'abc.png', 'file_contents_2_abcdefg')
        self.assertEqual(fs.get('abc.png'), 'file_contents_2_abcdefg')
        file_stream = fs.open('abc.png')
        self.assertEqual(file_stream.version, 2)
        self.assertEqual(file_stream.metadata.size,
                         len('file_contents_2_abcdefg'))

        self.assertEqual(fs.get('abc.png', 1), 'file_contents')
        old_file_stream = fs.open('abc.png', 1)
        self.assertEqual(old_file_stream.version, 1)
        self.assertEqual(old_file_stream.metadata.size, len('file_contents'))
    def test_invalid_filepaths_are_caught(self):
        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem('exploration/eid'))

        invalid_filepaths = [
            '..', '../another_exploration', '../', '/..', '/abc']

        for filepath in invalid_filepaths:
            with self.assertRaisesRegexp(IOError, 'Invalid filepath'):
                fs.isfile(filepath)
            with self.assertRaisesRegexp(IOError, 'Invalid filepath'):
                fs.open(filepath)
            with self.assertRaisesRegexp(IOError, 'Invalid filepath'):
                fs.get(filepath)
            with self.assertRaisesRegexp(IOError, 'Invalid filepath'):
                fs.commit(self.user_id, filepath, 'raw_file')
            with self.assertRaisesRegexp(IOError, 'Invalid filepath'):
                fs.delete(self.user_id, filepath)
            with self.assertRaisesRegexp(IOError, 'Invalid filepath'):
                fs.listdir(filepath)
Example #15
0
def save_new_exploration_from_yaml_and_assets(
        committer_id, yaml_content, title, category, exploration_id,
        assets_list):
    if assets_list is None:
        assets_list = []

    exploration = exp_domain.Exploration.from_yaml(
        exploration_id, title, category, yaml_content)
    commit_message = (
        'New exploration created from YAML file with title \'%s\'.'
        % exploration.title)

    _create_exploration(committer_id, exploration, commit_message, [{
        'cmd': CMD_CREATE_NEW,
        'title': exploration.title,
        'category': exploration.category,
    }])

    for (asset_filename, asset_content) in assets_list:
        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem(exploration_id))
        fs.commit(committer_id, asset_filename, asset_content)
Example #16
0
def classify(exp_id, exp_param_specs, state, handler_name, answer, params):
    """Normalize the answer and return the first rulespec that it satisfies."""
    interaction_instance = interaction_registry.Registry.get_interaction_by_id(
        state.interaction.id)
    normalized_answer = interaction_instance.normalize_answer(
        answer, handler_name)

    handler = next(h for h in state.interaction.handlers
                   if h.name == handler_name)
    fs = fs_domain.AbstractFileSystem(fs_domain.ExplorationFileSystem(exp_id))
    input_type = interaction_instance.get_handler_by_name(
        handler_name).obj_type
    for rule_spec in handler.rule_specs:
        if rule_domain.evaluate_rule(rule_spec.definition, exp_param_specs,
                                     input_type, params, normalized_answer,
                                     fs):
            return rule_spec

    raise Exception(
        'No matching rule found for handler %s. Rule specs are %s.' %
        (handler.name,
         [rule_spec.to_dict() for rule_spec in handler.rule_specs]))
Example #17
0
def classify(exp_id, state, answer, params):
    """Normalize the answer and select among the answer groups the group in
    which the answer best belongs. The best group is decided by finding the
    first rule best satisfied by the answer. Returns a dict with the following
    keys:
        'outcome': A dict representing the outcome of the answer group matched.
        'rule_spec_string': A descriptive string representation of the rule
            matched.
        'answer_group_index': An index into the answer groups list indicating
            which one was selected as the group which this answer belongs to.
            This is equal to the number of answer groups if the default outcome
            was matched.
        'classification_certainty': A normalized value within the range of
            [0, 1] representing at which confidence level the answer belongs in
            the chosen answer group. A certainty of 1 means it is the best
            possible match. A certainty of 0 means it is matched to the default
            outcome.
    When the default rule is matched, outcome is the default_outcome of the
    state's interaction and the rule_spec_string is just 'Default.'
    """
    interaction_instance = interaction_registry.Registry.get_interaction_by_id(
        state.interaction.id)
    normalized_answer = interaction_instance.normalize_answer(answer)

    # Find the first group that satisfactorily matches the given answer. This is
    # done by ORing (maximizing) all truth values of all rules over all answer
    # groups. The group with the highest truth value is considered the best
    # match.
    best_matched_answer_group = None
    best_matched_answer_group_index = len(state.interaction.answer_groups)
    best_matched_rule_spec = None
    best_matched_truth_value = 0.0
    for (i, answer_group) in enumerate(state.interaction.answer_groups):
        fs = fs_domain.AbstractFileSystem(
            fs_domain.ExplorationFileSystem(exp_id))
        input_type = interaction_instance.answer_type
        ored_truth_value = 0.0
        best_rule_spec = None
        for rule_spec in answer_group.rule_specs:
            evaluated_truth_value = rule_domain.evaluate_rule(
                rule_spec, input_type, params, normalized_answer, fs)
            if evaluated_truth_value > ored_truth_value:
                ored_truth_value = evaluated_truth_value
                best_rule_spec = rule_spec
        if ored_truth_value > best_matched_truth_value:
            best_matched_truth_value = ored_truth_value
            best_matched_rule_spec = best_rule_spec
            best_matched_answer_group = answer_group
            best_matched_answer_group_index = i

    # The best matched group must match above a certain threshold. If no group
    # meets this requirement, then the default 'group' automatically matches
    # resulting in the outcome of the answer being the default outcome of the
    # state.
    if (best_matched_truth_value >=
            feconf.DEFAULT_ANSWER_GROUP_CLASSIFICATION_THRESHOLD):
        return {
            'outcome': best_matched_answer_group.outcome.to_dict(),
            'rule_spec_string': (
                best_matched_rule_spec.stringify_classified_rule()),
            'answer_group_index': best_matched_answer_group_index,
            'classification_certainty': best_matched_truth_value
        }
    elif state.interaction.default_outcome is not None:
        return {
            'outcome': state.interaction.default_outcome.to_dict(),
            'rule_spec_string': exp_domain.DEFAULT_RULESPEC_STR,
            'answer_group_index': len(state.interaction.answer_groups),
            'classification_certainty': 0.0
        }

    raise Exception('Something has seriously gone wrong with the exploration. '
        'Oppia does not know what to do with this answer. Please contact the '
        'exploration owner.')
Example #18
0
 def test_get_and_save(self):
     fs = fs_domain.AbstractFileSystem(
         fs_domain.ExplorationFileSystem('eid'))
     fs.commit(self.user_id, 'abc.png', 'file_contents')
     self.assertEqual(fs.get('abc.png'), 'file_contents')
Example #19
0
 def setUp(self):
     super(ExplorationFileSystemUnitTests, self).setUp()
     self.user_email = '*****@*****.**'
     self.user_id = self.get_user_id_from_email(self.user_email)
     self.fs = fs_domain.AbstractFileSystem(
         fs_domain.ExplorationFileSystem('exploration/eid'))