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)
    def post(self):
        # Protected for admins only by app.yaml so taskqueue can hit this URL
        step = self.request_int("step", default = 0)

        if step == YouTubeSyncStep.START:
            self.startYouTubeSync()
        elif step == YouTubeSyncStep.UPDATE_VIDEO_STATS:
            self.updateVideoStats()

        log = YouTubeSyncStepLog()
        log.step = step
        log.generation = int(Setting.last_youtube_sync_generation_start())
        log.put()

        # check to see if we have more steps to go
        if step < YouTubeSyncStep.UPDATE_VIDEO_STATS:
            self.task_step(step + 1)
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
                },
            ]
        }
    def post(self):
        # Protected for admins only by app.yaml so taskqueue can hit this URL
        step = self.request_int("step", default = 0)

        if step == YouTubeSyncStep.START:
            self.startYouTubeSync()
        elif step == YouTubeSyncStep.UPDATE_VIDEO_STATS:
            self.updateVideoStats()

        log = YouTubeSyncStepLog()
        log.step = step
        log.generation = int(Setting.last_youtube_sync_generation_start())
        log.put()

        # check to see if we have more steps to go
        if step < YouTubeSyncStep.UPDATE_VIDEO_STATS:
            self.task_step(step + 1)
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)
Beispiel #7
0
    # return last item in the list
    return prev_topic


# A number to increment if the layout of the page, as expected by the client
# side JS changes, and the JS is changed to update it. This version is
# independent of topic content version, and is to do with code versions
_layout_version = 2


@layer_cache.cache_with_key_fxn(
        lambda ajax=False, version_number=None:
        "library_content_by_topic_%s_v%s.%s" % (
        "ajax" if ajax else "inline",
        version_number if version_number else Setting.topic_tree_version(),
        _layout_version))
def library_content_html(ajax=False, version_number=None):
    """" Returns the HTML for the structure of the topics as they will be
    populated on the homepage. Does not actually contain the list of video
    names as those are filled in later asynchronously via the cache.
    """
    if version_number:
        version = topic_models.TopicVersion.get_by_number(version_number)
    else:
        version = topic_models.TopicVersion.get_default_version()
    root = topic_models.Topic.get_root(version)
    if root == None:
        return ""
    tree = root.make_tree(types=["Topics", "Video", "Url"])
    topics = flatten_tree(tree)
Beispiel #8
0
            raise SmartHistoryLoadException(
                "Post attempt failed to SmartHistory with :" + str(e))
            return

        self.response.out.write(data)   

    @staticmethod
    def clearCache():
        Setting.smarthistory_version(int(Setting.smarthistory_version()) + 1)  

    #load the resource from smart history's server and then cache it
    #in the data store. If it is an image then cache it in the blob
    #store and store the blobkey in the data store
    @layer_cache.cache_with_key_fxn(
        lambda self: "smart_history_v%s_%s" % (
            Setting.smarthistory_version(), self.request.path_qs), 
        layer=layer_cache.Layers.Datastore, 
        expiration=SMARTHISTORY_CACHE_EXPIRATION_TIME, 
        persist_across_app_versions=True, 
        permanent_key_fxn=lambda self: "smart_history_permanent_%s" % (
            self.request.path_qs))
    def load_resource(self):
        path = self.request.path_qs

        #img is in users browser cache - we don't want to cache a
        #Not-Modified response otherwise people who don't have image
        #in browser cache won't get it
        headers = dict((k, v) for (k, v) in self.request.headers.iteritems()
                       if k not in ["If-Modified-Since", "If-None-Match",
                                    "Content-Length", "Host"])
Beispiel #9
0
        except Exception, e:
            raise SmartHistoryLoadException(
                "Post attempt failed to SmartHistory with :" + str(e))
            return

        self.response.out.write(data)

    @staticmethod
    def clearCache():
        Setting.smarthistory_version(int(Setting.smarthistory_version()) + 1)

    #load the resource from smart history's server and then cache it in the data store
    #if it is an image then cache it in the blob store and store the blobkey in the data store
    @layer_cache.cache_with_key_fxn(
        lambda self: "smart_history_v%s_%s" %
        (Setting.smarthistory_version(), self.request.path_qs),
        layer=layer_cache.Layers.Datastore,
        expiration=SMARTHISTORY_CACHE_EXPIRATION_TIME,
        persist_across_app_versions=True,
        permanent_key_fxn=lambda self: "smart_history_permanent_%s" %
        (self.request.path_qs))
    def load_resource(self):
        path = self.request.path_qs

        #img is in users browser cache - we don't want to cache a Not-Modified response otherwise people who don't have image in browser cache won't get it
        headers = dict(
            (k, v) for (k, v) in self.request.headers.iteritems() if k not in
            ["If-Modified-Since", "If-None-Match", "Content-Length", "Host"])

        logging.info("getting resource " + str(path) + " from " +
                     SMARTHISTORY_URL)
Beispiel #10
0
import layer_cache
import topic_models
import video_models
from url_model import Url
from setting_model import Setting


@layer_cache.cache_with_key_fxn(lambda version_number=None: 
    "video_title_dicts_%s" % (
    version_number or Setting.topic_tree_version()))
