跳转至

工作空间

工作区允许学生通过浏览器内前端(例如 VS Code 和 JupyterLab)在持久远程容器中工作。远程容器由讲师配置,为每个问题提供自定义、统一的环境。工作区问题与标准 ZJUI-Learn 自动评分管道集成。

目录结构

questions
|
+-- my_ungraded_workspace     # for an ungraded workspace question
|   +-- info.json             # metadata for my_ungraded_workspace
|   +-- question.html         # HTML template for my_ungraded_workspace
|   +-- server.py             # secret server-side code for my_ungraded_workspace (optional)
|   |
|   +-- clientFilesQuestion   # files accessible to the client web browser (optional)
|   |   `-- fig1.png
|   |
|   `-- workspace             # copied into the student's workspace container home dir (optional)
|       +-- .bashrc
|       +-- starter_code.h
|       `-- starter_code.c
|
`-- my_autograded_workspace   # for an externally graded workspace question
    +-- info.json             # metadata for my_autograded_workspace
    +-- question.html         # HTML template for my_autograded_workspace
    +-- server.py             # secret server-side code for my_autograded_workspace (optional)
    |
    +-- clientFilesQuestion   # files accessible to the client web browser (optional)
    |   `-- fig1.png
    |
    +-- tests                 # external grading files (see other doc)
    |   +-- correct_answer.c
    |   `-- test_run.py
    |
    `-- workspace             # copied into the student's workspace container home dir (optional)
        +-- .bashrc
        +-- starter_code.h
        `-- starter_code.c

设置

info.json

问题的 info.json 应设置 singleVariantworkspaceOptions 属性:

  • "singleVariant": true 将防止学生工作空间因生成新变体而重置
    • 请注意,Staff view 中仍会生成新的变体
  • workspaceOptions 包含以下属性:
    • image:为 IDE 提供服务并包含所需编译器、调试器等的 Docker Hub 映像。
    • gradedFiles(可选,默认无):保存提交时将从工作区容器复制出的文件路径列表(相对于 home 路径)。然后,这些文件可用于评分(自动评分或手动评分)、在提交面板中预览,并包含在教师提交下载中。文件可以位于子目录中,但必须显式列出文件(例如 dir/file.txt)或使用通配符(例如 dir/*)。如果文件位于子目录中,则将在自动评分器内重建该文件的相对路径。允许使用通配符(例如,您可以指定 dir/*.c),并且将匹配工作区中与其匹配的任何文件。带有通配符的路径被认为是可选的。支持以下通配符:
      • * matches everything except path separators and hidden files (names starting with .).
      • ** can be used to identify files in all subdirectories of the workspace (e.g., **/*.py will copy the files with .py extension in the home directory and in all its subdirectories).
      • ? matches any single character except path separators.
      • [seq] matches any character in seq.
    • args(可选,默认无):传递给 Docker 映像的命令行参数。它可以是一个字符串(例如,"--auth none")或字符串数​​组(例如,["--auth", "none"])。
    • enableNetworking(可选,默认 false):是否允许工作区连接到公共互联网。默认情况下禁用此功能,以使安全、隔离的执行成为默认行为。在本地开发模式下运行 ZJUI-Learn 时,不强制执行此限制。强烈建议考试问题使用默认设置(无网络),因为网络访问可用于作弊。仅在作业问题时启用联网,并且仅在严格要求时启用,例如从互联网下载数据。
    • environment(可选,默认 {}):在工作区容器内设置的环境变量。使用 {"VAR": "value", ...} 设置变量,并使用 {"VAR": null} 取消设置变量(null 周围没有引号)。默认情况下,ZJUI-Learn 在所有工作区中包含以下环境变量:
      • WORKSPACE_BASE_URL: the base URL for the workspace container, which can be used to construct URLs for API requests to the workspace container.
      • WORKSPACE_NETWORKING_DISABLED: set when the workspace has networking disabled. This can be used to conditionally enable or disable features in the workspace based on its ability to access the internet.

homeportrewriteUrl 属性也可用,但它们通常在 Docker 映像标签中设置,而不是在 info.json 中设置。有关更多信息,请参阅自定义工作区图像

对于未分级的工作区,完整的 info.json 文件应类似于:

