def testPendingClientRegsDelete(self): fakeEmail = "*****@*****.**" client = Client("testclient") client.update(createKey = False) common.makeValid(client) (resultPre, resultReg) = client.preRegister("this_is_the_super_secret_id", fakeEmail) studyList = Client.getPendingClientRegs(fakeEmail) self.assertEqual(studyList, ["testclient"]) Client.deletePendingClientRegs(fakeEmail) afterDelList = Client.getPendingClientRegs(fakeEmail) self.assertEqual(afterDelList, [])
def register(userEmail): import uuid from datetime import datetime from dao.client import Client # We are accessing three databases here: # - The list of pending registrations (people who have filled out demographic # information but not installed the app) # - The mapping from the userEmail to the user UUID # - The mapping from the UUID to other profile information about the user # The first two are indexed by the user email. We will use the same field # name in both to indicate that it is a shared key. This also allows us to # have a simple query that we can reuse. userEmailQuery = {'user_email': userEmail} # First, we construct the email -> uuid mapping and store it in the appropriate database. # At this point, we don't know or care whether the user is part of a study # We also store a create timestamp just because that's always a good idea # What happens if the user calls register() again? Do we want to generate a new UUID? # Do we want to update the create timestamp? # For now, let's assume that the answer to both of those questions is yes, # because that allows us to use upsert :) # A bonus fix is that if something is messed up in the DB, calling create again will fix it. # This is the UUID that will be stored in the trip database # in order to do some fig leaf of anonymity # If we have an existing entry, should we change the UUID or not? If we # change the UUID, then there will be a break in the trip history. Let's # change for now since it makes the math easier. anonUUID = uuid.uuid3(uuid.NAMESPACE_URL, "mailto:%s" % userEmail.encode("UTF-8")) emailUUIDObject = { 'user_email': userEmail, 'uuid': anonUUID, 'update_ts': datetime.now() } writeResultMap = get_uuid_db().update(userEmailQuery, emailUUIDObject, upsert=True) # Note, if we did want the create_ts to not be overwritten, we can use the # writeResult to decide how to deal with the values # Now, we look to see if the user is part of a study. We can either store # this information in the profile database, or the mapping, or both. For now, # let us store this in the profile database since it is sufficient for it to # be associated with the UUID, we anticipate using it for customization, and # we assume that other customization stuff will be stored in the profile. # We could also assume that we will create the profile if we created the map # and update if we updated. But that has some reliability issues. For # example, what if creating the map succeeded but creating the profile # failed? Subsequently calling the method again to try and fix the profile # will continue to fail because we will be trying to update. # Much better to deal with it separately by doing a separate upsert # Second decision: what do we do if the user is not part of a study? Create a # profile anyway with an empty list, or defer the creation of the profile? # # Decision: create profile with empty list for two reasons: # a) for most of the functions, we want to use the profile data. We should # only use the email -> uuid map in the API layer to get the UUID, and use # the UUID elsewhere. So we need to have profiles for non-study participants # as well. # b) it will also make the scripts to update the profile in the background # easier to write. They won't have to query the email -> UUID database and # create the profile if it doesn't exist - they can just work off the profile # database. # TODO: Write a script that periodically goes through and identifies maps # that don't have an associated profile and fix them study_list = Client.getPendingClientRegs(userEmail) writeResultProfile = User.createProfile(anonUUID, datetime.now(), study_list) if 'err' not in writeResultProfile: # update was successful! # Either upserted or updatedExisting will be true # We can now cleanup the entry from the pending database # Note that we could also move this to a separate cleanup script because # eventual consistency is good enough for us # If there is a profile entry for a particular signup, then delete it Client.deletePendingClientRegs(userEmail) return User.fromUUID(anonUUID)
def register(userEmail): import uuid from datetime import datetime from dao.client import Client # We are accessing three databases here: # - The list of pending registrations (people who have filled out demographic # information but not installed the app) # - The mapping from the userEmail to the user UUID # - The mapping from the UUID to other profile information about the user # The first two are indexed by the user email. We will use the same field # name in both to indicate that it is a shared key. This also allows us to # have a simple query that we can reuse. userEmailQuery = {'user_email': userEmail} # First, we construct the email -> uuid mapping and store it in the appropriate database. # At this point, we don't know or care whether the user is part of a study # We also store a create timestamp just because that's always a good idea # What happens if the user calls register() again? Do we want to generate a new UUID? # Do we want to update the create timestamp? # For now, let's assume that the answer to both of those questions is yes, # because that allows us to use upsert :) # A bonus fix is that if something is messed up in the DB, calling create again will fix it. # This is the UUID that will be stored in the trip database # in order to do some fig leaf of anonymity # If we have an existing entry, should we change the UUID or not? If we # change the UUID, then there will be a break in the trip history. Let's # change for now since it makes the math easier. anonUUID = uuid.uuid3(uuid.NAMESPACE_URL, "mailto:%s" % userEmail.encode("UTF-8")) emailUUIDObject = {'user_email': userEmail, 'uuid': anonUUID, 'update_ts': datetime.now()} writeResultMap = get_uuid_db().update(userEmailQuery, emailUUIDObject, upsert=True) # Note, if we did want the create_ts to not be overwritten, we can use the # writeResult to decide how to deal with the values # Now, we look to see if the user is part of a study. We can either store # this information in the profile database, or the mapping, or both. For now, # let us store this in the profile database since it is sufficient for it to # be associated with the UUID, we anticipate using it for customization, and # we assume that other customization stuff will be stored in the profile. # We could also assume that we will create the profile if we created the map # and update if we updated. But that has some reliability issues. For # example, what if creating the map succeeded but creating the profile # failed? Subsequently calling the method again to try and fix the profile # will continue to fail because we will be trying to update. # Much better to deal with it separately by doing a separate upsert # Second decision: what do we do if the user is not part of a study? Create a # profile anyway with an empty list, or defer the creation of the profile? # # Decision: create profile with empty list for two reasons: # a) for most of the functions, we want to use the profile data. We should # only use the email -> uuid map in the API layer to get the UUID, and use # the UUID elsewhere. So we need to have profiles for non-study participants # as well. # b) it will also make the scripts to update the profile in the background # easier to write. They won't have to query the email -> UUID database and # create the profile if it doesn't exist - they can just work off the profile # database. # TODO: Write a script that periodically goes through and identifies maps # that don't have an associated profile and fix them study_list = Client.getPendingClientRegs(userEmail) writeResultProfile = User.createProfile(anonUUID, datetime.now(), study_list) if 'err' not in writeResultProfile: # update was successful! # Either upserted or updatedExisting will be true # We can now cleanup the entry from the pending database # Note that we could also move this to a separate cleanup script because # eventual consistency is good enough for us # If there is a profile entry for a particular signup, then delete it Client.deletePendingClientRegs(userEmail) return User.fromUUID(anonUUID)