class Invitation(db.Model): id = db.Column(db.Integer, primary_key=True) key_for_sharing = db.Column(db.String(40), nullable=False, unique=True, default=lambda: str(uuid.uuid4())) inviter_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) inviter = db.relationship('User', lazy='joined', backref='invites') team_id = db.Column(db.Integer, db.ForeignKey('team.id'), nullable=False) team = db.relationship('Team', lazy='joined', backref='invitations') invited_email = db.Column(db.String(127)) message = db.Column(db.Text) status = db.Column(db.String(127)) created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True) @hybrid_property def is_deleted(self): return self.deleted_at == None @property def already_accepted(self): self.status == 'accepted' def mark_accepted(self): self.status = 'accepted'
class TeamUser(db.Model): user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True) user = db.relationship('User', backref='team_users') team_id = db.Column(db.Integer, db.ForeignKey('team.id'), primary_key=True) team = db.relationship('Team', backref='team_users') created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True) @hybrid_property def is_deleted(self): return self.deleted_at == None
class SurveyAnswer(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) mission_id = db.Column(db.Integer, db.ForeignKey('mission.id'), primary_key=True) mission = db.relationship('Mission', backref='mission_survey_answers', single_parent=True, cascade='all, delete-orphan') user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True) user = db.relationship('User', backref='user_survey_answers', single_parent=True, cascade='all, delete-orphan') question_id = db.Column(db.String(5)) answer = db.Column(db.String(256)) survey_version = db.Column(db.Integer) created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True)
class MissionGoal(db.Model): mission_id = db.Column(db.Integer, db.ForeignKey('mission.id'), primary_key=True) mission = db.relationship('Mission', backref='mission_goals', single_parent=True, cascade='all, delete-orphan') goal_id = db.Column(db.Integer, db.ForeignKey('goal.id'), primary_key=True) goal = db.relationship('Goal', backref='goal_mission') week = db.Column(db.SmallInteger) created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True) @hybrid_property def is_deleted(self): return self.deleted_at == None
class UserAchievement(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(127)) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) user = db.relationship('User', backref='achievements') created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True) @hybrid_property def is_deleted(self): return self.deleted_at == None
class Goal(db.Model): id = db.Column(db.Integer, primary_key=True) short_description = db.Column(db.Text) category = db.Column(db.String(127), default='diet') created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True) visible_after = db.Column(PendulumDateTimeField(), nullable=True) hidden_after = db.Column(PendulumDateTimeField(), nullable=True) class Serializer(db.Model.Serializer): short_description = serpy.StrField() category = serpy.StrField() actual_goal = GoalSerializer() @property def actual_goal(self): cat_goals = GOALS_BY_CATEGORY[self.category] return [ g for g in cat_goals['goals'] if g['name'] == self.short_description ][0] @hybrid_property def is_visible(self): now = pendulum.now('UTC') return (((self.visible_after == None) | (self.visible_after < now)) & ((self.hidden_after == None) | (self.hidden_after > now))) @hybrid_property def is_deleted(self): return self.deleted_at == None
class Team(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(127), default='') description = db.Column(db.Text, default='Best. Crew. Ever.') captain_id = db.Column(db.Integer, db.ForeignKey('user.id')) captain = db.relationship('User', backref='captain_of') members = association_proxy('team_users', 'user', creator=lambda member: TeamUser(user=member)) created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True) def generic_invitation_from(self, inviter) -> Invitation: """returns an invitation not addressed to a particular email""" # find an existing invitation from this person for i in self.invitations: if i.inviter == inviter and i.invited_email is None: return i # if we got here, no such luck; gotta make our own luck invitation = Invitation( inviter=inviter, team=self, ) invitation.save() return invitation @hybrid_property def is_deleted(self): return self.deleted_at == None
class Pledge(db.Model): user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True) mission_id = db.Column(db.Integer, db.ForeignKey('mission.id'), primary_key=True) mission = db.relationship('Mission', backref='pledges') goal_id = db.Column(db.Integer, db.ForeignKey('goal.id'), primary_key=True) goal = db.relationship('Goal', backref='pledges') fulfilled = db.Column(db.Boolean, default=False) start_at = db.Column(PendulumDateTimeField, nullable=True, default=None) end_at = db.Column(PendulumDateTimeField, nullable=True, default=None) created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True) @hybrid_property def is_deleted(self): return self.deleted_at == None
class User(db.Model): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(127), unique=True) name = db.Column(db.String(127), default='') password_hash = db.Column(db.String(127), nullable=True) email_confirmed = db.Column(db.Boolean, default=False) teams = association_proxy('team_users', 'team') created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True) def set_password(self, password): self.password_hash = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) @hybrid_property def is_active(self): return self.deleted_at == None @property def first_name(self): return self.name.split(' ')[0] # required by Flask Login @property def is_authenticated(self): return True @property def is_anonymous(self): return False def get_id(self): return str(self.id)
class TeamAchievement(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(127)) team_id = db.Column(db.Integer, db.ForeignKey('team.id')) team = db.relationship('Team', backref='achievements') mission_id = db.Column(db.Integer, db.ForeignKey('mission.id'), nullable=True) mission = db.relationship('Mission', backref='achievements') created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True) @hybrid_property def is_deleted(self): return self.deleted_at == None
class Mission(db.Model): id = db.Column(db.Integer, primary_key=True) uuid = db.Column(db.String(32), unique=True, default=lambda: uuid.uuid4().hex) title = db.Column(db.String(127), default='') short_description = db.Column(db.String(127), default='') duration_in_weeks = db.Column(db.SmallInteger, default=4) frozen = db.Column(db.Boolean, default=False) started_at = db.Column(PendulumDateTimeField(), nullable=True) team_id = db.Column(db.Integer, db.ForeignKey('team.id')) team = db.relationship('Team', backref='missions') goals = association_proxy('mission_goals', 'goal', creator=lambda goal: MissionGoal(goal=goal)) created_at = db.Column(PendulumDateTimeField(), default=lambda: pendulum.now('UTC')) deleted_at = db.Column(PendulumDateTimeField(), nullable=True) class Serializer(db.Model.Serializer): uuid = serpy.StrField() title = serpy.StrField() short_description = serpy.StrField() duration_in_weeks = serpy.IntField() goals = Goal.Serializer(many=True) @property def is_deleted(self): return self.deleted_at != None @property def days_until_start(self): time_left = self.start_time - pendulum.now('UTC') return time_left.in_days() @property def primary_goal_str(self): if not self.goals: return None else: return self.goals[0].short_description @property def co2_saved_str(self): # todo: pull from DB return '345kg' @property def start_time_str(self): return self.started_at.format('dddd [the] Do [of] MMMM') @property def end_time(self): return self.started_at.add(weeks=self.duration_in_weeks) @property def end_time_str(self): return self.end_time.format('dddd [the] Do [of] MMMM') @property def is_over(self): now = pendulum.now('UTC') mission_end_time = self.started_at.add(weeks=self.duration_in_weeks) return now >= mission_end_time @property def is_running(self): now = pendulum.now('UTC') mission_end_time = self.started_at.add(weeks=self.duration_in_weeks) return now < mission_end_time and now >= self.started_at and not self.is_deleted @property def is_upcoming(self): now = pendulum.now('UTC') return now < self.started_at and not self.is_deleted @property def start_time(self): return self.started_at @property def end_time(self): return self.started_at.add(weeks=self.duration_in_weeks) @property def mission_day(self): return (pendulum.now('UTC') - self.started_at).in_days() @property def duration_in_days(self): return self.duration_in_weeks * 7