Skip to content

Commit

Permalink
Add log. (#20)
Browse files Browse the repository at this point in the history
* Add log.

* Support python 3.8.

* Fix failed test in python38.
Change directory sturcture.
Add test for log.py.

* Delete Tools folder.
  • Loading branch information
ste1hi authored Jun 16, 2024
1 parent 2bf649d commit 4b1e776
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ test:
coverage report

clean-file:
rm -rf dist build *.egg-info htmlcov .coverage .mypy_cache OiRunner/__pycache__ tests/__pycache__
rm -rf dist build *.egg-info htmlcov .coverage .mypy_cache OiRunner/__pycache__ OiRunner/tools/__pycache__ tests/__pycache__
35 changes: 32 additions & 3 deletions OiRunner/BetterRunner.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
import subprocess as sp
import argparse
import sys
import os
import re
import shutil
import subprocess as sp
import sys
import time
from typing import Optional

from .submit import Submit
from .tools import log


class Functions:
Expand Down Expand Up @@ -38,6 +39,7 @@ def _modify_file(self, file_name: str, file_type: str) -> int:
flag = 0
if not os.path.exists("~tmp"):
os.mkdir("~tmp")
log.logger.info("Make ~tmp directory.")
with open(file_name, "r") as f:
for line in f:
file_path = os.path.join("~tmp", f"{i}.{file_type}")
Expand All @@ -47,15 +49,19 @@ def _modify_file(self, file_name: str, file_type: str) -> int:
else:
if not a:
print(f"error:{file_name} is empty.")
log.logger.critical(f"error:{file_name} is empty.")
shutil.rmtree("~tmp")
log.logger.info("Delete ~tmp directory.")
sys.exit(11)
with open(file_path, "w") as f:
f.write(a)
i += 1
a = ""
if not flag:
print(f"error:{file_name} is empty.")
log.logger.critical(f"error:{file_name} is empty.")
shutil.rmtree("~tmp")
log.logger.info("Delete ~tmp directory.")
sys.exit(11)

if a:
Expand Down Expand Up @@ -83,6 +89,7 @@ def _output(self, num: int, opt_file: str) -> None:

with open(opt_file, "w") as out:
out.write(a)
log.logger.info("Write output file.")

def delete_freopen(self, path: str) -> None:
'''
Expand All @@ -92,19 +99,22 @@ def delete_freopen(self, path: str) -> None:
path -- The cpp file path.
'''
if not os.path.exists(path):
log.logger.critical(f"{path} file not exists.")
raise ValueError("File not exists.")

with open(path, "r") as file:
content = file.read()

back_file_path = os.path.join(os.path.dirname(path), os.path.basename(path) + ".bak")
shutil.copy2(path, back_file_path)
log.logger.info(f"Backup file is made in {back_file_path}.")

re_match = r'freopen(\s)*\((\s)*"(\w)*\.{0,1}(\w)*"(\s)*,(\s)*"\w"(\s)*,(\s)*std\w{2,3}(\s)*\)(\s)*[,|;]'
changed_content = re.sub(re_match, "", content)

with open(path, "w") as file:
file.write(changed_content)
log.logger.info("Delete freopen.")


class BetterRunner:
Expand Down Expand Up @@ -141,6 +151,7 @@ def cmd_parse(self) -> None:
self.args.name = "./" + self.args.name + ".out"
elif sys.platform == "win32":
self.args.name = self.args.name + ".exe"
log.logger.debug(f"Output file name is set to {self.args.name}")

def compile(self) -> None:
'''
Expand All @@ -156,13 +167,16 @@ def compile(self) -> None:
compile.wait()
if compile.returncode == 0:
print("Compilation successful.")
log.logger.info("Compilation successful.")
else:
print("Compilation failed.")
log.logger.critical("Compilation failed.")
sys.exit(1)

# Can't sent Ctrl+c and get the messages.
except KeyboardInterrupt: # pragma: no cover
print("\nManually exit, wish AC~(^ v ^)")
log.logger.info("Manually exit.")
sys.exit()

def _check(self, opt_file: str, ipt_file: str, ans_file: str,
Expand Down Expand Up @@ -203,6 +217,8 @@ def _check(self, opt_file: str, ipt_file: str, ans_file: str,

if run.returncode != 0:
print(f"The return value is {run.returncode}. There may be issues with the program running.")
log.logger.error(f"The return value is {run.returncode}. There may be issues with the program running.")
log.logger.debug("Program completed.")

with open(ans_file, "r") as ans, open(opt_file, "r") as my_ans:
ans_list = [line.rstrip() for line in ans if line.rstrip()]
Expand All @@ -212,13 +228,15 @@ def _check(self, opt_file: str, ipt_file: str, ans_file: str,
print(f"Correct answer, takes {now:.5} seconds.")
else:
print("Correct answer.")
log.logger.debug("Judgement finished.")
return True
else:
if if_print:
if len(ans_list) < 10:
print(f"Standard answer:{ans_list}\nYour answer:{my_ans_list}")
else:
print("The number of answer lines is too large.")
log.logger.info("The number of answer lines is too large.")
print("Wrong answer.")
print("Error data:")
with open(ipt_file, "r") as _in:
Expand All @@ -231,16 +249,18 @@ def _check(self, opt_file: str, ipt_file: str, ans_file: str,
print(data)
else:
print("The number of data words is too large.")
log.logger.info("The number of data words is too large.")
else:
print("Wrong answer.")

log.logger.debug("Judgement finished.")
return False

def run(self) -> None:
'''Main function.'''
try:
if self.args.directgdb:
gdb = sp.Popen(["gdb", self.args.name])
log.logger.info("GDB is started.")
gdb.wait()
sys.exit()

Expand All @@ -252,8 +272,10 @@ def run(self) -> None:

if os.path.exists("~tmp"):
shutil.rmtree("~tmp")
log.logger.info("Delete ~tmp directory.")
self.func._modify_file(self.input_file, "in")
i = self.func._modify_file(self.answer_file, "ans")
log.logger.info(f"The number of test data is {i}.")

for file_num in range(1, i + 1):
out_file = os.path.join("~tmp", f"{file_num}.out")
Expand All @@ -266,12 +288,14 @@ def run(self) -> None:
print(f"#final:Accuracy{((i - flag) / i): .2%}")
self.func._output(i, self.output_file)
shutil.rmtree("~tmp")
log.logger.info("Delete ~tmp directory.")

if flag == 0 and self.args.freopen:
self.func.delete_freopen(self.args.filename + ".cpp")

if flag == 0 and self.args.remote is not None:
print("\nSubmitting to remote judge.")
log.logger.info("Submitting to remote judge.")
self.submit = Submit()
enableO2 = not self.args.disabledO2
rid = self.submit.upload_answer(self.args.remote, self.args.filename + ".cpp",
Expand All @@ -280,12 +304,14 @@ def run(self) -> None:

if self.args.gdb and flag > 0:
gdb = sp.Popen(["gdb", self.args.name])
log.logger.info("GDB is started.")
gdb.wait()

else:
if self.args.onlyinput:
with open(self.input_file, "r") as _in:
print("The file has been executed.")
log.logger.info("The file has been executed.")
run = sp.Popen([self.args.name], stdin=_in)

elif self.args.onlyoutput:
Expand All @@ -298,20 +324,23 @@ def run(self) -> None:
run.wait()
if run.returncode != 0:
print(f"The return value is {run.returncode}. There may be issues with the program running.")
log.logger.error(f"The return value is {run.returncode}. There may be issues with the program running.")

if self.args.remote is not None:
print("\nSubmitting to remote judge.")
self.submit = Submit()
enableO2 = not self.args.disabledO2
rid = self.submit.upload_answer(self.args.remote, self.args.filename + ".cpp",
enableO2, self.args.language)
log.logger.info(f"Submit rid is {rid}.")
self.submit.get_record(rid, if_show_details=self.args.print)

# Can't sent Ctrl+c and get the messages.
except KeyboardInterrupt: # pragma: no cover
if os.path.exists("~tmp"):
shutil.rmtree("~tmp")
print("\nManually exit, wish AC~(^ v ^)")
log.logger.info("Manually exit.")


def main():
Expand Down
29 changes: 27 additions & 2 deletions OiRunner/submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import requests

from .tools import log
from .util import ACCEPT, BAD_URL, CONTENT_PAT, CONTENT_TYPE, CONTENT_VALUE_PAT, MATE_TAG_PAT
from .util import PARAMS, QUESTION_URL, RECORD_URL, STATUS_CODE, SUBMIT_URL, USER_AGENT

Expand All @@ -23,20 +24,25 @@ def __init__(self) -> None:
'''
client_id = os.getenv("__client_id")
uid = os.getenv("_uid")
log.logger.debug("Get environment variable.")

if client_id is None or uid is None:
print("Missing the required environment variable.('__client_id' or '_uid')")
log.logger.critical("Missing the required environment variable.('__client_id' or '_uid')")
sys.exit(12)

self._cookies = {
'__client_id': client_id,
'_uid': uid
}
log.logger.debug(f"Set cookies {self._cookies}.")
self.session = requests.Session()
self.headers: MutableMapping[str, Union[str, bytes]] = {"User-Agent": USER_AGENT}
log.logger.debug("Get csrf token.")
self._csrf_token = self._get_csrf_token()
self.headers["Accept"] = ACCEPT
self.headers["content-type"] = CONTENT_TYPE
log.logger.debug(f"Set headers {self.headers}.")

def _get_csrf_token(self) -> str:
'''
Expand All @@ -55,32 +61,41 @@ def _get_csrf_token(self) -> str:
self.session.headers = self.headers
self.session.cookies.update(self._cookies)
html_content = self.session.get(BAD_URL).text
log.logger.debug(f"Request {BAD_URL} to get csrf-token.")
log.logger.debug(f"It returned {html_content}.")

# Get csrf-token meta tag in html file.
meta_tag_pat = re.compile(MATE_TAG_PAT)
meta_tag = re.search(meta_tag_pat, html_content)

if meta_tag is None:
print("The server returned anomalous data (which does not contain meta tag).")
log.logger.critical("The server returned anomalous data (which does not contain meta tag).")
sys.exit(21)

# Get content attribute in meta tag.
meta = meta_tag.group()
log.logger.debug(f"Meta tag is {meta}")
content_pat = re.compile(CONTENT_PAT)
content_result = re.search(content_pat, meta)
if content_result is None:
print("The server returned anomalous data (which does not contain 'content' in meta tag).")
log.logger.critical("The server returned anomalous data (which does not contain 'content' in meta tag).")
sys.exit(22)
content = content_result.group()
log.logger.debug(f"The content of meta tag is {content}")

# Get the value of content attribute.
content_value_pat = re.compile(CONTENT_VALUE_PAT)
content_value_result = re.search(content_value_pat, content)
# It must include a quote mark.
if content_value_result is None: # pragma: no cover
print("The server returned anomalous data (which does not contain anything in the content attribute of meta tag).")
log.logger.critical("The server returned anomalous data "
"(which does not contain anything in the content attribute of meta tag).")
sys.exit(23)
content_value = content_value_result.group()
log.logger.debug(f"Content value is {content_value}")

token = content_value.strip('"') # Remove quotes before and after strings

Expand Down Expand Up @@ -112,6 +127,8 @@ def upload_answer(self, question: str, file_path: str,
self.headers["x-csrf-token"] = self._csrf_token
self.headers["referer"] = question_url
url = SUBMIT_URL + question
log.logger.debug(f"Request url is {url}.")
log.logger.debug(f"Request headers are {self.headers}.")

with open(file_path, "r") as code_file:
code = code_file.read()
Expand All @@ -121,13 +138,14 @@ def upload_answer(self, question: str, file_path: str,
"lang": lang,
"code": code
}

log.logger.info("Request api.")
response = self.session.post(url=url, json=data)

try:
return str(response.json()["rid"])
except KeyError:
print(f"An error occurred while uploading the answer, with the server returning: \n {response.text}")
log.logger.critical(f"An error occurred while uploading the answer, with the server returning: \n {response.text}")
sys.exit(24)

def get_record(self, rid: str, retry_interval: float = 1,
Expand All @@ -145,27 +163,33 @@ def get_record(self, rid: str, retry_interval: float = 1,
if_show_details -- Whether to show the details of record.
'''
record_url = RECORD_URL + rid
log.logger.debug(f"Record url is {record_url}.")
self.headers.pop("referer", None)
self.headers.pop("content-type", None)

counter = 0
while True:
counter += 1
log.logger.debug(f"Retry count {counter}.")
time.sleep(retry_interval)
if retry_count != -1 and counter > retry_count:
return None

record = self.session.get(url=record_url, params=PARAMS)
log.logger.debug(f"Record is {record}.")
data = record.json()["currentData"]
judge_result: Dict[str, dict] = data["record"]["detail"]["judgeResult"]
test_case_groups: dict = data["testCaseGroup"]
case_counter = 0
finished_test = judge_result["finishedCaseCount"]

for test_case_group in test_case_groups:
case_counter += len(test_case_group)

if case_counter == judge_result["finishedCaseCount"]:
if case_counter == finished_test:
log.logger.debug("All tests are finished.")
break
log.logger.debug(f"The number of tests is {case_counter}, already have finished {finished_test}.")

total_score = 0
if type(judge_result["subtasks"]) is list:
Expand All @@ -174,6 +198,7 @@ def get_record(self, rid: str, retry_interval: float = 1,
subtasks = list(judge_result["subtasks"].values())
for subtask in subtasks:
total_score += subtask["score"]
log.logger.debug(f"Subtasks are {subtasks}.")

if if_show_details:
print("\ndetails:")
Expand Down
1 change: 1 addition & 0 deletions OiRunner/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
29 changes: 29 additions & 0 deletions OiRunner/tools/log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
import logging
import sys


def init_logging():
_logger = logging.getLogger("OiRunner")
loglevel = logging.DEBUG

# Python 3.8 didn't support encoding argument.
# See at https://docs.python.org/3/howto/logging.html#logging-to-a-file.
if sys.version_info < (3, 9):
logging.basicConfig(filename="OiRunner.log",
level=loglevel,
format="[%(asctime)s][%(levelname)s][%(name)s-%(filename)s-%(funcName)s-%(lineno)d]:%(message)s",
datefmt="%Y/%m/%d %H:%M:%S"
)
else:
logging.basicConfig(filename="OiRunner.log",
encoding="utf-8",
level=loglevel,
format="[%(asctime)s][%(levelname)s][%(name)s-%(filename)s-%(funcName)s-%(lineno)d]:%(message)s",
datefmt="%Y/%m/%d %H:%M:%S"
)

return _logger


logger = init_logging()
Loading

0 comments on commit 4b1e776

Please sign in to comment.