def test_get_exploration_opportunity_summaries_by_ids_returns_list_of_objects(self): # pylint: disable=line-too-long output = ( opportunity_services.get_exploration_opportunity_summaries_by_ids( [])) self.assertEqual(output, []) opportunities = ( opportunity_services.get_exploration_opportunity_summaries_by_ids( ['0'])) self.assertEqual(len(opportunities), 1) self.assertIsInstance(opportunities[0], opportunity_domain.ExplorationOpportunitySummary) self.assertEqual(opportunities[0].id, '0')
def test_get_exploration_opportunity_summaries_by_ids(self): output = ( opportunity_services.get_exploration_opportunity_summaries_by_ids( [])) self.assertEqual(output, {}) opportunities = ( opportunity_services.get_exploration_opportunity_summaries_by_ids( ['0'])) self.assertEqual(len(opportunities), 1) self.assertIsInstance(opportunities['0'], opportunity_domain.ExplorationOpportunitySummary) self.assertEqual(opportunities['0'].id, '0')
def test_get_exploration_opportunity_summaries_by_ids_for_invalid_id(self): opportunities = ( opportunity_services.get_exploration_opportunity_summaries_by_ids( ['badID'])) self.assertEqual(len(opportunities), 1) self.assertEqual(opportunities['badID'], None)
def reject_voiceover_application(voiceover_application_id, reviewer_id, rejection_message): """Rejects the voiceover application of given voiceover application id. Args: voiceover_application_id: str. The is of the voiceover application which need to be rejected. reviewer_id: str. The user ID of the reviewer. rejection_message: str. The plain text message submitted by the reviewer while rejecting the application. """ voiceover_application = get_voiceover_application_by_id( voiceover_application_id) if reviewer_id == voiceover_application.author_id: raise Exception('Applicants are not allowed to review their own ' 'voiceover application.') reviewer = user_services.UserActionsInfo(user_id=reviewer_id) voiceover_application.reject(reviewer.user_id, rejection_message) _save_voiceover_applications([voiceover_application]) if voiceover_application.target_type == feconf.ENTITY_TYPE_EXPLORATION: opportunities = ( opportunity_services.get_exploration_opportunity_summaries_by_ids( [voiceover_application.target_id])) email_manager.send_rejected_voiceover_application_email( voiceover_application.author_id, opportunities[0].chapter_title, voiceover_application.language_code, rejection_message)
def accept_voiceover_application(voiceover_application_id, reviewer_id): """Accept the voiceover application of given voiceover application id. Args: voiceover_application_id: str. The id of the voiceover application which need to be accepted. reviewer_id: str. The user ID of the reviewer. Raises: Exception. Reviewer ID is same as the author ID. """ voiceover_application = get_voiceover_application_by_id( voiceover_application_id) if reviewer_id == voiceover_application.author_id: raise Exception( 'Applicants are not allowed to review their own ' 'voiceover application.') reviewer = user_services.get_user_actions_info(reviewer_id) voiceover_application.accept(reviewer_id) _save_voiceover_applications([voiceover_application]) if voiceover_application.target_type == feconf.ENTITY_TYPE_EXPLORATION: rights_manager.assign_role_for_exploration( reviewer, voiceover_application.target_id, voiceover_application.author_id, rights_domain.ROLE_VOICE_ARTIST) opportunity_services.update_exploration_voiceover_opportunities( voiceover_application.target_id, voiceover_application.language_code) opportunities = ( opportunity_services.get_exploration_opportunity_summaries_by_ids([ voiceover_application.target_id])) email_manager.send_accepted_voiceover_application_email( voiceover_application.author_id, opportunities[voiceover_application.target_id].chapter_title, voiceover_application.language_code) # TODO(#7969): Add notification to the user's dashboard for the accepted # voiceover application. voiceover_application_models = ( suggestion_models.GeneralVoiceoverApplicationModel .get_voiceover_applications( voiceover_application.target_type, voiceover_application.target_id, voiceover_application.language_code)) rejected_voiceover_applications = [] for model in voiceover_application_models: voiceover_application = _get_voiceover_application_from_model( model) if not voiceover_application.is_handled: voiceover_application.reject( reviewer_id, 'We have to reject your application as another ' 'application for the same opportunity got accepted.') rejected_voiceover_applications.append(voiceover_application) _save_voiceover_applications(rejected_voiceover_applications)
def map(item): """Implements the map function (generator). Computes word counts of translations suggestions and outputs suggestion metadata. Args: item: GeneralSuggestionModel. An instance of GeneralSuggestionModel. Yields: tuple(key, recent_activity_commits). Where: key: str. The entity ID of the corresponding TranslationContributionStatsModel. dict. Has the keys: suggestion_status: str. The translation suggestion status. edited_by_reviewer: bool. Whether the translation suggestion was edited by a reviewer. content_word_count: int. The word count of the translation suggestion content HTML. last_updated_date: date. The last updated date of the translation suggestion. """ if item.suggestion_type != feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT: return suggestion = suggestion_services.get_suggestion_from_model(item) # Try to extract the topic ID from the corresponding exploration # opportunity. topic_id = '' exp_id = suggestion.target_id exp_opportunity_dict = ( opportunity_services.get_exploration_opportunity_summaries_by_ids( [exp_id])) exp_opportunity = exp_opportunity_dict[exp_id] if exp_opportunity is not None: topic_id = exp_opportunity.topic_id # Count the number of words in the original content, ignoring any HTML # tags and attributes. content_plain_text = html_cleaner.strip_html_tags( suggestion.change.content_html) content_word_count = len(content_plain_text.split()) key = suggestion_models.TranslationContributionStatsModel.generate_id( suggestion.language_code, suggestion.author_id, topic_id) translation_contribution_stats_dict = { 'suggestion_status': suggestion.status, 'edited_by_reviewer': suggestion.edited_by_reviewer, 'content_word_count': content_word_count, 'last_updated_date': suggestion.last_updated.date().isoformat() } yield (key, translation_contribution_stats_dict)
def _get_target_id_to_exploration_opportunity_dict(suggestions): """Returns a dict of target_id to exploration opportunity summary dict. Args: suggestions: list(BaseSuggestion). A list of suggestions to retrieve opportunity dicts. Returns: dict. Dict mapping target_id to corresponding exploration opportunity summary dict. """ target_ids = set([s.target_id for s in suggestions]) opportunities = ( opportunity_services.get_exploration_opportunity_summaries_by_ids( list(target_ids))) return {opp.id: opp.to_dict() for opp in opportunities}
def test_get_exploration_opportunity_summary_from_model_populates_new_lang( self): observed_log_messages = [] def _mock_logging_function(msg, *args): """Mocks logging.info().""" observed_log_messages.append(msg % args) opportunities = ( opportunity_services.get_exploration_opportunity_summaries_by_ids( ['0'])) self.assertEqual(len(opportunities), 1) opportunity = opportunities[0] self.assertFalse( 'new_lang' in opportunity.incomplete_translation_language_codes) mock_supported_languages = constants.SUPPORTED_AUDIO_LANGUAGES + [{ 'id': 'new_lang', 'description': 'New language', 'relatedLanguages': ['new_lang'] }] self.assertEqual(len(observed_log_messages), 0) with self.swap(logging, 'info', _mock_logging_function), self.swap( constants, 'SUPPORTED_AUDIO_LANGUAGES', mock_supported_languages): opportunities = (opportunity_services. get_exploration_opportunity_summaries_by_ids( ['0'])) self.assertEqual(len(opportunities), 1) opportunity = opportunities[0] self.assertTrue('new_lang' in opportunity.incomplete_translation_language_codes) self.assertEqual(len(observed_log_messages), 1) self.assertEqual( observed_log_messages[0], 'Missing language codes [u\'new_lang\'] in exploration ' 'opportunity model with id 0')
def _get_target_id_to_exploration_opportunity_dict(suggestions): """Returns a dict of target_id to exploration opportunity summary dict. Args: suggestions: list(BaseSuggestion). A list of suggestions to retrieve opportunity dicts. Returns: dict. Dict mapping target_id to corresponding exploration opportunity summary dict. """ target_ids = set([s.target_id for s in suggestions]) opportunity_id_to_opportunity_dict = { opp_id: (opp.to_dict() if opp is not None else None) for opp_id, opp in ( opportunity_services.get_exploration_opportunity_summaries_by_ids( list(target_ids)).items()) } return opportunity_id_to_opportunity_dict
def _get_reviewable_exploration_opportunity_summaries( self, user_id, topic_name ): """Returns exploration opportunity summaries that have translation suggestions that are reviewable by the supplied user. The result is sorted in descending order by topic, story, and story node order. Args: user_id: str. The user ID of the user for which to filter translation suggestions. topic_name: str. A topic name for which to filter the exploration opportunity summaries. If 'All' is supplied, all available exploration opportunity summaries will be returned. Returns: list(ExplorationOpportunitySummary). A list of the matching exploration opportunity summaries. """ # 1. Fetch the eligible topics. # 2. Fetch the stories for the topics. # 3. Get the reviewable translation suggestion target IDs for the user. # 4. Get story exploration nodes in order, filtering for explorations # that have in review translation suggestions. if topic_name is None: topics = topic_fetchers.get_all_topics() else: topic = topic_fetchers.get_topic_by_name(topic_name) if topic is None: raise self.InvalidInputException( 'The supplied input topic: %s is not valid' % topic_name) topics = [topic] topic_stories = story_fetchers.get_stories_by_ids([ reference.story_id for topic in topics for reference in topic.get_all_story_references() if reference.story_is_published]) topic_exp_ids = [ node.exploration_id for story in topic_stories for node in story.story_contents.get_ordered_nodes() ] in_review_suggestions, _ = ( suggestion_services .get_reviewable_translation_suggestions_by_offset( user_id, topic_exp_ids, None, 0)) # Filter out suggestions that should not be shown to the user. # This is defined as a set as we only care about the unique IDs. in_review_suggestion_target_ids = { suggestion.target_id for suggestion in suggestion_services.get_suggestions_with_translatable_explorations( in_review_suggestions) } exp_ids = [ exp_id for exp_id in topic_exp_ids if exp_id in in_review_suggestion_target_ids ] return ( opportunity_services.get_exploration_opportunity_summaries_by_ids( exp_ids).values())