info.json
{
  "uuid": "...",
  "title": "...",
  "topic": "...",
  "tags": ["..."],
  "type": "v3",
  "singleVariant": true,
  "workspaceOptions": { "image": "prairielearn/workspace-vscode-python" }
}

对于外部分级工作区,完整的 info.json 文件应类似于:

info.json
{
  "uuid": "...",
  "title": "...",
  "topic": "...",
  "tags": ["..."],
  "type": "v3",
  "singleVariant": true,
  "workspaceOptions": {
    "image": "prairielearn/workspace-vscode-cpp",
    "gradedFiles": ["starter_code.h", "starter_code.c", "docs/*.txt"]
  },
  "gradingMethod": "External",
  "externalGradingOptions": {
    "image": "..."
  }
}

question.html

应使用工作区元素 <pl-workspace>Open workspace 按钮包含在所有工作区问题中。

对于未分级的工作区,最小的 question.html 应类似于:

<pl-question-panel>
  This is a minimal workspace question.
  <pl-workspace></pl-workspace>
</pl-question-panel>

对于外部评分工作区,工作区提交面板 <pl-submission-panel> 应包含文件预览元素 <pl-file-preview>。这将使学生不仅可以预览提交的文件,还可以接收文件提交错误消息。

外部分级工作空间的最小 question.html 应类似于:

<pl-question-panel>
  This is a minimal workspace question with external grading.
  <pl-external-grader-variables params-name="names_from_user"></pl-external-grader-variables>
  <pl-workspace></pl-workspace>
</pl-question-panel>

<pl-submission-panel>
  <pl-external-grader-results></pl-external-grader-results>
  <pl-file-preview></pl-file-preview>
</pl-submission-panel>

在工作区主目录中创建文件

工作区问题可以选择在常规 ZJUI-Learn 问题目录结构 中包含 workspace/ 子目录。如果此 workspace/ 子目录存在,则其内容将被复制到学生工作区容器的主目录中,如相应工作区映像所指定。

使用工作区的问题也可以是随机的,即包括包含随机和动态内容的文件。这可以通过两种方式完成:使用基于 Mustache 的模板文件或使用问题目录中的 server.py 文件。对于模板文件,工作区问题可以选择在常规问题目录结构中包含 workspaceTemplates/ 子目录。内容将被复制到学生工作区容器的主目录中,就像 workspace/ 目录一样。但是,此目录中的文件可能包含 Mustache 标签(例如 {{params.value}}),该标签将替换为 server.py 设置的等效值。文件名可以选择包含 .mustache 扩展名,文件在呈现给学生之前将被重命名。 workspaceTemplates/ 中的文件将保留其执行权限(例如,如果您在课程存储库中使用 chmod +x script.sh,它将在工作区中保持可执行状态)。例如,如果server.pydata["params"]["starting_value"]设置为17,则如果workspaceTemplates内部的文件main.py.mustache具有以下内容:

# ...
starting_value = {{params.starting_value}}
# ...

然后,一个名为 main.py 的文件将呈现给学生,其中包含渲染的内容:

# ...
starting_value = 17
# ...

对于更微调的随机文件,还可以在 server.py 中设置 _workspace_files 参数,其中包含要在工作区主目录中创建的潜在动态文件的数组。数组的每个元素必须包含一个 name 属性,其中包含文件名(可以包含带目录的路径)以及以下内容之一:

  • contents 属性,包含文件的内容。
  • questionFileserverFilesCourseFile 属性,分别指向问题目录或课程的 serverFilesCourse 目录中的现有文件。

此外,每个元素可以可选地包括:

  • 用于设置文件权限的 mode 属性。必须是 0o755(对于可执行文件)或 0o644(对于非可执行文件)。

例如:

