def test_user_has_not_logged_in_last_n_days(self): user_query_1_id = user_query_services.save_new_user_query( self.submitter_id, has_not_logged_in_for_n_days=6) self._run_one_off_job(user_query_1_id) query_1 = user_models.UserQueryModel.get(user_query_1_id) # List of users who has not logged_in in last 6 days. self.assertItemsEqual(query_1.user_ids, [self.user_e_id]) user_query_2_id = user_query_services.save_new_user_query( self.submitter_id, has_not_logged_in_for_n_days=2) self._run_one_off_job(user_query_2_id) query_2 = user_models.UserQueryModel.get(user_query_2_id) # List of users logged_in in last 2 days. qualifying_user_ids = [self.user_a_id, self.user_e_id] self.assertItemsEqual(query_2.user_ids, qualifying_user_ids) # Test for legacy user. user_settings = user_services.get_user_settings(self.user_a_id) user_services.update_last_logged_in(user_settings, None) user_query_3_id = user_query_services.save_new_user_query( self.submitter_id, has_not_logged_in_for_n_days=6) self._run_one_off_job(user_query_3_id) query = user_models.UserQueryModel.get(user_query_3_id) # Make sure that legacy user is included in qualified user's list. self.assertItemsEqual(query.user_ids, [self.user_a_id, self.user_e_id])
def test_combination_of_query_params(self): user_query_1_id = user_query_services.save_new_user_query( self.submitter_id, { 'created_at_least_n_exps': 1, 'used_logic_proof_interaction': False, 'created_collection': False }) self._run_one_off_job(user_query_1_id) user_query_2_id = user_query_services.save_new_user_query( self.submitter_id, { 'edited_at_least_n_exps': 1, 'used_logic_proof_interaction': False, 'created_collection': False }) self._run_one_off_job(user_query_2_id) user_query_3_id = user_query_services.save_new_user_query( self.submitter_id, { 'created_at_least_n_exps': 1, 'edited_at_least_n_exps': 1, 'used_logic_proof_interaction': False, 'created_collection': False, }) self._run_one_off_job(user_query_3_id) qualifying_user_ids_a = [ self.user_b_id, self.user_d_id, self.user_e_id ] qualifying_user_ids_b = ([ self.user_b_id, self.user_c_id, self.user_d_id, self.user_e_id ]) qualifying_user_ids_combined = ([ self.user_b_id, self.user_d_id, self.user_e_id ]) query_1 = user_models.UserQueryModel.get(user_query_1_id) query_2 = user_models.UserQueryModel.get(user_query_2_id) query_combined = user_models.UserQueryModel.get(user_query_3_id) self.assertEqual(len(query_1.user_ids), 3) self.assertEqual(sorted(query_1.user_ids), sorted(qualifying_user_ids_a)) self.assertEqual(len(query_2.user_ids), 4) self.assertEqual(sorted(query_2.user_ids), sorted(qualifying_user_ids_b)) self.assertEqual(len(query_combined.user_ids), 3) self.assertEqual(sorted(query_combined.user_ids), sorted(qualifying_user_ids_combined))
def test_that_user_unsubscribed_from_emails_is_skipped(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, created_at_least_n_exps=1) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) self.assertNotIn(self.user_f_id, query.user_ids)
def test_email_dashboard_data_handler(self): self.login(self.SUBMITTER_EMAIL) response = self.get_json('/emaildashboarddatahandler', params={'num_queries_to_fetch': 1}) self.assertEqual(response['recent_queries'], []) user_query_id = user_query_services.save_new_user_query( self.submitter_id, inactive_in_last_n_days=10, created_at_least_n_exps=5, has_not_logged_in_for_n_days=30) response = self.get_json('/emaildashboarddatahandler', params={'num_queries_to_fetch': 1}) self.assertEqual(len(response['recent_queries']), 1) recent_query = response['recent_queries'][0] self.assertEqual(recent_query['id'], user_query_id) self.assertEqual(recent_query['status'], feconf.USER_QUERY_STATUS_PROCESSING) self.assertNotIn('submitter_id', recent_query) self.logout()
def test_bulk_email_handler_with_invalid_query_id_raises_400(self): self.login(self.SUBMITTER_EMAIL, is_super_admin=True) user_query_id = user_query_services.save_new_user_query( self.submitter_id, self.SAMPLE_QUERY_PARAM) job_id = user_query_jobs_one_off.UserQueryOneOffJob.create_new() user_query_jobs_one_off.UserQueryOneOffJob.enqueue( job_id, additional_job_params={'query_id': user_query_id}) self.assertEqual( self.count_jobs_in_mapreduce_taskqueue( taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS), 1) # Complete execution of query. with self.swap(feconf, 'CAN_SEND_EMAILS', True): self.process_and_flush_pending_mapreduce_tasks() self.assertEqual( self.count_jobs_in_mapreduce_taskqueue( taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS), 0) csrf_token = self.get_new_csrf_token() response = self.post_json('/emaildashboardtestbulkemailhandler/%s' % 'invalid_query_id', {}, csrf_token=csrf_token, expected_status_int=400) self.assertEqual(response['error'], '400 Invalid query id.') self.logout()
def test_email_dashboard_data_handler(self): self.login(self.SUBMITTER_EMAIL, is_super_admin=True) response = self.get_json( '/emaildashboarddatahandler', params={'num_queries_to_fetch': 1}) self.assertEqual(response['recent_queries'], []) user_query_id = user_query_services.save_new_user_query( self.submitter_id, self.SAMPLE_QUERY_PARAM) response = self.get_json( '/emaildashboarddatahandler', params={'num_queries_to_fetch': 1}) self.assertEqual(len(response['recent_queries']), 1) recent_query = response['recent_queries'][0] self.assertEqual(recent_query['id'], user_query_id) self.assertEqual( recent_query['status'], feconf.USER_QUERY_STATUS_PROCESSING) self.assertNotIn('submitter_id', recent_query) self.logout()
def test_user_has_edited_fewer_than_n_exps(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, edited_fewer_than_n_exps=1) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) self.assertItemsEqual(query.user_ids, [self.user_a_id])
def test_cancel_email_handler_with_invalid_query_id_raises_400(self): self.login(self.SUBMITTER_EMAIL) user_query_id = user_query_services.save_new_user_query( self.submitter_id, inactive_in_last_n_days=10, created_at_least_n_exps=5, has_not_logged_in_for_n_days=30) job_id = user_query_jobs_one_off.UserQueryOneOffJob.create_new() user_query_jobs_one_off.UserQueryOneOffJob.enqueue( job_id, additional_job_params={'query_id': user_query_id}) self.assertEqual( self.count_jobs_in_mapreduce_taskqueue( taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS), 1) # Complete execution of query. with self.swap(feconf, 'CAN_SEND_EMAILS', True): self.process_and_flush_pending_mapreduce_tasks() self.assertEqual( self.count_jobs_in_mapreduce_taskqueue( taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS), 0) csrf_token = self.get_new_csrf_token() response = self.post_json('/emaildashboardcancelresult/%s' % 'invalid_query_id', {}, csrf_token=csrf_token, expected_status_int=400) self.assertEqual(response['error'], '400 Invalid query id.') self.logout()
def test_email_dashboard_result_page_with_mismatch_of_query_id_raises_401( self): self.login(self.SUBMITTER_EMAIL) user_query_1_id = user_query_services.save_new_user_query( self.submitter_id, inactive_in_last_n_days=10, created_at_least_n_exps=5, has_not_logged_in_for_n_days=30) user_query_2_id = user_query_services.save_new_user_query( self.new_submitter_id, inactive_in_last_n_days=10, created_at_least_n_exps=5, has_not_logged_in_for_n_days=30) job_id_1 = user_query_jobs_one_off.UserQueryOneOffJob.create_new() user_query_jobs_one_off.UserQueryOneOffJob.enqueue( job_id_1, additional_job_params={'query_id': user_query_1_id}) job_id_2 = user_query_jobs_one_off.UserQueryOneOffJob.create_new() user_query_jobs_one_off.UserQueryOneOffJob.enqueue( job_id_2, additional_job_params={'query_id': user_query_2_id}) self.assertEqual( self.count_jobs_in_mapreduce_taskqueue( taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS), 2) # Complete execution of query. with self.swap(feconf, 'CAN_SEND_EMAILS', True): self.process_and_flush_pending_mapreduce_tasks() self.assertEqual( self.count_jobs_in_mapreduce_taskqueue( taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS), 0) csrf_token = self.get_new_csrf_token() # Raises authorization error when passing a query id whose associated # query model is not created by the logged in user. response = self.post_json('/emaildashboardresult/%s' % user_query_2_id, {}, csrf_token=csrf_token, expected_status_int=401) self.assertEqual( response['error'], '%s is not an authorized user for this query.' % (self.submitter_id)) self.logout()
def test_user_has_created_at_least_n_exps(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, created_at_least_n_exps=1) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) self.assertItemsEqual(query.user_ids, [self.user_b_id, self.user_d_id, self.user_e_id])
def test_user_is_inactive_in_last_n_days(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, inactive_in_last_n_days=3) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) # List of users who were not active in last 3 days. self.assertItemsEqual(query.user_ids, [self.user_e_id])
def test_that_user_without_user_contribution_model_is_skipped(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, { 'used_logic_proof_interaction': True, 'created_collection': False }) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) self.assertNotIn(self.user_g_id, query.user_ids)
def test_user_has_used_logic_proof_interaction(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, { 'used_logic_proof_interaction': True, 'created_collection': False }) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) self.assertItemsEqual(query.user_ids, [self.user_e_id])
def test_user_has_edited_at_least_n_exps(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, { 'edited_at_least_n_exps': 1 }) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) qualifying_user_ids = [ self.user_b_id, self.user_c_id, self.user_d_id, self.user_e_id] self.assertItemsEqual(query.user_ids, qualifying_user_ids)
def test_that_user_unsubscribed_from_emails_is_skipped(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, { 'created_at_least_n_exps': 1, 'used_logic_proof_interaction': False, 'created_collection': False }) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) self.assertNotIn(self.user_f_id, query.user_ids)
def test_combination_of_query_params(self): user_query_1_id = user_query_services.save_new_user_query( self.submitter_id, created_at_least_n_exps=1) self._run_one_off_job(user_query_1_id) user_query_2_id = user_query_services.save_new_user_query( self.submitter_id, edited_at_least_n_exps=1) self._run_one_off_job(user_query_2_id) user_query_3_id = user_query_services.save_new_user_query( self.submitter_id, created_at_least_n_exps=1, edited_at_least_n_exps=1) self._run_one_off_job(user_query_3_id) qualifying_user_ids_a = [ self.user_b_id, self.user_d_id, self.user_e_id ] qualifying_user_ids_b = ([ self.user_b_id, self.user_c_id, self.user_d_id, self.user_e_id ]) qualifying_user_ids_combined = ([ self.user_b_id, self.user_d_id, self.user_e_id ]) query_1 = user_models.UserQueryModel.get(user_query_1_id) query_2 = user_models.UserQueryModel.get(user_query_2_id) query_combined = user_models.UserQueryModel.get(user_query_3_id) self.assertEqual(len(query_1.user_ids), 3) self.assertEqual(sorted(query_1.user_ids), sorted(qualifying_user_ids_a)) self.assertEqual(len(query_2.user_ids), 4) self.assertEqual(sorted(query_2.user_ids), sorted(qualifying_user_ids_b)) self.assertEqual(len(query_combined.user_ids), 3) self.assertEqual(sorted(query_combined.user_ids), sorted(qualifying_user_ids_combined))
def post(self): """Post handler for query.""" data = self.normalized_payload.get('data') kwargs = {key: data[key] for key in data if data[key] is not None} user_query_id = user_query_services.save_new_user_query( self.user_id, kwargs) # Start MR job in background. user_query = (user_query_services.get_user_query(user_query_id, strict=True)) data = {'query': _generate_user_query_dicts([user_query])[0]} self.render_json(data)
def test_user_has_edited_at_least_n_exps(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, { 'edited_at_least_n_exps': 1, 'used_logic_proof_interaction': False, 'created_collection': False }) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) qualifying_user_ids = [ self.user_b_id, self.user_c_id, self.user_d_id, self.user_e_id] self.assertItemsEqual(query.user_ids, qualifying_user_ids)
def test_that_correct_email_is_sent_upon_completion(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, { 'edited_fewer_than_n_exps': 1, 'used_logic_proof_interaction': False, 'created_collection': False }) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) self.assertEqual( query.query_status, feconf.USER_QUERY_STATUS_COMPLETED) expected_email_html_body = ( 'Hi submit,<br>' 'Your query with id %s has succesfully completed its ' 'execution. Visit the result page ' '<a href="https://www.oppia.org/emaildashboardresult/%s">' 'here</a> ' 'to see result of your query.<br><br>' 'Thanks!<br>' '<br>' 'Best wishes,<br>' 'The Oppia Team<br>' '<br>' 'You can change your email preferences via the ' '<a href="http://localhost:8181/preferences">Preferences</a> page.' ) % (user_query_id, user_query_id) expected_email_text_body = ( 'Hi submit,\n' 'Your query with id %s has succesfully completed its ' 'execution. Visit the result page here ' 'to see result of your query.\n\n' 'Thanks!\n' '\n' 'Best wishes,\n' 'The Oppia Team\n' '\n' 'You can change your email preferences via the ' 'Preferences page.' ) % user_query_id messages = self._get_sent_email_messages( self.USER_SUBMITTER_EMAIL) self.assertEqual( messages[0].html.decode(), expected_email_html_body) self.assertEqual( messages[0].body.decode(), expected_email_text_body)
def test_user_is_inactive_in_last_n_days(self): number_of_days = 3 user_query_id = user_query_services.save_new_user_query( self.submitter_id, { 'inactive_in_last_n_days': number_of_days }) self._run_one_off_job(user_query_id) query = user_models.UserQueryModel.get(user_query_id) # user_d has created an exploration 10 days ago but edited an # exploration 2 days ago. self.assertNotIn(self.user_d_id, query.user_ids) # List of users who were not active in last 3 days. self.assertItemsEqual(query.user_ids, [self.user_e_id])
def test_that_correct_email_is_sent_upon_failure(self): user_query_id = user_query_services.save_new_user_query( self.submitter_id, { 'edited_fewer_than_n_exps': 1, 'used_logic_proof_interaction': False, 'created_collection': False }) self._run_one_off_job_resulting_in_failure(user_query_id) query = user_models.UserQueryModel.get(user_query_id) self.assertEqual( query.query_status, feconf.USER_QUERY_STATUS_FAILED) expected_email_html_body = ( 'Hi submit,<br>' 'Your query with id %s has failed due to error ' 'during execution. ' 'Please check the query parameters and submit query again.<br><br>' 'Thanks!<br>' '<br>' 'Best wishes,<br>' 'The Oppia Team<br>' '<br>' 'You can change your email preferences via the ' '<a href="http://localhost:8181/preferences">Preferences</a> page.' ) % user_query_id expected_email_text_body = ( 'Hi submit,\n' 'Your query with id %s has failed due to error ' 'during execution. ' 'Please check the query parameters and submit query again.\n\n' 'Thanks!\n' '\n' 'Best wishes,\n' 'The Oppia Team\n' '\n' 'You can change your email preferences via the Preferences page.' ) % user_query_id messages = self._get_sent_email_messages( self.USER_SUBMITTER_EMAIL) self.assertEqual( messages[0].html.decode(), expected_email_html_body) self.assertEqual( messages[0].body.decode(), expected_email_text_body)
def test_query_status_check_handler(self): self.login(self.SUBMITTER_EMAIL, is_super_admin=True) user_query_id = user_query_services.save_new_user_query( self.submitter_id, self.SAMPLE_QUERY_PARAM) query_data = self.get_json('/querystatuscheck', params={'query_id': user_query_id})['query'] self.assertEqual(query_data['id'], user_query_id) self.assertEqual(query_data['status'], feconf.USER_QUERY_STATUS_PROCESSING) self.assertEqual(query_data['submitter_username'], self.SUBMITTER_USERNAME) self.assertNotIn('submitter_id', query_data) self.logout()
def post(self): """Post handler for query.""" data = self.payload['data'] kwargs = {key: data[key] for key in data if data[key] is not None} self._validate(kwargs) user_query_id = user_query_services.save_new_user_query( self.user_id, **kwargs) # Start MR job in background. job_id = user_query_jobs_one_off.UserQueryOneOffJob.create_new() params = {'query_id': user_query_id} user_query_jobs_one_off.UserQueryOneOffJob.enqueue( job_id, additional_job_params=params) user_query = (user_query_services.get_user_query(user_query_id, strict=True)) data = {'query': _generate_user_query_dicts([user_query])[0]} self.render_json(data)
def test_query_status_check_handler(self): self.login(self.SUBMITTER_EMAIL) user_query_id = user_query_services.save_new_user_query( self.submitter_id, inactive_in_last_n_days=10, created_at_least_n_exps=5, has_not_logged_in_for_n_days=30) query_data = self.get_json('/querystatuscheck', params={'query_id': user_query_id})['query'] self.assertEqual(query_data['id'], user_query_id) self.assertEqual(query_data['status'], feconf.USER_QUERY_STATUS_PROCESSING) self.assertEqual(query_data['submitter_username'], self.SUBMITTER_USERNAME) self.assertNotIn('submitter_id', query_data) self.logout()
def test_save_new_query_model(self): query_param = { 'inactive_in_last_n_days': 10, 'created_at_least_n_exps': 5, 'has_not_logged_in_for_n_days': 30 } user_query_id = user_query_services.save_new_user_query( self.admin_user_id, query_param) query_model = user_models.UserQueryModel.get(user_query_id) self.assertEqual(query_model.submitter_id, self.admin_user_id) self.assertEqual(query_model.inactive_in_last_n_days, query_param['inactive_in_last_n_days']) self.assertEqual(query_model.created_at_least_n_exps, query_param['created_at_least_n_exps']) self.assertEqual(query_model.has_not_logged_in_for_n_days, query_param['has_not_logged_in_for_n_days']) self.assertIsNone(query_model.created_fewer_than_n_exps) self.assertIsNone(query_model.edited_at_least_n_exps) self.assertIsNone(query_model.edited_fewer_than_n_exps)
def test_save_new_query_model(self): inactive_in_last_n_days = 10 created_at_least_n_exps = 5 has_not_logged_in_for_n_days = 30 user_query_id = user_query_services.save_new_user_query( self.admin_user_id, inactive_in_last_n_days=inactive_in_last_n_days, created_at_least_n_exps=created_at_least_n_exps, has_not_logged_in_for_n_days=has_not_logged_in_for_n_days) query_model = user_models.UserQueryModel.get(user_query_id) self.assertEqual(query_model.submitter_id, self.admin_user_id) self.assertEqual(query_model.inactive_in_last_n_days, inactive_in_last_n_days) self.assertEqual(query_model.created_at_least_n_exps, created_at_least_n_exps) self.assertEqual(query_model.has_not_logged_in_for_n_days, has_not_logged_in_for_n_days) self.assertIsNone(query_model.created_fewer_than_n_exps) self.assertIsNone(query_model.edited_at_least_n_exps) self.assertIsNone(query_model.edited_fewer_than_n_exps)
def test_save_new_query_model(self) -> None: query_param = { 'inactive_in_last_n_days': 10, 'created_at_least_n_exps': 5, 'has_not_logged_in_for_n_days': 30 } user_query_id = user_query_services.save_new_user_query( self.admin_user_id, query_param) query_model = user_models.UserQueryModel.get(user_query_id) # Ruling out the possibility of None for mypy type checking. assert query_model is not None self.assertEqual(query_model.submitter_id, self.admin_user_id) self.assertEqual(query_model.inactive_in_last_n_days, query_param['inactive_in_last_n_days']) self.assertEqual(query_model.created_at_least_n_exps, query_param['created_at_least_n_exps']) self.assertEqual(query_model.has_not_logged_in_for_n_days, query_param['has_not_logged_in_for_n_days']) self.assertIsNone(query_model.created_fewer_than_n_exps) self.assertIsNone(query_model.edited_at_least_n_exps) self.assertIsNone(query_model.edited_fewer_than_n_exps)