Пример #1
0
    def get(self):
        user_data = (user_models.UserData.current() or
                     user_models.UserData.pre_phantom())
        user_exercise_graph = exercise_models.UserExerciseGraph.get(user_data)

        show_review_drawer = (not user_exercise_graph.has_completed_review())

        template_values = {
            # TODO: should be camel cased once entire knowledgemap.js codebase
            # is switched to camel case
            'map_coords': jsonify(
                deserializeMapCoords(user_data.map_coords),
                camel_cased=False),
            'topic_graph_json': jsonify(
                topics_layout(user_data, user_exercise_graph),
                camel_cased=False),
            'graph_dict_data': exercise_graph_dict_json(user_data),
            'user_data': user_data,
            'selected_nav_link': 'practice',
            'show_review_drawer': show_review_drawer,
        }

        if show_review_drawer:
            template_values['review_statement'] = 'Attain mastery'
            template_values['review_call_to_action'] = "I'll do it"

        bingo('suggested_activity_exercises_landing')

        self.render_jinja2_template('viewexercises.html', template_values)
Пример #2
0
    def get(self):

        version_name = self.request.get('version', 'edit')
        topics = map(str, self.request.get_all("topic") or self.request.get_all("topic[]"))

        edit_version = TopicVersion.get_by_id(version_name)
        if edit_version is None:
            default_version = TopicVersion.get_default_version()
            if default_version is None:
                # Assuming this is dev, there is an empty datastore and we need an import
                edit_version = TopicVersion.create_new_version()
                edit_version.edit = True
                edit_version.put()
                create_root(edit_version)
            else:
                raise Exception("Wait for setting default version to finish making an edit version.")

        if self.request.get('migrate', False):
            return self.topic_migration()
        if self.request.get('fixdupes', False):
            return self.fix_duplicates()

        root = Topic.get_root(edit_version)
        data = root.get_visible_data()
        tree_nodes = [data]
        
        template_values = {
            'edit_version': jsonify(edit_version),
            'tree_nodes': jsonify(tree_nodes)
            }
 
        self.render_jinja2_template('topics-admin.html', template_values)
        return
Пример #3
0
    def get(self):
        user_data = (user_models.UserData.current() or
                     user_models.UserData.pre_phantom())
        user_exercise_graph = exercise_models.UserExerciseGraph.get(user_data)

        if user_data.reassess_from_graph(user_exercise_graph):
            user_data.put()

        show_review_drawer = (not user_exercise_graph.has_completed_review())

        template_values = {
            # TODO: should be camel cased once entire knowledgemap.js codebase
            # is switched to camel case
            'map_coords': jsonify(
                deserializeMapCoords(user_data.map_coords),
                camel_cased=False),
            'topic_graph_json': jsonify(
                topics_layout(user_data, user_exercise_graph),
                camel_cased=False),
            'graph_dict_data': exercise_graph_dict_json(
                user_data, user_exercise_graph),
            'user_data': user_data,
            'selected_nav_link': 'practice',
            'show_review_drawer': show_review_drawer,
        }

        if show_review_drawer:
            template_values['review_statement'] = 'Attain mastery'
            template_values['review_call_to_action'] = "I'll do it"

        self.render_jinja2_template('viewexercises.html', template_values)
Пример #4
0
    def get(self):

        version_name = self.request.get('version', 'edit')
        from_version_name = self.request.get('from_version_name', None)

        edit_version = TopicVersion.get_by_id(version_name)
        if from_version_name:
            from_version = TopicVersion.get_by_id(from_version_name)
            edit_version = TopicVersion.create_edit_version(from_version)
        elif edit_version is None:
            default_version = TopicVersion.get_default_version()
            if default_version == None:
                #invi: should only run when topic tree is empty
                edit_version = TopicVersion.create_new_version()
                edit_version.edit = True
                edit_version.put()
                create_root(edit_version)
                edit_version.put()
                edit_version.edit = False
                edit_version.default = True
                edit_version.put()
                edit_version = TopicVersion.create_new_version()
                edit_version.edit = True
                edit_version.put()
                create_root(edit_version)
                edit_version.put()
            else:
                edit_version = TopicVersion.create_edit_version()

        if self.request.get('autoupdate', False):
            self.render_jinja2_template('autoupdate_in_progress.html', {"edit_version": edit_version})
            return
        if self.request.get('autoupdate_begin', False):
            return self.topic_update_from_live(edit_version)
        if self.request.get('migrate', False):
            return self.topic_migration()
        if self.request.get('fixdupes', False):
            return self.fix_duplicates()

        root = Topic.get_root(edit_version)
        data = root.get_visible_data()
        tree_nodes = [data]

        template_values = {
            'edit_version': jsonify(edit_version),
            'tree_nodes': jsonify(tree_nodes)
            }

        self.render_jinja2_template('topics-admin.html', template_values)
        return
Пример #5
0
    def get(self):

        version_name = self.request.get('version', 'edit')

        edit_version = TopicVersion.get_by_id(version_name)
        if edit_version is None:
            default_version = TopicVersion.get_default_version()
            if default_version is None:
                # Create empty default version
                default_version = TopicVersion.create_new_version()
                root = create_root(default_version)
                default_version.default = True
                default_version.edit = False
                default_version.put()
                map_layout = layout.MapLayout.get_for_version(default_version)
                if not map_layout.has_layout:
                    # TODO(KNL): Load map layout from location
                    map_layout.layout = json.loads('{"polylines":[],"topics":{"Getallen":{"icon_url":"\x2Fimages\x2Fpower-mode\x2Fbadges\x2Fdefault-40x40.png","id":"getallen","standalone_title":"Getallen","x":0,"y":4},"Verhoudingen":{"icon_url":"\x2Fimages\x2Fpower-mode\x2Fbadges\x2Fdefault-40x40.png","id":"verhoudingen","standalone_title":"Verhoudingen","x":0,"y":7},"Meten en Meetkunde":{"icon_url":"\x2Fimages\x2Fpower-mode\x2Fbadges\x2Fdefault-40x40.png","id":"meetkunde","standalone_title":"Meten en Meetkunde","x":3,"y":4},"Verbanden":{"icon_url":"\x2Fimages\x2Fpower-mode\x2Fbadges\x2Fdefault-40x40.png","id":"verbanden","standalone_title":"Verbanden","x":3,"y":7}}}');
                    map_layout.put()
                # Create empty edit version
                old_tree = root.make_tree(types=["Topics"], include_hidden=True)
                edit_version = TopicVersion.create_new_version()
                edit_version.copied_from = default_version
                edit_version.edit = True
                TopicVersion.copy_tree(root, edit_version)
                edit_version.put()
            else:
                raise Exception("Wait for setting default version to finish making an edit version.")

        if self.request.get('autoupdate', False):
            self.render_jinja2_template('autoupdate_in_progress.html', {"edit_version": edit_version})
            return
        if self.request.get('autoupdate_begin', False):
            return self.topic_update_from_live(edit_version)
        if self.request.get('migrate', False):
            return self.topic_migration()
        if self.request.get('fixdupes', False):
            return self.fix_duplicates()

        root = Topic.get_root(edit_version)
        data = root.get_visible_data()
        tree_nodes = [data]

        template_values = {
            'edit_version': jsonify(edit_version),
            'tree_nodes': jsonify(tree_nodes)
            }
 
        self.render_jinja2_template('topics-admin.html', template_values)
        return
Пример #6
0
    def jsonified(*args, **kwargs):
        obj = func(*args, **kwargs)

        if isinstance(obj, current_app.response_class):
            return obj

        return obj if type(obj) == str else apijsonify.jsonify(obj)
Пример #7
0
    def jsonified(*args, **kwargs):
        obj = func(*args, **kwargs)

        if isinstance(obj, current_app.response_class):
            return obj

        return obj if type(obj) == str else apijsonify.jsonify(obj)