def generate(data):

    # Generate 1000 random bytes
    random_binary = os.urandom(1000)
    # Generate 1000 random printable ASCII characters, ending with a line break
    random_text = "".join(random.choices(string.printable, k=1000)) + "\n"

    data["params"]["_workspace_files"] = [
        # By default, `contents` is interpreted as regular text
        {"name": "static.txt", "contents": "test file with data\n"},
        # The contents can be dynamic
        {"name": "dynamic.txt", "contents": random_text},
        # If the name contains a path, the necessary directories are created
        {"name": "path/with/long/file/name.txt", "contents": random_text},
        # Binary data must be encoded using hex or base64, and the encoding must be provided
        {
            "name": "binary1.bin",
            "contents": random_binary.hex(),
            "encoding": "hex",
        },
        {
            "name": "binary2.bin",
            "contents": base64.b64encode(random_binary).decode(),
            "encoding": "base64",
        },
        # A question file can also be added by using its path in the question instead of its contents
        {"name": "provided.txt", "questionFile": "clientFilesQuestion/provided.txt"},
        # A file can also be added by using its path in serverFilesCourse
        {"name": "course.txt", "serverFilesCourseFile": "data.txt"},
        # To make an empty file, set `contents` to None or an empty string
        {"name": "empty.txt", "contents": None},
        # To make an executable file, set the mode to 0o755
        {
            "name": "run_tests.sh",
            "contents": "#!/bin/bash\necho 'Running tests...'\n",
            "mode": 0o755,
        },
    ]

默认情况下,contents 应该是 UTF-8 格式的字符串。要提供二进制内容,必须使用 base64 或十六进制对值进行编码,如上面的示例所示。在这种情况下,还必须提供 encoding 属性。必须准确提供 questionFileserverFilesCourseFilecontents 之一。如果需要空文件,则 contents 可以设置为 None 或空字符串。

如果一个文件名出现在多个位置,则以下优先顺序生效:

  • 来自_workspace_files的动态内容具有最高优先级;
  • 接下来考虑workspaceTemplates/目录中的文件;
  • workspace/ 目录中的文件被视为最后。

维护的工作区图像

ZJUI-Learn 提供并维护以下工作区映像:

自定义工作区图像

如果您需要安装上述图像的默认版本中不存在的特定依赖项供学生使用,您可以构建自定义工作区映像。如果您想使用上面不支持的特定的基于浏览器的编辑器,您还可以创建和构建您自己的自定义工作区图像。

如果您要创建的工作区映像不是从上述维护的映像之一派生的,则必须确保它响应公开端口上的常规 HTTP 请求,并且将其配置为以 UID 1001 的用户身份运行。工作区映像必须在无头模式下运行,即没有任何 GUI。强烈建议您在映像中包含以下 Dockerfile labels

  • com.prairielearn.workspace.port:Docker 映像内工作区应用程序使用的公开端口号。它必须响应 HTTP 请求。
  • com.prairielearn.workspace.rewrite-url:是否重写 URL,以便工作区容器将所有请求视为源自 / ("true") 或 /pl/workspace/<workspace_id>/container/ ("false")。
  • com.prairielearn.workspace.home:Docker 镜像内的主目录。这将是初始工作区文件 的基目录,以及保存提交时从工作区容器复制出的任何评分文件的基目录。

如果您无法使用这些标签,或者在特定问题中必须覆盖它们,您可以在 info.jsonworkspaceOptions 下的问题级别使用相应的设置(请参阅设置)。

  • port:覆盖图像中的com.prairielearn.workspace.port标签。
  • home:覆盖图像中的com.prairielearn.workspace.home标签。这不能用于切换正在运行的用户或其主目录。
  • rewriteUrl:覆盖图像中的com.prairielearn.workspace.rewrite-url标签。

如果您使用自己的编辑器,则必须确保它经常自动保存任何工作并将其保存到磁盘上上面指定的主目录中。我们尽一切努力确保工作区的可靠执行,但偶尔的硬件故障或其他问题可能会导致工作区意外终止。学生将能够快速重新启动他们的工作区,以在新的底层主机上启动它,但如果工作区代码不经常自动保存他们的工作,则他们的工作可能会丢失。

本地运行(在 Docker 上)

为了在本地 Docker 环境中运行工作区,docker 命令必须包含支持创建本地“同级”容器的选项。关于如何运行Docker的详细说明可以在安装说明中找到。

使用工作区进行开发(在 Docker 中)

对于开发,请按照使用本地源代码安装 中所述运行 Docker 容器,同时还将上述工作区特定的参数添加到 Docker 命令行。在容器内运行:

make dev-all

或者,您可以独立运行 make dev-workspace-hostmake dev

生产中的权限

在本地运行工作区容器时,用户/组是 docker 的默认设置,通常是 root。在生产中,工作区在 user:group 设置为 1001:1001 的情况下运行。如果工作区依赖 root 权限(例如,使用低于 1024 的端口号),那么它可能在本地工作但在生产中失败。要在本地测试工作区,请像这样运行它:

