fix common cartridge validation errors on course export

closes CNVS-38795

Test Plan
  1. Create a course with all of the following
     - weighted assignment groups
     - quizzes (with at least one question)
     - outcome with at least one aligned item
     - module with at least one assignment
     - one or more rubrics
     - no media tracks
  2. Export the course to a common cartridge (CC) file
  3. Download and unzip the CC
  4. Validate all XML files from the CC vs. cccv1p0.xsd
     (with the exception of imsmanifest.xml and
      assessment_qti.xml since these use IMS's schemata
      and not our own; see the 2-Nov-17 comment on the
      ticket for details)
  5. Add a media track to the course and repeat (1-4)
  6. Import the CC from (5) into a new course

Change-Id: Ib2ef70737189d0107159bf0a93a7cd86d2dfd1c0
Reviewed-on: https://gerrit.instructure.com/127393
Reviewed-by: James Williams  <jamesw@instructure.com>
Tested-by: Jenkins
Product-Review: James Williams  <jamesw@instructure.com>
QA-Review: James Williams  <jamesw@instructure.com>
This commit is contained in:
Matt Taylor 2017-09-22 09:15:29 -05:00 committed by Jeremy Stanley
parent e41f0de4f0
commit eba07fd3fe
9 changed files with 177 additions and 79 deletions

View File

@ -185,7 +185,7 @@ module CC
node.rubric_external_identifier assignment.rubric.id
end
node.rubric_use_for_grading assoc.use_for_grading
node.rubric_hide_score_total assoc.hide_score_total
node.rubric_hide_score_total !!assoc.hide_score_total
if assoc.summary_data && assoc.summary_data[:saved_comments]
node.saved_rubric_comments do |sc_node|
assoc.summary_data[:saved_comments].each_pair do |key, vals|

View File

@ -24,6 +24,7 @@ module CC
include LearningOutcomes
include Rubrics
include Events
include WebResources
def add_canvas_non_cc_data
migration_id = create_key(@course)
@ -43,8 +44,10 @@ module CC
resources << run_and_set_progress(:files_meta_path, nil, I18n.t('course_exports.errors.file_meta', "Failed to export file meta data"))
resources << run_and_set_progress(:create_events, 25, I18n.t('course_exports.errors.events', "Failed to export calendar events"))
File.write(File.join(@canvas_resource_dir, CCHelper::MEDIA_TRACKS), '') # just in case an error happens later
resources << File.join(CCHelper::COURSE_SETTINGS_DIR, CCHelper::MEDIA_TRACKS)
if export_media_objects?
File.write(File.join(@canvas_resource_dir, CCHelper::MEDIA_TRACKS), '') # just in case an error happens later
resources << File.join(CCHelper::COURSE_SETTINGS_DIR, CCHelper::MEDIA_TRACKS)
end
# Create the syllabus resource
if @course.syllabus_body && (export_symbol?(:syllabus_body) || export_symbol?(:all_syllabus_body))
@ -117,6 +120,7 @@ JOKE
rel_path = File.join(CCHelper::COURSE_SETTINGS_DIR, CCHelper::COURSE_SETTINGS)
document = Builder::XmlMarkup.new(:target=>course_file, :indent=>2)
end
document.instruct!
document.course("identifier" => migration_id,
"xmlns" => CCHelper::CANVAS_NAMESPACE,
@ -156,7 +160,7 @@ JOKE
end
@course.disable_setting_defaults do # so that we don't copy defaulted settings
atts.each do |att|
atts.uniq.each do |att|
c.tag!(att, @course.send(att)) unless @course.send(att).nil? || @course.send(att) == ''
end
end

View File

@ -22,7 +22,7 @@ module CC
module CCHelper
CANVAS_NAMESPACE = 'http://canvas.instructure.com/xsd/cccv1p0'
XSD_URI = 'http://canvas.instructure.com/xsd/cccv1p0.xsd'
XSD_URI = 'https://canvas.instructure.com/xsd/cccv1p0.xsd'
# IMS formats/types
IMS_DATE = "%Y-%m-%d"

View File

@ -66,7 +66,8 @@ module CC::Importer::Canvas
'self_enrollment', 'hide_final_grade', 'grading_standard_enabled',
'hide_distribution_graphs', 'allow_student_discussion_topics',
'allow_student_discussion_editing', 'show_announcements_on_home_page',
'restrict_student_future_view', 'restrict_student_past_view'
'restrict_student_future_view', 'restrict_student_past_view', 'show_total_grade_as_points',
'organize_epub_by_content_type', 'enable_offline_web_export'
].each do |bool_val|
val = get_bool_val(doc, bool_val)
course[bool_val] = val unless val.nil?

