def _code_fork(request, code, user): '''复制已有公开代码''' # 权限检测 if not (code.author == user or code.public): return sorry(request, 403, text='没有复制权限') # 验证用户代码数未超标 if user.code_set.filter( ai_type=code.ai_type).count() >= settings.MAX_CODE_PER_GAME: return sorry(request, 403, text='已超过最大可保留代码数') # 生成新代码 new_code = Code(ai_type=code.ai_type, author=user) new_code.edit_datetime = timezone.now() new_code.public = code.public new_code.name = ('副本-' + code.name)[:20] # 保存副本代码 new_content = ContentFile(code.content.read()) new_code.content.save('test', new_content) new_code.save() messages.info(request, '创建副本"%s"成功' % new_code.name) return redirect('/home/')
def forgotpasswd(request, code=None): if code == None: # 验证页面 form = forms.ForgotPasswdForm() # 判断发送邮件逻辑 if request.method == 'POST': form = forms.ForgotPasswdForm(request.POST) # 检查用户名与学号匹配情况 if form.is_valid(): name = request.POST.get('username') code = request.POST.get('stu_code') try: user = User.objects.get(username=name, stu_code=code) except: form.add_error('username', '用户名与学号不匹配') form.add_error('stu_code', '用户名与学号不匹配') else: # 小组用户无邮箱 if user.is_team: return sorry(request, text=[ '小组用户不可通过邮箱重置密码', '若忘记密码请联系课程团队', ]) # 发送邮件 if form.is_valid(): send_valid_email(user, request, 'forgotpw') return redirect('/login/') else: messages.warning(request, '请检查非法输入') return render(request, 'forgotpasswd.html', locals()) else: try: checker = UserResetPwMail.objects.get(check_hash=code) if timezone.now() > checker.send_time + timezone.timedelta( settings.EMAIL_VALID_LAST_DAYS): raise ValueError form = forms.ResetPasswdForm() if request.method == 'POST': form = forms.ResetPasswdForm(request.POST) if request.POST.get('new_passwd') != request.POST.get( 'new_pw2'): form.add_error('new_pw2', '两次密码输入不同') if form.is_valid(): checker.user.set_passwd(form.cleaned_data['new_pw2']) set_user(request, checker.user) messages.info(request, '密码已重设') checker.delete() return redirect('/home/') messages.warning(request, '请检查非法输入') return render(request, 'changepasswd.html', locals()) except: return sorry(request, text='无效的链接')
def course_results(request, AI_type, folder): try: AI_type = int(AI_type) assert AI_type in settings.AI_TYPES except: return sorry(request, text='无效的游戏类型') title = settings.AI_TYPES[AI_type] request.session['curr_game'] = AI_type # 设置当前页面游戏 loader = Factory(AI_type) file_folder = '%s/results/%03d' % (settings.MEDIA_ROOT, AI_type) if not os.path.isdir(file_folder): return sorry(request, text='比赛记录未开放') if folder: folder = folder.replace('\\', '/').rstrip('/') file_folder = os.path.join(file_folder, folder) print(file_folder) if not (os.path.isdir(file_folder)): if os.path.isfile(file_folder): return course_view(request, AI_type, file_folder) return sorry(request, text='目录不存在') pool = os.listdir(file_folder) folders, files, errors = [], [], [] for f in pool: f1 = os.path.join(file_folder, f) if os.path.isfile(f1): # 读取记录 try: rec_unit = { 'name': f, # 记录名称 'record': loader.load_record_path(f1), # 记录对象 'time': time.ctime(os.path.getmtime(f1)), # 修改时间 } files.append(rec_unit) except Exception as e: errors.append(f) else: folders.append(f) # 加载tags for record in files: record['tags'] = loader.analyze_tags(record['record']) # 根据GET参数决定排序方式 sort_method = request.GET.get( 'sort', request.session.get('course_results_sort', 'name'), ) request.session['course_results_sort'] = sort_method # 缓存参数于session中 sort_keys = {'name': '名称', 'time': '时间'} for key in sort_keys: if sort_method == key: files.sort(key=lambda x: x[key]) break return render(request, 'results/filetree.html', locals())
def game_info(request, AI_type): """列出所有可用的比赛,显示其规则,引用至站内对战入口与github项目""" # 验证游戏ID存在 try: AI_type = int(AI_type) assert AI_type in settings.AI_TYPES except: return sorry(request, text='无效的游戏类型') title = settings.AI_TYPES[AI_type] request.session['curr_game'] = AI_type # 设置当前页面游戏 try: return render(request, 'game_info/%s.html' % AI_type, locals()) except: return sorry(request, text='WORK IN PROGRESS')
def upload(request): ai_type = request.GET.get('id', '') user = get_user(request) if request.method == 'POST': form = forms.CodeUploadForm(request.POST, request.FILES) if form.is_valid(): # 验证用户代码数未超标 code_count = user.code_set.filter( ai_type=form.cleaned_data['ai_type']).count() code_max = 1 if user.is_team else settings.MAX_CODE_PER_GAME if code_count >= code_max: return sorry(request, 403, text=[ '已超过最大可上传代码数', '请删除不必要的代码', '或在已有代码上进行修改', ]) code = form.instance code.author = user code.edit_datetime = timezone.now() form.save() messages.info(request, '上传文件"%s"成功' % code.name) return redirect('/home/') else: messages.warning(request, '请检查非法输入') return render(request, 'upload.html', locals()) form = forms.CodeUploadForm() return render(request, 'upload.html', locals())
def view_record(request, match_name, record_id): try: match = PairMatch.objects.get(name=match_name) except: return sorry(request, text='无效的比赛地址') match_dir = os.path.join(settings.PAIRMATCH_DIR, match_name) loader = Factory(match.ai_type) try: record_id = int(record_id) record_content = loader.stringfy_record(match_dir, record_id) except: return sorry(request, text='无效的记录编号') not_last_record = (record_id + 1 != match.finished_rounds) return render(request, 'renderer/%s.html' % match.ai_type, locals())
def ladder(request, AI_type): '''天梯''' # 读取参数 try: AI_type = int(AI_type) assert AI_type in settings.AI_TYPES except: return sorry(request, text='无效的比赛编号') title = settings.AI_TYPES[AI_type] request.session['curr_game'] = AI_type # 设置当前页面游戏 # 代码排序 all_codes = Code.objects.filter( ai_type=AI_type, author__is_team=False, ) # 用户均分统计 user_info = all_codes.values('author').annotate( score=Max('score'), count=Count('id')).values( 'author', 'score', 'count').order_by('-score')[:settings.MAX_LADDER_USER] for grp in user_info: grp['user'] = User.objects.get(id=grp['author']) return render(request, 'ladder.html', locals())
def inner(request, *a, **kw): user = get_user(request) if user.is_team: return sorry(request, 403, text=[ '当前页面不可访问', '小组用户所有比赛由系统自动发起', ]) return func(request, *a, **kw)
def view_pairmatch(request, match_name): # 读取比赛对象 try: match = PairMatch.objects.get(name=match_name) except: return sorry(request, text='无效的比赛地址') match_dir = os.path.join(settings.PAIRMATCH_DIR, match_name) # 检测权限 user = get_user(request) my_match = (match.code1.author == user) or user.is_admin # 处理操作请求 if my_match: op = request.GET.get('op') if match.status == 1 and op == 'stop': match_monitor.kill_match('match', match.name) messages.info(request, '比赛已中止') elif match.status != 1 and op == 'del': if not settings.CAN_DELETE_MATCH_RESULT: return sorry(request, text='删除比赛记录功能已关闭') upper = match.code1.id match.delete() messages.info(request, '比赛记录已删除') return redirect('/code/%d' % upper) # 读取比赛记录 loader = Factory(match.ai_type) records = loader.load_records(match) result_summary = loader.summary_records(records) result_stat = result_summary['stat'] # 读取tag record_tags = [] for record in records: try: record_tags.append(loader.analyze_tags(record)) except: record_tags.append([]) record_pairs = zip(records, record_tags) return render(request, 'view_match.html', locals())
def course_view(request, AI_type, file): try: AI_type = int(AI_type) assert AI_type in settings.AI_TYPES except: return sorry(request, text='无效的游戏类型') title = settings.AI_TYPES[AI_type] loader = Factory(AI_type) record = loader.load_record_path(file) record_content = loader.stringfy_record_obj(record) return render(request, 'renderer/%s.html' % AI_type, locals())
def ranked_match(request, AI_type): '''积分匹配赛''' # 读取参数 try: AI_type = int(AI_type) assert AI_type in settings.AI_TYPES except: return sorry(request, text='无效的比赛编号') title = settings.AI_TYPES[AI_type] request.session['curr_game'] = AI_type # 设置当前页面游戏 # 获取可选AI列表 codes = Code.objects.filter( ai_type=AI_type, author__is_team=False, # 排除小组代码 ) my_codes = codes.filter(author=request.session['userid']) # 我方所有 # 读取筛选条件 my_code = request.GET.get('code1', '') if request.method == 'POST': form = forms.PairMatchFormFactory.get(AI_type, request.POST) my_code = request.POST.get('code1') if not (my_code and my_codes.filter(id=my_code)): form.errors['code1'] = '非法输入: %s' % my_code # 选取目标代码 if form.is_valid(): my_code_obj = my_codes.filter(id=my_code)[0] target_codes = codes.exclude(author=request.session['userid']) target_codes = sorted( target_codes, key=lambda code: abs(code.score - my_code_obj.score ))[:settings.RANKING_RANDOM_RANGE] if not target_codes: form.errors['code1'] = '暂无可用的匹配代码' _limit_rounds(request, form, my_code) # 限制发起局数 if form.is_valid(): # run match target = random.choice(target_codes).id match_name = match_monitor.start_match(AI_type, my_code, target, form, True) messages.info(request, '创建匹配赛成功') return redirect('/match/' + match_name) else: # invalid input messages.warning(request, '请检查非法输入') return render(request, 'ranked_match.html', locals()) form = forms.PairMatchFormFactory.get(AI_type) return render(request, 'ranked_match.html', locals())
def ladder_teams(request, AI_type): """ 小组账号限定天梯 """ # 读取参数 try: AI_type = int(AI_type) assert AI_type in settings.AI_TYPES except: return sorry(request, text='无效的比赛编号') title = settings.AI_TYPES[AI_type] request.session['curr_game'] = AI_type # 设置当前页面游戏 return render(request, 'ladder_teams.html', locals())
def view_code(request, code_id, code_op=None): '''查看代码对象、分发下级命令''' # 获取代码对象 try: code = Code.objects.get(id=int(code_id)) except: return sorry(request, text='无效的代码编号') # 检测权限 user = get_user(request) my_code = (code.author == user) or user.is_admin # 代码主页 if code_op == None: return render(request, 'view_code.html', locals()) # 代码编辑页 elif code_op == 'edit': return _code_editor(request, code, user, True) # 代码删除页 elif code_op == 'del': return _code_del(request, code, user) # 查看公开代码 elif code_op == 'view': return _code_editor(request, code, user, False) # 复制公开代码 elif code_op == 'fork': if not settings.CAN_FORK_PUBLIC_CODE: return sorry(request, text='拷贝公开代码功能已关闭') return _code_fork(request, code, user) return sorry(request, text=['亲亲', '"%s"这样的命令' % code_op, '是不存在的呢'])
def view_user(request, userid): '''其它用户主页,快速访问对战页面''' # 验证用户 try: user = User.objects.get(id=userid) except: try: user = User.objects.get(username=userid) except: return sorry('该用户不存在') # 本用户主页 if user.id == request.session.get('userid'): return redirect('/home/') return home(request, user)
def _code_del(request, code, user): '''验证密码删除代码''' # 权限检测 if code.author != user or user.is_team: return sorry(request, 403, text='没有删除权限') # 检测密码验证 if request.method == 'POST': pw = request.POST.get('check_pw', '') # 删除代码 if user.match_passwd(pw): code.delete() messages.info(request, '代码已删除') return redirect('/home/') # 密码错误 else: messages.warning(request, '密码错误') return render(request, 'delete_code.html', locals())
def user_settings(request): ''' 个人设置 ''' user = get_user(request) form = forms.SettingsForm(request.POST or user.__dict__) # 小组用户不可修改 if user.is_team: return sorry(request, 403, text='小组用户不可改名') # GET请求 if request.method == 'GET': return render(request, 'settings.html', locals()) form = forms.SettingsForm(request.POST) if form.is_valid(): user.nickname = form.cleaned_data['nickname'] user.real_name = form.cleaned_data['real_name'] user.save() return redirect('/home/') else: messages.warning(request, '请检查非法输入') return render(request, 'changepasswd.html', locals())
def pairmatch(request, AI_type): '''启动一对一比赛''' # 读取参数 try: AI_type = int(AI_type) assert AI_type in settings.AI_TYPES except: return sorry(request, text='无效的比赛编号') title = settings.AI_TYPES[AI_type] request.session['curr_game'] = AI_type # 设置当前页面游戏 # 获取可选AI列表 codes = Code.objects.filter( ai_type=AI_type, author__is_team=False, # 排除小组代码 ) my_codes = codes.filter(author=request.session['userid']) # 我方所有 # 筛选对方代码 code2_empty = True try: target_user = request.GET.get('user2') if target_user: target_codes = codes.filter(author=target_user) if target_codes: code2_empty = False else: messages.warning(request, '用户没有上传代码') except: messages.warning(request, '输入用户非法') if code2_empty: target_codes = codes.all() # 所有代码 # 获取自由模式得分参数,转换为百分比 score_ratio = settings.SCORE_FACTOR_NORANK * 100 # 读取筛选条件 my_code = request.GET.get('code1', '') target_code = request.GET.get('code2', '') if request.method == 'POST': form = forms.PairMatchFormFactory.get(AI_type, request.POST) my_code = request.POST.get('code1') target_code = request.POST.get('code2') if not (my_code and my_codes.filter(id=my_code)): form.errors['code1'] = '非法输入: %s' % my_code if not (target_code and target_codes.filter(id=target_code)): form.errors['code2'] = '非法输入: %s' % target_code _limit_rounds(request, form, my_code) # 限制发起局数 if form.is_valid(): # run match match_name = match_monitor.start_match(AI_type, my_code, target_code, form) messages.info(request, '创建比赛成功') return redirect('/match/' + match_name) else: # invalid input messages.warning(request, '请检查非法输入') return render(request, 'pairmatch.html', locals()) form = forms.PairMatchFormFactory.get(AI_type) return render(request, 'pairmatch.html', locals())
def _code_editor(request, code, user, is_edit): ''' CodeMirror代码编辑器页 提供编辑自己代码+查看公开代码功能 ''' # 检测权限及重定向 my_code = (code.author == user) or user.is_admin if is_edit: if not my_code: # 非本人进入编辑模式 return redirect('/code/%s/view/' % code.id) else: if my_code: # 本人进入查看模式 return redirect('/code/%s/edit/' % code.id) elif not code.public: # 查看非公开代码 return sorry(request, 403, text='没有编辑权限') # GET请求输出代码至编辑器 if request.method == 'GET': code_content = code.content.read().decode('utf-8', 'ignore') return render(request, 'edit_code.html', locals()) # 非编辑模式拒绝POST elif not is_edit: return JsonResponse({}) # POST请求ajax res = {} to_update = False # 更新名称 new_name = request.POST.get('name') if new_name != None: new_name = new_name[:20].strip() or '未命名' to_update = True code.name = new_name res['name'] = new_name # 更新代码是否公开 new_public = request.POST.get('public') try: new_public = int(new_public) except: pass if isinstance(new_public, int): to_update = True code.public = bool(new_public) # 验证更新代码 new_code = request.POST.get('code') if new_code: loader = Factory(code.ai_type) validated = False # 尝试读取代码 try: ast, warnings = loader.load_code(new_code, True, True) for line in warnings: messages.warning(request, '注意: ' + line) validated = True res['code_status'] = 0 except Exception as e: messages.warning(request, '代码有误: ' + str(e)) res['code_status'] = 1 # 保存代码 if validated: code.content.open('wb') code.content.write(new_code.encode('utf-8', errors='ignore')) code.content.close() code.edit_datetime = timezone.now() code.save() to_update = True if to_update: messages.info(request, '更新代码"%s"成功' % code.name) code.save() return JsonResponse(res)
def invite_match(request, AI_type): # TODO: 支持向其他用户发起指定参数的比赛 request.session['curr_game'] = AI_type # 设置当前页面游戏 return sorry(request, text='WORK IN PROGRESS')