Example #1
0
File: vadd.py Project: xmcp/ddl-bee
def add_zone():
    """
    INPUT:
         names: list of str
    """
    names = request.json['names']

    z_o, _ = g.user.zones(need_list=False)
    z_o = z_o.get(None, [])
    if len(z_o) + len(names) > current_app.config['LIMIT_ZONES']:
        flash('课程数量超出限制', 'error')
        g.action_success = False
        return

    cur = mysql.get_db().cursor()
    z_onew = z_o
    for name in names:
        if len(name) > current_app.config['MAX_NAME_LENGTH']:
            flash('名称长度超出限制', 'error')
            g.action_success = False
            return

        cur.execute(
            '''
            insert into zones (next_zid, name, uid) values (null, %s, %s)    
        ''', [name, g.user.uid])
        z_onew = z_onew + [cur.lastrowid]

    model.update_linkedlist(z_o, z_onew, 'zones')
Example #2
0
def delete_zone():
    """
    INPUT:
        ids: list of int
    """
    zids=list(set(request.json['ids']))

    z_o,_=g.user.zones(need_list=False)
    z_o=z_o.get(None,[])
    z_onew=z_o[:]

    for zid in zids:
        if zid not in z_o:
            flash('课程不存在或没有权限','error')
            g.action_success=False
            return

    cur=mysql.get_db().cursor()
    for zid in zids:
        cur.execute('''
            delete from zones where zid=%s and uid=%s
        ''',[zid,g.user.uid])
        z_onew.remove(zid)

    model.update_linkedlist(z_o,z_onew,'zones')
Example #3
0
File: vadd.py Project: xmcp/ddl-bee
def proc_extpid(name_list):
    """ Convert share pattern to external pids.
    :param name_list: list of string which may contain share pattern
    :return: yields a list of [name, extpid or None]
    """
    cur = mysql.get_db().cursor()
    for nameline in name_list:
        name, at, share_hash = nameline.rpartition('@@')
        if not at:  # '','','name'
            yield [share_hash, None]
        else:  # 'name','@@','hash'
            if not share_hash:
                raise SisterErrorMsg(nameline + ':分享ID无效')

            cur.execute(
                '''
                select uid,pid from projects where share_hash=%s
            ''', [share_hash])
            res = cur.fetchone()
            if not res:
                raise SisterErrorMsg(nameline + ':未找到分享ID')
            if res[0] == g.user.uid:
                raise SisterErrorMsg(nameline + ':不能分享给自己')

            yield [name, res[1]]
Example #4
0
def update_linkedlist(before, after, table_name):
    """ Modify linked list and write to database.
    :param before: [id...] original linked list status to reduce writes, None for unknown
    :param after: [id...] new linked list status which will write to db
    :param table_name: table name, in ['zones','projects','tasks']
    """
    key_name = {
        'zones': 'zid',
        'projects': 'pid',
        'tasks': 'tid'
    }.get(table_name, None)
    if key_name is None:
        raise ValueError('update_linkedlist table_name is %r' % table_name)

    cur = mysql.get_db().cursor()
    # noinspection SqlWithoutWhere
    sql = 'update %s set next_%s=%%s where %s=%%s and uid=%%s' % (
        table_name, key_name, key_name)

    before = ([] if before is None else before) + [None]
    after = list(after) + [None]

    before_nxts = {before[i]: before[i + 1] for i in range(len(before) - 1)}
    chgs = []
    for i in range(len(after) - 1):
        if after[i + 1] != before_nxts.get(after[i], ...):
            chgs.append([after[i + 1], after[i], g.user.uid])
    if chgs:
        cur.executemany(sql, chgs)
