def dateobj_to_int(dateobj):
    return int(dateobj.strftime(date_fmtstr)) def today():
    return date.today() class RawSQL(object):
    def __init__(self, sql):
    def sql(self):
def _parse_cond(conds):
def __parse_cond(cond):
def ___parse_cond(cond):
def expand_cond(cond):
def _parse_comp_cond(cond, forcelimit=None):
def _strify(st):
def _encode(obj):
def _encode_name(identifier):
def _parse_extras(kwargs):
def _mk_multi_return(row, cols, coldict):
def _execute(sql, write=False, ret='fetch'):
def _done(commit=True):
def fetch_info(info):
def fetch_onerow(table, coldict, conds, **kwargs):
def fetch_oneentry(table, col, conds, **kwargs):
def fetch_onecol(table, col, conds, **kwargs):
def fetch_multirow(table, coldict, conds, **kwargs):
def insert_row(table, insert):
def insert_or_update_row(table, insert, update):
def update_row(table, update, conds):
def delete_rows(table, conds): def _done(commit=True):
    def __call__(cls, *args, **kwargs):
    def __new__(cls, key, timeout):
    def __init__(self):
    def load(key):
    def unserialize(data):
    def save(self):
    def serialize(obj):
    def modified(self):
    def detach(self):  # in TARS's voice
    def __init__(self, value=''):
    def set(self, value):
    def get(self):
    def __nonzero__(self):
    def serialize(self, obj):
