From 5531f1f68d85f1b65125e5ebebf3a73cdb8586e1 Mon Sep 17 00:00:00 2001
From: Tim Daniel Metzler
Date: Wed, 26 Jul 2023 14:45:55 +0200
Subject: [PATCH] Remove authoring component and replace it with e2xauthoring
---
e2xgrader/converters/__init__.py | 4 -
e2xgrader/converters/converter.py | 28 --
e2xgrader/converters/generateexercise.py | 40 --
e2xgrader/models/__init__.py | 17 -
e2xgrader/models/assignmentmodel.py | 34 --
e2xgrader/models/basemodel.py | 21 -
e2xgrader/models/exercisemodel.py | 44 --
e2xgrader/models/presetmodel.py | 71 ---
.../presets/questions/Code (Autograded).ipynb | 92 ----
.../presets/questions/Code (Manual).ipynb | 68 ---
.../models/presets/questions/Diagram.ipynb | 73 ---
.../models/presets/questions/Freetext.ipynb | 63 ---
.../presets/questions/Multiple Choice.ipynb | 54 ---
.../presets/questions/Single Choice.ipynb | 52 ---
.../presets/questions/Upload Files.ipynb | 64 ---
.../models/presets/template/Footer.ipynb | 47 --
.../models/presets/template/Group Info.ipynb | 42 --
.../models/presets/template/Header.ipynb | 47 --
.../presets/template/Student Info.ipynb | 41 --
e2xgrader/models/taskmodel.py | 127 ------
e2xgrader/models/taskpoolmodel.py | 61 ---
e2xgrader/models/templatemodel.py | 67 ---
e2xgrader/preprocessors/authoring/__init__.py | 19 -
.../preprocessors/authoring/addtaskheader.py | 98 -----
.../preprocessors/authoring/copyfiles.py | 91 ----
.../preprocessors/authoring/copynotebooks.py | 21 -
.../preprocessors/authoring/filltemplate.py | 37 --
.../authoring/generatetaskids.py | 60 ---
.../preprocessors/authoring/makeexercise.py | 73 ---
.../preprocessors/authoring/preprocessor.py | 10 -
.../preprocessors/authoring/removeexercise.py | 19 -
.../apps/authoring/__init__.py | 3 -
.../apps/authoring/apihandlers.py | 166 -------
.../apps/authoring/authoring.py | 31 --
.../apps/authoring/handlers.py | 138 ------
.../authoring/notebookvariableextractor.py | 16 -
.../authoring/static/css/editexercise.css | 54 ---
.../apps/authoring/static/css/sidebar.css | 46 --
.../apps/authoring/static/css/taskcreator.css | 149 -------
.../apps/authoring/static/js/assignments.js | 59 ---
.../apps/authoring/static/js/base.js | 150 -------
.../apps/authoring/static/js/exercises.js | 82 ----
.../apps/authoring/static/js/makeexercise.js | 416 ------------------
.../apps/authoring/static/js/taskpools.js | 124 ------
.../apps/authoring/static/js/tasks.js | 148 -------
.../apps/authoring/static/js/templates.js | 144 ------
.../templates/authoring/assignments.tpl | 33 --
.../authoring/templates/authoring/base.tpl | 46 --
.../templates/authoring/editexercise.tpl | 95 ----
.../templates/authoring/exercises.tpl | 40 --
.../templates/authoring/tablebase.tpl | 35 --
.../templates/authoring/taskcreator.tpl | 43 --
.../templates/authoring/taskpools.tpl | 37 --
.../authoring/templates/authoring/tasks.tpl | 43 --
.../templates/authoring/templates.tpl | 41 --
.../server_extensions/teacher/teacher.py | 2 +-
pyproject.toml | 2 +-
57 files changed, 2 insertions(+), 3726 deletions(-)
delete mode 100644 e2xgrader/converters/__init__.py
delete mode 100644 e2xgrader/converters/converter.py
delete mode 100644 e2xgrader/converters/generateexercise.py
delete mode 100644 e2xgrader/models/__init__.py
delete mode 100644 e2xgrader/models/assignmentmodel.py
delete mode 100644 e2xgrader/models/basemodel.py
delete mode 100644 e2xgrader/models/exercisemodel.py
delete mode 100644 e2xgrader/models/presetmodel.py
delete mode 100644 e2xgrader/models/presets/questions/Code (Autograded).ipynb
delete mode 100644 e2xgrader/models/presets/questions/Code (Manual).ipynb
delete mode 100644 e2xgrader/models/presets/questions/Diagram.ipynb
delete mode 100644 e2xgrader/models/presets/questions/Freetext.ipynb
delete mode 100644 e2xgrader/models/presets/questions/Multiple Choice.ipynb
delete mode 100644 e2xgrader/models/presets/questions/Single Choice.ipynb
delete mode 100644 e2xgrader/models/presets/questions/Upload Files.ipynb
delete mode 100644 e2xgrader/models/presets/template/Footer.ipynb
delete mode 100644 e2xgrader/models/presets/template/Group Info.ipynb
delete mode 100644 e2xgrader/models/presets/template/Header.ipynb
delete mode 100644 e2xgrader/models/presets/template/Student Info.ipynb
delete mode 100644 e2xgrader/models/taskmodel.py
delete mode 100644 e2xgrader/models/taskpoolmodel.py
delete mode 100644 e2xgrader/models/templatemodel.py
delete mode 100644 e2xgrader/preprocessors/authoring/__init__.py
delete mode 100644 e2xgrader/preprocessors/authoring/addtaskheader.py
delete mode 100644 e2xgrader/preprocessors/authoring/copyfiles.py
delete mode 100644 e2xgrader/preprocessors/authoring/copynotebooks.py
delete mode 100644 e2xgrader/preprocessors/authoring/filltemplate.py
delete mode 100644 e2xgrader/preprocessors/authoring/generatetaskids.py
delete mode 100644 e2xgrader/preprocessors/authoring/makeexercise.py
delete mode 100644 e2xgrader/preprocessors/authoring/preprocessor.py
delete mode 100644 e2xgrader/preprocessors/authoring/removeexercise.py
delete mode 100644 e2xgrader/server_extensions/apps/authoring/__init__.py
delete mode 100644 e2xgrader/server_extensions/apps/authoring/apihandlers.py
delete mode 100644 e2xgrader/server_extensions/apps/authoring/authoring.py
delete mode 100644 e2xgrader/server_extensions/apps/authoring/handlers.py
delete mode 100644 e2xgrader/server_extensions/apps/authoring/notebookvariableextractor.py
delete mode 100644 e2xgrader/server_extensions/apps/authoring/static/css/editexercise.css
delete mode 100644 e2xgrader/server_extensions/apps/authoring/static/css/sidebar.css
delete mode 100644 e2xgrader/server_extensions/apps/authoring/static/css/taskcreator.css
delete mode 100644 e2xgrader/server_extensions/apps/authoring/static/js/assignments.js
delete mode 100644 e2xgrader/server_extensions/apps/authoring/static/js/base.js
delete mode 100644 e2xgrader/server_extensions/apps/authoring/static/js/exercises.js
delete mode 100644 e2xgrader/server_extensions/apps/authoring/static/js/makeexercise.js
delete mode 100644 e2xgrader/server_extensions/apps/authoring/static/js/taskpools.js
delete mode 100644 e2xgrader/server_extensions/apps/authoring/static/js/tasks.js
delete mode 100644 e2xgrader/server_extensions/apps/authoring/static/js/templates.js
delete mode 100644 e2xgrader/server_extensions/apps/authoring/templates/authoring/assignments.tpl
delete mode 100644 e2xgrader/server_extensions/apps/authoring/templates/authoring/base.tpl
delete mode 100644 e2xgrader/server_extensions/apps/authoring/templates/authoring/editexercise.tpl
delete mode 100644 e2xgrader/server_extensions/apps/authoring/templates/authoring/exercises.tpl
delete mode 100644 e2xgrader/server_extensions/apps/authoring/templates/authoring/tablebase.tpl
delete mode 100644 e2xgrader/server_extensions/apps/authoring/templates/authoring/taskcreator.tpl
delete mode 100644 e2xgrader/server_extensions/apps/authoring/templates/authoring/taskpools.tpl
delete mode 100644 e2xgrader/server_extensions/apps/authoring/templates/authoring/tasks.tpl
delete mode 100644 e2xgrader/server_extensions/apps/authoring/templates/authoring/templates.tpl
diff --git a/e2xgrader/converters/__init__.py b/e2xgrader/converters/__init__.py
deleted file mode 100644
index 51ac4676..00000000
--- a/e2xgrader/converters/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from .converter import Converter
-from .generateexercise import GenerateExercise
-
-__all__ = ["Converter", "GenerateExercise"]
diff --git a/e2xgrader/converters/converter.py b/e2xgrader/converters/converter.py
deleted file mode 100644
index c4c890ab..00000000
--- a/e2xgrader/converters/converter.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from traitlets import List
-from traitlets.config import Config, LoggingConfigurable
-from traitlets.utils.importstring import import_item
-
-
-class Converter(LoggingConfigurable):
- preprocessors = List([], help="List of preprocessors for the converter")
-
- def __init__(self, config=None):
- with_default_config = self.default_config()
- if config:
- with_default_config.merge(config)
- self.init_preprocessors()
-
- def init_preprocessors(self):
- self._preprocessors = []
- for preprocessor in self.preprocessors:
- if isinstance(preprocessor, type):
- self._preprocessors.append(preprocessor())
- else:
- self._preprocessors.append(import_item(preprocessor)())
-
- def default_config(self):
- return Config()
-
- def convert(self, resources):
- for preprocessor in self._preprocessors:
- preprocessor.preprocess(resources)
diff --git a/e2xgrader/converters/generateexercise.py b/e2xgrader/converters/generateexercise.py
deleted file mode 100644
index 3b02618c..00000000
--- a/e2xgrader/converters/generateexercise.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import tempfile
-
-from traitlets import List
-
-from ..preprocessors.authoring import (
- AddTaskHeader,
- CopyFiles,
- CopyNotebooks,
- FillTemplate,
- GenerateTaskIDs,
- MakeExercise,
- RemoveExercise,
-)
-from .converter import Converter
-
-
-class GenerateExercise(Converter):
- preprocessors = List(
- [
- RemoveExercise,
- CopyNotebooks,
- FillTemplate,
- CopyFiles,
- GenerateTaskIDs,
- AddTaskHeader,
- MakeExercise,
- ]
- )
-
- def __init__(self, coursedir, config=None):
- super(GenerateExercise, self).__init__(config=config)
- self.coursedir = coursedir
-
- def convert(self, resources):
- with tempfile.TemporaryDirectory() as tmp:
- resources["tmp_dir"] = tmp
- resources["course_prefix"] = self.coursedir.root
- resources["source_dir"] = self.coursedir.source_directory
- for preprocessor in self._preprocessors:
- preprocessor.preprocess(resources)
diff --git a/e2xgrader/models/__init__.py b/e2xgrader/models/__init__.py
deleted file mode 100644
index 61da332f..00000000
--- a/e2xgrader/models/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from .assignmentmodel import AssignmentModel
-from .basemodel import BaseModel
-from .exercisemodel import ExerciseModel
-from .presetmodel import PresetModel
-from .taskmodel import TaskModel
-from .taskpoolmodel import TaskPoolModel
-from .templatemodel import TemplateModel
-
-__all__ = [
- "TaskModel",
- "TemplateModel",
- "TaskPoolModel",
- "AssignmentModel",
- "ExerciseModel",
- "PresetModel",
- "BaseModel",
-]
diff --git a/e2xgrader/models/assignmentmodel.py b/e2xgrader/models/assignmentmodel.py
deleted file mode 100644
index b7baeb64..00000000
--- a/e2xgrader/models/assignmentmodel.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import glob
-import os
-
-from traitlets import Unicode
-
-from .basemodel import BaseModel
-
-
-class AssignmentModel(BaseModel):
- directory = Unicode("source", help="The directory where assignments go.")
-
- def __get_assignment_info(self, assignment):
- return len(glob.glob(os.path.join(self.base_path(), assignment, "*.ipynb")))
-
- def list(self, **kwargs):
- if not os.path.isdir(self.base_path()):
- os.makedirs(self.base_path(), exist_ok=True)
- assignmentfolders = os.listdir(self.base_path())
- assignments = []
- for assignmentfolder in assignmentfolders:
- if assignmentfolder.startswith("."):
- continue
- exercises = self.__get_assignment_info(assignmentfolder)
- assignments.append(
- {
- "name": assignmentfolder,
- "exercises": exercises,
- "link": os.path.join(
- "taskcreator", "assignments", assignmentfolder
- ),
- }
- )
-
- return assignments
diff --git a/e2xgrader/models/basemodel.py b/e2xgrader/models/basemodel.py
deleted file mode 100644
index 5e8205a6..00000000
--- a/e2xgrader/models/basemodel.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import re
-
-from traitlets import Unicode
-from traitlets.config import LoggingConfigurable
-
-from ..utils import get_nbgrader_config
-
-
-class BaseModel(LoggingConfigurable):
- directory = Unicode(".", help="The directory of the model")
-
- def __init__(self, coursedir):
- self.coursedir = coursedir
- self.__pattern = re.compile(r"^\w+[\w\s]*\w+$")
- self.config = get_nbgrader_config()
-
- def base_path(self):
- return self.coursedir.format_path(self.directory, ".", ".")
-
- def is_valid_name(self, name):
- return self.__pattern.match(name) is not None
diff --git a/e2xgrader/models/exercisemodel.py b/e2xgrader/models/exercisemodel.py
deleted file mode 100644
index ec0fb8c0..00000000
--- a/e2xgrader/models/exercisemodel.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import glob
-import os
-import shutil
-
-from traitlets import Unicode
-
-from .basemodel import BaseModel
-
-
-class ExerciseModel(BaseModel):
- directory = Unicode("source", help="The directory where assignments go.")
-
- def get(self, **kwargs):
- return {"name": kwargs["name"], "assignment": kwargs["assignment"]}
-
- def remove(self, **kwargs):
- assignment = kwargs["assignment"]
- name = kwargs["name"]
- base_path = os.path.join(self.base_path(), assignment)
- exercise_files = os.path.join(base_path, "{}_files".format(name))
- if os.path.exists(exercise_files):
- shutil.rmtree(exercise_files)
- exercise = os.path.join(base_path, "{}.ipynb".format(name))
- if os.path.exists(exercise):
- os.remove(exercise)
-
- def list(self, **kwargs):
- assignment = kwargs["assignment"]
- base_path = os.path.join(self.base_path(), assignment)
- exercisenbs = glob.glob(os.path.join(base_path, "*.ipynb"))
- exercises = []
- for exercisenb in exercisenbs:
- name = os.path.split(exercisenb)[-1].replace(".ipynb", "")
- exercises.append(
- {
- "name": name,
- "assignment": assignment,
- "link": os.path.join(
- "taskcreator", "assignments", assignment, name
- ),
- }
- )
-
- return exercises
diff --git a/e2xgrader/models/presetmodel.py b/e2xgrader/models/presetmodel.py
deleted file mode 100644
index fc5eb528..00000000
--- a/e2xgrader/models/presetmodel.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import os
-
-import nbformat
-from traitlets import Unicode
-
-from .basemodel import BaseModel
-
-
-class PresetModel(BaseModel):
- task_preset_path = Unicode(
- os.path.join(os.path.dirname(__file__), "presets", "questions")
- ).tag(config=True)
-
- template_preset_path = Unicode(
- os.path.join(
- os.path.dirname(__file__),
- "presets",
- "template",
- )
- ).tag(config=True)
-
- extra_task_preset_path = Unicode(default_value=None, allow_none=True).tag(
- config=True
- )
-
- extra_template_preset_path = Unicode(default_value=None, allow_none=True).tag(
- config=True
- )
-
- def list_presets(self, preset_path):
- presets = []
- if not os.path.exists(preset_path):
- return presets
- for item in os.listdir(preset_path):
- if ".ipynb_checkpoints" in item:
- continue
- if os.path.isfile(os.path.join(preset_path, item)) and item.endswith(
- ".ipynb"
- ):
- presets.append(os.path.splitext(item)[0])
- return sorted(presets)
-
- def get_preset(self, preset_path, preset_name):
- path = os.path.join(preset_path, "{}.ipynb".format(preset_name))
- if os.path.isfile(path):
- nb = nbformat.read(path, as_version=4)
- return nb.cells
-
- def list_question_presets(self):
- presets = self.list_presets(self.task_preset_path)
- if self.extra_task_preset_path is not None:
- presets.extend(self.list_presets(self.extra_task_preset_path))
- return presets
-
- def get_question_preset(self, preset_name):
- if preset_name in self.list_presets(self.task_preset_path):
- return self.get_preset(self.task_preset_path, preset_name)
- elif self.extra_task_preset_path is not None:
- return self.get_preset(self.extra_task_preset_path, preset_name)
-
- def list_template_presets(self):
- presets = self.list_presets(self.template_preset_path)
- if self.extra_template_preset_path is not None:
- presets.extend(self.list_presets(self.extra_template_preset_path))
- return presets
-
- def get_template_preset(self, preset_name):
- if preset_name in self.list_presets(self.template_preset_path):
- return self.get_preset(self.template_preset_path, preset_name)
- elif self.extra_template_preset_path is not None:
- return self.get_preset(self.extra_template_preset_path, preset_name)
diff --git a/e2xgrader/models/presets/questions/Code (Autograded).ipynb b/e2xgrader/models/presets/questions/Code (Autograded).ipynb
deleted file mode 100644
index 25a8d01b..00000000
--- a/e2xgrader/models/presets/questions/Code (Autograded).ipynb
+++ /dev/null
@@ -1,92 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "nbgrader": {
- "grade": false,
- "grade_id": "task_question",
- "locked": true,
- "schema_version": 3,
- "solution": false,
- "task": false
- }
- },
- "source": [
- "## Question\n",
- "\n",
- "Please write your question here!"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "nbgrader": {
- "grade": false,
- "grade_id": "task",
- "locked": false,
- "schema_version": 3,
- "solution": true,
- "task": false
- }
- },
- "outputs": [],
- "source": [
- "### BEGIN SOLUTION\n",
- "# Answer\n",
- "\n",
- "# Please write your code answer here!\n",
- "\n",
- "### END SOLUTION"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "nbgrader": {
- "grade": true,
- "grade_id": "test_task",
- "locked": true,
- "points": 0,
- "schema_version": 3,
- "solution": false,
- "task": false
- }
- },
- "outputs": [],
- "source": [
-
- "### BEGIN HIDDEN TESTS\n",
- "# Test\n",
- "\n",
- "# Please write your code test here!\n",
- "\n",
- "### END HIDDEN TESTS"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Create Assignment",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/presets/questions/Code (Manual).ipynb b/e2xgrader/models/presets/questions/Code (Manual).ipynb
deleted file mode 100644
index ea23430e..00000000
--- a/e2xgrader/models/presets/questions/Code (Manual).ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "nbgrader": {
- "grade": false,
- "grade_id": "task_question",
- "locked": true,
- "schema_version": 3,
- "solution": false,
- "task": false
- }
- },
- "source": [
- "## Question\n",
- "\n",
- "Please write your question here!"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "nbgrader": {
- "grade": true,
- "grade_id": "task",
- "locked": false,
- "points": 0,
- "schema_version": 3,
- "solution": true,
- "task": false
- }
- },
- "outputs": [],
- "source": [
- "### BEGIN SOLUTION\n",
- "# Answer\n",
- "\n",
- "# Please write your code answer here!\n",
- "\n",
- "### END SOLUTION"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Create Assignment",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/presets/questions/Diagram.ipynb b/e2xgrader/models/presets/questions/Diagram.ipynb
deleted file mode 100644
index 3cf51c61..00000000
--- a/e2xgrader/models/presets/questions/Diagram.ipynb
+++ /dev/null
@@ -1,73 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "nbgrader": {
- "grade": false,
- "grade_id": "task_question",
- "locked": true,
- "schema_version": 3,
- "solution": false,
- "task": false
- }
- },
- "source": [
- "## Question\n",
- "\n",
- "Please write your question here!"
- ]
- },
- {
- "attachments": {
- "diagram.png": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAAAUAAAADwCAYAAABxLb1rAAAABHNCSVQICAgIfAhkiAAAAylJREFUeJzt1DEBACAMwDDAv+chY0cTBb16Z2YOQNDbDgDYYoBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZH97eBdx+cY1yAAAAAElFTkSuQmCC"
- }
- },
- "cell_type": "markdown",
- "metadata": {
- "extended_cell": {
- "type": "diagram"
- },
- "nbgrader": {
- "grade": true,
- "grade_id": "task",
- "locked": false,
- "points": 0,
- "schema_version": 3,
- "solution": true,
- "task": false
- }
- },
- "source": [
- "# Answer\n",
- "\n",
- "\n",
- "\n",
- "![diagram](attachment:diagram.png)"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Create Assignment",
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.12"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/presets/questions/Freetext.ipynb b/e2xgrader/models/presets/questions/Freetext.ipynb
deleted file mode 100644
index 1b53425a..00000000
--- a/e2xgrader/models/presets/questions/Freetext.ipynb
+++ /dev/null
@@ -1,63 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "nbgrader": {
- "grade": false,
- "grade_id": "task_question",
- "locked": true,
- "schema_version": 3,
- "solution": false,
- "task": false
- }
- },
- "source": [
- "## Question\n",
- "\n",
- "Please write your question here!"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nbgrader": {
- "grade": true,
- "grade_id": "task",
- "locked": false,
- "points": 0,
- "schema_version": 3,
- "solution": true,
- "task": false
- }
- },
- "source": [
- "## Answer\n",
- "\n",
- "Please write your answer here!"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Create Assignment",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/presets/questions/Multiple Choice.ipynb b/e2xgrader/models/presets/questions/Multiple Choice.ipynb
deleted file mode 100644
index df212192..00000000
--- a/e2xgrader/models/presets/questions/Multiple Choice.ipynb
+++ /dev/null
@@ -1,54 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "extended_cell": {
- "choice": [],
- "num_of_choices": 3,
- "type": "multiplechoice"
- },
- "nbgrader": {
- "grade": true,
- "grade_id": "task",
- "locked": false,
- "points": 0,
- "schema_version": 3,
- "solution": true,
- "task": false
- }
- },
- "source": [
- "## Multiplechoice Question\n",
- "\n",
- "- Choice 1\n",
- "- Choice 2\n",
- "- Choice 3\n",
- "\n",
- "Hint: Add the choices as list items, then select the correct answer!"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Create Assignment",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/presets/questions/Single Choice.ipynb b/e2xgrader/models/presets/questions/Single Choice.ipynb
deleted file mode 100644
index 5dbf876a..00000000
--- a/e2xgrader/models/presets/questions/Single Choice.ipynb
+++ /dev/null
@@ -1,52 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "extended_cell": {
- "type": "singlechoice"
- },
- "nbgrader": {
- "grade": true,
- "grade_id": "task",
- "locked": false,
- "points": 0,
- "schema_version": 3,
- "solution": true,
- "task": false
- }
- },
- "source": [
- "## Singlechoice Question\n",
- "\n",
- "- Choice 1\n",
- "- Choice 2\n",
- "- Choice 3\n",
- "\n",
- "Hint: Add the choices as list items, then select the correct answer!"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Create Assignment",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/presets/questions/Upload Files.ipynb b/e2xgrader/models/presets/questions/Upload Files.ipynb
deleted file mode 100644
index d9edd428..00000000
--- a/e2xgrader/models/presets/questions/Upload Files.ipynb
+++ /dev/null
@@ -1,64 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "nbgrader": {
- "grade": false,
- "grade_id": "task_question",
- "locked": true,
- "schema_version": 3,
- "solution": false,
- "task": false
- }
- },
- "source": [
- "## Question\n",
- "\n",
- "Please write your question here!"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "extended_cell": {
- "type": "attachments"
- },
- "nbgrader": {
- "grade": true,
- "grade_id": "task",
- "locked": false,
- "points": 0,
- "schema_version": 3,
- "solution": true,
- "task": false
- }
- },
- "source": [
- "# Answer"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Create Assignment",
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.12"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/presets/template/Footer.ipynb b/e2xgrader/models/presets/template/Footer.ipynb
deleted file mode 100644
index 788b7146..00000000
--- a/e2xgrader/models/presets/template/Footer.ipynb
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "nbassignment": {
- "type": "footer"
- },
- "nbgrader": {
- "grade": false,
- "grade_id": "footer",
- "locked": true,
- "schema_version": 3,
- "solution": false,
- "task": false
- }
- },
- "source": [
- "### This is a footer cell\n",
- "\n",
- "All footer cells will always appear at the bottom of the notebook"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Create Assignment",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/presets/template/Group Info.ipynb b/e2xgrader/models/presets/template/Group Info.ipynb
deleted file mode 100644
index c0665b19..00000000
--- a/e2xgrader/models/presets/template/Group Info.ipynb
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "nbassignment": {
- "type": "group_info"
- }
- },
- "outputs": [],
- "source": [
- "# Please fill in the usernames of all your team members\n",
- "\n",
- "member1 = ''\n",
- "member2 = ''"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Edit Metadata",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/presets/template/Header.ipynb b/e2xgrader/models/presets/template/Header.ipynb
deleted file mode 100644
index 66c4862f..00000000
--- a/e2xgrader/models/presets/template/Header.ipynb
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "nbassignment": {
- "type": "header"
- },
- "nbgrader": {
- "grade": false,
- "grade_id": "header",
- "locked": true,
- "schema_version": 3,
- "solution": false,
- "task": false
- }
- },
- "source": [
- "### This is a header cell\n",
- "\n",
- "It will always appear at the top of the notebook"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Create Assignment",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/presets/template/Student Info.ipynb b/e2xgrader/models/presets/template/Student Info.ipynb
deleted file mode 100644
index 9995b1ea..00000000
--- a/e2xgrader/models/presets/template/Student Info.ipynb
+++ /dev/null
@@ -1,41 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "nbassignment": {
- "type": "student_info"
- }
- },
- "outputs": [],
- "source": [
- "# Please fill in your matriculation number\n",
- "\n",
- "matriculation = ''"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Edit Metadata",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/e2xgrader/models/taskmodel.py b/e2xgrader/models/taskmodel.py
deleted file mode 100644
index 16faf6f9..00000000
--- a/e2xgrader/models/taskmodel.py
+++ /dev/null
@@ -1,127 +0,0 @@
-import os
-import shutil
-from textwrap import dedent
-
-import nbformat
-from traitlets import Unicode
-
-from ..utils.nbgrader_cells import get_valid_name
-from .basemodel import BaseModel
-
-
-class TaskModel(BaseModel):
- directory = Unicode("pools", help="The directory where the task pools go.")
-
- def new_taskbook(self, name):
- nb = nbformat.v4.new_notebook()
-
- nb.metadata["nbassignment"] = {"type": "task"}
- header = nbformat.v4.new_markdown_cell()
-
- header.source = dedent(
- """
- # {}
-
- Here you should give the general information about the task.
-
- Then add questions via the menu above.
-
- A task should be self contained.
- """.format(
- name
- )
- )
-
- header.metadata["nbgrader"] = {
- "grade_id": "{}_Header".format(get_valid_name(name)),
- "locked": True,
- "solution": False,
- "grade": False,
- "task": False,
- "schema_version": 3,
- }
-
- nb.cells = [header]
-
- return nb
-
- def new(self, **kwargs):
- name = kwargs["name"]
- pool = kwargs["pool"]
- if self.is_valid_name(name):
- path = os.path.join(self.base_path(), pool, name)
- if os.path.exists(path):
- return {
- "success": False,
- "error": f"A task with the name {name} already exists!",
- }
- else:
- base_path = os.path.join(self.base_path(), pool)
- os.makedirs(os.path.join(base_path, name, "img"), exist_ok=True)
- os.makedirs(os.path.join(base_path, name, "data"), exist_ok=True)
- filename = "{}.ipynb".format(name)
- nb = self.new_taskbook(name)
- path = os.path.join(base_path, name, filename)
- nbformat.write(nb, path)
- return {"success": True, "path": os.path.join("notebooks", path)}
- else:
- return {"success": False, "error": "Invalid name"}
-
- def remove(self, **kwargs):
- name = kwargs["name"]
- pool = kwargs["pool"]
- base_path = os.path.join(self.base_path(), pool)
- shutil.rmtree(os.path.join(base_path, name))
-
- def get(self, **kwargs):
- name = kwargs["name"]
- pool = kwargs["pool"]
- points, questions = self.__get_task_info(name, pool)
- return {
- "name": name,
- "points": points,
- "questions": questions,
- "pool": pool,
- }
-
- def list(self, **kwargs):
- pool = kwargs["pool"]
- base_path = os.path.join(self.base_path(), pool)
- if not os.path.exists(base_path):
- return []
- taskfolders = os.listdir(base_path)
- tasks = []
- for taskfolder in taskfolders:
- if taskfolder.startswith("."):
- continue
- points, questions = self.__get_task_info(taskfolder, pool)
- tasks.append(
- {
- "name": taskfolder,
- "points": points,
- "questions": questions,
- "pool": pool,
- "link": os.path.join("tree", base_path, taskfolder),
- }
- )
-
- return tasks
-
- def __get_task_info(self, task, pool):
- base_path = os.path.join(self.base_path(), pool)
- notebooks = [
- file
- for file in os.listdir(os.path.join(base_path, task))
- if file.endswith(".ipynb")
- ]
-
- points = 0
- questions = 0
-
- for notebook in notebooks:
- nb = nbformat.read(os.path.join(base_path, task, notebook), as_version=4)
- for cell in nb.cells:
- if "nbgrader" in cell.metadata and cell.metadata.nbgrader.grade:
- points += cell.metadata.nbgrader.points
- questions += 1
- return points, questions
diff --git a/e2xgrader/models/taskpoolmodel.py b/e2xgrader/models/taskpoolmodel.py
deleted file mode 100644
index 3f00221c..00000000
--- a/e2xgrader/models/taskpoolmodel.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import os
-import shutil
-
-from traitlets import Unicode
-
-from .basemodel import BaseModel
-
-
-class TaskPoolModel(BaseModel):
- directory = Unicode("pools", help="The directory where the task pools go.")
-
- def new(self, **kwargs):
- name = kwargs["name"]
- if self.is_valid_name(name):
- path = os.path.join(self.base_path(), name)
- if os.path.exists(path):
- return {
- "success": False,
- "error": f"A pool with the name {name} already exists!",
- }
- else:
- os.makedirs(path, exist_ok=True)
- return {"success": True, "path": path}
- else:
- return {"success": False, "error": "Invalid name"}
-
- def remove(self, **kwargs):
- name = kwargs["name"]
- path = os.path.join(self.base_path(), name)
- shutil.rmtree(path)
-
- def get(self, **kwargs):
- name = kwargs["name"]
- tasks = self.__get_pool_info(name)
- return {
- "name": name,
- "tasks": tasks,
- "link": os.path.join("taskcreator", "pools", name),
- }
-
- def list(self, **kwargs):
- if not os.path.isdir(self.base_path()):
- os.makedirs(self.base_path(), exist_ok=True)
- poolfolders = os.listdir(self.base_path())
- pools = []
- for poolfolder in poolfolders:
- if poolfolder.startswith("."):
- continue
- tasks = self.__get_pool_info(poolfolder)
- pools.append(
- {
- "name": poolfolder,
- "tasks": tasks,
- "link": os.path.join("taskcreator", "pools", poolfolder),
- }
- )
-
- return pools
-
- def __get_pool_info(self, name):
- return len(os.listdir(os.path.join(self.base_path(), name)))
diff --git a/e2xgrader/models/templatemodel.py b/e2xgrader/models/templatemodel.py
deleted file mode 100644
index 70c47fd9..00000000
--- a/e2xgrader/models/templatemodel.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import os
-import shutil
-
-import nbformat
-from traitlets import Unicode
-
-from ..utils.nbgrader_cells import new_read_only_cell
-from .basemodel import BaseModel
-
-
-class TemplateModel(BaseModel):
- directory = Unicode("templates", help="The directory where the templates go.")
-
- def __init__(self, course_prefix):
- super().__init__(course_prefix)
- os.makedirs(self.base_path(), exist_ok=True)
-
- def new(self, **kwargs):
- name = kwargs["name"]
- if self.is_valid_name(name):
- path = os.path.join(self.base_path(), name)
- if os.path.exists(path):
- return {
- "success": False,
- "error": f"A template with the name {name} already exists!",
- }
- else:
- self.log.info("Creating a new template")
- self.log.info(name)
- os.makedirs(os.path.join(self.base_path(), name, "img"), exist_ok=True)
- os.makedirs(os.path.join(self.base_path(), name, "data"), exist_ok=True)
- filename = "{}.ipynb".format(name)
- nb = nbformat.v4.new_notebook()
- nb.metadata["nbassignment"] = {"type": "template"}
- cell = new_read_only_cell(
- grade_id="HeaderA",
- source=(
- "### This is a header cell\n\n"
- "It will always appear at the top of the notebook"
- ),
- )
- cell.metadata["nbassignment"] = {"type": "header"}
- nb.cells = [cell]
- path = os.path.join(self.base_path(), name, filename)
- nbformat.write(nb, path)
- return {"success": True, "path": os.path.join("notebooks", path)}
- else:
- return {"success": False, "error": "Invalid name"}
-
- def remove(self, **kwargs):
- name = kwargs["name"]
- shutil.rmtree(os.path.join(self.base_path(), name))
-
- def list(self, **kwargs):
- templatefolders = os.listdir(self.base_path())
- templates = []
- for templatefolder in templatefolders:
- if templatefolder.startswith("."):
- continue
- templates.append(
- {
- "name": templatefolder,
- "link": os.path.join("tree", self.base_path(), templatefolder),
- }
- )
-
- return templates
diff --git a/e2xgrader/preprocessors/authoring/__init__.py b/e2xgrader/preprocessors/authoring/__init__.py
deleted file mode 100644
index 6e30c266..00000000
--- a/e2xgrader/preprocessors/authoring/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from .addtaskheader import AddTaskHeader
-from .copyfiles import CopyFiles
-from .copynotebooks import CopyNotebooks
-from .filltemplate import FillTemplate
-from .generatetaskids import GenerateTaskIDs
-from .makeexercise import MakeExercise
-from .preprocessor import Preprocessor
-from .removeexercise import RemoveExercise
-
-__all__ = [
- "Preprocessor",
- "RemoveExercise",
- "CopyNotebooks",
- "CopyFiles",
- "GenerateTaskIDs",
- "MakeExercise",
- "FillTemplate",
- "AddTaskHeader",
-]
diff --git a/e2xgrader/preprocessors/authoring/addtaskheader.py b/e2xgrader/preprocessors/authoring/addtaskheader.py
deleted file mode 100644
index f60762e8..00000000
--- a/e2xgrader/preprocessors/authoring/addtaskheader.py
+++ /dev/null
@@ -1,98 +0,0 @@
-import os
-from textwrap import dedent
-
-import nbformat
-
-from ...utils.nbgrader_cells import get_points, get_task_info
-from .preprocessor import Preprocessor
-
-
-class AddTaskHeader(Preprocessor):
- def get_header(self, idx, points):
- header = nbformat.v4.new_markdown_cell()
- header.metadata["nbgrader"] = {
- "grade_id": "taskheader_{}".format(idx),
- "locked": True,
- "solution": False,
- "grade": False,
- "task": False,
- "schema_version": 3,
- }
- header.source = dedent(
- """
- # Task {}
-
- **[{} Point(s)]**
- """.format(
- idx, points
- )
- ).strip()
- return header
-
- def get_sub_header(self, idx, sub_idx, points):
- header = nbformat.v4.new_markdown_cell()
- header.metadata["nbgrader"] = {
- "grade_id": "taskheader_{}_{}".format(idx, sub_idx),
- "locked": True,
- "solution": False,
- "grade": False,
- "task": False,
- "schema_version": 3,
- }
- header.source = dedent(
- """
- ## Task {}.{}
-
- **[{} Point(s)]**
- """.format(
- idx, sub_idx, points
- )
- ).strip()
- return header
-
- def add_headers(self, nb, idx):
- total_points = sum([get_points(cell) for cell in nb.cells])
- task = get_task_info(nb)
-
- if len(task["subtasks"]) < 1:
- return nb
-
- new_cells = []
- header = self.get_header(idx, total_points)
- new_cells.append(header)
- if "header" in task:
- new_cells.append(nb.cells[task["header"]])
-
- if len(task["subtasks"]) == 1:
- new_cells.extend([nb.cells[i] for i in task["subtasks"][0]])
- if "other" in task:
- new_cells.extend([nb.cells[i] for i in task["other"]])
- nb.cells = new_cells
- return nb
-
- if len(task["subtasks"]) > 1:
- for sub_idx, subtask in enumerate(task["subtasks"]):
- points = sum([get_points(nb.cells[i]) for i in subtask])
- new_cells.append(self.get_sub_header(idx, sub_idx + 1, points))
- new_cells.extend([nb.cells[i] for i in subtask])
-
- if "other" in task:
- new_cells.extend([nb.cells[i] for i in task["other"]])
- nb.cells = new_cells
- return nb
-
- def preprocess(self, resources):
- if not resources["exercise_options"]["task-headers"]:
- return
- idx = 0
- for task_dict in resources["tasks"]:
- task = os.path.join(task_dict["pool"], task_dict["task"])
- task_path = os.path.join(resources["tmp_dir"], "tasks", task)
- notebooks = [
- file for file in os.listdir(task_path) if file.endswith(".ipynb")
- ]
- for nb_file in notebooks:
- idx += 1
- task_nb = nbformat.read(os.path.join(task_path, nb_file), as_version=4)
- task_nb = self.add_headers(task_nb, idx)
- nbformat.write(task_nb, os.path.join(task_path, nb_file))
diff --git a/e2xgrader/preprocessors/authoring/copyfiles.py b/e2xgrader/preprocessors/authoring/copyfiles.py
deleted file mode 100644
index a933f129..00000000
--- a/e2xgrader/preprocessors/authoring/copyfiles.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import filecmp
-import glob
-import os
-import shutil
-
-import nbformat
-
-from .preprocessor import Preprocessor
-
-
-class CopyFiles(Preprocessor):
- def rename(self, task, old_name, new_name):
- old_file_name = os.path.split(old_name)[1]
- new_file_name = os.path.split(new_name)[1]
- for nb_file in glob.glob(os.path.join(task, "*.ipynb")):
- nb = nbformat.read(nb_file, as_version=4)
- for cell in nb.cells:
- cell.source = cell.source.replace(old_name, new_name)
- if old_file_name != new_file_name:
- cell.source = cell.source.replace(old_file_name, new_file_name)
- nbformat.write(nb, nb_file)
-
- def get_new_name(self, file, dst):
- suffix = 1
- name, extension = os.path.splitext(file)
- new_name = "{}_{}{}".format(name, suffix, extension)
- while os.path.exists(os.path.join(dst, new_name)):
- suffix += 1
- new_name = "{}_{}{}".format(name, suffix, extension)
- return new_name
-
- def get_files(self, task):
- finds = []
- for subdir in ["img", "data"]:
- for root, dirs, files in os.walk(os.path.join(task, subdir)):
- dirs[:] = [d for d in dirs if d not in [".ipynb_checkpoints"]]
- for file in files:
- finds.append(os.path.relpath(os.path.join(root, file), task))
- return finds
-
- def copyfile(self, src, dst):
- """
- Copy file
-
- Arguments:
- src -- source file
- dst -- destination file
- Returns:
- status -- True if dst does not exists or is equal to src,
- False if dst exists and differs from src.
- In this case nothing is copied
- """
- if os.path.exists(dst):
- return filecmp.cmp(src, dst)
- dirs = os.path.split(dst)[0]
- os.makedirs(dirs, exist_ok=True)
- shutil.copyfile(src, dst)
- return True
-
- def copyfiles(self, src, dst, resources):
- exercise_base = "{}_files".format(resources["exercise"])
- for file in self.get_files(src):
- src_file = os.path.join(src, file)
- dst_file = os.path.join(dst, file)
- new_name = os.path.join(exercise_base, file)
- if not self.copyfile(src_file, dst_file):
- # File with that name already exists
- renamed = self.get_new_name(file, dst)
- self.copyfile(src_file, os.path.join(dst, renamed))
- new_name = os.path.join(exercise_base, renamed)
- # Rename in notebook
- self.rename(src, file, new_name)
-
- def preprocess(self, resources):
- file_folder = os.path.join(
- resources["course_prefix"],
- resources["source_dir"],
- resources["assignment"],
- "{}_files".format(resources["exercise"]),
- )
- os.makedirs(file_folder, exist_ok=True)
-
- for task_dict in resources["tasks"]:
- task = os.path.join(task_dict["pool"], task_dict["task"])
- task_path = os.path.join(resources["tmp_dir"], "tasks", task)
- self.copyfiles(task_path, file_folder, resources)
-
- template_path = os.path.join(
- resources["tmp_dir"], "template", resources["template"]
- )
- self.copyfiles(template_path, file_folder, resources)
diff --git a/e2xgrader/preprocessors/authoring/copynotebooks.py b/e2xgrader/preprocessors/authoring/copynotebooks.py
deleted file mode 100644
index 1895ef39..00000000
--- a/e2xgrader/preprocessors/authoring/copynotebooks.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os
-import shutil
-
-from .preprocessor import Preprocessor
-
-
-class CopyNotebooks(Preprocessor):
- def preprocess(self, resources):
- for task_dict in resources["tasks"]:
- task = task_dict["task"]
- pool = task_dict["pool"]
- task = os.path.join(pool, task)
- src = os.path.join(resources["course_prefix"], self.task_path, task)
- dst = os.path.join(resources["tmp_dir"], "tasks", task)
- shutil.copytree(src, dst)
- src = os.path.join(
- resources["course_prefix"], self.template_path, resources["template"]
- )
- dst = os.path.join(resources["tmp_dir"], "template", resources["template"])
- shutil.copytree(src, dst)
- return resources
diff --git a/e2xgrader/preprocessors/authoring/filltemplate.py b/e2xgrader/preprocessors/authoring/filltemplate.py
deleted file mode 100644
index 87c8b7e9..00000000
--- a/e2xgrader/preprocessors/authoring/filltemplate.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import os
-import re
-
-import nbformat
-
-from .preprocessor import Preprocessor
-
-
-class FillTemplate(Preprocessor):
- def __init__(self):
- self.__pattern = re.compile(r"({{\s*(\w+)\s*}})")
-
- def replace(self, nb, replacements):
- replaced = nbformat.v4.new_notebook()
- variables = []
- for cell in nb.cells:
- source = cell.source
- variables = self.__pattern.findall(source)
- new_cell = cell.copy()
- for variable in variables:
- new_cell.source = new_cell.source.replace(
- variable[0], replacements[variable[1]]
- )
- replaced.cells.append(new_cell)
- return replaced
-
- def preprocess(self, resources):
- template_path = os.path.join(
- resources["tmp_dir"],
- "template",
- resources["template"],
- "{}.ipynb".format(resources["template"]),
- )
-
- template_nb = nbformat.read(template_path, as_version=4)
- template_nb = self.replace(template_nb, resources["template-options"])
- nbformat.write(template_nb, template_path)
diff --git a/e2xgrader/preprocessors/authoring/generatetaskids.py b/e2xgrader/preprocessors/authoring/generatetaskids.py
deleted file mode 100644
index 65f866b3..00000000
--- a/e2xgrader/preprocessors/authoring/generatetaskids.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import glob
-import os
-
-import nbformat
-
-from ...utils.nbgrader_cells import (
- get_task_info,
- get_valid_name,
- is_description,
- is_grade,
- is_solution,
-)
-from .preprocessor import Preprocessor
-
-
-class GenerateTaskIDs(Preprocessor):
- def generate_ids(self, nb, name):
- task = get_task_info(nb)
-
- ids = []
- suffix = ord("A")
-
- for subtask in task["subtasks"]:
- subtask_id = "{}_{}".format(name, chr(suffix))
- ids.append(subtask_id)
- suffix += 1
- tests = 0
- headers = 0
- for idx in subtask:
- cell = nb.cells[idx]
- if is_description(cell):
- cell.metadata.nbgrader.grade_id = "{}_Description{}".format(
- subtask_id, headers
- )
- headers += 1
- elif is_solution(cell):
- cell.metadata.nbgrader.grade_id = subtask_id
- elif is_grade(cell):
- cell.metadata.nbgrader.grade_id = "test_{}{}".format(
- subtask_id, tests
- )
- tests += 1
-
- if "header" in task:
- header = nb.cells[task["header"]]
- header.metadata.nbgrader.grade_id = "{}_Header".format("".join(ids))
-
- return nb
-
- def preprocess(self, resources):
- for task_dict in resources["tasks"]:
- task = os.path.join(task_dict["pool"], task_dict["task"])
- task_path = os.path.join(resources["tmp_dir"], "tasks", task)
- nb_files = glob.glob(os.path.join(task_path, "*.ipynb"))
- for nb_file in nb_files:
- nb = nbformat.read(nb_file, as_version=4)
- name = os.path.splitext(os.path.basename(nb_file))[0]
- name = get_valid_name(name)
- self.generate_ids(nb, name)
- nbformat.write(nb, nb_file)
diff --git a/e2xgrader/preprocessors/authoring/makeexercise.py b/e2xgrader/preprocessors/authoring/makeexercise.py
deleted file mode 100644
index 6e99ecd3..00000000
--- a/e2xgrader/preprocessors/authoring/makeexercise.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import os
-
-import nbformat
-from jupyter_client.kernelspec import KernelSpecManager
-
-from .preprocessor import Preprocessor
-
-
-class MakeExercise(Preprocessor):
- def new_notebook(self, resources):
- if "kernel" in resources["exercise_options"]:
- kernelspec = (
- KernelSpecManager()
- .get_kernel_spec(resources["exercise_options"]["kernel"])
- .to_dict()
- )
- return nbformat.v4.new_notebook(
- metadata={
- "kernelspec": {
- "name": resources["exercise_options"]["kernel"],
- "display_name": kernelspec["display_name"],
- }
- }
- )
- else:
- return nbformat.v4.new_notebook()
-
- def get_cell_type(self, cell):
- if ("nbassignment" in cell.metadata) and ("type" in cell.metadata.nbassignment):
- return cell.metadata.nbassignment.type
-
- def preprocess(self, resources):
- exercise = self.new_notebook(resources)
- template_path = os.path.join(
- resources["tmp_dir"],
- "template",
- resources["template"],
- "{}.ipynb".format(resources["template"]),
- )
-
- template_nb = nbformat.read(template_path, as_version=4)
- header = [
- cell
- for cell in template_nb.cells
- if self.get_cell_type(cell) in ["header", "student_info", "group_info"]
- ]
- footer = [
- cell for cell in template_nb.cells if self.get_cell_type(cell) == "footer"
- ]
-
- exercise.cells.extend(header)
-
- for task_dict in resources["tasks"]:
- task = os.path.join(task_dict["pool"], task_dict["task"])
- task_path = os.path.join(resources["tmp_dir"], "tasks", task)
- notebooks = [
- file for file in os.listdir(task_path) if file.endswith(".ipynb")
- ]
- for notebook in notebooks:
- task_nb = nbformat.read(os.path.join(task_path, notebook), as_version=4)
- exercise.cells.extend(task_nb.cells)
-
- exercise.cells.extend(footer)
-
- nbformat.write(
- exercise,
- os.path.join(
- resources["course_prefix"],
- resources["source_dir"],
- resources["assignment"],
- "{}.ipynb".format(resources["exercise"]),
- ),
- )
diff --git a/e2xgrader/preprocessors/authoring/preprocessor.py b/e2xgrader/preprocessors/authoring/preprocessor.py
deleted file mode 100644
index 04b511f7..00000000
--- a/e2xgrader/preprocessors/authoring/preprocessor.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from traitlets import Unicode
-from traitlets.config import LoggingConfigurable
-
-
-class Preprocessor(LoggingConfigurable):
- template_path = Unicode("templates")
- task_path = Unicode("pools")
-
- def preprocess(self, resources):
- raise NotImplementedError
diff --git a/e2xgrader/preprocessors/authoring/removeexercise.py b/e2xgrader/preprocessors/authoring/removeexercise.py
deleted file mode 100644
index 03c9ded1..00000000
--- a/e2xgrader/preprocessors/authoring/removeexercise.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import os
-import shutil
-
-from .preprocessor import Preprocessor
-
-
-class RemoveExercise(Preprocessor):
- def preprocess(self, resources):
- base_path = os.path.join(
- resources["course_prefix"], resources["source_dir"], resources["assignment"]
- )
- exercise_files = os.path.join(
- base_path, "{}_files".format(resources["exercise"])
- )
- if os.path.exists(exercise_files):
- shutil.rmtree(exercise_files)
- exercise_nb = os.path.join(base_path, "{}.ipynb".format(resources["exercise"]))
- if os.path.exists(exercise_nb):
- os.remove(exercise_nb)
diff --git a/e2xgrader/server_extensions/apps/authoring/__init__.py b/e2xgrader/server_extensions/apps/authoring/__init__.py
deleted file mode 100644
index 6ca25a4f..00000000
--- a/e2xgrader/server_extensions/apps/authoring/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .authoring import AuthoringApp
-
-__all__ = ["AuthoringApp"]
diff --git a/e2xgrader/server_extensions/apps/authoring/apihandlers.py b/e2xgrader/server_extensions/apps/authoring/apihandlers.py
deleted file mode 100644
index 95dbb5f9..00000000
--- a/e2xgrader/server_extensions/apps/authoring/apihandlers.py
+++ /dev/null
@@ -1,166 +0,0 @@
-import json
-import os
-
-from e2xcore import urljoin
-from jupyter_client.kernelspec import KernelSpecManager
-from nbgrader.server_extensions.formgrader.base import check_xsrf
-from tornado import web
-
-from e2xgrader.converters import GenerateExercise
-from e2xgrader.models import (
- AssignmentModel,
- ExerciseModel,
- PresetModel,
- TaskModel,
- TaskPoolModel,
- TemplateModel,
-)
-
-from ..e2xgraderapi.base import E2xApiHandler
-from .notebookvariableextractor import NotebookVariableExtractor
-
-
-class BaseApiManageHandler(E2xApiHandler):
- def initialize(self, model_cls):
- self.__model = model_cls(self.coursedir)
-
- @web.authenticated
- @check_xsrf
- def post(self, **kwargs):
- self.write(self.__model.new(**kwargs))
-
- @web.authenticated
- @check_xsrf
- def delete(self, **kwargs):
- self.__model.remove(**kwargs)
- self.write({"status": True})
-
- @web.authenticated
- @check_xsrf
- def get(self, **kwargs):
- self.write(json.dumps(self.__model.get(**kwargs)))
-
- @web.authenticated
- @check_xsrf
- def put(self, **kwargs):
- self.write(self.__model.new(**kwargs))
-
-
-class BaseApiListHandler(E2xApiHandler):
- def initialize(self, model_cls):
- self.__model = model_cls(self.coursedir)
-
- @web.authenticated
- @check_xsrf
- def get(self, **kwargs):
- self.write(json.dumps(self.__model.list(**kwargs)))
-
-
-class PresetHandler(E2xApiHandler):
- def initialize(self):
- self.__model = PresetModel(self.coursedir)
-
- def _list_template(self):
- self.write(json.dumps(self.__model.list_template_presets()))
-
- def _get_template(self):
- name = self.get_argument("name")
- self.write(json.dumps(self.__model.get_template_preset(name)))
-
- def _list_question(self):
- self.write(json.dumps(self.__model.list_question_presets()))
-
- def _get_question(self):
- name = self.get_argument("name")
- self.write(json.dumps(self.__model.get_question_preset(name)))
-
- @web.authenticated
- @check_xsrf
- def get(self):
- action = self.get_argument("action")
- preset_type = self.get_argument("type")
- handler = getattr(self, "_{}_{}".format(action, preset_type))
- handler()
-
-
-class TemplateVariableHandler(E2xApiHandler):
- @web.authenticated
- @check_xsrf
- def get(self):
- template = self.get_argument("template")
- variables = NotebookVariableExtractor().extract(
- os.path.join(
- self.url_prefix, "templates", template, "{}.ipynb".format(template)
- )
- )
- self.write(json.dumps(variables))
-
-
-class KernelSpecHandler(E2xApiHandler):
- @web.authenticated
- @check_xsrf
- def get(self):
- self.write(json.dumps(KernelSpecManager().get_all_specs()))
-
-
-class GenerateExerciseHandler(E2xApiHandler):
- @web.authenticated
- @check_xsrf
- def get(self):
- resources = json.loads(self.get_argument("resources"))
- GenerateExercise(coursedir=self.coursedir).convert(resources)
- self.write({"status": True})
-
-
-pool_regex = r"(?P[^/]+)"
-name_regex = r"(?P[^/]+)"
-assignment_regex = r"(?P[^/]+)"
-
-api_url = urljoin("e2x", "authoring", "api")
-default_handlers = [
- (urljoin(api_url, "presets"), PresetHandler),
- (
- urljoin(api_url, "assignments", "?"),
- BaseApiListHandler,
- dict(model_cls=AssignmentModel),
- ),
- (
- urljoin(api_url, "template", name_regex, "?"),
- BaseApiManageHandler,
- dict(model_cls=TemplateModel),
- ),
- (
- urljoin(api_url, "templates", "?"),
- BaseApiListHandler,
- dict(model_cls=TemplateModel),
- ),
- (
- urljoin(api_url, "pool", name_regex, "?"),
- BaseApiManageHandler,
- dict(model_cls=TaskPoolModel),
- ),
- (urljoin(api_url, "pools", "?"), BaseApiListHandler, dict(model_cls=TaskPoolModel)),
- (
- urljoin(api_url, "pools", pool_regex, "?"),
- BaseApiListHandler,
- dict(model_cls=TaskModel),
- ),
- (
- urljoin(api_url, "task", pool_regex, name_regex, "?"),
- BaseApiManageHandler,
- dict(model_cls=TaskModel),
- ),
- (
- urljoin(api_url, "exercise", assignment_regex, name_regex, "?"),
- BaseApiManageHandler,
- dict(model_cls=ExerciseModel),
- ),
- (
- urljoin(api_url, "assignments", assignment_regex, "?"),
- BaseApiListHandler,
- dict(model_cls=ExerciseModel),
- ),
- (urljoin(api_url, "templates", "variables"), TemplateVariableHandler),
- (urljoin(api_url, "kernelspec"), KernelSpecHandler),
- (urljoin(api_url, "generate_exercise"), GenerateExerciseHandler),
-]
diff --git a/e2xgrader/server_extensions/apps/authoring/authoring.py b/e2xgrader/server_extensions/apps/authoring/authoring.py
deleted file mode 100644
index 948a76ab..00000000
--- a/e2xgrader/server_extensions/apps/authoring/authoring.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import os
-
-from e2xcore import BaseApp
-from nbgrader.apps.baseapp import NbGrader
-from tornado import web
-
-from .apihandlers import default_handlers as default_api_handlers
-from .handlers import default_handlers
-
-
-class AuthoringApp(NbGrader, BaseApp):
- template_path = os.path.join(os.path.dirname(__file__), "templates")
- static_path = os.path.join(os.path.dirname(__file__), "static")
-
- def __init__(self, **kwargs):
- NbGrader.__init__(self, **kwargs)
- BaseApp.__init__(self, **kwargs)
-
- def load_app(self):
- self.log.info("Loading the e2xgrader authoring app")
- self.add_template_path(self.template_path)
- self.add_handlers(default_api_handlers)
- self.add_handlers(default_handlers)
- static_handlers = [
- (
- r"/e2x/authoring/static/(.*)",
- web.StaticFileHandler,
- {"path": self.static_path},
- ),
- ]
- self.add_handlers(static_handlers)
diff --git a/e2xgrader/server_extensions/apps/authoring/handlers.py b/e2xgrader/server_extensions/apps/authoring/handlers.py
deleted file mode 100644
index 305f6bf2..00000000
--- a/e2xgrader/server_extensions/apps/authoring/handlers.py
+++ /dev/null
@@ -1,138 +0,0 @@
-import os
-import sys
-
-from e2xcore import urljoin
-from nbgrader.server_extensions.formgrader.base import (
- BaseHandler,
- check_notebook_dir,
- check_xsrf,
-)
-from tornado import web
-
-from e2xgrader.models import ExerciseModel, TaskPoolModel, TemplateModel
-
-app_url = urljoin("e2x", "authoring", "app")
-
-
-class TaskcreatorHandler(BaseHandler):
- @web.authenticated
- @check_xsrf
- @check_notebook_dir
- def get(self):
- self.redirect(urljoin(self.base_url, app_url, "assignments"))
-
-
-class ManageAssignmentsHandler(BaseHandler):
- @web.authenticated
- @check_xsrf
- @check_notebook_dir
- def get(self):
- html = self.render(
- os.path.join("authoring", "assignments.tpl"),
- url_prefix=self.url_prefix,
- base_url=self.base_url,
- windows=(sys.prefix == "win32"),
- )
- self.write(html)
-
-
-class ManageExercisesHandler(BaseHandler):
- @web.authenticated
- @check_xsrf
- @check_notebook_dir
- def get(self, assignment):
- html = self.render(
- os.path.join("authoring", "exercises.tpl"),
- url_prefix=self.url_prefix,
- base_url=self.base_url,
- assignment=assignment,
- windows=(sys.prefix == "win32"),
- )
- self.write(html)
-
-
-class ManagePoolsHandler(BaseHandler):
- @web.authenticated
- @check_xsrf
- @check_notebook_dir
- def get(self):
- html = self.render(
- os.path.join("authoring", "taskpools.tpl"),
- url_prefix=self.url_prefix,
- base_url=self.base_url,
- windows=(sys.prefix == "win32"),
- )
- self.write(html)
-
-
-class ManageTasksHandler(BaseHandler):
- @web.authenticated
- @check_xsrf
- @check_notebook_dir
- def get(self, pool):
- html = self.render(
- os.path.join("authoring", "tasks.tpl"),
- url_prefix=self.url_prefix,
- base_url=self.base_url,
- pool=pool,
- windows=(sys.prefix == "win32"),
- )
- self.write(html)
-
-
-class ManageTemplatesHandler(BaseHandler):
- @web.authenticated
- @check_xsrf
- @check_notebook_dir
- def get(self):
- html = self.render(
- os.path.join("authoring", "templates.tpl"),
- url_prefix=self.url_prefix,
- base_url=self.base_url,
- windows=(sys.prefix == "win32"),
- )
- self.write(html)
-
-
-class EditExercisesHandler(BaseHandler):
- def initialize(self):
- self._model = ExerciseModel(self.coursedir)
-
- @web.authenticated
- @check_xsrf
- @check_notebook_dir
- def get(self, assignment, exercise):
- html = self.render(
- os.path.join("authoring", "editexercise.tpl"),
- url_prefix=self.url_prefix,
- base_url=self.base_url,
- exercise=exercise,
- assignment=assignment,
- templates=TemplateModel(self.coursedir).list(),
- pools=TaskPoolModel(self.coursedir).list(),
- windows=(sys.prefix == "win32"),
- )
- self.write(html)
-
-
-default_handlers = [
- (urljoin(app_url, "?"), TaskcreatorHandler),
- (urljoin(app_url, "assignments", "?"), ManageAssignmentsHandler),
- (
- urljoin(app_url, "assignments", r"(?P[^/]+)", "?"),
- ManageExercisesHandler,
- ),
- (urljoin(app_url, "pools", "?"), ManagePoolsHandler),
- (urljoin(app_url, "pools", r"(?P[^/]+)", "?"), ManageTasksHandler),
- (urljoin(app_url, "templates", "?"), ManageTemplatesHandler),
- (
- urljoin(
- app_url,
- "assignments",
- r"(?P[^/]+)",
- r"(?P[^/]+)",
- "?",
- ),
- EditExercisesHandler,
- ),
-]
diff --git a/e2xgrader/server_extensions/apps/authoring/notebookvariableextractor.py b/e2xgrader/server_extensions/apps/authoring/notebookvariableextractor.py
deleted file mode 100644
index b436c6a1..00000000
--- a/e2xgrader/server_extensions/apps/authoring/notebookvariableextractor.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import re
-
-import nbformat
-
-
-class NotebookVariableExtractor:
- def __init__(self):
- self.__pattern = re.compile(r"{{\s*(\w+)\s*}}")
-
- def extract(self, nb_path):
- nb = nbformat.read(nb_path, as_version=4)
- variables = []
- for cell in nb.cells:
- source = cell.source
- variables.extend(self.__pattern.findall(source))
- return variables
diff --git a/e2xgrader/server_extensions/apps/authoring/static/css/editexercise.css b/e2xgrader/server_extensions/apps/authoring/static/css/editexercise.css
deleted file mode 100644
index d1514e69..00000000
--- a/e2xgrader/server_extensions/apps/authoring/static/css/editexercise.css
+++ /dev/null
@@ -1,54 +0,0 @@
-#tasks td {
- text-align: center;
-}
-
-.mid-col {
- width: 20%;
-}
-
-.mid-col button {
- display: block;
- width: 6em;
- margin: auto;
- margin-top: 0.5em;
-}
-
-.side-col {
- width: 40%;
-}
-
-#selected-tasks {
- min-width: 100%;
- min-height: 15em;
-}
-
-#available-tasks {
- min-width: 100%;
- min-height: 15em;
-}
-
-#template-options {
- margin-top: 1em;
-}
-
-select[name="pool"] {
- margin-left: 1em;
-}
-
-.e2xtable {
- min-width: 40%;
-}
-
-.e2xtable th {
- text-align: center;
-}
-
-.btn-controls {
- display: block;
- width: 6em;
- margin: 0.1em;
-}
-
-.btn-primary a {
- text-decoration: none;
-}
diff --git a/e2xgrader/server_extensions/apps/authoring/static/css/sidebar.css b/e2xgrader/server_extensions/apps/authoring/static/css/sidebar.css
deleted file mode 100644
index 9ca066b8..00000000
--- a/e2xgrader/server_extensions/apps/authoring/static/css/sidebar.css
+++ /dev/null
@@ -1,46 +0,0 @@
-.sidebar {
- background-color: #f2f2f2;
- height: 100%;
- min-height: 100%;
- line-height: 3em;
-}
-
-.sidebar a {
- display: block;
- padding: 0.2em 0 0.2em 1em;
- margin: 0;
- text-decoration: none;
- color: #555;
- font-size: 1.5em;
-}
-
-.sidebar h3 {
- display: block;
- padding: 1.5em;
- margin: 0;
- font-size: 2em;
- background-color: #2980b9;
- color: #fff;
- margin-bottom: 1em;
-}
-
-.sidebar a:hover {
- background-color: #ddd;
-}
-
-.sidebar .active {
- background-color: #5f5f5f;
- color: #f2f2f2;
-}
-
-.sidebar .active:hover {
- background-color: #5f5f5f;
- color: #f2f2f2;
-}
-
-.body {
- padding-left: 2em;
- padding-right: 2em;
- width: 100%;
- overflow: auto;
-}
diff --git a/e2xgrader/server_extensions/apps/authoring/static/css/taskcreator.css b/e2xgrader/server_extensions/apps/authoring/static/css/taskcreator.css
deleted file mode 100644
index 563f47e1..00000000
--- a/e2xgrader/server_extensions/apps/authoring/static/css/taskcreator.css
+++ /dev/null
@@ -1,149 +0,0 @@
-body {
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-size: 13px;
- line-height: 1.42857143;
- color: #000;
- display: flex;
- position: absolute;
- margin: 0;
- height: 100%;
- width: 100%;
-}
-
-.e2xbtn {
- color: #fff;
- background-color: #2980b9;
- border: none;
- border-radius: 0.3em;
- box-shadow: 0px 0.2em 0.2px #3896d3;
- padding: 0.5em 1em 0.5em 1em;
-}
-
-.e2xbtn:hover {
- color: #fff;
- background-color: #51aae6;
- border: none;
- border-radius: 0.3em;
- box-shadow: 0px 0.2em 0px #76bceb;
-}
-
-#create_question {
- margin-left: 3em;
-}
-
-#create_question a {
- border: 1px solid #dae4e7;
- padding: 0.5em;
- background-color: #eaf4f7;
- text-decoration: none;
- margin-left: -9px;
- color: #333;
-}
-
-.e2xtable {
- border-collapse: collapse;
- border-spacing: 0;
- margin-bottom: 1em;
- border: 1px solid #ddd;
- min-width: 100%;
-}
-
-.e2xtable td {
- text-align: center;
- padding: 0.5em;
- border: 1px solid #ddd;
-}
-
-.e2xtable tr:nth-child(even) {
- background-color: white;
-}
-
-.e2xtable tr:nth-child(odd) {
- background-color: #f2f2f2;
-}
-
-.e2xtable .column2 {
- float: right;
-}
-
-.e2xtable select {
- height: 2.5em;
- width: 15em;
- border: 1px solid #bbb;
-}
-
-.e2xtable input[type="text"] {
- height: 2.5em;
- width: 15em;
-}
-
-#taskdiv {
- margin: auto;
-}
-
-.breadcrumbs li {
- display: inline-block;
-}
-
-.breadcrumbs ul {
- padding: 0em;
-}
-
-.breadcrumbs {
- padding-left: 0.5em;
- background-color: #f2f2f2;
- border-radius: 0.5em;
- line-height: 2em;
-}
-
-a {
- color: #337ab7;
- text-decoration: none;
-}
-
-a:hover {
- text-decoration: underline;
-}
-
-th:nth-child(1) {
- text-align: left;
- padding-left: 0.8em;
-}
-
-td:nth-child(1) {
- text-align: left;
- padding-left: 0.8em;
-}
-
-th {
- line-height: 3em;
-}
-
-svg {
- color: #23527c;
-}
-
-.e2xtable thead th {
- background-color: #2980b9;
- color: white;
-}
-
-.help {
- background-color: #eee;
- border-radius: 0.5em;
- padding: 0.5em;
- margin-bottom: 2em;
-}
-
-#generate-exercise {
- margin-bottom: 2em;
-}
-
-.btn a {
- text-decoration: none;
- color: #fff;
-}
-
-.btn {
- margin: 0.5em;
-}
diff --git a/e2xgrader/server_extensions/apps/authoring/static/js/assignments.js b/e2xgrader/server_extensions/apps/authoring/static/js/assignments.js
deleted file mode 100644
index 24155f20..00000000
--- a/e2xgrader/server_extensions/apps/authoring/static/js/assignments.js
+++ /dev/null
@@ -1,59 +0,0 @@
-let AssignmentUI = Backbone.View.extend({
- events: {},
-
- initialize: function () {
- this.$assignment_name = this.$el.find(".assignment-name");
- this.$number_of_exercises = this.$el.find(".number-of-exercises");
-
- this.listenTo(this.model, "sync", this.render);
- this.render();
- },
-
- render: function () {
- let name = this.model.get("name");
- this.$assignment_name.append(
- $(" ")
- .attr("href", base_url + "/e2x/authoring/app/assignments/" + name)
- .text(name)
- );
- this.$number_of_exercises.text(this.model.get("exercises"));
- },
-});
-
-function insertRow(table) {
- let row = $(" ");
- row.append($(" ").addClass("assignment-name"));
- row.append($(" ").addClass("number-of-exercises"));
- table.append(row);
- return row;
-}
-
-function loadAssignments() {
- console.log("Loading the assignments");
- let tbl = $("#main_table");
- models = new Assignments();
- views = [];
- models.loaded = false;
- models.fetch({
- success: function () {
- tbl.empty();
- models.each(function (model) {
- let view = new AssignmentUI({
- model: model,
- el: insertRow(tbl),
- });
- views.push(view);
- });
- tbl.parent().DataTable();
-
- models.loaded = true;
- },
- });
-}
-
-let models = undefined;
-let views = [];
-
-$(window).on("load", function () {
- loadAssignments();
-});
diff --git a/e2xgrader/server_extensions/apps/authoring/static/js/base.js b/e2xgrader/server_extensions/apps/authoring/static/js/base.js
deleted file mode 100644
index bec8dec9..00000000
--- a/e2xgrader/server_extensions/apps/authoring/static/js/base.js
+++ /dev/null
@@ -1,150 +0,0 @@
-let Assignment = Backbone.Model.extend({
- idAttribute: "name",
- urlRoot: base_url + "/e2x/authoring/api/assignment",
-});
-
-let Assignments = Backbone.Collection.extend({
- model: Assignment,
- url: base_url + "/e2x/authoring/api/assignments",
-});
-
-let Exercise = Backbone.Model.extend({
- idAttribute: "name",
- initialize: function (options) {
- this.urlRoot =
- base_url + "/e2x/authoring/api/exercise/" + options.assignment;
- },
-});
-
-let Exercises = Backbone.Collection.extend({
- model: Exercise,
- initialize: function (options) {
- this.url =
- base_url + "/e2x/authoring/api/assignments/" + options.assignment;
- },
-});
-
-let Pool = Backbone.Model.extend({
- idAttribute: "name",
- urlRoot: base_url + "/e2x/authoring/api/pool",
-});
-
-let Pools = Backbone.Collection.extend({
- model: Pool,
- url: base_url + "/e2x/authoring/api/pools/",
- comparator: "name",
-});
-
-let Task = Backbone.Model.extend({
- idAttribute: "name",
- initialize: function (options) {
- this.urlRoot = base_url + "/e2x/authoring/api/task/" + options.pool;
- },
-});
-
-let Tasks = Backbone.Collection.extend({
- model: Task,
- initialize: function (options) {
- this.url = base_url + "/e2x/authoring/api/pools/" + options.pool;
- },
- comparator: "name",
-});
-
-let Template = Backbone.Model.extend({
- idAttribute: "name",
- urlRoot: base_url + "/e2x/authoring/api/template",
-});
-
-let Templates = Backbone.Collection.extend({
- model: Template,
- url: base_url + "/e2x/authoring/api/templates/",
-});
-
-let BaseUI = Backbone.View.extend({
- events: {},
-
- initialize: function () {
- this.fields = [];
- },
-
- clear: function () {
- this.fields.forEach((field) => field.empty());
- },
-
- openRemoveModal: function (body, title) {
- let footer = $("
");
- footer.append(
- $(" ").addClass("btn btn-danger save").text("Delete")
- );
- footer.append(
- $(" ")
- .addClass("btn ")
- .attr("data-dismiss", "modal")
- .text("Cancel")
- );
-
- let $modal = createModal("remove-pool-modal", title, body, footer);
- let $modal_save = $modal.find("button.save");
- let that = this;
- $modal_save.click(function () {
- that.removeModel();
- $modal.modal("hide");
- });
- },
-
- removeModel: function () {
- this.model.destroy();
- this.remove();
- dataTable.row(this.$el).remove().draw();
- },
-});
-
-function insertRow(table, classes) {
- let row = $(" ");
- classes.forEach(function (cls) {
- row.append($(" ").addClass(cls));
- });
- table.append(row);
- return row;
-}
-
-function getNewModalElements(element_name) {
- let body = $("
").append(
- $("
").text(
- "Please specify the name of the new " +
- element_name.toLowerCase() +
- ". Names can consist of characters, digits, spaces and underscores."
- )
- );
- let table = $("").addClass("table table-striped form-table");
- let tablebody = $(" ");
- body.append(table);
- table.append(tablebody);
- let name = $(" ");
- tablebody.append(name);
- name.append($(" ").addClass("align-middle").text("Name"));
- name.append(
- $(" ").append(
- $(" ")
- .addClass("modal-name")
- .attr("pattern", "[A-Za-z0-9]+")
- .attr("type", "text")
- )
- );
- let footer = $("
");
- footer.append(
- $(" ")
- .addClass("btn btn-primary save")
- .text("Add " + element_name)
- );
- footer.append(
- $(" ")
- .addClass("btn btn-danger")
- .attr("data-dismiss", "modal")
- .text("Cancel")
- );
- return {
- body: body,
- footer: footer,
- };
-}
diff --git a/e2xgrader/server_extensions/apps/authoring/static/js/exercises.js b/e2xgrader/server_extensions/apps/authoring/static/js/exercises.js
deleted file mode 100644
index e65a4217..00000000
--- a/e2xgrader/server_extensions/apps/authoring/static/js/exercises.js
+++ /dev/null
@@ -1,82 +0,0 @@
-let ExerciseUI = BaseUI.extend({
- events: {},
-
- initialize: function () {
- this.$exercise_name = this.$el.find(".exercise-name");
- this.$remove_exercise = this.$el.find(".remove-exercise");
-
- this.listenTo(this.model, "sync", this.render);
- this.render();
- },
-
- render: function () {
- let name = this.model.get("name");
- this.$exercise_name.append(
- $(" ")
- .attr(
- "href",
- notebook_url + "source/" + assignment + "/" + name + ".ipynb"
- )
- .text(name)
- );
-
- this.$remove_exercise.append(
- $(" ")
- .attr("href", "#")
- .click(_.bind(this.removeExerciseModal, this))
- .append($(" ").text("Remove"))
- );
- },
-
- removeExerciseModal: function () {
- let body = $("
");
- body.append(
- $("
").text("Are you sure you want to delete the exercise?")
- );
- body.append($("
").text("This action can't be undone!"));
-
- this.openRemoveModal(
- body,
- "Delete exercise " + this.model.get("name") + "?"
- );
- },
-});
-
-function insertRow(table) {
- let row = $(" ");
- row.append($(" ").addClass("exercise-name"));
- row.append($(" ").addClass("remove-exercise"));
- table.append(row);
- return row;
-}
-
-function addView(model, table) {
- let row = insertRow(table);
- return new ExerciseUI({
- model: model,
- el: row,
- });
-}
-
-function loadExercises() {
- console.log("Loading the exercises");
- let tbl = $("#main_table");
- models = new Exercises({ assignment: assignment });
- models.loaded = false;
- models.fetch({
- success: function () {
- tbl.empty();
- models.each((model) => addView(model, tbl));
- dataTable = tbl.parent().DataTable();
-
- models.loaded = true;
- },
- });
-}
-
-let models = undefined;
-let dataTable = undefined;
-
-$(window).on("load", function () {
- loadExercises();
-});
diff --git a/e2xgrader/server_extensions/apps/authoring/static/js/makeexercise.js b/e2xgrader/server_extensions/apps/authoring/static/js/makeexercise.js
deleted file mode 100644
index 06eeb3a7..00000000
--- a/e2xgrader/server_extensions/apps/authoring/static/js/makeexercise.js
+++ /dev/null
@@ -1,416 +0,0 @@
-class FormTab {
- constructor($el) {
- this.$el = $el;
- }
-
- validate() {
- return true;
- }
-
- getFormData() {
- return {};
- }
-}
-
-class ExerciseName extends FormTab {
- constructor() {
- super($("#exercise"));
- this.$input = this.$el.find("#exercise-name");
- this.exercise_names = [];
- this.exercises = new Exercises({ assignment: assignment });
- this.exercises.fetch({
- success: () => this.handleLoadExercises(),
- });
- }
-
- handleLoadExercises() {
- let that = this;
-
- this.exercises.each(function (exercise) {
- that.exercise_names.push(exercise.get("name"));
- });
-
- console.log(this.exercise_names);
- }
-
- validate() {
- if (this.$input.val().trim() === "") {
- createLogModal(
- "validate-exercise-name",
- "Invalid exercise name",
- "The name of the exercise cannot be empty!"
- );
- return false;
- } else if (!/^[a-zA-Z0-9_-]+$/.test(this.$input.val())) {
- createLogModal(
- "validate-exercise-name",
- "Invalid exercise name",
- "The name of the exercise can only consist of the following characters: A-Z, a-z, 0-9, -, _"
- );
- return false;
- } else if (this.exercise_names.includes(this.$input.val())) {
- createLogModal(
- "validate-exercise-name",
- "Exercise already exists",
- "An exercise with this name already exists. Please delete it first if you want to overwrite it."
- );
- return false;
- }
- return true;
- }
-
- getFormData() {
- return {
- exercise: this.$input.val(),
- };
- }
-}
-
-class TemplateSelect extends FormTab {
- constructor() {
- super($("#template"));
- this.$select = $("#template-select");
- this.table = $("#template-variables");
- this.dataTable = this.table.DataTable({
- paging: false,
- searching: false,
- info: false,
- columnDefs: [{ orderable: false, targets: -1 }],
- language: {
- emptyTable: "No template selected or template has no variables to set!",
- },
- });
- this.templates = new Templates();
- this.templates.fetch({
- success: () => this.createSelect(),
- });
- }
-
- createSelect() {
- this.$select.empty();
- this.$select.append(
- $(" ").attr("value", "").text("Select your template")
- );
- let that = this;
- this.templates.each(function (template) {
- that.$select.append(
- $(" ")
- .attr("value", template.get("name"))
- .text(template.get("name"))
- );
- });
- this.$select.change(function () {
- let choice = $(this).val();
- if (choice !== "") {
- $.ajax({
- url: base_url + "/e2x/authoring/api/templates/variables",
- type: "get",
- data: {
- template: choice,
- },
- success: function (response) {
- let options = $.parseJSON(response);
- that.populateTable(options);
- },
- error: function (xhr) {
- console.log("Oh no!");
- console.log(xhr);
- },
- });
- } else {
- that.populateTable([]);
- }
- });
- }
-
- populateTable(options) {
- let that = this;
- let tbody = this.table.find("tbody");
- this.dataTable.clear();
-
- options.forEach(function (option) {
- let row = $(" ")
- .append($(" ").text(option))
- .append(
- $(" ").append(
- $(" ").attr("type", "text").attr("id", option)
- )
- );
- tbody.append(row);
- that.dataTable.row.add(row).draw();
- });
-
- this.table.DataTable().draw();
- }
-
- validate() {
- if (this.$select.val() === "") {
- createLogModal(
- "validate-template",
- "No template selected",
- "You must select a template!"
- );
- return false;
- }
- return true;
- }
-
- getFormData() {
- let data = {
- template: this.$select.val(),
- "template-options": {},
- };
- this.table.find("input").each(function () {
- data["template-options"][$(this).attr("id")] = $(this).val();
- });
- return data;
- }
-}
-
-class TaskSelect extends FormTab {
- constructor() {
- super($("#tasks"));
- this.$select = $("#pool-select");
- this.$selected_tasks = $("#selected-tasks");
- this.$available_tasks = $("#available-tasks");
- this.$add = $("#add");
- this.$remove = $("#remove");
- this.registerControls();
- this.pools = new Pools();
- this.data = {};
- let that = this;
-
- this.pools.fetch({
- success: () => that.handleLoadPools(),
- });
- }
-
- registerControls() {
- let that = this;
- this.$add.click(function () {
- let selection = that.$available_tasks.val();
- let pool = that.$select.val();
- selection.forEach(function (option) {
- // Check if option with same value and pool exists
- let existing_option = $(
- '#selected-tasks option[value="' + option + '"]'
- );
- if (
- existing_option.length < 1 ||
- existing_option.attr("data-pool") !== pool
- ) {
- that.$selected_tasks.append(
- $(" ")
- .attr("value", option)
- .attr("data-pool", pool)
- .text(pool + "/" + option)
- );
- }
- });
- });
-
- this.$remove.click(function () {
- $("#selected-tasks option:selected").each(function (idx, el) {
- $(this).remove();
- });
- });
- }
-
- handleLoadPools() {
- this.$select.empty();
- this.$select.append(
- $(" ").attr("value", "").text("Select your task pool")
- );
- let that = this;
- this.pools.each(function (pool) {
- that.$select.append(
- $(" ").attr("value", pool.get("name")).text(pool.get("name"))
- );
- });
-
- this.$select.change(function () {
- let choice = $(this).val();
-
- if (choice !== "") {
- that.tasks = new Tasks({
- pool: choice,
- });
- that.tasks.fetch({
- success: () => that.handleLoadTasks(),
- });
- }
- });
- }
-
- handleLoadTasks() {
- let that = this;
- this.$available_tasks.empty();
- this.tasks.each(function (task) {
- that.$available_tasks.append(
- $(" ")
- .attr("value", task.get("name"))
- .attr(
- "title",
- "Task: " +
- task.get("name") +
- ", " +
- task.get("questions") +
- " questions, " +
- task.get("points") +
- " points"
- )
- .text(task.get("name"))
- );
- });
- }
-
- getFormData() {
- let tasks = [];
- $("#selected-tasks option").each(function (idx, el) {
- tasks.push({
- task: $(this).val(),
- pool: $(this).attr("data-pool"),
- });
- });
- return {
- tasks: tasks,
- };
- }
-
- validate() {
- return true;
- }
-}
-
-class ExerciseOptions extends FormTab {
- constructor() {
- super($("#options"));
- this.$kernel_select = this.$el.find("#kernel-select");
- this.$task_headers = this.$el.find("#task-headers");
- this.createKernelSelect();
- }
-
- createKernelSelect() {
- let that = this;
- $.ajax({
- url: base_url + "/e2x/authoring/api/kernelspec",
- type: "get",
- success: function (response) {
- let kernelspecs = $.parseJSON(response);
- for (const [kernel_name, kernelspec] of Object.entries(kernelspecs)) {
- let option = $(" ")
- .attr("value", kernel_name)
- .append(kernelspec.spec.display_name);
- that.$kernel_select.append(option);
- }
- },
- error: function (xhr) {
- console.log("Oh no!");
- console.log(xhr);
- },
- });
- }
-
- getFormData() {
- return {
- exercise_options: {
- kernel: this.$kernel_select.val(),
- "task-headers": this.$task_headers.prop("checked"),
- },
- };
- }
-}
-
-class TabView {
- show() {
- this.tabs = [
- new ExerciseName(),
- new TemplateSelect(),
- new TaskSelect(),
- new ExerciseOptions(),
- ];
- this.current = this.tabs[0];
- this.cursor = 0;
- this.addNavButtons();
- }
-
- addNavButtons() {
- let that = this;
- for (let i = 0; i < this.tabs.length; i++) {
- if (i > 0) {
- this.tabs[i].$el.append(
- $(" ")
- .text("Previous")
- .addClass("btn btn-primary")
- .click(() => that.select(i - 1))
- );
- }
- if (i < this.tabs.length - 1) {
- this.tabs[i].$el.append(
- $(" ")
- .text("Next")
- .addClass("btn btn-primary")
- .click(() => that.select(i + 1))
- );
- }
- }
- this.tabs[this.tabs.length - 1].$el.append(
- $(" ")
- .text("Finalize")
- .addClass("btn btn-primary")
- .click(() => that.finalize())
- );
- }
-
- finalize() {
- let data = {
- assignment: assignment,
- };
- this.tabs.forEach((tab) => Object.assign(data, tab.getFormData()));
- $.ajax({
- url: base_url + "/e2x/authoring/api/generate_exercise",
- type: "get",
- dataType: "json",
- data: {
- resources: JSON.stringify(data),
- },
- success: function (response) {
- window.location.href =
- notebook_url +
- "source/" +
- assignment +
- "/" +
- data["exercise"] +
- ".ipynb";
- },
- error: function (xhr) {
- console.log("OH NO!");
- console.log(xhr);
- alert("There was an error generating the exercise.");
- },
- });
- }
-
- validate() {
- return this.current.validate();
- }
-
- clamp(val) {
- return Math.max(0, Math.min(val, this.tabs.length - 1));
- }
-
- select(index) {
- let new_index = this.clamp(index);
- if (new_index < this.cursor || this.validate()) {
- this.current.$el.toggle();
- this.current = this.tabs[new_index];
- this.cursor = new_index;
- this.current.$el.toggle();
- }
- }
-}
-
-$(window).on("load", function () {
- let view = new TabView();
- view.show();
-});
diff --git a/e2xgrader/server_extensions/apps/authoring/static/js/taskpools.js b/e2xgrader/server_extensions/apps/authoring/static/js/taskpools.js
deleted file mode 100644
index 8ac35188..00000000
--- a/e2xgrader/server_extensions/apps/authoring/static/js/taskpools.js
+++ /dev/null
@@ -1,124 +0,0 @@
-let PoolUI = BaseUI.extend({
- events: {},
-
- initialize: function () {
- this.$pool_name = this.$el.find(".pool-name");
- this.$number_of_tasks = this.$el.find(".number-of-tasks");
- this.$remove_pool = this.$el.find(".remove-pool");
-
- this.fields = [this.$pool_name, this.$number_of_tasks, this.$remove_pool];
-
- this.listenTo(this.model, "sync", this.render);
- this.render();
- },
-
- render: function () {
- this.clear();
- let name = this.model.get("name");
-
- this.$pool_name.append(
- $(" ")
- .attr("href", base_url + "/e2x/authoring/app/pools/" + name)
- .text(name)
- );
- this.$number_of_tasks.text(this.model.get("tasks"));
- this.$remove_pool.append(
- $(" ")
- .attr("href", "#")
- .click(_.bind(this.removePoolModal, this))
- .append($(" ").text("Remove"))
- );
- },
-
- removePoolModal: function () {
- let body = $("
");
- body.append(
- $("
").text("Are you sure you want to delete the task pool?")
- );
- body.append(
- $("
").text(
- "It contains " +
- this.model.get("tasks") +
- " tasks that will be deleted!"
- )
- );
- body.append($("
").text("This action can't be undone!"));
-
- this.openRemoveModal(body, "Delete pool " + this.model.get("name") + "?");
- },
-});
-
-function addView(model, table) {
- let row = insertRow(table, ["pool-name", "number-of-tasks", "remove-pool"]);
-
- return new PoolUI({
- model: model,
- el: row,
- });
-}
-
-function loadPools() {
- let tbl = $("#main_table");
- models = new Pools();
- models.loaded = false;
- models.fetch({
- success: function () {
- tbl.empty();
- models.each((model) => addView(model, tbl));
- dataTable = tbl.parent().DataTable({
- columnDefs: [
- { orderable: false, targets: [-1] },
- { searchable: false, targets: [-1] },
- ],
- });
- models.loaded = true;
- },
- });
-}
-
-function newPool() {
- let elem = getNewModalElements("Task Pool");
-
- let $modal = createModal(
- "new-pool-modal",
- "Create a new task pool",
- elem.body,
- elem.footer
- );
-
- let $modal_save = $modal.find("button.save");
- $modal_save.click(function () {
- let $modal_name = $modal.find("input.modal-name").val();
- let pool = new Pool();
- pool.save(
- {
- name: $modal_name,
- tasks: 0,
- },
- {
- success: function (pool) {
- if (pool.get("success")) {
- $modal.modal("hide");
- let row = addView(pool, $("#main_table")).el;
- dataTable.row.add(row).draw();
- models.add([pool]);
- } else {
- createLogModal(
- "error-modal",
- "Error",
- "There was an error creating the pool " + pool.get("name") + "!",
- pool.get("error")
- );
- }
- },
- }
- );
- });
-}
-
-let models = undefined;
-let dataTable = undefined;
-
-$(window).on("load", function () {
- loadPools();
-});
diff --git a/e2xgrader/server_extensions/apps/authoring/static/js/tasks.js b/e2xgrader/server_extensions/apps/authoring/static/js/tasks.js
deleted file mode 100644
index 7a28eeed..00000000
--- a/e2xgrader/server_extensions/apps/authoring/static/js/tasks.js
+++ /dev/null
@@ -1,148 +0,0 @@
-let TaskUI = BaseUI.extend({
- events: {},
-
- initialize: function () {
- this.$task_name = this.$el.find(".task-name");
- this.$number_of_questions = this.$el.find(".number-of-questions");
- this.$points = this.$el.find(".points");
- this.$edit_task = this.$el.find(".edit-task");
- this.$remove_task = this.$el.find(".remove-task");
-
- this.fields = [
- this.$task_name,
- this.$number_of_questions,
- this.$points,
- this.$edit_task,
- this.$remove_task,
- ];
-
- this.listenTo(this.model, "sync", this.render);
- this.render();
- },
-
- render: function () {
- this.clear();
- let name = this.model.get("name");
- this.$task_name.append(
- $(" ")
- .attr("href", tree_url + "pools/" + pool + "/" + name)
- .text(name)
- );
- this.$points.text(this.model.get("points"));
- this.$number_of_questions.text(this.model.get("questions"));
- this.$edit_task.append(
- $(" ")
- .attr(
- "href",
- notebook_url + "pools/" + pool + "/" + name + "/" + name + ".ipynb"
- )
- .text("Edit")
- );
- this.$remove_task.append(
- $(" ")
- .attr("href", "#")
- .click(_.bind(this.removeTaskModal, this))
- .append($(" ").text("Remove"))
- );
- },
-
- removeTaskModal: function () {
- let body = $("
");
- body.append($("
").text("Are you sure you want to delete the task?"));
- body.append($("
").text("This action can't be undone!"));
-
- this.openRemoveModal(body, "Delete task " + this.model.get("name") + "?");
- },
-});
-
-function addView(model, table) {
- let row = insertRow(table, [
- "task-name",
- "number-of-questions",
- "points",
- "edit-task",
- "remove-task",
- ]);
- return new TaskUI({
- model: model,
- el: row,
- });
-}
-
-function loadTasks() {
- let tbl = $("#main_table");
- models = new Tasks({ pool: pool });
- models.loaded = false;
- models.fetch({
- success: function () {
- tbl.empty();
-
- models.each((model) => addView(model, tbl));
- dataTable = tbl.parent().DataTable({
- columnDefs: [
- { orderable: false, targets: [-1, -2] },
- { searchable: false, targets: [-1, -2] },
- ],
- });
- models.loaded = true;
- },
- });
-}
-
-function newTask() {
- let elem = getNewModalElements("Task");
-
- let $modal = createModal(
- "new-task-modal",
- "Create a new task",
- elem.body,
- elem.footer
- );
-
- let $modal_save = $modal.find("button.save");
- $modal_save.click(function () {
- let $modal_name = $modal.find("input.modal-name").val();
- let task = new Task({
- pool: pool,
- name: $modal_name,
- });
-
- task.save(undefined, {
- success: function () {
- if (task.get("success")) {
- task.fetch({
- success: function () {
- $modal.modal("hide");
- let row = addView(task, $("#main_table")).el;
- dataTable.row.add(row).draw();
- models.add([task]);
- window.location.href =
- notebook_url +
- "pools/" +
- pool +
- "/" +
- $modal_name +
- "/" +
- $modal_name +
- ".ipynb";
- },
- });
- } else {
- createLogModal(
- "error-modal",
- "Error",
- "There was an error creating the task " + task.get("name") + "!",
- task.get("error")
- );
- }
- },
- });
- });
-}
-
-let models = undefined;
-let dataTable = undefined;
-
-$(window).on("load", function () {
- loadTasks();
-});
diff --git a/e2xgrader/server_extensions/apps/authoring/static/js/templates.js b/e2xgrader/server_extensions/apps/authoring/static/js/templates.js
deleted file mode 100644
index a0e69384..00000000
--- a/e2xgrader/server_extensions/apps/authoring/static/js/templates.js
+++ /dev/null
@@ -1,144 +0,0 @@
-let TemplateUI = BaseUI.extend({
- events: {},
-
- initialize: function () {
- this.$template_name = this.$el.find(".template-name");
- this.$edit_template = this.$el.find(".edit-template");
- this.$remove_template = this.$el.find(".remove-template");
-
- this.fields = [
- this.$template_name,
- this.$edit_template,
- this.$remove_template,
- ];
-
- this.listenTo(this.model, "sync", this.render);
- this.render();
- },
-
- render: function () {
- this.clear();
- let name = this.model.get("name");
-
- this.$template_name.append(
- $(" ")
- .attr("href", tree_url + "templates/" + name)
- .text(name)
- );
- this.$edit_template.append(
- $(" ")
- .attr(
- "href",
- notebook_url + "templates/" + name + "/" + name + ".ipynb"
- )
- .text("Edit")
- );
- this.$remove_template.append(
- $(" ")
- .attr("href", "#")
- .click(_.bind(this.removeTemplateModal, this))
- .append($(" ").text("Remove"))
- );
- },
-
- removeTemplateModal: function () {
- let body = $("
");
- body.append(
- $("
").text("Are you sure you want to delete the template?")
- );
- body.append($("
").text("This action can't be undone!"));
-
- this.openRemoveModal(
- body,
- "Delete template " + this.model.get("name") + "?"
- );
- },
-});
-
-function addView(model, table) {
- let row = insertRow(table, [
- "template-name",
- "edit-template",
- "remove-template",
- ]);
- return new TemplateUI({
- model: model,
- el: row,
- });
-}
-
-function loadTemplates() {
- let tbl = $("#main_table");
- models = new Templates();
- models.loaded = false;
- models.fetch({
- success: function () {
- tbl.empty();
- models.each((model) => addView(model, tbl));
- dataTable = tbl.parent().DataTable({
- columnDefs: [
- { orderable: false, targets: [-1, -2] },
- { searchable: false, targets: [-1, -2] },
- ],
- });
-
- models.loaded = true;
- },
- });
-}
-
-function newTemplate() {
- let elem = getNewModalElements("Template");
-
- let $modal = createModal(
- "new-template-modal",
- "Create a new exercise template",
- elem.body,
- elem.footer
- );
-
- let $modal_save = $modal.find("button.save");
- $modal_save.click(function () {
- let $modal_name = $modal.find("input.modal-name").val();
- let template = new Template();
- template.save(
- {
- name: $modal_name,
- tasks: 0,
- },
- {
- success: function (template) {
- if (template.get("success")) {
- $modal.modal("hide");
- let row = addView(template, $("#main_table")).el;
- dataTable.row.add(row).draw();
- models.add([template]);
- window.location.href =
- notebook_url +
- "templates/" +
- $modal_name +
- "/" +
- $modal_name +
- ".ipynb";
- } else {
- createLogModal(
- "error-modal",
- "Error",
- "There was an error creating the template " +
- template.get("name") +
- "!",
- template.get("error")
- );
- }
- },
- }
- );
- });
-}
-
-let models = undefined;
-let dataTable = undefined;
-
-$(window).on("load", function () {
- loadTemplates();
-});
diff --git a/e2xgrader/server_extensions/apps/authoring/templates/authoring/assignments.tpl b/e2xgrader/server_extensions/apps/authoring/templates/authoring/assignments.tpl
deleted file mode 100644
index 876743d6..00000000
--- a/e2xgrader/server_extensions/apps/authoring/templates/authoring/assignments.tpl
+++ /dev/null
@@ -1,33 +0,0 @@
-{%- extends 'authoring/tablebase.tpl' -%}
-
-{% block head %}
-{{ super() }}
-
-{% endblock head %}
-
-{% block sidebar %}
-{{ super() }}
-
-{% endblock sidebar %}
-
-{% block headline %}
-Assignments
-{% endblock headline %}
-{% block breadcrumbs %}
-Assignments
-{% endblock breadcrumbs %}
-
-{% block help %}
-Choose your assignment
-Here you can choose which assignment you want to create an exercise for. An exercise is a single Jupyter Notebook consisting of tasks. Assignments have to be created via nbgrader .
-{% endblock help %}
-
-{% block table_head %}
-Name
-Number of Exercises
-{% endblock table_head %}
-{% block table_body %}
-Loading...
-{% endblock table_body %}
diff --git a/e2xgrader/server_extensions/apps/authoring/templates/authoring/base.tpl b/e2xgrader/server_extensions/apps/authoring/templates/authoring/base.tpl
deleted file mode 100644
index 3051cfdb..00000000
--- a/e2xgrader/server_extensions/apps/authoring/templates/authoring/base.tpl
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
- nbassignment
-
- {%- block head -%}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {%- endblock -%}
-
-
-
-
-
-
- {%- block body -%}
- {%- endblock -%}
-
-
\ No newline at end of file
diff --git a/e2xgrader/server_extensions/apps/authoring/templates/authoring/editexercise.tpl b/e2xgrader/server_extensions/apps/authoring/templates/authoring/editexercise.tpl
deleted file mode 100644
index 2bbdb209..00000000
--- a/e2xgrader/server_extensions/apps/authoring/templates/authoring/editexercise.tpl
+++ /dev/null
@@ -1,95 +0,0 @@
-{%- extends 'authoring/base.tpl' -%}
-
-{% block head %}
-{{ super() }}
-
-
-{% endblock head %}
-
-{% block sidebar %}
-{{ super() }}
-
-{% endblock sidebar %}
-
-{% block body %}
-
-Edit Exercise - {{ exercise }}
-
-
-
-
-
1. Please choose a name for your exercise
-
The name can contain upper and lower case letters. etc
-
-
-
-
2. Please choose a template for your exercise
-
2.1 Select the template
-
-
- Template Choose a template
-
-
-
2.2 Set variables of template
-
You can define variables in templates by enclosing them in curly braces (e.g. {{ var }}
). You can set the values here.
-
-
-
-
3. Please select the tasks for your exercise
-
-
- Pool Choose a task pool
-
-
-
-
- Selected Tasks Controls Available Tasks
-
-
-
-
-
-
-
-
- Add
- Remove
-
-
-
-
-
-
-
-
-
-
-
4. General options for the exercise
-
- Option Value
-
- Add Task Headers
- Kernel
-
-
-
-
-
-
-{% endblock body %}
\ No newline at end of file
diff --git a/e2xgrader/server_extensions/apps/authoring/templates/authoring/exercises.tpl b/e2xgrader/server_extensions/apps/authoring/templates/authoring/exercises.tpl
deleted file mode 100644
index f286b9dd..00000000
--- a/e2xgrader/server_extensions/apps/authoring/templates/authoring/exercises.tpl
+++ /dev/null
@@ -1,40 +0,0 @@
-{%- extends 'authoring/tablebase.tpl' -%}
-
-{% block head %}
-
-{{ super() }}
-
-{% endblock head %}
-
-{% block sidebar %}
-{{ super() }}
-
-{% endblock sidebar %}
-
-{% block headline %}
-Assignments
-{% endblock headline %}
-{% block breadcrumbs %}
-Assignments
- > {{ assignment }}
-{% endblock breadcrumbs %}
-
-{% block help %}
-Choose your exercise
-Here you can choose or create an exercise. An exercise is a single Jupyter Notebook consisting of tasks..
-{% endblock help %}
-
-{% block table_head %}
-Name
-Remove
-{% endblock table_head %}
-{% block table_body %}
-Loading...
-{% endblock table_body %}
-{% block add_new %}
-Add Exercise
-{% endblock add_new %}
diff --git a/e2xgrader/server_extensions/apps/authoring/templates/authoring/tablebase.tpl b/e2xgrader/server_extensions/apps/authoring/templates/authoring/tablebase.tpl
deleted file mode 100644
index d5ebfa93..00000000
--- a/e2xgrader/server_extensions/apps/authoring/templates/authoring/tablebase.tpl
+++ /dev/null
@@ -1,35 +0,0 @@
-{%- extends 'authoring/base.tpl' -%}
-
-{% block body %}
-
-{% block headline %}
-{% endblock %}
-
-
- {% block breadcrumbs %}
- {% endblock %}
-
-
-
-
- {% block help %}
- {% endblock %}
-
-
-
-
-
-
- {% block table_head %}
- {% endblock %}
-
-
- {% block table_body %}
- {% endblock %}
-
-
- {% block add_new %}
- {% endblock %}
-
-
-{% endblock body %}
\ No newline at end of file
diff --git a/e2xgrader/server_extensions/apps/authoring/templates/authoring/taskcreator.tpl b/e2xgrader/server_extensions/apps/authoring/templates/authoring/taskcreator.tpl
deleted file mode 100644
index 06b6bb49..00000000
--- a/e2xgrader/server_extensions/apps/authoring/templates/authoring/taskcreator.tpl
+++ /dev/null
@@ -1,43 +0,0 @@
-{%- extends 'base.tpl' -%}
-
-{% block head %}
-
-{% endblock head %}
-
-{% block body %}
-
- Hello
-
-
-
-{% endblock body %}
\ No newline at end of file
diff --git a/e2xgrader/server_extensions/apps/authoring/templates/authoring/taskpools.tpl b/e2xgrader/server_extensions/apps/authoring/templates/authoring/taskpools.tpl
deleted file mode 100644
index 7ebb125c..00000000
--- a/e2xgrader/server_extensions/apps/authoring/templates/authoring/taskpools.tpl
+++ /dev/null
@@ -1,37 +0,0 @@
-{%- extends 'authoring/tablebase.tpl' -%}
-
-{% block head %}
-{{ super() }}
-
-{% endblock head %}
-
-{% block sidebar %}
-{{ super() }}
-
-{% endblock sidebar %}
-
-{% block headline %}
-Task Pools
-{% endblock headline %}
-{% block breadcrumbs %}
-Task Pools
-{% endblock breadcrumbs %}
-
-{% block help %}
-Manage task pools
-Task pools are collections of tasks about the same topic. A task consists of a collection of related questions.
-{% endblock help %}
-
-{% block table_head %}
-Name
-Number of Tasks
-Remove
-{% endblock table_head %}
-{% block table_body %}
-Loading...
-{% endblock table_body %}
-{% block add_new %}
-Add Task Pool
-{% endblock add_new %}
diff --git a/e2xgrader/server_extensions/apps/authoring/templates/authoring/tasks.tpl b/e2xgrader/server_extensions/apps/authoring/templates/authoring/tasks.tpl
deleted file mode 100644
index 3b1e6116..00000000
--- a/e2xgrader/server_extensions/apps/authoring/templates/authoring/tasks.tpl
+++ /dev/null
@@ -1,43 +0,0 @@
-{%- extends 'authoring/tablebase.tpl' -%}
-
-{% block head %}
-
-{{ super() }}
-
-{% endblock head %}
-
-{% block sidebar %}
-{{ super() }}
-
-{% endblock sidebar %}
-
-{% block headline %}
-Tasks
-{% endblock headline %}
-{% block breadcrumbs %}
-Task Pools
-> {{ pool }}
-{% endblock breadcrumbs %}
-
-{% block help %}
-Tasks
-A task is a single Jupyter notebook consisting of a task with questions (i.e. Task 1.1, Task 1.2, Task1.3).
-{% endblock help %}
-
-{% block table_head %}
-Name
-# of Questions
-Points
-Edit
-Remove
-{% endblock table_head %}
-{% block table_body %}
-Loading...
-{% endblock table_body %}
-{% block add_new %}
-Add Task
-{% endblock add_new %}
diff --git a/e2xgrader/server_extensions/apps/authoring/templates/authoring/templates.tpl b/e2xgrader/server_extensions/apps/authoring/templates/authoring/templates.tpl
deleted file mode 100644
index 12ce897b..00000000
--- a/e2xgrader/server_extensions/apps/authoring/templates/authoring/templates.tpl
+++ /dev/null
@@ -1,41 +0,0 @@
-{%- extends 'authoring/tablebase.tpl' -%}
-
-{% block head %}
-{{ super() }}
-
-{% endblock head %}
-
-{% block sidebar %}
-{{ super() }}
-
-{% endblock sidebar %}
-
-
-{% block headline %}
-Templates
-{% endblock headline %}
-{% block breadcrumbs %}
-Templates
-{% endblock breadcrumbs %}
-
-{% block help %}
-Create and edit templates
-Templates are used for creating exercises. A template consists of header and footer cells and special cells like student info.
-You can use variables in templates by enclosing them in double curly braces (e.g. {{ my_var }} .
-When creating an exercise you can set the values for those variables.
-{% endblock help %}
-
-
-{% block table_head %}
-Name
-Edit
-Remove
-{% endblock table_head %}
-{% block table_body %}
-Loading...
-{% endblock table_body %}
-{% block add_new %}
-Add Template
-{% endblock add_new %}
diff --git a/e2xgrader/server_extensions/teacher/teacher.py b/e2xgrader/server_extensions/teacher/teacher.py
index 8a02ccd1..50dceae0 100644
--- a/e2xgrader/server_extensions/teacher/teacher.py
+++ b/e2xgrader/server_extensions/teacher/teacher.py
@@ -1,7 +1,7 @@
+from e2xauthoring.app import AuthoringApp
from traitlets import Any, List
from ..apps.assignment_list import AssignmentList
-from ..apps.authoring import AuthoringApp
from ..apps.e2xgraderapi import E2xGraderApi
from ..apps.formgrader import FormgradeApp
from ..apps.help import Help
diff --git a/pyproject.toml b/pyproject.toml
index 7b8ee892..0c7ab71b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -21,7 +21,7 @@ classifiers=[
]
dependencies = [
"e2xcore",
- "nbgrader>=0.7,<0.9",
+ "e2xauthoring",
"pandas"
]
dynamic = ["version"]