Пример #8
0
    def add_global_template_values(self, template_values):
        template_values['App'] = App
        template_values['None'] = None

        if not template_values.has_key('user_data'):
            user_data = UserData.current()
            template_values['user_data'] = user_data

        user_data = template_values['user_data']

        template_values['username'] = user_data.nickname if user_data else ""
        template_values[
            'viewer_profile_root'] = user_data.profile_root if user_data else "/profile/nouser"
        template_values['points'] = user_data.points if user_data else 0
        template_values[
            'logged_in'] = not user_data.is_phantom if user_data else False
        template_values['http_host'] = os.environ["HTTP_HOST"]

        # Always insert a post-login request before our continue url
        template_values['continue'] = util.create_post_login_url(
            template_values.get('continue') or self.request.uri)
        template_values['login_url'] = (
            '%s&direct=1' % util.create_login_url(template_values['continue']))
        template_values['logout_url'] = util.create_logout_url(
            self.request.uri)

        template_values['is_mobile'] = False
        template_values['is_mobile_capable'] = False
        template_values['is_ipad'] = False

        if self.is_mobile_capable():
            template_values['is_mobile_capable'] = True
            template_values['is_ipad'] = self.is_ipad()

            if 'is_mobile_allowed' in template_values and template_values[
                    'is_mobile_allowed']:
                template_values['is_mobile'] = self.is_mobile()

        # overridable hide_analytics querystring that defaults to true in dev
        # mode but false for prod.
        hide_analytics = self.request_bool("hide_analytics", App.is_dev_server)
        template_values['hide_analytics'] = hide_analytics

        # client-side error logging
        template_values['include_errorception'] = gandalf('errorception')

        if user_data:
            goals = GoalList.get_current_goals(user_data)
            goals_data = [g.get_visible_data() for g in goals]
            if goals_data:
                template_values['global_goals'] = jsonify(goals_data)

        app_host = self.request.host.split(":")[0]
        if app_host[0].isdigit():
            app_host = app_host.partition(".")[2]
        template_values['webengage_id'] = App.webengage_id.get(
            app_host, app_host) if App.webengage_id else ""

        return template_values
Пример #9
0
    def get_search_data(self, exercise_videos):
        # TODO(csilvers): get rid of circular dependency here
        import exercise_video_model
        import topic_models
        exercises = []
        parent_supertopic_key = self.topic_string_keys[0]
        for parent in self.topic_string_keys:
            if parent in topic_models.Topic._super_topic_ids:
                parent_supertopic_key = parent

        parent_supertopic = db.get(parent_supertopic_key)
        parent_supertopic_data = {
            "id": parent_supertopic.id,
            "url": parent_supertopic.relative_url,
            "title": parent_supertopic.standalone_title,
            "description": parent_supertopic.description,
        }

        if self.key() in exercise_videos:
            exvid = exercise_videos[self.key()]
            exercise = exvid.exercise
            exercise_data = {
                "name": exercise.display_name,
                "url": exercise.relative_url,
            }
            exercises.append(exercise_data)

        subtitles_key_name = VideoSubtitles.get_key_name('en', self.youtube_id)
        subtitles = VideoSubtitles.get_by_key_name(subtitles_key_name)
        subtitles_text = u""
        if subtitles:
            subtitles_json = subtitles.load_json()
            subtitles_text = u" ".join([sub["text"] for sub in subtitles_json if "text" in sub])

        return {
            "kind": "Video",
            "id": self.readable_id,
            "key_id": self.key().id(),
            "title": self.title,
            "description": self.description,
            "keywords": self.keywords,
            "ka_url": self.ka_url,
            "parent_topic": jsonify.jsonify(parent_supertopic_data),
            "related_exercises": jsonify.jsonify(exercises),
            "subtitles": subtitles_text,
        }
Пример #10
0
    def jsonified(*args, **kwargs):
        obj = func(*args, **kwargs)

        if isinstance(obj, current_app.response_class):
            return obj

        camel_cased = has_flask_request_context() and flask.request.values.get("casing") == "camel"

        return apijsonify.jsonify(obj, camel_cased=camel_cased)
Пример #11
0
 def render_jsonp(self, obj, camel_cased=False):
     if isinstance(obj, basestring):
         json_string = obj
     else:
         json_string = jsonify(obj, camel_cased=camel_cased)
     callback = self.request_string("callback")
     if callback:
         self.response.out.write("%s(%s)" % (callback, json_string))
     else:
         self.response.out.write(json_string)
Пример #12
0
 def render_jsonp(self, obj, camel_cased=False):
     if isinstance(obj, basestring):
         json_string = obj
     else:
         json_string = jsonify(obj, camel_cased=camel_cased)
     callback = self.request_string("callback")
     if callback:
         self.response.out.write("%s(%s)" % (callback, json_string))
     else:
         self.response.out.write(json_string)
Пример #13
0
    def jsonified(*args, **kwargs):
        obj = func(*args, **kwargs)

        if isinstance(obj, current_app.response_class):
            return obj

        camel_cased = (has_flask_request_context()
                       and flask.request.values.get("casing") == "camel")

        return apijsonify.jsonify(obj, camel_cased=camel_cased)
Пример #14
0
def get_badge_notifications_json():
    """ Retrieves the list of UserBadge objects just earned by the current
        user. Returns a jsonified list of the associated badge objects.
    """
    notifications_dict = notifications.Notifier.pop()

    badge_names = [n.badge_name for n in notifications_dict["badges"]]
    if len(badge_names) <= 0:
        return None

    return jsonify({"badges": get_badges(badge_names)}, camel_cased=True)
Пример #15
0
def get_badge_notifications_json():
    """ Retrieves the list of UserBadge objects just earned by the current
        user. Returns a jsonified list of the associated badge objects.
    """
    notifications_dict = notifications.Notifier.pop()

    badge_names = [n.badge_name for n in notifications_dict["badges"]]
    if len(badge_names) <= 0:
        return None

    return jsonify({"badges": get_badges(badge_names)}, camel_cased=True)
Пример #16
0
    def add_global_template_values(self, template_values):
        template_values["App"] = App
        template_values["None"] = None

        if not template_values.has_key("user_data"):
            user_data = UserData.current()
            template_values["user_data"] = user_data

        user_data = template_values["user_data"]
        email = user_data.email if user_data else ""
        template_values["username"] = user_data.nickname if user_data else ""
        template_values["user_email"] = email if not is_facebook_user_id(email) else ""
        template_values["viewer_profile_root"] = user_data.profile_root if user_data else "/profile/nouser"
        template_values["points"] = user_data.points if user_data else 0
        template_values["logged_in"] = not user_data.is_phantom if user_data else False
        template_values["http_host"] = os.environ["HTTP_HOST"]

        # Always insert a post-login request before our continue url
        template_values["continue"] = util.create_post_login_url(template_values.get("continue") or self.request.uri)
        template_values["login_url"] = "%s&direct=1" % util.create_login_url(template_values["continue"])
        template_values["logout_url"] = util.create_logout_url(self.request.uri)

        template_values["is_mobile"] = False
        template_values["is_mobile_capable"] = False
        template_values["is_ipad"] = False

        if self.is_mobile_capable():
            template_values["is_mobile_capable"] = True
            template_values["is_ipad"] = self.is_ipad()

            if "is_mobile_allowed" in template_values and template_values["is_mobile_allowed"]:
                template_values["is_mobile"] = self.is_mobile()

        # overridable hide_analytics querystring that defaults to true in dev
        # mode but false for prod.
        hide_analytics = self.request_bool("hide_analytics", App.is_dev_server)
        template_values["hide_analytics"] = hide_analytics

        # client-side error logging
        template_values["include_errorception"] = gandalf("errorception")

        if user_data:
            goals = GoalList.get_current_goals(user_data)
            goals_data = [g.get_visible_data() for g in goals]
            if goals_data:
                template_values["global_goals"] = jsonify(goals_data)

        app_host = self.request.host.split(":")[0]
        if app_host[0].isdigit():
            app_host = app_host.partition(".")[2]
        template_values["webengage_id"] = App.webengage_id.get(app_host, app_host) if App.webengage_id else ""

        return template_values