class _RedisMetaclass(ABCMeta):
class RedisStuff(object):
class RedisDict(RedisStuff, dict):
class RedisList(RedisStuff, list):
class ImmutableMixin(object):
class RedisCache(RedisStuff, ImmutableMixin): class ClubType(Enum):
    """Enum regarding the type of a club."""
    ACADEMICS = 1
    SPORTS = 2
    ARTS = 3
    SERVICES = 4
    ENTERTAINMENT = 5
    OTHERS = 6
    SCHOOL_TEAMS = 7

    @property
    def format_name(self):
        """Formats the value of the enum for display."""
        return ['', 'Academic', 'Sport', 'Art', 'Service',
                'Entertainment', 'Other', 'School Team'][self.value] class UserType(Enum):
    """Enum regarding the type of a user."""
    STUDENT = 1
    TEACHER = 2
    ADMIN = 3
    CLUB_ADMIN = 4
    CLASSROOM_ADMIN = 5
    DIRECTOR = 6

    @property
    def format_name(self):
        """Formats the value of the enum for display."""
        return ['', 'Student', 'Teacher', 'Admin',
                'Club Administrator',
                'Classroom Reservation Administrator',
                'Director'][self.value] class ClubJoinMode(Enum):
    """Enum regarding the join mode of a club."""
    FREE_JOIN = 1
    BY_INVITATION = 2

    @property
    def format_name(self):
        """Formats the value of the enum for display."""
        return ['', 'Free to Join', 'Invitation'][self.value] class BaseObject(object):
    __metaclass__ = _BaseMetaclass

    @property
    def id(self):
        """ID of the object."""
        if not self.is_real:
            raise NotImplementedError
        return self._id

    @property
    def is_real(self):
        """
        Whether the object is temporary object (False) or permanent (True).
        """
        return self._id > 0

    @classmethod
    def new(cls):
        """Returns a new temporary object for creation."""
        return cls(0)

    def __eq__(self, other):
        if not isinstance(other, BaseObject):
            return NotImplemented
        try:
            return self.id == other.id
        except NotImplementedError:
            return NotImplemented

    def __ne__(self, other):
        if not isinstance(other, BaseObject):
            return NotImplemented
        try:
            return self.id != other.id
        except NotImplementedError:
            return NotImplemented

    def __hash__(self):
        return hash(self.id) class Property(object):
    """
    Represent a value in the specified column of the current table.

    Selection is done by the id of the owner object match the value in the
    identifier column in the current table.

    :param basestring dbname: column name in relational database
    :param ie: import/export method
    :type ie: a single value of, or a tuple with two values of,
        None, NotImplemented, basestring, a serializer module, BaseObject,
        Enum, or callable
    :param bool search: whether the column should be searchable
    :param bool rediscached: whether the column should be cached on Redis
    :param bool search_require_true: if True, the object will only be
        searchable if this column evaluates to True
    :param error_default: if not Ellipsis, when the importing fails,
        import this value as a fallback; else raise the error
    """
    def __init__(prop, dbname, ie=None, search=False, rediscached=False,
                 search_require_true=False, error_default=Ellipsis):
        super(Property, prop).__init__()
        prop.dbname = dbname
        prop.imp, prop.exp = _get_ie(ie)
        prop.search = _get_search(search, ie)
        prop.rediscached = rediscached
        prop.search_require_true = search_require_true
        prop.error_default = error_default

    def __get__(prop, self, owner=None):
        if self is None:
            return prop
        if prop.name not in self._cache:
            if prop.rediscached:
                cache = redis.RedisCache(
                    prop._get_redis_key(self), REDIS_CACHE_TIME)
                if not cache.new:
                    data = cache.get()
                else:
                    data = self._data[prop.name]
                    cache.set(data)
            else:
                data = self._data[prop.name]

            try:
                value = prop.imp(data)
            except Exception:
                if prop.error_default is not Ellipsis:
                    value = prop.imp(prop.error_default)
                else:
                    raise
            self._cache[prop.name] = value
        return self._cache[prop.name]

    def __set__(prop, self, value):
        # Proxies can't pass isinstance check
        try:
            value = value._get_current_object()
        except AttributeError:
            pass

        # Make strings simple
        try:
            value = value.strip()
        except AttributeError:
            pass

        self._cache[prop.name] = value

        dbvalue = prop.exp(value)
        if self.is_real:
            if self._dbdata is None:
                self._data
            if dbvalue == self._dbdata[prop.name]:
                return

            self._dbdata[prop.name] = dbvalue

            database.update_row(
                self.table,
                {prop.dbname: dbvalue},
                {self.identifier: self.id}
            )

            # multiple search_require_true support is done in _escreate
            if prop.search_require_true:
                if value:
                    self._escreate()
                else:
                    elasticsearch.delete(self.table, self.id)

            if prop.search and self._es_requirement_good():
                elasticsearch.update(
                    self.table,
                    self.id,
                    {prop.name: prop.search(value)}
                )

            if prop.rediscached:
                cache = redis.RedisCache(
                    prop._get_redis_key(self), REDIS_CACHE_TIME)
                cache.set(dbvalue)
        else:
            self._dbdata = self._dbdata or {}
            self._dbdata[prop.name] = dbvalue

    def __delete__(prop, self):
        if prop.name in self._cache:
            del self._cache[prop.name]

        if self.is_real:
            self._dbdata = None
        else:
            if prop.name in self._dbdata:
                del self._dbdata[prop.name]

    def _get_redis_key(prop, self):
        return ':'.join(['cache', self.table, str(self.id), prop.name]) class ListProperty(object):
    """
    Represent a list of value in the specified column in another table.

    Selection is done by the id of the owner object match the value in the
    column specified by ``this`` in the specified table, and extraction is
    done on the column specified by ``that``.

    :param basestring table: table name
    :param basestring this: column name to match the id with
    :param basestring that: column name to return values with
    :param ie: import/export method, onlt the export part matters
    :type ie: a single value of, or a tuple with two values of,
        None, NotImplemented, basestring, a serializer module, BaseObject,
        Enum, or callable
    """
    def __init__(prop, table, this, that, ie=None):
        super(ListProperty, prop).__init__()
        prop.table = table
        prop.this = this
        prop.that = that
        prop.imp, prop.exp = _get_ie(ie)

    def __get__(prop, self, owner=None):
        if self is None:
            return prop
        if prop.name not in self._cache:
            tempdata = database.fetch_onecol(
                prop.table,
                prop.that,
                {prop.this: self.id}
            )
            self._cache[prop.name] = \
                [prop.imp(member) for member in tempdata]

        return self._cache[prop.name]

    def __set__(prop, self, value):
        raise AttributeError("ListProperty is not writable.")

    def __delete__(prop, self):
        if prop.name in self._cache:
            del self._cache[prop.name] def paged_db_read(func):
    """Decorator function to have a database read with paging abilities."""
    def get_pager(limit):
        tempstorage = type(b'tempstorage', (object,), {})

        def pager_fetch(*args, **kwargs):
            f, table, cols, conds = args
            conds = database.expand_cond(conds)
            if limit:
                conds['limit'] = limit

            ret = f(table, cols, conds, _calc_found=True, **kwargs)

            tempstorage.count = database.fetch_info(
                database.RawSQL('FOUND_ROWS()'))

            return ret

        def pager_return(data):
            if limit:
                return tempstorage.count, data

            return data

        return pager_fetch, pager_return

    @wraps(func)
    def decorated_function(*args, **kwargs):
        return func(pager=get_pager(kwargs.pop('limit', None)),
                    *args, **kwargs)

    return decorated_function