View File

@ -192,10 +192,13 @@ module CC
tracks_file.close
end
def export_media_objects?
CanvasKaltura::ClientV3.config && !for_course_copy
end
MAX_MEDIA_OBJECT_SIZE = 4.gigabytes
def add_media_objects(html_content_exporter)
return if for_course_copy
return unless CanvasKaltura::ClientV3.config
return unless export_media_objects?
# check to make sure we don't export more than 4 gigabytes of media objects
total_size = 0

View File

@ -3,14 +3,60 @@
xmlns="http://canvas.instructure.com/xsd/cccv1p0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<!-- todo: useful comments as documentation ;) -->
<!--
Builder::XmlMarkup generates XML elements with an empty value when the corresponding
Ruby expression is nil, but XML validators do not allow an empty value for non-string
types. Define optional_integer, optional_dateTime and optional_float types for such elements.
-->
<xs:simpleType name="optional_integer">
<xs:union>
<xs:simpleType>
<xs:restriction base='xs:string'>
<xs:length value="0"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType>
<xs:restriction base='xs:integer' />
</xs:simpleType>
</xs:union>
</xs:simpleType>
<xs:simpleType name="optional_dateTime">
<xs:union>
<xs:simpleType>
<xs:restriction base='xs:string'>
<xs:length value="0"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType>
<xs:restriction base='xs:dateTime' />
</xs:simpleType>
</xs:union>
</xs:simpleType>
<xs:simpleType name="optional_float">
<xs:union>
<xs:simpleType>
<xs:restriction base='xs:string'>
<xs:length value="0"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType>
<xs:restriction base='xs:float' />
</xs:simpleType>
</xs:union>
</xs:simpleType>
<xs:element name="course">
<xs:complexType>
<xs:all minOccurs="0" maxOccurs="1">
<xs:element name="title" type="xs:string" minOccurs="1"/>
<xs:element name="course_code" type="xs:string" minOccurs="0"/>
<xs:element name="start_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="conclude_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="start_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="conclude_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="tab_configuration" type="xs:string" minOccurs="0"/>
<xs:element name="is_public" type="xs:boolean" minOccurs="0"/>
<xs:element name="indexed" type="xs:boolean" minOccurs="0"/>
<xs:element name="publish_grades_immediately" type="xs:boolean" minOccurs="0"/>
@ -30,15 +76,21 @@
<xs:element name="hide_final_grade" type="xs:boolean" minOccurs="0"/>
<xs:element name="grading_standard_enabled" type="xs:boolean" minOccurs="0"/>
<xs:element name="grading_standard_identifier_ref" type="xs:string" minOccurs="0"/>
<xs:element name="grading_standard_id" type="xs:integer" minOccurs="0"/>
<xs:element name="storage_quota" type="xs:float" minOccurs="0"/>
<xs:element name="grading_standard_id" type="optional_integer" minOccurs="0"/>
<xs:element name="storage_quota" type="optional_float" minOccurs="0"/>
<xs:element name="lock_all_announcements" type="xs:boolean" minOccurs="0"/>
<xs:element name="public_syllabus" type="xs:boolean" minOccurs="0"/>
<xs:element name="hide_distribution_graphs" type="xs:boolean" minOccurs="0"/>
<xs:element name="allow_student_discussion_topics" type="xs:boolean" minOccurs="0"/>
<xs:element name="allow_student_discussion_editing" type="xs:boolean" minOccurs="0"/>
<xs:element name="show_announcements_on_home_page" type="xs:boolean" minOccurs="0"/>
<xs:element name="home_page_announcement_limit" type="optional_integer" minOccurs="0"/>
<xs:element name="restrict_student_future_view" type="xs:boolean" minOccurs="0"/>
<xs:element name="restrict_student_past_view" type="xs:boolean" minOccurs="0"/>
<xs:element name="public_syllabus_to_auth" type="xs:boolean" minOccurs="0"/>
<xs:element name="show_total_grade_as_points" type="xs:boolean" minOccurs="0"/>
<xs:element name="organize_epub_by_content_type" type="xs:boolean" minOccurs="0"/>
<xs:element name="enable_offline_web_export" type="xs:boolean" minOccurs="0"/>
<xs:element name="image_url" type="xs:string" minOccurs="0"/>
<xs:element name="image_identifier_ref" type="xs:string" minOccurs="0"/>
<xs:element name="default_view" minOccurs="0">
@ -84,10 +136,11 @@
<xs:complexType name="assignmentType">
<xs:all minOccurs="0" maxOccurs="1">
<xs:element name="title" type="xs:string" minOccurs="1"/>
<xs:element name="due_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="lock_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="unlock_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="peer_reviews_due_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="workflow_state" type="xs:string" minOccurs="0"/>
<xs:element name="due_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="lock_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="unlock_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="peer_reviews_due_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="all_day_date" type="xs:date" minOccurs="0"/>
<xs:element name="assignment_group_identifierref" type="xs:string" minOccurs="0"/>
<xs:element name="grading_standard_identifierref" type="xs:string" minOccurs="0"/>
@ -98,20 +151,21 @@
<xs:element name="rubric_hide_score_total" type="xs:boolean" minOccurs="0"/>
<xs:element name="quiz_identifierref" type="xs:string" minOccurs="0"/>
<xs:element name="allowed_extensions" type="xs:string" minOccurs="0"/>
<xs:element name="points_possible" type="xs:float" minOccurs="0"/>
<xs:element name="min_score" type="xs:float" minOccurs="0"/>
<xs:element name="max_score" type="xs:float" minOccurs="0"/>
<xs:element name="mastery_score" type="xs:float" minOccurs="0"/>
<xs:element name="points_possible" type="optional_float" minOccurs="0"/>
<xs:element name="min_score" type="optional_float" minOccurs="0"/>
<xs:element name="max_score" type="optional_float" minOccurs="0"/>
<xs:element name="mastery_score" type="optional_float" minOccurs="0"/>
<xs:element name="grading_type" type="xs:string" minOccurs="0"/>
<xs:element name="all_day" type="xs:boolean" minOccurs="0"/>
<xs:element name="submission_types" type="xs:string" minOccurs="0"/>
<xs:element name="position" type="xs:integer" minOccurs="0"/>
<xs:element name="position" type="optional_integer" minOccurs="0"/>
<xs:element name="turnitin_enabled" type="xs:boolean" minOccurs="0"/>
<xs:element name="turnitin_settings" type="xs:string" minOccurs="0"/>
<xs:element name="vericite_enabled" type="xs:boolean" minOccurs="0"/>
<xs:element name="peer_review_count" type="xs:integer" minOccurs="0"/>
<xs:element name="peer_review_count" type="optional_integer" minOccurs="0"/>
<xs:element name="peer_reviews_assigned" type="xs:boolean" minOccurs="0"/>
<xs:element name="peer_reviews" type="xs:boolean" minOccurs="0"/>
<xs:element name="intra_group_peer_reviews" type="xs:boolean" minOccurs="0"/>
<xs:element name="automatic_peer_reviews" type="xs:boolean" minOccurs="0"/>
<xs:element name="anonymous_peer_reviews" type="xs:boolean" minOccurs="0"/>
<xs:element name="moderated_grading" type="xs:boolean" minOccurs="0"/>
@ -123,6 +177,7 @@
<xs:element name="external_tool_url" type="xs:string" minOccurs="0"/>
<xs:element name="external_tool_new_tab" type="xs:boolean" minOccurs="0"/>
<xs:element name="freeze_on_copy" type="xs:boolean" minOccurs="0"/>
<xs:element name="module_locked" type="xs:boolean" minOccurs="0"/>
<xs:element name="muted" type="xs:boolean" minOccurs="0"/>
<xs:element name="omit_from_final_grade" type="xs:boolean" minOccurs="0"/>
<xs:element name="only_visible_to_overrides" type="xs:boolean" minOccurs="0"/>
@ -138,7 +193,7 @@
<xs:element name="saved_rubric_comments" minOccurs="0">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="comment" type="xs:string">
<xs:element name="comment">
<xs:complexType>
<xs:attribute name="criterion_id" type="xs:string" use="required"/>
</xs:complexType>
@ -149,14 +204,14 @@
<xs:element name="assignment_overrides" minOccurs="0">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="override" type="xs:string">
<xs:element name="override">
<xs:complexType>
<xs:attribute name="set_type" type="xs:string" use="required"/>
<xs:attribute name="set_id" type="xs:string" use="required"/>
<xs:attribute name="title" type="xs:string" use="required"/>
<xs:attribute name="due_at" type="xs:dateTime"/>
<xs:attribute name="unlock_at" type="xs:dateTime"/>
<xs:attribute name="lock_at" type="xs:dateTime"/>
<xs:attribute name="due_at" type="optional_dateTime"/>
<xs:attribute name="unlock_at" type="optional_dateTime"/>
<xs:attribute name="lock_at" type="optional_dateTime"/>
<xs:attribute name="due_at_overridden" type="xs:boolean"/>
<xs:attribute name="unlock_at_overridden" type="xs:boolean"/>
<xs:attribute name="lock_at_overridden" type="xs:boolean"/>
@ -175,10 +230,10 @@
<xs:element name="topic_id" type="xs:ID" minOccurs="1"/>
<xs:element name="title" type="xs:string" minOccurs="1"/>
<xs:element name="assignment" type="assignmentType" minOccurs="0"/>
<xs:element name="posted_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="delayed_post_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="lock_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="position" type="xs:integer" minOccurs="0"/>
<xs:element name="posted_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="delayed_post_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="lock_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="position" type="optional_integer" minOccurs="0"/>
<xs:element name="pinned" type="xs:boolean" minOccurs="0"/>
<xs:element name="require_initial_post" type="xs:boolean" minOccurs="0"/>
<xs:element name="external_feed_identifierref" type="xs:string" minOccurs="0"/>
@ -189,7 +244,7 @@
<xs:element name="allow_rating" type="xs:boolean" minOccurs="0"/>
<xs:element name="only_graders_can_rate" type="xs:boolean" minOccurs="0"/>
<xs:element name="sort_by_rating" type="xs:boolean" minOccurs="0"/>
<xs:element name="todo_date" type="xs:dateTime" minOccurs="0"/>
<xs:element name="todo_date" type="optional_dateTime" minOccurs="0"/>
<xs:element name="discussion_type" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
@ -220,12 +275,13 @@
<xs:all minOccurs="0">
<xs:element name="title" type="xs:string" minOccurs="1"/>
<xs:element name="workflow_state" type="xs:string" minOccurs="1"/>
<xs:element name="position" type="xs:integer" minOccurs="0"/>
<xs:element name="start_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="end_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="unlock_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="position" type="optional_integer" minOccurs="0"/>
<xs:element name="start_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="end_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="unlock_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="locked" type="xs:boolean" minOccurs="0"/>
<xs:element name="require_sequential_progress" type="xs:boolean" minOccurs="0"/>
<xs:element name="requirement_count" type="xs:integer" minOccurs="0"/>
<xs:element name="requirement_count" type="optional_integer" minOccurs="0"/>
<xs:element name="prerequisites" minOccurs="0">
<xs:complexType>
@ -252,9 +308,12 @@
<xs:element name="content_type" type="xs:string" minOccurs="1"/>
<xs:element name="title" type="xs:string" minOccurs="1"/>
<xs:element name="identifierref" type="xs:string" minOccurs="0"/>
<xs:element name="global_identifierref" type="xs:string" minOccurs="0"/>
<xs:element name="url" type="xs:string" minOccurs="0"/>
<xs:element name="position" type="xs:integer" minOccurs="0"/>
<xs:element name="indent" type="xs:integer" minOccurs="0"/>
<xs:element name="position" type="optional_integer" minOccurs="0"/>
<xs:element name="indent" type="optional_integer" minOccurs="0"/>
<xs:element name="workflow_state" type="xs:string" minOccurs="0"/>
<xs:element name="new_tab" type="xs:string" minOccurs="0"/>
</xs:all>
<xs:attribute name="identifier" type="xs:ID" use="required"/>
</xs:complexType>
@ -270,8 +329,8 @@
<xs:complexType>
<xs:all minOccurs="0">
<xs:element name="identifierref" type="xs:string" minOccurs="1"/>
<xs:element name="min_score" type="xs:float" minOccurs="0"/>
<xs:element name="max_score" type="xs:float" minOccurs="0"/>
<xs:element name="min_score" type="optional_float" minOccurs="0"/>
<xs:element name="max_score" type="optional_float" minOccurs="0"/>
</xs:all>
<xs:attribute name="type" type="xs:string" use="required"/>
</xs:complexType>
@ -336,8 +395,8 @@
<xs:complexType>
<xs:all minOccurs="0">
<xs:element name="title" type="xs:string" minOccurs="1"/>
<xs:element name="position" type="xs:integer" minOccurs="0"/>
<xs:element name="group_weight" type="xs:float" minOccurs="0"/>
<xs:element name="position" type="optional_integer" minOccurs="0"/>
<xs:element name="group_weight" type="optional_float" minOccurs="0"/>
<xs:element name="rules" minOccurs="0">
<xs:complexType>
<xs:sequence>
@ -354,7 +413,7 @@
</xs:simpleType>
</xs:element>
<xs:element name="identifierref" type="xs:string" minOccurs="0"/>
<xs:element name="drop_count" type="xs:integer" minOccurs="0"/>
<xs:element name="drop_count" type="optional_integer" minOccurs="0"/>
</xs:all>
</xs:complexType>
</xs:element>
@ -379,7 +438,7 @@
<xs:element name="data" type="xs:string" minOccurs="1"/>
</xs:all>
<xs:attribute name="identifier" type="xs:ID" use="required"/>
<xs:attribute name="version" type="xs:integer" default="1"/>
<xs:attribute name="version" type="optional_integer" default="1"/>
</xs:complexType>
</xs:element>
</xs:sequence>
@ -393,7 +452,7 @@
<xs:all minOccurs="0">
<xs:element name="id" type="xs:string" minOccurs="0"/>
<xs:element name="description" type="xs:string" minOccurs="1"/>
<xs:element name="points" type="xs:float" minOccurs="1"/>
<xs:element name="points" type="optional_float" minOccurs="1"/>
<xs:element name="criterion_id" type="xs:string" minOccurs="0"/>
<xs:element name="long_description" type="xs:string" minOccurs="0"/>
</xs:all>
@ -411,10 +470,10 @@
<xs:all minOccurs="0">
<xs:element name="title" type="xs:string" minOccurs="0"/>
<xs:element name="description" type="xs:string" minOccurs="0"/>
<xs:element name="points_possible" type="xs:float" minOccurs="0"/>
<xs:element name="mastery_points" type="xs:float" minOccurs="0"/>
<xs:element name="points_possible" type="optional_float" minOccurs="0"/>
<xs:element name="mastery_points" type="optional_float" minOccurs="0"/>
<xs:element name="calculation_method" type="xs:string" minOccurs="0"/>
<xs:element name="calculation_int" type="xs:integer" minOccurs="0"/>
<xs:element name="calculation_int" type="optional_integer" minOccurs="0"/>
<xs:element name="ratings" type="ratingsType" minOccurs="0"/>
<xs:element name="external_identifier" type="xs:string" minOccurs="0"/>
<xs:element name="is_global_outcome" type="xs:boolean" minOccurs="0"/>
@ -427,8 +486,8 @@
<xs:element name="content_type" type="xs:string" minOccurs="1"/>
<xs:element name="content_id" type="xs:string" minOccurs="1"/>
<xs:element name="mastery_type" type="xs:string" minOccurs="0"/>
<xs:element name="mastery_score" type="xs:float" minOccurs="0"/>
<xs:element name="position" type="xs:integer" minOccurs="0"/>
<xs:element name="mastery_score" type="optional_float" minOccurs="0"/>
<xs:element name="position" type="optional_integer" minOccurs="0"/>
</xs:all>
</xs:complexType>
</xs:element>
@ -465,7 +524,7 @@
<xs:element name="read_only" type="xs:boolean" minOccurs="0"/>
<xs:element name="reusable" type="xs:boolean" minOccurs="0"/>
<xs:element name="public" type="xs:boolean" minOccurs="0"/>
<xs:element name="points_possible" type="xs:float" minOccurs="0"/>
<xs:element name="points_possible" type="optional_float" minOccurs="0"/>
<xs:element name="hide_score_total" type="xs:boolean" minOccurs="0"/>
<xs:element name="free_form_criterion_comments" type="xs:boolean" minOccurs="0"/>
<xs:element name="external_identifier" type="xs:string" minOccurs="0"/>
@ -479,8 +538,8 @@
<xs:element name="criterion_id" type="xs:string" minOccurs="0"/>
<xs:element name="description" type="xs:string" minOccurs="0"/>
<xs:element name="long_description" type="xs:string" minOccurs="0"/>
<xs:element name="points" type="xs:float" minOccurs="0"/>
<xs:element name="mastery_points" type="xs:float" minOccurs="0"/>
<xs:element name="points" type="optional_float" minOccurs="0"/>
<xs:element name="mastery_points" type="optional_float" minOccurs="0"/>
<xs:element name="ignore_for_scoring" type="xs:boolean" minOccurs="0"/>
<xs:element name="learning_outcome_identifierref" type="xs:string" minOccurs="0"/>
<xs:element name="criterion_use_range" type="xs:boolean" minOccurs="0"/>
@ -534,11 +593,12 @@
<xs:all minOccurs="0" maxOccurs="1">
<xs:element name="title" type="xs:string" minOccurs="1"/>
<xs:element name="description" type="xs:string" minOccurs="1"/>
<xs:element name="points_possible" type="xs:float" minOccurs="0"/>
<xs:element name="points_possible" type="optional_float" minOccurs="0"/>
<xs:element name="assignment" type="assignmentType" minOccurs="0"/>
<xs:element name="lock_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="unlock_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="due_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="lock_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="unlock_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="module_locked" type="xs:boolean" minOccurs="0"/>
<xs:element name="due_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="shuffle_answers" type="xs:boolean" minOccurs="0"/>
<xs:element name="hide_results" type="xs:string" minOccurs="0"/>
<xs:element name="require_lockdown_browser" type="xs:boolean" minOccurs="0"/>
@ -548,11 +608,13 @@
<xs:element name="access_code" type="xs:string" minOccurs="0"/>
<xs:element name="ip_filter" type="xs:string" minOccurs="0"/>
<xs:element name="show_correct_answers" type="xs:boolean" minOccurs="0"/>
<xs:element name="show_correct_answers_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="hide_correct_answers_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="anonymous_submissions" type="xs:boolean" minOccurs="0"/>
<xs:element name="could_be_locked" type="xs:boolean" minOccurs="0"/>
<xs:element name="available" type="xs:boolean" minOccurs="0"/>
<xs:element name="time_limit" type="xs:integer" minOccurs="0"/>
<xs:element name="allowed_attempts" type="xs:integer" minOccurs="0"/>
<xs:element name="time_limit" type="optional_integer" minOccurs="0"/>
<xs:element name="allowed_attempts" type="optional_integer" minOccurs="0"/>
<xs:element name="one_question_at_a_time" type="xs:boolean" minOccurs="0"/>
<xs:element name="cant_go_back" type="xs:boolean" minOccurs="0"/>
<xs:element name="one_time_results" type="xs:boolean" minOccurs="0"/>
@ -576,6 +638,7 @@
<xs:restriction base="xs:string">
<xs:enumeration value="keep_latest"/>
<xs:enumeration value="keep_highest"/>
<xs:enumeration value="keep_average"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
@ -583,14 +646,14 @@
<xs:element name="assignment_overrides" minOccurs="0">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="override" type="xs:string">
<xs:element name="override">
<xs:complexType>
<xs:attribute name="set_type" type="xs:string" use="required"/>
<xs:attribute name="set_id" type="xs:string" use="required"/>
<xs:attribute name="title" type="xs:string" use="required"/>
<xs:attribute name="due_at" type="xs:dateTime"/>
<xs:attribute name="unlock_at" type="xs:dateTime"/>
<xs:attribute name="lock_at" type="xs:dateTime"/>
<xs:attribute name="due_at" type="optional_dateTime"/>
<xs:attribute name="unlock_at" type="optional_dateTime"/>
<xs:attribute name="lock_at" type="optional_dateTime"/>
<xs:attribute name="due_at_overridden" type="xs:boolean"/>
<xs:attribute name="unlock_at_overridden" type="xs:boolean"/>
<xs:attribute name="lock_at_overridden" type="xs:boolean"/>
@ -613,8 +676,8 @@
<xs:all minOccurs="0">
<xs:element name="title" type="xs:string" minOccurs="0"/>
<xs:element name="description" type="xs:string" minOccurs="0"/>
<xs:element name="start_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="end_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="start_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="end_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="all_day_date" type="xs:date" minOccurs="0"/>
<xs:element name="all_day" type="xs:boolean" minOccurs="0"/>
</xs:all>
@ -630,12 +693,11 @@
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="media">
<xs:complexType>
<xs:attribute name="identifierref" type="xs:ID" use="required"/>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="track">
<xs:complexType>
<xs:attribute name="identifierref" type="xs:ID" use="required"/>
<xs:attribute name="kind" type="xs:string" use="required">
<xs:attribute name="kind" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="subtitles"/>
@ -650,6 +712,7 @@
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="identifierref" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
@ -667,8 +730,8 @@
<xs:all minOccurs="0">
<xs:element name="hidden" type="xs:boolean" minOccurs="0"/>
<xs:element name="locked" type="xs:boolean" minOccurs="0"/>
<xs:element name="lock_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="unlock_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="lock_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="unlock_at" type="optional_dateTime" minOccurs="0"/>
</xs:all>
<xs:attribute name="path" type="xs:string" use="required"/>
</xs:complexType>
@ -685,11 +748,15 @@
<xs:element name="display_name" type="xs:string" minOccurs="0"/>
<xs:element name="hidden" type="xs:boolean" minOccurs="0"/>
<xs:element name="locked" type="xs:boolean" minOccurs="0"/>
<xs:element name="lock_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="unlock_at" type="xs:dateTime" minOccurs="0"/>
<xs:element name="lock_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="unlock_at" type="optional_dateTime" minOccurs="0"/>
<xs:element name="usage_rights" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="use_justification" type="xs:string" use="required">
<xs:all minOccurs="0">
<xs:element name="legal_copyright" type="xs:string" minOccurs="0"/>
<xs:element name="license" type="xs:string" minOccurs="0"/>
</xs:all>
<xs:attribute name="use_justification" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="public_domain"/>
@ -700,10 +767,6 @@
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:all minOccurs="0">
<xs:element name="legal_copyright" type="xs:string" minOccurs="0"/>
<xs:element name="license" type="xs:string" minOccurs="0"/>
</xs:all>
</xs:complexType>
</xs:element>
</xs:all>

