Skip to main content

A Canvas course management tool

Project description

Easel

A Canvas course management tool.

Installation

pip install easel-cli

To install from the root of the repository:

pip install -e .

Usage

When connected to a Canvas course, easel will read in a yaml file and create the corresponding component in Canvas on that course. Currently, easel requires you to run its commands from the root of your course directory (where the component subdirectories are located). This is where easel will initialize its database: .easeldb.

Getting Started

For each of these operations, refer to their detailed description and usage below.

  1. Tell easel about your Canvas instance (only needs to be run once): easel login <canvas_base_url> <api_token>
  2. Initialize easel in a course-specific directory: easel init
  3. Add a course or courses (i.e., sections): easel course add <canvas_course_url>
  4. Create yaml files describing your course content and push them to your course: easel push [component_filepath ...]

Easel makes it easy to not have to start from scratch by first pulling the material from a previous course and pushing it to a new course:

  1. Tell easel about your Canvas instance: easel login <canvas_base_url> <api_token>
  2. Initialize easel in a course-specific directory: easel init
  3. Add the previous course: easel course add <previous_canvas_course_url>
  4. Pull everything from it: easel pull
  5. Add the new course: easel course add <new_canvas_course_url>
  6. Push everything to the new course: easel --course <new_course_id> push
  7. At this point, you may remove your old course: easel course remove <old_course_id>

Individual Component References

These are the components currently managed by easel. For configuration, see the linked documentation.

Command Reference

Login

easel login <canvas_base_url> <api_token>

E.g.,

easel login https://school.instructure.com yourT0kenH3re

Only needs to be run once per client machine. Records the Canvas url and token to be used later. Canvas tokens can be generated in "Account->Settings->+New Access Token".

Init

easel init

Run this one time per course directory. It will initialize the easel database in the current directory. It will also create subdirectories for each Canvas component type that easel supports.

At this time, easel requires components to be organized by directory but this is hopefully a temporary restriction.

Course

easel course add <canvas_course_url>

E.g.,

easel course add https://school.instructure.com/courses/615446

Hooks up the database to a Canvas course. Run this one time per Canvas course (once per section taught per semester).

easel course list

List all Canvas courses that are tracked in the database.

Push

Reads in and pushes a specific component (or multiple components) to the configured courses. A push reads the information of each component stored locally and for each one, makes a POST or PUT request to Canvas, depending on whether you are creating or updating the component in the Canvas course.

easel push

or to push to a specific course:

easel --course <course_id> push

To push a specific component or components:

easel push [component_filepath ...]

E.g.,

easel push pages/lesson-1.yaml

Notes on pushing files:

  • Files placed in the files directory will be pushed as they are (ignoring the files parent directory).
  • Supports multiple filename arguments and wildcards for batch pushing.
  • Use the --hidden flag to unpublish the file(s) as hidden when pushed (by default canvas publishes files when you upload them).
  • When pushing a directory, easel will push all of its child files.

Course filtering:

Use the --course flag (alternatively -c) to specify a subset of your courses. I prefer to use the section number to identify a course. For example, to push a page to only sections 01 and 02, I would use this command:

easel push -c 01 -c 02 pages/lesson-1.yaml

Remove

