def showProblemDetail(self, pid): PrintUtil.info("正在加载题目...") p = self.getProblemInfo(globalVar.BASE_CONF.get('contest', 'cid'), pid) if not p: return os.system('clear') print(p.problemDetail())
def showProblemList(self): PrintUtil.info("正在加载题目列表...") # 先尝试从缓存的文件中加载 i = 1 try: with open(globalVar.BASE_CONF_PATH + 'problemList', 'rb') as file_object: pList = pickle.load(file_object) for p in pList: print(p.problemSimple, end=' ') if i % 3 == 0: print('') i = i + 1 print('') except: # 出错就从oj线上加载 pList = self.getProblemList( globalVar.BASE_CONF.get('contest', 'cid')) for p in pList: print(p.problemSimple(), end=' ') if i % 3 == 0: print('') i = i + 1 print('')
def genCode(self, pid, codetype): PrintUtil.info('代码文件生成中...') p = self.getProblemInfo(globalVar.BASE_CONF.get('contest', 'cid'), pid) title = p.title.split('(')[0].strip() code = '/*' + p.problemContent() + '\n*/\n\n' code = re.sub(r'\r', '', code) ccode = '#include <stdio.h>\n\nint main(){\n\n return 0;\n}' cppcode = '#include <iostream> \n\n#include <cstdio>\nusing namespace std;\nint main()\n{\n\n return 0;\n}' javacode = 'import java.util.*;\n\npublic class Main{\n public static void main(String args[]){\n\n }\n}' suffix = '.c' if codetype == 'c': code = code + ccode suffix = '.c' elif codetype == 'cpp': suffix = '.cpp' code = code + cppcode elif codetype == 'java': suffix = '.java' code = code + javacode fileName = pid + '.' + title + suffix f = open("./" + fileName, "w") f.write(code) f.flush() f.close() PrintUtil.info('文件 [ ' + fileName + ' ] 保存成功 :) ')
def getProblemInfo(self, cid, pid): ctype = globalVar.BASE_CONF.get('contest', 'ctype') cpass = globalVar.BASE_CONF.get('contest', 'cpass') data = {'id': cid, 'type': ctype} pwdProbleUrl = globalVar.OJ_CONF.get('urls', 'pwdProblems') resp = RequestUtil.doGet(pwdProbleUrl, data) \ if cpass == '1' else RequestUtil.doGet(AojApi.getUrl('problems'), data) # 解析网页数据 soup = BeautifulSoup(resp.text, "lxml") pdiv = soup.find('div', id='title_' + pid) if not pdiv: PrintUtil.error("你已经AC了或者没有该题目") sys.exit(0) p = Problem() p.pid = re.sub("\D", "", pdiv['id']) p.title = pdiv.find('div', class_='nav').string.split('.')[1].strip() p.timeAndMem = pdiv.find('div', class_='common').string.strip() contentDiv = pdiv.find_all('div') p.content = contentDiv[3].pre.string p.descr_input = contentDiv[5].pre.string p.descr_output = contentDiv[7].pre.string p.ex_input = contentDiv[9].pre.string p.ex_output = contentDiv[11].pre.string p.code = '' return p
def getPassedDetail(self, cid, pid): resp = RequestUtil.doGet(AojApi.getUrl('passedProblem'), {'id': cid}) soup = BeautifulSoup(resp.text, "lxml") p = soup.find('div', class_='nav', text=re.compile(r'.*' + pid + '.*')) if not p: PrintUtil.error("没有该题目...") sys.exit(0) infolist = [ 'title', 'content', 'descr_input', 'descr_output', 'ex_input', 'ex_output', 'code', 'score' ] j = 0 problem = Problem() problem.pid = '' problem.timeAndMem = '' for i in range(0, 31): s = '' if p.string is not None: s = p.string elif p.pre is not None: s = p.pre.string elif p.span is not None: s = p.span.string elif p.textarea is not None: s = p.textarea.string if s is not None and s.strip() != '': setattr(problem, infolist[j], s.strip()) j = j + 1 p = p.next_sibling return problem
def getProblemList(self, cid): ctype = globalVar.BASE_CONF.get('contest', 'ctype') cpass = globalVar.BASE_CONF.get('contest', 'cpass') data = {'id': cid, 'type': ctype} pwdProbleUrl = globalVar.OJ_CONF.get('urls', 'pwdProblems') resp = RequestUtil.doGet(pwdProbleUrl, data) \ if cpass == '1' else RequestUtil.doGet(AojApi.getUrl('problems'), data) # 解析网页数据 soup = BeautifulSoup(resp.text, "lxml") # 仅获取题目和id pList = [] allProblemDiv = soup.find_all('div', id=re.compile(r'title_\d*')) if not allProblemDiv: PrintUtil.warn("题目空了,你可能已经AK了.") sys.exit(0) for pdiv in allProblemDiv: p = Problem() p.pid = re.sub("\D", "", pdiv['id']) title = pdiv.find('div', class_='nav').string.split('.')[1].strip() tempStrs = title.split('(') p.score = '分数:' + re.sub("\D", "", tempStrs[len(tempStrs) - 1]).strip() p.title = title.split('(')[0].strip() pList.append(p) # 按分数排序 pList = sorted(pList, key=lambda pList: pList.score) return pList
def use_commond(ojOperate): if arg_len == 3: arg2 = sys.argv[2] #if re.match('^\d+$', arg2): ojOperate.saveContestInfo(arg2) return PrintUtil.error("参数错误") help_commond()
def saveContestInfo(self, cid): PrintUtil.info('正在获取比赛信息...') # 保存比赛信息 globalVar.BASE_CONF.set('contest', 'cid', cid) with open(globalVar.BASE_PATH + 'base.conf', 'w') as fw: globalVar.BASE_CONF.write(fw) self.saveProblemList() PrintUtil.info("设置比赛成功!\n")
def showRanking(self): cid = globalVar.BASE_CONF.get('contest', 'cid') PrintUtil.info("加载排名中...") rList = self.getRankingList(cid) # rList = rList.reverse() for i in range(0, len(rList))[::-1]: rList[i].showUserInfo()
def checkout_commond(): # 切换oj if arg_len == 3: globalVar.BASE_CONF.set('base', 'oj_name', sys.argv[2]) with open(globalVar.BASE_CONF_PATH, 'w') as fw: globalVar.BASE_CONF.write(fw) return PrintUtil.error("参数错误") help_commond()
def test_commond(ojOperate): if arg_len == 3: arg1 = sys.argv[1] arg2 = sys.argv[2] if arg1 == "test": ojOperate.testCode(arg2) return PrintUtil.error("参数错误") help_commond()
def passed_commond(ojOperate): if arg_len == 2: ojOperate.showPassed() return elif arg_len == 3 and re.match('^\d+$', sys.argv[2]): ojOperate.showPassedDetail(sys.argv[2]) return PrintUtil.error("参数错误") help_commond()
def submit_commond(ojOperate): if arg_len == 3: arg1 = sys.argv[1] arg2 = sys.argv[2] if arg1 == "submit": ojOperate.showSubmitResult(arg2) return PrintUtil.error("参数错误") help_commond()
def login(self, username, password): data = {'email': username, 'passwd': password} resp = RequestUtil.doPost(url=AojApi.getUrl('login'), data=data) jdata = json.loads(resp.text) if jdata['code'] == 0 and jdata['response']['message'] == 'success': return True else: PrintUtil.error(jdata['errorMessage']) return False
def showContestList(self, containPassed): contestList = self.getContestList(containPassed) for c in contestList: if isinstance(c, Contest): c.problemDetail() else: PrintUtil.error('contest type error') break print(''.join(' id' + '\t' + '{:35}'.format('名称') + '语言' + '\t' + '结束时间' + '\t\t' + '描述'))
def showPassedDetail(self, pid): cid = globalVar.BASE_CONF.get('contest', 'cid') problem = self.getPassedDetail(cid, pid) try: problem.code.index('输入描述') PrintUtil.info(problem.code) except: print(problem.problemDetail()) PrintUtil.success(problem.score)
def showSubmitResult(self, fileName): pid = fileName.split('.')[0] if not re.match('^\d+$', pid): PrintUtil.error("文件命名错误,请以'id.'开头, 例: 70.简单求和.c") return try: f = open(fileName, "r") except: PrintUtil.error("没有找到文件.检查文件名是否有误") return code = f.read() f.close() self.submitCode(code, pid)
def list_commond(ojOperate): if arg_len == 3 or arg_len == 4: arg2 = sys.argv[2] if arg2 == "-c": if arg_len == 4 and sys.argv[3] == '-a': ojOperate.showContestList(True) else: ojOperate.showContestList(False) return elif arg2 == "-p": ojOperate.showProblemList() return PrintUtil.error("参数错误\n") help_commond()
def input_name_pass_login(ojOperate): username = input(termcolor.colored('请输入用户名: ', 'cyan')) password = getpass.getpass(termcolor.colored('请输入密码: ', 'cyan')) loginSuccess = ojOperate.login(username, password) if loginSuccess: # 保存cookie RequestUtil.session.cookies.save(ignore_discard=True, ignore_expires=True) PrintUtil.info('登录成功!') # 保存账号密码 option = input(termcolor.colored(u'\n是否保存用户名及密码? (yes/no) ', 'cyan')) if option == 'yes': # 保存密码 try: globalVar.BASE_CONF.set('user', 'username', username) # 先简单base64编码加密意思意思... bytesString = password.encode(encoding="utf-8") encodestr = base64.b64encode(bytesString) globalVar.BASE_CONF.set('user', 'password', encodestr.decode(encoding='utf-8')) with open(globalVar.BASE_CONF_PATH, 'w') as fw: globalVar.BASE_CONF.write(fw) PrintUtil.success('保存密码成功 :)') except Exception as e: PrintUtil.info("保存密码失败 :(") PrintUtil.error(e) else: sys.exit(0)
def testCode(self, fileName): # 编译 compilep = subprocess.Popen(['g++', fileName], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) res = '' for line in compilep.stdout.readlines(): res = res + line.decode() if res.find('error') >= 0: PrintUtil.info('编译错误') PrintUtil.error(res) return # 执行 ## 读取测试数据 pid = fileName.split('.')[0] problem = self.getProblemInfo( globalVar.BASE_CONF.get('contest', 'cid'), pid) ex_input = problem.ex_input ex_output = problem.ex_output execp = subprocess.Popen(['./a.out'], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) out, err = execp.communicate(input=ex_input.encode()) PrintUtil.info("测试输出:") print(out.decode(), end="") PrintUtil.info("正确输出:") print(ex_output, end="")
def getPassedList(self, cid): resp = RequestUtil.doGet(AojApi.getUrl('passedProblem'), {'id': cid}) soup = BeautifulSoup(resp.text, "lxml") titles = soup.find_all('div', class_='nav') if not titles: PrintUtil.error("你还没有做过此比赛的题目 :)") sys.exit(0) problemList = [] for t in titles: title = t.string.strip().split('.')[1] tempStrs = title.split('(') p = Problem() p.score = int(re.sub("\D", "", tempStrs[len(tempStrs) - 1])) p.title = title.split('(')[0].strip() p.pid = t.string.strip().split('.')[0] problemList.append(p) return problemList
def help_commond(): info = "\n" \ " list -c | 列出所有正在进行的比赛\n" \ " use id | 根据id选择比赛\n" \ " list -p | 列出当前比赛题目\n" \ " show id | 显示id对应题目的详细信息\n" \ " show id -g c | 显示题目信息并生成c语言代码文件 可选参数cpp java\n" \ " submit filename | 提交代码文件判题\n" \ " show ranking | 显示当前参加比赛对应的排名\n" \ " list -c -a | 列出所有进行和已结束的比赛\n" \ " passed | 显示所有已提交过的题目列表\n" \ " passed id | 显示已提交题目详细信息\n" \ " login | 登录\n" \ " checkout 'ojName'| 切换oj\n" \ " help | 显示此帮助信息\n" \ "\n" \ "--------------------------------------------------\n" PrintUtil.info(info) sys.exit(0)
def show_commond(ojOperate): if arg_len == 3: arg2 = sys.argv[2] if re.match('^\d+$', arg2): ojOperate.showProblemDetail(arg2) return elif arg2 == "ranking": ojOperate.showRanking() return elif arg_len == 5: arg2 = sys.argv[2] arg3 = sys.argv[3] arg4 = sys.argv[4] if re.match('\d+', arg2) and arg3 == '-g' and re.match( r'^(java|c|cpp)$', arg4): ojOperate.showProblemDetail(arg2) ojOperate.genCode(arg2, arg4) return PrintUtil.error("参数错误") help_commond()
def login(self, username, password): # 验证码识别率较低..索性尝试5次 tryloginTime = 5 while (tryloginTime > 0): resp = RequestUtil.doPost(url=AojApi.getUrl('login'), data=self.getLoginData( username, password)) soup = BeautifulSoup(resp.text, "lxml") divlist = soup.find_all('div', class_='user') if len(divlist) > 3: info = divlist[3].font.string if info != "验证码有误": PrintUtil.error(info) return False else: return True tryloginTime = tryloginTime - 1 if tryloginTime <= 0: PrintUtil.error("oooops...验证码识别失败,再试试?")
def getRankingList(self, cid): rankData = {"id": cid} resp = RequestUtil.doGet(AojApi.getUrl('rank'), rankData) soup = BeautifulSoup(resp.text, "lxml") rankingTr = soup.find_all('tr', id=re.compile('\d*')) if not rankingTr: PrintUtil.error("没有该比赛排名...重新选择比赛") sys.exit(0) rList = [] for tr in rankingTr: stu = UserInfo() td = tr.find_all('td') stu.rank = td[0].div.string stu.username = td[1].div.string stu.name = td[2].div.string stu.stuid = td[3].div.string stu.college = td[4].div.string stu.major = td[5].div.string stu.score = td[6].div.string stu.subTime = td[7].div.string rList.append(stu) return rList
def submitCode(self, code, pid): subData = { 'answer': code, 'id': pid, 'type': globalVar.BASE_CONF.get('contest', 'ctype') } resp = RequestUtil.doPost(AojApi.getUrl('submitCode'), subData) # {"id":"125","result":"Wrong Answer.","score":0,"time":"21:34:35"} try: jdata = json.loads(resp.text) result = jdata['result'] score = jdata['score'] time = jdata['time'] color = "red" if result == "Answer Correct.": color = "green" print( termcolor.colored(result, color) + '\n' + "得分:" + termcolor.colored(str(score), color) + '\n' + "提交时间:" + termcolor.colored(time, color)) except: PrintUtil.error('oops!提交出错了,请重新提交. *_*.')
def saveContestInfo(self, cid): PrintUtil.info('正在获取比赛信息...') resp = RequestUtil.doGet(AojApi.getUrl('contest')) jdata = json.loads(resp.text) datalist = jdata.get('list') ctype = '1' # 类型 ...1代表c 0表示java cpass = '******' # 是否需要密码 0为不需要 # 根据比赛id查找比赛类型 for data in datalist: if str(data['id']) == cid: ctype = data['isjava'] break # 判断是否需要密码 data = {'id': cid, 'type': ctype} needPasswordTest = RequestUtil.doGet(AojApi.getUrl('problems'), data) # Struts Problem Report页面报错编码不是utf-8 if 'ISO-8859-1' in needPasswordTest.encoding: PrintUtil.error('没有该比赛 -_-\"') return try: # 如果不需要密码就没有这个头信息会抛异常,比之前的解析内容判断速度快,虽然不雅 hasKeyContentLength = needPasswordTest.headers['Content-Length'] cpass = '******' passwd = input(termcolor.colored(u'你需要输入密码参加该比赛: ', 'green')) joindata = {'password': passwd, 'id': cid} passwdisRight = RequestUtil.doGet(AojApi.getUrl('pwdProblems'), joindata) if passwdisRight.text == 'no': PrintUtil.error('密码错误!') return except: pass # 保存比赛信息 globalVar.BASE_CONF.set('contest', 'cid', cid) globalVar.BASE_CONF.set('contest', 'ctype', ctype) globalVar.BASE_CONF.set('contest', 'cpass', cpass) with open(globalVar.BASE_PATH + 'base.conf', 'w') as fw: globalVar.BASE_CONF.write(fw) self.saveProblemList() PrintUtil.info("设置比赛成功! 'coj list -p' 显示题目列表\n")
def saveProblemList(self): PrintUtil.info('正在缓存题目信息...') pList = self.getProblemList(globalVar.BASE_CONF.get('contest', 'cid')) if not pList: PrintUtil.warn('获取题目信息失败') try: with open(globalVar.BASE_PATH + 'problemList', 'wb') as file_object: pickle.dump(pList, file_object) except Exception as e: PrintUtil.error('缓存失败 :(') print(e) pass
def aoj_cli_main(): if arg_len < 2: PrintUtil.error("参数错误") help_commond() return arg1 = sys.argv[1] # 不需要判断登录的命令 if arg1 == "help": help_commond() sys.exit(0) if arg1 == "checkout": checkout_commond() sys.exit(0) # 获取 对应oj 的操作类 try: module = __import__( globalVar.OJ_NAME + '.' + globalVar.OJ_NAME + 'Operate', globals(), locals(), [globalVar.OJ_NAME + 'Operate']) ojClass = getattr(module, globalVar.OJ_NAME.capitalize() + 'Operate') ojOperate = ojClass() except ModuleNotFoundError: # todo:添加支持oj列表 PrintUtil.error("不支持oj: " + globalVar.OJ_NAME + ", 使用 aoj checkout 'oj name' 切换") sys.exit(0) if arg1 == "login": input_name_pass_login(ojOperate) sys.exit(0) check_login(ojOperate) if arg1 == "list": list_commond(ojOperate) elif arg1 == "use": use_commond(ojOperate) elif arg1 == "show": show_commond(ojOperate) elif arg1 == "submit": submit_commond(ojOperate) elif arg1 == "passed": passed_commond(ojOperate) elif arg1 == "test": test_commond(ojOperate) else: PrintUtil.error("参数错误") help_commond()
def check_login(ojOperate): # 判断是否登录 if not os.path.exists(globalVar.BASE_PATH + ".cookies/" + globalVar.OJ_NAME): logo = "" \ " \n" \ " █████╗ ██████╗ ██╗ \n" \ " ██╔══██╗ ██╔═══██╗ ██║ \n" \ " ███████║ ██║ ██║ ██║ \n" \ " ██╔══██║ ██║ ██║ ██ ██║ \n" \ " ██║ ██║ ╚██████╔╝ ╚█████╔╝ \n" \ " ╚═╝ ╚═╝ ╚═════╝ ╚════╝ \n" \ PrintUtil.info(logo) PrintUtil.info("欢迎使用 aoj ,登录后享受丝滑刷题") PrintUtil.info("使用\'aoj help\' 查看帮助信息") sys.exit(0) elif not ojOperate.isLogin(): PrintUtil.info('登录失效') # 验证用户是否已经保存密码 try: username = globalVar.BASE_CONF.get('user', 'username') encodePassword = globalVar.BASE_CONF.get('user', 'password') # 解码 password = base64.b64decode( encodePassword.encode('utf-8')).decode('utf-8') if username == '' or password == '': input_name_pass_login(ojOperate) else: # 账号密码不为空, 尝试自动登录 PrintUtil.info('正在尝试重新登录...') isSuccess = ojOperate.login(username, password) if isSuccess: # 保存cookie RequestUtil.session.cookies.save(ignore_discard=True, ignore_expires=True) PrintUtil.success("自动登录成功!") else: globalVar.BASE_CONF.set('user', 'password', '') with open(globalVar.BASE_CONF_PATH, 'w') as fw: globalVar.BASE_CONF.write(fw) PrintUtil.error("尝试自动登录失败, 手动登录 :(") input_name_pass_login(ojOperate) except KeyboardInterrupt: pass except: PrintUtil.error("登录失败,请稍后重试 :<") sys.exit(0)