Esempio n. 1
0
class ChatRecords(Base, db.Model):
    """保存聊天记录 Model
    """

    __tablename__ = "chat_records"

    id_ = Column("id", db.Integer, db.Sequence("chat_records_id_seq"), primary_key=True)
    self_id = Column(db.String(32))
    ctx_id = Column(db.String(64))
    msg = Column(db.String)
    out = Column(db.Boolean, default=False)

    @classmethod
    async def add_msg(cls, event: Event, out: bool = False):
        await ChatRecords.create(
            self_id=str(event.self_id),
            ctx_id=context_id(event),
            msg=str(event.message),
            out=out,
        ),

    @classmethod
    async def get_last_msg(cls, event: Event):
        return (
            await ChatRecords.query.where(cls.ctx_id == context_id(event))
            .where(cls.self_id == str(event.self_id),)
            .order_by(cls.id_.desc())
            .gino.first()
        )
Esempio n. 2
0
class CETScore(Base, db.Model):
    """计划课程成绩 Model
    """

    _cn_list = ["准考证号", "考试场次", "语言级别", "总分", "听力", "阅读", "写作", "综合"]
    _en_list = [
        "exam_id",
        "exam_name",
        "level",
        "total",
        "listen",
        "read",
        "write",
        "common",
    ]
    __tablename__ = "score_cet"
    id_ = Column("id",
                 db.Integer,
                 db.Sequence("cet_score_id_seq"),
                 primary_key=True)
    student_id = Column(
        db.String(32),
        db.ForeignKey("user.student_id",
                      onupdate="CASCADE",
                      ondelete="SET NULL"),
        primary_key=True,
    )

    exam_id = Column(db.String(32), primary_key=True)
    exam_name = Column(db.String(64))
    level = Column(db.String(16))
    total = Column(db.String(16))
    listen = Column(db.String(16))
    read = Column(db.String(16))
    write = Column(db.String(16))
    common = Column(db.String(16))

    @classmethod
    async def add_or_update_one(cls, student_id, series):
        kwargs = dict(student_id=student_id)
        for cn_key, en_key in zip(cls._cn_list, cls._en_list):
            kwargs[en_key] = series[cn_key]
        sco = (await cls.query.where(cls.student_id == student_id
                                     ).where(cls.exam_id == series["准考证号"]
                                             ).gino.first())
        if sco:
            await sco.update(**kwargs).apply()
        else:
            await cls.create(**kwargs)

    @classmethod
    async def add_or_update(cls, student_id, table):
        for _, series in table.iterrows():
            await cls.add_or_update_one(student_id, series)
Esempio n. 3
0
class PhysicalOrCommonScore(Base, db.Model):
    """课程成绩 Model
    """

    _cn_list = ["学期", "课程", "课程号", "学分", "正考", "补考", "绩点"]
    _en_list = [
        "term",
        "course_name",
        "course_id",
        "credit",
        "score",
        "make_up_score",
        "gpa",
    ]
    __tablename__ = "score_physic_or_common"
    id_ = Column("id",
                 db.Integer,
                 db.Sequence("physic_or_common_score_id_seq"),
                 primary_key=True)
    student_id = Column(
        db.String(32),
        db.ForeignKey("user.student_id",
                      onupdate="CASCADE",
                      ondelete="SET NULL"),
        primary_key=True,
    )
    course_id = Column(db.String(16), primary_key=True)
    term = Column(db.String(64), primary_key=True)  # 学期
    course_name = Column(db.String(64))
    credit = Column(db.String(16))
    score = Column(db.String(16))  # 可能考试成绩是 `通过`
    make_up_score = Column(db.String(16))  # 补考成绩
    gpa = Column(db.String(16))
    cata = Column(db.String(16))

    @classmethod
    async def add_or_update_one(cls, student_id, cata, series):
        kwargs = dict(student_id=student_id, cata=cata)
        for cn_key, en_key in zip(cls._cn_list, cls._en_list):
            kwargs[en_key] = series[cn_key]
        sco = (await cls.query.where(cls.student_id == student_id
                                     ).where(cls.course_id == series["课程号"]
                                             ).where(cls.term == series["学期"]
                                                     ).gino.first())
        if sco:
            await sco.update(**kwargs).apply()
        else:
            await cls.create(**kwargs)

    @classmethod
    async def add_or_update(cls, student_id, score_dict, cata):
        table = score_dict[cata]
        for _, series in table.iterrows():
            await cls.add_or_update_one(student_id, cata, series)