Example #5
0
def delete_task():
    """
    INPUT:
        ids: list of int
        parent_id: int
    """
    tids=list(set(request.json['ids']))
    pid=int(request.json['parent_id'])

    t_o,_=g.user.tasks(pid,need_list=False)
    t_o=t_o.get(pid,[])
    t_onew=t_o[:]

    for tid in tids:
        if tid not in t_o:
            flash('任务不存在或没有权限','error')
            g.action_success=False
            return

    cur=mysql.get_db().cursor()
    for tid in tids:
        cur.execute('''
            delete from tasks where tid=%s and uid=%s
        ''',[tid,g.user.uid])
        t_onew.remove(tid)

    model.update_linkedlist(t_o,t_onew,'tasks')
Example #6
0
    def projects(self, zid=None, *, need_list=True):
        """ Get projects and their ordering.
        :param zid: specify zone id to retrive, None to retrive all
        :param need_list: False if second ret val is unnecessary (will return None)
        :return: {zid: [pid, ...]}, {pid: {'name','id',...}, ...}
        """
        cur = mysql.get_db().cursor()
        sql = '''
            select pid,next_pid,zid,name,extpid,share_hash,share_name from projects where uid=%s
        '''

        if zid is None:
            cur.execute(sql, [self.uid])
        else:
            cur.execute(sql + ' and zid=%s', [self.uid, zid])

        def recover(_group, ll_dict):
            update_linkedlist(None, ll_dict.keys(), 'projects')

        return sort_linkedlist(
            cur.fetchall(),
            recover,
            idx_id=0,
            idx_next=1,
            idx_group=2,
            attrs=[
                'id', None, 'parent_id', 'name', '_extpid', 'share_hash',
                'share_name'
            ] if need_list else None,
        )
Example #7
0
def update_complete():
    """
    INPUT:
        ids: list of int
        completeness: str
    """
    tids = list(request.json['ids'])
    completeness = str(request.json['completeness'])
    assert completeness in ['todo', 'done', 'highlight',
                            'ignored'], 'invalid completeness'

    if len(tids) > current_app.config['LIMIT_TASKS_PER_PROJECT']:
        flash('任务数量超出限制', 'error')
        g.action_success = False
        return

    cur = mysql.get_db().cursor()

    for tid in tids:
        tid = int(tid)

        # set status from placeholder to active, if have permission
        cur.execute(
            '''
            update tasks set status='active' where tid=%s and uid=%s and status='placeholder'
        ''', [tid, g.user.uid])

        # keeping description_idx same
        cur.execute(
            '''
            insert into completes (uid, tid, completeness, update_timestamp) values (%s, %s, %s, unix_timestamp())
            on duplicate key update completeness=%s, update_timestamp=unix_timestamp()
        ''', [g.user.uid, tid, completeness, completeness])
Example #8
0
File: vadd.py Project: xmcp/ddl-bee
def add_project():
    """
    INPUT:
        names: list of str
        parent_id: int
    """
    zid = int(request.json['parent_id'])
    try:
        names = list(proc_extpid(request.json['names']))
    except SisterErrorMsg as e:
        flash(e.msg, 'error')
        g.action_success = False
        return

    if not g.user.check_zone(zid):
        flash('课程不存在或没有权限', 'error')
        g.action_success = False
        return

    p_o, _ = g.user.projects(zid, need_list=False)
    p_o = p_o.get(zid, [])
    if len(p_o) + len(names) > current_app.config['LIMIT_PROJECTS_PER_ZONE']:
        flash('类别数量超出限制', 'error')
        g.action_success = False
        return

    already_extpids = set(g.user.imported_extpids())
    for name, extpid in names:
        if extpid:
            if g.user.ring > current_app.config['MAX_RING_FOR_SHARING']:
                flash('你所在的用户组不能导入共享', 'error')
                g.action_success = False
                return

            if extpid in already_extpids:
                flash(name + ':已经添加过了', 'error')
                g.action_success = False
                return
            else:
                already_extpids.add(extpid)

    cur = mysql.get_db().cursor()
    p_onew = p_o
    for name, extpid in names:
        if len(name) > current_app.config['MAX_NAME_LENGTH']:
            flash('名称长度超出限制', 'error')
            g.action_success = False
            return

        cur.execute(
            '''
            insert into projects (next_pid, name, uid, zid, extpid) values (null, %s, %s, %s, %s)    
        ''', [name, g.user.uid, zid, extpid])
        p_onew = p_onew + [cur.lastrowid]

    model.update_linkedlist(p_o, p_onew, 'projects')
