Package: easel-cli
EaselA Canvas course management tool.Installationpip install easel-cliTo install from the root of the repository:pip install -e .UsageWhen 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 StartedFor each of these operations, refer to their detailed description and usage
below.Tell easel about your Canvas instance (only needs to be run once):easel login <canvas_base_url> <api_token>Initialize easel in a course-specific directory:easel initAdd a course or courses (i.e., sections):easel course add <canvas_course_url>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:Tell easel about your Canvas instance:easel login <canvas_base_url> <api_token>Initialize easel in a course-specific directory:easel initAdd the previous course:easel course add <previous_canvas_course_url>Pull everything from it:easel pullAdd the new course:easel course add <new_canvas_course_url>Push everything to the new course:easel --course <new_course_id> pushAt this point, you may remove your old course:easel course remove <old_course_id>Individual Component ReferencesThese are the components currently managed by easel. For configuration, see the
linked documentation.AssignmentsAssignment GroupsExternal ToolsCourse Grading SchemeCourse SettingsModulesNavigation TabsPagesQuizzesSyllabusCommand ReferenceLogineasel login <canvas_base_url> <api_token>E.g.,easel login https://school.instructure.com yourT0kenH3reOnly 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".Initeasel initRun 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.Courseeasel course add <canvas_course_url>E.g.,easel course add https://school.instructure.com/courses/615446Hooks up the database to a Canvas course. Run this one time per Canvas course
(once per section taught per semester).easel course listList all Canvas courses that are tracked in the database.PushReads 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 pushor to push to a specific course:easel --course <course_id> pushTo push a specific component or components:easel push [component_filepath ...]E.g.,easel push pages/lesson-1.yamlNotes on pushing files:Files placed in thefilesdirectory will be pushed as they are (ignoring
thefilesparent directory).Supports multiple filename arguments and wildcards for batch pushing.Use the--hiddenflag to unpublish the file(s) as hidden when pushed (by
default canvas publishes files when you upload them).When pushing a directory,easelwill push all of its child files.Course filtering:Use the--courseflag (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.yamlRemoveRemove 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.yamlNote for files:Supports multiple filename arguments and wildcards for batch removing.When removing a directory,easelwill remove all of its child files (however
the empty directory will remain in Canvas).File StructureFor 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
calledpages). 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 theexamplesdirectory for examples).DatesWhen specifying dates (e.g., due_at, unlock_at, lock_at),ISO 8601 formatshould be used. This
is temporary until I can build out an internal date management system.TODOI'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 interactivelymanage datetimes for userrelative semester/time specificatione.g.,module 1 day 2 start of class,module 6 class 1 start of class,week 4 day -1 end of classstart 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 holidaysuser specifies dependency tree for modules in terms of
prerequisite moduleseasel schedules the modules based on semester datesdeadlines are declared with respect to the module (which may carry
over to another week, depending on holidays, etc.fieldsdue_atdetect if already in iso format and if not, parse as the
relative formatelength (for modules only)the number of 50 minute blocks in this moduleprevious module?next module?implementationdetect semester datesstart and end dates (including finals?)holidaysmap modules to the semester based on module length and semester datesif no modules, just use weeksAPI requires strings in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ (e.g., "2013-01-23T23:59:00-07:00")automate daylight savings translationsmaybe consider automating final exam times tootest other types of module itemsworking:PageAssignmentFileSubHeaderExternalUrlneed testing:DiscussionQuizpulls down as an assignment?ExternalToolbetter loggingI've been assuming user pulls or pushes from the course's root directory. Need
to search for the component dirsFigure 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 iterationpush: pushes to all courses, unless specified (e.g., -c 02)pull: pulls from all courses, checks for and reports any differencesneed to add a prompt for overwrite, manually merge, or abortpull/push everything in transactionsuse db as intermediate step, only go to Canvas if db transaction succeededworkflow for pulling whether to overwrite, manually merge, or abortWhen pushing, update database with result (e.g., when pushing to a new
course, the canvas id will be different)merge prompthttps://twitter.com/_wilfredh/status/1413002445366591496add a progress bar for pushing and pullingadd a command to publish components rather than changing the published field
in the file?GUI?https://github.com/willmcgugan/richhttps://github.com/willmcgugan/textualhttps://github.com/pfalcon/picotuihttps://docs.python.org/3/howto/curses.htmlsupport 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__fromquiz_question.pydelete folderssince I don't explicitly create folders, I don't have their ids, so I'd
have to get that at some point and track it to eventually delete ithttps://canvas.instructure.com/doc/api/files.html#method.folders.api_destroyauto generate syllabus partsThoughtsEnforce directories? (e.g., pages, assignments, modules)Or when pushing a component, save its filepath in the dbComponent 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 themone 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.yamlThe preprocessor would intercept thequestion_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 groupsallow 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)