Esempio n. 4
0
class SubContent(Base, db.Model):
    """所有订阅内容 Model
    """

    __tablename__ = "sub_content"

    link = db.Column(db.String(128), primary_key=True)
    name = db.Column(db.String(128))
    content = db.Column(db.LargeBinary)

    @classmethod
    async def add_or_update(cls, link, name, content) -> "SubContent":
        sub = await cls.get(link)
        if sub:
            await sub.update(link=link, name=name, content=content).apply()
        else:
            sub = await cls.create(link=link, name=name, content=content)
        return sub

    @classmethod
    async def check_update(cls):
        logger.info("开始检查RSS更新")
        all_subs = await SubContent.query.gino.all()
        if all_subs:
            await asyncio.wait([cls._check_one(sub) for sub in all_subs])
        logger.info("结束检查RSS更新")

    @classmethod
    async def _check_one(cls, sub):
        users = await SubUser.get_users(sub.link)
        if not users:
            return
        logger.info("检查" + sub.name + "更新")
        logger.info(sub.name + "的用户们:" + str(users))
        event_list = [ctx_id2event(user.ctx_id) for user in users]

        content = await get_rss_info(sub.link)
        old_content = pickle.loads(sub.content)
        diffs = diff(content, old_content)
        logger.info(sub.name + "的更新" + str(diffs))
        msgs = mk_msg_content(content, diffs)

        await asyncio.gather(*[send_msgs(event, msgs) for event in event_list])
        await SubContent.add_or_update(sub.link, sub.name,
                                       pickle.dumps(content))
Esempio n. 5
0
class CourseStudent(Base, db.Model):
    """学生选课表 Model
    """

    __tablename__ = "course_student"

    student_id = Column(
        db.String(32),
        db.ForeignKey("user.student_id",
                      onupdate="CASCADE",
                      ondelete="SET NULL"),
        primary_key=True,
    )
    course_json = Column(JSONB, nullable=False, server_default="{}")

    def __str__(self):
        return f"<CourseStudent id:{self.student_id}>"

    @classmethod
    async def add_or_update(cls, student_id, course_json) -> "CourseStudent":
        c_stu = await cls.get(student_id)
        if c_stu:
            await c_stu.update(course_json=course_json).apply()
        else:
            c_stu = await cls.create(student_id=student_id,
                                     course_json=course_json)
        return c_stu

    @classmethod
    async def get_course(cls, qq: int) -> Union["CourseStudent", str]:
        if not await User.check(qq):
            return "NOT_BIND"
        _bot = get_bot()
        query = cls.join(User).select()
        course_student = await query.where(User.qq == str(qq)).gino.first()
        if course_student is None:
            await add_job(cls.update_course, args=[qq])
            await _bot.send(qq2event(qq), "正在抓取课表,抓取过后我会直接发给你!")
            return "WAIT"
        return course_student

    @classmethod
    async def update_course(cls, qq: int):
        user: User = await User.get(str(qq))
        if not user:
            return
        _bot = get_bot()
        sess = await User.get_session(user)
        res = await run_sync_func(get_course_api, sess)
        if res:
            c_stu = await cls.add_or_update(student_id=user.student_id,
                                            course_json=json.dumps(res))
            await call_command(_bot, qq2event(qq), "cs")
            return c_stu
Esempio n. 6
0
class SubUser(Base, db.Model):
    """用户订阅 Model
    """

    __tablename__ = "sub_user"

    ctx_id = db.Column(db.String(64), primary_key=True)
    link = db.Column(
        db.String(128),
        db.ForeignKey("sub_content.link",
                      onupdate="CASCADE",
                      ondelete="SET NULL"),
        primary_key=True,
    )
    only_title = db.Column(db.Boolean, default=True)

    def __repr__(self):
        return f"<SubUser {self.ctx_id} {self.link}>"

    @classmethod
    async def add_sub(cls, event: Event, url: str, only_title=False):
        # TODO: UTF8
        try:
            d = await get_rss_info(url)
        except Exception as e:
            logger.exception(e)
            await send(get_bot(), event, "获取订阅信息失败,但已添加到订阅中,我们会稍后重试。")
            d = {"channel": {}, "entries": []}

        if not d:
            return None
        info = d["channel"]
        title = info.get("title", url)
        sub = await SubContent.add_or_update(
            link=url,
            name=title,
            content=pickle.dumps(d),
        )
        await SubUser.create(ctx_id=context_id(event, mode="group"),
                             link=sub.link,
                             only_title=only_title)
        return title

    @classmethod
    async def get_sub(cls, event: Event, url: str):
        ctx_id = context_id(event, mode="group")
        loader = SubUser.load(sub_content=SubContent)
        sub = (await
               cls.outerjoin(SubContent).select().where(cls.link == url).where(
                   cls.ctx_id == ctx_id).gino.load(loader).first())
        return sub

    @classmethod
    async def get_user_subs(cls, event: Event) -> List["SubUser"]:
        ctx_id = context_id(event, mode="group")
        loader = SubUser.load(sub_content=SubContent)
        sub = (await cls.outerjoin(SubContent).select().where(
            cls.ctx_id == ctx_id).gino.load(loader).all())
        return sub

    @classmethod
    async def remove_sub(cls, event: Event, url: str):
        ctx_id = context_id(event, mode="group")
        sub = (await
               cls.query.where(cls.link == url).where(cls.ctx_id == ctx_id
                                                      ).gino.first())
        await sub.delete()
        return True

    @classmethod
    async def get_users(cls, url: str):
        sub = await cls.query.where(cls.link == url).gino.all()
        return sub
