class StudyPlan(TimestampedModel): """Study plan model class""" __tablename__ = "study_plans" name = db.Column(db.String(100), nullable=False) description = db.Column(db.String) order = db.Column( db.Enum(OrderTypeEnum), default=OrderTypeEnum.oldest, nullable=False ) see_solved = db.Column(db.Boolean(), default=False) user_id = db.Column(db.Integer, db.ForeignKey("users.id")) user = db.relationship(User, backref=backref("study_plans", cascade="all,delete")) def __init__(self, **kwargs): """Initialize a study plan""" user = False for k, v in kwargs.items(): if k == "user": user = True if user and "user_id" in kwargs: kwargs.pop("user_id") if "order" not in kwargs: kwargs["order"] = OrderTypeEnum.oldest super(StudyPlan, self).__init__(**kwargs) def __repr__(self): return f"<StudyPlan: {self.name} - {self.state}>" @property def to_json(self): return dict( id=self.id, name=self.name, description=self.description, user=self.user_id, order=self.order.value, ) @classmethod def create_default_study_plan(cls, user_id): user = User.query.get_or_404(user_id) default_plan = StudyPlan( name="Default", description="Default Study Plan", user=user ) default_plan.save() return default_plan def delete(self): create_default = False user = User.query.filter_by(id=self.user_id).first() if user and len(user.study_plans) < 2: create_default = True db.session.delete(self.query.filter_by(id=self.id).first()) db.session.commit() if create_default: self.create_default_study_plan(user_id=user.id)
class StudySessionLog(TimestampedModel): """ Tracks a study session. Records each card as a study session progresses """ __tablename__ = "study_session_logs" study_session_id = db.Column( db.Integer, db.ForeignKey("study_sessions.id"), nullable=False ) card_id = db.Column(db.Integer, db.ForeignKey("cards.id"), nullable=False) study_session = db.relationship( StudySession, backref=backref("study_session_logs", cascade="all,delete") ) def __init__(self, **kwargs): """Initialize a Study Session Log""" super(StudySessionLog, self).__init__(**kwargs) def __repr__(self): return f"<StudySessionLog: {self.study_session.deck.name} - {self.state}>"
class StudySession(TimestampedModel): """ Represents a study session. Keeps track of deck study progress """ __tablename__ = "study_sessions" deck_id = db.Column(db.Integer, db.ForeignKey("decks.id"), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) known = db.Column(db.Integer, nullable=True, default=0) unknown = db.Column(db.Integer, nullable=True, default=0) deck = db.relationship( Deck, backref=backref("study_sessions", cascade="all,delete") ) user = db.relationship( User, backref=backref("study_sessions", cascade="all,delete") ) def __init__(self, **kwargs): """Initialize a Study Session""" super(StudySession, self).__init__(**kwargs) def save(self): db.session.add(self) db.session.commit() @property def to_json(self): return dict( id=self.id, state=self.state, deck_id=self.deck_id, user_id=self.user_id, known=self.known, unknown=self.unknown, ) def __repr__(self): return f"<StudySession: {self.deck.name} - {self.state}>"
class Card(TimestampedModel): """Card model class""" __tablename__ = "cards" front = db.Column(db.Text(), nullable=False) back = db.Column(db.Text(), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) deck_id = db.Column(db.Integer, db.ForeignKey("decks.id"), nullable=False) user = db.relationship(User, backref=backref("cards", cascade="all,delete")) deck = db.relationship(Deck, backref=backref("cards", cascade="all,delete")) def __init__(self, **kwargs): """Initialize a card""" super(Card, self).__init__(**kwargs) @property def short_front(self): return self.front[:50] if len(self.front) > 50 else self.front def __repr__(self): return ( f"<Card: {self.short_front} - {self.user.username}" f" - {self.deck.name}>" ) @property def to_json(self): return dict( id=self.id, state=self.state, front=self.front, back=self.back, short_front=self.short_front, user_id=self.user_id, deck_id=self.deck_id, ) @classmethod def get_next_card(cls, study_session_id, deck_id): session = StudySession.query.filter_by( id=study_session_id, user_id=g.user.id, deck_id=deck_id, state="Studying" ).first() if session is None: abort(404) study_logs = db.session.query(StudySessionLog.card_id).filter_by( study_session_id=session.id ) study_plan = db.session.query(StudyPlan).filter_by(user_id=g.user.id).first() ordering = func.random() if study_plan: if study_plan.order.value == "latest": ordering = Card.date_created.desc() elif study_plan.order.value == "oldest": ordering = Card.date_created.asc() card = ( db.session.query(Card) .filter( Card.state == "Active", Card.user_id == g.user.id, Card.deck_id == session.deck_id, ~(Card.id.in_(study_logs)), ) .order_by(ordering) .first() ) return card
class Deck(TimestampedModel): """Deck model class""" __tablename__ = "decks" name = db.Column(db.String(100), nullable=False) description = db.Column(db.String) user_id = db.Column(db.Integer, db.ForeignKey("users.id")) parent_id = db.Column(db.Integer, db.ForeignKey("decks.id")) user = db.relationship(User, backref=backref("decks", cascade="all,delete")) children = db.relationship("Deck", cascade="all,delete") def __init__(self, **kwargs): """Initialize a Deck""" user = False for k, v in kwargs.items(): if k == "user": user = True if user and "user_id" in kwargs: kwargs.pop("user_id") super(Deck, self).__init__(**kwargs) def save(self): if self.parent_id and Deck.query.filter_by(id=self.parent_id).first() is None: raise ValueError("Parent does not exist") db.session.add(self) db.session.commit() @property def to_json(self): return dict( id=self.id, name=self.name, state=self.state, description=self.description, user=self.user_id, parent=self.parent_id, child_count=self.child_count, card_count=self.card_count, stats=self.quick_stats, date_created=self.date_created, date_updated=self.date_updated, ) @property def children_to_json(self): return dict([child.to_json for child in self.children]) @property def child_count(self): return len(self.children) @property def card_count(self): return len(self.cards) @property def quick_stats(self): stats = { "progress": 0, "known": 0, "unknown": 0, "remaining": self.card_count, "last_studied": "", } if self.state != "New": study_session = ( StudySession.query.filter_by(deck_id=self.id, user_id=g.user.id) .order_by(StudySession.date_updated.desc()) .first() ) if study_session: known, unknown = study_session.known, study_session.unknown stats["known"] = known stats["unknown"] = unknown stats["remaining"] = self.card_count - (known + unknown) stats["last_studied"] = study_session.date_updated if all([known + unknown > 0, self.card_count > 0]): stats["progress"] = int(((known + unknown) / self.card_count) * 100) return stats @classmethod def create_default_deck(cls, user_id): user = User.query.get_or_404(user_id) default_deck = Deck( name="Default", description="Default Deck", user=user, state="New" ) default_deck.save() return default_deck def delete(self): create_default = False user = User.query.filter_by(id=self.user_id).first() if user and len(user.decks) < 2: create_default = True db.session.delete(self.query.filter_by(id=self.id).first()) db.session.commit() if create_default: self.create_default_deck(user_id=user.id) def __repr__(self): return f"<Deck: {self.name}>"