View File

@ -21,6 +21,9 @@ require File.expand_path(File.dirname(__FILE__) + '/../../lti2_course_spec_helpe
require 'nokogiri'
describe "Common Cartridge exporting" do
let(:ccc_schema) do
get_ccc_schema
end
it "should collect errors and finish running" do
course = course_model
@ -172,24 +175,29 @@ describe "Common Cartridge exporting" do
expect(doc.at_css("learningOutcomeGroup[identifier=#{mig_id(@log3)}]")).to be_nil
expect(doc.at_css("learningOutcome[identifier=#{mig_id(@lo)}]")).not_to be_nil
expect(doc.at_css("learningOutcome[identifier=#{mig_id(@lo2)}]")).to be_nil
expect(ccc_schema.validate(doc)).to be_empty
doc = Nokogiri::XML.parse(@zip_file.read("course_settings/assignment_groups.xml"))
expect(doc.at_css("assignmentGroup[identifier=#{mig_id(@ag)}]")).not_to be_nil
expect(doc.at_css("assignmentGroup[identifier=#{mig_id(@ag2)}]")).to be_nil
expect(ccc_schema.validate(doc)).to be_empty
doc = Nokogiri::XML.parse(@zip_file.read("course_settings/rubrics.xml"))
expect(doc.at_css("rubric[identifier=#{mig_id(@rubric)}]")).not_to be_nil
expect(doc.at_css("rubric[identifier=#{mig_id(@rubric2)}]")).to be_nil
expect(ccc_schema.validate(doc)).to be_empty
expect(@manifest_doc.at_css("item[identifier=LearningModules] item[identifier=#{mig_id(@cm)}]")).not_to be_nil
expect(@manifest_doc.at_css("item[identifier=LearningModules] item[identifier=#{mig_id(@cm2)}]")).to be_nil
doc = Nokogiri::XML.parse(@zip_file.read("course_settings/module_meta.xml"))
expect(doc.at_css("module[identifier=#{mig_id(@cm)}]")).not_to be_nil
expect(doc.at_css("module[identifier=#{mig_id(@cm2)}]")).to be_nil
expect(ccc_schema.validate(doc)).to be_empty
doc = Nokogiri::XML.parse(@zip_file.read("course_settings/events.xml"))
expect(doc.at_css("event[identifier=#{mig_id(@event)}]")).not_to be_nil
expect(doc.at_css("event[identifier=#{mig_id(@event2)}]")).to be_nil
expect(ccc_schema.validate(doc)).to be_empty
end
it "should create a quizzes-only export" do
@ -404,6 +412,16 @@ describe "Common Cartridge exporting" do
doc = Nokogiri::XML.parse(@zip_file.read("course_settings/assignment_groups.xml"))
expect(doc.at_css("assignmentGroup[identifier=#{mig_id(@ag)}]")).not_to be_nil
expect(doc.at_css("assignmentGroup[identifier=#{mig_id(@ag2)}]")).not_to be_nil
expect(ccc_schema.validate(doc)).to be_empty
end
it "has valid course settings XML" do
# include all possible settings, not just changed ones
# (if this test fails, you need to add your setting to lib/cc/xsd/cccv1p0.xsd)
allow(@course).to receive(:disable_setting_defaults).and_yield
run_export
doc = Nokogiri::XML.parse(@zip_file.read("course_settings/course_settings.xml"))
expect(ccc_schema.validate(doc)).to be_empty
end
it "should not export syllabus if not selected" do
@ -480,6 +498,7 @@ describe "Common Cartridge exporting" do
expect(@zip_file.read(file_node['href'])).to eql(track.content)
track_doc = Nokogiri::XML(@zip_file.read('course_settings/media_tracks.xml'))
expect(track_doc.at_css('media_tracks media track[locale=tlh][kind=subtitles][identifierref=id4164d7d594985594573e63f8ca15975]')).to be_present
expect(ccc_schema.validate(track_doc)).to be_empty
end
it "should export CC 1.3 assignments" do

View File

@ -30,3 +30,8 @@ end
def get_cc_export_file(rel_path)
File.join(CC_XML_EXPORT_DIR, rel_path)
end
def get_ccc_schema
xsd_filename = File.join(File.expand_path(File.dirname(__FILE__)), '../../../lib/cc/xsd/cccv1p0.xsd')
Nokogiri::XML::Schema(File.read(xsd_filename))
end

View File

@ -434,6 +434,9 @@ describe ContentMigration do
@copy_from.allow_student_discussion_editing = false
@copy_from.restrict_student_future_view = true
@copy_from.restrict_student_past_view = true
@copy_from.show_total_grade_as_points = true
@copy_from.organize_epub_by_content_type = true
@copy_from.enable_offline_web_export = true
@copy_from.save!
run_course_copy
@ -453,7 +456,7 @@ describe ContentMigration do
atts = Course.clonable_attributes
atts -= Canvas::Migration::MigratorHelper::COURSE_NO_COPY_ATTS
atts.each do |att|
expect(@copy_to.send(att)).to eq @copy_from.send(att)
expect(@copy_to.send(att)).to eq(@copy_from.send(att)), "@copy_to.#{att}: expected #{@copy_from.send(att)}, got #{@copy_to.send(att)}"
end
expect(@copy_to.tab_configuration).to eq @copy_from.tab_configuration
end