Пример #17
0
def exercise_message(exercise,
                     user_exercise_graph,
                     sees_graph=False,
                     review_mode=False):
    """Render UserExercise html for APIActionResults["exercise_message_html"] listener in khan-exercise.js.

    This is called **each time** a problem is either attempted or a hint is called (via /api/v1.py)
    returns nothing unless a user is struggling, proficient, etc. then it returns the appropriat template

    See Also: APIActionResults

    sees_graph is part of an ab_test to see if a small graph will help
    """

    # TODO(david): Should we show a message if the user gets a problem wrong
    #     after proficiency, to explain that this exercise needs to be reviewed?

    exercise_states = user_exercise_graph.states(exercise.name)

    if review_mode and user_exercise_graph.has_completed_review():
        filename = 'exercise_message_review_finished.html'

    elif (exercise_states['proficient'] and not exercise_states['reviewing']
          and not review_mode):
        if sees_graph:
            filename = 'exercise_message_proficient_withgraph.html'
        else:
            filename = 'exercise_message_proficient.html'

    elif exercise_states['struggling']:
        filename = 'exercise_message_struggling.html'
        suggested_prereqs = []
        if exercise.prerequisites:
            proficient_exercises = user_exercise_graph.proficient_exercise_names(
            )
            for prereq in exercise.prerequisites:
                if prereq not in proficient_exercises:
                    suggested_prereqs.append({
                        'ka_url':
                        exercise_models.Exercise.get_relative_url(prereq),
                        'display_name':
                        exercise_models.Exercise.to_display_name(prereq),
                    })
        exercise_states['suggested_prereqs'] = apijsonify.jsonify(
            suggested_prereqs)

    else:
        return None

    return shared_jinja.get().render_template(filename, **exercise_states)
Пример #18
0
    def get(self):

        version_name = self.request.get('version', 'edit')
        topics = map(
            str,
            self.request.get_all("topic") or self.request.get_all("topic[]"))

        edit_version = TopicVersion.get_by_id(version_name)
        if edit_version is None:
            default_version = TopicVersion.get_default_version()
            if default_version is None:
                # Assuming this is dev, there is an empty datastore and we need an import
                edit_version = TopicVersion.create_new_version()
                edit_version.edit = True
                edit_version.put()
                create_root(edit_version)
            else:
                raise Exception(
                    "Wait for setting default version to finish making an edit version."
                )

        if self.request.get('migrate', False):
            return self.topic_migration()
        if self.request.get('fixdupes', False):
            return self.fix_duplicates()

        root = Topic.get_root(edit_version)
        data = root.get_visible_data()
        tree_nodes = [data]

        template_values = {
            'edit_version': jsonify(edit_version),
            'tree_nodes': jsonify(tree_nodes)
        }

        self.render_jinja2_template('topics-admin.html', template_values)
        return
Пример #19
0
    def new(self):
        if not gandalf.bridge.gandalf("scratchpads"):
            self.error(404)
            raise ClosedBetaException

        env_js = {
            "user": user_models.UserData.current()
        }

        self.render_jinja2_template('scratchpads/code.html', {
            "pagetitle": "New Scratchpad",
            "selected_nav_link": "explore",
            "selected_id": "new-scratchpad",
            "env_js": jsonify.jsonify(env_js)
        })
Пример #20
0
    def index_tutorials(self):
        if not gandalf.bridge.gandalf("scratchpads"):
            self.error(404)
            raise ClosedBetaException

        self.render_jinja2_template("scratchpads/tutorials.html", {
            "selected_nav_link": "explore",
            "selected_id": "show-tutorials",
            "pagetitle": "Tutorials",
            "env_js": jsonify.jsonify({
                "tutorialScratchpads": list(scratchpad_models.Scratchpad
                    .get_all_tutorials()
                    .run(batch_size=1000))
            })
        })
Пример #21
0
    def return_login_json(handler, user_data, cont="/"):
        """Handle a successful login for a user by redirecting them
        to the PostLogin URL with the auth token, which will ultimately set
        the auth cookie for them.

        This level of indirection is needed since the Login/Register handlers
        must accept requests with password strings over https, but the rest
        of the site is not (yet) using https, and therefore must use a
        non-https cookie.
        """

        auth_token = AuthToken.for_user(user_data)
        handler.response.write(jsonify.jsonify({
                    'auth': auth_token.value,
                    'continue': cont
                }, camel_cased=True))
Пример #22
0
def topic_browser_data(version_number=None, show_topic_pages=False):
    """ Returns the JSON data necessary to render the topic browser embedded
        in the page header on the client. """

    if version_number:
        version = topic_models.TopicVersion.get_by_number(version_number)
    else:
        version = None

    root = topic_models.Topic.get_root(version)
    if not root:
        return ""

    tree = root.make_tree(types = ["Topics"])
    topics_list = topic_browser_get_topics(tree, show_topic_pages=show_topic_pages)

    return apijsonify.jsonify(topics_list)
Пример #23
0
def exercise_message(exercise, user_exercise_graph, sees_graph=False,
        review_mode=False):
    """Render UserExercise html for APIActionResults["exercise_message_html"] listener in khan-exercise.js.

    This is called **each time** a problem is either attempted or a hint is called (via /api/v1.py)
    returns nothing unless a user is struggling, proficient, etc. then it returns the appropriat template

    See Also: APIActionResults

    sees_graph is part of an ab_test to see if a small graph will help
    """

    # TODO(david): Should we show a message if the user gets a problem wrong
    #     after proficiency, to explain that this exercise needs to be reviewed?

    exercise_states = user_exercise_graph.states(exercise.name)

    if review_mode and user_exercise_graph.has_completed_review():
        filename = 'exercise_message_review_finished.html'

    elif (exercise_states['proficient'] and not exercise_states['reviewing'] and
            not review_mode):
        if sees_graph:
            filename = 'exercise_message_proficient_withgraph.html'
        else:
            filename = 'exercise_message_proficient.html'

    elif exercise_states['struggling']:
        filename = 'exercise_message_struggling.html'
        suggested_prereqs = []
        if exercise.prerequisites:
            proficient_exercises = user_exercise_graph.proficient_exercise_names()
            for prereq in exercise.prerequisites:
                if prereq not in proficient_exercises:
                    prereq_ex = Exercise.get_by_name(prereq)
                    suggested_prereqs.append({
                          'ka_url': prereq_ex.relative_url,
                          'display_name': prereq_ex.display_name,
                          })
        exercise_states['suggested_prereqs'] = apijsonify.jsonify(
                suggested_prereqs)

    else:
        return None

    return shared_jinja.get().render_template(filename, **exercise_states)
    def render(self, node, unused_handler):
        """Embed just a <script> tag that will in turn create an <iframe>."""
        name = node.attrib.get('name')
        caption = name.replace('_', ' ')

        return cElementTree.XML(
            """
<div style='width: 450px;'>
  Khan Academy Exercise: %s
  <br/>
  <script>
    // customize the style of the exercise iframe
    var ity_ef_style = "width: 770px;";
    var %s_data = %s;
  </script>
  <script src="%s" type="text/javascript"></script>
</div>""" % (
    cgi.escape(caption), name, jsonify.jsonify(get_user_exercise(name)), 'khan-exercises/embed.js?static:%s' % name))
    def render(self, node, unused_handler):
        """Embed just a <script> tag that will in turn create an <iframe>."""
        name = node.attrib.get('name')
        caption = name.replace('_', ' ')

        return cElementTree.XML("""
<div style='width: 450px;'>
  Khan Academy Exercise: %s
  <br/>
  <script>
    // customize the style of the exercise iframe
    var ity_ef_style = "width: 770px;";
    var %s_data = %s;
  </script>
  <script src="%s" type="text/javascript"></script>
</div>""" % (cgi.escape(caption), name, jsonify.jsonify(
            get_user_exercise(name)),
             'khan-exercises/embed.js?static:%s' % name))
Пример #26
0
    def return_login_json(handler, user_data, cont="/"):
        """Handle a successful login for a user by redirecting them
        to the PostLogin URL with the auth token, which will ultimately set
        the auth cookie for them.

        This level of indirection is needed since the Login/Register handlers
        must accept requests with password strings over https, but the rest
        of the site is not (yet) using https, and therefore must use a
        non-https cookie.
        """

        auth_token = AuthToken.for_user(user_data)
        handler.response.write(
            jsonify.jsonify({
                'auth': auth_token.value,
                'continue': cont
            },
                            camel_cased=True))
Пример #27
0
def topic_browser_data(version_number=None, show_topic_pages=False):
    """ Returns the JSON data necessary to render the topic browser embedded
        in the page header on the client. """

    if version_number:
        version = topic_models.TopicVersion.get_by_number(version_number)
    else:
        version = None

    root = topic_models.Topic.get_root(version)
    if not root:
        return ""

    tree = root.make_tree(types=["Topics"])
    topics_list = topic_browser_get_topics(tree,
                                           show_topic_pages=show_topic_pages)

    return apijsonify.jsonify(topics_list)
