def set_from_file(self): self.init_code = "" self.iter_code = "" self.text = "\n\n<h3>ERROR: no code exists for question '{}' for language '{}'!</h3>".format( self.q_id, PageLanguage.toStr(self.language)) self.page.add_lines( "\n<!-- Rendering question '{}' for language '{}' -->\n\n".format( self.q_id, PageLanguage.toStr(self.language))) q = self.repository.get_question(self.q_id) if q is None: return if "init.lua" in q.keys(): #logging.debug("%s, %s", str(q.keys()), str(q)) self.init_code = q["init.lua"] if "iter.lua" in q.keys(): self.iter_code = q["iter.lua"] text_key = "text." + PageLanguage.toStr(self.language) if text_key in q.keys(): self.text = q[text_key] else: logging.error("Question '{}' not found ({}).".format( self.q_id, text_key))
def get_prev_next_questions_browse_url(self): prev_question, next_question = self.choose_next_question_browse() if prev_question: url_prev = self.page.page_params.create_url( op=PageOperation.BROWSE, language=PageLanguage.toStr( self.page.page_params.get_param("language")), q_id=prev_question["name"], q_num="0", beta=True if self.page.page_params.get_param("beta") else None) else: url_prev = None if next_question: url_next = self.page.page_params.create_url( op=PageOperation.BROWSE, language=PageLanguage.toStr( self.page.page_params.get_param("language")), q_id=next_question["name"], q_num="0", beta=True if self.page.page_params.get_param("beta") else None) else: url_next = None return url_prev, url_next
def prepare_user_stats_chart(pg, u_ID): if (u_ID == "UNKNOWN" or u_ID == "local:UNKNOWN"): user_stats = context.c.session.get("anon_stats") else: user_stats = render_stats_data(pg, u_ID) pg.template_params["template_name"] = pg.page_params.get_param("language").value + "/" + "stats.html.j2" pg.template_params["class_gen_str"] = pg.get_messages()["year"] #pg.template_params["class_gen_str"] = 'Razred' pg.template_params["stats"] = {} if user_stats: sorted_levels = list(user_stats['level'].keys()) sorted_levels.sort() else: sorted_levels = [] #for level_key, level_val in user_stats['level'].items(): for level_key in sorted_levels: level_val = user_stats['level'][level_key] level_short=level_val['level_short'] pg.template_params["stats"][level_key] = {} pg.template_params["stats"][level_key]['level_short'] = level_short for theme_key, theme_val in level_val['theme'].items(): pg.template_params["stats"][level_key][theme_key] = draw_chart(theme_val['subtheme'], pg) pg.template_params["url_year"] = pg.page_params.create_url( op=PageOperation.MENU_YEAR, \ language = PageLanguage.toStr(pg.page_params.get_param("language")), year = "", \ theme = "", \ subtheme = "", \ topic = "", \ difficulty = "", \ period = "", \ beta = True if pg.page_params.get_param("beta") else None) pg.template_params["url_theme"] = pg.page_params.create_url( op=PageOperation.MENU_THEME, \ language = PageLanguage.toStr(pg.page_params.get_param("language")), year=pg.page_params.get_param("year"), \ theme = "", \ subtheme = "", \ topic = "", \ difficulty = "", \ period = "", \ beta = True if pg.page_params.get_param("beta") else None) #print(json.dumps(pg.template_params["stats"], indent=2)) return
def get_language_details(self, language=None): if language is None: language = PageLanguage.toStr( self.page_params.get_param("language")) if language in self.app_data.messages.keys(): return self.app_data.messages[language] return self.app_data.messages["rs"]
def get_prev_question_test_url(self): if len(context.c.session.get("history")) <= 1: url_prev = self.page.page_params.create_url(\ op = PageOperation.MENU_THEME, language = PageLanguage.toStr(self.page.page_params.get_param("language")), beta=True if self.page.page_params.get_param("beta") else None) else: q_id = context.c.session.get("history")[-2]["q_id"] q_number = self.get_q_number() url_prev = self.page.page_params.create_url(\ op = PageOperation.TEST_PREV, language = PageLanguage.toStr(self.page.page_params.get_param("language")), q_id = q_id, q_num = str(q_number-1), beta=True if self.page.page_params.get_param("beta") else None) return url_prev
def logout(self): logging.info("logout: closing session") context.c.user = None context.c.session.close() # url = self.page_params.get_param("root") + "?op={}".format(PageOperation.MENU_USER.value) url = self.page_params.create_url( \ op = PageOperation.MENU_USER, language = PageLanguage.toStr(self.page_params.get_param("language")), beta = True if self.page_params.get_param("beta") else None ) return url
def get_next_question_test_url(self, total_questions): # We may choose to offer fewer questions if there aren't enough available more_questions = False if not context.c.session.get("history") or len( context.c.session.get("history")) == 0: # Starting a new test, record the start time in epoch seconds context.c.session.set("test_id", int(time.time() * 1000)) next_question, more_questions = self.choose_next_question_test() q_number = self.get_q_number() if q_number >= total_questions or not more_questions: url_next = self.page.page_params.create_url( op=PageOperation.SUMMARY, language=PageLanguage.toStr( self.page.page_params.get_param("language")), beta=True if self.page.page_params.get_param("beta") else None) url_skip = url_next else: url_next = self.page.page_params.create_url( op=PageOperation.TEST, language=PageLanguage.toStr( self.page.page_params.get_param("language")), q_id=next_question["name"], q_num=str(q_number + 1), beta=True if self.page.page_params.get_param("beta") else None) url_skip = self.page.page_params.create_url( op=PageOperation.TEST, language=PageLanguage.toStr( self.page.page_params.get_param("language")), q_id=next_question["name"], q_num=str(q_number + 1), skipped="true", beta=True if self.page.page_params.get_param("beta") else None) return url_next, url_skip
def choose_next_question_browse(self, last_question_id=None): log_str = "Next question for browse in {}/{}/{}/{}/{}/{} - ".format( self.page.page_params.get_param("year"), self.page.page_params.get_param("theme"), self.page.page_params.get_param("subtheme"), self.page.page_params.get_param("topic"), context.c.session.get("period"), context.c.session.get("difficulty")) if not last_question_id: last_question_id = context.c.session.get("browse_last_q") subtheme, topic, period, difficulty = self._get_page_params() questions = list( self.page.repository.get_content_questions( PageLanguage.toStr( self.page.page_params.get_param("language")), self.page.page_params.get_param("year"), self.page.page_params.get_param("theme"), subtheme=subtheme, topic=topic, period=period, difficulty=difficulty).values()) questions.sort(key=lambda x: [ x["rank_subtheme"], x["rank_topic"], x["period"], x["difficulty"] ]) next_q = None prev_q = None if not last_question_id: next_q = questions[0] prev_q = None else: for idx, q in enumerate(questions): if last_question_id == q["name"].strip(): if idx + 1 < len(questions): next_q = questions[idx + 1] if idx - 1 >= 0: prev_q = questions[idx - 1] break log_str += "next_q: {}, prev_q: {}".format(next_q, prev_q) #print("\n\n\n", log_str, "\n\n\n") return prev_q, next_q
def render_edit(page): page.template_params["template_name"] = Design_dev._add_language( page, "dev/edit.html.j2") Design_dev.render_menu(page) page.template_params["action"] = page.page_params.get_param("root") page.template_params["q_id"] = page.page_params.get_param("q_id") page.template_params["language"] = PageLanguage.toStr( page.page_params.get_param("language")) page.template_params["init_code"] = page.page_params.get_param( "init_code") page.template_params["iter_code"] = page.page_params.get_param( "iter_code") page.template_params["text"] = page.page_params.get_param("text") if page.question is not None: page.question.eval_with_exception(True)
def login_anon(self) -> str: if not context.c.user: user_id = 'UNKNOWN' name = 'UNKNOWN' email = None user_language = "rs" logging.debug("Login anonymous user UNKNOWN") self.userdb.login_test(user_id, name, email, user_language) else: logging.info("login(): User already logged in") # url = self.page_params.get_param("root") + \ # "?op={}".format(PageOperation.toStr(PageOperation.CONFIRM_ANON)) url = self.page_params.create_url( \ op = PageOperation.CONFIRM_ANON, language = PageLanguage.toStr(self.page_params.get_param("language")), beta = True if self.page_params.get_param("beta") else None ) return url
def eval(self, page): # Parse text indices = [{"start": -1, "type": "text"}] cend = -1 self.page.add_lines("\n\n<!-- QUESTIONS START -->\n\n") #self.page.add_lines("<div id='question' style='display:table; margin:0 auto;'>\n") self.page.add_lines( "<div id='question' style='display:inline-block; margin:0 auto; width: inherit'>\n" ) self.page.add_lines( "<script>var test_id = {}; var test_order = {};</script>\n".format( self.test_id, self.test_order)) btext = self.make_pretty(self.text) # Identify commands and strings while True: cstart = btext.find("@", cend + 1) if cstart == -1: indices.append({"start": len(btext), "type": "end"}) break cend = btext.find("@", cstart + 1) if cend == -1: raise Exception( "Code block started in text at position {} not finished". format(cstart)) indices.append({"start": cstart, "type": "code"}) indices.append({"start": cend, "type": "text"}) # Find repeates items = [] strings = [] start_index = None for ind in range(0, len(indices) - 1): if (indices[ind]["type"] == "code"): if (indices[ind + 1]["start"] - indices[ind]["start"] - 1 > len("repeat()") and btext[indices[ind]["start"] + 1:indices[ind]["start"] + 1 + len("repeat")] == "repeat" and btext[indices[ind]["start"] + 1 + len("repeat")] == "(" and btext[indices[ind + 1]["start"] - 1] == ")"): if not start_index is None: raise Exception("Nested repeat in text: {}".format( btext[indices[ind]["start"] + 1:indices[ind]["start"] + 1 + len("repeat")])) no_iter = int( btext[indices[ind]["start"] + 1 + len("repeat("):indices[ind + 1]["start"] - 1]) items.append({"type": "repeat", "no_iter": no_iter}) strings.append("") start_index = ind elif (indices[ind + 1]["start"] - indices[ind]["start"] - 1 == len("/repeat") and btext[indices[ind]["start"] + 1:indices[ind]["start"] + 1 + len("/repeat")] == "/repeat"): if start_index is None: raise Exception("Repeat ended without starting") items.append({"type": "end", "start": start_index}) strings.append("") start_index = None else: items.append({ "type": "code", "string": btext[indices[ind]["start"] + 1:indices[ind + 1]["start"]] }) strings.append(btext[indices[ind]["start"] + 1:indices[ind + 1]["start"]]) elif (indices[ind]["type"] == "text"): items.append({ "type": "text", "string": btext[indices[ind]["start"] + 1:indices[ind + 1]["start"]] }) strings.append(btext[indices[ind]["start"] + 1:indices[ind + 1]["start"]]) # Eval code code = self.main_script_begin # Define Lua include function with proper paths # TBD TODO: This is still reading from a file. Rewrite to use repository # Special provisioning for Serbian cyrillic if self.language == PageLanguage.RS and self.cyrillic: lua_lang_t = PageLanguage.RSC.value code = code + """ function require_if_exists(name) local f=io.open(name,"r") if f~=nil then io.close(f); dofile(name) end end function require_if_exists_t(name, name_t) local f_t=io.open(name_t,"r") if f_t~=nil then io.close(f_t); dofile(name_t) else local f=io.open(name,"r") if f~=nil then io.close(f); dofile(name) end end end function include(name) local root_path = '""" + self.questions_root_path + """'; local question_path = '""" + self.q_id + """'; local language = '""" + PageLanguage.toStr( self.language) + """'; local language_t = '""" + lua_lang_t + """'; require_if_exists_t(root_path.."/"..question_path.."/"..name.."."..language..".lua", root_path.."/"..question_path.."/"..name.."."..language_t..".lua"); require_if_exists_t(root_path.."/global/"..name.."."..language..".lua", root_path.."/global/"..name.."."..language_t..".lua"); require_if_exists(root_path.."/global/"..name..".lua"); end """ else: code = code + """ function require_if_exists(name) local f=io.open(name,"r") if f~=nil then io.close(f); dofile(name) end end function include(name) local root_path = '""" + self.questions_root_path + """'; local question_path = '""" + self.q_id + """'; local language = '""" + PageLanguage.toStr( self.language) + """'; require_if_exists(root_path.."/"..question_path.."/"..name.."."..language..".lua"); require_if_exists(root_path.."/global/"..name.."."..language..".lua"); require_if_exists(root_path.."/global/"..name..".lua"); end """ # It seems that Lupa doesn't know about integer type that was new in Lua 5.3 # Everything Lupa returns is treated as a float, causing bad printout format # We use these Lua wrapper functions to convert to int within Lua # Note that this is a dangerous trick as search and replace calls! code = code + """ function sh_random(m, n) rnd = lib.math.random r = rnd(m,n) if m~=nil or n~=nil then return math.tointeger(r) end return r end function sh_round(n) return math.tointeger(lib.math._round(n)) end """ code = code + self.init_code + "\n" ind = 0 start_repeat = None no_iter = None loop = 1 while ind < len(items): item = items[ind] if item["type"] == "text": code = code + "page.add_lines(strings[{}])\n".format(ind) elif item["type"] == "code": # Look for include() directives if (len(strings[ind]) > len("include()") and strings[ind][0:len("include(")] == "include(" and strings[ind][len(strings[ind]) - 1] == ")"): inc_file = strings[ind][len("include("):len(strings[ind]) - 1] q = self.repository.get_question(self.q_id) inc_name = inc_file + "." + PageLanguage.toStr( self.language) + ".lua" include_code = "" if q is not None and inc_name in q.keys(): include_code = q[inc_name] else: g = self.repository.get_globals() if g is not None and inc_name in g.keys(): include_code = g[inc_name] code = code + include_code # Ignore alignment tags elif (strings[ind] != "left" and strings[ind] != "right" and strings[ind] != "center" and \ strings[ind] != "H1" and strings[ind] != "H2" and strings[ind] != "H3" and \ strings[ind] != "/H1" and strings[ind] != "/H2" and strings[ind] != "/H3"): code = code + "output = {}\n".format(strings[ind]) code = code + "if (output ~= nil) then page.add_lines(output) end\n" elif item["type"] == "repeat": no_iter = item["no_iter"] start_repeat = ind loop = 1 code = code + "ITEM = {}\n".format(loop) code = code + self.iter_code + "\n" elif item["type"] == "end": loop = loop + 1 if loop == no_iter + 1: loop = 1 start_repeat = None else: code = code + "ITEM = {}\n".format(loop) code = code + self.iter_code + "\n" ind = start_repeat ind = ind + 1 code = code + self.main_script_end code = code.replace("\\", "\\\\") # Replace math.random with lib.math.random so we can log all random values code = code.replace("math.random(", "sh_random(") # Replace round with a local wrapper to force it to become int (Lupa doesn't know about int types) code = code.replace("lib.math.round(", "sh_round(") # DEBUG if False: logging.debug("\n\n********************\nSTRINGS: \n") for i in range(0, len(strings)): logging.debug("string[{}]: {}".format(i, strings[i])) logging.debug("\n\n********************\nCODE: {}".format(code)) lua_fun = self.lua.eval(code) lua_fun(self.page, self.lib, strings) if self.lib is not None: self.lib.add_check_button_code() self.lib.add_clear_button_code() self.lib.add_solution_button_code() self.lib.add_error_report_button_code() self.page.add_lines("</div>\n") self.page.add_lines("\n\n<!-- QUESTIONS END -->\n\n")
def register(self, args): # TBD: we record UNKNOWN for testing only. # In deployment we should ignore registering requests from UNKNOWN # (and not even send them from the page) try: user_id = context.c.user.user_id if context.c.user else "UNKNOWN" except: user_id = "UNKNOWN" pass if args["response_type"] == ResponseOperation.toStr(ResponseOperation.SUBMIT) or \ args["response_type"] == ResponseOperation.toStr(ResponseOperation.SKIP): # Record answer to the database correct = 0 incorrect = 0 questions = "" if "q_id" in args.keys() and "now" in args.keys(): if "l_id" not in args.keys( ) or not args["l_id"] or args["l_id"] is None: l_id = "" else: l_id = args["l_id"] if "language" not in args.keys( ) or not args["language"] or args["language"] is None: language = "" else: language = args["language"] # for key, v in args.items(): # value = v # if key[0:5] == "q_res": # questions = questions + key + "=" + value + "," # if value == "true": # correct = correct + 1 # else: # incorrect = incorrect + 1 questions = "" if 'detailed' in args.keys(): for k, v in args['detailed'].items(): if v == "true": correct = correct + 1 else: incorrect = incorrect + 1 questions = str(args['detailed']) values = "" if 'values' in args.keys(): values = str(args['values']) shown_solution = False if "shown_solutions" in args.keys() and type( args["shown_solutions"]) == bool: shown_solution = args["shown_solutions"] hist = context.c.session.get("history") if (len(hist) > 0 and (hist[-1]["correct"] == 0 or correct > hist[-1]["correct"])) and not shown_solution: hist[-1]["correct"] = correct hist[-1]["incorrect"] = incorrect context.c.session.set("history", hist) # We register temporary stats for anonymous users that are stored with the cookie if (user_id == "UNKNOWN" or user_id == "local:UNKNOWN") and \ context.c.session.get("last_q_year") and context.c.session.get("last_q_theme") and \ context.c.session.get("last_q_subtheme") and context.c.session.get("difficulty"): year = context.c.session.get("last_q_year") theme = context.c.session.get("last_q_theme") subtheme = context.c.session.get("last_q_subtheme") difficulty = context.c.session.get("difficulty") anon_stats = context.c.session.get("anon_stats") if not anon_stats: anon_stats = {} context.c.session.set("anon_stats", anon_stats) add_anon_stats(self, anon_stats, args["q_id"], \ PageLanguage.fromStr(language), \ year, theme, subtheme, difficulty, correct, incorrect) response = { "user_id": user_id, "question_id": args["q_id"], "list_id": l_id, "year": context.c.session.get("last_q_year") if context.c.session.get("last_q_year") else "", "theme": context.c.session.get("last_q_theme") if context.c.session.get("last_q_theme") else "", "subtheme": context.c.session.get("last_q_subtheme") if context.c.session.get("last_q_subtheme") else "", "topic": context.c.session.get("last_q_topic") if context.c.session.get("last_q_topic") else "", "period": context.c.session.get("period") if context.c.session.get("period") else "", "difficulty": context.c.session.get("difficulty") if context.c.session.get("difficulty") else "", "language": language, "test_id": args["test_id"], "test_order": args["test_order"], "response_type": args["response_type"], "attempt": args["attempt"], "shown_solutions": args["shown_solutions"], "time": args["start"], "duration": int(args["now"]) - int(args["start"]), "correct": correct, "incorrect": incorrect, "questions": questions, "values": values } # logging.debug("Register results: user_id={}, q_id={}, l_id={}, lang={}, response_type={}, " + # "attempt={}, start={}, duration={}, correct={}, incorrect={}, questions=\"{}\"".format( # str(user_id), str(args["q_id"]), str(l_id), # str(language), str(args["response_type"]), str(args["attempt"]), # str(args["start"]), str(int(args["now"]) - int(args["start"])), # str(correct), str(incorrect), str(questions))) try: self.app_data.storage.record_response(response) except Exception as err: logging.error( "Error submitting record response: {}\n{}".format( err, helpers.get_stack_trace())) # Also register responses to LogAnalytics for better visibility log_json = { "user": user_id, "q_id": args["q_id"], "l_id": l_id, "year": context.c.session.get("last_q_year") if context.c.session.get("last_q_year") else "", "theme": context.c.session.get("last_q_theme") if context.c.session.get("last_q_theme") else "", "subtheme": context.c.session.get("last_q_subtheme") if context.c.session.get("last_q_subtheme") else "", "topic": context.c.session.get("last_q_topic") if context.c.session.get("last_q_topic") else "", "period": context.c.session.get("period") if context.c.session.get("period") else "", "difficulty": context.c.session.get("difficulty") if context.c.session.get("difficulty") else "", "language": language, "response_type": args["response_type"], "attempt": args["attempt"], "shown_solutions": args["shown_solutions"], "time": args["start"], "duration": int(args["now"]) - int(args["start"]), "correct": correct, "incorrect": incorrect } self.app_data.log_json("Register", log_json) else: logging.error( "Register operation with incomplete parameters: {}\n{}". format(args, helpers.get_stack_trace())) else: # This is often hit by crawlers, so ignore logging.debug("Unknown register operation: {}".format( args["response_type"])) return "ABC"
def render_feedback(page): language = page.page_params.get_param("language") page.template_params["template_name"] = Design_dev._add_language( page, "dev/feedback.html.j2") Design_dev.render_menu(page) storage = Storage_az_table() no_days = int(page.page_params.get_param("interval")) from_date = datetime.today() - timedelta(no_days) feedbacks = storage.get_all_user_feedback(from_date) feedbacks.sort(key=lambda i: i["Timestamp"], reverse=True) page.template_params["attributes"] = {} page.template_params["questions"] = [] for f in feedbacks: #print(f["question_id"], f["type"], f["Timestamp"], f["language"], f["comment"], f["random_vals"]) q_id = f["question_id"] q = {} q["q_id"] = q_id q["attributes"] = {} q["attributes"]["Type"] = f["type"] q["attributes"]["Time"] = str(f["Timestamp"]) q["attributes"]["Lang"] = f["language"] q["attributes"]["User"] = f["user_id"] q["attributes"]["Comment"] = f["comment"] # This one-liner doesn't preserve value ordering in python 3.6 # rand_vals = list(json.loads(f["random_vals"].replace("'", "\"")).values()) json_vals = json.loads(f["random_vals"].replace("'", "\"")) sorted_json_vals = {} for k, v in json_vals.items(): if "rnd_arr_" in k: num_k = k[8:] else: num_k = k[4:] sorted_json_vals[int(num_k)] = v rand_vals = [] for k, v in sorted(sorted_json_vals.items()): rand_vals.append(v) q["link"] = page.page_params.create_url_edit( \ op = PageOperation.VIEW, \ language = PageLanguage.toStr(language), q_id = q_id) # Generate question and paste it into the list page.page_params.set_param("q_id", q_id) gq = question.Question(page, language=language, rand_vals=rand_vals) gq.set_from_file_with_exception() try: gq.eval_with_exception() except: page.add_lines("Problem with the question!\n") logging.error( "\n\nERROR: problem with question {}!\n\n".format(q_id)) pass q["lib_id"] = gq.lib.lib_id q["text"] = "" for l in page.script_lines: q["text"] = q["text"] + l + "\n" for l in page.lines: q["text"] = q["text"] + str(l) previous_params = page.template_params page.clear() page.template_params = previous_params page.template_params["questions"].append(q)
def get_default_list(self): return self.get_all_lists( PageLanguage.toStr(self.page_params.get_param("language")))[0]
def main(self, req, headers, timers, args): logging.debug("\n\nMAIN ARGS: {}\n\n".format(args)) if headers is None or req is None: # Cherrypy - ignore cookies # Special ops to register a user reported error # DEBUG: This is only for testing and may not implement the full feedback functionality if "op" in args.keys() and args["op"] == PageOperation.toStr( PageOperation.FEEDBACK): return self.feedback(args) self.page_params.parse(args, legacy=True) return Design.main(self) # No caching headers.set_no_store() if args["root"] == "edit": # Old style EDIT page with parameter passing in GET URL # DEBUG: these two special ops are for testing and are not fully functional in edit mode # Special ops to register results if "op" in args.keys() and args["op"] == PageOperation.toStr( PageOperation.REGISTER): return self.register(args) # Special ops to register a user reported error elif "op" in args.keys() and args["op"] == PageOperation.toStr( PageOperation.FEEDBACK): return self.feedback(args) self.page_params.parse_edit(in_args=args) # self.page_params.print_params() return Design.main(self) with context.new_context(req, headers): context.c.timers = timers with self.sessiondb.init_session(req, headers) as session: context.c.session = session context.c.user = self.userdb.get_user(session.user_id()) # Debug if False: session.print() # Special ops to register results if "op" in args.keys() and args["op"] == PageOperation.toStr( PageOperation.REGISTER): return self.register(args) # Special ops to register a user reported error elif "op" in args.keys() and args["op"] == PageOperation.toStr( PageOperation.FEEDBACK): return self.feedback(args) else: self.page_params.set_url(req.get_url()) # DEBUG (log.debug level required for the below to work) # print("\n\nPRE PARSING:\n") # #print("\n\nARGS: \n{}\n".format(json.dumps(args, indent=2))) # print("\n\nARGS: \n{}\n".format(args)) # session.print() # # Parameters stored in a session, # # only updates passed in URL self.page_params.parse(in_args=args, session=session) if self.page_params.get_param("language"): #print("\n\n**** DIRECT: ", self.page_params.get_param("language")) self.userdb.update_selected_language( self.page_params.get_param("language").value) elif context.c.user and context.c.user.selected_language: self.page_params.set_param( "language", PageLanguage.fromStr( context.c.user.selected_language)) #print("\n\n**** FROM DB: ", self.page_params.get_param("language")) else: self.page_params.set_param( "language", PageLanguage.get_default(req.get_preferred_lang())) #print("\n\n**** DEFAULT: ", self.page_params.get_param("language")) # DEBUG (log.debug level required for the below to work) # print("\n\nPOST PARSING:\n") # self.page_params.print_params() # session.print() logging.debug("=== New /main request, op = {}".format( self.page_params.get_param('op'))) if False: self.page_params.print_params() logging.info( "\n\n === New request: op={} - " "q_id={}, l_id={}, language={}, " "init_code={}, iter_code={}, text={}.\n\n".format( PageOperation.toStr( self.page_params.get_param("op")), self.page_params.get_param("q_id"), self.page_params.get_param("l_id"), PageLanguage.toStr( self.page_params.get_param("language")), self.page_params.get_param("init_code"), self.page_params.get_param("iter_code"), self.page_params.get_param("text"), )) log_json = {"url": req.get_url()} if context.c.user and context.c.user.user_id: log_json["user"] = context.c.user.user_id if self.page_params.get_param("op"): log_json["op"] = PageOperation.toStr( self.page_params.get_param("op")) if self.page_params.get_param("q_id"): log_json["q_id"] = self.page_params.get_param("q_id") if self.page_params.get_param("l_id"): log_json["l_id"] = self.page_params.get_param("l_id") if self.page_params.get_param("language"): log_json["language"] = PageLanguage.toStr( self.page_params.get_param("language")) if self.page_params.get_param("year"): log_json["year"] = self.page_params.get_param("year") if self.page_params.get_param("theme"): log_json["theme"] = self.page_params.get_param("theme") if self.page_params.get_param("subtheme"): log_json["subtheme"] = self.page_params.get_param( "subtheme") if self.page_params.get_param("topic"): log_json["topic"] = self.page_params.get_param("topic") if context.c.session.get("period"): log_json["period"] = context.c.session.get("period") if context.c.session.get("difficulty"): log_json["difficulty"] = context.c.session.get( "difficulty") if self.page_params.get_param("skipped"): log_json["skipped"] = self.page_params.get_param( "skipped") # HTTP header log_json["accepted_lang"] = req.get_header( 'Accept-Language') log_json["preferred_lang"] = req.get_preferred_lang() log_json["remote_addr"] = req.get_header('Remote-Addr') log_json["user_agent"] = req.get_header('User-Agent') self.app_data.log_json("Access", log_json) with context.c.timers.new_section("design.main"): return Design.main(self)
def get_default_question(self): return self.get_all_questions( PageLanguage.toStr(self.page_params.get_param("language")))[0]
def render_menu(page): page.template_params["menu"] = {} ### Questions/lists # Edit or view question if page.page_params.get_param( "op") == PageOperation.EDIT or page.page_params.get_param( "op") == PageOperation.VIEW or page.page_params.get_param( "op") == PageOperation.GENERATE: if page.page_params.get_param("op") == PageOperation.GENERATE: page_name = PageOperation.toStr(PageOperation.EDIT) else: page_name = PageOperation.toStr( page.page_params.get_param("op")) page.template_params["menu"][ "current_choice"] = page.page_params.get_param("q_id") page.template_params["menu"]["choices"] = page.get_all_questions( PageLanguage.toStr(page.page_params.get_param("language"))) page.template_params["menu"]["choice_action"] = page.page_params.create_url_edit( \ op = encap_str(page_name), \ q_id = "this.value", \ language = "sel_lang.value", \ js = True) # View list elif page.page_params.get_param( "op") == PageOperation.LIST or page.page_params.get_param( "op") == PageOperation.TEST: page.template_params["menu"][ "current_choice"] = page.page_params.get_param("l_id") page.template_params["menu"]["choices"] = page.get_all_lists() page.template_params["menu"]["choice_action"] = page.page_params.create_url_edit(\ l_id = "this.value", \ language = "sel_lang.value", \ js = True) # View feedbacks elif page.page_params.get_param("op") == PageOperation.VIEW_FEEDBACK: page.template_params["menu"]["current_choice"] = "7" page.template_params["menu"]["choices"] = ["7", "14", "30"] page.template_params["menu"]["choice_action"] = page.page_params.create_url_edit(\ interval = "this.value", \ language = "sel_lang.value", \ js = True) ### Languages page.template_params["menu"]["languages"] = page.get_language_list() page.template_params["menu"]["current_language"] = PageLanguage.toStr( page.page_params.get_param("language")) if page.page_params.get_param( "op") == PageOperation.EDIT or page.page_params.get_param( "op") == PageOperation.VIEW: page.template_params["menu"]["language_action"] = page.page_params.create_url_edit( \ q_id = "choice_id.value", \ language = "this.value", \ js = True) # View list elif page.page_params.get_param( "op") == PageOperation.LIST or page.page_params.get_param( "op") == PageOperation.TEST: page.template_params["menu"]["language_action"] = page.page_params.create_url_edit( \ l_id = "choice_id.value", \ language = "this.value", \ js = True) # Generate? else: page.template_params["menu"]["language_action"] = page.page_params.create_url_edit( \ q_id = "choice_id.value", \ language = "this.value", \ js = True) ### Operations options = [ PageOperation.toStr(PageOperation.EDIT), PageOperation.toStr(PageOperation.VIEW), PageOperation.toStr(PageOperation.LIST), PageOperation.toStr(PageOperation.VIEW_FEEDBACK), PageOperation.toStr(PageOperation.STATS) ] page.template_params["menu"]["operations"] = options page.template_params["menu"][ "current_operation"] = PageOperation.toStr( page.page_params.get_param("op")) page.template_params["menu"]["operation_action"] = page.page_params.create_url_edit( \ op = "sel_op.value", \ js = True)
def choose_next_question_test(self): log_str = "Next question for test in {}/{}/{}/{}/{}/{} - ".format( self.page.page_params.get_param("year"), self.page.page_params.get_param("theme"), self.page.page_params.get_param("subtheme"), self.page.page_params.get_param("topic"), context.c.session.get("period"), context.c.session.get("difficulty")) # Total number of unique questions (including previously asked ones) remaining_question = 0 potential_questions = {"all": [], "topics": {}, "difficulty": {}} potential_questions_w_repeat = [] past_questions = {} last_question = None if context.c.session.get("history"): for hist in context.c.session.get("history"): last_question = hist["q_id"] if not hist["q_id"] in past_questions.keys(): past_questions[hist["q_id"]] = { "number": 1, "correct": 1 if hist["incorrect"] == 0 else 0, "skipped": ("skipped" in hist.keys() and hist["skipped"]) } else: past_questions[hist["q_id"]]["number"] = past_questions[ hist["q_id"]]["number"] + 1 if hist["incorrect"] == 0: past_questions[ hist["q_id"]]["correct"] = past_questions[ hist["q_id"]]["correct"] + 1 if "skipped" in hist.keys() and hist["skipped"]: past_questions[hist["q_id"]]["skipped"] = True prev_question = None # Find the list of all question in the subtopic (potential_questions_w_repeat), # and also those among them that haven't been already asked in this session (potential_questions) subtheme, topic, period, difficulty = self._get_page_params() for _, next_q in self.page.repository.get_content_questions( PageLanguage.toStr( self.page.page_params.get_param("language")), self.page.page_params.get_param("year"), self.page.page_params.get_param("theme"), subtheme=subtheme, topic=topic, period=period, difficulty=difficulty).items(): add_in_random = True diff = 0 if not next_q["name"] in past_questions.keys(): potential_questions["all"].append(next_q) topic = self._make_topic(next_q) if topic not in potential_questions["topics"].keys(): potential_questions["topics"][topic] = {} if next_q["difficulty"] not in potential_questions["topics"][ topic].keys(): potential_questions["topics"][topic][ next_q["difficulty"]] = [] potential_questions["topics"][topic][ next_q["difficulty"]].append(next_q) if next_q["difficulty"] not in potential_questions[ "difficulty"].keys(): potential_questions["difficulty"][ next_q["difficulty"]] = [] potential_questions["difficulty"][next_q["difficulty"]].append( next_q) if next_q["random"] >= 10 or remaining_question >= 100: diff = 100 remaining_question = 100 else: diff = next_q["random"] if next_q["random"] > 0 else 1 remaining_question = remaining_question + diff else: if next_q["name"] == last_question: prev_question = next_q ask_again = not past_questions[next_q["name"]]["skipped"] ask_again = ask_again and not (\ (past_questions[next_q["name"]]["correct"] >= 1 and next_q["random"] < 5) or \ (past_questions[next_q["name"]]["correct"] >= 2 and next_q["random"] >= 5 and next_q["random"] < 10) or \ (past_questions[next_q["name"]]["correct"] >= 3 and next_q["random"] >= 10) \ ) ask_again = ask_again and not \ (next_q["random"] < 10 and next_q["random"] <= past_questions[next_q["name"]]["number"]) if not ask_again: add_in_random = False else: if next_q["random"] >= 10 or remaining_question >= 100: diff = 100 remaining_question = 100 else: diff = next_q["random"] - past_questions[next_q["name"]]["number"] \ if next_q["random"] - past_questions[next_q["name"]]["number"] >= 0 else 0 remaining_question = remaining_question + diff #print("Q: {} ({}) - {} {}".format(next_q["name"], add_in_random, diff, remaining_question)) if add_in_random: potential_questions_w_repeat.append(next_q) # print("remaining_question={}\npotential_questions={}\npotential_questions_w_repeat={}".format( # remaining_question, json.dumps(potential_questions, indent=2), json.dumps(potential_questions_w_repeat, indent=2) # )) # if not potential_questions_w_repeat: # total = len(all_potential_questions_w_repeat) # index = random.randrange(total) # potential_questions_w_repeat.append(all_potential_questions_w_repeat[index]) next_question = { "name": "", "subtheme": "", "topic": "", "period": "", "difficulty": "", "random": False } # Do we insiste on a specific topic in a subtheme or we can is_topic_specified = not self.page.page_params.get_param("subtheme") == '*' and \ not self.page.page_params.get_param("topic") == '*' if prev_question or is_topic_specified: if prev_question: topic = self._make_topic(prev_question) else: topic = self.page.page_params.get_param("subtheme") + "|" + \ self.page.page_params.get_param("topic") if topic in potential_questions["topics"].keys(): # There are more potential questions in the same topic that have not been asked before, choose one candidates = [] if prev_question: candidate_difficulty = prev_question["difficulty"] elif context.c.session.get("difficulty") == "*": candidate_difficulty = "1" else: candidate_difficulty = context.c.session.get("difficulty") log_str = log_str + "found topic {} ".format(topic) if candidate_difficulty in potential_questions["topics"][ topic].keys(): # There are more questions in the same topic with the same difficulty, choose one candidates = potential_questions["topics"][topic][ candidate_difficulty] log_str = log_str + "difficulty {} ".format( candidate_difficulty) elif str(int(candidate_difficulty) + 1) in potential_questions["topics"][topic].keys(): # There are more questions in the same topic with the next difficulty, choose one candidates = potential_questions["topics"][topic][str( int(candidate_difficulty) + 1)] log_str = log_str + "difficulty {} ".format( str(int(candidate_difficulty) + 1)) elif str(int(candidate_difficulty) + 2) in potential_questions["topics"][topic].keys(): candidates = potential_questions["topics"][topic][str( int(candidate_difficulty) + 2)] log_str = log_str + "difficulty {} ".format( str(int(candidate_difficulty) + 2)) else: log_str = log_str + "no difficulty found (!) - " if candidates: total = len(candidates) index = random.randrange(total) next_question = candidates[index] log_str = log_str + "selected new question: {}".format( next_question["name"]) if not next_question["name"]: # A suitable question from the same topic not found, find an easy one from a new topic # (if new topic allowed, otherwise potential_questions will be empty) candidates = [] if "1" in potential_questions["difficulty"].keys(): # There are more questions in the same topic with the same difficulty, choose one candidates = potential_questions["difficulty"]["1"] log_str = log_str + "new topic, difficulty: 1 - " elif "2" in potential_questions["difficulty"].keys(): # There are more questions in the same topic with the next difficulty, choose one candidates = potential_questions["difficulty"]["2"] log_str = log_str + "new topic, difficulty: 2 - " elif "3" in potential_questions["difficulty"].keys(): candidates = potential_questions["difficulty"]["3"] log_str = log_str + "new topic, difficulty: 3 - " else: log_str = log_str + "no new topics - " if candidates: total = len(candidates) index = random.randrange(total) next_question = candidates[index] log_str = log_str + "selected: {}- {}".format( self._make_topic(next_question), next_question["name"]) if not next_question["name"] and potential_questions_w_repeat: # Otherwise give randomly any previously asked randomizable question total = len(potential_questions_w_repeat) index = random.randrange(total) next_question = potential_questions_w_repeat[index] log_str = log_str + "no new topics - selected previous q: {}".format( next_question["name"]) elif not next_question["name"]: log_str = log_str + "no new topics - no question selected!" logging.info("\n\n ************ " + log_str + "\n\n") # logging.info("\n\nNEXT Q: {}\n\n {}\n\n {}\n\n {}\n\n".format( # potential_questions_w_repeat, potential_questions, next_question, remaining_question)) # Serve at most remaining_question #remaining_question = (remaining_question - 1) if remaining_question > 0 else remaining_question logging.info("Remaining questions: {}".format(remaining_question)) if remaining_question == 0: more_questions = False else: more_questions = True return next_question, more_questions
def __init__(self, page, q_id=None, language=None, test_id=None, test_order=None, init_code=None, iter_code=None, text=None, rand_vals=None): self.lua = LuaRuntime(unpack_returned_tuples=True) self.page = page self.repository = page.repository self.rel_path = page.app_data.rel_path # If we have more questions on the same page make sure all use pseudo-random thus unique IDs self.q_unique_id = str(int(random.random() * 1000000000)) if q_id: self.q_id = q_id else: self.q_id = page.page_params.get_param("q_id") self.page.add_script_lines( "<script> global_q_id = \"{}\";</script>".format(self.q_id)) if language: self.language = language else: self.language = page.page_params.get_param("language") # Special provisioing for Serbian cyrillic if self.language == PageLanguage.RSC: self.language = PageLanguage.RS self.cyrillic = True self.page.add_script_lines( "<script> global_language = \"{}\";</script>".format( PageLanguage.toStr(self.language))) # Parameters useful for error reporting if self.page.page_params.get_param("l_id"): self.page.add_script_lines( "<script> global_l_id = \"{}\";</script>".format( self.page.page_params.get_param("l_id"))) if self.page.page_params.get_param("root"): self.page.add_script_lines( "<script> global_root = \"{}\";</script>".format( self.page.page_params.get_param("root"))) self.test_id = test_id if test_id else 0 self.test_order = test_order if test_order else 0 if not init_code is None: self.init_code = init_code else: self.init_code = page.page_params.get_param("init_code") if not iter_code is None: self.iter_code = iter_code else: self.iter_code = page.page_params.get_param("iter_code") if not text is None: self.text = text else: self.text = page.page_params.get_param("text") self.lib = Library(self, rand_vals=rand_vals) logging.debug("Rendering question %s, language=%s", self.question_url(), PageLanguage.toStr(self.language)) self.questions_root_path = self.rel_path + "/" + self.questions_rel_path
def render_list(page): language = page.page_params.get_param("language") qlist = page.repository.get_list(page.page_params.get_param("l_id")) page.template_params["template_name"] = Design_dev._add_language( page, "dev/list.html.j2") Design_dev.render_menu(page) if "name" in qlist.keys(): page.template_params["name"] = qlist["name"] else: page.template_params["name"] = "UNKNOWN" page.template_params["attributes"] = {} for key, value in qlist.items(): if key != "name" and key != "questions": page.template_params["attributes"][key] = value page.template_params["questions"] = [] page.template_params["summary"] = {} for i in qlist["questions"]: if "subtheme" in i.keys(): if isinstance(i["subtheme"], list): asubtheme = i["subtheme"] else: asubtheme = [i["subtheme"]] else: asubtheme = ["UNKNOWN"] for subtheme in asubtheme: topic = i["topic"] if "topic" in i.keys() else "UNKNOWN" difficulty = i["difficulty"] if "difficulty" in i.keys( ) else "UNKNOWN" period = i["period"] if "period" in i.keys() else "UNKNOWN" if subtheme not in page.template_params["summary"].keys(): page.template_params["summary"][subtheme] = {} if topic not in page.template_params["summary"][subtheme].keys( ): page.template_params["summary"][subtheme][topic] = { "difficulty": {}, "period": {} } if difficulty not in page.template_params["summary"][subtheme][ topic]["difficulty"].keys(): page.template_params["summary"][subtheme][topic][ "difficulty"][difficulty] = 0 if period not in page.template_params["summary"][subtheme][ topic]["period"].keys(): page.template_params["summary"][subtheme][topic]["period"][ period] = 0 page.template_params["summary"][subtheme][topic]["difficulty"][difficulty] = \ page.template_params["summary"][subtheme][topic]["difficulty"][difficulty] + 1 page.template_params["summary"][subtheme][topic]["period"][period] = \ page.template_params["summary"][subtheme][topic]["period"][period] + 1 q_id = i["name"] q = {} q["q_id"] = q_id q["attributes"] = {} for key, value in i.items(): if key != "name": q["attributes"][key] = [value] q["link"] = page.page_params.create_url_edit( \ op = PageOperation.VIEW, \ language = PageLanguage.toStr(language), q_id = q_id) # Generate question and paste it into the list page.page_params.set_param("q_id", q_id) gq = question.Question(page, language=language) gq.set_from_file_with_exception() try: gq.eval_with_exception() except: page.add_lines("Problem with the question!\n") logging.error( "\n\nERROR: problem with question {}!\n\n".format(q_id)) pass q["lib_id"] = gq.lib.lib_id q["text"] = "" for l in page.script_lines: q["text"] = q["text"] + l + "\n" for l in page.lines: q["text"] = q["text"] + str(l) previous_params = page.template_params page.clear() page.template_params = previous_params page.template_params["questions"].append(q)