def testUpdateConversations(self): """Tests that the updateConversations task correctly adds a user to all conversations they should be in but aren't, and removes them from conversations they shouldn't be in. """ def run_update_task(user_key, program_key): """Runs the task and test that it runs correctly. Args: user_key: Key (ndb) of User. program_key: Key (ndb) of GCIProgram. """ update_conversations.spawnUpdateConversationsTask(user_key, program_key) self.assertTasksInQueue(n=1, url=TASK_URL) for task in self.get_tasks(): if task['url'] == TASK_URL: params = task['params'] self.assertIn('user_key', params) self.assertEqual(params['user_key'], user_key.urlsafe()) self.assertIn('program_key', params) self.assertEqual(params['program_key'], program_key.urlsafe()) # Test task post_data = { 'user_key': user_key.urlsafe(), 'program_key': program_key.urlsafe(), } response = self.post(TASK_URL, post_data) self.assertEqual(response.status_code, httplib.OK) self.clear_task_queue() # Create a couple dummy organizations org_keys = map( lambda org: ndb.Key.from_old_key(org.key()), list(self.conv_utils.program_helper.createNewOrg() for x in range(2))) # Create various dummy users user_admin_key = self.conv_utils.createUser( return_key=True, roles=[conversation_utils.ADMIN], admin_organizations=[org_keys[0]]) user_mentor_key = self.conv_utils.createUser( return_key=True, roles=[conversation_utils.MENTOR], mentor_organizations=[org_keys[0], org_keys[1]]) user_mentor_student_key = self.conv_utils.createUser( return_key=True, roles=[conversation_utils.MENTOR, conversation_utils.STUDENT]) user_winner_key = self.conv_utils.createUser( return_key=True, winning_organization=org_keys[1], roles=[conversation_utils.WINNER, conversation_utils.STUDENT]) # Conversation for program admins and mentors conv_a = self.conv_utils.createConversation(subject='') conv_a.recipients_type = conversation_model.PROGRAM conv_a.include_admins = True conv_a.include_mentors = True conv_a.put() # Conversation for first org's admins conv_b = self.conv_utils.createConversation(subject='') conv_b.recipients_type = conversation_model.ORGANIZATION conv_b.organization = org_keys[0] conv_b.include_admins = True conv_b.put() # Conversation for second org's mentors conv_c = self.conv_utils.createConversation(subject='') conv_c.recipients_type = conversation_model.ORGANIZATION conv_c.organization = org_keys[1] conv_c.include_mentors = True conv_c.put() # Conversation for program mentors and students conv_d = self.conv_utils.createConversation(subject='') conv_d.recipients_type = conversation_model.PROGRAM conv_d.include_mentors = True conv_d.include_students = True conv_d.put() # Conversation for program students, created by a non-student conv_e = self.conv_utils.createConversation(subject='') conv_e.creator = user_admin_key conv_e.recipients_type = conversation_model.PROGRAM conv_e.include_students = True conv_e.put() self.conv_utils.addUser(conversation=conv_e.key, user=conv_e.creator) # Conversation for basically nobody, in which the participants should not # be changed after the conversation's creation, and all users are added. # This is to ensure that users won't be removed if the conversation's # auto_update_users property is False. conv_f = self.conv_utils.createConversation(subject='') conv_f.recipients_type = conversation_model.PROGRAM conv_f.auto_update_users = False conv_f.put() self.conv_utils.addUser(conversation=conv_f.key, user=user_admin_key) self.conv_utils.addUser(conversation=conv_f.key, user=user_mentor_key) self.conv_utils.addUser( conversation=conv_f.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_f.key, user=user_winner_key) # Conversation for that all users fit the criteria to participate in, but # should not be added after the converation's creation. conv_g = self.conv_utils.createConversation(subject='') conv_g.recipients_type = conversation_model.PROGRAM conv_g.include_students = True conv_g.include_mentors = True conv_g.include_admins = True conv_g.auto_update_users = False conv_g.put() # Conversation for program winners, created by a non-winner conv_h = self.conv_utils.createConversation(subject='') conv_h.recipients_type = conversation_model.PROGRAM conv_h.include_winners = True conv_h.put() # Conversation for winners of first organization conv_i = self.conv_utils.createConversation(subject='') conv_i.recipients_type = conversation_model.ORGANIZATION conv_i.organization = org_keys[0] conv_i.include_winners = True conv_i.put() # Conversation for winners of second organization conv_j = self.conv_utils.createConversation(subject='') conv_j.recipients_type = conversation_model.ORGANIZATION conv_j.organization = org_keys[1] conv_j.include_winners = True conv_j.put() # Refresh each user's conversations run_update_task(user_admin_key, self.program_key) run_update_task(user_mentor_key, self.program_key) run_update_task(user_mentor_student_key, self.program_key) run_update_task(user_winner_key, self.program_key) # Test that admin user is in the correct conversations expected_keys = set([conv_a.key, conv_b.key, conv_e.key, conv_f.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_admin_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor user is in the correct conversations expected_keys = set([conv_a.key, conv_c.key, conv_d.key, conv_f.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor/student user is in the correct conversations expected_keys = set([conv_a.key, conv_d.key, conv_e.key, conv_f.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_student_key))) self.assertEqual(expected_keys, actual_keys) # Test that winner user is in the correct conversations expected_keys = set( [conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_j.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_winner_key))) self.assertEqual(expected_keys, actual_keys) # Add all three users to all conversations self.conv_utils.addUser(conversation=conv_b.key, user=user_mentor_key) self.conv_utils.addUser( conversation=conv_b.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_c.key, user=user_admin_key) self.conv_utils.addUser( conversation=conv_c.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_d.key, user=user_admin_key) self.conv_utils.addUser(conversation=conv_e.key, user=user_mentor_key) self.conv_utils.addUser( conversation=conv_h.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_h.key, user=user_mentor_key) self.conv_utils.addUser(conversation=conv_h.key, user=user_admin_key) self.conv_utils.addUser( conversation=conv_i.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_i.key, user=user_mentor_key) self.conv_utils.addUser(conversation=conv_i.key, user=user_admin_key) self.conv_utils.addUser( conversation=conv_j.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_j.key, user=user_mentor_key) self.conv_utils.addUser(conversation=conv_j.key, user=user_admin_key) self.conv_utils.addUser(conversation=conv_a.key, user=user_winner_key) self.conv_utils.addUser(conversation=conv_b.key, user=user_winner_key) self.conv_utils.addUser(conversation=conv_c.key, user=user_winner_key) self.conv_utils.addUser(conversation=conv_i.key, user=user_winner_key) # Test that admin user is in all conversations except for G expected_keys = set([ conv_a.key, conv_b.key, conv_c.key, conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_i.key, conv_j.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_admin_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor user is in all conversations except for G expected_keys = set([ conv_a.key, conv_b.key, conv_c.key, conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_i.key, conv_j.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor/student user is in all conversations except for G expected_keys = set([ conv_a.key, conv_b.key, conv_c.key, conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_i.key, conv_j.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_student_key))) self.assertEqual(expected_keys, actual_keys) # Test that winner user is in all conversations except for G expected_keys = set([ conv_a.key, conv_b.key, conv_c.key, conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_i.key, conv_j.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_winner_key))) self.assertEqual(expected_keys, actual_keys) # Refresh each user's conversations. Because we just added all users to # all conversations, refreshing each user should actually remove them from # conversations they don't belong to. run_update_task(user_admin_key, self.program_key) run_update_task(user_mentor_key, self.program_key) run_update_task(user_mentor_student_key, self.program_key) run_update_task(user_winner_key, self.program_key) # Test that admin user is in the correct conversations expected_keys = set([conv_a.key, conv_b.key, conv_e.key, conv_f.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_admin_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor user is in the correct conversations expected_keys = set([conv_a.key, conv_c.key, conv_d.key, conv_f.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor/student user is in the correct conversations expected_keys = set([conv_a.key, conv_d.key, conv_e.key, conv_f.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_student_key))) self.assertEqual(expected_keys, actual_keys) # Test that winner user is in the correct conversations expected_keys = set( [conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_j.key]) actual_keys = set(map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_winner_key))) self.assertEqual(expected_keys, actual_keys)
def testUpdateConversations(self): """Tests that the updateConversations task correctly adds a user to all conversations they should be in but aren't, and removes them from conversations they shouldn't be in. """ def run_update_task(user_key, program_key): """Runs the task and test that it runs correctly. Args: user_key: Key (ndb) of User. program_key: Key (ndb) of GCIProgram. """ update_conversations.spawnUpdateConversationsTask( user_key, program_key) self.assertTasksInQueue(n=1, url=TASK_URL) for task in self.get_tasks(): if task['url'] == TASK_URL: params = task['params'] self.assertIn('user_key', params) self.assertEqual(params['user_key'], user_key.urlsafe()) self.assertIn('program_key', params) self.assertEqual(params['program_key'], program_key.urlsafe()) # Test task post_data = { 'user_key': user_key.urlsafe(), 'program_key': program_key.urlsafe(), } response = self.post(TASK_URL, post_data) self.assertEqual(response.status_code, httplib.OK) self.clear_task_queue() # Create a couple dummy organizations org_keys = map( lambda org: ndb.Key.from_old_key(org.key()), list(self.conv_utils.program_helper.createNewOrg() for x in range(2))) # Create various dummy users user_admin_key = self.conv_utils.createUser( return_key=True, roles=[conversation_utils.ADMIN], admin_organizations=[org_keys[0]]) user_mentor_key = self.conv_utils.createUser( return_key=True, roles=[conversation_utils.MENTOR], mentor_organizations=[org_keys[0], org_keys[1]]) user_mentor_student_key = self.conv_utils.createUser( return_key=True, roles=[conversation_utils.MENTOR, conversation_utils.STUDENT]) user_winner_key = self.conv_utils.createUser( return_key=True, winning_organization=org_keys[1], roles=[conversation_utils.WINNER, conversation_utils.STUDENT]) # Conversation for program admins and mentors conv_a = self.conv_utils.createConversation(subject='') conv_a.recipients_type = conversation_model.PROGRAM conv_a.include_admins = True conv_a.include_mentors = True conv_a.put() # Conversation for first org's admins conv_b = self.conv_utils.createConversation(subject='') conv_b.recipients_type = conversation_model.ORGANIZATION conv_b.organization = org_keys[0] conv_b.include_admins = True conv_b.put() # Conversation for second org's mentors conv_c = self.conv_utils.createConversation(subject='') conv_c.recipients_type = conversation_model.ORGANIZATION conv_c.organization = org_keys[1] conv_c.include_mentors = True conv_c.put() # Conversation for program mentors and students conv_d = self.conv_utils.createConversation(subject='') conv_d.recipients_type = conversation_model.PROGRAM conv_d.include_mentors = True conv_d.include_students = True conv_d.put() # Conversation for program students, created by a non-student conv_e = self.conv_utils.createConversation(subject='') conv_e.creator = user_admin_key conv_e.recipients_type = conversation_model.PROGRAM conv_e.include_students = True conv_e.put() self.conv_utils.addUser(conversation=conv_e.key, user=conv_e.creator) # Conversation for basically nobody, in which the participants should not # be changed after the conversation's creation, and all users are added. # This is to ensure that users won't be removed if the conversation's # auto_update_users property is False. conv_f = self.conv_utils.createConversation(subject='') conv_f.recipients_type = conversation_model.PROGRAM conv_f.auto_update_users = False conv_f.put() self.conv_utils.addUser(conversation=conv_f.key, user=user_admin_key) self.conv_utils.addUser(conversation=conv_f.key, user=user_mentor_key) self.conv_utils.addUser(conversation=conv_f.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_f.key, user=user_winner_key) # Conversation for that all users fit the criteria to participate in, but # should not be added after the converation's creation. conv_g = self.conv_utils.createConversation(subject='') conv_g.recipients_type = conversation_model.PROGRAM conv_g.include_students = True conv_g.include_mentors = True conv_g.include_admins = True conv_g.auto_update_users = False conv_g.put() # Conversation for program winners, created by a non-winner conv_h = self.conv_utils.createConversation(subject='') conv_h.recipients_type = conversation_model.PROGRAM conv_h.include_winners = True conv_h.put() # Conversation for winners of first organization conv_i = self.conv_utils.createConversation(subject='') conv_i.recipients_type = conversation_model.ORGANIZATION conv_i.organization = org_keys[0] conv_i.include_winners = True conv_i.put() # Conversation for winners of second organization conv_j = self.conv_utils.createConversation(subject='') conv_j.recipients_type = conversation_model.ORGANIZATION conv_j.organization = org_keys[1] conv_j.include_winners = True conv_j.put() # Refresh each user's conversations run_update_task(user_admin_key, self.program_key) run_update_task(user_mentor_key, self.program_key) run_update_task(user_mentor_student_key, self.program_key) run_update_task(user_winner_key, self.program_key) # Test that admin user is in the correct conversations expected_keys = set([conv_a.key, conv_b.key, conv_e.key, conv_f.key]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_admin_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor user is in the correct conversations expected_keys = set([conv_a.key, conv_c.key, conv_d.key, conv_f.key]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor/student user is in the correct conversations expected_keys = set([conv_a.key, conv_d.key, conv_e.key, conv_f.key]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_student_key))) self.assertEqual(expected_keys, actual_keys) # Test that winner user is in the correct conversations expected_keys = set( [conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_j.key]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_winner_key))) self.assertEqual(expected_keys, actual_keys) # Add all three users to all conversations self.conv_utils.addUser(conversation=conv_b.key, user=user_mentor_key) self.conv_utils.addUser(conversation=conv_b.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_c.key, user=user_admin_key) self.conv_utils.addUser(conversation=conv_c.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_d.key, user=user_admin_key) self.conv_utils.addUser(conversation=conv_e.key, user=user_mentor_key) self.conv_utils.addUser(conversation=conv_h.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_h.key, user=user_mentor_key) self.conv_utils.addUser(conversation=conv_h.key, user=user_admin_key) self.conv_utils.addUser(conversation=conv_i.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_i.key, user=user_mentor_key) self.conv_utils.addUser(conversation=conv_i.key, user=user_admin_key) self.conv_utils.addUser(conversation=conv_j.key, user=user_mentor_student_key) self.conv_utils.addUser(conversation=conv_j.key, user=user_mentor_key) self.conv_utils.addUser(conversation=conv_j.key, user=user_admin_key) self.conv_utils.addUser(conversation=conv_a.key, user=user_winner_key) self.conv_utils.addUser(conversation=conv_b.key, user=user_winner_key) self.conv_utils.addUser(conversation=conv_c.key, user=user_winner_key) self.conv_utils.addUser(conversation=conv_i.key, user=user_winner_key) # Test that admin user is in all conversations except for G expected_keys = set([ conv_a.key, conv_b.key, conv_c.key, conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_i.key, conv_j.key ]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_admin_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor user is in all conversations except for G expected_keys = set([ conv_a.key, conv_b.key, conv_c.key, conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_i.key, conv_j.key ]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor/student user is in all conversations except for G expected_keys = set([ conv_a.key, conv_b.key, conv_c.key, conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_i.key, conv_j.key ]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_student_key))) self.assertEqual(expected_keys, actual_keys) # Test that winner user is in all conversations except for G expected_keys = set([ conv_a.key, conv_b.key, conv_c.key, conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_i.key, conv_j.key ]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_winner_key))) self.assertEqual(expected_keys, actual_keys) # Refresh each user's conversations. Because we just added all users to # all conversations, refreshing each user should actually remove them from # conversations they don't belong to. run_update_task(user_admin_key, self.program_key) run_update_task(user_mentor_key, self.program_key) run_update_task(user_mentor_student_key, self.program_key) run_update_task(user_winner_key, self.program_key) # Test that admin user is in the correct conversations expected_keys = set([conv_a.key, conv_b.key, conv_e.key, conv_f.key]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_admin_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor user is in the correct conversations expected_keys = set([conv_a.key, conv_c.key, conv_d.key, conv_f.key]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_key))) self.assertEqual(expected_keys, actual_keys) # Test that mentor/student user is in the correct conversations expected_keys = set([conv_a.key, conv_d.key, conv_e.key, conv_f.key]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_mentor_student_key))) self.assertEqual(expected_keys, actual_keys) # Test that winner user is in the correct conversations expected_keys = set( [conv_d.key, conv_e.key, conv_f.key, conv_h.key, conv_j.key]) actual_keys = set( map( lambda conv_user: conv_user.conversation, gciconversation_logic.queryForProgramAndUser( program=self.program_key, user=user_winner_key))) self.assertEqual(expected_keys, actual_keys)
def confirm_delete(profile): """Deletes the given profile entity and also the user entity if possible. 1. Deletes the profile. 2. Deletes the user entity if no other profiles exist for the user. 3. Removes the user from task notification subscription lists. 4. Replaces GCITask created_by, modified_by, student and GCIComment created_by properties with dummy "melange_deleted_user" profile or user entity. 5. Replaces GCIMessage author with dummy "melange_deleted_user". 6. Replaces GCIConversation creator with dummy "melange_deleted_user". 7. Removes GCIConversationUser entities representing the user's involvement in a GCIConversation. This method implements a giant XG transaction, but should not take a long time because experience has shown that there won't be too much data to modify or delete. Args: profile: GCIProfile entity of the user. """ profile_key = profile.key() program_ndb_key = ndb.Key.from_old_key(profile.program.key()) user_ndb_key = ndb.Key.from_old_key(profile.parent_key()) # Cannot delete the user entity if the user has other profiles, so set it # to False in that case. user_delete = not (profile_logic.hasOtherGCIProfiles(profile) or profile_logic.hasOtherGCIProfiles(profile)) task_sub_q = task_model.GCITask.all().filter('subscribers', profile) task_sub_remove_list = [] for task in task_sub_q.run(): task_sub_remove_list.append(task) tasks_created_by_q = task_model.GCITask.all().filter('created_by', profile) task_created_list = [] for task in tasks_created_by_q.run(): task_created_list.append(task) tasks_modified_by_q = task_model.GCITask.all().filter('modified_by', profile) task_modified_list = [] for task in tasks_modified_by_q.run(): task_modified_list.append(task) tasks_student_q = task_model.GCITask.all().filter('student', profile) task_student_list = [] for task in tasks_student_q.run(): task_student_list.append(task) comments_created_by_q = comment_model.GCIComment.all().filter( 'created_by', profile.user) comments_created_by_list = [] for comment in comments_created_by_q.run(): comments_created_by_list.append(comment) conversations = conversation_logic.queryForProgramAndCreator( program_ndb_key, user_ndb_key) messages = message_logic.queryForUser(user_ndb_key) conversation_users = conversation_logic.queryForProgramAndUser( program_ndb_key, user_ndb_key) dummy_user = user_logic.getOrCreateDummyMelangeDeletedUser() dummy_profile = profile_logic.getOrCreateDummyMelangeDeletedProfile( profile.program) dummy_user_ndb_key = ndb.Key.from_old_key(dummy_user.key()) options = db.create_transaction_options(xg=True) def delete_account_txn(): entities_to_save = set([]) entities_to_del = set([]) # The batch size for query.run() is 20, in most of the cases we have # seen so far the user had a few tasks with subscriptions, created_by, # modified_by etc., so this should still be single datastore hits per # loop. Also, by running the query outside the transaction we may run # into situations of user subscribing to the task or creating or modifying # tasks or performing another activity after this batch fetch. However, # the chances of that happening is very low and can be traded-off for # the bigger problem of half run transactions. for conversation in conversations: conversation.creator = dummy_user_ndb_key conversation.put() for message in messages: message.author = dummy_user_ndb_key message.put() for conversation_user in conversation_users: conversation_user.key.delete() for task in task_sub_remove_list: task.subscribers.remove(profile_key) entities_to_save.add(task) for task in task_created_list: task.created_by = dummy_profile entities_to_save.add(task) for task in task_modified_list: task.modified_by = dummy_profile entities_to_save.add(task) for task in task_student_list: task.student = dummy_profile entities_to_save.add(task) for comment in comments_created_by_list: comment.created_by = dummy_user entities_to_save.add(comment) if profile.student_info: entities_to_del.add(profile.student_info) entities_to_del.add(profile) if user_delete: entities_to_del.add(profile.parent()) db.put(entities_to_save) db.delete(entities_to_del) db.run_in_transaction_options(options, delete_account_txn)
def _getQuery(self): """See ConversationList._getQuery for full specification.""" return gciconversation_logic.queryForProgramAndUser( ndb.Key.from_old_key(self.data.program.key()), ndb.Key.from_old_key(self.data.user.key()))
def confirm_delete(profile): """Deletes the given profile entity and also the user entity if possible. 1. Deletes the profile. 2. Deletes the user entity if no other profiles exist for the user. 3. Removes the user from task notification subscription lists. 4. Replaces GCITask created_by, modified_by, student and GCIComment created_by properties with dummy "melange_deleted_user" profile or user entity. 5. Replaces GCIMessage author with dummy "melange_deleted_user". 6. Replaces GCIConversation creator with dummy "melange_deleted_user". 7. Removes GCIConversationUser entities representing the user's involvement in a GCIConversation. This method implements a giant XG transaction, but should not take a long time because experience has shown that there won't be too much data to modify or delete. Args: profile: GCIProfile entity of the user. """ profile_key = profile.key() program_ndb_key = ndb.Key.from_old_key(profile.program.key()) user_ndb_key = ndb.Key.from_old_key(profile.parent_key()) # Cannot delete the user entity if the user has other profiles, so set it # to False in that case. user_delete = not (profile_logic.hasOtherGCIProfiles(profile) or profile_logic.hasOtherGCIProfiles(profile)) task_sub_q = task_model.GCITask.all().filter('subscribers', profile) task_sub_remove_list = [] for task in task_sub_q.run(): task_sub_remove_list.append(task) tasks_created_by_q = task_model.GCITask.all().filter('created_by', profile) task_created_list = [] for task in tasks_created_by_q.run(): task_created_list.append(task) tasks_modified_by_q = task_model.GCITask.all().filter( 'modified_by', profile) task_modified_list = [] for task in tasks_modified_by_q.run(): task_modified_list.append(task) tasks_student_q = task_model.GCITask.all().filter('student', profile) task_student_list = [] for task in tasks_student_q.run(): task_student_list.append(task) comments_created_by_q = comment_model.GCIComment.all().filter( 'created_by', profile.user) comments_created_by_list = [] for comment in comments_created_by_q.run(): comments_created_by_list.append(comment) conversations = conversation_logic.queryForProgramAndCreator( program_ndb_key, user_ndb_key) messages = message_logic.queryForUser(user_ndb_key) conversation_users = conversation_logic.queryForProgramAndUser( program_ndb_key, user_ndb_key) dummy_user = user_logic.getOrCreateDummyMelangeDeletedUser() dummy_profile = profile_logic.getOrCreateDummyMelangeDeletedProfile( profile.program) dummy_user_ndb_key = ndb.Key.from_old_key(dummy_user.key()) options = db.create_transaction_options(xg=True) def delete_account_txn(): entities_to_save = set([]) entities_to_del = set([]) # The batch size for query.run() is 20, in most of the cases we have # seen so far the user had a few tasks with subscriptions, created_by, # modified_by etc., so this should still be single datastore hits per # loop. Also, by running the query outside the transaction we may run # into situations of user subscribing to the task or creating or modifying # tasks or performing another activity after this batch fetch. However, # the chances of that happening is very low and can be traded-off for # the bigger problem of half run transactions. for conversation in conversations: conversation.creator = dummy_user_ndb_key conversation.put() for message in messages: message.author = dummy_user_ndb_key message.put() for conversation_user in conversation_users: conversation_user.key.delete() for task in task_sub_remove_list: task.subscribers.remove(profile_key) entities_to_save.add(task) for task in task_created_list: task.created_by = dummy_profile entities_to_save.add(task) for task in task_modified_list: task.modified_by = dummy_profile entities_to_save.add(task) for task in task_student_list: task.student = dummy_profile entities_to_save.add(task) for comment in comments_created_by_list: comment.created_by = dummy_user entities_to_save.add(comment) if profile.student_info: entities_to_del.add(profile.student_info) entities_to_del.add(profile) if user_delete: entities_to_del.add(profile.parent()) db.put(entities_to_save) db.delete(entities_to_del) db.run_in_transaction_options(options, delete_account_txn)