class ExercisesCreatedHistogram(request_handler.RequestHandler): @user_util.open_access def get(self): past_days = self.request_int('past_days', 7) today_dt = dt.datetime.combine(dt.date.today(), dt.time()) earliest_dt = today_dt - dt.timedelta(days=past_days) self.response.out.write( self.get_histogram_spline_for_highcharts(earliest_dt)) @layer_cache.cache_with_key_fxn(lambda self, date: "%s|%s" % (Setting.cached_exercises_date(), date), expiration=CACHE_EXPIRATION_SECS, layer=layer_cache.Layers.Memcache) 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)
class ExerciseNumberTrivia(request_handler.RequestHandler): @user_util.open_access def get(self): number = self.request_int('num', len(Exercise.get_all_use_cache())) self.render_json(self.number_facts_for_geckboard_text(number)) @staticmethod @layer_cache.cache_with_key_fxn(lambda number: "%s|%s" % (Setting.cached_exercises_date(), number), expiration=CACHE_EXPIRATION_SECS, layer=layer_cache.Layers.Memcache) def number_facts_for_geckboard_text(number): import exercisestats.number_trivia as number_trivia math_fact = number_trivia.math_facts.get( number, 'This number is interesting. Why? Suppose there exists uninteresting ' 'natural numbers. Then the smallest in that set would be ' 'interesting by virtue of being the first: a contradiction. ' 'Thus all natural numbers are interesting.') year_fact = number_trivia.year_facts.get( number, 'nothing interesting happened') misc_fact_keys = sorted(number_trivia.misc_facts.keys()) first_available_num = misc_fact_keys[ bisect.bisect_left(misc_fact_keys, number) - 1] greater_than_fact = number_trivia.misc_facts[first_available_num] text1 = 'We now have more exercises than %s (%s)!' % ( cgi.escape(greater_than_fact), str(first_available_num)) text2 = math_fact text3 = "In year %d, %s" % (number, cgi.escape(year_fact)) return { 'item': [ { 'text': text1 }, { 'text': text2 }, { 'text': text3 }, ] }
class ExercisesLastAuthorCounter(request_handler.RequestHandler): @user_util.open_access def get(self): self.render_json(self.exercise_counter_for_geckoboard_rag()) @staticmethod @layer_cache.cache_with_key_fxn( lambda: "last_author_%s" % Setting.cached_exercises_date(), expiration=CACHE_EXPIRATION_SECS, layer=layer_cache.Layers.Memcache) 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, }, ] }
class ExerciseStatsMapGraph(request_handler.RequestHandler): # TODO: Just move this logic into get and make get_use_cache take a day parameter. def get_request_params(self): default_day = dt.date.today() - dt.timedelta(days=2) interested_day = self.request_date('date', "%Y/%m/%d", default_day) return {'interested_day': interested_day} @user_util.open_access def get(self): self.response.out.write(self.get_use_cache()) @layer_cache.cache_with_key_fxn( lambda self: "%s|%s" % (Setting.cached_exercises_date(), self.get_request_params()), expiration=CACHE_EXPIRATION_SECS, layer=layer_cache.Layers.Memcache) 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_cache_key(self, exids, dates, title='', showLegend=False): return "%s|%s|%s|%s|%s" % (Setting.cached_exercises_date(), sorted(exids), sorted(dates), title, showLegend)
def get_cache_key(self, exids, dates, title="", showLegend=False): return "%s|%s|%s|%s|%s" % (Setting.cached_exercises_date(), sorted(exids), sorted(dates), title, showLegend)