def video_title_dicts(version_number=None):
    if version_number:
        version = topic_models.TopicVersion.get_by_number(version_number)
    else:
        version = None

    return map(lambda video: {
        "title": video.title,
        "key": str(video.key()),
        "relative_url": "/video/%s" % video.readable_id,
        "id": video.readable_id
    }, [v for v in video_models.Video.get_all_live(version=version)
        if v is not None])


@layer_cache.cache_with_key_fxn(lambda version_number=None: 
    "url_title_dicts_%s" % (
    version_number or Setting.topic_tree_version()))
def url_title_dicts(version_number=None):
    if version_number:
        version = topic_models.TopicVersion.get_by_number(version_number)
 def startYouTubeSync(self):
     Setting.last_youtube_sync_generation_start(int(Setting.last_youtube_sync_generation_start()) + 1)
Beispiel #12
0
    # return last item in the list
    return prev_topic


# A number to increment if the layout of the page, as expected by the client
# side JS changes, and the JS is changed to update it. This version is
# independent of topic content version, and is to do with code versions
_layout_version = 1


@layer_cache.cache_with_key_fxn(
    lambda ajax=False, version_number=None:
    "library_content_by_topic_%s_v%s.%s" %
    ("ajax" if ajax else "inline", version_number
     if version_number else Setting.topic_tree_version(), _layout_version))
def library_content_html(ajax=False, version_number=None):
    """" Returns the HTML for the structure of the topics as they will be
    populated on the homepage. Does not actually contain the list of video
    names as those are filled in later asynchronously via the cache.
    """
    if version_number:
        version = topic_models.TopicVersion.get_by_number(version_number)
    else:
        version = topic_models.TopicVersion.get_default_version()

    tree = topic_models.Topic.get_root(version).make_tree(
        types=["Topics", "Video", "Url"])
    topics = flatten_tree(tree)

    # TODO(tomyedwab): Remove this once the confusion over the old
Beispiel #13
0
            else:
                raise SmartHistoryLoadException(response.status_code)
        except Exception, e:
            raise SmartHistoryLoadException("Post attempt failed to SmartHistory with :" + str(e))
            return

        self.response.out.write(data)

    @staticmethod
    def clearCache():
        Setting.smarthistory_version(int(Setting.smarthistory_version()) + 1)

    # load the resource from smart history's server and then cache it in the data store
    # if it is an image then cache it in the blob store and store the blobkey in the data store
    @layer_cache.cache_with_key_fxn(
        lambda self: "smart_history_v%s_%s" % (Setting.smarthistory_version(), self.request.path_qs),
        layer=layer_cache.Layers.Datastore,
        expiration=SMARTHISTORY_CACHE_EXPIRATION_TIME,
        persist_across_app_versions=True,
        permanent_key_fxn=lambda self: "smart_history_permanent_%s" % (self.request.path_qs),
    )
    def load_resource(self):
        path = self.request.path_qs

        # img is in users browser cache - we don't want to cache a Not-Modified response otherwise people who don't have image in browser cache won't get it
        headers = dict(
            (k, v)
            for (k, v) in self.request.headers.iteritems()
            if k not in ["If-Modified-Since", "If-None-Match", "Content-Length", "Host"]
        )
import layer_cache
import topic_models
import video_models
from url_model import Url
from setting_model import Setting


@layer_cache.cache_with_key_fxn(
    lambda version_number=None: "video_title_dicts_%s" %
    (version_number or Setting.topic_tree_version()))
def video_title_dicts(version_number=None):
    if version_number:
        version = topic_models.TopicVersion.get_by_number(version_number)
    else:
        version = None

    return map(
        lambda video: {
            "title": video.title,
            "key": str(video.key()),
            "relative_url": "/video/%s" % video.readable_id,
            "id": video.readable_id
        }, [
            v for v in video_models.Video.get_all_live(version=version)
            if v is not None
        ])


@layer_cache.cache_with_key_fxn(
    lambda version_number=None: "url_title_dicts_%s" %
    (version_number or Setting.topic_tree_version()))
 def startYouTubeSync(self):
     Setting.last_youtube_sync_generation_start(int(Setting.last_youtube_sync_generation_start()) + 1)
Beispiel #16
0
 def clearCache():
     Setting.smarthistory_version(int(Setting.smarthistory_version()) + 1)
Beispiel #17
0
    # return last item in the list
    return prev_topic


# A number to increment if the layout of the page, as expected by the client
# side JS changes, and the JS is changed to update it. This version is
# independent of topic content version, and is to do with code versions
_layout_version = 2


@layer_cache.cache_with_key_fxn(
        lambda ajax=False, version_number=None:
        "library_content_by_topic_%s_v%s.%s" % (
        "ajax" if ajax else "inline",
        version_number if version_number else Setting.topic_tree_version(),
        _layout_version))
def library_content_html(ajax=False, version_number=None):
    """" Returns the HTML for the structure of the topics as they will be
    populated on the homepage. Does not actually contain the list of video
    names as those are filled in later asynchronously via the cache.
    """
    if version_number:
        version = topic_models.TopicVersion.get_by_number(version_number)
    else:
        version = topic_models.TopicVersion.get_default_version()

    tree = topic_models.Topic.get_root(version).make_tree(
        types=["Topics", "Video", "Url"])
    topics = flatten_tree(tree)
 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)
Beispiel #19
0
 def clearCache():
     Setting.smarthistory_version(int(Setting.smarthistory_version()) + 1)  
Beispiel #20
0
 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)