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, completion_counter=datediff(date_completed, start_date, units="seconds"), ) elog.counter = own_device.increment_and_get_counter() elog.sign(own_device) # have to sign after setting the counter elog.save(imported=True) # avoid userlog issues # 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 settings.USER_LOG_MAX_RECORDS_PER_USER != 0: 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.full_clean() ulog.save() user_logs.append(ulog) exercise_logs.append(elog) return (exercise_logs, user_logs)