Example #9
0
 def imported_extpids(self):
     """ Return list of extpids that the user has already imported.
     :return: list of extpids
     """
     cur = mysql.get_db().cursor()
     cur.execute(
         '''
         select extpid from projects where uid=%s and extpid is not null
     ''', [self.uid])
     return [x[0] for x in cur.fetchall()]
Example #10
0
def get_user_from_token(token):
    cur = mysql.get_db().cursor()
    cur.execute(
        'select uid,name,ring,splash_index,settings from users where user_token=%s',
        [token])
    res = cur.fetchone()
    if res:
        uid, name, ring, splash_index, settings = res
        return model.User(uid, name, ring, splash_index, settings)
    else:
        return None
Example #11
0
 def check_project(self, pid):
     """ Check whether the user have control of the given pid.
     :param pid: int
     :return: bool
     """
     cur = mysql.get_db().cursor()
     cur.execute(
         '''
         select count(*) from projects where pid=%s and uid=%s
     ''', [pid, self.uid])
     return cur.fetchone()[0] != 0
Example #12
0
File: vadd.py Project: xmcp/ddl-bee
def market_search_project():
    """
    INPUT:
        term: str
    OUTPUT:
        error: str or null
        error_msg: str if error occurs
        result: list of {name,share_hash,pid}
        tasks: list of tasks
    """

    term = str(request.json['term'])

    if not term:
        raise SisterErrorMsg('请输入搜索词')
    if len(term) < 2:
        raise SisterErrorMsg('请输入至少两个字')

    cur = mysql.get_db().cursor()
    if g.user.ring == 0 and term == '.*':
        cur.execute('''
            select name,share_hash,share_name,pid from projects where share_hash is not null order by pid desc limit 0,50
        ''')
    else:
        cur.execute(
            '''
            select name,share_hash,share_name,pid from projects where match(share_name) against (%s in natural language mode) and share_hash is not null limit 0,25
        ''', [term])

    res = [
        {
            'name': name,
            'share_hash': '' +
            share_hash,  # will intentionally die here if share_hash is None
            'share_name': share_name,
            'pid': pid,
        } for name, share_hash, share_name, pid in cur.fetchall()
    ]

    pids = [r['pid'] for r in res]
    if pids:
        tasks_o, tasks_li = g.user.tasks(pids,
                                         bypass_permission=True,
                                         need_completes=False)
    else:
        tasks_o = []
        tasks_li = {}

    return {
        'error': None,
        'result': res,
        'tasks_o': tasks_o,
        'tasks_li': tasks_li,
    }
Example #13
0
def reset_splash_index():
    """
    INPUT: none
    """
    cur = mysql.get_db().cursor()
    cur.execute(
        '''
        update users set splash_index=%s where uid=%s
    ''', [current_app.config['INITIAL_SPLASH_INDEX'], g.user.uid])

    flash('已重置欢迎页面进度', 'success')
    raise SisterProceed()