docker run -it --rm -p HOST_PORT:CLIENT_PORT --user 1001:1001 IMAGE_NAME

例如,使用JupyterLab imageexample JupyterLab工作区使用端口8080,因此可以像这样成功运行:

docker run -it --rm -p 8080:8080 --user 1001:1001 prairielearn/workspace-jupyterlab-python

告诉学生什么

强烈鼓励教师在任何正式测验或考试之前让学生接触 ZJUI-Learn 工作空间环境以及课程中使用的特定工作空间。特别是,某些工作空间的行为方式可能是学生需要注意的。教师应向学生提供以下方面的指导:

  • 如何保存他们的工作并提交。学生应该了解在工作空间环境中保存文件与在 ZJUI-Learn 本身上保存/提交文件之间的区别。特别是,学生应该清楚,工作区中保存的文件不会自动评分。
  • 工作区特定的说明。不同的工作空间环境可能包括用于保存文件、编译、测试和以其他方式使用环境的不同指令,以及可能在某些上下文中相关的不同目录结构。此外,某些工作区在特定情况下可能需要额外的操作。例如,在 Jupyter 工作区中,学生可能需要知道如何中断或重新启动内核。
  • 关于工作区开始时间的期望。在最坏的情况下,工作区可能需要几分钟的时间来启动新机器和提取图像。这在定时评估中可能尤其重要,因为工作区开始时间可能会影响学生完成作业的能力。

工作区加载故障排除

多种问题可能会阻止工作区加载,包括防病毒软件、浏览器扩展和网络问题。如果工作区加载失败,以下步骤可能会帮助您解决问题或向 ZJUI-Learn 团队提供更多信息:

  • 等待:等待加载进度指示器达到100%。如果第一次将工作区映像拉至特定主机,这可能需要几分钟的时间。即使工作区的状态更改为正在运行,缓慢的网络也可能会在加载工作区的客户端资产时导致一段时间内出现空白页面。
  • 重新启动: 如果几分钟后工作区仍未加载,请尝试通过单击顶部导航栏中的“重新启动”按钮来重新启动工作区。如上所述,给工作区几分钟的时间来加载。
  • 尝试其他浏览器:某些浏览器扩展可能会干扰工作区加载。如果您安装了其他浏览器,请尝试在该浏览器中加载工作区。还可以考虑尝试私人/隐身窗口,这将禁用大多数扩展。
  • 检查浏览器开发人员工具控制台选项卡:打开浏览器的开发人员工具并导航到“控制台”选项卡。这可能包含有助于诊断问题的错误消息。
    • 在 Chrome 或 Edge 上,您可以通过打开右上角的三点菜单,选择“更多工具”,然后选择“开发人员工具”来打开开发人员工具。
    • 在 Safari 上,您必须先打开开发者工具。在菜单栏中,单击“Safari”⇾“设置”⇾“高级”,然后选中“向 Web 开发人员显示功能”。然后,您可以通过选择“开发”⇾“显示 Web 检查器”来打开开发人员工具。
    • 在 Firefox 上,您可以通过打开右上角的 Firefox 菜单,选择“更多工具”,然后选择“Web 开发人员工具”来打开开发人员工具。
  • 检查浏览器开发人员工具网络选项卡:使用上述步骤,打开开发人员工具并导航到“网络”选项卡。此选项卡将显示浏览器发出的所有网络请求,包括工作区发出的请求。查找花费很长时间或失败的请求。您可能需要重新加载页面才能查看所有请求。如果您想向 ZJUI-Learn 团队提供更多信息,您可以将网络日志保存到 HAR 文件并发送。 注意:HAR文件可能包含敏感信息,共享时请务必小心。仅与您信任的人分享。
    • 在 Chrome 或 Edge 上,单击“网络”选项卡菜单栏上的向下箭头(当您将鼠标悬停在其上时,它会显示一个带有文本“导出 HAR...”的工具提示)并保存 HAR 文件。
    • 在 Safari 上,单击右上角的“导出”按钮并保存 HAR 文件。
    • 在 Firefox 上,单击“网络”选项卡中的设置齿轮,然后选择“全部另存为 HAR”。