Remove a given component(s) from the canvas course. This does not delete the yaml file or the local database entry for the component. But it will remove the database record which tracks that component in Canvas (i.e., it's Canvas ID).

easel remove [component_filepath ...]

E.g.,

easel remove pages/lesson-1.yaml

Note for files:

  • Supports multiple filename arguments and wildcards for batch removing.
  • When removing a directory, easel will remove all of its child files (however the empty directory will remain in Canvas).

File Structure

For now it is required to store component files in separate directories, named for their component type (e.g., store definition files for pages in a directory called pages). This requirement may be removed in the future.

Each individual component is defined by a single file using yaml. When a component has some associated body/description content, it should be included in markdown as part of the component's yaml configuration using a multiline string (see the examples directory for examples).

Dates

When specifying dates (e.g., due_at, unlock_at, lock_at), ISO 8601 format should be used. This is temporary until I can build out an internal date management system.

TODO

I'll try to keep this list in order, with the items I'm prioritizing to get done sooner listed first.

  • add a new command which generates a component config file formatted and filled with common options
    • -i flag could prompt user to enter required options interactively
  • manage datetimes for user
    • relative semester/time specification
      • e.g.,
        • module 1 day 2 start of class,
        • module 6 class 1 start of class,
        • week 4 day -1 end of class
        • start of week 2 (first day of the week in the morning)
        • end of week 3 (last day of the week at midnight)
      • instead of weeks use modules?
        • define module with respect to 150-minute chunks (equivalent of one week)
        • gives us more flexibility for holidays
        • user specifies dependency tree for modules in terms of prerequisite modules
        • easel schedules the modules based on semester dates
        • deadlines are declared with respect to the module (which may carry over to another week, depending on holidays, etc.
      • fields
        • due_at
          • detect if already in iso format and if not, parse as the relative formate
        • length (for modules only)
          • the number of 50 minute blocks in this module
        • previous module?
        • next module?
      • implementation
        • detect semester dates
          • start and end dates (including finals?)
          • holidays
        • map modules to the semester based on module length and semester dates
          • if no modules, just use weeks
    • API requires strings in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ (e.g., "2013-01-23T23:59:00-07:00")
    • automate daylight savings translations
    • maybe consider automating final exam times too
  • test other types of module items
    • working:
      • Page
      • Assignment
      • File
      • SubHeader
      • ExternalUrl
    • need testing:
      • Discussion
      • Quiz
        • pulls down as an assignment?
      • ExternalTool
  • better logging
  • I've been assuming user pulls or pushes from the course's root directory. Need to search for the component dirs
  • Figure out the workflow for editing page/assignment content. Canvas uses html, I'd prefer to express it in markdown.
    • First proposal: locally in markdown, convert to html when pushing. Don't edit content in Canvas (since we can't faithfully convert html to md). Pulling would not overwrite the component's contents.
  • multiple courses (i.e., sections).
    • implicit iteration
      • push: pushes to all courses, unless specified (e.g., -c 02)
      • pull: pulls from all courses, checks for and reports any differences
        • need to add a prompt for overwrite, manually merge, or abort
  • pull/push everything in transactions
    • use db as intermediate step, only go to Canvas if db transaction succeeded
    • workflow for pulling whether to overwrite, manually merge, or abort
    • When pushing, update database with result (e.g., when pushing to a new course, the canvas id will be different)
  • merge prompt
  • add a progress bar for pushing and pulling
  • add a command to publish components rather than changing the published field in the file?
  • GUI?
  • support Formula type quiz questions. it's almost there but it probably requires the weird json list formatting as with QuizQuestion.answers. See the TODO comment in __iter__ from quiz_question.py
  • delete folders
  • auto generate syllabus parts

Thoughts

  • Enforce directories? (e.g., pages, assignments, modules)
    • Or when pushing a component, save its filepath in the db
  • Component files that only have yaml (no md or html), should the extension be yaml or stay consistent with md?
  • We should enable expressing dates/times that are relative to the section meeting time (e.g., beginning of class, end of class, Fridays)
  • would it be worth adding in grading stuff eventually?
  • Some fields would be useful to Easel but not necessary for instructor edits (e.g., record ids, component status). Do we keep those in the DB but not write them to file?
  • should quiz questions be in their own file? Options:
    • a single quiz's questions in one file. easier to implement but it would be harder to reuse them
    • one file per question, easy to move around, but how to uniquely identify each question? (for the name of the file)
    • one file per question category (e.g., all requrements engineering questions) this is probably the best user-focused approach, but harder to implement?
  • Question groups only work with question banks. We can't create question banks via the api. What other option do we have? Ideally we create a question group directly with the questions that should go in it. E.g.,
    - question_name: g1
      question_type: group
      questions:
        - quiz_questions/functions.yaml
        - quiz_questions/functions.yaml
        - quiz_questions/functions.yaml
    
    The preprocessor would intercept the question_type (it's invalid anyway) and make the api call to create the group, passing in the questions. So remember that in case they open up question groups to work by specifying a question directly.
  • by default, canvas courses do not enable weighted assignment groups
    • allow users to update the course with this (or make it the default?)
    • I set it up for now to automatically weight (default to True in push_syllabus of course.py)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

easel-cli-1.1.1.tar.gz (49.6 kB view hashes)

Uploaded Source

Built Distribution

easel_cli-1.1.1-py3-none-any.whl (53.7 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page