from oclubs.utils.dates import dateobj_to_int, today
from oclubs.access import database, redis
from oclubs.enums import ClubType, UserType, ClubJoinMode
from oclubs.objs.base import BaseObject, Property, ListProperty, paged_db_read
        from oclubs.objs import Activity
        from oclubs.objs import Activity, Upload
        from oclubs.exceptions import AlreadyExists
                           random_order=bool(amount), limit=amount)
        if amount:
            return ret[1]
        else:
            return ret

    @classmethod
    def set_excellentclubs(cls, newval):
        newval = [item.id for item in newval]
        lst = cls._excellentclubs()
        lst[:] = newval

    # @deprecated
    @classmethod
    def randomclubs(cls, amount, *args, **kwargs):
        ret = cls.allclubs(random_order=True, limit=amount,
                           *args, **kwargs)
        try:
            return ret[1]
        except IndexError:
            return ret

    @classmethod
    @paged_db_read
    def allclubs(cls, club_types=None, excellent_only=False, grade_limit=(),
                 active_only=True, additional_conds=(), random_order=False,
                 pager=None):
        conds = {}
        if additional_conds:
            conds.update(additional_conds)

        conds['where'] = conds.get('where', [])

        if club_types:
            types = [club_type.value for club_type in club_types]
            conds['where'].append(('in', 'club_type', types))
        if excellent_only:
            conds['where'].append(('in', 'club_id', cls._excellentclubs()))
        if grade_limit:
            conds['join'] = conds.get('join', [])
            conds['join'].append(
                ('inner', 'user', [('user_id', 'club_leader')]))
            conds['where'].append(('in', 'user_grade', grade_limit))
        if active_only:
            conds['where'].append(('=', 'club_inactive', False))
            conds['where'].append(('=', 'club_reactivate', True))
        if random_order:
            conds['order'] = conds.get('order', [])
            conds['order'].append((database.RawSQL('RAND()'), True))

        pager_fetch, pager_return = pager

        tempdata = pager_fetch(
            database.fetch_onecol,
            cls.table,
            cls.identifier,
            conds
        )
        return pager_return([cls(item) for item in tempdata])

    def activities(self, times=(), dates=(True, True), **kwargs):

        return Activity.get_activities_conditions(
            times,
            {
                'where': [('=', 'act_club', self.id)],
            },
            dates=dates,
            order_by_time=True,
            **kwargs
        )

    @paged_db_read
    def allactphotos(self, pager=None):

        pager_fetch, pager_return = pager
        tempdata = pager_fetch(
            database.fetch_multirow,
            'act_pic',
            {
                'actpic_upload': 'upload',
                'act_id': 'activity',
            },
            {
                'join': [('inner', 'activity', [('actpic_act', 'act_id')])],
                'where': [('=', 'act_club', self.id)],
            }
        )

        for item in tempdata:
            item['upload'] = Upload(item['upload'])
            item['activity'] = Activity(item['activity'])

        return pager_return(tempdata)

    def add_member(self, user):
        database.insert_row('club_member',
                            {'cm_club': self.id, 'cm_user': user.id})
        user.delete_invitation(self)
        del self.members

    def remove_member(self, user):
        database.delete_rows('club_member',
                             {'cm_club': self.id, 'cm_user': user.id})
        del self.members

    def send_invitation(self, user):

        try:
            database.insert_row(
                'invitation',
                {'invitation_club': self.id,
                 'invitation_user': user.id,
                 'invitation_date': dateobj_to_int(today())}
            )
        except AlreadyExists:
            pass

    @classmethod
    def get_clubs_special_access(cls, user):

        if user.type == UserType.STUDENT: