def create_question(language, *, odict): """Create a question document and save it to database.""" question = Question(description="word flashcard", skill="Vocabulary", language=language.lower(), word=odict["Word"], part_of_speech=odict["Part_of_Speech"], source_id=odict["Source_Id"]) # css.DictReader escapes newline characters, so split with: '\\n ' question.all_answers = odict["All_Answers"].split('\\n ') if odict["Child_Appropriate"].lower() == "no": question.child_appropriate = False question.audio = odict["Audio"].split(", ") question.images = [] # adding info for the first image fileroot, ext = splitext(odict["Photo1"]) ext = ext.lstrip(".") question.images.append([fileroot, ext, float(odict["Photo1_ar"])]) # if there are more images, add info for them for i in [1, 2, 3]: if odict[f"Photo{i+1}"]: fileroot, ext = splitext(odict[f"Photo{i+1}"]) ext = ext.lstrip(".") question.images.append( [fileroot, ext, float(odict[f"Photo{i+1}_ar"])]) question.save()
def _read_question_from_json(language, side): """Read and return questions from json files. Generates a list of question ids and then for the first qusestion returns it as a Question object. Arguments: language: str -- language of the question side: "front" or "back" Return: Tuple: (a list of question ids, question as Question object) """ os.chdir("C:/Users/Lukasz/Python/ErroresBuenos/lalang/questions/default") with open(f"stream_default_{language.lower()}_{side}.json", "r", encoding="utf8") as f: question_list = json.load(f) question_ids_list = [] # store the question ids in a list for q in question_list: question_ids_list.append(q["id"]) # for the first question, convert the dictionary into Question object question = Question() for k, v in question_list[0].items(): setattr(question, k, v) return question_ids_list, question
def update_questions(db_name, language, filename): """Update questions from a csv file to a MongoDB database.""" update_counter = 0 added_counter = 0 mongoengine.connect(db_name, host="localhost", port=27017) with open(filename, encoding="utf8") as csv_file: csv_reader = csv.DictReader(csv_file) for row in csv_reader: questions = Question.objects(language=language, source_id=row["Source_Id"]) if questions.count() == 0: create_question(language, odict=row) added_counter += 1 for question in questions: question.word = row["Word"] question.part_of_speech = row["Part_of_Speech"] question.hint = row["Hint"] question.source_id = row["Source_Id"] question.audio = row["Audio"].split(", ") if row["Child_Appropriate"].lower() == "no": question.child_appropriate = False else: question.child_appropriate = True # css.DictReader escapes newline characters, so split with: # '\\n ' question.all_answers = row["All_Answers"].split('\\n ') question.part_of_speech = row["Part_of_Speech"] # preparing info for the first image fileroot, ext = splitext(row["Photo1"]) ext = ext.lstrip(".") # updating the image array question.images = [] question.images.append([fileroot, ext, float(row["Photo1_ar"])]) # if there are more images, update them for i in [1, 2, 3]: if row[f"Photo{i+1}"]: fileroot, ext = splitext(row[f"Photo{i+1}"]) ext = ext.lstrip(".") question.images.append([fileroot, ext, float(row[f"Photo{i+1}_ar"])]) question.save() update_counter += 1 print(f"Total records updated: {update_counter} for {language}") print(f"Total new records added: {added_counter} for {language}")
def get_queue_question(language, student_id): """Read and return first question stored in student's question queue in db. Read question queue in Student document, and use the id to retrieve a question from Question collection in db. Randomly pick between the "front" and "back" question queues. Return the question as a Question object, or None if no questions in queue. Argument: language: str student_id = str -- argument for ObjectId() in MongoDB Return: Tuple: (Question object or None, question side or None) """ mongoengine.connect("lalang_db", host="localhost", port=27017) # check this EmbeddedDocumentList field exists before adding to it; # if all list items get deleted, then MongoDB will delete the list field # so guard against that assert not Student.objects(id=student_id, __raw__={"language_progress": None}).first() # get embedded document for this student for this language language_embed_doc = Student.objects( id=student_id).first().language_progress.filter( language=language) # in case the list or queue doesn't exist try: # check if the embedded document exists and question queues # are not empty if language_embed_doc and language_embed_doc[0].f_question_queue: # if there are questions in queue, query and add them to list # pick randomly between the front and back question queues if random.choices([0, 1], cum_weights=[40, 100])[0]: side = "front" question_id = language_embed_doc[0].f_question_queue[0] else: side = "back" question_id = language_embed_doc[0].b_question_queue[0] return Question.objects(id=question_id).first(), side else: return None, None except IndexError: return None, None
def add_questions_to_db(db_name, language, filename): """Copy questions from a csv file to a MongoDB database.""" print(sys.path) print(os.getcwd()) mongoengine.connect(db_name, host="localhost", port=27017) with open(filename, encoding="utf8") as csv_file: csv_reader = csv.reader(csv_file, delimiter=",") column_names = next(csv_reader) with open(filename, encoding="utf8") as csv_file: csv_reader = csv.DictReader(csv_file, delimiter=",") for row in csv_reader: question = Question(description="word flashcard", skill="Vocabulary", language=language.lower(), word=row[column_names[0]], part_of_speech=row[column_names[1]], audio_files=row[column_names[2]], image_files=row[column_names[3]]) question.save()
mongoengine.connect("lalang_db", host="localhost", port=27017) # query to generate options for user input language_in = input("What language are these questions for? ").lower() num_questions = 5 # dummy variable student_id = "" query_args = {"language": f"{language_in}", "description": "word flashcard", "child_appropriate": True} results_iter = Question.objects(__raw__=query_args) total_questions = results_iter.count() print("Total number of questions: ", total_questions) question_set = set() while len(question_set) < num_questions: question_set.add(random.randint(0, total_questions)) question_list = sorted(list(question_set)) question_list_steps = [] question_list_steps.append(question_list[0]) for i in range(1, len(question_list)):
""" import mongoengine import csv import sys import os sys.path.append("C:\\Users\\Lukasz\\Python\\ErroresBuenos") from lalang.db_model import Question db_in = input("Which database should the questions be deleted from? ") mongoengine.connect(db_in, host="localhost", port=27017) del_match = {"word": {"$regex": "^DU"}, "language": "Polish"} del_records_iter = Question.objects(__raw__=del_match) num_del_records = del_records_iter.count() if num_del_records > 0: deleted_counter = 0 print(num_del_records, " records found for deletion.") for del_record in del_records_iter: del_record_contents = ( f"{{\"word\": \"{del_record.word}\", \"description\": \"{del_record.description}\", " f"\"skill\": \"{del_record.skill}\", \"language\": \"{del_record.language}\"}}" ) del_confirm = input("Do you want to delete record: " f"{del_record_contents}? ") input_choices = {"yes", "y", "1"} if del_confirm.lower() in input_choices: del_record.delete()
def prep_questions(language, side, student_id, num_questions_needed): """Pick new questions and save them in db. Randomly query Question collection for more questions in the database, add them to student's queue in Student document - i.e. save them in db. Argument: language: str side: "front" or "back" - which side of the question card student_id = str or ObjectId() from MongoDB num_questions_needed: integer Precondition: num_questions_needed > 0 Return: None """ # set the flags for the queues and stacks, either "f" or "b" s = side[0] # set the flags for the queues and stacks from the opposite question side if s == "f": op = "b" else: op = "f" num_questions_added = 0 # force to string if ObjectId passed student_id = str(student_id) mongoengine.connect("lalang_db", host="localhost", port=27017) query_args = {"language": f"{language}", "description": "word flashcard"} questions_iter = Question.objects(__raw__=query_args) # get embedded document for this student for this language language_embed_doc = Student.objects( id=student_id).first().language_progress.filter( language=language) logging.info("Num of questions in queue at beginning of prep_questions: ") logging.info(len(getattr(language_embed_doc[0], f"{s}_question_queue"))) # if answered_corr_stack is "big", reduce it, i.e. release questions # for review size_corr_stack = len(getattr(language_embed_doc[0], f"{s}_answered_corr_stack")) logging.info(f"size_corr_stack: {size_corr_stack}") if size_corr_stack > START_REVIEW: logging.info("We need to release questions for review") questions_to_release = (size_corr_stack - BASE_ANSWERED_CORRECTLY) logging.info(f"Number of questions to release: {questions_to_release}") del getattr(language_embed_doc[0], f"{s}_answered_corr_stack")[:questions_to_release] logging.info("Questions released for review.") # draw random question from all possible questions # keep drawing questions until you get {num_questions_needed} questions while num_questions_added < num_questions_needed: logging.info("In the while loop in prep_questions") q_used = False duplicate_check_passed = False # for side="back", draw from three sources: # 25% of the time from f_answered_wrong_stack, # i.e. the opposite - "front" - side of the question # 25% of the time from b_answered_wrong_stack # 50% of the time from all questions in the Question collection # for side="front", draw from four sources: # 20% of the time from b_answered_wrong_stack, # i.e. the opposite - "back" - side of the question # 15% of the time from f_answered_wrong_stack # 50% of the time from all questions in the Question collection # 15% of the time from f_answered_review_stack choices = [0, 1, 2, 3] if side == "back": weights = [25, 50, 100, 100] draw_bin = random.choices(choices, cum_weights=weights)[0] else: weights = [20, 35, 85, 100] draw_bin = random.choices(choices, cum_weights=weights)[0] # if picking from all questions in the Question collection if draw_bin == 2: drawn_id = _draw_random_question(questions_iter) # if picking from wrong_stack from the opposite side if draw_bin == 0: # check if the opposite answered_wrong_stack is big enough # to draw from; if so, draw from the first 10 questions if (len(getattr(language_embed_doc[0], f"{op}_answered_wrong_stack")) >= MIN_WRONG_STACK_SIZE_FOR_DRAW): pick = random.choice(range(10)) drawn_id = getattr(language_embed_doc[0], f"{op}_answered_wrong_stack")[pick] else: # it didn't work, so just draw a random question # from all questions in the collection drawn_id = _draw_random_question(questions_iter) # if picking from wrong_stack from the same side if draw_bin == 1: # check if the answered_wrong_stack for the same side is big enough # to draw from; if so, use the first question in the stack if (len(getattr(language_embed_doc[0], f"{op}_answered_wrong_stack")) >= MIN_WRONG_STACK_SIZE_FOR_DRAW): drawn_id = getattr(language_embed_doc[0], f"{op}_answered_wrong_stack").pop(0) # we already know that this drawn question is not present # in any relevant queue or stack, so it can be used. # So, set a flag to stop further verification. duplicate_check_passed = True else: # it didn't work, so just draw a random question # from all questions in the collection drawn_id = _draw_random_question(questions_iter) # if picking from f_answered_review_stack for "front" sided question if draw_bin == 3: # check if f_answered_review_stack is big enough to draw from; # if so, use the first question in the stack if (len(language_embed_doc[0].f_answered_review_stack) >= MIN_REVIEW_STACK_SIZE_FOR_DRAW): drawn_id = language_embed_doc[0].f_answered_review_stack.pop(0) # we already know that this drawn question is not present # in any relevant queue or stack, so it can be used. # So, set a flag to stop further verification. duplicate_check_passed = True else: # it didn't work, so just draw a random question # from all questions in the collection drawn_id = _draw_random_question(questions_iter) # check that the drawn question is not in the queue, or the two stacks # of answered questions; if not, then add it to the queue if not duplicate_check_passed: for q_id in getattr(language_embed_doc[0], f"{s}_question_queue"): if drawn_id == q_id: q_used = True break if not duplicate_check_passed and not q_used: for q_id in getattr(language_embed_doc[0], f"{s}_answered_wrong_stack"): if drawn_id == q_id: q_used = True break if not q_used: for q_id in getattr(language_embed_doc[0], f"{s}_answered_corr_stack"): if drawn_id == q_id: q_used = True break if duplicate_check_passed or not q_used: # question not used, so add its id to the Student queue of questions getattr(language_embed_doc[0], f"{s}_question_queue").append(drawn_id) language_embed_doc.save() num_questions_added += 1 logging.info(f"Num of questions added to queue: {num_questions_added}") # query again to check the size of the question queue language_embed_doc = Student.objects( id=student_id).first().language_progress.filter( language=language) logging.info("Num of questions in queue at the end of prep_questions: ") logging.info(len(getattr(language_embed_doc[0], f"{s}_question_queue"))) return None
def dict_to_question_obj(question_as_dict): """Take question represented as dictionary and return Question object.""" q_obj = Question() for k, v in question_as_dict.items(): setattr(q_obj, k, v) return q_obj