class Role(BaseModel): __tablename__ = 'role' id = Column(String, primary_key=True) proj_id = Column(String, ForeignKey('proj.id', ondelete='CASCADE')) dep = Column(DEP, index=True) name = Column(String, unique=True) note = Column(Text, nullable=True) taken = Column(Boolean, default=False) proj = relationship('Proj', back_populates='roles') pits = relationship('Pit', back_populates='role', lazy='dynamic', passive_deletes=True) __table_args__ = (UniqueConstraint('proj_id', 'dep', 'name', name='_role_uc'), ) __id_len__ = 12 @property def pit(self): return self.pits.filter(Pit.status.in_({Pit.S.fin, Pit.S.fin_p})).one() def __json__(self): return { 'id': self.id, 'dep': self.dep, 'name': self.name, 'note': self.note, }
class Chat(BaseModel): __tablename__ = 'chat' id = Column(String, primary_key=True) order = Column(Integer) proj_id = Column(String, ForeignKey('proj.id', ondelete='CASCADE')) pink_id = Column(String, ForeignKey('pink.id', ondelete='SET NULL')) reply_to_id = Column(String, ForeignKey('chat.id', ondelete='CASCADE'), nullable=True) deleted = Column(Boolean, default=False) ver = Column(Integer, default=0) reply_to_ver = Column(Integer, nullable=True) content = Column(Text) at = Column(DateTime) history = Column(JSONB, default=dict) proj = relationship('Proj', back_populates='chats') reply_to = relationship('Chat', remote_side=[id], back_populates='replies') replies = relationship('Chat', remote_side=[id], order_by='Chat.order', back_populates='reply_to', lazy='dynamic') __id_len__ = 14 def update(self, now, content): if self.ver > 0: self.history[self.ver] = { 'reply_to_ver': self.reply_to_ver, 'content': self.content, 'at': self.at.timestamp(), } self.ver += 1 if self.reply_to_id: self.reply_to_ver = self.reply_to.ver self.content = content self.at = now def set_order(self, proj_timestamp, now): self.order = (now.timestamp() - proj_timestamp.timestamp()) * 10 def __json__(self): return { 'id': self.id, 'order': self.order, 'deps': self.deps, 'pink_id': self.pink_id, 'reply_to_id': self.reply_to_id, 'ver': self.ver, 'content': self.content, 'at': self.at }
class Pink(BaseModel): __tablename__ = 'pink' id = Column(String, primary_key=True) name = Column(String, unique=True, index=True) email = Column(String, unique=True) qq = Column(String, nullable=True) other = Column(String, nullable=True) deps = Column(ARRAY(DEP)) _pwd = Column(String) active = Column(Boolean, default=True) pits = relationship('Pit', back_populates='pink', lazy='dynamic', passive_deletes=True) ducks = relationship('Duck', back_populates='pink', lazy='dynamic', passive_deletes=True) lemons = relationship('Lemon', back_populates='pink', lazy='dynamic', passive_deletes=True) __id_len__ = 9 @property def pwd(self): raise AttributeError('SHOLD NOT READ PWD') @pwd.setter def pwd(self, new_pwd): self._pwd = pwd_hasher.hash(new_pwd) def check_pwd(self, pwd): return pwd_hasher.verify(self._pwd, pwd) def __json__(self): return { 'id': self.id, 'name': self.name, 'qq': self.qq, 'other': self.other, 'deps': self.deps }
class Lemon(BaseModel): __tablename__ = 'lemon' id = Column(String, primary_key=True) key = Column(String) pink_id = Column(String, ForeignKey('pink.id', ondelete='CASCADE')) ip = Column(String) device_id = Column(UUID) expiration = Column(DateTime) timestamp = Column(DateTime) pink = relationship('Pink', back_populates='lemons') __table_args__ = (UniqueConstraint('pink_id', 'device_id', name='_lemon_uc'), ) __id_len__ = 10 def __json__(self): return {'id': self.id, 'ip': self.ip, 'exp': self.expiration}
class Mango(BaseModel): __tablename__ = 'mango' id = Column(String, primary_key=True) pit_id = Column(String, ForeignKey('pit.id', ondelete='CASCADE')) ver = Column(Integer, default=1) mime = Column(String) sha1 = Column(String, unique=True) modified_at = Column(DateTime) timestamp = Column(DateTime) metainfo = Column(JSONB) pit = relationship('Pit', back_populates='mangos') __table_args__ = (UniqueConstraint('pit_id', 'ver', name='_mango_uc'), ) @property def mtype(self): return self.mime.split('/')[0]
class Duck(BaseModel): __tablename__ = 'duck' pink_id = Column(String, ForeignKey('pink.id', ondelete='CASCADE'), primary_key=True) node = Column(String, primary_key=True) allow = Column(Boolean) scopes = Column(ARRAY(String)) pink = relationship('Pink', back_populates='lemons') __table_args__ = (UniqueConstraint('pink_id', 'node', name='_duck_uc'), ) def __json__(self): return { 'pink_id': self.pink_id, 'node': self.node, 'allow': self.allow, 'scopes': self.scopes }
class Pit(BaseModel): __tablename__ = 'pit' # Status class S(ZEnum): init = 'I' pending = 'P' working = 'w' delayed = 'd' past_due = 'p' auditing = 'a' fin = 'F' fin_p = 'Fp' dropped = 'D' id = Column(String, primary_key=True) role_id = Column(String, ForeignKey('role.id', ondelete='CASCADE')) pink_id = Column(String, ForeignKey('pink.id', ondelete='SET NULL')) status = Column(Enum(S, name='pit_status'), default=S.init, index=True) start_at = Column(DateTime, nullable=True) finish_at = Column(DateTime, nullable=True) due = Column(DateTime, nullable=True) timestamp = Column(DateTime) track = Column(ARRAY(String), default=list) pink = relationship('Pink', back_populates='pits') role = relationship('Role', back_populates='pits') mangos = relationship('Mango', back_populates='pit', order_by='Mango.ver.desc()', lazy='dynamic', passive_deletes=True) __id_len__ = 13 @property def mango(self): return self.mangos.order_by(Mango.ver.desc()).first() # Trace class T(ZEnum): pick_f = 'P' drop = 'd' shift = '<-' cascade = '->' past_due = '<>' fake_past_due = '><' submit = 's' submit_f = 'S' redo = 'r' check_pass = '******' check_fail = 'x' extend = '+' def add_track(self, info: 'T', now, by=''): base = f'{info.value} - {now}' if info in (Pit.T.past_due, Pit.T.pick_f, Pit.T.submit_f, Pit.T.check_fail, Pit.T.check_pass): self.track.append(f'{base} by:{by}') elif info in (Pit.T.shift, Pit.T.cascade): self.track.append(f'{base} by:{by} from:{self.due}') elif info == Pit.T.extend: self.track.append(f'{base} from:{self.due}') else: self.track.append(base) def __json__(self): return { 'id': self.id, 'role_id': self.role_id, 'dep': self.role.dep, 'role': self.role.name, 'pink_id': self.pink_id, 'status': self.status, 'start_at': self.start_at, 'finish_at': self.finish_at, 'due': self.due }
class Proj(BaseModel): __tablename__ = 'proj' # Status class S(ZEnum): pre = 'p' freezed = 'FF' working = 'w' upload = 'u' fin = 'F' # Category class C(ZEnum): doc = 'documentary' sub = 'sub-content' ani = 'animation' id = Column(String, primary_key=True) title = Column(String, index=True) source = Column(String) cat = Column(Enum(C, name='proj_cat')) suff = Column(String) status = Column(Enum(S, name='proj_status'), default=S.pre) leader_id = Column(String, ForeignKey('pink.id', ondelete='SET NULL')) word_count = Column(Integer) start_at = Column(DateTime, nullable=True) finish_at = Column(DateTime, nullable=True) url = Column(String, nullable=True) timestamp = Column(DateTime) track = Column(ARRAY(String), default=list) roles = relationship('Role', back_populates='proj', lazy='dynamic', passive_deletes=True) chats = relationship('Chat', back_populates='proj', lazy='dynamic', passive_deletes=True) __table_args__ = (UniqueConstraint('source', 'cat', 'suff', name='_proj_uc'), ) __id_len__ = 11 @hybrid_property def display_title(self): return f'{self.title}({self.suff})' if self.suff else self.title # Trace class T(ZEnum): re_open = 'r' freeze = 'F' upload = 'U' start = 's' def add_track(self, info: 'Proj.T', now, by=''): base = f'{info.value} - {now}' if info == Proj.T.re_open: self.track.append(f'{base} by:{by}') else: self.track.append(base) def __json__(self): return { 'id': self.id, 'title': self.display_title, 'source': self.source, 'cat': self.cat, 'status': self.status, 'leader_id': self.leader_id, 'word_count': self.word_count, 'start_at': self.start_at }