Example #14
0
File: vadd.py Project: xmcp/ddl-bee
def add_task():
    """
    INPUT:
        names: list of str
        parent_id: int
        task_due_first: int or null
        task_due_delta: int (days) or null
    """
    names = request.json['names']
    pid = int(request.json['parent_id'])
    task_due_first = request.json['task_due_first']
    task_due_delta = int(request.json['task_due_delta'])
    if task_due_first is not None:
        task_due_first = int(task_due_first)

    if not 0 <= task_due_delta < 1000:
        flash('截止日期间隔错误', 'error')
        g.action_success = False
        return

    if not g.user.check_project(pid):
        flash('类别不存在或没有权限', 'error')
        g.action_success = False
        return

    t_o, _ = g.user.tasks(pid, need_list=False)
    t_o = t_o.get(pid, [])
    if len(t_o) + len(names) > current_app.config['LIMIT_TASKS_PER_PROJECT']:
        flash('任务数量超出限制', 'error')
        g.action_success = False
        return

    cur = mysql.get_db().cursor()
    t_onew = t_o
    for idx, name in enumerate(names):
        if len(name) > current_app.config['MAX_NAME_LENGTH']:
            flash('名称长度超出限制', 'error')
            g.action_success = False
            return

        cur.execute(
            '''
            insert into tasks (next_tid, name, uid, pid, status, due) values (null, %s, %s, %s, %s, %s)     
        ''', [
                name, g.user.uid, pid,
                'placeholder' if len(names) > 1 else 'active',
                None if task_due_first is None else
                (task_due_first + idx * 86400 * task_due_delta)
            ])
        t_onew = t_onew + [cur.lastrowid]

    model.update_linkedlist(t_o, t_onew, 'tasks')
Example #15
0
def update_task():
    """
    INPUT:
        id: int
        name: str
        status: str
        desc: str or null
        due: int or null
    """
    tid = int(request.json['id'])
    name = str(request.json['name'])
    status = str(request.json['status'])
    assert status in ['placeholder', 'active'], 'invalid status'
    due = request.json['due']
    desc = request.json['desc']

    if due is not None:
        due = int(due)
    if desc is not None:
        desc = str(desc)

    if len(name) > current_app.config['MAX_NAME_LENGTH']:
        flash('名称长度超出限制', 'error')
        g.action_success = False
        return

    cur = mysql.get_db().cursor()

    cur.execute(
        '''
        select description from tasks where tid=%s and uid=%s
    ''', [tid, g.user.uid])
    res = cur.fetchone()
    if not res:
        flash('任务不存在或没有权限', 'error')
        g.action_success = False
        return

    prefix_len = str_prefix_length(res[0] or '', desc or '')
    if prefix_len < len(
            res[0]
            or ''):  # prefix gets shorter, so need to remove affected desc idx
        cur.execute(
            '''
            update completes set description_idx=%s where tid=%s and description_idx>%s
        ''', [prefix_len, tid, prefix_len])

    cur.execute(
        '''
        update tasks set name=%s, status=%s, due=%s, description=%s where tid=%s and uid=%s
    ''', [name, status, due, desc, tid, g.user.uid])
Example #16
0
def update_project():
    """
    INPUT:
        id: int
        name: str
        shared: bool
        share_name: str
    """
    pid = int(request.json['id'])
    name = str(request.json['name'])
    shared = bool(request.json['shared'])
    share_name = request.json['share_name']
    if share_name is not None:
        share_name = str(share_name)

    if shared and g.user.ring > current_app.config['MAX_RING_FOR_SHARING']:
        flash('你所在的用户组不能创建共享', 'error')
        g.action_success = False
        return

    if len(name) > current_app.config['MAX_NAME_LENGTH']:
        flash('名称长度超出限制', 'error')
        g.action_success = False
        return

    cur = mysql.get_db().cursor()
    cur.execute(
        '''
        update projects set name=%s where pid=%s and uid=%s
    ''', [name, pid, g.user.uid])

    if shared:
        # gen share hash if not already shared
        cur.execute(
            '''
            update projects set share_hash=%s where pid=%s and uid=%s and extpid is null and share_hash is null
        ''', [gen_share_hash(), pid, g.user.uid])
        # update share name
        cur.execute(
            '''
            update projects set share_name=%s where pid=%s and uid=%s and extpid is null
        ''', [share_name, pid, g.user.uid])
    else:
        # clear share hash and share name
        cur.execute(
            '''
            update projects set share_hash=null, share_name=null where pid=%s and uid=%s
        ''', [pid, g.user.uid])
