Skip to content

Latest commit

 

History

History
298 lines (200 loc) · 9.53 KB

how_it_works.md

File metadata and controls

298 lines (200 loc) · 9.53 KB

How the NBExchange service works

nbexchange is an extension to nbgrader which provides a mechanism for assignments to transferred in a distributed Jupyter Notebooks environment. (See https://nbgrader.readthedocs.io/en/stable/exchange/exchange_api.html for documentation on how nbgrader expects to operate.)

Configuration documentation is in the README.md

It's all about the actions

Fundamentally, the exchange revolves around action table - this is where we record who does what, and the location of the file is held.

The location follows a standard format:

path.join(
    base_storage_location,
    org_id,
    action,
    course_code,
    assignment_code,
    time.now(),
    filename
)

The calls

Lets follow an assignment cycle, and see how the exchange records everything

In all cases, the user is authenticated using the get_current_user method, and subscribed to the course with the role defined in that call.

All calls check that the user is subscribed to the course given in the parameter

list

GET /assignments?course_id=$cid

Get list of all assignments associated with that course. We return a list of all released assignments.

release-assignment

POST /assignment?course_id=$cid&assignment_id=$aid, files = _zip-file_

We verify the user is an instructor, and subscribed to the course.

  1. Creates the assignment and link it to the course,
  2. Grabs the first uploaded file (we use .zip files for assignments) and store it in a location,
  3. Creates an action record, noting action=released, the assignment, file location, who did the action, and add a timestamp

fetch-assignment

GET /assignment?course_id=$cid&assignment_id=$aid
  1. Finds the released action for that assignment, and download the file from the given location
  2. Creates an action record, noting action=fetched, the assignment, file location, who did the action, and add a timestamp

submit

POST /submission?course_id=$cid&assignment_id=$aid, files = _zip-file_
  1. Grabs the first uploaded file (we use .zip files for assignments) and store it in a location,
  2. Creates an action record, noting action=submitted, the assignment, file location, who did the action, and add a timestamp

collect

We verify the user is an instructor, and subscribed to the course.

Get a list of all available submissions (GET /collections?course_id=$cid&assignment_id=$aid - optional &user_id=$uid)

For each submission listed:

  1. Download the file from the given location
  2. Add the student details to the gradebook database
  3. Create an action record, noting action=collected, the assignment, file location, who did the action, and add a timestamp

release-feedback

We verify the user is an instructor, and subscribed to the course.

Each autograded .ipynb file has a matching .html file - For each .html file:

  1. Grabs the first uploaded file (POST /feedback?course_id=$cid&assignment_id=$aid&notebook=$nb&student=$sid&timestamp=$ts&checksum=$cs, files = text-file), and store it in a location,
    • timestamp ($ts), in this instance, it the timestamp recorded from the student submission.
  2. Records the details, noting the notebook, the instructor (this.user), the student ($sid), and the given timestamp ($ts)
  3. Creates an action record, noting action=feedback_released, the assignment, file location, who did the action, and add a timestamp

fetch-feedback

GET /feedback?course_id=$cid&assignment_id=$aid
  1. Downloads all the feedback for the current user, for the given course & assignment.
  2. Note that the (.html) feedback files are held as base64-encoded content in the returned data-object.
  3. Creates an action record, noting action=feedback_fetched, the assignment, file location, who did the action, and add a timestamp

history

GET /history?course_id=$cid&action=$acc

Gets a list of courses, assignments, and actions - optionally filtered by course_id and/or action

Users will see all the released action for the courses they're subscribed to, plus any actions they themselved perfomed.

If a user has been an Instructor on a course, they can see all the actions for that course.

API Specification for the NBExchange service

All URLs relative to /services/nbexchange

Assignments

.../assignments?course_id=$course_code

GET: returns list of assignments

Returns

{"success": True,
    "value": [{
        "assignment_id": "$assignment_code",
        "course_id": "$course_code",
        "student_id": Int
        "status": Str,
        "path": path,
        "notebooks": [
            { "notebook_id": x.name,
              "has_exchange_feedback": False,
              "feedback_updated": False,
              "feedback_timestamp": None, } for x in assignment.notebooks],
        "timestamp": action.timestamp.strftime(
            "%Y-%m-%d %H:%M:%S.%f %Z"
        ),
    },
    {},..
    ]}

or

{"success": False, "note": $note}

Assignment

.../assignment?course_id=$course_code&assignment_id=$assignment_code

GET: downloads assignment

Returns binary data or raises Exception (which is returned as a 503 error)

POST: (role=instructor, with file): Add ("release") an assignment returns

{"success": True, "note": "Released"}

or raises Exception (which is returned as a 503 error)

DELETE: (role=instructor, with file): Remove an assignment.

Marks an asiignment as active: False, and forgets any associated notebooks. Returns

{"success": True,
 "note": "Assignment '$assignment_code' on course '$course_code' marked as unreleased by user $user"
}

Takes as optional parameter purge. This will delete the notebooks, the assignment, and any associated data (actions, feedback, etc). Returns

{"success": True,
 "note": "Assignment '$assignment_code' on course '$course_code' deleted and purged from the database by user $user"
}

If there are permission issues, returns

{"success": False, "note": $note}

Submission

.../submission?course_id=$course_code&assignment_id=$assignment_code

POST: stores the submission for that user returns

{"success": True, "note": "Released"}

or raises Exception (which is returned as a 503 error)

Collections

.../collections?course_id=$course_code&assignment_id=$assignment_code

GET: gets a list of submitted items Return: same as Assignments <#assignments>

Collection

.../collections?course_id=$course_code&assignment_id=$assignment_code&path=$url_encoded_path

GET: downloads submitted assignment Return: similar to Assignment <#assignment>, but adds the full_name, email, and lms_user_id fields along-side student_id et al.

Feedback

GET: downloads feedback

.../feedback?course_id=$course_code&assignment_id=$assignment_code

Optional parameter

user_id=$user_id

Return: Returns a data structure similar to the above, except value is a list of feedback items:

{"success": True,
    "value": [{
            "content": base64-encoded html file
            "filename": notebook-name.html
            "timestamp": timestamp.strftime("%Y-%m-%d %H:%M:%S.%f %Z")
            "checksum": checksum
    },
    {},..
    ]}

POST: uploads feedback (one notebook at a time)

.../feedback?course_id=$course_code&assignment_id=$assignment_code&notebook=$nb_name&student=$sid&timestamp=$ts&checksum=$abc123

If there are permission issues, returns

{"success": False, "note": $note}

else

{"success": True, "note": "Feedback released"}

or raises an error - should be a 404 or 412.

History

.../history?course_id=$course_code&action=$action

GET: returns list of actions, grouped by course, and then assignment

Returns

{"success": True,
    "value": [{
        course_id: Int,
        course_code: Str,
        course_title: Str,
        role: [Str, Str, ..],
        user_id: [Str, Str, ..],
        isInstructor: Bool,
        assignments: [
            {
                assignment_code: Str,
                assignment_id: Int
                actions: [
                    {
                        action: Str,
                        timestamp: timestamp.strftime("%Y-%m-%d %H:%M:%S.%f %Z"),
                        user: Str
                    },
                    {...},
                ],
                "action_summary": {
                    "released": Int
                    "submitted": Int
                    ...
                },

            },
        ],
   },
   {...},
]}

or

{"success": False, "note": $note}