def _are_nodes_valid_for_publishing(story_nodes): """Validates the story nodes before publishing. Args: story_nodes: list(dict(str, *)). The list of story nodes dicts. Raises: Exception: The story node doesn't contain any exploration id or the exploration id is invalid or isn't published yet. """ exploration_id_list = [] for node in story_nodes: if not node.exploration_id: raise Exception('Story node with id %s does not contain an ' 'exploration id.' % node.id) exploration_id_list.append(node.exploration_id) explorations = exp_fetchers.get_multiple_explorations_by_id( exploration_id_list, strict=False) for node in story_nodes: if not node.exploration_id in explorations: raise Exception('Exploration id %s doesn\'t exist.' % node.exploration_id) multiple_exploration_rights = ( rights_manager.get_multiple_exploration_rights_by_ids( exploration_id_list)) for exploration_rights in multiple_exploration_rights: if exploration_rights.is_private(): raise Exception('Exploration with id %s isn\'t published.' % exploration_rights.id)
def get_displayable_exp_summary_dicts_matching_ids(exploration_ids, user=None): """Gets a summary of explorations in human readable form from exploration ids. Given a list of exploration ids, optionally filters the list for explorations that are currently non-private and not deleted, and returns a list of dicts of the corresponding exploration summaries. This function can also filter based on a user ID who has edit access to the corresponding exploration, where the editor ID is for private explorations. Please use this function when needing summary information to display on exploration summary tiles in the frontend. Args: exploration_ids: list(str). List of exploration ids. user: UserActionsInfo or None. Object having user_id, role and actions for given user. Return: list(dict). A list of exploration summary dicts in human readable form. Example: [ { 'category': u'A category', 'community_owned': False, 'id': 'eid2', 'language_code': 'en', 'num_views': 0, 'objective': u'An objective', 'status': 'public', 'tags': [], 'thumbnail_bg_color': '#a33f40', 'thumbnail_icon_url': self.get_static_asset_url( '/images/subjects/Lightbulb.svg'), 'title': u'Exploration 2 Albert title', }, ] """ exploration_summaries = ( exp_services.get_exploration_summaries_matching_ids(exploration_ids)) exploration_rights_objects = ( rights_manager.get_multiple_exploration_rights_by_ids(exploration_ids)) filtered_exploration_summaries = [] for (exploration_summary, exploration_rights) in ( zip(exploration_summaries, exploration_rights_objects)): if exploration_summary is None: continue if exploration_rights is None: continue if exploration_summary.status == ( rights_manager.ACTIVITY_STATUS_PRIVATE): if user is None: continue if not rights_manager.check_can_edit_activity( user, exploration_rights): continue filtered_exploration_summaries.append(exploration_summary) return get_displayable_exp_summary_dicts(filtered_exploration_summaries)
def test_get_multiple_exploration_rights(self): exp_ids = ['exp1', 'exp2', 'exp3', 'exp4'] # saving only first 3 explorations to check that None is returned for # non-existing exploration. for exp_id in exp_ids[:3]: self.save_new_valid_exploration(exp_id, self.user_id_admin) exp_rights = rights_manager.get_multiple_exploration_rights_by_ids( exp_ids) self.assertEqual(len(exp_rights), 4) for rights_object in exp_rights[:3]: self.assertIsNotNone(rights_object) self.assertIsNone(exp_rights[3])
def get_exploration_metadata_dicts(exploration_ids, user): """Given a list of exploration ids, optionally filters the list for explorations that are currently non-private and not deleted, and returns a list of dicts of the corresponding exploration summaries for collection node search. Args: exploration_ids: list(str). A list of exploration ids for which exploration metadata dicts are to be returned. user: UserActionsInfo. Object having user_id, role and actions for given user. Returns: list(dict). A list of metadata dicts corresponding to the given exploration ids. Each dict has three keys: 'id': the exploration id; 'title': the exploration title; 'objective': the exploration objective. """ exploration_summaries = ( exp_services.get_exploration_summaries_matching_ids(exploration_ids)) exploration_rights_objects = ( rights_manager.get_multiple_exploration_rights_by_ids(exploration_ids)) filtered_exploration_summaries = [] for (exploration_summary, exploration_rights) in (zip(exploration_summaries, exploration_rights_objects)): if exploration_summary is None: continue if exploration_rights is None: continue if exploration_summary.status == ( rights_manager.ACTIVITY_STATUS_PRIVATE): if user.user_id is None: continue if not rights_manager.check_can_edit_activity( user, exploration_rights): continue filtered_exploration_summaries.append(exploration_summary) return [ summary.to_metadata_dict() for summary in filtered_exploration_summaries ]
def _are_nodes_valid_for_publishing(story_nodes): exploration_id_list = [] for node in story_nodes: if not node.exploration_id: raise Exception('Story node with id %s does not contain an ' 'exploration id.' % node.id) exploration_id_list.append(node.exploration_id) for exploration in exp_services.get_multiple_explorations_by_id( exploration_id_list): if exploration is None: raise Exception('Exploration id %s doesn\'t exist.' % exploration.id) multiple_exploration_rights = ( rights_manager.get_multiple_exploration_rights_by_ids( exploration_id_list)) for exploration_rights in multiple_exploration_rights: if exploration_rights.is_private(): raise Exception('Exploration with id %s isn\'t published.' % exploration_rights.id)
def validate_explorations_for_story(exp_ids, strict): """Validates the explorations in the given story and checks whether they are compatible with the mobile app and ready for publishing. Args: exp_ids: list(str). The exp IDs to validate. strict: bool. Whether to raise an Exception when a validation error is encountered. If not, a list of the error messages are returned. strict should be True when this is called before saving the story and False when this function is called from the frontend. Returns: list(str). The various validation error messages (if strict is False). Raises: ValidationError. Expected story to only reference valid explorations. ValidationError. Exploration with ID is not public. Please publish explorations before adding them to a story. ValidationError. All explorations in a story should be of the same category. """ validation_error_messages = [] # Strict = False, since the existence of explorations is checked below. exps_dict = ( exp_fetchers.get_multiple_explorations_by_id(exp_ids, strict=False)) exp_rights = ( rights_manager.get_multiple_exploration_rights_by_ids(exp_ids)) exp_rights_dict = {} for rights in exp_rights: if rights is not None: exp_rights_dict[rights.id] = rights.status for exp_id in exp_ids: if exp_id not in exps_dict: error_string = ( 'Expected story to only reference valid explorations, but found' ' a reference to an invalid exploration with ID: %s' % exp_id) if strict: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) else: if exp_rights_dict[exp_id] != constants.ACTIVITY_STATUS_PUBLIC: error_string = ( 'Exploration with ID %s is not public. Please publish ' 'explorations before adding them to a story.' % exp_id) if strict: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) if exps_dict: for exp_id in exp_ids: if exp_id in exps_dict: sample_exp_id = exp_id break common_exp_category = exps_dict[sample_exp_id].category for exp_id in exps_dict: exp = exps_dict[exp_id] if exp.category != common_exp_category: error_string = ( 'All explorations in a story should be of the ' 'same category. The explorations with ID %s and %s have' ' different categories.' % (sample_exp_id, exp_id)) if strict: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) try: validation_error_messages.extend( exp_services.validate_exploration_for_story(exp, strict)) except Exception as e: logging.exception( 'Exploration validation failed for exploration with ID: ' '%s. Error: %s' % (exp_id, e)) raise Exception(e) return validation_error_messages
def validate_explorations_for_story(exp_ids, raise_error): """Validates the explorations in the given story and checks whether they are compatible with the mobile app. Args: exp_ids: list(str). The exp IDs to validate. raise_error: bool. Whether to raise an Exception when a validation error is encountered. If not, a list of the error messages are returned. raise_error should be True when this is called before saving the story and False when this function is called from the frontend. Returns: list(str). The various validation error messages (if raise_error is False). Raises: ValidationError. Expected story to only reference valid explorations. ValidationError. Exploration with ID is not public. Please publish explorations before adding them to a story. ValidationError. All explorations in a story should be of the same category. ValidationError. Invalid language found for exploration. ValidationError. Expected no exploration to have parameter values in it. ValidationError. Invalid interaction in exploration. ValidationError. RTE content in state of exploration with ID is not supported on mobile. """ validation_error_messages = [] # Strict = False, since the existence of explorations is checked below. exps_dict = ( exp_fetchers.get_multiple_explorations_by_id(exp_ids, strict=False)) exp_rights = ( rights_manager.get_multiple_exploration_rights_by_ids(exp_ids)) exp_rights_dict = {} for rights in exp_rights: if rights is not None: exp_rights_dict[rights.id] = rights.status for exp_id in exp_ids: if exp_id not in exps_dict: error_string = ( 'Expected story to only reference valid explorations, but found' ' a reference to an invalid exploration with ID: %s' % exp_id) if raise_error: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) else: if exp_rights_dict[exp_id] != constants.ACTIVITY_STATUS_PUBLIC: error_string = ( 'Exploration with ID %s is not public. Please publish ' 'explorations before adding them to a story.' % exp_id) if raise_error: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) if exps_dict: for exp_id in exp_ids: if exp_id in exps_dict: sample_exp_id = exp_id break common_exp_category = exps_dict[sample_exp_id].category for exp_id in exps_dict: exp = exps_dict[exp_id] if exp.category != common_exp_category: error_string = ( 'All explorations in a story should be of the ' 'same category. The explorations with ID %s and %s have' ' different categories.' % (sample_exp_id, exp_id)) if raise_error: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) if ( exp.language_code not in android_validation_constants.SUPPORTED_LANGUAGES): error_string = ( 'Invalid language %s found for exploration ' 'with ID %s.' % (exp.language_code, exp_id)) if raise_error: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) if exp.param_specs or exp.param_changes: error_string = ( 'Expected no exploration to have parameter ' 'values in it. Invalid exploration: %s' % exp.id) if raise_error: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) for state_name in exp.states: state = exp.states[state_name] if not state.interaction.is_supported_on_android_app(): error_string = ( 'Invalid interaction %s in exploration ' 'with ID: %s.' % (state.interaction.id, exp.id)) if raise_error: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) if not state.is_rte_content_supported_on_android(): error_string = ( 'RTE content in state %s of exploration ' 'with ID %s is not supported on mobile.' % (state_name, exp.id)) if raise_error: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) if state.interaction.id == 'EndExploration': recommended_exploration_ids = ( state.interaction.customization_args[ 'recommendedExplorationIds'].value) if len(recommended_exploration_ids) != 0: error_string = ( 'Exploration with ID: %s contains exploration ' 'recommendations in its EndExploration interaction.' % (exp.id)) if raise_error: raise utils.ValidationError(error_string) validation_error_messages.append(error_string) return validation_error_messages