百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

在线编程网站开发教程:二、开发webcoding,实现在线编程

itomcoil 2024-12-23 11:08 24 浏览

一、准备工作

  1. 下载bootstrap,解压缩到statics目录下。
  2. 下载codemirror,解压缩到statics目录下。
  3. 在templates目录下创建webcoding,新建模板文件codit.html。
  4. ubuntu下apt命令安装docker、docker-compose。
  5. 下载JudgeServer,git clone https://github.com/QingdaoU/JudgeServer

二、简版在线编程网页前端

先上效果图:



在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>

大功告成,看看运行效果。

相关推荐

PS小技巧 调整命令,让人物肤色变得更加白皙 #后期修图

我们来看一下如何去将人物的皮肤变得更加的白皙。·首先选中图层,Ctrl键加J键复制一层。·打开这里的属性面板,选择快速操作删除背景,这样就会将人物进行单独的抠取。·接下来在上方去添加一个黑白调整图层,...

把人物肤色提亮的方法和技巧

PS后期调白肤色提亮照片的方法。一白遮百丑,所以对于Photoshop后期来说把人物肤色调白是一项非常重要的任务。就拿这张素材图片来说,这张素材图片人脸的肤色主要偏红、偏黄,也不够白皙,该怎样对它进行...

《Photoshop教程》把美女图片调成清爽色彩及润肤技巧

关注PS精品教程,每天不断更新~~室内人物图片一般会偏暗,人物脸部、肤色及背景会出现一些杂点。处理之前需要认真的给人物磨皮及美白,然后再整体润色。最终效果原图一、用修补工具及图章工具简单去除大一点的黑...

PS后期对皮肤进行美白的技巧

PS后期进行皮肤美白的技巧。PS后期对皮肤进行美白的技巧:·打开素材图片之后直接复制原图。·接下来直接点击上方的图像,选择应用图像命令。·在通道这里直接选择红通道,混合这里直接选择柔光,然后点击确定。...

493 [PS调色]调模特通透肤色

效果对比:效果图吧:1、光位图:2、拍摄参数:·快门:160;光圈:8;ISO:1003、步骤分解图:用曲线调整图层调出基本色调。用可选颜色调整图层调整红色、黄色、白色和灰色4种颜色的混合比例。用色彩...

先选肤色再涂面部,卡戴珊的摄影师透露:为明星拍完照后怎么修图

据英国媒体12月17日报道,真人秀明星金·卡戴珊终于承认,她把女儿小北P进了家族的圣诞贺卡,怪不得粉丝们都表示这张贺卡照得非常失败。上周,这位39岁的女星遭到了一些粉丝针对这张照片的批评,她于当地时间...

如何在PS中运用曲线复制另一张照片的色调

怎样把另一张作品的外观感觉,套用到自己的照片上?单靠肉眼来猜,可能很不容易,而来自BenSecret的教学,关键是在PS使用了两个工具,让你可以准确比较两张照片的曝光、色调与饱和度,方便你调整及复制...

PS在LAB模式下调出水嫩肤色的美女

本PS教程主要使用Photoshop使用LAB模式调出水嫩肤色的美女,教程调色比较独特。作者比较注重图片高光部分的颜色,增加质感及肤色调红润等都是在高光区域完成。尤其在Lab模式下,用高光选区调色后图...

在Photoshop图像后期处理中如何将人物皮肤处理得白皙通透

我们在人像后期处理中,需要将人物皮肤处理的白皙通透,处理方法很多,大多数都喜欢使用曲线、磨皮等进行调整,可以达到亮但是不透,最终效果往往不是很好,今天就教大家一种如何将任务皮肤处理得白皙通透,希望能帮...

PS调色自学教程:宝宝照片快速调通透,简单实用!

PS调色自学教程:宝宝照片快速调通透。·首先复制图层,然后选择进入ACR滤镜,选择曲线锁定照片的亮部,也就高光位置,其他部位补亮一点,尤其是阴影的部位补亮多一些,让画面的层次均匀一点。·然后回到基本项...

【干货】如何利用PS进行人物美化

人物图像美化在Photoshop中非常常用,Photoshop作为一款功能强大的图像处理软件,不仅可以对人像进行基本的调色、美化和修复等处理,还可以改变人物的线条和幅度,如调整脸部器官和脸型的大小、调...

教大家一种可以快速把肤色处理均匀的方法@抖音短视频

快速把肤色处理均匀的方法。今天教大家一种可以快速把肤色处理均匀的方法。像这张照片整体肤色走紫红色,但是局部偏黄缘处理起来非常的麻烦。其实我们只需要新建空白图层,图层混合模式更改为颜色,再选择画笔工具把...

PS调色教程 利用RAW调出干净通透的肤色

要么不发,要么干货。后期教程来噜~用RAW调出干净通透的肤色。这次终于不会原片比PS后好看了吧。如果你依然这么觉得,请不要残忍的告诉我这个事实,泪谢TAT)附送拍摄花絮,感谢各位的支持更多风格请关注m...

photoshop后期皮肤变白的技巧

PS后期皮肤变白的技巧。1.PS后期让皮肤变白的方法有很多种,接下来教你一种非常简单容易上手的方法。2.打开素材图片之后,直接在小太极下拉框的位置添加一个纯色调整图层,颜色设置一个纯白色,点击...

Photoshop调出人物的淡雅粉嫩肤色教程

本教程主要使用Photoshop调出人物的淡雅粉嫩肤色教程,最终的效果非常的通透迷人,下面让我们一起来学习.出自:86ps效果图:原图:1、打开原图复制一层。2、用Topaz滤镜磨皮(点此下载)。3、...