def testPendingClientRegs(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"]) brokenStudyList = Client.getPendingClientRegs("*****@*****.**") self.assertEqual(brokenStudyList, [])
def testClientSpecificSettersWithOverride(self): from dao.user import User 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"]) user = User.register("*****@*****.**") self.assertEqual(user.getFirstStudy(), 'testclient') dummyPredModeMap = {'walking': 1.0} dummySection = common.createDummySection(startTime = datetime.now() - timedelta(seconds = 60 * 60), endTime = datetime.now(), startLoc = [-122, 34], endLoc = [-122, 35], predictedMode = dummyPredModeMap) clientSetQuery = client.clientSpecificSetters(user.uuid, dummySection['_id'], dummyPredModeMap) self.assertEqual(clientSetQuery, {'$set': {'test_auto_confirmed': {'mode': 1, 'prob': 1.0}}}) # Apply the change get_section_db().update({'_id': dummySection['_id']}, clientSetQuery) retrievedSection = get_section_db().find_one({'_id': dummySection['_id']}) self.assertEqual(retrievedSection['test_auto_confirmed']['mode'], 1)
def setupClientTest(self): # At this point, the more important test is to execute the query and see # how well it works from dao.user import User from dao.client import Client import tests.common from get_database import get_section_db fakeEmail = "*****@*****.**" client = Client("testclient") client.update(createKey = False) tests.common.makeValid(client) (resultPre, resultReg) = client.preRegister("this_is_the_super_secret_id", fakeEmail) studyList = Client.getPendingClientRegs(fakeEmail) self.assertEqual(studyList, ["testclient"]) user = User.register("*****@*****.**") self.assertEqual(user.getFirstStudy(), 'testclient') dummyPredModeMap = {'walking': 1.0} dummySection = tests.common.createDummySection( startTime = datetime.now() - timedelta(seconds = 60 * 60), endTime = datetime.now(), startLoc = [-122, 34], endLoc = [-122, 35], predictedMode = dummyPredModeMap) return (user, dummySection, dummyPredModeMap)
def testPreRegisterFail(self): client = Client("testclient") client.update(createKey = False) common.makeValid(client) (resultPre, resultReg) = client.preRegister("this_is_the_super_secret_id", "*****@*****.**") self.assertEqual(resultPre, 1) self.assertEqual(resultReg, 0) pendingRegs = Client.getPendingClientRegs("*****@*****.**") self.assertEqual(pendingRegs, ["testclient"])
def testPreRegisterExistingUser(self): from dao.user import User user = User.register("*****@*****.**") client = Client("testclient") client.update(createKey = False) common.makeValid(client) (resultPre, resultReg) = client.preRegister("this_is_the_super_secret_id", "*****@*****.**") self.assertEqual(resultPre, 0) self.assertEqual(resultReg, 1) self.assertEqual(user.getStudy(), ['testclient']) pendingRegs = Client.getPendingClientRegs("*****@*****.**") self.assertEqual(pendingRegs, [])
def testGetSectionFilter(self): from dao.user import User 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"]) user = User.register("*****@*****.**") self.assertEqual(user.getFirstStudy(), 'testclient') self.assertEqual(client.getSectionFilter(user.uuid), []) # Now, set the update timestamp to two weeks ago common.updateUserCreateTime(user.uuid) self.assertEqual(client.getSectionFilter(user.uuid), [{'test_auto_confirmed.prob': {'$lt': 0.9}}])
def setupUserAndClient(self): # At this point, the more important test is to execute the query and see # how well it works from dao.user import User from dao.client import Client import tests.common from datetime import datetime, timedelta from get_database import get_section_db fakeEmail = "*****@*****.**" client = Client("gamified") client.update(createKey = False) tests.common.makeValid(client) (resultPre, resultReg) = client.preRegister("this_is_the_super_secret_id", fakeEmail) studyList = Client.getPendingClientRegs(fakeEmail) self.assertEqual(studyList, ["gamified"]) user = User.register("*****@*****.**") self.assertEqual(user.getFirstStudy(), 'gamified') self.user = user
def setupUserAndClient(self): # At this point, the more important test is to execute the query and see # how well it works from dao.user import User from dao.client import Client import tests.common from datetime import datetime, timedelta from get_database import get_section_db fakeEmail = "*****@*****.**" client = Client("gamified") client.update(createKey=False) tests.common.makeValid(client) (resultPre, resultReg) = client.preRegister("this_is_the_super_secret_id", fakeEmail) studyList = Client.getPendingClientRegs(fakeEmail) self.assertEqual(studyList, ["gamified"]) user = User.register("*****@*****.**") self.assertEqual(user.getFirstStudy(), 'gamified') self.user = user
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)