Пример #28
0
    def add_global_template_values(self, template_values):
        template_values['App'] = App
        template_values['None'] = None

        if not template_values.has_key('user_data'):
            user_data = UserData.current()
            template_values['user_data'] = user_data

        user_data = template_values['user_data']

        template_values['username'] = user_data.nickname if user_data else ""
        template_values['points'] = user_data.points if user_data else 0
        template_values[
            'logged_in'] = not user_data.is_phantom if user_data else False

        # Always insert a post-login request before our continue url
        template_values['continue'] = util.create_post_login_url(
            template_values.get('continue') or self.request.uri)
        template_values['login_url'] = (
            '%s&direct=1' % util.create_login_url(template_values['continue']))
        template_values['logout_url'] = util.create_logout_url(
            self.request.uri)

        template_values['is_mobile'] = False
        template_values['is_mobile_capable'] = False

        if self.is_mobile_capable():
            template_values['is_mobile_capable'] = True
            if 'is_mobile_allowed' in template_values and template_values[
                    'is_mobile_allowed']:
                template_values['is_mobile'] = self.is_mobile()

        # overridable hide_analytics querystring that defaults to true in dev
        # mode but false for prod.
        hide_analytics = self.request_bool("hide_analytics", App.is_dev_server)
        template_values['hide_analytics'] = hide_analytics

        if user_data:
            goals = GoalList.get_current_goals(user_data)
            goals_data = [g.get_visible_data() for g in goals]
            if goals_data:
                template_values['global_goals'] = jsonify(goals_data)

        return template_values
Пример #29
0
    def add_global_template_values(self, template_values):
        template_values['App'] = App
        template_values['None'] = None

        if not template_values.has_key('user_data'):
            user_data = UserData.current()
            template_values['user_data'] = user_data

        user_data = template_values['user_data']

        template_values['username'] = user_data.nickname if user_data else ""
        template_values['points'] = user_data.points if user_data else 0
        template_values['logged_in'] = not user_data.is_phantom if user_data else False

        # Always insert a post-login request before our continue url
        template_values['continue'] = util.create_post_login_url(template_values.get('continue') or self.request.uri)
        template_values['login_url'] = ('%s&direct=1' % util.create_login_url(template_values['continue']))
        template_values['logout_url'] = util.create_logout_url(self.request.uri)

        template_values['is_mobile'] = False
        template_values['is_mobile_capable'] = False

        if self.is_mobile_capable():
            template_values['is_mobile_capable'] = True
            if 'is_mobile_allowed' in template_values and template_values['is_mobile_allowed']:
                template_values['is_mobile'] = self.is_mobile()

        # overridable hide_analytics querystring that defaults to true in dev
        # mode but false for prod.
        hide_analytics = self.request_bool("hide_analytics", App.is_dev_server)
        template_values['hide_analytics'] = hide_analytics

        if user_data:
            goals = GoalList.get_current_goals(user_data)
            goals_data = [g.get_visible_data() for g in goals]
            if goals_data:
                template_values['global_goals'] = jsonify(goals_data)

        return template_values
Пример #30
0
    def show(self, slug, scratchpad_id):
        # Only the scratchpad_id is used to retrieve the Scratchpad - the slug
        # is ignored
        scratchpad = scratchpad_models.Scratchpad.get_by_id(int(scratchpad_id))

        if not scratchpad or scratchpad.deleted:
            self.error(404)
            raise PageNotFoundException

        user = (user_models.UserData.current() or
                user_models.UserData.pre_phantom())
        creator = user_models.UserData.get_from_user_id(scratchpad.user_id)
        creator_profile = profiles.util_profile.UserProfile.from_user(
            creator, actor=user)

        env_js = {
            "user": user,
            "scratchpad": scratchpad
        }

        selected_id = ""

        if scratchpad.category in ("official", "tutorial"):
            selected_id = "show-explorations"

        elif user and creator and user.user_id == creator.user_id:
            selected_id = "my-explorations"

        self.render_jinja2_template('scratchpads/code.html', {
            "pagetitle": scratchpad.title,
            "selected_nav_link": "explore",
            "selected_id": selected_id,
            "creator_profile": creator_profile,
            "scratchpad": scratchpad,
            "show_scratchpad_review_system": (user and user.developer and
                creator and creator.developer),
            "env_js": jsonify.jsonify(env_js)
        })
Пример #31
0
    def add_global_template_values(self, template_values):
        template_values['App'] = App
        template_values['None'] = None

        if not template_values.has_key('user_data'):
            user_data = user_models.UserData.current()
            template_values['user_data'] = user_data

        user_data = template_values['user_data']

        template_values['username'] = user_data.nickname if user_data else ""
        template_values['viewer_profile_root'] = user_data.profile_root if user_data else "/profile/nouser/"
        template_values['points'] = user_data.points if user_data else 0
        template_values['logged_in'] = not user_data.is_phantom if user_data else False
        template_values['http_host'] = os.environ["HTTP_HOST"]

        # Always insert a post-login request before our continue url
        template_values['continue'] = util.create_post_login_url(template_values.get('continue') or self.request.uri)
        template_values['login_url'] = ('%s&direct=1' % util.create_login_url(template_values['continue']))
        template_values['logout_url'] = util.create_logout_url(self.request.uri)

        template_values['is_mobile'] = False
        template_values['is_mobile_capable'] = False
        template_values['is_ipad'] = False

        if self.is_mobile_capable():
            template_values['is_mobile_capable'] = True
            template_values['is_ipad'] = self.is_ipad()

            if 'is_mobile_allowed' in template_values and template_values['is_mobile_allowed']:
                template_values['is_mobile'] = self.is_mobile()

        # overridable hide_analytics querystring that defaults to true in dev
        # mode but false for prod.
        hide_analytics = self.request_bool("hide_analytics", App.is_dev_server)
        template_values['hide_analytics'] = hide_analytics

        # client-side error logging
        template_values['include_errorception'] = gandalf('errorception')

        # Analytics
        template_values['mixpanel_enabled'] = gandalf('mixpanel_enabled')

        if False: # Enable for testing only
            template_values['mixpanel_test'] = "70acc4fce4511b89477ac005639cfee1"
            template_values['mixpanel_enabled'] = True
            template_values['hide_analytics'] = False

        if template_values['mixpanel_enabled']:
            template_values['mixpanel_id'] = gae_bingo.identity.identity()

        if not template_values['hide_analytics']:
            superprops_list = user_models.UserData.get_analytics_properties(user_data)

            # Create a superprops dict for MixPanel with a version number
            # Bump the version number if changes are made to the client-side analytics
            # code and we want to be able to filter by version.
            template_values['mixpanel_superprops'] = dict(superprops_list)

            # Copy over first 4 per-user properties for GA (5th is reserved for Bingo)
            template_values['ga_custom_vars'] = superprops_list[0:4]

        if user_data:
            user_goals = goals.models.GoalList.get_current_goals(user_data)
            goals_data = [g.get_visible_data() for g in user_goals]
            if goals_data:
                template_values['global_goals'] = jsonify(goals_data)

        # Disable topic browser in the header on mobile devices
        template_values['watch_topic_browser_enabled'] = not self.is_mobile_capable()

        template_values['show_topic_pages'] = True

        return template_values
Пример #32
0
 def render_json(self, obj, camel_cased=False):
     json_string = jsonify(obj, camel_cased=camel_cased)
     self.response.content_type = "application/json"
     self.response.out.write(json_string)
