class KnownWordProbability(db.Model): __table_args__ = {'mysql_collate': 'utf8_bin'} __tablename__ = 'known_word_probability' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False) user = db.relationship("User") user_word_id = db.Column(db.Integer, db.ForeignKey('user_word.id'), nullable=True) user_word = db.relationship("UserWord") ranked_word_id = db.Column(db.Integer, db.ForeignKey("ranked_word.id"), nullable=True) ranked_word = db.relationship("RankedWord") probability = db.Column(db.DECIMAL(10, 9), nullable=False) db.CheckConstraint('probability>=0', 'probability<=1') def __init__(self, user, user_word, ranked_word, probability): self.user = user self.user_word = user_word self.ranked_word = ranked_word self.probability = probability def word_has_just_beek_bookmarked(self): self.probability /= 2 @classmethod def calculateKnownWordProb(cls, exerciseProb, encounterProb): return 0.8 * float(exerciseProb) + 0.2 * float(encounterProb) @classmethod def find(cls, user, user_word, ranked_word, probability=None): try: return cls.query.filter_by(user=user, user_word=user_word, ranked_word=ranked_word).one() except sqlalchemy.orm.exc.NoResultFound: return cls(user, user_word, ranked_word, probability) @classmethod def find_all_by_user(cls, user): return cls.query.filter_by(user=user).all() @classmethod def find_all_by_user_cached(cls, user): known_probabilities_cache = {} known_probabilities = cls.find_all_by_user(user) for known_probability in known_probabilities: user_word = known_probability.user_word # TODO: Why are there many KnownWordProbabilities with no user word in the database? if user_word is not None: known_probabilities_cache[ user_word.word] = known_probability.probability return known_probabilities_cache @classmethod def find_all_by_user_with_rank(cls, user): known_probs = cls.query.filter_by(user=user).all() for p in known_probs: if p.ranked_word is None: known_probs.remove(p) return known_probs @classmethod def exists(cls, user, user_word, ranked_word): try: cls.query.filter_by(user=user, user_word=user_word, ranked_word=ranked_word).one() return True except sqlalchemy.orm.exc.NoResultFound: return False @classmethod def get_probably_known_words(cls, user): return cls.query.filter(cls.user == user).filter( cls.probability >= 0.9).all()
class EncounterBasedProbability(db.Model): __tablename__ = 'encounter_based_probability' __table_args__ = {'mysql_collate': 'utf8_bin'} DEFAULT_PROBABILITY = 0.5 id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False) user = db.relationship("User") ranked_word_id = db.Column(db.Integer, db.ForeignKey("ranked_word.id"), nullable=False) ranked_word = db.relationship("RankedWord") not_looked_up_counter = db.Column(db.Integer, nullable=False) probability = db.Column(db.DECIMAL(10, 9), nullable=False) db.UniqueConstraint(user_id, ranked_word_id) db.CheckConstraint('probability>=0', 'probability<=1') def __init__(self, user, ranked_word, not_looked_up_counter, probability): self.user = user self.ranked_word = ranked_word self.not_looked_up_counter = not_looked_up_counter self.probability = probability @classmethod def find(cls, user, ranked_word, default_probability=None): try: return cls.query.filter_by(user=user, ranked_word=ranked_word).one() except sqlalchemy.orm.exc.NoResultFound: return cls(user, ranked_word, 1, default_probability) @classmethod def find_all(cls): return cls.query.all() @classmethod def find_all_by_user(cls, user): return cls.query.filter_by(user=user).all() @classmethod def exists(cls, user, ranked_word): try: cls.query.filter_by(user=user, ranked_word=ranked_word).one() return True except sqlalchemy.orm.exc.NoResultFound: return False @classmethod def find_or_create(cls, word, user, language): ranked_word = RankedWord.find(word.lower(), language) if EncounterBasedProbability.exists(user, ranked_word): enc_prob = EncounterBasedProbability.find(user, ranked_word) enc_prob.not_looked_up_counter += 1 enc_prob.boost_prob() else: enc_prob = EncounterBasedProbability.find( user, ranked_word, EncounterBasedProbability.DEFAULT_PROBABILITY) return enc_prob def reset_prob(self): # Why is this 0.5? Should be lower! I just looked up the word... # ugh... self.probability = 0.5 def word_has_just_beek_bookmarked(self): """ the user can't know this word very well if he's bookmarking it again :return: """ self.probability /= 2 # This function controls if prob is already 1.0, else it adds 0.1. It maximum adds 0.1, therefore cannot exceed 1 def boost_prob(self): if float(self.probability) <> 1.0: self.probability = float(self.probability) + 0.1
class ExerciseBasedProbability(db.Model): __tablename__ = 'exercise_based_probability' __table_args__ = {'mysql_collate': 'utf8_bin'} id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False) user = db.relationship("User") user_word_id = db.Column(db.Integer, db.ForeignKey('user_word.id'), nullable=False) user_word = db.relationship("UserWord") probability = db.Column(db.DECIMAL(10, 9), nullable=False) db.UniqueConstraint(user_id, user_word_id) db.CheckConstraint('probability>=0', 'probability<=1') DEFAULT_MIN_PROBABILITY = 0.1 DEFAULT_MAX_PROBABILITY = 1.0 def __init__(self, user, user_word, probability): self.user = user self.user_word = user_word self.probability = probability @classmethod def find(cls, user, user_word): try: return cls.query.filter_by(user=user, user_word=user_word).one() except sqlalchemy.orm.exc.NoResultFound: return cls(user, user_word, 0.1) @classmethod def exists(cls, user, user_word): try: cls.query.filter_by(user=user, user_word=user_word).one() return True except sqlalchemy.orm.exc.NoResultFound: return False @classmethod def find_all(cls): return cls.query.all() def wrong_formula(self, count_wrong_after_another): if self.DEFAULT_MIN_PROBABILITY * count_wrong_after_another >= float( self.probability): self.probability = decimal.Decimal('0.1') else: self.probability = ( float(self.probability) - self.DEFAULT_MIN_PROBABILITY * count_wrong_after_another) def correct_formula(self, count_correct_after_another): if float( self.probability ) + self.DEFAULT_MIN_PROBABILITY * count_correct_after_another >= 1.0: self.probability = decimal.Decimal('1.0') else: self.probability = ( float(self.probability) + self.DEFAULT_MIN_PROBABILITY * count_correct_after_another) def update_probability_after_adding_bookmark_with_same_word( self, bookmark, user): count_bookmarks_with_same_word = len( Bookmark.find_all_by_user_and_word(user, bookmark.origin)) self.probability = ( float(self.probability * count_bookmarks_with_same_word) + 0.1) / ( count_bookmarks_with_same_word + 1 ) # compute avg probability of all bookmarks with same word #calculates the probability of knowing a certain bookmark after a exercise_outcome. def calculate_known_bookmark_probability(self, bookmark): count_correct_after_another = 0 count_wrong_after_another = 0 sorted_exercise_log_after_date = sorted(bookmark.exercise_log, key=lambda x: x.time, reverse=False) for exercise in sorted_exercise_log_after_date: if exercise.outcome.outcome == ExerciseOutcome.TOO_EASY: self.probability = decimal.Decimal('1.0') count_wrong_after_another = 0 elif exercise.outcome.outcome == ExerciseOutcome.SHOW_SOLUTION: self.probability //= 2 if float(self.probability) < 0.1: self.probability = decimal.Decimal('0.1') count_correct_after_another = 0 elif exercise.outcome.outcome == ExerciseOutcome.CORRECT: count_correct_after_another += 1 count_wrong_after_another = 0 if float(self.probability) < 1.0: self.correct_formula(count_correct_after_another) else: self.probability = decimal.Decimal('1.0') elif exercise.outcome.outcome == ExerciseOutcome.WRONG: count_wrong_after_another += 1 count_correct_after_another = 0 if float(self.probability) > 0.1: self.wrong_formula(count_wrong_after_another) else: self.probability = decimal.Decimal('0.1') def halfProbability(self): self.probability /= 2 if float(self.probability) < 0.1: self.probability = decimal.Decimal('0.1')