Example #17
0
def update_settings():
    """
    IPNUT:
        settings: json object
    """
    settings_obj = request.json['settings']
    settings_str = json.dumps(settings_obj)

    if len(settings_str) > current_app.config['SETTINGS_MAX_BYTES']:
        flash('设置大小超过限制', 'error')
        g.action_success = False
        return

    cur = mysql.get_db().cursor()
    cur.execute('''
        update users set settings=%s where uid=%s
    ''', [settings_str, g.user.uid])
    raise SisterProceed()
Example #18
0
def update_zone():
    """
    INPUT:
        id: int
        name: str
    """
    zid = int(request.json['id'])
    name = str(request.json['name'])

    if len(name) > current_app.config['MAX_NAME_LENGTH']:
        flash('名称长度超出限制', 'error')
        g.action_success = False
        return

    cur = mysql.get_db().cursor()
    cur.execute(
        '''
        update zones set name=%s where zid=%s and uid=%s
    ''', [name, zid, g.user.uid])
Example #19
0
def update_desc_idx():
    """
    INPUT:
        id: int
        desc_idx: int or null
    """
    tid = int(request.json['id'])
    desc_idx = request.json['desc_idx']
    if desc_idx is not None:
        desc_idx = int(desc_idx)

    cur = mysql.get_db().cursor()

    # keeping completeness same
    cur.execute(
        '''
        insert into completes (uid, tid, completeness, update_timestamp, description_idx) values (%s, %s, 'todo', unix_timestamp(), %s)
        on duplicate key update description_idx=%s
    ''', [g.user.uid, tid, desc_idx, desc_idx])
Example #20
0
 def __init__(self, uid, name, ring, splash_index, settings):
     self.uid = uid
     assert isinstance(self.uid, int), 'uid not integer: %r' % self.uid
     self.name = name
     self.ring = ring
     self.splash_index = splash_index
     try:
         self.settings = json.loads(settings)
     except Exception as e:
         current_app.logger.exception('unserialize settings %r for user %d',
                                      settings, self.uid)
         flash(
             'user settings %r unserialize failed for user %d: %s %s' %
             (settings, self.uid, type(e), e), 'error')
         cur = mysql.get_db().cursor()
         cur.execute(
             '''
             update users set settings='{}' where uid=%s
         ''', [self.uid])
         self.settings = {}
Example #21
0
    def zones(self, *, need_list=True):
        """ Get zones and their ordering.
        :param need_list: False if second ret val is unnecessary (will return None)
        :return: {None: [zid, ...]}, {zid: {'name','id'}, ...}
        """
        cur = mysql.get_db().cursor()
        cur.execute(
            '''
            select zid,next_zid,name from zones where uid=%s
        ''', [self.uid])

        def recover(_group, ll_dict):
            update_linkedlist(None, ll_dict.keys(), 'zones')

        return sort_linkedlist(
            cur.fetchall(),
            recover,
            idx_id=0,
            idx_next=1,
            idx_group=None,
            attrs=['id', None, 'name'] if need_list else None,
        )
Example #22
0
    def tasks(self,
              pid=None,
              *,
              need_list=True,
              bypass_permission=False,
              need_completes=True):
        """ Get tasks and their ordering.
        :param pid: int or list (for multiple). specify project id to retrive, None to retrive all
        :param need_list: False if second ret val is unnecessary (will return None)
        :param bypass_permission: use with `pid`. set to True to remove limit of only fetching tasks of current user
        :param need_completes: False to skip joining with completes (and always return empty completeness)
        :return: {pid: [tid, ...]}, {tid: {'name','id','status','due'...}, ...}
        """
        cur = mysql.get_db().cursor()

        if need_completes:
            sql = '''
                select tasks.tid,next_tid,pid,name,status,due,ifnull(completeness,'todo'),completes.update_timestamp,description,completes.description_idx from tasks
                left join completes on tasks.tid=completes.tid and completes.uid=%s
                where 1 
            '''
            sql_args = [g.user.uid]
        else:
            sql = '''
                select tid,next_tid,pid,name,status,due,'todo',null,description,null from tasks
                where 1 
            '''
            sql_args = []

        if bypass_permission and pid is None:
            raise ValueError('bypassing permission without pid')

        if pid is not None:
            if isinstance(pid, list):
                if not pid:
                    return {}, {}
                sql += ' and tasks.pid in %s'
                sql_args.append(pid)
            else:
                sql += ' and tasks.pid=%s'
                sql_args.append(pid)

        if not bypass_permission:
            sql += ' and tasks.uid=%s'
            sql_args.append(self.uid)

        cur.execute(sql, sql_args)

        def recover(_group, ll_dict):
            update_linkedlist(None, ll_dict.keys(), 'tasks')

        return sort_linkedlist(
            cur.fetchall(),
            recover,
            idx_id=0,
            idx_next=1,
            idx_group=2,
            attrs=[
                'id', None, 'parent_id', 'name', 'status', 'due',
                'completeness', 'complete_timestamp', 'desc', 'desc_idx'
            ] if need_list else None,
        )
