Example #1
0
class ViewRecord(db.Model):
    __tablename__ = 'user_view_record'

    _cache_key_prefix = 'user:view:record:'
    _records_by_student_and_post_and_type_cache_key = (
        _cache_key_prefix + 'student:%s:post:%s:type:%s')

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    student_id = db.Column(db.String(56))
    post_id = db.Column(db.String(56))
    post_type_ = db.Column(db.SmallInteger)
    create_time = db.Column(db.DateTime, default=datetime.now)

    def __init__(self, student_id, post_id, post_type):
        self.student_id = student_id
        self.post_id = post_id
        self.post_type_ = post_type.value

    @property
    def post_type(self):
        return PostType(self.post_type_)

    @classmethod
    def get(cls, id_):
        return cls.query.filter_by(id=id_).first()

    @classmethod
    def gets(cls, student_id, post_id, post_type=PostType.course_post):
        cache_key = cls._records_by_student_and_post_and_type_cache_key % (
            student_id, post_id, post_type.value)
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        records = cls.query.filter_by(student_id=student_id,
                                      post_id=post_id,
                                      post_type_=post_type.value).all()
        if records:
            mc.set(cache_key, pickle.dumps(records).hex())
            mc.expire(cache_key, ONE_DAY)
        return records

    @classmethod
    def add(cls, student_id, post_id, post_type=PostType.course_post):
        record = ViewRecord(student_id, post_id, post_type)
        db.session.add(record)
        db.session.commit()
        record.clear_cache()

    @classmethod
    def delete_records_by_post(cls, post_id, post_type):
        sql = ('delete from user_view_record '
               'where post_id={post_id} '
               'and post_type_={post_type}'.format(post_id=post_id,
                                                   post_type=post_type.value))
        db.engine.execute(sql)

    def clear_cache(self):
        mc.delete(self._records_by_student_and_post_and_type_cache_key %
                  (self.student_id, self.post_id, self.post_type_))
Example #2
0
class FilePhoto(db.Model):
    __tablename__ = 'file_photo'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_id = db.Column(db.Integer)
    bucket = db.Column(db.String(80))
    file_name = db.Column(db.String(80))
    filesize = db.Column(db.String(80))
    hash = db.Column(db.String(128))
    status = db.Column(db.Integer)
    create_time = db.Column(db.DateTime, default=datetime.now)
    update_time = db.Column(db.DateTime, default=datetime.now)

    def __init__(self,
                 user_id,
                 bucket,
                 file_name,
                 status=FileStatus.ready_to_upload,
                 filesize=None,
                 hash=None):
        self.user_id = user_id
        self.bucket = bucket
        self.file_name = file_name
        self.filesize = filesize
        self.hash = hash
        self.status = status.value

    def dump(self):
        return dict(id=self.id, file_name=self.file_name)

    @classmethod
    def add(cls, user_id, bucket, file_name):
        photo = FilePhoto(user_id, bucket, file_name)
        db.session.add(photo)
        db.session.commit()
        return photo.id

    @classmethod
    def get(cls, id_):
        return cls.query.filter_by(id=id_).first()

    @classmethod
    def get_by_file_name(cls, file_name):
        return cls.query.filter_by(file_name=file_name).first()

    @classmethod
    def get_by_user(cls, user_id):
        return cls.query.filter_by(user_id=user_id).all()

    def update(self, filesize, hash, status=FileStatus.has_uploaded):
        self.filesize = filesize
        self.hash = hash
        self.status = status.value
        db.session.add(self)
        db.session.commit()
Example #3
0
class UserBehavior(db.Model):
    __tablename__ = 'user_behavior'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_id = db.Column(db.Integer)
    type_ = db.Column(db.SmallInteger)
    detail = db.Column(db.Text)
    create_time = db.Column(db.DateTime, default=datetime.now)

    def __init__(self, user_id, type_, detail):
        self.user_id = user_id
        self.type_ = type_.value
        self.detail = detail

    def dump(self):
        return dict(id=self.id, type=self.type_, detail=self.detail)

    @property
    def behavior_type(self):
        return UserBehaviorType(self.type_)

    @property
    def behavior_name(self):
        return self.behavior_type.name

    @classmethod
    def add(cls, user_id, type_, detail=None):
        detail = json.dumps(detail)
        behavior = UserBehavior(user_id, type_, detail)
        db.session.add(behavior)
        db.session.commit()

    @classmethod
    def get(cls, id_):
        return cls.query.filter_by(id=id_).first()

    @classmethod
    def get_by_type(cls, type_):
        return cls.query.filter_by(type_=type_.value).all()

    @classmethod
    def get_by_user(cls, user_id):
        return cls.query.filter_by(user_id=user_id).all()

    @classmethod
    def get_by_user_and_type(cls, user_id, type_):
        return cls.query.filter_by(user_id=user_id, type_=type_.value).all()
