def setUp(self): '''Performed before every test''' super(TestExploreMethods, self).setUp() # create a facility and user that can be referred to in models across tests self.facility = Facility(name=self.FACILITY) self.facility.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user1.set_password(self.PASSWORD) self.user1.save() #add one exercise self.original_exerciselog = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog.points = self.ORIGINAL_POINTS self.original_exerciselog.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog.latest_activity_timestamp = self.TIMESTAMP self.original_exerciselog.completion_timestamp = self.TIMESTAMP self.original_exerciselog.save() #create a request factory for later instantiation of request self.factory = RequestFactory()
def setUp(self): '''Performed before every test''' super(TestHelperMethods, self).setUp() # user + facility self.facility = Facility(name=self.FACILITY) self.facility.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user1.set_password(self.PASSWORD) self.user1.save() # insert some exercise activity self.original_exerciselog1 = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog1.points = self.ORIGINAL_POINTS self.original_exerciselog1.attempts = self.ORIGINAL_POINTS self.original_exerciselog1.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog1.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog1.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog1.struggling = False self.original_exerciselog1.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user=self.user1) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.struggling = False self.original_exerciselog2.save()
class TestExploreMethods(KALiteTestCase): ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 3 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "number_line" USERNAME1 = "test_user_explore_1" PASSWORD = "******" FACILITY = "Test Facility Explore" TIMESTAMP = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) def setUp(self): '''Performed before every test''' # create a facility and user that can be referred to in models across tests self.facility = Facility(name=self.FACILITY) self.facility.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user1.set_password(self.PASSWORD) self.user1.save() #add one exercise self.original_exerciselog = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog.points = self.ORIGINAL_POINTS self.original_exerciselog.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog.latest_activity_timestamp = self.TIMESTAMP self.original_exerciselog.completion_timestamp = self.TIMESTAMP self.original_exerciselog.save() #create a request factory for later instantiation of request self.factory = RequestFactory() def test_explore_overall(self): '''get_explore_recommendations()''' #create a request object and set the language attribute request = self.factory.get('/content_recommender?explore=true') request.language = settings.LANGUAGE_CODE actual = get_explore_recommendations(self.user1, request) self.assertEqual(actual[0].get("interest_topic").get("id"), "arithmetic")
def setUp(self): '''Performed before every test''' super(TestNextMethods, self).setUp() self.EXERCISE_ID = self.content_exercises[0].id self.EXERCISE_ID2 = self.content_exercises[1].id self.EXERCISE_ID_STRUGGLE = self.content_exercises[2].id # create a facility and user that can be referred to in models across tests self.facility = Facility(name=self.FACILITY) self.facility.save() self.facilitygroup = FacilityGroup(name=self.GROUP, description="", facility=self.facility) self.facilitygroup.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility, group=self.facilitygroup) self.user1.set_password(self.PASSWORD) self.user1.save() self.user2 = FacilityUser(username=self.USERNAME2, facility=self.facility, group=self.facilitygroup) self.user2.set_password(self.PASSWORD) self.user2.save() #user 1 - now add some mock data into exercise log self.original_exerciselog = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog.points = self.ORIGINAL_POINTS self.original_exerciselog.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog.save() #user 2 self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID, user = self.user2, struggling=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user = self.user2, struggling=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.save() self.original_exerciselog3 = ExerciseLog(exercise_id=self.EXERCISE_ID_STRUGGLE, user = self.user2, struggling=True) self.original_exerciselog3.points = self.ORIGINAL_POINTS self.original_exerciselog3.attempts = self.ORIGINAL_POINTS #intentionally made larger to trigger struggling self.original_exerciselog3.streak_progress = 0 self.original_exerciselog3.attempts = 100 self.original_exerciselog3.latest_activity_timestamp = self.TIMESTAMP_STRUGGLE self.original_exerciselog3.completion_timestamp = self.TIMESTAMP_STRUGGLE self.original_exerciselog3.save()
def setUp(self): """Performed before every test""" # a brand new user self.facility = Facility(name=self.FACILITY) self.facility.save() self.user_with_no_activity = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user_with_no_activity.set_password(self.PASSWORD) self.user_with_no_activity.save() # a user with valid exercises self.user_with_activity = FacilityUser(username=self.USERNAME2, facility=self.facility) self.user_with_activity.set_password(self.PASSWORD) self.user_with_activity.save() # a user with invalid exercises self.user_with_old_activity = FacilityUser(username=self.USERNAME3, facility=self.facility) self.user_with_old_activity.set_password(self.PASSWORD) self.user_with_old_activity.save() # add some exercises for second user (both incomplete) self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user_with_activity, complete=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user=self.user_with_activity, complete=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.save() self.original_exerciselog3 = ExerciseLog(exercise_id=self.INVALID_EXERCISE_ID, user=self.user_with_old_activity, complete=False) self.original_exerciselog3.points = self.ORIGINAL_POINTS self.original_exerciselog3.attempts = self.ORIGINAL_POINTS self.original_exerciselog3.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog3.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog3.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog3.save()
def compute_total_points(user): if user.is_teacher: return None else: return (VideoLog.get_points_for_user(user) + ExerciseLog.get_points_for_user(user) + ContentLog.get_points_for_user(user))
def update_all_distributed_callback(request): """ """ if request.method != "POST": raise PermissionDenied("Only POST allowed to this URL endpoint.") videos = json.loads(request.POST["video_logs"]) exercises = json.loads(request.POST["exercise_logs"]) user = FacilityUser.objects.get(id=request.POST["user_id"]) node_ids = [node.get("id") for node in get_content_items()] # Save videos n_videos_uploaded = 0 for video in videos: video_id = video['video_id'] youtube_id = video['youtube_id'] # Only save video logs for videos that we recognize. if video_id not in node_ids: logging.warn("Skipping unknown video %s" % video_id) continue try: (vl, _) = VideoLog.get_or_initialize(user=user, video_id=video_id) # has to be that video_id, could be any youtube_id for key,val in video.iteritems(): setattr(vl, key, val) logging.debug("Saving video log for %s: %s" % (video_id, vl)) vl.save() n_videos_uploaded += 1 except KeyError: # logging.error("Could not save video log for data with missing values: %s" % video) except Exception as e: error_message = _("Unexpected error importing videos: %(err_msg)s") % {"err_msg": e} return JsonResponseMessageError(error_message, status=500) # Save exercises n_exercises_uploaded = 0 for exercise in exercises: # Only save video logs for videos that we recognize. if exercise['exercise_id'] not in node_ids: logging.warn("Skipping unknown video %s" % exercise['exercise_id']) continue try: (el, _) = ExerciseLog.get_or_initialize(user=user, exercise_id=exercise["exercise_id"]) for key,val in exercise.iteritems(): setattr(el, key, val) logging.debug("Saving exercise log for %s: %s" % (exercise['exercise_id'], el)) el.save() n_exercises_uploaded += 1 except KeyError: logging.error("Could not save exercise log for data with missing values: %s" % exercise) except Exception as e: error_message = _("Unexpected error importing exercises: %(err_msg)s") % {"err_msg": e} return JsonResponseMessageError(error_message, status=500) return JsonResponseMessageSuccess(_("Uploaded %(num_exercises)d exercises and %(num_videos)d videos") % { "num_exercises": n_exercises_uploaded, "num_videos": n_videos_uploaded, })
def update_all_distributed_callback(request): """ """ if request.method != "POST": raise PermissionDenied("Only POST allowed to this URL endpoint.") videos = json.loads(request.POST["video_logs"]) exercises = json.loads(request.POST["exercise_logs"]) user = FacilityUser.objects.get(id=request.POST["user_id"]) node_cache = get_node_cache() # Save videos n_videos_uploaded = 0 for video in videos: video_id = video['video_id'] youtube_id = video['youtube_id'] # Only save video logs for videos that we recognize. if video_id not in node_cache["Content"]: logging.warn("Skipping unknown video %s" % video_id) continue try: (vl, _) = VideoLog.get_or_initialize(user=user, video_id=video_id) # has to be that video_id, could be any youtube_id for key,val in video.iteritems(): setattr(vl, key, val) logging.debug("Saving video log for %s: %s" % (video_id, vl)) vl.save() n_videos_uploaded += 1 except KeyError: # logging.error("Could not save video log for data with missing values: %s" % video) except Exception as e: error_message = _("Unexpected error importing videos: %(err_msg)s") % {"err_msg": e} return JsonResponseMessageError(error_message) # Save exercises n_exercises_uploaded = 0 for exercise in exercises: # Only save video logs for videos that we recognize. if exercise['exercise_id'] not in node_cache['Exercise']: logging.warn("Skipping unknown video %s" % exercise['exercise_id']) continue try: (el, _) = ExerciseLog.get_or_initialize(user=user, exercise_id=exercise["exercise_id"]) for key,val in exercise.iteritems(): setattr(el, key, val) logging.debug("Saving exercise log for %s: %s" % (exercise['exercise_id'], el)) el.save() n_exercises_uploaded += 1 except KeyError: logging.error("Could not save exercise log for data with missing values: %s" % exercise) except Exception as e: error_message = _("Unexpected error importing exercises: %(err_msg)s") % {"err_msg": e} return JsonResponseMessageError(error_message) return JsonResponseMessageSuccess(_("Uploaded %(num_exercises)d exercises and %(num_videos)d videos") % { "num_exercises": n_exercises_uploaded, "num_videos": n_videos_uploaded, })
def test_unit_switch(self): """ Tests that when a unit is switched a negative value gift_card is created to nullify current user points. """ set_current_unit_settings_value(self.facility.id, 2) exerciselog = ExerciseLog(user=self.student, exercise_id="addition_1", streak_progress=8, attempts=8, points=10, complete=True) exerciselog.save() # Set to unit 3 to trigger signal. set_current_unit_settings_value(self.facility.id, 3) transactionlogs = StoreTransactionLog.objects.filter( user=self.student, context_id="2", context_type="unit_points_reset", item="gift_card") self.assertTrue(len(transactionlogs) == 1) for transactionlog in transactionlogs: self.assertTrue(transactionlog.value == -exerciselog.points) newexerciselog = ExerciseLog(user=self.student, exercise_id="addition_2", streak_progress=8, attempts=8, points=20, complete=True) newexerciselog.save() # Move back a unit set_current_unit_settings_value(self.facility.id, 2) # Check that newexerciselog has been nullified transactionlogs = StoreTransactionLog.objects.filter( user=self.student, context_id="3", context_type="unit_points_reset", item="gift_card") self.assertTrue(len(transactionlogs) == 1) for transactionlog in transactionlogs: self.assertTrue(transactionlog.value == -newexerciselog.points) # Check that the original transactionlog has been deleted transactionlogs = StoreTransactionLog.objects.filter( user=self.student, context_id="2", context_type="unit_points_reset", item="gift_card") self.assertTrue(len(transactionlogs) == 0)
def before_scenario(context, scenario): base_before_scenario(context, scenario) if "with_progress" in context.tags: user = FacilityUser.objects.get(username=context.user, facility=getattr( context, "facility", None)) exercises = random.sample(get_exercise_cache().keys(), 2) for exercise in exercises: log = ExerciseLog( exercise_id=exercise, user=user, streak_progress=50, attempts=15, latest_activity_timestamp=datetime.datetime.now()) log.save() context.exercises = exercises videos = random.sample(get_content_cache().keys(), 2) for video in videos: log = VideoLog(youtube_id=video, video_id=video, user=user, total_seconds_watched=100, points=600, latest_activity_timestamp=datetime.datetime.now()) log.save() context.videos = videos
def _setup(self, num_logs=50, **kwargs): super(OneHundredRandomLogUpdates, self)._setup(**kwargs) node_cache = get_node_cache() try: self.user = FacilityUser.objects.get(username=self.username) except: #take username from ExerciseLog all_exercises = ExerciseLog.objects.all() self.user = FacilityUser.objects.get(id=all_exercises[0].user_id) print self.username, " not in FacilityUsers, using ", self.user self.num_logs = num_logs #give the platform a chance to cache the logs ExerciseLog.objects.filter(user=self.user).delete() for x in range(num_logs): while True: ex_idx = int(self.random.random() * len(node_cache["Exercise"].keys())) ex_id = node_cache["Exercise"].keys()[ex_idx] if not ExerciseLog.objects.filter(user=self.user, exercise_id=ex_id): break ex = ExerciseLog(user=self.user, exercise_id=ex_id) ex.save() self.exercise_list = ExerciseLog.objects.filter(user=self.user) self.exercise_count = self.exercise_list.count() VideoLog.objects.filter(user=self.user).delete() for x in range(num_logs): while True: vid_idx = int(self.random.random() * len(node_cache["Content"].keys())) vid_id = node_cache["Content"].keys()[vid_idx] if not VideoLog.objects.filter(user=self.user, video_id=vid_id): break vid = VideoLog(user=self.user, video_id=vid_id) vid.save() self.video_list = VideoLog.objects.filter(user=self.user) self.video_count = self.video_list.count()
def before_feature(context, feature): base_before_feature(context, feature) if "with_progress" in feature.tags: user = FacilityUser.objects.get(username=context.user, facility=getattr(context, "facility", None)) exercises = random.sample(get_exercise_cache().keys(), 2) for exercise in exercises: log = ExerciseLog( exercise_id=exercise, user=user, streak_progress=50, attempts=15, latest_activity_timestamp=datetime.datetime.now() ) log.save() context.exercises = exercises videos = random.sample(get_content_cache().keys(), 2) for video in videos: log = VideoLog( youtube_id=video, video_id=video, user=user, total_seconds_watched=100, points=600, latest_activity_timestamp=datetime.datetime.now() ) log.save() context.videos = videos
def before_scenario(context, scenario): base_before_scenario(context, scenario) if "with_progress" in context.tags: user = FacilityUser.objects.get(username=context.user, facility=getattr(context, "facility", None)) exercises = get_random_content(kinds=["Exercise"], limit=2) for exercise in exercises: log = ExerciseLog( exercise_id=exercise.get("id"), user=user, streak_progress=50, attempts=15, latest_activity_timestamp=datetime.datetime.now() ) log.save() context.exercises = exercises videos = get_random_content(kinds=["Video"], limit=2) for video in videos: log = VideoLog( youtube_id=video.get("id"), video_id=video.get("id"), user=user, total_seconds_watched=100, points=600, latest_activity_timestamp=datetime.datetime.now() ) log.save() context.videos = videos
def setUp(self): '''Performed before every test''' #a brand new user self.facility = Facility(name=self.FACILITY) self.facility.save() self.user_with_no_activity = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user_with_no_activity.set_password(self.PASSWORD) self.user_with_no_activity.save() #a user with valid exercises self.user_with_activity = FacilityUser(username=self.USERNAME2, facility=self.facility) self.user_with_activity.set_password(self.PASSWORD) self.user_with_activity.save() #add some exercises for second user (both incomplete) self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user_with_activity, complete=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user=self.user_with_activity, complete=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.save()
def create_playlist_progress(cls, user, quiz=True): default_playlist = "g4_u401_p1" playlist = [pl for pl in Playlist.all() if pl.id == default_playlist] assert ( playlist[0].id == default_playlist ), "Unexpected playlist ID. Update tests to match new playlists.json" playlist = playlist[0] # Creating one specific entry for a specific item in the playlist # Note (aron): The new nalanda playlists don't have any videos now # So I commented those out # VideoLog(**{ # "user": user, # "video_id": "nFsQA2Zvy1o", # "youtube_id": "nFsQA2Zvy1o", # "total_seconds_watched": 290, # "points": 750, # "complete": True, # }).save() ExerciseLog( **{ "user": user, "exercise_id": "telling_time", "streak_progress": 50, "attempts": 25, "points": 100, "complete": False, "struggling": True, }).save() if quiz: QuizLog( **{ "user": user, "quiz": default_playlist, "complete": True, "attempts": 1, "response_log": "[4]", "total_correct": 4, "total_number": 6, }).save() return playlist
def setUp(self): '''Performed before every test''' # create a facility and user that can be referred to in models across tests self.facility = Facility(name=self.FACILITY) self.facility.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user1.set_password(self.PASSWORD) self.user1.save() #add one exercise self.original_exerciselog = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog.points = self.ORIGINAL_POINTS self.original_exerciselog.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog.latest_activity_timestamp = self.TIMESTAMP self.original_exerciselog.completion_timestamp = self.TIMESTAMP self.original_exerciselog.save() #create a request factory for later instantiation of request self.factory = RequestFactory()
class TestHelperMethods(KALiteTestCase): TOPIC_TO_TEST = 'decimal-to-fraction-pre-alg' #actually a subtopic, but is still valid ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 3 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "number_line" EXERCISE_ID2 = "radius_diameter_and_circumference" USERNAME1 = "test_user_helper1" PASSWORD = "******" FACILITY = "Test Facility Next Steps" TIMESTAMP_LATER = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) TIMESTAMP_EARLY = datetime.datetime(2014, 10, 8, 15, 59, 59, 370290) def setUp(self): '''Performed before every test''' #user + facility self.facility = Facility(name=self.FACILITY) self.facility.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user1.set_password(self.PASSWORD) self.user1.save() #insert some exercise activity self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.struggling = False self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user=self.user1) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.struggling = False self.original_exerciselog2.save() def tearDown(self): '''Performed after each test''' self.user_with_activity = None self.user_with_no_activity = None def test_exercises_from_topics(self): '''get_exercises_from_topics()''' expected = [ 'converting_fractions_to_decimals', 'converting_decimals_to_fractions_1', 'converting_decimals_to_fractions_2' ] actual = get_exercises_from_topics([self.TOPIC_TO_TEST]) self.assertEqual(expected, actual) def test_most_recent_exercises(self): '''get_most_recent_exercises()''' first = "radius_diameter_and_circumference" second = "number_line" expected = [unicode(first, 'utf-8'), unicode(second, 'utf-8')] actual = get_most_recent_exercises(self.user1) self.assertSequenceEqual(expected, actual)
class TestNextMethods(KALiteTestCase): ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 0 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "number_line" EXERCISE_ID2 = "radius_diameter_and_circumference" EXERCISE_ID_STRUGGLE = "counting-out-1-20-objects" USERNAME1 = "test_user_next1" USERNAME2 = "test_user_next2" PASSWORD = "******" FACILITY = "Test Facility Next Steps" TIMESTAMP_LATER = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) TIMESTAMP_EARLY = datetime.datetime(2014, 10, 8, 15, 59, 59, 370290) TIMESTAMP_STRUGGLE = datetime.datetime(2014, 9, 17, 17, 43, 36, 405260) GROUP = 'Test Group Next Steps' def setUp(self): '''Performed before every test''' # create a facility and user that can be referred to in models across tests self.facility = Facility(name=self.FACILITY) self.facility.save() self.facilitygroup = FacilityGroup(name=self.GROUP, description="", facility=self.facility) self.facilitygroup.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility, group=self.facilitygroup) self.user1.set_password(self.PASSWORD) self.user1.save() self.user2 = FacilityUser(username=self.USERNAME2, facility=self.facility, group=self.facilitygroup) self.user2.set_password(self.PASSWORD) self.user2.save() #user 1 - now add some mock data into exercise log self.original_exerciselog = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog.points = self.ORIGINAL_POINTS self.original_exerciselog.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog.save() #user 2 self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user2, struggling=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user=self.user2, struggling=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.save() self.original_exerciselog3 = ExerciseLog( exercise_id=self.EXERCISE_ID_STRUGGLE, user=self.user2, struggling=True) self.original_exerciselog3.points = self.ORIGINAL_POINTS self.original_exerciselog3.attempts = self.ORIGINAL_POINTS #intentionally made larger to trigger struggling self.original_exerciselog3.streak_progress = 0 self.original_exerciselog3.attempts = 100 self.original_exerciselog3.latest_activity_timestamp = self.TIMESTAMP_STRUGGLE self.original_exerciselog3.completion_timestamp = self.TIMESTAMP_STRUGGLE self.original_exerciselog3.save() #set all other exercise's struggling param to false def test_group_recommendations(self): '''get_group_recommendations()''' user = FacilityUser.objects.filter(username=self.USERNAME1)[0] expected = ["radius_diameter_and_circumference"] actual = get_group_recommendations(user) self.assertEqual(expected, actual, "Group recommendations incorrect.") def test_struggling(self): '''get_struggling_exercises()''' expected = [unicode(self.EXERCISE_ID_STRUGGLE, 'utf-8')] actual = get_struggling_exercises( FacilityUser.objects.filter( username=self.USERNAME2).order_by('-id')[0]) self.assertSequenceEqual(expected, actual, "Struggling return incorrect.") def test_exercise_prereqs(self): '''get_exercise_prereqs()''' ex_id = 'equivalent_fractions' expected = ['visualizing-equivalent-fractions'] actual = get_exercise_prereqs([ex_id]) self.assertEqual(expected, actual, "Exercise Prereqs incorrect.")
class TestResumeMethods(KALiteTestCase): ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 3 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "number_line" EXERCISE_ID2 = "radius_diameter_and_circumference" INVALID_EXERCISE_ID = "s_diameter_and_circumference" USERNAME1 = "test_user_resume1" USERNAME2 = "test_user_resume2" USERNAME3 = "test_user_resume3" PASSWORD = "******" FACILITY = "Test Facility Resume" TIMESTAMP_LATER = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) TIMESTAMP_EARLY = datetime.datetime(2014, 10, 8, 15, 59, 59, 370290) def setUp(self): """Performed before every test""" # a brand new user self.facility = Facility(name=self.FACILITY) self.facility.save() self.user_with_no_activity = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user_with_no_activity.set_password(self.PASSWORD) self.user_with_no_activity.save() # a user with valid exercises self.user_with_activity = FacilityUser(username=self.USERNAME2, facility=self.facility) self.user_with_activity.set_password(self.PASSWORD) self.user_with_activity.save() # a user with invalid exercises self.user_with_old_activity = FacilityUser(username=self.USERNAME3, facility=self.facility) self.user_with_old_activity.set_password(self.PASSWORD) self.user_with_old_activity.save() # add some exercises for second user (both incomplete) self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user_with_activity, complete=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user=self.user_with_activity, complete=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.save() self.original_exerciselog3 = ExerciseLog( exercise_id=self.INVALID_EXERCISE_ID, user=self.user_with_old_activity, complete=False) self.original_exerciselog3.points = self.ORIGINAL_POINTS self.original_exerciselog3.attempts = self.ORIGINAL_POINTS self.original_exerciselog3.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog3.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog3.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog3.save() def test_get_most_recent_incomplete_item(self): '''get_most_recent_incomplete_item()''' # test user with activity first expected = { "id": unicode(self.EXERCISE_ID2, 'utf-8'), "timestamp": self.TIMESTAMP_LATER, "kind": "Exercise" } actual = get_most_recent_incomplete_item(self.user_with_activity) self.assertEqual(expected, actual) # new user just created (no activity logged) self.assertIsNone( get_most_recent_incomplete_item(user=self.user_with_no_activity)) def test_get_empty_list_invalid_resume(self): # Used to mock a request object that is only queried for its 'lang' property. class MicroMock(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) # test user with invalid activity actual = get_resume_recommendations(self.user_with_old_activity, MicroMock(language="en")) # ensure that no recommendations are returned self.assertEqual(len(actual), 0)
class TestHelperMethods(KALiteTestCase): ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 3 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "number_line" EXERCISE_ID2 = "radius_diameter_and_circumference" USERNAME1 = "test_user_helper1" PASSWORD = "******" FACILITY = "Test Facility Next Steps" TIMESTAMP_LATER = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) TIMESTAMP_EARLY = datetime.datetime(2014, 10, 8, 15, 59, 59, 370290) def setUp(self): '''Performed before every test''' # user + facility self.facility = Facility(name=self.FACILITY) self.facility.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user1.set_password(self.PASSWORD) self.user1.save() # insert some exercise activity self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.struggling = False self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user=self.user1) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.struggling = False self.original_exerciselog2.save() def tearDown(self): '''Performed after each test''' self.user_with_activity = None self.user_with_no_activity = None def test_exercises_from_topics(self): '''get_exercises_from_topics()''' expected = [u'counting-out-1-20-objects', u'counting-objects', u'one-more--one-less'] actual = get_exercises_from_topics(['cc-early-math-counting']) self.assertEqual(expected, actual) def test_most_recent_exercises(self): '''get_most_recent_exercises()''' first = "radius_diameter_and_circumference" second = "number_line" expected = [unicode(first, 'utf-8'), unicode(second, 'utf-8')] actual = get_most_recent_exercises(self.user1) self.assertSequenceEqual(expected, actual)
class TestNextMethods(KALiteTestCase): ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 0 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "number_line" EXERCISE_ID2 = "radius_diameter_and_circumference" EXERCISE_ID_STRUGGLE = "counting-out-1-20-objects" USERNAME1 = "test_user_next1" USERNAME2 = "test_user_next2" PASSWORD = "******" FACILITY = "Test Facility Next Steps" TIMESTAMP_LATER = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) TIMESTAMP_EARLY = datetime.datetime(2014, 10, 8, 15, 59, 59, 370290) TIMESTAMP_STRUGGLE = datetime.datetime(2014, 9, 17, 17, 43, 36, 405260) GROUP = 'Test Group Next Steps' def setUp(self): '''Performed before every test''' # create a facility and user that can be referred to in models across tests self.facility = Facility(name=self.FACILITY) self.facility.save() self.facilitygroup = FacilityGroup(name=self.GROUP, description="", facility=self.facility) self.facilitygroup.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility, group=self.facilitygroup) self.user1.set_password(self.PASSWORD) self.user1.save() self.user2 = FacilityUser(username=self.USERNAME2, facility=self.facility, group=self.facilitygroup) self.user2.set_password(self.PASSWORD) self.user2.save() #user 1 - now add some mock data into exercise log self.original_exerciselog = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog.points = self.ORIGINAL_POINTS self.original_exerciselog.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog.save() #user 2 self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID, user = self.user2, struggling=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user = self.user2, struggling=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.save() self.original_exerciselog3 = ExerciseLog(exercise_id=self.EXERCISE_ID_STRUGGLE, user = self.user2, struggling=True) self.original_exerciselog3.points = self.ORIGINAL_POINTS self.original_exerciselog3.attempts = self.ORIGINAL_POINTS #intentionally made larger to trigger struggling self.original_exerciselog3.streak_progress = 0 self.original_exerciselog3.attempts = 100 self.original_exerciselog3.latest_activity_timestamp = self.TIMESTAMP_STRUGGLE self.original_exerciselog3.completion_timestamp = self.TIMESTAMP_STRUGGLE self.original_exerciselog3.save() #set all other exercise's struggling param to false def test_group_recommendations(self): '''get_group_recommendations()''' user = FacilityUser.objects.filter(username=self.USERNAME1)[0] expected = ["radius_diameter_and_circumference"] actual = get_group_recommendations(user) self.assertEqual(expected, actual, "Group recommendations incorrect.") def test_struggling(self): '''get_struggling_exercises()''' expected = [unicode(self.EXERCISE_ID_STRUGGLE, 'utf-8')] actual = get_struggling_exercises(FacilityUser .objects .filter(username=self.USERNAME2) .order_by('-id')[0]) self.assertSequenceEqual(expected, actual, "Struggling return incorrect.") def test_exercise_prereqs(self): '''get_exercise_prereqs()''' ex_id = 'equivalent_fractions' expected = ['visualizing-equivalent-fractions'] actual = get_exercise_prereqs([ex_id]) self.assertEqual(expected, actual, "Exercise Prereqs incorrect.")
def generate_fake_exercise_logs(facility_user=None, topics=topics, start_date=datetime.datetime.now() - datetime.timedelta(days=30 * 6)): """Add exercise logs for the given topics, for each of the given users. If no users are given, they are created. If no topics exist, they are taken from the list at the top of this file. By default, users start learning randomly between 6 months ago and now. """ date_diff = datetime.datetime.now() - start_date exercise_logs = [] user_logs = [] # It's not a user: probably a list. # Recursive case if not hasattr(facility_user, "username"): # It's NONE :-/ generate the users first! if not facility_user: (facility_user, _, _) = generate_fake_facility_users() for topic in topics: for user in facility_user: (elogs, ulogs) = generate_fake_exercise_logs(facility_user=user, topics=[topic], start_date=start_date) exercise_logs.append(elogs) user_logs.append(ulogs) # Actually generate! else: # Get (or create) user type try: user_settings = json.loads(facility_user.notes) except: user_settings = sample_user_settings() facility_user.notes = json.dumps(user_settings) facility_user.save() date_diff_started = datetime.timedelta(seconds=datediff(date_diff, units="seconds") * user_settings["time_in_program"]) # when this user started in the program, relative to NOW for topic in topics: # Get all exercises related to the topic exercises = get_topic_exercises(topic_id=topic) # Problem: # Not realistic for students to have lots of unfinished exercises. # If they start them, they tend to get stuck, right? # # So, need to make it more probable that they will finish an exercise, # and less probable that they start one. # # What we need is P(streak|started), not P(streak) # Probability of doing any particular exercise p_exercise = probability_of(qty="exercise", user_settings=user_settings) logging.debug("# exercises: %d; p(exercise)=%4.3f, user settings: %s\n" % (len(exercises), p_exercise, json.dumps(user_settings))) # of exercises is related to for j, exercise in enumerate(exercises): if random.random() > p_exercise: continue # Probability of completing this exercise, and .. proportion of attempts p_completed = probability_of(qty="completed", user_settings=user_settings) p_attempts = probability_of(qty="attempts", user_settings=user_settings) attempts = int(random.random() * p_attempts * 30 + 10) # always enough to have completed completed = (random.random() < p_completed) if completed: streak_progress = 100 else: streak_progress = max(0, min(90, random.gauss(100 * user_settings["speed_of_learning"], 20))) streak_progress = int(floor(streak_progress / 10.)) * 10 points = streak_progress / 10 * 12 if completed else 0 # only get points when you master. # Choose a rate of exercises, based on their effort level and speed of learning. # Compute the latest possible start time. # Then sample a start time between their start time # and the latest possible start_time rate_of_exercises = 0.66 * user_settings["effort_level"] + 0.33 * user_settings["speed_of_learning"] # exercises per day time_for_attempts = min(datetime.timedelta(days=rate_of_exercises * attempts), date_diff_started) # protect with min time_delta_completed = datetime.timedelta(seconds=random.randint(int(datediff(time_for_attempts, units="seconds")), int(datediff(date_diff_started, units="seconds")))) date_completed = datetime.datetime.now() - time_delta_completed # Always create new logging.info("Creating exercise log: %-12s: %-25s (%d points, %d attempts, %d%% streak on %s)" % ( facility_user.first_name, exercise["name"], points, attempts, streak_progress, date_completed, )) try: elog = ExerciseLog.objects.get(user=facility_user, exercise_id=exercise["name"]) except ExerciseLog.DoesNotExist: elog = ExerciseLog( user=facility_user, exercise_id=exercise["name"], attempts=int(attempts), streak_progress=streak_progress, points=int(points), complete=completed, completion_timestamp=date_completed, ) try: elog.save() # For now, make all attempts on an exercise into a single UserLog. seconds_per_attempt = 10 * (1 + user_settings["speed_of_learning"] * random.random()) time_to_navigate = 15 * (0.5 + random.random()) #between 7.5s and 22.5s time_to_logout = 5 * (0.5 + random.random()) # between 2.5 and 7.5s if UserLog.is_enabled(): ulog = UserLog( user=facility_user, activity_type=1, start_datetime = date_completed - datetime.timedelta(seconds=int(attempts * seconds_per_attempt + time_to_navigate)), end_datetime = date_completed + datetime.timedelta(seconds=time_to_logout), last_active_datetime = date_completed, ) ulog.save() user_logs.append(ulog) except Exception as e: logging.error("Error saving exercise log: %s" % e) continue exercise_logs.append(elog) return (exercise_logs, user_logs)
class TestExploreMethods(KALiteTestCase): ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 3 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "topic0-0-exercise-0" USERNAME1 = "test_user_explore_1" PASSWORD = "******" FACILITY = "Test Facility Explore" TIMESTAMP = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) def setUp(self): '''Performed before every test''' super(TestExploreMethods, self).setUp() # create a facility and user that can be referred to in models across tests self.facility = Facility(name=self.FACILITY) self.facility.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user1.set_password(self.PASSWORD) self.user1.save() #add one exercise self.original_exerciselog = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog.points = self.ORIGINAL_POINTS self.original_exerciselog.attempts = self.ORIGINAL_ATTEMPTS self.original_exerciselog.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog.latest_activity_timestamp = self.TIMESTAMP self.original_exerciselog.completion_timestamp = self.TIMESTAMP self.original_exerciselog.save() #create a request factory for later instantiation of request self.factory = RequestFactory() def test_explore_overall(self): '''get_explore_recommendations()''' # This test is super-slow because of get_explore_recommendations which # is already called and covered in BDD tests. # In order to maintain some level of feasible testing, I am cutting # the test short here. # /benjaoming return #create a request object and set the language attribute request = self.factory.get('/content_recommender?explore=true') request.language = settings.LANGUAGE_CODE actual = get_explore_recommendations(self.user1, request) logger.info(actual[0]) self.assertEqual(actual[0].get("topic-0-0").get("id"), "topic0-0-exercise-1")
class TestResumeMethods(KALiteTestCase): ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 3 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "number_line" EXERCISE_ID2 = "radius_diameter_and_circumference" USERNAME1 = "test_user_resume1" USERNAME2 = "test_user_resume2" PASSWORD = "******" FACILITY = "Test Facility Resume" TIMESTAMP_LATER = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) TIMESTAMP_EARLY = datetime.datetime(2014, 10, 8, 15, 59, 59, 370290) def setUp(self): """Performed before every test""" # a brand new user self.facility = Facility(name=self.FACILITY) self.facility.save() self.user_with_no_activity = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user_with_no_activity.set_password(self.PASSWORD) self.user_with_no_activity.save() # a user with valid exercises self.user_with_activity = FacilityUser(username=self.USERNAME2, facility=self.facility) self.user_with_activity.set_password(self.PASSWORD) self.user_with_activity.save() # add some exercises for second user (both incomplete) self.original_exerciselog2 = ExerciseLog( exercise_id=self.EXERCISE_ID, user=self.user_with_activity, complete=False ) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog( exercise_id=self.EXERCISE_ID2, user=self.user_with_activity, complete=False ) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.save() def test_get_most_recent_incomplete_item(self): """get_most_recent_incomplete_item()""" # test user with activity first expected = {"id": unicode(self.EXERCISE_ID2, "utf-8"), "timestamp": self.TIMESTAMP_LATER, "kind": "Exercise"} actual = get_most_recent_incomplete_item(self.user_with_activity) self.assertEqual(expected, actual) # new user just created (no activity logged) self.assertIsNone(get_most_recent_incomplete_item(user=self.user_with_no_activity))
class TestHelperMethods(KALiteTestCase): ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 3 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "number_line" EXERCISE_ID2 = "radius_diameter_and_circumference" USERNAME1 = "test_user_helper1" PASSWORD = "******" FACILITY = "Test Facility Next Steps" TIMESTAMP_LATER = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) TIMESTAMP_EARLY = datetime.datetime(2014, 10, 8, 15, 59, 59, 370290) def setUp(self): '''Performed before every test''' super(TestHelperMethods, self).setUp() # user + facility self.facility = Facility(name=self.FACILITY) self.facility.save() self.user1 = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user1.set_password(self.PASSWORD) self.user1.save() # insert some exercise activity self.original_exerciselog1 = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user1) self.original_exerciselog1.points = self.ORIGINAL_POINTS self.original_exerciselog1.attempts = self.ORIGINAL_POINTS self.original_exerciselog1.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog1.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog1.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog1.struggling = False self.original_exerciselog1.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user=self.user1) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.struggling = False self.original_exerciselog2.save() def tearDown(self): '''Performed after each test''' super(TestHelperMethods, self).tearDown() self.user_with_activity = None self.user_with_no_activity = None @unittest.skipIf( settings.RUNNING_IN_CI, "benjaoming: Disabling for now, cannot reproduce failures locally, " "but it fails consistently on Circle." ) def test_exercises_from_topics(self): '''get_exercises_from_topics()''' expected = [e.id for e in self.content_exercises[0:4]] actual = get_exercises_from_topics([self.content_subsubtopics[0].id]) actual = map(lambda s: str(s), actual) expected = set(expected) actual = set(sorted(actual)) self.assertEqual(expected, actual) def test_most_recent_exercises(self): '''get_most_recent_exercises()''' first = "radius_diameter_and_circumference" second = "number_line" expected = [unicode(first, 'utf-8'), unicode(second, 'utf-8')] actual = get_most_recent_exercises(self.user1) self.assertSequenceEqual(expected, actual)
class TestResumeMethods(KALiteTestCase): ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 3 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "number_line" EXERCISE_ID2 = "radius_diameter_and_circumference" USERNAME1 = "test_user_resume1" USERNAME2 = "test_user_resume2" PASSWORD = "******" FACILITY = "Test Facility Resume" TIMESTAMP_LATER = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) TIMESTAMP_EARLY = datetime.datetime(2014, 10, 8, 15, 59, 59, 370290) def setUp(self): '''Performed before every test''' #a brand new user self.facility = Facility(name=self.FACILITY) self.facility.save() self.user_with_no_activity = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user_with_no_activity.set_password(self.PASSWORD) self.user_with_no_activity.save() #a user with valid exercises self.user_with_activity = FacilityUser(username=self.USERNAME2, facility=self.facility) self.user_with_activity.set_password(self.PASSWORD) self.user_with_activity.save() #add some exercises for second user (both incomplete) self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user_with_activity, complete=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user=self.user_with_activity, complete=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.save() def test_get_most_recent_incomplete_item(self): '''get_most_recent_incomplete_item()''' #test user with activity first expected = { "id": unicode(self.EXERCISE_ID2, 'utf-8'), "timestamp": self.TIMESTAMP_LATER, "kind": "Exercise" } actual = get_most_recent_incomplete_item(self.user_with_activity) self.assertEqual(expected, actual) #new user just created (no activity logged) self.assertIsNone( get_most_recent_incomplete_item(user=self.user_with_no_activity))
def generate_fake_exercise_logs(facility_user=None, topics=topics, start_date=datetime.datetime.now() - datetime.timedelta(days=30 * 6)): """Add exercise logs for the given topics, for each of the given users. If no users are given, they are created. If no topics exist, they are taken from the list at the top of this file. By default, users start learning randomly between 6 months ago and now. """ own_device = Device.get_own_device() date_diff = datetime.datetime.now() - start_date exercise_logs = [] user_logs = [] # It's not a user: probably a list. # Recursive case if not hasattr(facility_user, "username"): # It's NONE :-/ generate the users first! if not facility_user: (facility_user, _, _) = generate_fake_facility_users() for topic in topics: for user in facility_user: (elogs, ulogs) = generate_fake_exercise_logs(facility_user=user, topics=[topic], start_date=start_date) exercise_logs.append(elogs) user_logs.append(ulogs) # Actually generate! else: # Get (or create) user type try: user_settings = json.loads(facility_user.notes) except: user_settings = sample_user_settings() facility_user.notes = json.dumps(user_settings) facility_user.save() date_diff_started = datetime.timedelta( seconds=datediff(date_diff, units="seconds") * user_settings["time_in_program"] ) # when this user started in the program, relative to NOW for topic in topics: # Get all exercises related to the topic exercises = get_topic_exercises(topic_id=topic) # Problem: # Not realistic for students to have lots of unfinished exercises. # If they start them, they tend to get stuck, right? # # So, need to make it more probable that they will finish an exercise, # and less probable that they start one. # # What we need is P(streak|started), not P(streak) # Probability of doing any particular exercise p_exercise = probability_of(qty="exercise", user_settings=user_settings) logging.debug( "# exercises: %d; p(exercise)=%4.3f, user settings: %s\n" % (len(exercises), p_exercise, json.dumps(user_settings))) # of exercises is related to for j, exercise in enumerate(exercises): if random.random() > p_exercise: continue # Probability of completing this exercise, and .. proportion of attempts p_completed = probability_of(qty="completed", user_settings=user_settings) p_attempts = probability_of(qty="attempts", user_settings=user_settings) attempts = int(random.random() * p_attempts * 30 + 10) # always enough to have completed completed = (random.random() < p_completed) if completed: streak_progress = 100 else: streak_progress = max( 0, min( 90, random.gauss( 100 * user_settings["speed_of_learning"], 20))) streak_progress = int(floor(streak_progress / 10.)) * 10 points = streak_progress / 10 * 12 if completed else 0 # only get points when you master. # Choose a rate of exercises, based on their effort level and speed of learning. # Compute the latest possible start time. # Then sample a start time between their start time # and the latest possible start_time rate_of_exercises = 0.66 * user_settings[ "effort_level"] + 0.33 * user_settings[ "speed_of_learning"] # exercises per day time_for_attempts = min( datetime.timedelta(days=rate_of_exercises * attempts), date_diff_started) # protect with min time_delta_completed = datetime.timedelta( seconds=random.randint( int(datediff(time_for_attempts, units="seconds")), int(datediff(date_diff_started, units="seconds")))) date_completed = datetime.datetime.now() - time_delta_completed # Always create new logging.info( "Creating exercise log: %-12s: %-25s (%d points, %d attempts, %d%% streak on %s)" % ( facility_user.first_name, exercise["name"], points, attempts, streak_progress, date_completed, )) try: elog = ExerciseLog.objects.get( user=facility_user, exercise_id=exercise["name"]) except ExerciseLog.DoesNotExist: elog = ExerciseLog( user=facility_user, exercise_id=exercise["name"], attempts=int(attempts), streak_progress=streak_progress, points=int(points), complete=completed, completion_timestamp=date_completed, ) try: elog.save(update_userlog=False) # For now, make all attempts on an exercise into a single UserLog. seconds_per_attempt = 10 * ( 1 + user_settings["speed_of_learning"] * random.random()) time_to_navigate = 15 * (0.5 + random.random() ) #between 7.5s and 22.5s time_to_logout = 5 * (0.5 + random.random() ) # between 2.5 and 7.5s if UserLog.is_enabled(): ulog = UserLog( user=facility_user, activity_type=1, start_datetime=date_completed - datetime.timedelta(seconds=int( attempts * seconds_per_attempt + time_to_navigate)), end_datetime=date_completed + datetime.timedelta(seconds=time_to_logout), last_active_datetime=date_completed, ) ulog.save() user_logs.append(ulog) except Exception as e: logging.error("Error saving exercise log: %s" % e) continue exercise_logs.append(elog) return (exercise_logs, user_logs)
def update_all_central_callback(request): """ Callback after authentication. Parses out the request token verification. Then finishes the request by getting an auth token. """ if not "ACCESS_TOKEN" in request.session: finish_auth(request) exercises = get_api_resource(request, "/api/v1/user/exercises") videos = get_api_resource(request, "/api/v1/user/videos") node_cache = get_node_cache() # Collate videos video_logs = [] for video in videos: # Assume that KA videos are all english-language, not dubbed (for now) video_id = youtube_id = video.get('video', {}).get('youtube_id', "") # Only save videos with progress if not video.get('seconds_watched', None): continue # Only save video logs for videos that we recognize. if video_id not in node_cache["Video"]: logging.warn("Skipping unknown video %s" % video_id) continue try: video_logs.append({ "video_id": video_id, "youtube_id": youtube_id, "total_seconds_watched": video['seconds_watched'], "points": VideoLog.calc_points(video['seconds_watched'], video['duration']), "complete": video['completed'], "completion_timestamp": convert_ka_date(video['last_watched']) if video['completed'] else None, }) logging.debug("Got video log for %s: %s" % (video_id, video_logs[-1])) except KeyError: # logging.error("Could not save video log for data with missing values: %s" % video) # Collate exercises exercise_logs = [] for exercise in exercises: # Only save exercises that have any progress. if not exercise.get('last_done', None): continue # Only save video logs for videos that we recognize. slug = exercise.get('exercise', "") if slug not in node_cache['Exercise']: logging.warn("Skipping unknown video %s" % slug) continue try: completed = exercise['streak'] >= 10 basepoints = node_cache['Exercise'][slug][0]['basepoints'] exercise_logs.append({ "exercise_id": slug, "streak_progress": min(100, 100 * exercise['streak']/10), # duplicates logic elsewhere "attempts": exercise['total_done'], "points": ExerciseLog.calc_points(basepoints, ncorrect=exercise['streak'], add_randomness=False), # no randomness when importing from KA "complete": completed, "attempts_before_completion": exercise['total_done'] if not exercise['practiced'] else None, #can't figure this out if they practiced after mastery. "completion_timestamp": convert_ka_date(exercise['proficient_date']) if completed else None, }) logging.debug("Got exercise log for %s: %s" % (slug, exercise_logs[-1])) except KeyError: logging.error("Could not save exercise log for data with missing values: %s" % exercise) # POST the data back to the distributed server try: dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.datetime) else None logging.debug("POST'ing to %s" % request.session["distributed_callback_url"]) response = requests.post( request.session["distributed_callback_url"], cookies={ "csrftoken": request.session["distributed_csrf_token"] }, data = { "csrfmiddlewaretoken": request.session["distributed_csrf_token"], "video_logs": json.dumps(video_logs, default=dthandler), "exercise_logs": json.dumps(exercise_logs, default=dthandler), "user_id": request.session["distributed_user_id"], } ) logging.debug("Response (%d): %s" % (response.status_code, response.content)) except requests.exceptions.ConnectionError as e: return HttpResponseRedirect(set_query_params(request.session["distributed_redirect_url"], { "message_type": "error", "message": _("Could not connect to your KA Lite installation to share Khan Academy data."), "message_id": "id_khanload", })) except Exception as e: return HttpResponseRedirect(set_query_params(request.session["distributed_redirect_url"], { "message_type": "error", "message": _("Failure to send data to your KA Lite installation: %s") % e, "message_id": "id_khanload", })) try: json_response = json.loads(response.content) if not isinstance(json_response, dict) or len(json_response) != 1: # Could not validate the message is a single key-value pair raise Exception(_("Unexpected response format from your KA Lite installation.")) message_type = json_response.keys()[0] message = json_response.values()[0] except ValueError as e: message_type = "error" message = unicode(e) except Exception as e: message_type = "error" message = _("Loading json object: %s") % e # If something broke on the distribute d server, we are SCREWED. # For now, just show the error to users. # # Ultimately, we have a message, would like to share with the distributed server. # if response.status_code != 200: # return HttpResponseServerError(response.content) return HttpResponseRedirect(set_query_params(request.session["distributed_redirect_url"], { "message_type": message_type, "message": message, "message_id": "id_khanload", }))
class TestResumeMethods(KALiteTestCase): ORIGINAL_POINTS = 37 ORIGINAL_ATTEMPTS = 3 ORIGINAL_STREAK_PROGRESS = 20 NEW_POINTS_LARGER = 22 NEW_ATTEMPTS = 5 NEW_STREAK_PROGRESS_LARGER = 10 NEW_POINTS_SMALLER = 0 NEW_STREAK_PROGRESS_SMALLER = 0 EXERCISE_ID = "number_line" EXERCISE_ID2 = "radius_diameter_and_circumference" INVALID_EXERCISE_ID = "s_diameter_and_circumference" USERNAME1 = "test_user_resume1" USERNAME2 = "test_user_resume2" USERNAME3 = "test_user_resume3" PASSWORD = "******" FACILITY = "Test Facility Resume" TIMESTAMP_LATER = datetime.datetime(2014, 11, 17, 20, 51, 2, 342662) TIMESTAMP_EARLY = datetime.datetime(2014, 10, 8, 15, 59, 59, 370290) def setUp(self): """Performed before every test""" # a brand new user self.facility = Facility(name=self.FACILITY) self.facility.save() self.user_with_no_activity = FacilityUser(username=self.USERNAME1, facility=self.facility) self.user_with_no_activity.set_password(self.PASSWORD) self.user_with_no_activity.save() # a user with valid exercises self.user_with_activity = FacilityUser(username=self.USERNAME2, facility=self.facility) self.user_with_activity.set_password(self.PASSWORD) self.user_with_activity.save() # a user with invalid exercises self.user_with_old_activity = FacilityUser(username=self.USERNAME3, facility=self.facility) self.user_with_old_activity.set_password(self.PASSWORD) self.user_with_old_activity.save() # add some exercises for second user (both incomplete) self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID, user=self.user_with_activity, complete=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_EARLY self.original_exerciselog2.save() self.original_exerciselog2 = ExerciseLog(exercise_id=self.EXERCISE_ID2, user=self.user_with_activity, complete=False) self.original_exerciselog2.points = self.ORIGINAL_POINTS self.original_exerciselog2.attempts = self.ORIGINAL_POINTS self.original_exerciselog2.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog2.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog2.save() self.original_exerciselog3 = ExerciseLog(exercise_id=self.INVALID_EXERCISE_ID, user=self.user_with_old_activity, complete=False) self.original_exerciselog3.points = self.ORIGINAL_POINTS self.original_exerciselog3.attempts = self.ORIGINAL_POINTS self.original_exerciselog3.streak_progress = self.ORIGINAL_STREAK_PROGRESS self.original_exerciselog3.latest_activity_timestamp = self.TIMESTAMP_LATER self.original_exerciselog3.completion_timestamp = self.TIMESTAMP_LATER self.original_exerciselog3.save() def test_get_most_recent_incomplete_item(self): '''get_most_recent_incomplete_item()''' # test user with activity first expected = { "id": unicode(self.EXERCISE_ID2, 'utf-8'), "timestamp": self.TIMESTAMP_LATER, "kind": "Exercise" } actual = get_most_recent_incomplete_item(self.user_with_activity) self.assertEqual(expected, actual) # new user just created (no activity logged) self.assertIsNone(get_most_recent_incomplete_item(user=self.user_with_no_activity)) def test_get_empty_list_invalid_resume(self): # Used to mock a request object that is only queried for its 'lang' property. class MicroMock(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) # test user with invalid activity actual = get_resume_recommendations(self.user_with_old_activity, MicroMock(language="en")) # ensure that no recommendations are returned self.assertEqual(len(actual), 0)