Skip to content
This repository has been archived by the owner on Sep 20, 2022. It is now read-only.

complete integration with LMS UI CDN #1

Merged
merged 3 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1 @@
include async_nbgrader/static/common.js
include formgradernext/static/common.js
24 changes: 7 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
# Async nbgrader
# Formgrader Next

A jupyter extension which adds async capabilities to nbgrader's auto-grading service.
A jupyter extension which serves a custom LMS UI for formgrader.

## Installation

1. Install this setup directly from GitHub using `pip install`:

```bash
pip install git+ssh://[email protected]/IllumiDesk/async-nbgrader.git
cd async-nbgrader
pip install git+ssh://[email protected]/IllumiDesk/formgradernext.git
cd formgradernext
```

> **NOTE**: future versions will publish the `async-nbgrader` package to PyPi.

2. Create and activate your virtual environment:

```bash
Expand All @@ -25,23 +23,15 @@ source venv/bin/activate
Install and activate all extensions (assignment list, create assignment, formgrader, and validate):

```bash
jupyter nbextension install --symlink --sys-prefix --py async_nbgrader --overwrite
jupyter nbextension enable --sys-prefix --py async_nbgrader
jupyter serverextension enable --sys-prefix --py async_nbgrader
jupyter nbextension install --symlink --sys-prefix --py formgradernext --overwrite
jupyter nbextension enable --sys-prefix --py formgradernext
jupyter serverextension enable --sys-prefix --py formgradernext
```

## Run the Auto-Grader

This package leverages the same Python API available with `nbgrader`. Therefore no additional changes are required to run the auto-grader for submitting assignments.

## Contributing

For general contribution guidelines, please refer to IllumiDesk's [contributing guidelines](https://github.com/IllumiDesk/illumidesk/blob/main/CONTRIBUTING.md).

The `async_nbgrader` package installs the `nbgrader` package as a required dependency, therefore you should not have to install it explicitly.

> The `async_nbgrader` package overrides `nbgrader`'s default auto-grading service (included with the `Formgrader` extension) by converting the grading service from a `syncronous` service to an `asyncronous` service. Thefore it's a good idea to get familiar with the `nbgrader` documentation (although not a must) to setup your local environment [by following these instructions](https://nbgrader.readthedocs.io/en/latest/contributor_guide/installation_developer.html).

Use `pytest` to run tests:

```bash
Expand Down
15 changes: 7 additions & 8 deletions formgradernext/__init__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
"""Tornado handlers for nbgrader background service."""
"""Tornado handlers for formgrader custom UI."""
import os

from .server_extensions.formgrader.handlers import load_jupyter_server_extension
from .handlers import load_jupyter_server_extension


def _jupyter_nbextension_paths():
return [
dict(
section="tree",
src=os.path.join('nbextensions', 'formgrader'),
dest="formgrader",
require="formgrader/main"
section="common",
src="static",
dest="formgradernext",
require="formgradernext/common"
),
]

def _jupyter_server_extension_paths():
return [
dict(module="async_nbgrader"),
dict(module="async_nbgrader.server_extensions.formgrader"),
dict(module="formgradernext"),
]
80 changes: 33 additions & 47 deletions formgradernext/handlers.py
Original file line number Diff line number Diff line change
@@ -1,73 +1,59 @@
import json
import os
import pkgutil
import requests
import sys

from jinja2 import Environment, BaseLoader
from tornado import web

from nbgrader.server_extensions.formgrader.apihandlers import AutogradeHandler
from nbgrader.server_extensions.formgrader.base import check_xsrf, check_notebook_dir
from notebook.base.handlers import IPythonHandler
from nbgrader.server_extensions.formgrader.base import (
BaseHandler,
check_xsrf,
check_notebook_dir,
)

from notebook.notebookapp import NotebookApp
from notebook.utils import url_path_join as ujoin

from .scheduler import scheduler
from .tasks import autograde_assignment
lms_version = os.environ.get("LMS_VERSION") or "0.1.0"

template_response = requests.get(
f"https://content.illumidesk.com/lms/{lms_version}/index.html"
)
template_html = template_response.text.replace(
"</head>", '<script>var base_url = "{{ base_url }}";</script></head>'
)


class AsyncAutogradeHandler(AutogradeHandler):
class LMSHandler(BaseHandler):
@web.authenticated
@check_xsrf
@check_notebook_dir
def post(self, assignment_id:str, student_id:str):
"""Handles a post request to initiate an auto grading job.

Args:
assignment_id (str): the assignment id which is equivalent to the assignment name.
student_id (str): the student id which is equivalent to the student name.
"""
scheduler.add_job(
autograde_assignment, "date", args=[None, assignment_id, student_id]
)
self.write(
json.dumps(
{
"success": True,
"queued": True,
"message": "Submission for Autograding queued",
}
def get(self):
html = (
Environment(loader=BaseLoader)
.from_string(template_html)
.render(
url_prefix=self.url_prefix,
base_url=self.base_url,
windows=(sys.prefix == "win32"),
course_id=self.api.course_id,
exchange=self.api.exchange,
exchange_missing=self.api.exchange_missing,
)
)
self.write(html)


class FormgraderStaticHandler(IPythonHandler):
def get(self):
# this is a hack to override text in formgrader, we are appending our JS module to a module imported in formgrader
original_data = pkgutil.get_data("nbgrader", "server_extensions/formgrader/static/js/utils.js").decode("utf-8")
common_js = pkgutil.get_data(__name__, "static/common.js").decode("utf-8")
self.write(original_data)
self.write(common_js)
self.set_header('Content-Type', 'application/javascript')
self.finish()

handlers = [
(r"/formgrader/api/submission/([^/]+)/([^/]+)/autograde", AsyncAutogradeHandler),
(r"/formgradernext/?", LMSHandler),
]

static_handlers = [
(r"/formgrader/static/js/utils.js$", FormgraderStaticHandler),
]

def rewrite(nbapp: NotebookApp, x):
web_app = nbapp.web_app
pat = ujoin(web_app.settings["base_url"], x[0].lstrip("/"))
return (pat,) + x[1:]


def load_jupyter_server_extension(nbapp: NotebookApp):
"""Start background processor"""
if os.environ.get("NBGRADER_ASYNC_MODE", "true") == "true":
nbapp.log.info("Starting background processor for asycn-nbgrader serverextension")
nbapp.web_app.add_handlers(".*$", [rewrite(nbapp, x) for x in handlers])
scheduler.start()
else:
nbapp.log.info("Skipping background processor and using standard nbgrader serverextension")
nbapp.web_app.add_handlers(".*$", [rewrite(nbapp, x) for x in static_handlers])
nbapp.web_app.add_handlers(".*$", [rewrite(nbapp, x) for x in handlers])
23 changes: 0 additions & 23 deletions formgradernext/scheduler.py

This file was deleted.

2 changes: 1 addition & 1 deletion formgradernext/static/common.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 0 additions & 31 deletions formgradernext/tasks.py

This file was deleted.

45 changes: 0 additions & 45 deletions formgradernext/tests/test_formgrader.py

This file was deleted.

3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
from setuptools import setup, find_packages

name = 'async_nbgrader'
name = 'formgradernext'
here = os.path.abspath(os.path.dirname(__file__))
version_ns = {}
with open(os.path.join(here, name, '_version.py')) as f:
Expand All @@ -15,7 +15,6 @@
"jupyter_core==4.7.1",
"notebook==6.4.2",
"nbgrader==0.6.2",
"apscheduler==3.7.0",
],
include_package_data=True
)
Expand Down