Example #23
0
        def decorated(*args, **kwargs):

            # check api version

            client_api_ver = request.args.get('sister_ver', None)
            if client_api_ver not in COMPATIBLE_SISTER_VER:
                return jsonify({
                    'error':
                    'SISTER_VER_MISMATCH',
                    'error_msg':
                    'API 版本与后端 (Sister %s) 不兼容' %
                    (', '.join(COMPATIBLE_SISTER_VER)),
                })

            g.user = None
            g.action_success = True

            # check auth and init g.user

            g.token = request.args.get('user_token', None)
            if g.token:
                g.user = get_user_from_token(g.token)

            if require_ring is not None:
                if g.user is None:
                    return jsonify({
                        'error': 'AUTH_REQUIRED',
                        'error_msg': '需要登录',
                        'backend': _backend_value(),
                    })
                elif g.user.ring > require_ring:
                    return jsonify({
                        'error': 'SISTER_ERROR',
                        'error_msg': '你所在的用户组不支持此操作',
                        'backend': _backend_value(),
                    })

            # check splash

            if enforce_splash and g.user:
                handler = splashes.SplashHandler.get_handler_by_index(
                    g.user.splash_index)
                if handler is not None:
                    return jsonify({
                        'error': 'SPLASH_REQUIRED',
                        'error_msg': '需要更新前端来完成 %s' % handler.splash_name,
                        'backend': _backend_value(),
                        'splash': {
                            'index': handler.splash_index,
                            'name': handler.splash_name,
                            'type': handler.splash_type,
                            'handout': handler.handout(g.user.uid),
                        },
                    })

            # do original view function

            try:
                res = f(*args, **kwargs)

                # check for default sister response

                if res is None:
                    if g.user and may_fallback:
                        res = g.user.build_sister_response()
                    else:
                        raise Exception('no available response')

            # error handling

            except SisterErrorMsg as e:
                return jsonify({
                    'error': 'SISTER_ERROR',
                    'error_msg': e.msg,
                    'backend': _backend_value(),
                })
            except SisterProceed:
                mysql.get_db().commit()
                return jsonify({
                    'error': 'PROCEED',
                    'error_msg': '操作完成,请刷新页面',
                    'backend': _backend_value(),
                })
            except Exception as e:
                current_app.logger.exception('exception in wrapped handler')
                return jsonify({
                    'error': 'BACKEND_EXCEPTION',
                    'error_msg': '%s %s' % (type(e), e),
                    'backend': _backend_value(),
                })

            # post processing

            else:
                mysql.get_db().commit()
                return jsonify({
                    'error':
                    None,
                    'backend':
                    _backend_value(),
                    'user':
                    None if g.user is None else g.user.user_info(),
                    'action_success':
                    g.action_success,
                    **res,
                })
