一、準備工作下載bootstrap,解壓縮到statics目錄下。下載codemirror,解壓縮到statics目錄下。在templates目錄下創建webcoding,新建模板文件codit.html。ubuntu下apt命令安裝docker、docker-compose。下載JudgeServer,git clone https://github.com/QingdaoU/JudgeServer二、簡版在線編程網頁前端先上效果圖:subprocess子進程執行代碼在codit.html中利用<div>來佈局,用<textarea>作為代碼編輯窗口。在head部分,引入bootstrap4的css文件和js文件,同時引入jquery.js配合bootstrap使用的,通過樣式來控制頁面佈局。div作為總的容器,內部包含若幹行,每行分為12列,根據需要從左右分成幾個部分,寬度由col-md-x中為個數字x(1<=x<=12)來分配。表單放在第二行,左右各空1例,代碼窗口占7列,語言選擇、標準輸入和提交按鈕占3列,標準輸出窗口在下方第三行。表單元素中定義瞭id和name,id是網頁中js要使用的,name是提交表單到後端使用的。html代碼如下:<html>
<head>
<title> 在線程序編輯器 </title>
<link rel="stylesheet" href="/static/bootstrap4/css/bootstrap.css">
</head>
<script type="text/javascript" src="/static/bootstrap4/js/bootstrap.js"></script>
<script type="text/javascript" src="/static/bootstrap4/jquery.min.js"></script>
<body>
<br><br>
<div class='container'>
<div class='row' ><!–第一行–>
<div class='col-md-4'></div>
<div class='col-md-5'> <font size='7' > 在線編程簡化版 </font></div>
<div class='col-md-3'></div>
</div>
<br>
<form id='frm_coding' method='POST'>
<div class='row'><!–第二行,表單–>
<div class='col-md-1'></div>
<div class='col-md-7'>
<textarea id='src_coding' name='src_coding' style='width:600px;height:450px;'> </textarea>
</div>
<div class='col-md-3'>
<select id='language' name='language'>
<option value='text/x-csrc'>c</option>
<option value='text/x-python' selected="selected">python</option>
<option value='text/x-c++src'>c++</option>
<option value='text/x-java'>java</option>
</select><br>
<font size='6' color='green'> input </font><br>
<textarea id='cin_coding' name='cin_coding' style='width:200px;height:300px;'></textarea>
<br><br>
<button class='btn btn-primary' id='sub_coding' type='button' onclick='up_data()'>調試運行</button>
</div>
<div class='col-md-1'></div>
</div>
</form>
<div class='row'><!–第三行,輸出運行結果–>
<div class='col-md-1'></div>
<div class='col-md-10'>
<font size='6' color='green'> output </font><br><br>
<textarea id='cout_coding' name='cout_coding' style="border:1px solid gray;width:100%; height:150px;overflow-y:auto"></textarea>
</div>
<div class='col-md-1'></div>
</div>
</div>
</body>
</html>表單提交采用瞭ajax局部提交,提交代碼如下:<script type="text/javascript">
function up_data(){
udata = $('#frm_coding').serializeArray();
$.ajax({
type: 'POST',
url: '/codej/',
data:JSON.stringify(udata),
contentType: 'application/json',
dataType: 'json',
success: function(data){
$("#cout_coding").text(data);
console.log(data);
},
error: function(data){
$("#cout_coding").text(data);
},
});
}
</script>上述代碼提交表單後,返回的信息更新到頁面中最下面的cout_conding文本框中。有關ajax()詳情參見https://www.cnblogs.com/tylerdonet/p/3520862.html 。三、後端編譯運行代碼在webcoding中的views.py中添加兩個執行代碼的視圖:def codejudge(request): 及def codejudgeserver(request):,在almond中的urls.py中添加三個路由地址:# webcoding的路由
path('codev/', codevelop, name='codev'),
path('codej/', codejudge, name='codej'),
path('codejs/', codejudgeserver, name='codejs'),這裡是把所有的路由放在主應用的urls.py文件中的,與大多數的django教程中不同 —— 在各自的應用中有urls.py文件放置各自應用的路由,在主應用的urls.py文件中包含其他應用的urls.py文件。下面實現視圖codejudge,前後端是采用json格式來傳遞數據的。下面代碼有詳細解釋#簡化版 後端 代碼編譯 運行
def codejudge(request):
#首先獲取表單數據udata,對數據整理一下放入字典x中
udata = json.loads(request.body)
print(udata)
x = dict()
for val in udata:
x[str(val['name'])] = val['value']
print(x)
result={}
#獲取表單數據也可以用request.POST來,看情況定
#根據選用的語言組織不同的編譯命令和運行命令
if x['language'] == 'text/x-csrc':
fsource = open('test.c','w')
fsource.write(x['src_coding'])
fsource.close()
fstdin = open('stdin.txt','w')
fstdin.write(x['cin_coding'])
fstdin.close()
compile_cmd ='gcc test.c -o test.so'
run_cmd='./test.so < stdin.txt'
elif x['language'] == 'text/x-c++src':
fsource = open('test.cpp','w')
fsource.write(x['src_coding'])
fsource.close()
fstdin = open('stdin.txt','w')
fstdin.write(x['cin_coding'])
fstdin.close()
compile_cmd ='g++ test.cpp -o test.so'
run_cmd='./test.so < stdin.txt'
elif x['language'] == 'text/x-java':
fsource = open('main.java','w')
fsource.write(x['src_coding'])
fsource.close()
fstdin = open('stdin.txt','w')
fstdin.write(x['cin_coding'])
fstdin.close()
compile_cmd ='javac main.java '
run_cmd='java main'
elif x['language'] == 'text/x-python':
fsource = open('test.py','w')
fsource.write(x['src_coding'])
fsource.close()
fstdin = open('stdin.txt','w')
fstdin.write(x['cin_coding'])
fstdin.close()
compile_cmd ='python -m py_compile test.py'
run_cmd='python __pycache__/test.cpython-310.pyc < stdin.txt'
#利用pyhon的子進程模塊subprocess來編譯和運行程序
out = err = ""
cominfo = subprocess.Popen(compile_cmd, shell=True,
stdin=subprocess.PIPE,stdout=subprocess.PIPE,
stderr=subprocess.PIPE,universal_newlines=True)
#等子進程編譯程序結束,並獲取信息
out, err = cominfo.communicate()
if err :
#編譯出錯,返回出錯信息
print("compile err:",err)
result = err
else:
#編譯通過,運行編譯之後的字節碼 或 可執行文件
runinfo = subprocess.Popen(run_cmd, shell=True,
stdin=subprocess.PIPE,stdout=subprocess.PIPE,
stderr=subprocess.PIPE,universal_newlines=True)
err = out = ""
#等子進程運行結束,並獲取運行結果信息
out, err = runinfo.communicate()
if err :
print('run error',err)
result = err
else:
result = out
return JsonResponse(result,safe=False)若編譯有語法錯誤,就返回錯誤信息到前端;編譯通過再運行程序,返回相應信息。信息采用json格式傳遞。這裡有一個重要問題是當提交的代碼有死循環等有害代碼時會對服務器的運行造成不利。接下來用到沙盒來隔離運行代碼,並通過服務來向前端提供編譯運行環境,避免並發造成服務器的阻塞。四、使用OnlineJudgeServer運行代碼準備工作:onlinejudge是開源的編程在線測評系統,由四個模塊組成。這裡用到其中Judger和JudgeServer,git clone https://github.com/QingdaoU/JudgeServer 下載到本地,當前目錄會有JudgeServer目錄,進入其中,復制cp ./docker-compose.example.yml ./docker-compose.yml。這是基於docker來佈署的,你的系統要安裝docker、docker-compose。使用命令docker-compose up -d,根據docker-compose.yml文件的內容來佈署,第一次運行會下載docker鏡像並運行容器,以後會隨系統一起啟動。使用命令docker ps可以查詢已經運行的docker容器。wuxc@wubuntu:~$ docker ps -f id=b4b5 –format "{{.ID}} {{.Image}} {{.Names}} "
b4b53a76634c wuxc/judgeserver:1.0.1 judgeserver_judge_server_1這是我修改JudgeServer目錄下的Dockerfile文件,docker build .來創建自己的JudgeServer鏡像,再修改docker-compose.yml相應部分。這樣可以使用編譯軟件的新版本及增加其他的編程語言。有關docker 的更多知識請查閱相關資料。如何使用:準備語言配置和調用方法,放在文件judge.py中供視圖webcoding.codejudgeserer視圖調用,部分內容如下#語言配置
py3_lang_config = {
"compile": {
"src_name": "solution.py",
"exe_name": "__pycache__/solution.cpython-38.pyc",
"max_cpu_time": 3000,
"max_real_time": 5000,
"max_memory": 128 * 1024 * 1024,
"compile_command": "/usr/bin/python3 -m py_compile {src_path}",
},
"run": {
"command": "/usr/bin/python3 {exe_path}",
"seccomp_rule": "general",
"env": ["PYTHONIOENCODING=UTF-8"] + default_env
}
}其他語言類似。定義一個調用服務的類,class JudgeClient(object):
def __init__(self, token, server_base_url):
self.token = hashlib.sha256(token.encode("utf-8")).hexdigest()
self.server_base_url = server_base_url.rstrip("/")
def _request(self, url, data=None):
kwargs = {"headers": {"X-Judge-Server-Token": self.token,
"Content-Type": "application/json"}}
if data:
kwargs["data"] = json.dumps(data)
try:
return requests.post(url, **kwargs).json()
except Exception as e:
raise JudgeError(str(e))
def ping(self):
return self._request(self.server_base_url + "/ping")
def judge(self, src, language_config, max_cpu_time, max_memory, test_case_id=None, test_case=None, spj_version=None,
spj_config=None,
spj_compile_config=None, spj_src=None, output=False):
if not (test_case or test_case_id) or (test_case and test_case_id):
raise ValueError("invalid parameter")
……這個類準備瞭一些參數,通過方法judge()來調用服務執行代碼。judge.py根據JudgeServer目錄下的client/Python下的幾個文件整理而來的。 接下來看看webcoding下的視圖函數codejudgeserver():#沙盒代碼編譯執行代碼
def codejudgeserver(request):
#獲取表單提交數據
……..
#根據表單數據選擇 語言配置
if x['language'] == "x-src-python":
lang_config = py3_lang_config
……
src = x['src_coding']
stdinput = x['cin_coding']
#調用JudgeServer,token值要與docker鏡像中一致
token = "YOUR_TOKEN_HERE"
client = JudgeClient(token=token, server_base_url="http://127.0.0.1:12357")
#測試與JudgeServer接連良好
print("ping")
print(client.ping(), "\n\n")
#執行代碼,返回結果,指定cpu memory最大值
result = client.judge(src=src, language_config=lang_config,
max_cpu_time=1000, max_memory=1024 * 1024 * 128,
test_case=[{"input": stdinput, "output": ""}], output=True)
if result['err'] != None:
y = result
else:
rusd = dict(result['data'][0])
print(rusd)
y = result['data'][0]['output']+'\n'
for k,v in rusd.items():
if k in ['cpu_time','memory','real_time']:
y += k+";"+str(v)+" "
print(y)
return JsonResponse(y, safe=False)沙盒判題系統執行代碼通過設置max_cpu_time和max_memory的值終止有害代碼的無限運行。test_case測試用例中僅用於stdinput輸入,不用output對比。對輸出的評判後續介紹,這裡能利用JudgeServer來編譯和運行程序就行瞭。五、用codemirror作代碼編輯器CodeMirror是一款在線的功能齊全的代碼編輯器,提供瞭很多流行語言的代碼高亮、自動縮進功能和符號匹配等功能。在模板codit.html中作些調整,下面兩個是使用 CodeMirror 必須引入的兩個主文件<!– 引入codemirror –>
<link rel="stylesheet" href="/static/codemirror/lib/codemirror.css">
<script type="text/javascript" src="/static/codemirror/lib/codemirror.js"></script>實現當前行背景高亮和括號匹配功能引入下列文件:<script type="text/javascript" src="/static/codemirror/addon/selection/active-line.js"></script>
<script type="text/javascript" src="/static/codemirror/addon/edit/matchbrackets.js"></script>實現某語言語法高亮顯示功能引入下列文件:<script type="text/javascript" src="/static/codemirror/mode/clike/clike.js"></script>
<script type="text/javascript" src="/static/codemirror/mode/python/python.js"></script>clike.js 實現瞭c,c++,java三個語言的語法高亮顯示功能。實現編輯器的主題樣式功能引入下列文件:<link rel="stylesheet" href="/static/codemirror/theme/monokai.css">
<link rel="stylesheet" href="/static/codemirror/theme/twilight.css">引入兩個樣式,可以切換使用。實現快捷鍵功能引入下列文件:<script type="text/javascript" src="/static/codemirror/keymap/sublime.js"></script>在模板文件templates/webcoding/codit.hml的head中添加以上各行代碼,再於up_data()函數的第一行加入:$('#src_coding').val(editor.getValue()),意思是把codemirror的編輯器editor中的代碼給表單元素id值為src_coding的文本框,以便提交。在up_data()函數的之後,添加函數modechange(value),作用是在選擇編程語言時切換相應的語法高亮。function modechange(value){
//alert(value);
txt = editor.getValue();
editor.setOption("mode", value);
editor.setValue(txt);
}在模板文件codit.html最後加入定義設置editor的代碼:<script>
// Script for the editor.
var editor = CodeMirror.fromTextArea(document.getElementById("src_coding"), {
styleActiveLine: true,
lineNumbers: true,
lineWrapping: true,
mode: 'text/x-python',
keyMap: "sublime",
autoCloseBrackets: true,
matchBrackets: true,
showCursorWhenSelecting: true,
theme: "monokai",
tabSize: 4,
indentUnit: 4,
smartIndent: true,
});
//editor.setCursor(0);
editor.setSize('auto', '480px');
</script>大功告成,看看運行效果。codemirror樣式一codemirror樣式二