Пример #33
0
    def add_global_template_values(self, template_values):
        template_values['App'] = App
        template_values['None'] = None

        if not template_values.has_key('user_data'):
            user_data = user_models.UserData.current()
            template_values['user_data'] = user_data

        user_data = template_values['user_data']

        display_name = ""
        if user_data:
            display_name = user_data.nickname or user_data.username

        template_values['server_time'] = time.time()

        # TODO(marcia): Remove username, points, logged_in template values
        # since they should be encapsulated in this UserProfile object
        logged_in_user_profile = util_profile.UserProfile.from_user(user_data,
                                                                    user_data)
        template_values['logged_in_user_profile'] = logged_in_user_profile

        # TODO(benkomalo): rename this global template property from "username"
        #    as it's not really the user's username, but just a display name.
        template_values['username'] = display_name
        template_values['points'] = user_data.points if user_data else 0
        template_values['logged_in'] = not user_data.is_phantom if user_data else False
        template_values['http_host'] = os.environ["HTTP_HOST"]

        # Always insert a post-login request before our continue url
        template_values['continue'] = url_util.create_post_login_url(
            template_values.get('continue') or self.request.uri)
        template_values['login_url'] = ('%s&direct=1' %
                                        url_util.create_login_url(
                                            template_values['continue']))
        template_values['logout_url'] = url_util.create_logout_url(
            self.request.uri)

        # TODO(stephanie): these settings are temporary; for FB testing purposes only
        template_values['site_base_url'] = 'http://%s' % os.environ["HTTP_HOST"]

        template_values['is_mobile'] = False
        template_values['is_mobile_capable'] = False
        template_values['is_ipad'] = False

        if self.is_mobile_capable():
            template_values['is_mobile_capable'] = True
            template_values['is_ipad'] = self.is_ipad()

            if 'is_mobile_allowed' in template_values and template_values['is_mobile_allowed']:
                template_values['is_mobile'] = self.is_mobile()

        # overridable hide_analytics querystring that defaults to true in dev
        # mode but false for prod.
        hide_analytics = self.request_bool("hide_analytics", App.is_dev_server)
        template_values['hide_analytics'] = hide_analytics

        # client-side error logging
        template_values['include_errorception'] = gandalf('errorception')

        # Analytics
        template_values['mixpanel_enabled'] = gandalf('mixpanel_enabled')

        # Enable for Mixpanel testing only
        # You will need to ask Tom, Kitt, or Marcia to add you to the "Khan
        # Academy Test" project on Mixpanel so that you can see the results.
        if False:
            template_values['mixpanel_test'] = "70acc4fce4511b89477ac005639cfee1"
            template_values['mixpanel_enabled'] = True
            template_values['hide_analytics'] = False

        if template_values['mixpanel_enabled']:
            template_values['mixpanel_id'] = gae_bingo.identity.identity()

        if not template_values['hide_analytics']:
            superprops_list = user_models.UserData.get_analytics_properties(user_data)

            # Create a superprops dict for MixPanel with a version number
            # Bump the version number if changes are made to the client-side
            # analytics code and we want to be able to filter by version.
            template_values['mixpanel_superprops'] = dict(superprops_list)

            # Copy over first 4 per-user properties for GA
            # (The 5th is reserved for Bingo)
            template_values['ga_custom_vars'] = superprops_list[0:4]

        if user_data:
            user_goals = goals.models.GoalList.get_current_goals(user_data)
            goals_data = [g.get_visible_data() for g in user_goals]
            if goals_data:
                template_values['global_goals'] = jsonify(goals_data)

        badges_earned = badges.util_badges.get_badge_notifications_json()
        template_values['badges_earned'] = badges_earned

        # Disable topic browser in the header on mobile devices
        template_values['watch_topic_browser_enabled'] = not self.is_mobile_capable()

        template_values['show_topic_pages'] = True

        return template_values
Пример #34
0
 def output(self, name, obj, json_raw):
     self.response.write("//--- %s \n" % name)
     self.response.write(json_raw)
     self.response.write("\n")
     self.response.write(jsonify(obj))
     self.response.write("\n\n")
Пример #35
0
    def add_global_template_values(self, template_values):
        template_values['App'] = App
        template_values['None'] = None

        if not template_values.has_key('user_data'):
            user_data = user_models.UserData.current()
            template_values['user_data'] = user_data

        user_data = template_values['user_data']

        template_values['username'] = user_data.nickname if user_data else ""
        template_values['viewer_profile_root'] = user_data.profile_root if user_data else "/profile/nouser/"
        template_values['points'] = user_data.points if user_data else 0
        template_values['logged_in'] = not user_data.is_phantom if user_data else False
        template_values['http_host'] = os.environ["HTTP_HOST"]

        # Always insert a post-login request before our continue url
        template_values['continue'] = util.create_post_login_url(template_values.get('continue') or self.request.uri)
        template_values['login_url'] = ('%s&direct=1' % util.create_login_url(template_values['continue']))
        template_values['logout_url'] = util.create_logout_url(self.request.uri)

        template_values['is_mobile'] = False
        template_values['is_mobile_capable'] = False
        template_values['is_ipad'] = False

        if self.is_mobile_capable():
            template_values['is_mobile_capable'] = True
            template_values['is_ipad'] = self.is_ipad()

            if 'is_mobile_allowed' in template_values and template_values['is_mobile_allowed']:
                template_values['is_mobile'] = self.is_mobile()

        # overridable hide_analytics querystring that defaults to true in dev
        # mode but false for prod.
        hide_analytics = self.request_bool("hide_analytics", App.is_dev_server)
        template_values['hide_analytics'] = hide_analytics

        # client-side error logging
        template_values['include_errorception'] = gandalf('errorception')

        # Analytics
        template_values['mixpanel_enabled'] = gandalf('mixpanel_enabled')

        if False: # Enable for testing only
            template_values['mixpanel_test'] = "70acc4fce4511b89477ac005639cfee1"
            template_values['mixpanel_enabled'] = True
            template_values['hide_analytics'] = False

        if template_values['mixpanel_enabled']:
            template_values['mixpanel_id'] = gae_bingo.identity.identity()

        if not template_values['hide_analytics']:
            superprops_list = user_models.UserData.get_analytics_properties(user_data)

            # Create a superprops dict for MixPanel with a version number
            # Bump the version number if changes are made to the client-side analytics
            # code and we want to be able to filter by version.
            template_values['mixpanel_superprops'] = dict(superprops_list)

            # Copy over first 4 per-user properties for GA (5th is reserved for Bingo)
            template_values['ga_custom_vars'] = superprops_list[0:4]

        if user_data:
            user_goals = goals.models.GoalList.get_current_goals(user_data)
            goals_data = [g.get_visible_data() for g in user_goals]
            if goals_data:
                template_values['global_goals'] = jsonify(goals_data)

        # Disable topic browser in the header on mobile devices
        template_values['watch_topic_browser_enabled'] = not self.is_mobile_capable()

        # Begin topic pages A/B test
        if template_values['mixpanel_enabled']:
            show_topic_pages = ab_test("Show topic pages", ["show", "hide"], ["topic_pages_view_page", "topic_pages_started_video", "topic_pages_completed_video"])
            analytics_bingo = {"name": "Bingo: Topic pages", "value": show_topic_pages}
            template_values['analytics_bingo'] = analytics_bingo
        else:
            show_topic_pages = "hide"
        template_values['show_topic_pages'] = (show_topic_pages == "show")
        # End topic pages A/B test

        return template_values
Пример #36
0
 def render_json(self, obj, camel_cased=False):
     json_string = jsonify(obj, camel_cased=camel_cased)
     self.response.content_type = "application/json"
     self.response.out.write(json_string)
Пример #37
0
    def get(self, topic_path, exid=None):

        title = None
        description = None
        review_mode = "review" == topic_path

        practice_mode = bool(exid)
        practice_exercise = None

        topic = None
        topic_exercise_badge = None

        user_exercises = None

        if review_mode:

            title = "Review"

        else:

            topic_path_list = topic_path.split("/")
            topic_id = topic_path_list[-1]

            if len(topic_id) > 0:
                topic = topic_models.Topic.get_by_id(topic_id)

            # Topics are required
            if topic:
                title = topic.standalone_title
                topic_exercise_badge = topic.get_exercise_badge()

            if exid:
                practice_exercise = exercise_models.Exercise.get_by_name(exid)

                # Exercises are not required but must be valid if supplied
                if not practice_exercise:
                    raise MissingExerciseException("Missing exercise w/ exid '%s'" % exid)

                title = practice_exercise.display_name
                description = practice_exercise.description

        user_data = user_models.UserData.current() or user_models.UserData.pre_phantom()

        if practice_mode:
            # Practice mode involves a single exercise only
            user_exercises = exercise_models.UserExercise.next_in_practice(user_data, practice_exercise)
        elif review_mode:
            # Review mode sends down up to a certain limit of review exercises
            user_exercises = exercise_models.UserExercise.next_in_review(user_data, n=MAX_CARDS_PER_REVIEW_STACK)
        else:
            # Topics mode context switches between multiple exercises
            user_exercises = exercise_models.UserExercise.next_in_topic(user_data, topic)

        if len(user_exercises) == 0:
            # If something has gone wrong and we didn't get any UserExercises,
            # somebody could've hit the /review URL without any review problems
            # or we hit another issue. Send 'em back to the dashboard for now.
            self.redirect("/exercisedashboard")
            return

        stack = get_dummy_stack(review_mode)
        cards = get_review_cards(user_exercises) if review_mode else get_problem_cards(user_exercises)

        # We have to compute this and save it before JSON-ifiying because it
        # modifies user_exercises, which we JSONify as well.
        problem_history_values = self.problem_history_values(user_data, user_exercises[0]) if practice_mode else {}

        template_values = {
            "title": title,
            "description": description,
            "selected_nav_link": "practice",
            "renderable": True,
            "read_only": False,
            "stack_json": jsonify(stack, camel_cased=True),
            "cards_json": jsonify(cards, camel_cased=True),
            "review_mode_json": jsonify(review_mode, camel_cased=True),
            "practice_mode_json": jsonify(practice_mode, camel_cased=True),
            "topic_json": jsonify(topic, camel_cased=True),
            "topic_exercise_badge_json": jsonify(topic_exercise_badge, camel_cased=True),
            "practice_exercise_json": jsonify(practice_exercise, camel_cased=True),
            "user_data_json": jsonify(user_data, camel_cased=True),
            "user_exercises_json": jsonify(user_exercises, camel_cased=True),
            "show_intro": user_data.is_phantom or user_data.is_pre_phantom,
        }

        # Add disabled browser warnings
        template_values.update(self.browser_support_values())

        # Add history data to template context if we're viewing an old problem
        template_values.update(problem_history_values)

        self.render_jinja2_template("exercises/exercise_template.html", template_values)
