def get_json_response(self): # Currently accepts: { "buckets", "all", "newest" } to_show = self.request_string("show", "buckets") past_days = self.request_int("past_days", 7) refresh_secs = self.request_int("rsecs", 30) today = dt.date.today() # We don't use App Engine Query filters so as to avoid adding entries to index.yaml days = [today - dt.timedelta(days=i) for i in range(0, past_days)] if to_show == "all": exercise_names = [ex.name for ex in Exercise.get_all_use_cache()] return self.exercise_over_time_for_highcharts(exercise_names, days, "All Exercises", showLegend=True) if to_show == "newest": exercises = Exercise.get_all_use_cache() exercises.sort(key=lambda ex: ex.creation_date, reverse=True) exercise_names = [ex.name for ex in exercises] num_newest = self.request_int("newest", 5) exid = exercise_names[get_bucket_cursor(refresh_secs, num_newest)] title = "Newest Exercises - %s" % Exercise.to_display_name(exid) return self.exercise_over_time_for_highcharts([exid], days, title, showLegend=True) num_buckets = self.request_int("buckets", NUM_BUCKETS) bucket_index = self.request_int("ix", 0) bucket_size = get_bucket_size(num_buckets, bucket_index) bucket_cursor = get_bucket_cursor(refresh_secs, bucket_size) exercise_names = exercises_in_bucket(num_buckets, bucket_index) exid = exercise_names[bucket_cursor] return self.exercise_over_time_for_highcharts([exid], days, Exercise.to_display_name(exid))
def get_use_cache(self): params = self.get_request_params() # Get the maximum so we know how the data label circles should be scaled most_new_users = 1 ex_stat_dict = {} for ex in Exercise.get_all_use_cache(): stat = ExerciseStatistic.get_by_date(ex.name, params['interested_day']) ex_stat_dict[ex.name] = stat if stat: most_new_users = max(most_new_users, stat.num_new_users()) data_points = [] min_y, max_y = -1, 1 for ex in Exercise.get_all_use_cache(): stat = ex_stat_dict[ex.name] y, x = -int(ex.h_position), int(ex.v_position) min_y, max_y = min(y, min_y), max(y, max_y) # Set the area of the circle proportional to the data value radius = 1 if stat: radius = math.sqrt( float(stat.num_new_users()) / most_new_users) * MAX_POINT_RADIUS point = { 'x': x, 'y': y, 'name': ex.display_name, 'marker': { 'radius': max(radius, 1) }, } data_points.append(point) context = { 'title': 'Exercises map - First attempts', 'series': { 'name': 'First attempts', 'data_values': json.dumps(data_points), }, 'minYValue': min_y - 1, 'maxYValue': max_y + 1, } return self.render_jinja2_template_to_string( 'exercisestats/highcharts_scatter_map.json', context)
def get_use_cache(self): params = self.get_request_params() # Get the maximum so we know how the data label circles should be scaled most_new_users = 1 ex_stat_dict = {} for ex in Exercise.get_all_use_cache(): stat = ExerciseStatistic.get_by_date(ex.name, params['interested_day']) ex_stat_dict[ex.name] = stat if stat: most_new_users = max(most_new_users, stat.num_new_users()) data_points = [] min_y, max_y = -1, 1 for ex in Exercise.get_all_use_cache(): stat = ex_stat_dict[ex.name] y, x = -int(ex.h_position), int(ex.v_position) min_y, max_y = min(y, min_y), max(y, max_y) # Set the area of the circle proportional to the data value radius = 1 if stat: radius = math.sqrt(float(stat.num_new_users()) / most_new_users) * MAX_POINT_RADIUS point = { 'x': x, 'y': y, 'name': ex.display_name, 'marker': { 'radius': max(radius, 1) }, } data_points.append(point) context = { 'title': 'Exercises map - First attempts', 'series': { 'name': 'First attempts', 'data_values': json.dumps(data_points), }, 'minYValue': min_y - 1, 'maxYValue': max_y + 1, } return self.render_jinja2_template_to_string( 'exercisestats/highcharts_scatter_map.json', context)
def get_histogram_spline_for_highcharts(self, earliest_dt=dt.datetime.min): histogram = {} for ex in Exercise.get_all_use_cache(): if ex.creation_date: creation_day = dt.datetime.combine(ex.creation_date, dt.time()) timestamp = to_unix_secs(creation_day) * 1000 histogram[timestamp] = histogram.get(timestamp, 0) + 1 total_exercises = {} prev_value = 0 for day, value in sorted(histogram.items()): prev_value = total_exercises[day] = prev_value + value # Only retain recent dates earliest_unix = to_unix_secs(earliest_dt) * 1000 histogram = [[k, v] for k, v in histogram.items() if k >= earliest_unix] total_exercises = [[k, v] for k, v in total_exercises.items() if k >= earliest_unix] context = { "series": [ { "name": "Histogram (created per day)", "type": "column", "data_values": json.dumps(histogram), "axis": 0, }, {"name": "Total exercises", "type": "spline", "data_values": json.dumps(total_exercises), "axis": 1}, ], # Let highcharts determine the scales for now. "axes": [{"max": "null"}, {"max": "null"}], } return self.render_jinja2_template_to_string( "exercisestats/highcharts_exercises_created_histogram.json", context )
def exercise_counter_for_geckoboard_rag(): exercises = Exercise.get_all_use_cache() exercises.sort(key=lambda ex: ex.creation_date, reverse=True) last_exercise = exercises[0] num_exercises = len(exercises) last_exercise_author = last_exercise.author.nickname( ) if last_exercise.author else 'random person' text = "Thanks %s for %s!" % (last_exercise_author, last_exercise.display_name) return { 'item': [ { 'value': None, 'text': '', }, { 'value': None, 'text': '', }, { 'value': num_exercises, 'text': text, }, ] }
def exercise_counter_for_geckoboard_rag(): exercises = Exercise.get_all_use_cache() exercises.sort(key=lambda ex: ex.creation_date, reverse=True) last_exercise = exercises[0] num_exercises = len(exercises) last_exercise_author = last_exercise.author.nickname() if last_exercise.author else 'random person' text = "Thanks %s for %s!" % (last_exercise_author, last_exercise.display_name) return { 'item': [ { 'value': None, 'text': '', }, { 'value': None, 'text': '', }, { 'value': num_exercises, 'text': text, }, ] }
def get(self): date_to_set = self.request_date("date", "%Y/%m/%d") exercises = Exercise.get_all_use_cache() updated = [] for ex in exercises: ex.creation_date = date_to_set updated.append(ex) db.put(updated)
def get_ip_addresses_for_geckoboard_map(date): ip_addresses = [] for ex in Exercise.get_all_use_cache(): stat = ExerciseStatistic.get_by_date(ex.name, date) if stat: ip_addresses += stat.histogram.get("ip_addresses", []) return {"points": {"point": [{"ip": addr} for addr in ip_addresses]}}
def get_ip_addresses_for_geckoboard_map(date): ip_addresses = [] for ex in Exercise.get_all_use_cache(): stat = ExerciseStatistic.get_by_date(ex.name, date) if stat: ip_addresses += stat.histogram.get('ip_addresses', []) return {'points': {'point': [{'ip': addr} for addr in ip_addresses]}}
def get(self): date_to_set = self.request_date('date', "%Y/%m/%d") exercises = Exercise.get_all_use_cache() updated = [] for ex in exercises: ex.creation_date = date_to_set updated.append(ex) db.put(updated)
def get_json_response(self): # Currently accepts: { "buckets", "all", "newest" } to_show = self.request_string('show', 'buckets') past_days = self.request_int('past_days', 7) refresh_secs = self.request_int('rsecs', 30) today = dt.date.today() # We don't use App Engine Query filters so as to avoid adding entries to index.yaml days = [today - dt.timedelta(days=i) for i in range(0, past_days)] if to_show == 'all': exercise_names = [ex.name for ex in Exercise.get_all_use_cache()] return self.exercise_over_time_for_highcharts(exercise_names, days, 'All Exercises', showLegend=True) if to_show == 'newest': exercises = Exercise.get_all_use_cache() exercises.sort(key=lambda ex: ex.creation_date, reverse=True) exercise_names = [ex.name for ex in exercises] num_newest = self.request_int('newest', 5) exid = exercise_names[get_bucket_cursor(refresh_secs, num_newest)] title = 'Newest Exercises - %s' % Exercise.to_display_name(exid) return self.exercise_over_time_for_highcharts([exid], days, title, showLegend=True) num_buckets = self.request_int('buckets', NUM_BUCKETS) bucket_index = self.request_int('ix', 0) bucket_size = get_bucket_size(num_buckets, bucket_index) bucket_cursor = get_bucket_cursor(refresh_secs, bucket_size) exercise_names = exercises_in_bucket(num_buckets, bucket_index) exid = exercise_names[bucket_cursor] return self.exercise_over_time_for_highcharts( [exid], days, Exercise.to_display_name(exid))
def exercise_counter_for_geckoboard_rag(): exercises = Exercise.get_all_use_cache() exercises.sort(key=lambda ex: ex.creation_date, reverse=True) last_exercise = exercises[0] num_exercises = len(exercises) last_exercise_author = last_exercise.author.nickname() if last_exercise.author else "random person" text = "Thanks %s for %s!" % (last_exercise_author, last_exercise.display_name) return { "item": [{"value": None, "text": ""}, {"value": None, "text": ""}, {"value": num_exercises, "text": text}] }
def exercises_in_bucket(num_buckets, bucket_index): exercise_names = [ex.name for ex in Exercise.get_all_use_cache()] exercise_names.sort() # These calculations evenly distribute exercises among buckets, with excess # going to the first few buckets. # In particular, max_capacity(buckets) - min_capacity(buckets) <= 1. num_exercises = len(exercise_names) min_bucket_size = num_exercises / num_buckets bucket_rem = num_exercises % num_buckets first = bucket_index * min_bucket_size + min(bucket_rem, bucket_index) return exercise_names[first : first + get_bucket_size(num_buckets, bucket_index)]
def exercises_in_bucket(num_buckets, bucket_index): exercise_names = [ex.name for ex in Exercise.get_all_use_cache()] exercise_names.sort() # These calculations evenly distribute exercises among buckets, with excess # going to the first few buckets. # In particular, max_capacity(buckets) - min_capacity(buckets) <= 1. num_exercises = len(exercise_names) min_bucket_size = num_exercises / num_buckets bucket_rem = num_exercises % num_buckets first = bucket_index * min_bucket_size + min(bucket_rem, bucket_index) return exercise_names[first:first + get_bucket_size(num_buckets, bucket_index)]
def get_ip_addresses_for_geckoboard_map(date): ip_addresses = [] for ex in Exercise.get_all_use_cache(): stat = ExerciseStatistic.get_by_date(ex.name, date) if stat: ip_addresses += stat.histogram.get('ip_addresses', []) return { 'points': { 'point': [{'ip': addr} for addr in ip_addresses] } }
def get_histogram_spline_for_highcharts(self, earliest_dt=dt.datetime.min): histogram = {} for ex in Exercise.get_all_use_cache(): if ex.creation_date: creation_day = dt.datetime.combine(ex.creation_date, dt.time()) timestamp = to_unix_secs(creation_day) * 1000 histogram[timestamp] = histogram.get(timestamp, 0) + 1 total_exercises = {} prev_value = 0 for day, value in sorted(histogram.items()): prev_value = total_exercises[day] = prev_value + value # Only retain recent dates earliest_unix = to_unix_secs(earliest_dt) * 1000 histogram = [[k, v] for k, v in histogram.items() if k >= earliest_unix] total_exercises = [[k, v] for k, v in total_exercises.items() if k >= earliest_unix] context = { 'series': [{ 'name': 'Histogram (created per day)', 'type': 'column', 'data_values': json.dumps(histogram), 'axis': 0, }, { 'name': 'Total exercises', 'type': 'spline', 'data_values': json.dumps(total_exercises), 'axis': 1, }], # Let highcharts determine the scales for now. 'axes': [ { 'max': 'null' }, { 'max': 'null' }, ], } return self.render_jinja2_template_to_string( 'exercisestats/highcharts_exercises_created_histogram.json', context)
def get_histogram_spline_for_highcharts(self, earliest_dt=dt.datetime.min): histogram = {} for ex in Exercise.get_all_use_cache(): if ex.creation_date: creation_day = dt.datetime.combine(ex.creation_date, dt.time()) timestamp = to_unix_secs(creation_day) * 1000 histogram[timestamp] = histogram.get(timestamp, 0) + 1 total_exercises = {} prev_value = 0 for day, value in sorted(histogram.items()): prev_value = total_exercises[day] = prev_value + value # Only retain recent dates earliest_unix = to_unix_secs(earliest_dt) * 1000 histogram = [[k,v] for k,v in histogram.items() if k >= earliest_unix] total_exercises = [[k,v] for k,v in total_exercises.items() if k >= earliest_unix] context = { 'series': [ { 'name': 'Histogram (created per day)', 'type': 'column', 'data_values': json.dumps(histogram), 'axis': 0, }, { 'name': 'Total exercises', 'type': 'spline', 'data_values': json.dumps(total_exercises), 'axis': 1, } ], # Let highcharts determine the scales for now. 'axes': [ { 'max': 'null' }, { 'max': 'null' }, ], } return self.render_jinja2_template_to_string( 'exercisestats/highcharts_exercises_created_histogram.json', context)
def get_bucket_size(num_buckets, bucket_index): num_exercises = len(Exercise.get_all_use_cache()) bucket_rem = num_exercises % num_buckets return (num_exercises / num_buckets) + (1 if bucket_index < bucket_rem else 0)
def get(self): number = self.request_int('num', len(Exercise.get_all_use_cache())) self.render_json(self.number_facts_for_geckboard_text(number))
def class_progress_report_graph_context(user_data, list_students): if not user_data: return {} list_students = sorted(list_students, key=lambda student: student.nickname) student_email_pairs = [ (escape(s.email), (s.nickname[:14] + '...') if len(s.nickname) > 17 else s.nickname) for s in list_students ] emails_escapejsed = [escapejs(s.email) for s in list_students] exercises_all = Exercise.get_all_use_cache() user_exercise_graphs = UserExerciseGraph.get(list_students) exercises_found = [] for exercise in exercises_all: for user_exercise_graph in user_exercise_graphs: graph_dict = user_exercise_graph.graph_dict(exercise.name) if graph_dict and graph_dict["total_done"]: exercises_found.append(exercise) break exercise_names = [(e.name, e.display_name, escapejs(e.name)) for e in exercises_found] exercise_list = [{ 'name': e.name, 'display_name': e.display_name } for e in exercises_found] exercise_data = {} for (student, _, _, user_exercise_graph) in izip(list_students, student_email_pairs, emails_escapejsed, user_exercise_graphs): student_email = student.email for (exercise, (_, _, _)) in izip(exercises_found, exercise_names): exercise_name = exercise.name graph_dict = user_exercise_graph.graph_dict(exercise_name) status = "" if graph_dict["proficient"]: if graph_dict["reviewing"]: status = "Review" else: status = "Proficient" if not graph_dict["explicitly_proficient"]: status = "Proficient (due to proficiency in a more advanced module)" elif graph_dict["struggling"]: status = "Struggling" elif graph_dict["total_done"] > 0: status = "Started" if student_email not in exercise_data: exercise_data[student_email] = { 'email': student.email, 'nickname': student.nickname, 'profile_root': student.profile_root, 'exercises': [] } if len(status) > 0: exercise_data[student_email]['exercises'].append({ "status": status, "progress": graph_dict["progress"], "total_done": graph_dict["total_done"], "last_done": graph_dict["last_done"] if graph_dict["last_done"] and graph_dict["last_done"].year > 1 else '', "last_done_ago": timesince_ago(graph_dict["last_done"]) if graph_dict["last_done"] and graph_dict["last_done"].year > 1 else '' }) else: exercise_data[student_email]['exercises'].append({ "name": exercise_name, "status": status, }) student_row_data = [data for (_, data) in exercise_data.iteritems()] return { 'exercise_names': exercise_list, 'exercise_data': student_row_data, 'coach_email': user_data.email, 'c_points': reduce(lambda a, b: a + b, map(lambda s: s.points, list_students), 0) }
def class_progress_report_graph_context(user_data, list_students): if not user_data: return {} list_students = sorted(list_students, key=lambda student: student.nickname) student_email_pairs = [(escape(s.email), (s.nickname[:14] + '...') if len(s.nickname) > 17 else s.nickname) for s in list_students] emails_escapejsed = [escapejs(s.email) for s in list_students] exercises_all = Exercise.get_all_use_cache() user_exercise_graphs = UserExerciseGraph.get(list_students) exercises_found = [] for exercise in exercises_all: for user_exercise_graph in user_exercise_graphs: graph_dict = user_exercise_graph.graph_dict(exercise.name) if graph_dict and graph_dict["total_done"]: exercises_found.append(exercise) break exercise_names = [(e.name, e.display_name, escapejs(e.name)) for e in exercises_found] exercise_list = [{'name': e.name, 'display_name': e.display_name} for e in exercises_found] exercise_data = {} for (student, _, _, user_exercise_graph) in izip(list_students, student_email_pairs, emails_escapejsed, user_exercise_graphs): student_email = student.email for (exercise, (_, _, _)) in izip(exercises_found, exercise_names): exercise_name = exercise.name graph_dict = user_exercise_graph.graph_dict(exercise_name) status = "" if graph_dict["proficient"]: if graph_dict["reviewing"]: status = "Herzien" else: status = "Gehaald" if not graph_dict["explicitly_proficient"]: status = ("Gehaald") elif graph_dict["struggling"]: status = "Moeite" elif graph_dict["total_done"] > 0: status = "Gestart" if student_email not in exercise_data: exercise_data[student_email] = { 'email': student.email, 'nickname': student.nickname, 'profile_root': student.profile_root, 'exercises': [] } if len(status) > 0: exercise_data[student_email]['exercises'].append({ "status": status, "progress": graph_dict["progress"], "total_done": graph_dict["total_done"], "last_done": (graph_dict["last_done"] if (graph_dict["last_done"] and graph_dict["last_done"].year > 1) else ''), "last_done_ago": (timesince_ago(graph_dict["last_done"]) if (graph_dict["last_done"] and graph_dict["last_done"].year > 1) else '') }) else: exercise_data[student_email]['exercises'].append({ "name": exercise_name, "status": status, }) student_row_data = [data for (_, data) in exercise_data.iteritems()] return { 'exercise_names': exercise_list, 'exercise_data': student_row_data, 'coach_email': user_data.email, 'c_points': reduce(lambda a, b: a + b, map(lambda s: s.points, list_students), 0) }
def get(self): number = self.request_int("num", len(Exercise.get_all_use_cache())) self.render_json(self.number_facts_for_geckboard_text(number))