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)
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
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)
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
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
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)
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
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, }
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)
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)
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)
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)
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
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)
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
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) })
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)) }) })
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))
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)
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))
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))
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)
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
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
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) })
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
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)
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
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")
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
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)
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)
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()
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)
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()
def jsonify(obj, camel_cased): return apijsonify.jsonify(obj, camel_cased=camel_cased)
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)