Пример #38
0
    def get(self, exid=None):

        # TODO(david): Is there some webapp2 magic that will allow me not to
        #     repeat this URL string in main.py?
        review_mode = self.request.path == "/review" 

        if not exid and not review_mode:
            self.redirect("/exercise/%s" % self.request_string("exid", default="addition_1"))
            return

        user_data = models.UserData.current() or models.UserData.pre_phantom()
        user_exercise_graph = models.UserExerciseGraph.get(user_data)

        if review_mode:
            # Take the first review exercise if available
            exid = (user_exercise_graph.review_exercise_names() or
                    user_exercise_graph.proficient_exercise_names() or
                    ["addition_1"])[0]
            reviews_left_count = user_exercise_graph.reviews_left_count()

        exercise = models.Exercise.get_by_name(exid)

        if not exercise:
            raise MissingExerciseException("Missing exercise w/ exid '%s'" % exid)

        user_exercise = user_data.get_or_insert_exercise(exercise)

        # Cache these so we don't have to worry about future lookups
        user_exercise.exercise_model = exercise
        user_exercise._user_data = user_data
        user_exercise._user_exercise_graph = user_exercise_graph
        user_exercise.summative = exercise.summative

        # Temporarily work around in-app memory caching bug
        exercise.user_exercise = None

        problem_number = self.request_int('problem_number', default=(user_exercise.total_done + 1))

        user_data_student = self.request_student_user_data(legacy=True) or user_data
        if user_data_student.key_email != user_data.key_email and not user_data_student.is_visible_to(user_data):
            user_data_student = user_data

        viewing_other = user_data_student.key_email != user_data.key_email

        # Can't view your own problems ahead of schedule
        if not viewing_other and problem_number > user_exercise.total_done + 1:
            problem_number = user_exercise.total_done + 1

        # When viewing another student's problem or a problem out-of-order, show read-only view
        read_only = viewing_other or problem_number != (user_exercise.total_done + 1)

        exercise_template_html = exercise_template()

        exercise_body_html, exercise_inline_script, exercise_inline_style, data_require, sha1 = exercise_contents(exercise)
        user_exercise.exercise_model.sha1 = sha1

        user_exercise.exercise_model.related_videos = map(lambda exercise_video: exercise_video.video, user_exercise.exercise_model.related_videos_fetch())
        for video in user_exercise.exercise_model.related_videos:
            video.id = video.key().id()

        renderable = True

        if read_only:
            # Override current problem number and user being inspected
            # so proper exercise content will be generated
            user_exercise.total_done = problem_number - 1
            user_exercise.user = user_data_student.user
            user_exercise.read_only = True

            if not self.request_bool("renderable", True):
                # We cannot render old problems that were created in the v1 exercise framework.
                renderable = False

            query = models.ProblemLog.all()
            query.filter("user = "******"exercise = ", exid)

            # adding this ordering to ensure that query is served by an existing index.
            # could be ok if we remove this
            query.order('time_done')
            problem_logs = query.fetch(500)

            problem_log = None
            for p in problem_logs:
                if p.problem_number == problem_number:
                    problem_log = p
                    break

            user_activity = []
            previous_time = 0

            if not problem_log or not hasattr(problem_log, "hint_after_attempt_list"):
                renderable = False
            else:
                # Don't include incomplete information
                problem_log.hint_after_attempt_list = filter(lambda x: x != -1, problem_log.hint_after_attempt_list)

                while len(problem_log.hint_after_attempt_list) and problem_log.hint_after_attempt_list[0] == 0:
                    user_activity.append([
                        "hint-activity",
                        "0",
                        max(0, problem_log.hint_time_taken_list[0] - previous_time)
                        ])

                    previous_time = problem_log.hint_time_taken_list[0]
                    problem_log.hint_after_attempt_list.pop(0)
                    problem_log.hint_time_taken_list.pop(0)

                # For each attempt, add it to the list and then add any hints
                # that came after it
                for i in range(0, len(problem_log.attempts)):
                    user_activity.append([
                        "correct-activity" if problem_log.correct else "incorrect-activity",
                        unicode(problem_log.attempts[i] if problem_log.attempts[i] else 0),
                        max(0, problem_log.time_taken_attempts[i] - previous_time)
                        ])

                    previous_time = 0

                    # Here i is 0-indexed but problems are numbered starting at 1
                    while (len(problem_log.hint_after_attempt_list) and
                            problem_log.hint_after_attempt_list[0] == i + 1):
                        user_activity.append([
                            "hint-activity",
                            "0",
                            max(0, problem_log.hint_time_taken_list[0] - previous_time)
                            ])

                        previous_time = problem_log.hint_time_taken_list[0]
                        # easiest to just pop these instead of maintaining
                        # another index into this list
                        problem_log.hint_after_attempt_list.pop(0)
                        problem_log.hint_time_taken_list.pop(0)

                user_exercise.user_activity = user_activity

                if problem_log.count_hints is not None:
                    user_exercise.count_hints = problem_log.count_hints

                user_exercise.current = problem_log.sha1 == sha1
        else:
            # Not read_only
            suggested_exercise_names = user_exercise_graph.suggested_exercise_names()
            if exercise.name in suggested_exercise_names:
                bingo('suggested_activity_visit_suggested_exercise')

        is_webos = self.is_webos()
        browser_disabled = is_webos or self.is_older_ie()
        renderable = renderable and not browser_disabled

        url_pattern = "/exercise/%s?student_email=%s&problem_number=%d"
        user_exercise.previous_problem_url = url_pattern % \
            (exid, user_data_student.key_email, problem_number - 1)
        user_exercise.next_problem_url = url_pattern % \
            (exid, user_data_student.key_email, problem_number + 1)

        user_exercise_json = jsonify.jsonify(user_exercise)

        template_values = {
            'exercise': exercise,
            'user_exercise_json': user_exercise_json,
            'exercise_body_html': exercise_body_html,
            'exercise_template_html': exercise_template_html,
            'exercise_inline_script': exercise_inline_script,
            'exercise_inline_style': exercise_inline_style,
            'data_require': data_require,
            'read_only': read_only,
            'selected_nav_link': 'practice',
            'browser_disabled': browser_disabled,
            'is_webos': is_webos,
            'renderable': renderable,
            'issue_labels': ('Component-Code,Exercise-%s,Problem-%s' % (exid, problem_number)),
            'alternate_hints_treatment': ab_test('Hints or Show Solution Dec 10',
                ViewExercise._hints_ab_test_alternatives,
                ViewExercise._hints_conversion_names,
                ViewExercise._hints_conversion_types,
                'Hints or Show Solution Nov 5'),
            'reviews_left_count': reviews_left_count if review_mode else "null",
        }
        self.render_jinja2_template("exercise_template.html", template_values)