Example #24
0
File: vadd.py Project: xmcp/ddl-bee
def whole_import():
    """
    INPUT:
        data: json object (same format as model output)
    OUTPUT:
        error: str or null
    """
    data = request.json['data']
    if data['backend']['cache_data_ver'] != IMPORT_DATA_VERSION:
        return {
            'error':
            '版本不匹配,只支持 %s 但数据版本为 %s' %
            (IMPORT_DATA_VERSION, data['backend']['cache_data_ver'])
        }
    if data['error']:
        return {'error': '数据有错误'}

    db = mysql.get_db()
    cur = db.cursor()

    z_o, _ = g.user.zones(need_list=False)
    z_o = z_o.get(None, [])
    if len(z_o) + len(data['zone_order']) > current_app.config['LIMIT_ZONES']:
        return {'error': '课程数量超出限制'}
    if len(z_o) >= len(data['zone_order']):  # prevent duplicate import
        return {'error': '您的账户已有数据,请删除现有课程后再导入'}

    def isinstance_or_null(x, c):
        return isinstance(x, c) or x is None

    next_zid = None
    for json_zid in data['zone_order'][::-1]:
        zone = data['zone'][str(json_zid)]
        if not isinstance(zone['name'], str) or len(
                zone['name']) > current_app.config['MAX_NAME_LENGTH']:
            return {'error': '名称长度超出限制'}
        if len(zone['project_order']
               ) > current_app.config['LIMIT_PROJECTS_PER_ZONE']:
            return {'error': '类别数量超出限制'}

        cur.execute(
            '''
            insert into zones (next_zid,name,uid) values (%s,%s,%s)
        ''', [next_zid, zone['name'], g.user.uid])
        db_zid = cur.lastrowid
        next_zid = db_zid

        next_pid = None
        for json_pid in zone['project_order'][::-1]:
            proj = data['project'][str(json_pid)]
            if not isinstance(proj['name'], str) or len(
                    proj['name']) > current_app.config['MAX_NAME_LENGTH']:
                return {'error': '名称长度超出限制'}
            if len(proj['task_order']
                   ) > current_app.config['LIMIT_TASKS_PER_PROJECT']:
                return {'error': '任务数量超出限制'}

            cur.execute(
                '''
                insert into projects (next_pid,name,uid,zid) values (%s,%s,%s,%s)
            ''', [next_pid, proj['name'], g.user.uid, db_zid])
            db_pid = cur.lastrowid
            next_pid = db_pid

            next_tid = None
            for json_tid in proj['task_order'][::-1]:
                task = data['task'][str(json_tid)]
                if not isinstance(task['name'], str) or len(
                        task['name']) > current_app.config['MAX_NAME_LENGTH']:
                    return {'error': '名称长度超出限制'}
                if task['completeness'] not in [
                        'todo', 'done', 'highlight', 'ignored'
                ]:
                    return {'error': '任务完成状态无效'}
                if task['status'] not in ['placeholder', 'active']:
                    return {'error': '任务布置状态无效'}
                if not isinstance_or_null(task['desc'],
                                          str) or not isinstance_or_null(
                                              task['desc_idx'], int):
                    return {'error': '任务备注无效'}
                if not isinstance_or_null(task['due'], int):
                    return {'error': '任务截止时间无效'}

                cur.execute(
                    '''
                    insert into tasks (next_tid, name, uid, pid, status, due, description) values (%s,%s,%s,%s,%s,%s,%s)
                ''', [
                        next_tid, task['name'], g.user.uid, db_pid,
                        task['status'], task['due'], task['desc']
                    ])
                db_tid = cur.lastrowid
                next_tid = db_tid

                if task['completeness'] != 'todo' or task['desc_idx']:
                    cur.execute(
                        '''
                        insert into completes (uid, tid, completeness, update_timestamp, description_idx) values (%s,%s,%s,unix_timestamp(),%s)
                    ''', [
                            g.user.uid, db_tid, task['completeness'],
                            task['desc_idx']
                        ])

    return {'error': None}
