def process( self, user_stats_model: user_models.UserStatsModel ) -> Iterable[user_models.UserStatsModel]: """Updates weekly dashboard stats with the current values. Args: user_stats_model: UserStatsModel. Model for which to update the weekly dashboard stats. Yields: UserStatsModel. The updated user stats model. """ model = job_utils.clone_model(user_stats_model) schema_version = model.schema_version if schema_version != feconf.CURRENT_DASHBOARD_STATS_SCHEMA_VERSION: user_services.migrate_dashboard_stats_to_latest_schema( model) # type: ignore[no-untyped-call] weekly_creator_stats = { user_services.get_current_date_as_string(): { # type: ignore[no-untyped-call] 'num_ratings': model.num_ratings or 0, 'average_ratings': model.average_ratings, 'total_plays': model.total_plays or 0 } } model.weekly_creator_stats_list.append(weekly_creator_stats) model.update_timestamps() yield model
class UserDashboardStatsTests(test_utils.GenericTestBase): """Test whether exploration-related statistics of a user change as events are registered. """ OWNER_EMAIL = '*****@*****.**' OWNER_USERNAME = '******' EXP_ID = 'exp1' USER_SESSION_ID = 'session1' CURRENT_DATE_AS_STRING = user_services.get_current_date_as_string() def setUp(self): super(UserDashboardStatsTests, self).setUp() self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) def _mock_get_current_date_as_string(self): return self.CURRENT_DATE_AS_STRING def test_get_user_dashboard_stats(self): exploration = self.save_new_valid_exploration(self.EXP_ID, self.owner_id, end_state_name='End') init_state_name = exploration.init_state_name event_services.StartExplorationEventHandler.record( self.EXP_ID, 1, init_state_name, self.USER_SESSION_ID, {}, feconf.PLAY_TYPE_NORMAL) self.assertEquals( user_jobs_continuous.UserStatsAggregator.get_dashboard_stats( self.owner_id), { 'total_plays': 0, 'num_ratings': 0, 'average_ratings': None }) (user_jobs_continuous_test.ModifiedUserStatsAggregator. start_computation()) self.process_and_flush_pending_tasks() self.assertEquals( user_jobs_continuous.UserStatsAggregator.get_dashboard_stats( self.owner_id), { 'total_plays': 1, 'num_ratings': 0, 'average_ratings': None }) def test_get_weekly_dashboard_stats_when_stats_model_is_none(self): exploration = self.save_new_valid_exploration(self.EXP_ID, self.owner_id, end_state_name='End') init_state_name = exploration.init_state_name event_services.StartExplorationEventHandler.record( self.EXP_ID, 1, init_state_name, self.USER_SESSION_ID, {}, feconf.PLAY_TYPE_NORMAL) self.assertEquals( user_services.get_weekly_dashboard_stats(self.owner_id), None) self.assertEquals( user_services.get_last_week_dashboard_stats(self.owner_id), None) with self.swap(user_services, 'get_current_date_as_string', self._mock_get_current_date_as_string): user_services.update_dashboard_stats_log(self.owner_id) self.assertEquals( user_services.get_weekly_dashboard_stats(self.owner_id), [{ self.CURRENT_DATE_AS_STRING: { 'total_plays': 0, 'num_ratings': 0, 'average_ratings': None } }]) def test_get_weekly_dashboard_stats(self): exploration = self.save_new_valid_exploration(self.EXP_ID, self.owner_id, end_state_name='End') init_state_name = exploration.init_state_name event_services.StartExplorationEventHandler.record( self.EXP_ID, 1, init_state_name, self.USER_SESSION_ID, {}, feconf.PLAY_TYPE_NORMAL) self.assertEquals( user_services.get_weekly_dashboard_stats(self.owner_id), None) self.assertEquals( user_services.get_last_week_dashboard_stats(self.owner_id), None) (user_jobs_continuous_test.ModifiedUserStatsAggregator. start_computation()) self.process_and_flush_pending_tasks() self.assertEquals( user_services.get_weekly_dashboard_stats(self.owner_id), None) self.assertEquals( user_services.get_last_week_dashboard_stats(self.owner_id), None) with self.swap(user_services, 'get_current_date_as_string', self._mock_get_current_date_as_string): user_services.update_dashboard_stats_log(self.owner_id) self.assertEquals( user_services.get_weekly_dashboard_stats(self.owner_id), [{ self.CURRENT_DATE_AS_STRING: { 'total_plays': 1, 'num_ratings': 0, 'average_ratings': None } }])
class DashboardStatsOneOffJobTests(test_utils.GenericTestBase): """Tests for the one-off dashboard stats job.""" CURRENT_DATE_AS_STRING = user_services.get_current_date_as_string() DATE_AFTER_ONE_WEEK = ( (datetime.datetime.utcnow() + datetime.timedelta(7)).strftime( feconf.DASHBOARD_STATS_DATETIME_STRING_FORMAT)) USER_SESSION_ID = 'session1' EXP_ID_1 = 'exp_id_1' EXP_ID_2 = 'exp_id_2' EXP_VERSION = 1 def _run_one_off_job(self): """Runs the one-off MapReduce job.""" job_id = user_jobs_one_off.DashboardStatsOneOffJob.create_new() user_jobs_one_off.DashboardStatsOneOffJob.enqueue(job_id) self.assertEqual( self.count_jobs_in_taskqueue( taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS), 1) self.process_and_flush_pending_tasks() def setUp(self): super(DashboardStatsOneOffJobTests, self).setUp() self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) def _mock_get_current_date_as_string(self): return self.CURRENT_DATE_AS_STRING def _rate_exploration(self, user_id, exp_id, rating): rating_services.assign_rating_to_exploration(user_id, exp_id, rating) def _record_play(self, exp_id, state): event_services.StartExplorationEventHandler.record( exp_id, self.EXP_VERSION, state, self.USER_SESSION_ID, {}, feconf.PLAY_TYPE_NORMAL) def test_weekly_stats_if_continuous_stats_job_has_not_been_run(self): exploration = self.save_new_valid_exploration( self.EXP_ID_1, self.owner_id) exp_id = exploration.id init_state_name = exploration.init_state_name self._record_play(exp_id, init_state_name) self._rate_exploration('user1', exp_id, 5) weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, None) self.assertEquals( user_services.get_last_week_dashboard_stats(self.owner_id), None) with self.swap(user_services, 'get_current_date_as_string', self._mock_get_current_date_as_string): self._run_one_off_job() weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) expected_results_list = [{ self._mock_get_current_date_as_string(): { 'num_ratings': 0, 'average_ratings': None, 'total_plays': 0 } }] self.assertEqual(weekly_stats, expected_results_list) self.assertEquals( user_services.get_last_week_dashboard_stats(self.owner_id), expected_results_list[0]) def test_weekly_stats_if_no_explorations(self): (user_jobs_continuous_test.ModifiedUserStatsAggregator. start_computation()) self.process_and_flush_pending_tasks() with self.swap(user_services, 'get_current_date_as_string', self._mock_get_current_date_as_string): self._run_one_off_job() weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, [{ self._mock_get_current_date_as_string(): { 'num_ratings': 0, 'average_ratings': None, 'total_plays': 0 } }]) def test_weekly_stats_for_single_exploration(self): exploration = self.save_new_valid_exploration( self.EXP_ID_1, self.owner_id) exp_id = exploration.id init_state_name = exploration.init_state_name self._record_play(exp_id, init_state_name) self._rate_exploration('user1', exp_id, 5) (user_jobs_continuous_test.ModifiedUserStatsAggregator. start_computation()) self.process_and_flush_pending_tasks() with self.swap(user_services, 'get_current_date_as_string', self._mock_get_current_date_as_string): self._run_one_off_job() weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, [{ self._mock_get_current_date_as_string(): { 'num_ratings': 1, 'average_ratings': 5.0, 'total_plays': 1 } }]) def test_weekly_stats_for_multiple_explorations(self): exploration_1 = self.save_new_valid_exploration( self.EXP_ID_1, self.owner_id) exp_id_1 = exploration_1.id exploration_2 = self.save_new_valid_exploration( self.EXP_ID_2, self.owner_id) exp_id_2 = exploration_2.id init_state_name_1 = exploration_1.init_state_name self._record_play(exp_id_1, init_state_name_1) self._rate_exploration('user1', exp_id_1, 5) self._rate_exploration('user2', exp_id_2, 4) (user_jobs_continuous_test.ModifiedUserStatsAggregator. start_computation()) self.process_and_flush_pending_tasks() with self.swap(user_services, 'get_current_date_as_string', self._mock_get_current_date_as_string): self._run_one_off_job() weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, [{ self._mock_get_current_date_as_string(): { 'num_ratings': 2, 'average_ratings': 4.5, 'total_plays': 1 } }]) def test_stats_for_multiple_weeks(self): exploration = self.save_new_valid_exploration( self.EXP_ID_1, self.owner_id) exp_id = exploration.id init_state_name = exploration.init_state_name self._rate_exploration('user1', exp_id, 4) self._record_play(exp_id, init_state_name) self._record_play(exp_id, init_state_name) (user_jobs_continuous_test.ModifiedUserStatsAggregator. start_computation()) self.process_and_flush_pending_tasks() with self.swap(user_services, 'get_current_date_as_string', self._mock_get_current_date_as_string): self._run_one_off_job() weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, [{ self._mock_get_current_date_as_string(): { 'num_ratings': 1, 'average_ratings': 4.0, 'total_plays': 2 } }]) (user_jobs_continuous_test.ModifiedUserStatsAggregator. stop_computation(self.owner_id)) self.process_and_flush_pending_tasks() self._rate_exploration('user2', exp_id, 2) (user_jobs_continuous_test.ModifiedUserStatsAggregator. start_computation()) self.process_and_flush_pending_tasks() def _mock_get_date_after_one_week(): """Returns the date of the next week.""" return self.DATE_AFTER_ONE_WEEK with self.swap(user_services, 'get_current_date_as_string', _mock_get_date_after_one_week): self._run_one_off_job() expected_results_list = [ { self._mock_get_current_date_as_string(): { 'num_ratings': 1, 'average_ratings': 4.0, 'total_plays': 2 } }, { _mock_get_date_after_one_week(): { 'num_ratings': 2, 'average_ratings': 3.0, 'total_plays': 2 } } ] weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, expected_results_list) self.assertEquals( user_services.get_last_week_dashboard_stats(self.owner_id), expected_results_list[1])
class DashboardStatsOneOffJobTests(test_utils.GenericTestBase): """Tests for the one-off dashboard stats job.""" CURRENT_DATE_AS_STRING = user_services.get_current_date_as_string() DATE_AFTER_ONE_WEEK = ((datetime.datetime.utcnow() + datetime.timedelta(7)).strftime( feconf.DASHBOARD_STATS_DATETIME_STRING_FORMAT)) USER_SESSION_ID = 'session1' EXP_ID_1 = 'exp_id_1' EXP_ID_2 = 'exp_id_2' EXP_VERSION = 1 def _run_one_off_job(self): """Runs the one-off MapReduce job.""" job_id = user_jobs_one_off.DashboardStatsOneOffJob.create_new() user_jobs_one_off.DashboardStatsOneOffJob.enqueue(job_id) self.assertEqual( self.count_jobs_in_mapreduce_taskqueue( taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS), 1) self.process_and_flush_pending_mapreduce_tasks() def setUp(self): super(DashboardStatsOneOffJobTests, self).setUp() self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) def mock_get_current_date_as_string(self): return self.CURRENT_DATE_AS_STRING def _rate_exploration(self, user_id, exp_id, rating): """Assigns rating to the exploration corresponding to the given exploration id. Args: user_id: str. The user id. exp_id: str. The exploration id. rating: int. The rating to be assigned to the given exploration. """ rating_services.assign_rating_to_exploration(user_id, exp_id, rating) def _record_play(self, exp_id, state): """Calls StartExplorationEventHandler and records the 'play' event corresponding to the given exploration id. Args: exp_id: str. The exploration id. state: dict(str, *). The state of the exploration corresponding to the given id. """ event_services.StartExplorationEventHandler.record( exp_id, self.EXP_VERSION, state, self.USER_SESSION_ID, {}, feconf.PLAY_TYPE_NORMAL) def test_weekly_stats(self): exploration = self.save_new_valid_exploration(self.EXP_ID_1, self.owner_id) exp_id = exploration.id init_state_name = exploration.init_state_name self._record_play(exp_id, init_state_name) self._rate_exploration('user1', exp_id, 5) weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, None) self.assertEqual( user_services.get_last_week_dashboard_stats(self.owner_id), None) with self.swap(user_services, 'get_current_date_as_string', self.mock_get_current_date_as_string): self._run_one_off_job() weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, [{ self.mock_get_current_date_as_string(): { 'num_ratings': 1, 'average_ratings': 5.0, 'total_plays': 1 } }]) self.assertEqual( user_services.get_last_week_dashboard_stats(self.owner_id), { self.mock_get_current_date_as_string(): { 'num_ratings': 1, 'average_ratings': 5.0, 'total_plays': 1 } }) def test_weekly_stats_if_no_explorations(self): with self.swap(user_services, 'get_current_date_as_string', self.mock_get_current_date_as_string): self._run_one_off_job() weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, [{ self.mock_get_current_date_as_string(): { 'num_ratings': 0, 'average_ratings': None, 'total_plays': 0 } }]) def test_weekly_stats_for_single_exploration(self): exploration = self.save_new_valid_exploration(self.EXP_ID_1, self.owner_id) exp_id = exploration.id init_state_name = exploration.init_state_name self._record_play(exp_id, init_state_name) self._rate_exploration('user1', exp_id, 5) event_services.StatsEventsHandler.record( self.EXP_ID_1, 1, { 'num_starts': 1, 'num_actual_starts': 0, 'num_completions': 0, 'state_stats_mapping': {} }) self.process_and_flush_pending_tasks() with self.swap(user_services, 'get_current_date_as_string', self.mock_get_current_date_as_string): self._run_one_off_job() weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, [{ self.mock_get_current_date_as_string(): { 'num_ratings': 1, 'average_ratings': 5.0, 'total_plays': 1 } }]) def test_weekly_stats_for_multiple_explorations(self): exploration_1 = self.save_new_valid_exploration( self.EXP_ID_1, self.owner_id) exp_id_1 = exploration_1.id exploration_2 = self.save_new_valid_exploration( self.EXP_ID_2, self.owner_id) exp_id_2 = exploration_2.id init_state_name_1 = exploration_1.init_state_name self._record_play(exp_id_1, init_state_name_1) self._rate_exploration('user1', exp_id_1, 5) self._rate_exploration('user2', exp_id_2, 4) event_services.StatsEventsHandler.record( self.EXP_ID_1, 1, { 'num_starts': 1, 'num_actual_starts': 0, 'num_completions': 0, 'state_stats_mapping': {} }) self.process_and_flush_pending_tasks() with self.swap(user_services, 'get_current_date_as_string', self.mock_get_current_date_as_string): self._run_one_off_job() weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, [{ self.mock_get_current_date_as_string(): { 'num_ratings': 2, 'average_ratings': 4.5, 'total_plays': 1 } }]) def test_stats_for_multiple_weeks(self): exploration = self.save_new_valid_exploration(self.EXP_ID_1, self.owner_id) exp_id = exploration.id init_state_name = exploration.init_state_name self._rate_exploration('user1', exp_id, 4) self._record_play(exp_id, init_state_name) self._record_play(exp_id, init_state_name) event_services.StatsEventsHandler.record( self.EXP_ID_1, 1, { 'num_starts': 2, 'num_actual_starts': 0, 'num_completions': 0, 'state_stats_mapping': {} }) self.process_and_flush_pending_tasks() with self.swap(user_services, 'get_current_date_as_string', self.mock_get_current_date_as_string): self._run_one_off_job() weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, [{ self.mock_get_current_date_as_string(): { 'num_ratings': 1, 'average_ratings': 4.0, 'total_plays': 2 } }]) self._rate_exploration('user2', exp_id, 2) def _mock_get_date_after_one_week(): """Returns the date of the next week.""" return self.DATE_AFTER_ONE_WEEK with self.swap(user_services, 'get_current_date_as_string', _mock_get_date_after_one_week): self._run_one_off_job() expected_results_list = [{ self.mock_get_current_date_as_string(): { 'num_ratings': 1, 'average_ratings': 4.0, 'total_plays': 2 } }, { _mock_get_date_after_one_week(): { 'num_ratings': 2, 'average_ratings': 3.0, 'total_plays': 2 } }] weekly_stats = user_services.get_weekly_dashboard_stats(self.owner_id) self.assertEqual(weekly_stats, expected_results_list) self.assertEqual( user_services.get_last_week_dashboard_stats(self.owner_id), expected_results_list[1])