Пример #39
0
    def get(self):
        if not hasattr(secrets, 'ka_api_consumer_key') or    \
           not hasattr(secrets, 'ka_api_consumer_secret') or \
           not hasattr(secrets_dev, 'ka_api_token_key') or   \
           not hasattr(secrets_dev, 'ka_api_token_secret'):
            return self.redirect("/")

        self.setup_oauth()

        self.email = self.request_string("email")
        if not self.email:
            raise "Must supply email for user to import"

        params = copy.copy(self._default_kinds)
        params.update(self.request.params)

        # get proper user from addition 1 userexercise
        user_id_json = json.loads(
            self.api("/api/v1/user/exercises/addition_1"))
        user = users.User(user_id_json['user'])

        # UserData
        user_data_json_raw = self.api("/api/v1/user")
        user_data = UserData.from_json(json.loads(user_data_json_raw),
                                       user=user)
        self.output('user_data', user_data, user_data_json_raw)
        user_data.put()

        if 'UserVideo' in params:
            user_videos_json = json.loads(self.api("/api/v1/user/videos"))
            user_videos = []
            for user_video_json in user_videos_json[:params['UserVideo']]:
                user_video = UserVideo.from_json(user_video_json,
                                                 user_data=user_data)
                user_videos.append(user_video)
                self.output('user_video', user_video, jsonify(user_video_json))

            video_logs = defaultdict(list)
            if 'VideoLog' in params:
                for user_video in user_videos:
                    ytid = user_video.video.youtube_id
                    video_logs_json = json.loads(
                        self.api("/api/v1/user/videos/%s/log" % ytid))
                    problem_log_limit = params['ProblemLog']
                    for video_log_json in video_logs_json[:problem_log_limit]:
                        video_log = VideoLog.from_json(video_log_json,
                                                       user_video.video, user)
                        video_logs[user_video].append(video_log)
                        self.output("video_log", video_log,
                                    jsonify(video_log_json))

                # delete old video logs
                query = VideoLog.all(keys_only=True)
                query.filter('user ='******'UserExercise' in params:
            user_exercises_json = json.loads(
                self.api("/api/v1/user/exercises"))
            user_exercises = []
            exercise_limit = params['UserExercise']
            for user_exercise_json in user_exercises_json[:exercise_limit]:
                user_exercise = UserExercise.from_json(user_exercise_json,
                                                       user_data)
                if user_exercise:
                    user_exercises.append(user_exercise)
                    self.output("user_exercise", user_exercise,
                                jsonify(user_exercise_json))

            problem_logs = defaultdict(list)
            if 'ProblemLog' in params:
                for user_exercise in user_exercises:
                    problem_logs_json = json.loads(
                        self.api("/api/v1/user/exercises/%s/log" %
                                 user_exercise.exercise))
                    problem_log_limit = params['ProblemLog']
                    problem_logs_json = problem_logs_json[:problem_log_limit]
                    for problem_log_json in problem_logs_json:
                        problem_log = ProblemLog.from_json(
                            problem_log_json,
                            user_data=user_data,
                            exercise=user_exercise.exercise_model)
                        problem_logs[user_exercise].append(problem_log)
                        self.output("problem_log", problem_log,
                                    jsonify(problem_log_json))

            db.put(user_exercises)
            for k, v in problem_logs.iteritems():
                db.put(v)

        if 'Goal' in params:
            with AutoNowDisabled(Goal):
                goals_json = json.loads(self.api("/api/v1/user/goals"))
                goals = []
                for goal_json in goals_json[:params['Goal']]:
                    goal = Goal.from_json(goal_json, user_data=user_data)
                    goals.append(goal)
                    self.output("goal", goal, jsonify(goal_json))

                db.put(goals)

                # need to tell the userdata that it has goals
                user_data.has_current_goals = not all(
                    [g.completed for g in goals])
                user_data.put()
Пример #40
0
    def get(self):
        user_data = models.UserData.current() or models.UserData.pre_phantom()

        exid = self.request_string("exid", default="addition_1")
        exercise = models.Exercise.get_by_name(exid)

        if not exercise:
            raise MissingExerciseException("Missing exercise w/ exid '%s'" %
                                           exid)

        user_exercise = user_data.get_or_insert_exercise(exercise)

        # Cache this so we don't have to worry about future lookups
        user_exercise.exercise_model = exercise
        user_exercise._user_data = user_data
        user_exercise.summative = exercise.summative

        # Temporarily work around in-app memory caching bug
        exercise.user_exercise = None

        problem_number = self.request_int('problem_number',
                                          default=(user_exercise.total_done +
                                                   1))

        user_data_student = self.request_user_data(
            "student_email") or user_data
        if user_data_student.key_email != user_data.key_email and not user_data_student.is_visible_to(
                user_data):
            user_data_student = user_data

        viewing_other = user_data_student.key_email != user_data.key_email

        # Can't view your own problems ahead of schedule
        if not viewing_other and problem_number > user_exercise.total_done + 1:
            problem_number = user_exercise.total_done + 1

        # When viewing another student's problem or a problem out-of-order, show read-only view
        read_only = viewing_other or problem_number != (
            user_exercise.total_done + 1)

        exercise_template_html = exercise_template()

        exercise_body_html, exercise_inline_script, exercise_inline_style, data_require, sha1 = exercise_contents(
            exercise)
        user_exercise.exercise_model.sha1 = sha1

        user_exercise.exercise_model.related_videos = map(
            lambda exercise_video: exercise_video.video,
            user_exercise.exercise_model.related_videos_fetch())
        for video in user_exercise.exercise_model.related_videos:
            video.id = video.key().id()

        renderable = True

        if read_only:
            # Override current problem number and user being inspected
            # so proper exercise content will be generated
            user_exercise.total_done = problem_number - 1
            user_exercise.user = user_data_student.user
            user_exercise.read_only = True

            if not self.request_bool("renderable", True):
                # We cannot render old problems that were created in the v1 exercise framework.
                renderable = False

            query = models.ProblemLog.all()
            query.filter("user = "******"exercise = ", exid)

            # adding this ordering to ensure that query is served by an existing index.
            # could be ok if we remove this
            query.order('time_done')
            problem_logs = query.fetch(500)

            problem_log = None
            for p in problem_logs:
                if p.problem_number == problem_number:
                    problem_log = p
                    break

            user_activity = []
            previous_time = 0

            if not problem_log or not hasattr(problem_log,
                                              "hint_after_attempt_list"):
                renderable = False
            else:
                # Don't include incomplete information
                problem_log.hint_after_attempt_list = filter(
                    lambda x: x != -1, problem_log.hint_after_attempt_list)

                while len(problem_log.hint_after_attempt_list
                          ) and problem_log.hint_after_attempt_list[0] == 0:
                    user_activity.append([
                        "hint-activity", "0",
                        max(
                            0, problem_log.hint_time_taken_list[0] -
                            previous_time)
                    ])

                    previous_time = problem_log.hint_time_taken_list[0]
                    problem_log.hint_after_attempt_list.pop(0)
                    problem_log.hint_time_taken_list.pop(0)

                # For each attempt, add it to the list and then add any hints
                # that came after it
                for i in range(0, len(problem_log.attempts)):
                    user_activity.append([
                        "correct-activity"
                        if problem_log.correct else "incorrect-activity",
                        unicode(problem_log.attempts[i] if problem_log.
                                attempts[i] else 0),
                        max(0,
                            problem_log.time_taken_attempts[i] - previous_time)
                    ])

                    previous_time = 0

                    # Here i is 0-indexed but problems are numbered starting at 1
                    while len(
                            problem_log.hint_after_attempt_list
                    ) and problem_log.hint_after_attempt_list[0] == i + 1:
                        user_activity.append([
                            "hint-activity", "0",
                            max(
                                0, problem_log.hint_time_taken_list[0] -
                                previous_time)
                        ])

                        previous_time = problem_log.hint_time_taken_list[0]
                        # easiest to just pop these instead of maintaining
                        # another index into this list
                        problem_log.hint_after_attempt_list.pop(0)
                        problem_log.hint_time_taken_list.pop(0)

                user_exercise.user_activity = user_activity

                if problem_log.count_hints is not None:
                    user_exercise.count_hints = problem_log.count_hints

        is_webos = self.is_webos()
        browser_disabled = is_webos or self.is_older_ie()
        renderable = renderable and not browser_disabled

        url_pattern = "/exercises?exid=%s&student_email=%s&problem_number=%d"
        user_exercise.previous_problem_url = url_pattern % \
            (exid, user_data_student.key_email , problem_number-1)
        user_exercise.next_problem_url = url_pattern % \
            (exid, user_data_student.key_email , problem_number+1)

        user_exercise_json = jsonify.jsonify(user_exercise)

        template_values = {
            'exercise':
            exercise,
            'user_exercise_json':
            user_exercise_json,
            'exercise_body_html':
            exercise_body_html,
            'exercise_template_html':
            exercise_template_html,
            'exercise_inline_script':
            exercise_inline_script,
            'exercise_inline_style':
            exercise_inline_style,
            'data_require':
            data_require,
            'read_only':
            read_only,
            'selected_nav_link':
            'practice',
            'browser_disabled':
            browser_disabled,
            'is_webos':
            is_webos,
            'renderable':
            renderable,
            'issue_labels':
            ('Component-Code,Exercise-%s,Problem-%s' % (exid, problem_number)),
            'alternate_hints_treatment':
            ab_test('Hints or Show Solution',
                    ViewExercise._hints_ab_test_alternatives,
                    ViewExercise._hints_conversion_names,
                    ViewExercise._hints_conversion_types)
        }

        self.render_jinja2_template("exercise_template.html", template_values)
Пример #41
0
    def get(self):
        if (
            not hasattr(secrets, "ka_api_consumer_key")
            or not hasattr(secrets, "ka_api_consumer_secret")
            or not hasattr(secrets_dev, "ka_api_token_key")
            or not hasattr(secrets_dev, "ka_api_token_secret")
        ):
            return self.redirect("/")

        self.setup_oauth()

        self.email = self.request_string("email")
        if not self.email:
            raise "Must supply email for user to import"

        params = copy.copy(self._default_kinds)
        params.update(self.request.params)

        # get proper user from addition 1 userexercise
        user_id_json = json.loads(self.api("/api/v1/user/exercises/addition_1"))
        user = users.User(user_id_json["user"])

        # UserData
        user_data_json_raw = self.api("/api/v1/user")
        user_data = UserData.from_json(json.loads(user_data_json_raw), user=user)
        self.output("user_data", user_data, user_data_json_raw)
        user_data.put()

        if "UserVideo" in params:
            user_videos_json = json.loads(self.api("/api/v1/user/videos"))
            user_videos = []
            for user_video_json in user_videos_json[: params["UserVideo"]]:
                user_video = UserVideo.from_json(user_video_json, user_data=user_data)
                user_videos.append(user_video)
                self.output("user_video", user_video, jsonify(user_video_json))

            video_logs = defaultdict(list)
            if "VideoLog" in params:
                for user_video in user_videos:
                    ytid = user_video.video.youtube_id
                    video_logs_json = json.loads(self.api("/api/v1/user/videos/%s/log" % ytid))
                    problem_log_limit = params["ProblemLog"]
                    for video_log_json in video_logs_json[:problem_log_limit]:
                        video_log = VideoLog.from_json(video_log_json, user_video.video, user)
                        video_logs[user_video].append(video_log)
                        self.output("video_log", video_log, jsonify(video_log_json))

                # delete old video logs
                query = VideoLog.all(keys_only=True)
                query.filter("user ="******"UserExercise" in params:
            user_exercises_json = json.loads(self.api("/api/v1/user/exercises"))
            user_exercises = []
            exercise_limit = params["UserExercise"]
            for user_exercise_json in user_exercises_json[:exercise_limit]:
                user_exercise = UserExercise.from_json(user_exercise_json, user_data)
                if user_exercise:
                    user_exercises.append(user_exercise)
                    self.output("user_exercise", user_exercise, jsonify(user_exercise_json))

            problem_logs = defaultdict(list)
            if "ProblemLog" in params:
                for user_exercise in user_exercises:
                    problem_logs_json = json.loads(self.api("/api/v1/user/exercises/%s/log" % user_exercise.exercise))
                    problem_log_limit = params["ProblemLog"]
                    problem_logs_json = problem_logs_json[:problem_log_limit]
                    for problem_log_json in problem_logs_json:
                        problem_log = ProblemLog.from_json(
                            problem_log_json, user_data=user_data, exercise=user_exercise.exercise_model
                        )
                        problem_logs[user_exercise].append(problem_log)
                        self.output("problem_log", problem_log, jsonify(problem_log_json))

            db.put(user_exercises)
            for k, v in problem_logs.iteritems():
                db.put(v)

        if "Goal" in params:
            with AutoNowDisabled(Goal):
                goals_json = json.loads(self.api("/api/v1/user/goals"))
                goals = []
                for goal_json in goals_json[: params["Goal"]]:
                    goal = Goal.from_json(goal_json, user_data=user_data)
                    goals.append(goal)
                    self.output("goal", goal, jsonify(goal_json))

                db.put(goals)

                # need to tell the userdata that it has goals
                user_data.has_current_goals = not all([g.completed for g in goals])
                user_data.put()
Пример #42
0
def jsonify(obj, camel_cased):
    return apijsonify.jsonify(obj, camel_cased=camel_cased)
Пример #43
0
def jsonify(obj, camel_cased):
    return apijsonify.jsonify(obj, camel_cased=camel_cased)
Пример #44
0
    def get(self, topic_path, exid=None):

        title = None
        description = None
        review_mode = "review" == topic_path

        practice_mode = bool(exid)
        practice_exercise = None

        topic = None
        topic_exercise_badge = None

        user_exercises = None

        if review_mode:

            title = "Review"

        else:

            topic_path_list = topic_path.split('/')
            topic_id = topic_path_list[-1]

            if len(topic_id) > 0:
                topic = topic_models.Topic.get_by_id(topic_id)

            # Topics are required
            if not topic:
                raise MissingExerciseException(
                    "Exercise '%s' is missing a topic" % exid)

            title = topic.standalone_title
            topic_exercise_badge = topic.get_exercise_badge()

            if exid:
                practice_exercise = exercise_models.Exercise.get_by_name(exid)

                # Exercises are not required but must be valid if supplied
                if not practice_exercise:
                    raise MissingExerciseException(
                        "Missing exercise w/ exid '%s'" % exid)

                title = practice_exercise.display_name
                description = practice_exercise.description

        user_data = user_models.UserData.current(
        ) or user_models.UserData.pre_phantom()

        if practice_mode:
            # Practice mode involves a single exercise only
            user_exercises = exercise_models.UserExercise.next_in_practice(
                user_data, practice_exercise)
        elif review_mode:
            # Review mode sends down up to a certain limit of review exercises
            user_exercises = exercise_models.UserExercise.next_in_review(
                user_data, n=MAX_CARDS_PER_REVIEW_STACK)
        else:
            # Topics mode context switches between multiple exercises
            user_exercises = exercise_models.UserExercise.next_in_topic(
                user_data, topic)

        if len(user_exercises) == 0:
            # If something has gone wrong and we didn't get any UserExercises,
            # somebody could've hit the /review URL without any review problems
            # or we hit another issue. Send 'em back to the dashboard for now.
            self.redirect("/exercisedashboard")
            return

        stack = get_dummy_stack(review_mode)
        cards = (get_review_cards(user_exercises)
                 if review_mode else get_problem_cards(user_exercises))

        # We have to compute this and save it before JSON-ifiying because it
        # modifies user_exercises, which we JSONify as well.
        problem_history_values = (self.problem_history_values(
            user_data, user_exercises[0]) if practice_mode else {})

        template_values = {
            "title":
            title,
            "description":
            description,
            "selected_nav_link":
            "practice",
            "renderable":
            True,
            "read_only":
            False,
            "stack_json":
            jsonify(stack, camel_cased=True),
            "cards_json":
            jsonify(cards, camel_cased=True),
            "review_mode_json":
            jsonify(review_mode, camel_cased=True),
            "practice_mode_json":
            jsonify(practice_mode, camel_cased=True),
            "topic_json":
            jsonify(topic, camel_cased=True),
            "topic_exercise_badge_json":
            jsonify(topic_exercise_badge, camel_cased=True),
            "practice_exercise_json":
            jsonify(practice_exercise, camel_cased=True),
            "user_data_json":
            jsonify(user_data, camel_cased=True),
            "user_exercises_json":
            jsonify(user_exercises, camel_cased=True),
            "show_intro":
            user_data.is_phantom or user_data.is_pre_phantom,
        }

        # Add disabled browser warnings
        template_values.update(self.browser_support_values())

        # Add history data to template context if we're viewing an old problem
        template_values.update(problem_history_values)

        self.render_jinja2_template("exercises/exercise_template.html",
                                    template_values)
Пример #45
0
 def output(self, name, obj, json_raw):
     self.response.write("//--- %s \n" % name)
     self.response.write(json_raw)
     self.response.write("\n")
     self.response.write(jsonify(obj))
     self.response.write("\n\n")