Esempio n. 7
0
class User(Base, db.Model):
    """用户表 Model
    """

    __tablename__ = "user"

    qq = Column(db.String(16), primary_key=True)
    student_id = Column(db.String(32), unique=True)
    password = Column(db.String(64), nullable=False)
    cookies = Column(db.LargeBinary)

    @classmethod
    async def add(cls, *, qq: int, student_id: int, password: str, cookies):
        user = User(
            student_id=str(student_id), qq=str(qq), password=password, cookies=cookies,
        )
        await user.create()
        return user

    @classmethod
    async def unbind(cls, qq: int):
        user = await User.query.where(User.qq == str(qq)).gino.first()
        await user.delete()
        return True

    @classmethod
    async def check(cls, qq: int):
        user = await cls.get(str(qq))
        if user:
            return user
        _bot = get_bot()
        await _bot.send(qq2event(qq), "未绑定,试试对我发送 `绑定`")
        return False

    @classmethod
    async def get_cookies(cls, user: "******"):
        from auth_swust import Login
        from auth_swust import request as login_request

        cookies = pickle.loads(user.cookies)
        sess = login_request.Session(cookies)
        res = await run_sync_func(
            sess.get, API.jwc_index, allow_redirects=False, verify=False
        )

        # 302重定向了,session 失效,重新获取session
        if res.status_code == 302 or res.status_code == 301:
            u_ = Login(user.student_id, user.password)
            is_log, _ = await run_sync_func(u_.try_login)
            if is_log:
                cookies = pickle.dumps(u_.get_cookies())
                await user.update(cookies=cookies).apply()
                logger.info(f"更新qq: {user.qq} 的 session")
                return u_.get_cookies()
        else:
            return cookies

    @classmethod
    async def get_session(cls, user: "******"):
        from auth_swust import request as login_request

        key = f"cookies/{user.qq}"
        cookies = await cache.get(key)
        if not cookies:
            cookies = await cls.get_cookies(user)
            await cache.set(key, cookies, ttl=Config.CACHE_SESSION_TIMEOUT)
        sess = login_request.Session(cookies)
        return sess
Esempio n. 8
0
class PlanScore(Base, db.Model):
    """计划课程成绩 Model
    """

    _cn_list = ["课程", "课程号", "学分", "课程性质", "正考", "补考", "绩点"]
    _en_list = [
        "course_name",
        "course_id",
        "credit",
        "property_",
        "score",
        "make_up_score",
        "gpa",
    ]
    __tablename__ = "score_plan"
    id_ = Column("id",
                 db.Integer,
                 db.Sequence("plan_score_id_seq"),
                 primary_key=True)
    student_id = Column(
        db.String(32),
        db.ForeignKey("user.student_id",
                      onupdate="CASCADE",
                      ondelete="SET NULL"),
        primary_key=True,
    )
    course_id = Column(db.String(16), primary_key=True)
    term = Column(db.String(64), primary_key=True)  # 学期
    course_name = Column(db.String(64))
    property_ = Column("property", db.String(64))  # 必修 选修 限选
    credit = Column(db.String(16))
    score = Column(db.String(16))  # 可能考试成绩是 `通过`
    make_up_score = Column(db.String(16))  # 补考成绩
    gpa = Column(db.String(16))
    season = Column(db.String(16))  # 春季 秋季 term中表现为春季2 秋季1

    @classmethod
    async def add_or_update_one(cls, student_id, term, season, series):
        kwargs = dict(student_id=student_id, term=term, season=season)
        for cn_key, en_key in zip(cls._cn_list, cls._en_list):
            kwargs[en_key] = series[cn_key]
        sco = (await
               cls.query.where(cls.student_id == student_id
                               ).where(cls.course_id == series["课程号"]
                                       ).where(cls.term == term).gino.first())
        if sco:
            await sco.update(**kwargs).apply()
        else:
            await cls.create(**kwargs)

    @classmethod
    async def add_or_update(cls, student_id, plan):
        terms = pd.unique(plan["term"])
        for term in terms:
            _term = plan[plan["term"] == term]
            seasons = pd.unique(_term["season"])
            for season in seasons:
                data = _term[_term["season"] == season]
                for _, series in data.iterrows():
                    await cls.add_or_update_one(student_id, term, season,
                                                series)

    @classmethod
    def to_df(cls, plan_scores: List["PlanScore"]):
        dct = defaultdict(list)
        for item in plan_scores:
            for en, cn in zip(PlanScore._en_list, PlanScore._cn_list):
                dct[cn].append(getattr(item, en, None))
            dct["term"].append(getattr(item, "term", None))
            dct["season"].append(getattr(item, "season", None))

        df = pd.DataFrame(data=dct)
        return df

    @classmethod
    async def load_score(cls, event: Event) -> pd.DataFrame:
        user = await User.check(event.user_id)
        if not user:
            return
        scores = await cls.query.where(cls.student_id == user.student_id
                                       ).gino.all()
        if not scores:
            return
        df = cls.to_df(scores)
        return df