when you add a learning outcome to a rubric, you can optionally choose to
ignore it when grading. this commit ensures that point totals acutally ignore
the line consistently across the varios places they are displayed, including
when creating the rubric, when grading from the speed grader, and when grading
from the assignment submission page.
test plan:
- create a learning outcome
- create an assignment with a rubric for grading
- make sure the rubric has at least one normal criterion for grading
- include the learning outcome, and choose ignore for grading
- make sure point totals are consistent in the rubric form
- grade the assignment with the speed_grader, make sure point totals are
consistent.
- modify the grade at /courses/c.id/assignments/a.id/submissions/user.id and
make sure point totals are still constistant.
Change-Id: I454d73897ee5d4e6413b7c653ffafa1c4ac98e2e
Reviewed-on: https://gerrit.instructure.com/8263
Reviewed-by: Cody Cutrer <cody@instructure.com>
Tested-by: Hudson <hudson@instructure.com>
fixes#6585, #7096
by normalizing, it's impossible to not maintain a column that isn't
there (yes, triple negative is intended in that sentence), thus
preventing sending notifications to the wrong user after a merge
or any other bugs that would have missing maintaining the
denormalization.
test plan:
* test notifications in general
* merge two active users together - their notification preferences
should survive the merge
Change-Id: I239d810c5499b94550b65128cac71c42cedd11ba
Reviewed-on: https://gerrit.instructure.com/8013
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Jacob Fugal <jacob@instructure.com>
Just fire off the kaltura upload jobs in the background, so that the
import can continue. Attachments in the special media_objects export
folder will still be deleted when those uploads complete.
Note we still wait for canvas common cartidge imports, since we need the
completed media object imports in order to translate the rich text
content. We plan to address this limitation later.
test plan:
* do a course import from a source other than canvas CC, verify that
the import finishes without waiting on Kaltura.
* do a course import from a canvas CC package with
some audio/video. Verify that the import finishes after waiting on
kaltura, but the attachments in /media_objects are removed as the
kaltura jobs complete.
fixes#5383
Change-Id: I7dbf12c211fc332fe9d4f16bc4d31fd0656c1941
Reviewed-on: https://gerrit.instructure.com/8031
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
test plan:
* import the same deleted enrollment several times; only one should be
in the db
Change-Id: If067578fc1b42e501e4c43243f9745654b633701
Reviewed-on: https://gerrit.instructure.com/8417
Reviewed-by: Zach Wily <zach@instructure.com>
Tested-by: Hudson <hudson@instructure.com>
affects: pseudonym/login api
test plan:
* send request as authorized user and verify that
pseudonym is updated;
* send request as unauthorized user and verify that
request is rejected with 401;
* attempt to set a protected attribute like password_salt
and verify that 400 is returned;
* attempt to change your own pseudonym and verify that it's
allowed.
Change-Id: I83562ddd6dbf83c47bcc1adadfda9f9e1a4867da
Reviewed-on: https://gerrit.instructure.com/8233
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
sometimes urls to files/images have relative urls instead of
the id of a file. Those should also be attempted to be
translated.
Test Plan:
* Make an assessment question with both types of urls
* run translate links on that question
* The urls should be happy when done
refs #6951
Change-Id: I9d4a526357871ac8ea7e1018df6c39f0dda95060
Reviewed-on: https://gerrit.instructure.com/8196
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
test plan:
* enroll a student, and link an observer
* as the student, take a quiz
* as the observer, go to the grade summary page
* click the quiz
* you should see the quiz results for the student
Change-Id: Id0bfa2e0c1c0c7f0ce9f21095781251be47d2847
Reviewed-on: https://gerrit.instructure.com/8392
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
was using a cached @context, so the first person
who viewed the assignment was the context
Change-Id: I35fe55040d00ee6cc7375e3e830fd38316b4e6f0
Reviewed-on: https://gerrit.instructure.com/8314
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Zach Pendleton <zachp@instructure.com>
The importer was incorrectly using the external tool's
migration id as the content tags migration id. This made
it so that links to the same tool just updated the first
tag instead of creating a new one.
Test Plan:
* Create an external tool
* Link two module items to it on the modules page
* Export that course and import it into a different course
* The imported module should have both items and not just the second one
refs #7017
Change-Id: Iba377b9085607e3503f7f0113a4b9720bf8f0305
Reviewed-on: https://gerrit.instructure.com/8290
Reviewed-by: Brian Whitmer <brian@instructure.com>
Tested-by: Hudson <hudson@instructure.com>
affects: users api
test plan:
* as account admin, send request to update
user's name, short name, sortable name,
and time zone and verify that changes are
made and user json is returned;
* as unauthorized user, send same request and
verify that 401 is returned.
Change-Id: I3ede1b0ae62143c47a564a709fade9259a07543e
Reviewed-on: https://gerrit.instructure.com/8027
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
in some S3 regions uploads are only eventually consistent. this means a
file may not be immediately available for reading after we've finished
uploading it. when an appropriate Attachment is created,
MediaObject.add_media_files is called to tell Kaltura to try and
download the file from S3; given the eventual consistency this download
could fail. so we'll delay the add_media_files call by a configurable
amount to give S3 time to settle. fixes#6787
test-plan:
- need delayed jobs running
- record media to kaltura (e.g. discussions); should still work
- set attachment_build_media_object_delay_seconds to something large,
such as 300 (5 minutes).
- record media to kaltura; new media should not be available for 5
minutes, but should be available shortly after the 5 minutes expire
Change-Id: Ie39a4a32c9819dcd577fb84c3a1e6df60b2b3da0
Reviewed-on: https://gerrit.instructure.com/8298
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
api actions use params[:user][:id] and an Account @context, non-api
actions use params[:user_id] and and either params[:account_id] or the
@domain_root_account. correctly handle both paths.
while we're at it, when the API call was added, the json response
changed (lost the pseudonym[] wrapper) and the javascript broke. fix
that, and fix the html so the javascript can correctly populate the
template.
test-plan:
- log in as a site admin
- add a login to a user with a non-default account selected in the
dropdown; the login should be created in the correct account
- log in as a non-site admin account admin
- add a login to a user (no account dropdown); the login should be
created in the current domain root account
- while adding accounts through the UI, observe proper addition of new
login to the logins list
- through the API, add a login to a user via a root account; should
succeed
- through the API, add a login to a user via a non-root account should
fail with a 400 Bad Request
- through the API, attempt to add a login without specifying any
parameters; should return a 404, not a 500
- through the API, attempt to list logins without specifying any
parameters; should return a 404, not a 500
Change-Id: Ic28e73a992916b81a18b40244bbb6d919cccf858
Reviewed-on: https://gerrit.instructure.com/8269
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Zach Pendleton <zachp@instructure.com>
fixes#5968
test plan:
- create a homework assignment with online text entry (or file upload)
- as a student, comment on the homework assignment (don't answer it
though)
- the teacher should not see a todo item for grading the homework
- the assignment page should not show a submission needing grading
Change-Id: Ibfd35b41ced27e59d865f42e823a0d9d46d25dac
Reviewed-on: https://gerrit.instructure.com/8273
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Zach Pendleton <zachp@instructure.com>
Tested-by: Zach Pendleton <zachp@instructure.com>
if we're redirecting to safe files and it's not for download, it's for
inline and we need to include that in the redirect url. also, be
explicit about wanting to download when clicking a link labeled
"Download".
fixes#5433
test-plan:
- have s3 storage enabled and a safe files domain
- upload an image as a file submission
- view the submission in speedgrader; image should be displayed
inline rather than being downloaded
- upload an image on the files tab
- click the download link; should download the image rather than
being shown in browser
- preview the uploaded image from the files tab; it should display
inline
- upload a flash file on the files tab
- preview the uploaded image from the files tab; it should display
inline (independent check necessary because flash is touchy about
content-disposition headers)
- click the download link; should download the file rather than
being shown in browser
Change-Id: Ibd48eb4629aa26052d3d1bce90444146033b9eeb
Reviewed-on: https://gerrit.instructure.com/8275
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
we use content-disposition to force a filename. but we don't always want
the attachment disposition. split cacheable_s3_url into two explicit
methods (cacheable_s3_download_url and cacheable_s3_inline_url) and use
the one at each call site that matches what the non-s3 code uses. refs #5433
while we're at it, switch to using rails caching (redis, memcache, etc.)
instead of the cached_s3_url db column (dropped, along with
s3_url_cached_at).
test-plan:
- turn on s3 file storage.
- put files into s3 then get them out again
- places a download (vs. inline) is expected, should get a download
prompt
- places an inline (vs. download; e.g. speedgrader) is expected,
should not get a download prompt
Change-Id: I34045279ebcf20f0fb0ba0d215b4fdd182176b24
Reviewed-on: https://gerrit.instructure.com/8220
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
test plan,
* fill out the "report a problem" form in the help
dialog and submit
* make sure the last ErrorReport in the db has a
url attribute
OR
if zendesk is enabled up for your environment,
check to make sure that the ticket created at
zendesk has a url and a become_user_id_uri
Change-Id: I4f05b2ebe5a0ba9412395a51457e4f5079e0ff69
Reviewed-on: https://gerrit.instructure.com/8297
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Jon Jensen <jon@instructure.com>
test plan:
1. go to an assignment submission page that allows
for media comments
2. click the media tab
3. close the dialog
4. open the dialog again
5. record media and save
The UI should update showing that the media was
saved. For a full circle approach, go into speed
grader to verify the recording is there.
Change-Id: I2e0b5eaaf4dd257e47a585d75b1917fbcaf5d1dd
Reviewed-on: https://gerrit.instructure.com/8214
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Zach Wily <zach@instructure.com>
since the data migration will take a long time to run, we don't want to
show the filter dropdown thingy until it can be used. along with that,
we now differentiate between not-yet-tagged (null) and no tags (empty
string).
also tweaked it so we show the computed default tags if the conversation
is not-yet-tagged. this way the transition will be seamless from a ui
point of view (there will never be a period where we don't show common
contexts in the ui)
test plan:
1. clear out all tags on conversations, conversation_participants, and
conversation_message_participants
2. confirm that common contexts still appear in the ui, but the filter
is not visible
3. re-run the tag migration
4. confirm that the filter appears once the migration completes (it
could appear slightly before it's done ... basically as soon as all
*your* conversations are tagged)
Change-Id: I6df6b0653bea9d306560917c29ff31625a7b6d93
Reviewed-on: https://gerrit.instructure.com/8340
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Jacob Fugal <jacob@instructure.com>
changed the name clear_rce to clear_wiki_rce in common
Change-Id: I9643bce5bf81f1c6f629266cb4d936f7ab28e70e
Reviewed-on: https://gerrit.instructure.com/8303
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Bryan Madsen <bryan@instructure.com>
implemented generic tags for conversations (and messages), but currently those
only track course/group asset strings. conversations are automatically tagged
with the most relevant courses/groups, i.e.
1. if a specific course/group is a recipient, it will be added as a tag
2. if a course can be inferred, it will be added as a tag. this can happen
several ways:
* if a user is added by browsing/searching under a course
* if a synthetic context or section is a recipient (e.g. course_1_students)
* if a group is a recipient
3. if there are no explicit or inferred tags, we do the old behavior (tag
courses/groups that are shared by > 50% of the audience)
some notes:
1. as was the previous behavior, you can only see tags (courses/groups) that
you belong to. for example, a teacher could send a message to a student
group, but the teacher would not see the group as a tag (though the
students would)
2. tags can be added as recipients are added (though we still just show <= 2
in the UI). a private conversation may get new tags if a bare PM is sent
and a different context can be inferred (see below)
3. private conversations may have tags removed, since we also track them at
a message level. the scenario is if you have a bunch of messages from your
Spanish 101 teacher. the course ends and you then take Spanish 102 from the
same teacher, and the teacher sends the class a bunch of PMs. if you delete
the old messages, your view of the conversation will lose the Spanish 101
tags.
added a course filter dropdown in the ui. currently this is limited to active
enrollments, and only returns courses (no groups)
test plan:
1. test automatic tagging for new conversations
* ensure explicit tags work (course as recipient)
* ensure inferred tags work (e.g. user under a course)
* ensure default tags work (e.g. send a PM to someone you have a class with)
2. test automatic tagging for adding recipients (same as above)
3. test automatic tagging for a new message on a private conversation, i.e.
1. find a user you share 2+ contexts with
2. start a private conversation w/ a single inferred tag, ensure that only
that tag is on the conversation
3. start another private conversation w/ a different inferred tag (this will
reuse the existing conversation), ensure it has both tags
4. test private conversation tag recalculation, i.e. perform test 3, remove a
message, and ensure there is just one tag again
5. test conversation filtering in the UI
migration:
there is no traditional migration per se, rather we will run something like:
Conversation.find_each{ |c| c.migrate_context_tags! }
Change-Id: If467c8739ef39a655ef5a528b0da77213130e825
Reviewed-on: https://gerrit.instructure.com/8225
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Jacob Fugal <jacob@instructure.com>
* hold/unhold/delete in one query, instead of loading each job and then saving it
* fix a long-standing bug where searching for a job by id and then
acting on it, would actually act on all jobs (holding all jobs, etc)
* tweak the timeouts/sleeps on jobs specs to remove about 10s from their runtime
refs #6875
Change-Id: I4db5df1a938bf60b95b9972ce8891adcdc16f4f5
Reviewed-on: https://gerrit.instructure.com/8210
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Cody Cutrer <cody@instructure.com>
sending the xml all at once in the selenium tests was causing
intermittent failures
Change-Id: I6fa0b589cb1be5d2207912ae2d19254e1236ef82
Reviewed-on: https://gerrit.instructure.com/8272
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
fixes#6769, #6939
Test plan:
- enable caching with redis or memcached
- log in to a course page
- use the api to add an external tool with course navigation (see the
redmine bug)
- refresh (you should see the external tool in the navigation)
- delete the external tool
- refresh (nav item should be gone)
Change-Id: I734bd726818ea84ffe333597eb0197db2200c235
Reviewed-on: https://gerrit.instructure.com/8141
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Whitmer <brian@instructure.com>
after importing a rubric into a new course, if you try to add that rubric to
a new assignment, and then immediately editing before saving the assignment
(and making the association), the edit will fail rather than properly creating
a new rubric with the edited changes.
test plan:
- create a course, assignment + rubric
- create a new course and import all content from old course
- create a new assignment, add the imported rubric
- try to edit the rubric immediately, and hit save
- the save should be successful
Change-Id: Ic1993bb5d1523e0066ff8ebd6e02b6d71b338aa4
Reviewed-on: https://gerrit.instructure.com/8111
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Cody Cutrer <cody@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
test plan:
* create a user with pseudonyms in multiple accounts
* log in as a user that's an admin in one account
* that admin should not be able to modify the first user's
pseudonym in other accounts
Change-Id: Ib076cffa00a748d03a45aa2c72f15cb0120a1825
Reviewed-on: https://gerrit.instructure.com/8258
Reviewed-by: Brian Palmer <brianp@instructure.com>
Tested-by: Cody Cutrer <cody@instructure.com>
In the old form, the form would post a blank
report[email] value. because of the way
guess_email works we can't just not submit it.
because it needs to override the "unknown" value
that was put there in the after_save callback when
it was created (but did not yet have an associated
user)
test_plan:
* run the spec
Manual steps:
* in an environment with the canvas_zendesk_plugin
set up, submit something a problem from the
help dialog
* make sure the ticket in zendesk has the right
email address.
Change-Id: I23d983da41e154815c7f28fe64fb70f03a0a9803
fixes: #7018
Reviewed-on: https://gerrit.instructure.com/8255
Reviewed-by: Jon Jensen <jon@instructure.com>
Tested-by: Hudson <hudson@instructure.com>
include all course files when doing a course export
test plan:
1. do a course export
2. confirm that all files are in the export and can be imported
Change-Id: I2cfcd9008f7ba9ef34a19b65c0beaca2d98eb810
Reviewed-on: https://gerrit.instructure.com/8252
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Zach Wily <zach@instructure.com>
test plan:
ensure you can turn service visibility on/off in the various places in
the ui
Change-Id: I0c59405a419aba73a881d70fd368e34d50cacac5
Reviewed-on: https://gerrit.instructure.com/8246
Reviewed-by: Jon Jensen <jon@instructure.com>
Tested-by: Hudson <hudson@instructure.com>
Based on page views created in each 5 minute period. Each 5 minute
bucket stores the user id for each user who requests a page during that
interval.
To get active user counts over long periods, just take the set union
(sunion) of multiple buckets. Buckets are named using the ISO8601
timestamp, rounded down to the 5 minute period.
The only downside of this approach is if you only want counts from the
sunion, you either have to get every user id back and count the returned
rows, or use sunionstore and then scard on the new set (and should
probably delete the tmp set after scard'ing)
test plan: generate page views as multiple users by visiting pages in
the app, then check the redis buckets -- e.g. "keys active_users*"
Change-Id: I3cb31c193096b588e161ba021197c8770bce45b8
Reviewed-on: https://gerrit.instructure.com/8241
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Zach Wily <zach@instructure.com>
Rack has a set limit for how large the "keyspace" for
requests can be. For courses with lots of items the request
exceeded this limit. This commit changes the copy/import
forms to have a single list of items to import instead of
lots of key/value pairs.
Test Plan:
* Manually run some course copies and content imports
closes#6889
Change-Id: I18b1e49261ef8c4ea724a8e0d02ac6bbdaac2fbc
Reviewed-on: https://gerrit.instructure.com/8120
Reviewed-by: Brian Palmer <brianp@instructure.com>
Tested-by: Hudson <hudson@instructure.com>
only support the 'starred' label, and that through its own field in
responses and updates. remove label UI from conversations, leaving just
stars.
test-plan:
- stars still work in conversations.
- labels are gone.
- ability to use freeform labels through API gone.
Change-Id: I61704cf604e081e1894c516462d0112c7e88302c
Reviewed-on: https://gerrit.instructure.com/8205
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Jon Jensen <jon@instructure.com>
add star/unstar conversation functionality. commandeers labels visual
location (top left corner), though label functionality (via context
menu) is still available. labels and stars are mutually exclusive since
stars are implemented using labels: if you star a conversation, any
other label is removed; if you label a starred conversation, the star is
lost.
test-plan:
- star/unstar conversations via icon, should behave as expected
- star/unstar conversations via context menu, should behave as expected
- label a unstarred conversation via context menu; should work as
before except label icon does not appear
- label a starred conversation via context menu; conversation should
be unstarred
- star a labeled conversation; should remove label
- star a conversation, go to the starred scope; the conversation
should be included
- unstar a conversation in the starred scope; the conversation should
be removed
Change-Id: I5e96d666983cf99b3236bd74e0e970e60f16e87a
Reviewed-on: https://gerrit.instructure.com/8098
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Jon Jensen <jon@instructure.com>
refs #6883
Use it when checking if a pseudonym is valid for authentication, but
not for if we need to create a new pseudonym for an account. This
means that you can authenticate via an implicit trust relationship,
but when you are invited to something, it will still try and create
a pseudonym for you in that account.
test plan:
* run existing specs (no behavior is changed without a plugin
to define trust relationships)
Change-Id: Ieb200da27351c185e640e9c50ae1a6054e502c63
Reviewed-on: https://gerrit.instructure.com/8076
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Jacob Fugal <jacob@instructure.com>
test plan:
* log in with site admin credentials at an account on a different shard
Change-Id: Ibdb26b345b409cbbd61cfdb45af563e8cdd0c138
Reviewed-on: https://gerrit.instructure.com/8075
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Tom Metge <tom@instructure.com>
Reviewed-by: Jacob Fugal <jacob@instructure.com>
test plan:
* do an api call providing as_user_id=, but no actual value
* it should succeed
Change-Id: I3114e672ea427935045fa64be7c03c922290d042
Reviewed-on: https://gerrit.instructure.com/8227
Reviewed-by: Zach Wily <zach@instructure.com>
Tested-by: Zach Wily <zach@instructure.com>
this commit makes submission first-class citizens in conversations. this
means that when submission comments are added/deleted, or assignments are
unmuted, conversations and messages will be updated accordingly
the main impacts in the ui are:
1. submissions can be deleted from conversations. if a new comment is
added, they will reappear
2. submissions factor into the message total for the conversation. each
submission counts as a single message, even if there are multiple
comments
3. submission messages affect unread-ness, and are reflected in the
timestamp and text in the conversation preview
test plan:
1. confirm submissions appear in the appropriate conversations, i.e.
* submissions with no comments should not appear in any conversations
* submissions where there are comments but not by instructors:
* should appear in each instructor's private conversation with the
submitter
* should not appear in the submitter's private conversations with
anyone
* submissions where there are comments by instructors:
* should appear in each commenting instructor's private conversation
with the submitter
* should appear in submitter's private conversations with each
commenting instructor
adding or removing submission comments should update private
conversations accordingly (e.g. when one teacher comments on a
submission, it should be removed from the other teachers' private
conversations with the submitter).
2. for each scenario above where the submission comments are added and
appear in conversations, ensure that the submission as a whole behaves
like a single conversation message, i.e.
* the unread conversations count is incremented and the private
conversation is marked as unread (if it didn't exist or was already
read)
* the latest submission comment and timestamp should be reflected in
the conversation pane on the left side
* you can delete the submission from the conversation. if new comments
are posted on the submission, the submission should reappear in the
conversation (provided it still matches the criteria in 1.). note
that submission can not be forwarded to other conversations.
3. submissions should differ from traditional conversation messages in
that:
* they should not trigger conversation notifications
* they should not create/bump conversation stream items. if a
conversation has non-submission messages, the submission and its
comments should appear in the stream item, but they should not
cause it to jump to the top
migration:
existing submissions/comments will be migrated in, but not necessarily
through a traditional rails migration. to bring in those messages, run
the following from the rails console:
Submission.find_each{ |s| s.create_or_update_conversations!(:migrate) }
Change-Id: I06dcb8728402a6c4c613d445b80432a1f2973b73
Reviewed-on: https://gerrit.instructure.com/8086
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Jacob Fugal <jacob@instructure.com>
postgres can't/won't use the index for a DISTINCT query, so be a
bit more explicit with it to get it to use the index
test plan:
* go to /error_reports
* it should load fast, there should be a categories dropdown,
and it should have a few things in it besides all categories
(assuming you have some error reports)
Change-Id: If7c71cd669e7eadc1792eefe1fb6e85a08c054ed
Reviewed-on: https://gerrit.instructure.com/8190
Reviewed-by: Brian Palmer <brianp@instructure.com>
Tested-by: Hudson <hudson@instructure.com>