class UserWord(db.Model, util.JSONSerializable): __tablename__ = 'user_words' __table_args__ = {'mysql_collate': 'utf8_bin'} id = db.Column(db.Integer, primary_key=True) word = db.Column(db.String(255), nullable=False, unique=True) language_id = db.Column(db.String(2), db.ForeignKey("language.id")) language = db.relationship("Language") rank_id = db.Column(db.Integer, db.ForeignKey("word_ranks.id"), nullable=True) rank = db.relationship("WordRank") db.UniqueConstraint(word, language_id) IMPORTANCE_LEVEL_STEP = 1000 IMPOSSIBLE_RANK = 1000000 IMPOSSIBLE_IMPORTANCE_LEVEL = IMPOSSIBLE_RANK / IMPORTANCE_LEVEL_STEP def __init__(self, word, language, rank=None): self.word = word self.language = language self.rank = rank def __repr__(self): return '<UserWord %r>' % (self.word) def serialize(self): return self.word # returns a number between def importance_level(self): if self.rank is not None: return max((10 - self.rank.rank / UserWord.IMPORTANCE_LEVEL_STEP), 0) else: return 0 # we use this in the bookmarks.html to show the rank. # for words in which there is no rank info, we don't display anything def importance_level_string(self): if self.rank == None: return "" b = "|" return b * self.importance_level() @classmethod def find(cls, word, language, rank=None): try: return (cls.query.filter(cls.word == word).filter( cls.language == language).one()) except sqlalchemy.orm.exc.NoResultFound: return cls(word, language, rank) @classmethod def find_rank(cls, word, language): return WordRank.find(word, language) @classmethod def find_all(cls): return cls.query.all()
class RankedWord(db.Model, util.JSONSerializable): __tablename__ = 'ranked_word' __table_args__ = {'mysql_collate': 'utf8_bin'} id = db.Column(db.Integer, primary_key=True) word = db.Column(db.String(255), nullable=False, unique=True, index=True) language_id = db.Column(db.String(2), db.ForeignKey("language.id")) language = db.relationship("Language") rank = db.Column(db.Integer) db.UniqueConstraint(word, language_id) def __init__(self, word, language, rank): self.word = word self.language = language self.rank = rank @classmethod def find(cls, word, language): word = word.lower() try: return (cls.query.filter(cls.word == word).filter( cls.language == language).one()) except sqlalchemy.orm.exc.NoResultFound: return None @classmethod def find_all(cls, language): return cls.query.filter(cls.language == language).all() @classmethod def exists(cls, word, language): word = word.lower() try: (cls.query.filter(cls.word == word).filter( cls.language == language).one()) return True except sqlalchemy.orm.exc.NoResultFound: return False @classmethod def words_list(cls): words_list = [] for word in cls.find_all(): words_list.append(word.word) return words_list
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')