def post(self): channel_id = self.get_argument('channel_id', '') begin_str = self.get_argument('begin_date') end_str = self.get_argument('end_date') # logger.info('LogToExcel[post] channel_id: %s' % channel_id) # logger.info('LogToExcel[post] begin_str: %s' % begin_str) # logger.info('LogToExcel[post] end_str: %s' % end_str) # if not channel_id: # raise _query = make_date_query(begin_str, end_str) if not _query: return self.write(error_res('begin data or end data is error')) if channel_id: _query['channel'] = channel_id res = M_DB.deny_list.find(_query).sort('created_time', DESCENDING) if res.count() == 0: return self.write(error_res('no query data')) sheet_name = channel_id if channel_id else u'all' self.set_header('Content-type', 'application/vnd.ms-excel') # self.set_header('Transfer-Encoding','chunked') self.set_header('Content-Disposition', 'attachment;filename="%s.xls"' % (sheet_name)) wb = xlwt.Workbook() wb.encoding = 'gbk' ws = wb.add_sheet(sheet_name) title = ['channel', 'URL', 'IPs', 'created_time'] for r in xrange(res.count() + 1): if r == 0: for x in xrange(4): ws.write(r, x, title[x]) else: for x in xrange(4): _data = res[r - 1][title[x]] if title[x] == 'IPs': _data = '&'.join(_data) elif title[x] == 'created_time': _data = _data.strftime('%Y-%m-%d %H:%M:%S') ws.write(r, x, _data) sio = StringIO.StringIO() wb.save(sio) self.write(sio.getvalue())
def get(self, task_id): login_name, authority = get_login_name(self) author_secure = get_login_author_secure(self) task = preload_b_e.find_one({'_id': ObjectId(task_id)}) if not task: error_res('no task') return self.render('everyday_task_change.html', author_secure=author_secure, loginname=login_name, task=task)
def get(self, task_id): login_name, authority = get_login_name(self) author_secure = get_login_author_secure(self) task = preload_b.find_one({'_id': ObjectId(task_id)}) if not task: error_res('no task') return start_datetime = task['start_datetime'].strftime('%Y-%m-%d %H:%M') self.render('task_change.html', author_secure=author_secure, loginname=login_name, task=task, start_datetime=start_datetime)
def post(self): channel_id = self.get_argument('channel_id', '') old_query_channel = self.get_argument('old_query_channel', '') if old_query_channel: channel_id = old_query_channel begin_str = self.get_argument('begin_date') end_str = self.get_argument('end_date') c_page = int(self.get_argument('c_page', 0)) _query = make_date_query(begin_str, end_str) if not _query: return self.write(error_res('begin data or end data is error')) if channel_id: _query['channel'] = channel_id res = get_all_conf(page=c_page, col=M_DB.deny_list, query=_query) page_list, can_pre_page, can_next_page = pager(res['totalpage'], c_page) self.render('log.html', begin_date=begin_str, end_date=end_str, channel_id=channel_id, old_query_channel=old_query_channel, all_log=res['all_conf'], totalpage=res['totalpage'], c_page=c_page, page_list=page_list, can_pre_page=can_pre_page, can_next_page=can_next_page)
def get(self, task_id): login_name, authority = get_login_name(self) author_secure = get_login_author_secure(self) task = preload_b.find_one({'_id': ObjectId(task_id)}) if not task_id: self.write(error_res('no task')) start_datetime = task['start_datetime'] now = datetime.datetime.now() if now > start_datetime and now - start_datetime < datetime.timedelta( days=1): self.write(error_res('Delete the task must be a day later')) preload_b.remove({'_id': ObjectId(task_id)}) preload_b_result.remove({'task_id': task_id}) self.redirect('/tasks')
def exec_create_event(req): # Login into client authorization.login() # Check if request stemmed from #events channel if not authorization.check_permission('event', req['channel_id']): return utils.error_res('Command must be submitted in #events channel', helpTxt, req['response_url']) # Check if command is 'list-all' if is_list_all_command(req['text']): list_all_events(req['response_url']) return # Parse text of body into correct components event, name, pwd, err = parse_event(req['text']) if err: utils.error_res(err, helpTxt, req['response_url']) return # Add event into the spreadsheet err = add_event(event, name, pwd) if err: utils.error_res(err, helpTxt, req['response_url']) return # Formatting for successful update success_text = 'Successfully added {name} as an event with password: {pwd} \n'.format( name=name, pwd=pwd) remind_text = 'To finish creating an event remember to add it to the UPE Google Calendar!' data = { 'response_type': 'ephemeral', "blocks": [{ "type": "section", "text": { "type": "mrkdwn", "text": success_text + remind_text } }, { "type": "divider" }] } requests.post(req['response_url'], json=data)
def get(self, task_id): login_name, authority = get_login_name(self) author_secure = get_login_author_secure(self) task = preload_b_e.find_one({'_id': ObjectId(task_id)}) if not task_id: self.write(error_res('no task')) preload_b_e.remove({'_id': ObjectId(task_id)}) self.redirect('/everyday/tasks')
def get(self, conf_id): login_name, authority = get_login_name(self) author_secure = get_login_author_secure(self) info = preload_b_admin.find_one({'_id': ObjectId(conf_id)}) if not info: return self.write(error_res('This UserConfig had already deleted')) try: preload_b_admin.remove({'_id': ObjectId(conf_id)}) except Exception, e: print 'UserConfig del is %s' % (e)
def post(self, task_id): login_name, authority = get_login_name(self) author_secure = get_login_author_secure(self) task = preload_b.find_one({'_id': ObjectId(task_id)}) if not task: self.write(error_res('no task')) return # if task['run_datetime']: # self.write(error_res('task had started')) # return now = datetime.datetime.now() username = self.get_argument('username', '') start_datetime_str = self.get_argument('start_datetime', '') start_datetime = datetime.datetime.strptime(start_datetime_str, '%Y-%m-%d %H:%M') #if start_datetime < now: # raise resource_url = self.get_argument('resource_url', '') concurrency = int(self.get_argument('concurrency', 0)) group_detail = [] group_num = int(self.get_argument('group_num', 0)) for g_n in xrange(group_num): new_g = g_n + 1 clients = self.get_argument('%s-client' % (new_g)) client_ips = clients.split('&') servers = self.get_argument('%s-server' % (new_g)) server_ips = servers.split('&') group_detail.append([client_ips, server_ips]) print '------TaskChange------' print username print start_datetime print resource_url print group_detail print concurrency print '------TaskChange over------' db_update(preload_b, {'_id': ObjectId(task_id)}, { '$set': { 'username': username, 'start_datetime': start_datetime, 'resource_url': resource_url, 'group_detail': group_detail, 'concurrency': concurrency } }) self.redirect('/tasks')
def post(self): limiter_id = self.get_argument('limiter_id') limiter_c1 = self.get_argument('limiter_c1') limiter_c2 = self.get_argument('limiter_c2') limiter_ttl = self.get_argument('limiter_ttl') logger.info( "LimiterPublicConfigC[post] limiter_c1: %s|| limiter_c2: %s|| limiter_ttl: %s" % (limiter_c1, limiter_c2, limiter_ttl)) if not limiter_c1: return self.write(error_res('Please put C1')) if not limiter_c2: return self.write(error_res('Please put C2')) if not limiter_ttl: return self.write(error_res('Please put TTL')) limiter_public_conf.update( {"_id": ObjectId(limiter_id)}, {'$set': { 'C1': limiter_c1, 'C2': limiter_c2, 'TTL': limiter_ttl }}) self.redirect('/limiter_conf/public_param')
def post(self): login_name, author = get_login_name(self) author_secure = get_login_author_secure(self) authority = self.get_arguments('authority') user = self.get_argument('user', '') author = '' author_all = '' for line in authority: if line == 'admin': author = 'admin' author_all = author_all + line + ',' author_all = author_all[:-1] if not user: return self.write(error_res('Please put user')) if not authority: return self.write(error_res('Please put authority')) print user print authority is_had = preload_b_admin.find({'username': user}).count() print is_had if is_had > 0: return self.write(error_res('This UserConfig had already added')) if author == 'admin': preload_b_admin.insert({ 'username': user, 'power': 'admin', 'created_time': datetime.datetime.now() }) else: preload_b_admin.insert({ 'username': user, 'power': author_all, 'created_time': datetime.datetime.now() }) self.redirect('/user_conf')
def get(self, conf_id): info = limiter_conf.find_one({'_id': ObjectId(conf_id)}) if not info: return self.write(error_res('This config had already deleted')) try: limiter_conf.remove({'_id': ObjectId(conf_id)}) except Exception as e: logger.info('LimiterConfigDel[get][error]: %s' % (traceback.format_exc(e), )) else: del_limiter_cache(info['channels'], info['category']) self.redirect('/limiter_conf')
def get(self, conf_id): info = control_conf.find_one({'_id': ObjectId(conf_id)}) if not info: return self.write(error_res('This config had already deleted')) try: control_conf.remove({'_id': ObjectId(conf_id)}) except Exception as e: logger.info('ConfigDel[error]: %s' % (traceback.format_exc(e), )) else: del_config_cache(info['channel'] + info.get('suffix', ''), info['category']) self.redirect('/conf')
def exec_track_candidates(req): # Login into client authorization.login() # Check if argument len is sufficient if len(req['text']) < 3: utils.error_res('Please submit an expression with more than two characters', helpTxt, req['response_url']) return # Retrieve candidate info according to text in Slack payload candidate_info = get_matched_candidates(req['text']) if len(candidate_info) == 0: utils.error_res('No candidates found with given keyword', helpTxt, req['response_url']) return # Format candidate info into Slack JSON format candidate_format_string = format_candidate_text(candidate_info) data = { 'response_type': 'ephemeral', 'blocks' : candidate_format_string, } requests.post(req['response_url'], json=data)
def post(self): conf_id = self.get_argument('conf_id') old_category = self.get_argument('old_category') category = self.get_argument('hidden_direction_c') logger.info("LimiterConfigChangeExec [post] category: %s" % category) old_channels = self.get_argument('old_channels') old_channels = old_channels.split(',') new_channels = self.get_arguments('box_channels') rate = float(self.get_argument('rate', 0.0)) Bbase = float(self.get_argument('bbase', 170)) Balarm = float(self.get_argument('balarm', 0.75)) Bhard = float(self.get_argument('bhard', 1)) Bgrade = int(self.get_argument('bgrade', 1)) Bpolice = float(self.get_argument('bpolice', 1)) if rate < 0: return self.write(error_res('Rate must be in 0, 500')) if rate > 500: return self.write(error_res('Rate must be in 0, 500')) # 单位换算 前台为Gb rate *= 1024 if not new_channels: return self.write(error_res('Please check channel')) limiter_conf.update({"_id": ObjectId(conf_id)}, { '$set': { 'channels': new_channels, 'category': category, 'rate': rate, 'Bbase': Bbase, 'Balarm': Balarm, 'Bhard': Bhard, 'Bgrade': Bgrade, 'Bpolice': Bpolice } }) # 删除旧缓存 logger.info( "LimiterConfigChangeExec [post] old_channels: %s|| old_category: %s" % (old_channels, old_category)) had_limiter_user = limiter_conf.find({"_id": ObjectId(conf_id)}, {'user': 1}) user = "" for h in had_limiter_user: user = h['user'] del_limiter_cache(old_channels, old_category) # 新添缓存 add_limiter_cache( new_channels, category, { 'rate': rate, '_id': str(conf_id), 'category': category, 'user': user, 'Bbase': Bbase, 'Balarm': Balarm, 'Bhard': Bhard, 'Bgrade': Bgrade, 'Bpolice': Bpolice }) self.redirect('/limiter_conf')
def post(self): category = self.get_argument('hidden_direction') logger.info("LimiterConfigAdd[post] all_directions: %s" % category) category = category.encode("utf-8") user = self.get_argument('user', '') # logger.info('LimiterConfigAdd[post] user: %s' % user) channels = self.get_arguments('box_channels') # logger.info('LimiterConfigAdd[post] channel: %s' % channel) # logger.info('LimiterConfigAdd[post] cycle: %s' % cycle) num = self.get_argument('num').encode("utf-8") Bbase = float(self.get_argument('bbase', 170)) Balarm = float(self.get_argument('balarm', 0.75)) Bhard = float(self.get_argument('bhard', 1)) Bgrade = int(self.get_argument('bgrade', 1)) Bpolice = float(self.get_argument('bpolice', 1.2)) if not category: return self.write(error_res('Please put category')) if not user: return self.write(error_res('Please put user')) if not num: return self.write(error_res('Please put num')) if float(num) < 0 or float(num) > 500: return self.write(error_res('Rate must be in 0, 500')) num = float(num) * 1024 logger.info('LimiterConfigAdd[post] num: %s' % num) is_had = limiter_conf.find({ 'user': user, 'category': category, 'channels': { '$in': channels } }).count() if is_had > 0: return self.write(error_res('This config had already added')) insert_id = limiter_conf.insert({ 'user': user, 'channels': channels, 'rate': float(num), 'category': category, 'Bbase': Bbase, 'Balarm': Balarm, 'Bhard': Bhard, 'Bgrade': Bgrade, 'Bpolice': Bpolice, 'created_time': datetime.datetime.now() }) logger.info('LimiterConfigAdd[post] insert_id: %s' % insert_id) add_limiter_cache( channels, category, { 'rate': float(num), '_id': str(insert_id), 'category': category, 'user': user, 'Bbase': Bbase, 'Balarm': Balarm, 'Bhard': Bhard, 'Bgrade': Bgrade, 'Bpolice': Bpolice }) self.redirect('/limiter_conf')
def exec_assign_challenge(req): # Login into client authorization.login() # Verify that the command was run in #officers or #softdev-bot-testing if not authorization.check_permission('officer', req['channel_id']): utils.error_res("Command must be submitted in #officers", helpTxt, req['response_url']) return # Parse expr for arguments officer_name, challenge_text, candidate_name, err_string = parse_challenge(req['text']) # Throw error if err_string is not 'None' if err_string: utils.error_res(err_string, helpTxt, req['response_url']) return # Find current column locations of candidate sheet col_dct = utils.get_candidate_sheet_col_numbers() # Locate matching candidate rows from candidate_name candidate_row_list = utils.get_candidate_row_number(candidate_name, candidateSheet, col_dct['name']) # If no candidate rows are returned, throw an error if len(candidate_row_list) <= 0: utils.error_res("Candidate could not be identified; please check your spelling and try again", helpTxt, req['response_url']) return # If too many candidate rows are returned, throw an error if len(candidate_row_list) > 3: utils.error_res("Too many candidate matches; please be more specific", helpTxt, req['response_url']) return # If two or three candidates rows, list them and throw an error if len(candidate_row_list) > 1 and len(candidate_row_list) <= 3: # Locate candidate name column index candidate_name_col = col_dct['name'] err_string = "Too many candidate matches; current matches: " # Iterate through the candidates to get their names for row_index in candidate_row_list: # Get candidate's name, and append to err_string curr_name = candidateSheet.cell(row_index, candidate_name_col).value err_string += curr_name + ", " utils.error_res(err_string[:len(err_string)-2], helpTxt, req['response_url']) return # Extract candidate row index from candidate_row_list candidate_row = candidate_row_list[0] # Locate challenge column index candidate_col = col_dct['challenge_desc'] # Update challenge cell with new challenge candidateSheet.update_cell(candidate_row, candidate_col, officer_name + ": " + challenge_text) # Formatting for successful update data = { 'response_type': 'ephemeral', "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "Successfully assigned challenge to candidate {candidate_name}".format(candidate_name=candidate_name) } }, { "type": "divider" } ] } requests.post(req['response_url'], json=data)
def post(self): category = self.get_argument('category', '') # logger.info('ConfigAdd[post] category: %s' % category) user = self.get_argument('user', '') # logger.info('ConfigAdd[post] user: %s' % user) channels = self.get_arguments('box_channels') # logger.info('ConfigAdd[post] channel: %s' % channel) suffix = self.get_argument('suffix') if not suffix: suffix = '' cycle = self.get_argument('cycle', 0) # if not cycle: # return self.write(error_res('Please put cycle')) cycle = 0 if not cycle else int(cycle) # logger.info('ConfigAdd[post] cycle: %s' % cycle) num = self.get_argument('num', 0) # if not num: # return self.write(error_res('Please put num')) num = 0 if not num else int(num) # logger.info('ConfigAdd[post] num: %s' % num) visit_cycle = self.get_argument('visit_cycle', 0) # if not visit_cycle: # return self.write(error_res('Please put visit_cycle')) visit_cycle = 0 if not visit_cycle else int(visit_cycle) # logger.info("ConfigAdd[post] visit_cycle: %s" % visit_cycle) visit_num = self.get_argument('visit_num', 0) # if not visit_num: # return self.write(error_res('Please put visit_num')) visit_num = 0 if not visit_num else int(visit_num) # logger.info("ConfigAdd[post] visit_num: %s" % visit_num) # cache_invalid_time = self.get_argument('cache_invalid_time') # if not cache_invalid_time: # return self.write(error_res('Please put cache_invalid_time')) cache_invalid_time = int(cycle) # logger.info("ConfigAdd[post] cache_invalid_time: %s" % cache_invalid_time) add_query_time = self.get_argument('add_query_time') rules_str = self.get_argument('rules_map') if rules_str: for line in rules_str.split('\n'): if len(line.split(',')) != 4: return self.write( error_res( 'Please enter the right field: multi-rules-config-board.' )) rules_map = [{ "theKey": i.split(',')[0].strip(), "theCycle": int(i.split(',')[1].strip()), "theNum": int(i.split(',')[2].strip()), "thePeriod": int(i.split(',')[3].strip()) } for i in rules_str.split('\n')] else: rules_map = [] # logger.info('ConfigAdd[post] rules_map: %s' % (rules_map, )) named_str = self.get_argument('named_map') if named_str: for line in named_str.split('\n'): if len(line.split(',')) != 5: return self.write( error_res('Please enter the right field: named.')) named_map = [{ "named": i.split(',')[0].strip(), "locationName": i.split(',')[1].strip(), "namedCycle": int(i.split(',')[2].strip()), "namedNum": int(i.split(',')[3].strip()), "namedPeriod": int(i.split(',')[4].strip()) } for i in named_str.split('\n')] else: named_map = [] logger.info('ConfigAdd[post] named_map: %s' % (named_map, )) if not add_query_time: # if not have add_query_time the add_query_time equal cycle add_query_time = cycle add_query_time = int(add_query_time) all_query_time = self.get_argument('all_query_time') if not all_query_time: all_query_time = 24 * 60 * 60 all_query_time = int(all_query_time) for c in channels: config_cache = get_config_cache(c + suffix, category) if config_cache: return self.write( error_res('%s %s config cache had already added' % (c + suffix, category))) for i in channels: logger.info( 'ConfigAdd[post] i: %s|| category: %s|| num: %s|| cycle: %s' % ( i, category, num, cycle, )) tempDict = { 'user': user, 'channel': i, 'suffix': suffix, 'num': num, 'category': category, 'cycle': cycle, 'visit_cycle': visit_cycle, 'visit_num': visit_num, 'cache_invalid_time': cache_invalid_time, 'created_time': datetime.datetime.now(), 'add_query_time': add_query_time, 'all_query_time': all_query_time } if rules_map: tempDict.update({'rules_map': rules_map}) if named_map: tempDict.update({'named_map': named_map}) control_conf.insert(tempDict) logger.info('ConfigAdd[post] type(suffix): %s|| suffix: %s' % (type(suffix), suffix)) makeSthStrInDict(tempDict, 'rules_map') makeSthStrInDict(tempDict, 'named_map') add_config_cache("%s%s" % (i, str(suffix)), category, tempDict) self.redirect('/conf')
def exec_checkoff_candidate(req): # Login into client authorization.login() # Parse the command text event_type, candidate_name, officer_name, err = parseText(req['text']) if err: utils.error_res(err, helpTxt, req['response_url']) return # Check command originated in correct channel err = is_valid_channel(event_type, req['channel_id']) if err: utils.error_res(err, helpTxt, req['response_url']) return # Find current column locations of candidate sheet col_dct = utils.get_candidate_sheet_col_numbers() # Match all candidates matched_candidiate_list = utils.get_candidate_row_number( candidate_name, candSheet, col_dct['name']) if len(matched_candidiate_list) == 0: utils.error_res("No matched candidate found", helpTxt, req['response_url']) return if len(matched_candidiate_list) > 3: utils.error_res( "Multiple candidate name matches, please be more specific", helpTxt, req['response_url']) return matched_candidate_names = get_candidate_names(matched_candidiate_list, col_dct['name']) if len(matched_candidiate_list) > 1: candidate_text = ', '.join(matched_candidate_names) utils.error_res( "Matched more than 1 candidate: {names}".format( names=candidate_text), helpTxt, req['response_url']) return # Depending on event type execute checkoff different commands text = None if event_type == 'oh': # Checkoff Office Hour text = checkoff_office_hours(matched_candidiate_list[0], col_dct['oh_checked_off_count']) elif event_type == 'social' or event_type == 'prof': # TODO utils.error_res("Command not implemented yet", helpTxt, req['response_url']) return elif event_type == 'chall': # Checkoff Challenge text = checkoff_challenge(matched_candidiate_list[0], col_dct['challenge_completed']) elif event_type == 'oc': # Checkoff Officer Chat text = checkoff_officer_chats(matched_candidiate_list[0], col_dct['officer_total_count'], officer_name) data = { 'response_type': 'ephemeral', "blocks": [{ "type": "section", "text": { "type": "mrkdwn", "text": text.format(name=matched_candidate_names[0]) } }, { "type": "divider" }] } requests.post(req['response_url'], json=data)
def post(self): _id = self.get_argument('conf_id', '') cycle = self.get_argument('cycle', 0) num = self.get_argument('num', 0) visit_cycle = self.get_argument('visit_cycle', 0) visit_num = self.get_argument('visit_num', 0) named_str = self.get_argument('named_map') rules_str = self.get_argument('rules_map') cache_invalid_time = cycle add_query_time = self.get_argument('add_query_time') all_query_time = self.get_argument('all_query_time') # if not cycle: # return self.write(error_res('Please put cycle')) # if not num: # return self.write(error_res('Please put num')) # if not visit_cycle: # return self.write(error_res('Please put visit_cycle')) # if not visit_num: # return self.write(error_res('Please put visit_num')) if rules_str: for line in rules_str.split('\n'): if len(line.split(',')) != 4: return self.write( error_res('Please enter the right rules_map')) if named_str: for line in named_str.split('\n'): if len(line.split(',')) != 5: return self.write( error_res('Please enter the right named_map')) # if not cache_invalid_time: # return self.write(error_res('Please put cache_invalid_time')) if not add_query_time: return self.write(error_res('Please put add_query_time')) if not all_query_time: return self.write(error_res('Please put all_query_time')) cycle = 0 if not cycle else int(cycle) num = 0 if not num else int(num) visit_cycle = 0 if not visit_cycle else int(visit_cycle) visit_num = 0 if not visit_num else int(visit_num) cache_invalid_time = 0 if not cache_invalid_time else int( cache_invalid_time) add_query_time = int(add_query_time) all_query_time = int(all_query_time) if named_str: named_map = [{ 'named': i.split(',')[0].strip(), 'locationName': i.split(',')[1].strip(), 'namedCycle': int(i.split(',')[2].strip()), 'namedNum': int(i.split(',')[3].strip()), 'namedPeriod': int(i.split(',')[4].strip()) } for i in named_str.split('\n')] else: named_map = [] if rules_str: rules_map = [{ 'theKey': i.split(',')[0].strip(), 'theCycle': int(i.split(',')[1].strip()), 'theNum': int(i.split(',')[2].strip()), 'thePeriod': int(i.split(',')[3].strip()) } for i in rules_str.split('\n')] else: rules_map = [] new_conf = { "cycle": cycle, "num": num, "visit_cycle": visit_cycle, "visit_num": visit_num, "cache_invalid_time": cache_invalid_time, "add_query_time": add_query_time, "all_query_time": all_query_time } if rules_map: new_conf.update({'rules_map': rules_map}) if named_map: new_conf.update({'named_map': named_map}) old_conf = control_conf.find_one({'_id': ObjectId(_id)}) if not old_conf: return self.write(error_res('This config had already deleted')) try: db_update(control_conf, {'_id': ObjectId(_id)}, {'$set': new_conf}) except Exception: logger.info('ConfigChangeExec[post][error]: %s' % (traceback.format_exc(), )) else: logger.info( 'ConfigChangeExec[post] channel: %s|| suffix: %s|| category: %s' % (old_conf['channel'], str(old_conf.get( 'suffix', '')), old_conf['category'])) makeSthStrInDict(new_conf, 'rules_map') makeSthStrInDict(new_conf, 'named_map') add_config_cache( "%s%s" % (old_conf['channel'], str(old_conf.get('suffix', ''))), old_conf['category'], new_conf) self.redirect('/conf')