Example #25
0
def register():
    """
    ARGS:
        user_token -> g.token as dealed in sister
    INPUT:
        regcode: str
    """
    if not g.token:
        raise SisterErrorMsg('未登录')
    regcode = str(request.json['regcode'])

    cur = mysql.get_db().cursor()

    try:
        info = user_control.get_info_from_user_token(g.token)
    except Exception as e:
        current_app.logger.exception('get info from user token failed')
        raise SisterErrorMsg('查询用户信息失败')

    if info is None:
        raise SisterErrorMsg('用户不存在')

    unique_id = info['unique_id']
    cur.execute(
        '''
        select count(*) from users where unique_id=%s
    ''', [unique_id])

    if cur.fetchone()[0] > 0:  # already registered: update user token
        cur.execute(
            '''
            update users set user_token=%s where unique_id=%s
        ''', [g.token, unique_id])

        flash('%s,欢迎回来' % info['name'], 'success')

    else:  # register new one
        try:
            reg = user_control.check_registration_code(regcode, info)
        except Exception as e:
            current_app.logger.exception('check registration code failed')
            raise SisterErrorMsg('检查注册条件失败')

        if reg['error'] is not None:
            raise SisterErrorMsg(reg['error'])

        if reg['splash_index'] is None:
            reg['splash_index'] = current_app.config['INITIAL_SPLASH_INDEX']

        cur.execute(
            '''
            insert into users (user_token, unique_id, name, ring, splash_index, remarks, settings)
            values (%s, %s, %s, %s, %s, %s, '{}')
        ''', [
                g.token, unique_id, info['name'], reg['ring'],
                reg['splash_index'], reg['remarks']
            ])

        flash('注册成功', 'success')

    raise SisterProceed()
Example #26
0
def refresh():
    cur = mysql.get_db().cursor()
    cur.execute(
        '''
        update users set last_refresh=unix_timestamp() where uid=%s
    ''', [g.user.uid])
Example #27
0
def _set_splash_index(uid, next_index):
    cur = mysql.get_db().cursor()
    cur.execute(
        '''
        update users set splash_index=%s where uid=%s
    ''', [next_index, uid])
Example #28
0
def delete_project():
    """
    INPUT:
        ids: list of int
        parent_id: int
    """
    pids=list(set(request.json['ids']))
    zid=int(request.json['parent_id'])

    p_o,_=g.user.projects(zid,need_list=False)
    p_o=p_o.get(zid,[])
    p_onew=p_o[:]

    for pid in pids:
        if pid not in p_o:
            flash('类别不存在或没有权限','error')
            g.action_success=False
            return

    cur=mysql.get_db().cursor()
    for pid in pids:
        p_onew.remove(pid)

        # get linked extpid
        cur.execute('''
            select extpid from projects where pid=%s
        ''',[pid])
        extpid=cur.fetchone()[0]

        if extpid is None: # src project
            cur.execute('''
                select count(*) from projects where extpid=%s
            ''',[pid])
            peercnt=cur.fetchone()[0]

            if peercnt==0: # no peers: safely delete
                cur.execute('''
                    delete from projects where pid=%s and uid=%s
                ''',[pid,g.user.uid])

            else: # otherwise: only remove from current user, delete it later
                cur.execute('''
                    update projects set uid=null, zid=null, share_hash=null where pid=%s and uid=%s
                ''',[pid,g.user.uid])
                cur.execute('''
                    update tasks set uid=null where pid=%s and uid=%s
                ''',[pid,g.user.uid])
                # completes will be deleted after last person deletes the project and tasks are deleted

        else: # imported project
            # delete self
            cur.execute('''
                delete from projects where pid=%s and uid=%s
            ''',[pid,g.user.uid])

            cur.execute('''
                select count(*) from projects where
                    (extpid=%s and pid!=%s) or /* other imported ones */
                    (pid=%s and uid is not null) /* original one that is not flagged as deletion */
            ''',[extpid,pid,extpid])
            peercnt=cur.fetchone()[0]

            if peercnt==0: # delete src
                cur.execute('''
                    delete from projects where pid=%s
                ''',[extpid])

    model.update_linkedlist(p_o,p_onew,'projects')