class CourseSchedule(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    course_id = db.Column(db.Integer, db.ForeignKey('course.id'))
    day = db.Column(db.Integer)
    start = db.Column(db.Integer)
    end = db.Column(db.Integer)
    frequency = db.Column(db.String(10))

    def __init__(self, course_id, day, start, end, frequency):
        self.course_id = course_id
        self.day = day
        self.start = start
        self.end = end
        self.frequency = frequency

    def dump(self):
        return dict(id=self.id, course_id=self.course_id,
                    day=self.day, start=self.start, end=self.end,
                    frequency=self.frequency)

    @classmethod
    def add(cls, course_id, day, start, end, frequency):
        schedule = CourseSchedule(course_id, day, start, end, frequency)
        db.session.add(schedule)
        db.session.commit()

    @classmethod
    def get(cls, id_):
        return cls.query.get(id_)

    @classmethod
    def get_by_course(cls, course_id):
        return cls.query.filter_by(course_id=course_id).all()
Example #5
0
class CoursePost(db.Model):
    __tablename__ = 'course_post'

    _cache_key_prefix = 'course:post:'
    _course_post_by_id_cache_key = _cache_key_prefix + 'id:%s'
    _post_pv_by_id_cache_key = _cache_key_prefix + 'pv:id:%s'
    _course_post_supply_by_post_id_cache_key = _cache_key_prefix + 'supply:id:%s'
    _course_post_demand_by_post_id_cache_key = _cache_key_prefix + 'demand:id:%s'

    MAX_EDIT_TIMES = 5

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    student_id = db.Column(db.Integer, db.ForeignKey('student.id'))
    status_ = db.Column(db.SmallInteger)
    switch = db.Column(db.SmallInteger)
    mobile = db.Column(db.String(80))
    wechat = db.Column(db.String(80))
    message = db.Column(db.String(256))
    pv_ = db.Column(db.Integer, default=0)
    editable = db.Column(db.SmallInteger, default=MAX_EDIT_TIMES)
    create_time = db.Column(db.DateTime, default=datetime.now)
    update_time = db.Column(db.DateTime, default=datetime.now)

    def __init__(self,
                 student_id,
                 switch,
                 mobile,
                 wechat,
                 message,
                 status=PostStatus.normal):
        self.student_id = student_id
        self.switch = switch.value
        self.mobile = mobile
        self.wechat = wechat
        self.message = message
        self.status_ = status.value

    def dump(self):
        return dict(id=self.id,
                    student=self.student.dump(),
                    supply=self.supply.share_dump() if self.supply else dict(),
                    demand=self.demand.share_dump() if self.demand else dict(),
                    switch=self.switch,
                    mobile=self.mobile,
                    wechat=self.wechat,
                    message=self.message,
                    pv=self.pv,
                    status=self.status_,
                    editable=self.editable,
                    create_time=self.create_time,
                    update_time=self.update_time,
                    fuzzy_id=self.fuzzy_id)

    def share_dump(self):
        return dict(id=self.id,
                    student=self.student.share_dump(),
                    supply=self.supply.share_dump() if self.supply else dict(),
                    demand=self.demand.share_dump() if self.demand else dict(),
                    message=self.message,
                    pv=self.pv,
                    status=self.status_,
                    create_time=self.create_time,
                    update_time=self.update_time)

    @classmethod
    def get(cls, id_):
        cache_key = cls._course_post_by_id_cache_key % id_
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        post = CoursePost.query.get(id_)
        if post:
            mc.set(cache_key, pickle.dumps(post).hex())
            mc.expire(cache_key, ONE_HOUR)
        return post

    @classmethod
    def gets(cls,
             limit=5,
             offset=0,
             order=OrderType.descending,
             closed=1,
             supply=None,
             demand=None):
        if supply and demand:
            return cls.gets_by_supply_and_demand(limit, offset, order, supply,
                                                 demand, closed)
        elif supply and not demand:
            return cls.gets_by_supply(limit, offset, order, supply, closed)
        elif not supply and demand:
            return cls.gets_by_demand(limit, offset, order, demand, closed)
        if order is OrderType.ascending:
            if closed:
                return CoursePost.query.limit(limit).offset(offset).all()
            elif not closed:
                return CoursePost.query.filter_by(
                    status_=PostStatus.normal.value).limit(limit).offset(
                        offset).all()
        if closed:
            return CoursePost.query.order_by(db.desc(
                cls.id)).limit(limit).offset(offset).all()
        elif not closed:
            return CoursePost.query.filter_by(
                status_=PostStatus.normal.value).order_by(db.desc(
                    cls.id)).limit(limit).offset(offset).all()

    @classmethod
    def gets_by_supply_and_demand(cls, limit, offset, order, supply, demand,
                                  closed):
        desc = 'desc' if order is OrderType.descending else ''
        sql = ('select course_supply.post_id as post_id '
               'from course_supply join course_demand'
               'where course_supply.post_id=course_demand.post_id '
               'and course_supply.course_id={supply} '
               'and course_demand.course_id={demand} '
               'order by post_id {desc} limit {offset}, {limit}'.format(
                   supply=supply,
                   demand=demand,
                   desc=desc,
                   offset=offset,
                   limit=limit))
        if not closed:
            sql = ('select course_supply.post_id as post_id '
                   'from course_supply join course_demand join course_post '
                   'where course_supply.post_id=course_demand.post_id '
                   'and course_supply.course_id={supply} '
                   'and course_demand.course_id={demand} '
                   'and course_post.status_={status} '
                   'order by post_id {desc} limit {offset}, {limit}'.format(
                       supply=supply,
                       demand=demand,
                       status=PostStatus.normal.value,
                       desc=desc,
                       offset=offset,
                       limit=limit))
        rs = db.engine.execute(sql)
        return [CoursePost.get(post_id) for (post_id, ) in rs]

    @classmethod
    def gets_by_supply(cls, limit, offset, order, supply, closed):
        desc = 'desc' if order is OrderType.descending else ''
        sql = ('select course_supply.post_id as post_id '
               'from course_supply join course_demand '
               'where course_supply.post_id=course_demand.post_id '
               'and course_supply.course_id={supply} '
               'order by post_id {desc} limit {offset}, {limit}'.format(
                   supply=supply, desc=desc, offset=offset, limit=limit))
        if not closed:
            sql = ('select course_supply.post_id as post_id '
                   'from course_supply join course_demand join course_post '
                   'where course_supply.post_id=course_demand.post_id '
                   'and course_supply.course_id={supply} '
                   'and course_post.status_={status} '
                   'order by post_id {desc} limit {offset}, {limit}'.format(
                       supply=supply,
                       status=PostStatus.normal.value,
                       desc=desc,
                       offset=offset,
                       limit=limit))
        rs = db.engine.execute(sql)
        return [CoursePost.get(post_id) for (post_id, ) in rs]

    @classmethod
    def gets_by_demand(cls, limit, offset, order, demand, closed):
        desc = 'desc' if order is OrderType.descending else ''
        sql = ('select course_supply.post_id as post_id '
               'from course_supply join course_demand '
               'where course_supply.post_id=course_demand.post_id '
               'and course_demand.course_id={demand} '
               'order by post_id {desc} limit {offset}, {limit}'.format(
                   demand=demand, desc=desc, offset=offset, limit=limit))
        if not closed:
            sql = ('select course_supply.post_id as post_id '
                   'from course_supply join course_demand join course_post '
                   'where course_supply.post_id=course_demand.post_id '
                   'and course_demand.course_id={demand} '
                   'and course_post.status_={status} '
                   'order by post_id {desc} limit {offset}, {limit}'.format(
                       demand=demand,
                       status=PostStatus.normal.value,
                       desc=desc,
                       offset=offset,
                       limit=limit))
        rs = db.engine.execute(sql)
        return [CoursePost.get(post_id) for (post_id, ) in rs]

    @classmethod
    def gets_by_student(cls,
                        student_id,
                        limit=10,
                        offset=0,
                        order=OrderType.descending):
        if order is OrderType.descending:
            return CoursePost.query.order_by(db.desc(cls.id)).filter_by(
                student_id=student_id).limit(limit).offset(offset).all()
        return CoursePost.query.filter_by(
            student_id=student_id).limit(limit).offset(offset).all()

    @classmethod
    def add(cls, student_id, supply_course_id, demand_course_id, switch,
            mobile, wechat, message):
        cls.validate_new_post(student_id, supply_course_id, demand_course_id)
        cls.validate_supply_and_demand(supply_course_id, demand_course_id)
        post = CoursePost(student_id, switch, mobile, wechat, message)
        db.session.add(post)
        db.session.commit()
        CourseSupply.add(post.id, supply_course_id)
        CourseDemand.add(post.id, demand_course_id)
        return post

    @classmethod
    def existed(cls, student_id, supply, demand):
        sql = ('select course_supply.post_id as post_id '
               'from course_supply join course_demand '
               'where course_supply.post_id=course_demand.post_id '
               'and course_supply.course_id={supply} '
               'and course_demand.course_id={demand}').format(supply=supply,
                                                              demand=demand)
        rs = db.engine.execute(sql)
        post_ids = [str(post_id) for (post_id, ) in rs]
        if post_ids:
            sql = ('select id from course_post '
                   'where id in {post_ids} '
                   'and status_={status} '
                   'and student_id={student_id}').format(
                       post_ids='(%s)' % ','.join(post_ids),
                       student_id=student_id,
                       status=PostStatus.normal.value)
            rs = db.engine.execute(sql)
            return bool(rs)
        return False

    @classmethod
    def validate_new_post(cls, student_id, supply_course_id, demand_course_id):
        cls.validate_supply_and_demand(supply_course_id, demand_course_id)
        if cls.existed(student_id, supply_course_id, demand_course_id):
            raise DuplicatedPostError()

    @staticmethod
    def validate_supply_and_demand(supply_course_id, demand_course_id):
        if supply_course_id == demand_course_id:
            raise SupplySameAsDemandError()

        if not supply_course_id and not demand_course_id:
            raise InvalidPostError()

    @classmethod
    def defuzzy(cls, fuzzy_id):
        return decrypt(fuzzy_id)

    @property
    def fuzzy_id(self):
        return encrypt(str(self.id))

    @property
    def student(self):
        return Student.get(self.student_id)

    @property
    def supply(self):
        cache_key = self._course_post_supply_by_post_id_cache_key % self.id
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        course_supply = CourseSupply.get_by_post(self.id)
        if course_supply and course_supply.course_id:
            mc.set(cache_key, pickle.dumps(course_supply).hex())
            mc.expire(cache_key, ONE_DAY)
            return course_supply
        return None

    @property
    def demand(self):
        cache_key = self._course_post_demand_by_post_id_cache_key % self.id
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        course_demand = CourseDemand.get_by_post(self.id)
        if course_demand and course_demand.course_id:
            mc.set(cache_key, pickle.dumps(course_demand).hex())
            mc.expire(cache_key, ONE_DAY)
            return course_demand
        return None

    @property
    def status(self):
        return PostStatus(self.status_)

    def _get_pv(self):
        key = self._post_pv_by_id_cache_key % self.id
        cached = int(rd.get(key)) if rd.get(key) else None
        if cached is not None:
            return cached
        rd.set(key, self.pv_)
        return self.pv_

    def _set_pv(self, pv_):
        rd.set(self._post_pv_by_id_cache_key % self.id, pv_)
        if pv_ % 7 == 0:
            if self.pv_ > pv_:
                pv_ += 7
            self.pv_ = pv_
            db.session.add(self)
            db.session.commit()
            self.clear_cache()

    pv = property(_get_pv, _set_pv)

    def update_self(self, data):
        if not data:
            return True
        if not self.editable:
            raise CannotEditPostError()
        message = data.get('message')
        switch = data.get('switch')
        wechat = data.get('wechat')
        if message:
            self.message = message
        if switch is not None:
            self.switch = switch
        if wechat is not None:
            self.wechat = wechat
        self.update_time = datetime.now()
        self.editable -= 1
        db.session.add(self)
        db.session.commit()
        self.clear_cache()
        return True

    def update_status(self, status):
        if status is PostStatus.normal:
            self.to_normal()
        if status is PostStatus.succeed:
            self.to_succeed()
        if status is PostStatus.abandoned:
            self.to_abandoned()

    def to_normal(self):
        if self.status is not PostStatus.normal:
            self.status_ = PostStatus.normal.value
            self.update_time = datetime.now()
            db.session.add(self)
            db.session.commit()
            self.clear_cache()

    def to_succeed(self):
        if self.status is not PostStatus.succeed:
            self.status_ = PostStatus.succeed.value
            self.update_time = datetime.now()
            db.session.add(self)
            db.session.commit()
            self.clear_cache()
            self.clear_related_view_records()

    def to_abandoned(self):
        if self.status is not PostStatus.abandoned:
            self.status_ = PostStatus.abandoned.value
            self.update_time = datetime.now()
            db.session.add(self)
            db.session.commit()
            self.clear_cache()
            self.clear_related_view_records()

    def update_supply(self, supply_course_id):
        supply = self.supply
        demand = self.demand
        self.validate_supply_and_demand(supply_course_id, demand.course_id)
        supply.course_id = supply_course_id
        self.update_time = datetime.now()
        db.session.add(supply)
        db.session.add(self)
        db.session.commit()
        self.clear_cache()

    def update_demand(self, demand_course_id):
        supply = self.supply
        demand = self.demand
        self.validate_supply_and_demand(supply.course_id, demand_course_id)
        demand.course_id = demand_course_id
        self.update_time = datetime.now()
        db.session.add(demand)
        db.session.add(self)
        db.session.commit()
        self.clear_cache()

    def clear_related_view_records(self):
        ViewRecord.delete_records_by_post(self.id, PostType.course_post)

    def delete(self):
        self.clear_cache()
        supply = CourseSupply.get_by_post(self.id)
        demand = CourseDemand.get_by_post(self.id)
        supply.delete()
        demand.delete()
        db.session.delete(self)
        db.session.commit()

    def clear_cache(self):
        mc.delete(self._course_post_by_id_cache_key % self.id)
        mc.delete(self._course_post_supply_by_post_id_cache_key % self.id)
        mc.delete(self._course_post_demand_by_post_id_cache_key % self.id)
Example #6
0
class WechatSession(db.Model):
    __tablename__ = 'wechat_session'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    open_id = db.Column(db.String(80), index=True, nullable=False)
    session_key = db.Column(db.String(80), nullable=False)
    third_session_key = db.Column(db.String(80), unique=True, index=True, nullable=False)
    create_time = db.Column(db.DateTime(), default=datetime.now())
    expire_time = db.Column(db.DateTime())

    _cache_key_prefix = 'wechat_session:'
    _wechat_session_by_id_cache_key = _cache_key_prefix + 'id:%s'
    _id_by_open_id_cache_key = _cache_key_prefix + 'open_id:%s'

    def __init__(self, open_id, session_key, third_session_key, expire_time):
        self.open_id = open_id
        self.session_key = session_key
        self.third_session_key = third_session_key
        self.expire_time = expire_time

    @classmethod
    def add(cls, open_id, session_key, expires_in=ONE_DAY):
        third_session_key = uuid.uuid4().hex
        instance = cls.get_by_open_id(open_id)
        if instance:
            instance.update(session_key, third_session_key, expires_in)
            return third_session_key

        expire_time = datetime.now() + timedelta(seconds=expires_in)
        wechat_session = WechatSession(
            open_id, session_key, third_session_key, expire_time)

        db.session.add(wechat_session)
        db.session.commit()
        mc.set(cls._id_by_open_id_cache_key % open_id, wechat_session.id)
        mc.expire(cls._id_by_open_id_cache_key % open_id, HALF_DAY)
        mc.set(cls._wechat_session_by_id_cache_key % wechat_session.id, wechat_session)
        mc.expire(cls._wechat_session_by_id_cache_key % wechat_session.id, HALF_DAY)
        return third_session_key

    @classmethod
    def get(cls, id_):
        cache_key = cls._wechat_session_by_id_cache_key % id_
        if mc.get(cache_key):
            wechat_session = pickle.loads(bytes.fromhex(mc.get(cache_key)))
            mc.expire(cache_key, HALF_DAY)
            return wechat_session
        wechat_session = cls.query.get(id_)
        if wechat_session:
            mc.set(cache_key, pickle.dumps(wechat_session).hex())
            mc.expire(cache_key, HALF_DAY)
        return wechat_session

    @classmethod
    def get_by_third_session_key(cls, third_session_key):
        id_ = mc.get(cls._id_by_open_id_cache_key % third_session_key)

        wechat_session = cls.get(id_) if id_ else cls.query.filter_by(
            third_session_key=third_session_key).first()

        if wechat_session and not wechat_session.expired:
            mc.set(cls._id_by_open_id_cache_key % wechat_session.open_id, wechat_session.id)
            mc.expire(cls._id_by_open_id_cache_key % wechat_session.open_id, HALF_DAY)
            return wechat_session
        return None

    @classmethod
    def get_by_open_id(cls, open_id):
        return cls.query.filter_by(open_id=open_id).first()

    @property
    def expired(self):
        return bool(datetime.now() > self.expire_time)

    @property
    def wechat_user(self):
        from black_market.model.wechat.user import WechatUser
        return WechatUser.get_by_open_id(self.open_id)

    def update(self, session_key, third_session_key, expires_in):
        self.session_key = session_key
        self.third_session_key = third_session_key
        self.expire_time = datetime.now() + timedelta(seconds=expires_in)
        db.session.add(self)
        db.session.commit()
        self.clear_cache()

    def invalidate_third_session_key(self):
        self.third_session_key = uuid.uuid4().hex
        db.session.add(self)
        db.session.commit()
        self.clear_cache()

    def delete(self):
        db.session.delete(self)
        db.session.commit()
        self.clear_cache()

    def clear_cache(self):
        mc.delete(self._id_by_open_id_cache_key % self.open_id)
        mc.delete(self._wechat_session_by_id_cache_key % self.id)
Example #7
0
class WechatUser(db.Model):
    __tablename__ = 'wechat_user'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    open_id = db.Column(db.String(80), unique=True, nullable=False, index=True)
    nickname = db.Column(db.String(80))
    avatar_url = db.Column(db.String(256))
    city = db.Column(db.String(80))
    country = db.Column(db.String(80))
    gender = db.Column(db.SmallInteger)
    language = db.Column(db.String(80))
    province = db.Column(db.String(80))
    create_time = db.Column(db.DateTime(), default=datetime.now())
    update_time = db.Column(db.DateTime(),
                            default=datetime.now(),
                            onupdate=datetime.now())

    _cache_key_prefix = 'wechat_user:'******'open_id:%s'

    def __init__(self, open_id, nickname, avatar_url, city, country, gender,
                 language, province, update_time):
        self.open_id = open_id
        self.nickname = nickname
        self.avatar_url = avatar_url
        self.city = city
        self.country = country
        self.gender = gender
        self.language = language
        self.province = province
        self.update_time = update_time

    @classmethod
    def add(cls, open_id, nickname, avatar_url, city, country, gender,
            language, province):
        instance = cls.get_by_open_id(open_id)
        if instance:
            instance.update(nickname, avatar_url, city, country, gender,
                            language, province)
            return instance.id

        update_time = datetime.now()
        wechat_user = WechatUser(open_id, nickname, avatar_url, city, country,
                                 gender, language, province, update_time)

        db.session.add(wechat_user)
        db.session.commit()
        return wechat_user.id

    @classmethod
    def get(cls, id_):
        return cls.query.get(id_)

    @classmethod
    def get_by_open_id(cls, open_id):
        cache_key = cls._wechat_user_by_open_id_cache_key % open_id
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        wechat_user = cls.query.filter_by(open_id=open_id).first()
        if wechat_user:
            mc.set(cache_key, pickle.dumps(wechat_user).hex())
            mc.expire(cache_key, ONE_DAY)
        return wechat_user

    def update(self, nickname, avatar_url, city, country, gender, language,
               province):
        self.nickname = nickname
        self.avatar_url = avatar_url
        self.city = city
        self.country = country
        self.gender = gender
        self.language = language
        self.province = province
        self.update_time = datetime.now()
        db.session.add(self)
        db.session.commit()
        self.clear_cache()

    def delete(self):
        self.clear_cache()
        db.session.delete(self)
        db.session.commit()

    def clear_cache(self):
        mc.delete(self._wechat_user_by_open_id_cache_key % self.open_id)
Example #8
0
class Course(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(80), unique=True)
    teacher = db.Column(db.String(80))
    credit = db.Column(db.Integer)

    _cache_key_prefix = 'course:'
    _course_cache_key = _cache_key_prefix + 'id:%s'
    _all_course_cache_key = _cache_key_prefix + 'all'

    def __init__(self, name, teacher, credit, type_):
        self.name = name
        self.teacher = teacher
        self.credit = credit
        self.type_ = type_

    def dump(self):
        return dict(id=self.id,
                    name=self.name,
                    teacher=self.teacher,
                    credit=self.credit,
                    schedules=[s.dump() for s in self.schedules])

    @classmethod
    def add(cls, name, teacher, credit, type_, schedules):
        course = Course(name, teacher, credit, type_.value)
        db.session.add(course)
        db.session.commit()
        for s in schedules:
            schedule = CourseSchedule(course.id, s['day'], s['start'],
                                      s['end'])
            db.session.add(schedule)
        db.session.commit()

    @classmethod
    def get(cls, id_):
        cache_key = cls._course_cache_key % id_
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        else:
            course = Course.query.get(id_)
            if course:
                mc.set(cache_key, pickle.dumps(course).hex())
                mc.expire(cache_key, ONE_DAY)
            return course

    @classmethod
    def gets(cls, limit=5, offset=0):
        return Course.query.limit(limit).offset(offset).all()

    @classmethod
    def get_all(cls):
        cache_key = cls._all_course_cache_key
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        else:
            courses = Course.query.all()
            mc.set(cache_key, pickle.dumps(courses).hex())
            mc.expire(cache_key, ONE_DAY)
            return courses

    @classmethod
    def get_by_name(cls, name):
        return Course.query.filter(Course.name.ilike('%' + name + '%'))

    @property
    def type(self):
        return CourseType(self.type_)

    @property
    def schedules(self):
        return CourseSchedule.get_by_course(self.id)

    def clear_cache(self):
        mc.delete(self._course_cache_key % self.id)
Example #9
0
class GoodsPost(db.Model):
    __tablename__ = 'goods_post'

    _cache_key_prefix = 'goods:post:'
    _goods_post_by_id_cache_key = _cache_key_prefix + 'id:%s'
    _post_pv_by_id_cache_key = _cache_key_prefix + 'pv:id:%s'

    MAX_EDIT_TIMES = 5

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    student_id = db.Column(db.Integer, db.ForeignKey('student.id'))
    status_ = db.Column(db.SmallInteger)
    switch = db.Column(db.SmallInteger)
    mobile = db.Column(db.String(80))
    wechat = db.Column(db.String(80))
    message = db.Column(db.String(256))
    imgs = db.Column(db.String(80))
    pv_ = db.Column(db.Integer, default=0)
    editable = db.Column(db.SmallInteger, default=MAX_EDIT_TIMES)
    create_time = db.Column(db.DateTime, default=datetime.now)
    update_time = db.Column(db.DateTime, default=datetime.now)

    def __init__(self,
                 student_id,
                 switch,
                 mobile,
                 wechat,
                 message,
                 imgs,
                 status=PostStatus.normal):
        self.student_id = student_id
        self.switch = switch.value
        self.mobile = mobile
        self.wechat = wechat
        self.message = message
        self.imgs = imgs
        self.status_ = status.value

    def dump(self):
        return dict(id=self.id,
                    student=self.student.dump(),
                    switch=self.switch,
                    mobile=self.mobile,
                    wechat=self.wechat,
                    message=self.message,
                    pv=self.pv,
                    status=self.status_,
                    editable=self.editable,
                    create_time=self.create_time,
                    update_time=self.update_time,
                    fuzzy_id=self.fuzzy_id)

    def share_dump(self):
        return dict(id=self.id,
                    student=self.student.share_dump(),
                    message=self.message,
                    pv=self.pv,
                    status=self.status_,
                    create_time=self.create_time,
                    update_time=self.update_time)

    @classmethod
    def get(cls, id_):
        cache_key = cls._goods_post_by_id_cache_key % id_
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        post = GoodsPost.query.get(id_)
        if post:
            mc.set(cache_key, pickle.dumps(post).hex())
            mc.expire(cache_key, ONE_HOUR)
        return post

    @classmethod
    def gets(cls, limit=5, offset=0, order=OrderType.descending):
        if order is OrderType.ascending:
            return GoodsPost.query.limit(limit).offset(offset).all()
        return GoodsPost.query.order_by(db.desc(
            cls.id)).limit(limit).offset(offset).all()

    @classmethod
    def gets_by_student(cls,
                        student_id,
                        limit=10,
                        offset=0,
                        order=OrderType.descending):
        if order is OrderType.descending:
            return GoodsPost.query.order_by(db.desc(cls.id)).filter_by(
                student_id=student_id).limit(limit).offset(offset).all()
        return GoodsPost.query.filter_by(
            student_id=student_id).limit(limit).offset(offset).all()

    @classmethod
    def add(cls, student_id, switch, mobile, wechat, imgs, message):
        post = GoodsPost(student_id, switch, mobile, wechat, imgs, message)
        db.session.add(post)
        db.session.commit()
        return post

    @classmethod
    def defuzzy(cls, fuzzy_id):
        return decrypt(fuzzy_id)

    @property
    def fuzzy_id(self):
        return encrypt(str(self.id))

    @property
    def student(self):
        return Student.get(self.student_id)

    @property
    def status(self):
        return PostStatus(self.status_)

    @property
    def image_ids(self):
        return self.imgs.split(',') if self.imgs else []

    @property
    def images_urls(self):
        # TODO get urls
        return self.image_ids

    def _get_pv(self):
        key = self._post_pv_by_id_cache_key % self.id
        cached = int(rd.get(key)) if rd.get(key) else None
        if cached is not None:
            return cached
        rd.set(key, self.pv_)
        return self.pv_

    def _set_pv(self, pv_):
        rd.set(self._post_pv_by_id_cache_key % self.id, pv_)
        if pv_ % 7 == 0:
            self.pv_ = pv_
            db.session.add(self)
            db.session.commit()
            self.clear_cache()

    pv = property(_get_pv, _set_pv)

    def update_self(self, data):
        if not data:
            return True
        if not self.editable:
            raise CannotEditPostError()
        message = data.get('message')
        switch = data.get('switch')
        wechat = data.get('wechat')
        if message:
            self.message = message
        if switch is not None:
            self.switch = switch
        if wechat is not None:
            self.wechat = wechat
        self.update_time = datetime.now()
        self.editable -= 1
        db.session.add(self)
        db.session.commit()
        self.clear_cache()
        return True

    def update_status(self, status):
        if status is PostStatus.normal:
            self.to_normal()
        if status is PostStatus.succeed:
            self.to_succeed()
        if status is PostStatus.abandoned:
            self.to_abandoned()

    def to_normal(self):
        if self.status is not PostStatus.normal:
            self.status_ = PostStatus.normal.value
            self.update_time = datetime.now()
            db.session.add(self)
            db.session.commit()
            self.clear_cache()

    def to_succeed(self):
        if self.status is not PostStatus.succeed:
            self.status_ = PostStatus.succeed.value
            self.update_time = datetime.now()
            db.session.add(self)
            db.session.commit()
            self.clear_cache()
            self.clear_related_view_records()

    def to_abandoned(self):
        if self.status is not PostStatus.abandoned:
            self.status_ = PostStatus.abandoned.value
            self.update_time = datetime.now()
            db.session.add(self)
            db.session.commit()
            self.clear_cache()
            self.clear_related_view_records()

    def clear_related_view_records(self):
        ViewRecord.delete_records_by_post(self.id, PostType.goods_post)

    def clear_cache(self):
        mc.delete(self._goods_post_by_id_cache_key % self.id)
Example #10
0
class Student(db.Model):
    __tablename__ = 'student'

    id = db.Column(db.Integer, db.ForeignKey('wechat_user.id'))
    name = db.Column(db.String(80))
    mobile = db.Column(db.String(80), primary_key=True, index=True)
    open_id = db.Column(db.String(80), db.ForeignKey('wechat_user.open_id'))
    type = db.Column(db.SmallInteger)
    grade = db.Column(db.String(10))
    status = db.Column(db.SmallInteger, default=AccountStatus.need_verify.value)
    create_time = db.Column(db.DateTime, default=datetime.now)
    update_time = db.Column(db.DateTime, default=datetime.now)
    MAX_VIEWCOUNT = 12

    _cache_key_prefix = 'student:'
    _student_cache_key = _cache_key_prefix + 'id:%s'
    _avatar_cache_key = _cache_key_prefix + 'avatar:%s'
    _remaining_viewcount_by_student_cache_key = _cache_key_prefix + 'remaining:viewcount:id:%s'

    def __init__(self, id_, mobile, open_id, type_, grade, status):
        self.id = id_
        self.name = ''
        self.mobile = mobile
        self.open_id = open_id
        self.type = type_.value
        self.grade = grade
        self.status = status.value

    def dump(self):
        return dict(
            id=self.id, username=self.username, mobile=self.mobile,
            grade=self.grade, type=self.type, status=self.status,
            avatar_url=self.avatar_url,
            create_time=self.create_time, update_time=self.update_time)

    def share_dump(self):
        return dict(
            id=self.id, username=self.username, grade=self.grade,
            type=self.type, avatar_url=self.avatar_url, create_time=self.create_time)

    @classmethod
    def add(cls, id_, mobile, open_id, type_, grade, status=AccountStatus.need_verify):
        wechat_user = WechatUser.get_by_open_id(open_id)
        if wechat_user is None:
            raise WechatUserNotFoundError()
        if Student.existed(mobile):
            raise MobileAlreadyExistedError()
        student = Student(id_, mobile, open_id, type_, grade, status)
        db.session.add(student)
        db.session.commit()
        return student.id

    @classmethod
    def get(cls, id_):
        cache_key = cls._student_cache_key % id_
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        student = cls.query.filter_by(id=id_).first()
        if student:
            mc.set(cache_key, pickle.dumps(student).hex())
            mc.expire(cache_key, ONE_HOUR)
        return student

    @classmethod
    def existed(cls, mobile):
        return bool(cls.query.filter_by(mobile=mobile).first())

    @property
    def wechat_user(self):
        return WechatUser.get_by_open_id(self.open_id)

    @property
    def avatar_url(self):
        return self.wechat_user.avatar_url

    @property
    def avatar(self):
        cache_key = self._avatar_cache_key % self.id
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        return self.cache_avatar(self.id, self.avatar_url)

    @staticmethod
    @app.task
    def cache_avatar(student_id, avatar_url):
        cache_key = Student._avatar_cache_key % student_id
        response = requests.get(avatar_url)
        image = response.content
        mc.set(cache_key, pickle.dumps(image).hex())
        mc.expire(cache_key, ONE_WEEK)
        return image

    @property
    def username(self):
        return self.name if self.name else self.wechat_user.nickname

    @property
    def posts(self, limit=10, offset=0):
        from black_market.model.post.course import CoursePost
        return CoursePost.gets_by_student(self.id, limit, offset)

    def update(self, type_, grade, name=None):
        if name:
            self.name = name.strip()
        self.type = type_.value
        self.grade = grade
        self.update_time = datetime.now()
        db.session.add(self)
        db.session.commit()
        self.clear_cache()
        return Student.get(self.id)

    def change_mobile(self, mobile):
        validator.validate_phone(mobile)
        if Student.existed(mobile):
            raise MobileAlreadyExistedError
        self.mobile = mobile
        db.session.add(self)
        db.session.commit()
        self.clear_cache()

    @property
    def account_status(self):
        return AccountStatus(self.status)

    @property
    def remaining_viewcount(self):
        cache_key = self._remaining_viewcount_by_student_cache_key % self.id
        if mc.get(cache_key):
            viewcount = int(mc.get(cache_key))
            return viewcount
        else:
            mc.set(cache_key, self.MAX_VIEWCOUNT)
            mc.expire(cache_key, ONE_DAY)
            return self.MAX_VIEWCOUNT

    def decr_viewcount(self):
        cache_key = self._remaining_viewcount_by_student_cache_key % self.id
        if mc.get(cache_key):
            viewcount = int(mc.get(cache_key))
            if viewcount <= 0:
                raise CannotViewPostContactError()
            mc.decr(cache_key)

    def need_verify(self):
        return self.account_status is AccountStatus.need_verify

    def is_normal(self):
        return self.account_status is AccountStatus.normal

    def to_normal(self):
        self.status = AccountStatus.normal.value
        db.session.add(self)
        db.session.commit()
        self.clear_cache()

    def delete(self):
        self.clear_cache()
        db.session.delete(self)
        db.session.commit()

    def clear_cache(self):
        mc.delete(self._student_cache_key % self.id)
        mc.delete(self._avatar_cache_key % self.id)
        mc.delete(self._remaining_viewcount_by_student_cache_key % self.id)
Example #11
0
class CourseDemand(db.Model):
    __tablename__ = 'course_demand'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    post_id = db.Column(db.Integer, db.ForeignKey('course_post.id'))
    course_id = db.Column(db.Integer)

    _cache_key_prefix = 'course:post:demand:'
    _course_post_demand_by_id_cache_key = _cache_key_prefix + 'id:%s'

    def __init__(self, post_id, course_id):
        self.post_id = post_id
        self.course_id = course_id

    def dump(self):
        return dict(id=self.id,
                    post_id=self.post_id,
                    course=self.course.dump())

    def share_dump(self):
        return dict(course=self.course.dump())

    @classmethod
    def add(cls, post_id, course_id):
        demand = CourseDemand(post_id, course_id)
        db.session.add(demand)
        db.session.commit()
        return demand.id

    @classmethod
    def get(cls, id_):
        cache_key = cls._course_post_demand_by_id_cache_key % id_
        if mc.get(cache_key):
            return pickle.loads(bytes.fromhex(mc.get(cache_key)))
        course_demand = CourseDemand.query.get(id_)
        if course_demand:
            mc.set(cache_key, pickle.dumps(course_demand).hex())
            mc.expire(cache_key, ONE_DAY)
        return course_demand

    @classmethod
    def get_by_post(cls, id_):
        return CourseDemand.query.filter_by(post_id=id_).first()

    @classmethod
    def get_by_course(cls, id_):
        return CourseDemand.query.filter_by(course_id=id_).all()

    @property
    def post(self):
        from black_market.model.post.course import CoursePost
        return CoursePost.get(self.post_id)

    @property
    def course(self):
        return Course.get(self.course_id)

    @property
    def course_name(self):
        return self.course.name

    @property
    def course_teacher(self):
        return self.course.teacher

    def clear_cache(self):
        mc.delete(self._course_post_demand_by_id_cache_key % self.id)

    def delete(self):
        self.clear_cache()
        db.session.delete(self)
        db.session.commit()