canvas-lms/db/migrate/20101210192618_init_canvas_...

4760 lines
202 KiB
Ruby

# frozen_string_literal: true
#
# Copyright (C) 2011 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# rubocop:disable Migration/AddIndex, Migration/ChangeColumn, Migration/Execute, Migration/IdColumn
# rubocop:disable Migration/PrimaryKey, Migration/RootAccountId, Rails/CreateTableWithTimestamps
# rubocop:disable Rails/ThreeStateBooleanColumn
class InitCanvasDb < ActiveRecord::Migration[7.0]
prepend Canvas::ActiveRecord::Migration::DeferForeignKeys
tag :predeploy
def create_aua_log_partition(index)
table_name = :"aua_logs_#{index}"
create_table table_name do |t|
t.references :asset_user_access, null: false, index: false
t.timestamp :created_at, null: false
end
# Intentionally not adding FK on asset_user_access_id as the records are transient
# and we're trying to do as little work as possible on the insert to these
# and can be thrown away if they don't match anything anyway as the log is compacted.
end
def up
connection.transaction(requires_new: true) do
create_extension(:pg_trgm, schema: connection.shard.name, if_not_exists: true)
rescue ActiveRecord::StatementInvalid
raise ActiveRecord::Rollback
end
execute(<<~SQL.squish)
CREATE FUNCTION #{connection.quote_table_name("setting_as_int")}( IN p_setting TEXT ) RETURNS INT4 as $$
DECLARE
v_text text;
v_int8 int8;
BEGIN
v_text := current_setting( p_setting, true );
IF v_text IS NULL THEN
RETURN NULL;
END IF;
IF NOT v_text ~ '^-?[0-9]{1,10}$' THEN
RETURN NULL;
END IF;
v_int8 := v_text::INT8;
IF v_int8 > 2147483647 OR v_int8 < -2147483648 THEN
RETURN NULL;
END IF;
RETURN v_int8::int4;
END;
$$ language plpgsql;
SQL
execute(<<~SQL.squish)
CREATE FUNCTION #{connection.quote_table_name("guard_excessive_updates")}() RETURNS TRIGGER AS $BODY$
DECLARE
record_count integer;
max_record_count integer;
BEGIN
SELECT count(*) FROM oldtbl INTO record_count;
max_record_count := COALESCE(setting_as_int('inst.max_update_limit.' || TG_TABLE_NAME), setting_as_int('inst.max_update_limit'), '#{PostgreSQLAdapterExtensions::DEFAULT_MAX_UPDATE_LIMIT}');
IF record_count > max_record_count THEN
IF current_setting('inst.max_update_fail', true) IS NOT DISTINCT FROM 'true' THEN
RAISE EXCEPTION 'guard_excessive_updates: % to %.% failed', TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME USING DETAIL = 'Would update ' || record_count || ' records but max is ' || max_record_count || ', orig query: ' || current_query();
ELSE
RAISE WARNING 'guard_excessive_updates: % to %.% was dangerous', TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME USING DETAIL = 'Updated ' || record_count || ' records but threshold is ' || max_record_count || ', orig query: ' || current_query();
END IF;
END IF;
RETURN NULL;
END
$BODY$ LANGUAGE plpgsql;
SQL
set_search_path("guard_excessive_updates")
metadata = ActiveRecord::InternalMetadata.new(connection)
metadata[:guard_dangerous_changes_installed] = "true"
# there may already be tables from plugins
connection.tables.grep_v(/^_/).each do |table|
add_guard_excessive_updates(table)
end
# these tables are referenced the most, so it's nice to put them first so we
# don't have to defer creation of foreign keys to them
create_table :accounts do |t|
t.string :name, limit: 255
t.timestamps precision: nil
t.string :workflow_state, default: "active", null: false, limit: 255
t.timestamp :deleted_at
t.references :parent_account, foreign_key: { to_table: :accounts }, index: false
t.string :sis_source_id, limit: 255
t.references :sis_batch, foreign_key: true, index: { where: "sis_batch_id IS NOT NULL" }
t.references :current_sis_batch, index: false
t.references :root_account, null: false, foreign_key: { to_table: :accounts, deferrable: :immediate }, index: false
t.references :last_successful_sis_batch, index: false
t.string :membership_types, limit: 255
t.string :default_time_zone, limit: 255
t.string :external_status, default: "active", limit: 255
t.bigint :storage_quota
t.bigint :default_storage_quota
t.boolean :enable_user_notes, default: false
t.string :allowed_services, limit: 255
t.text :turnitin_pledge
t.text :turnitin_comments
t.string :turnitin_account_id, limit: 255
t.string :turnitin_salt, limit: 255
t.string :turnitin_crypted_secret, limit: 255
t.boolean :show_section_name_as_course_name, default: false
t.boolean :allow_sis_import, default: false
t.string :equella_endpoint, limit: 255
t.text :settings
t.string :uuid, limit: 255, index: { unique: true }
t.string :default_locale, limit: 255
t.text :stuck_sis_fields
t.bigint :default_user_storage_quota
t.string :lti_guid, limit: 255
t.bigint :default_group_storage_quota
t.string :turnitin_host, limit: 255
t.string :integration_id, limit: 255
t.string :lti_context_id, limit: 255, index: { unique: true }
t.string :brand_config_md5, limit: 32, index: { where: "brand_config_md5 IS NOT NULL" }
t.string :turnitin_originality, limit: 255
t.string :account_calendar_subscription_type,
default: "manual",
null: false,
limit: 255,
index: { where: "account_calendar_subscription_type <> 'manual'" }
t.references :latest_outcome_import,
foreign_key: { to_table: :outcome_imports },
index: { where: "latest_outcome_import_id IS NOT NULL" }
t.references :course_template,
foreign_key: { to_table: :courses },
index: { where: "course_template_id IS NOT NULL" }
t.boolean :account_calendar_visible, default: false, null: false
t.references :grading_standard, foreign_key: true, index: { where: "grading_standard_id IS NOT NULL" }
t.replica_identity_index
t.index [:name, :parent_account_id]
t.index [:parent_account_id, :root_account_id]
t.index [:sis_source_id, :root_account_id], where: "sis_source_id IS NOT NULL", unique: true
t.index [:integration_id, :root_account_id],
unique: true,
name: "index_accounts_on_integration_id",
where: "integration_id IS NOT NULL"
t.foreign_key :brand_configs, column: :brand_config_md5, primary_key: :md5
end
create_table :cloned_items do |t|
t.bigint :original_item_id
t.string :original_item_type, limit: 255
t.timestamps precision: nil
end
create_table :courses do |t|
t.string :name, limit: 255
t.references :account, null: false, foreign_key: true
t.string :group_weighting_scheme, limit: 255
t.string :workflow_state, null: false, limit: 255
t.string :uuid, limit: 255, index: true
t.timestamp :start_at
t.timestamp :conclude_at
t.references :grading_standard, index: false
t.boolean :is_public
t.boolean :allow_student_wiki_edits
t.timestamps precision: nil
t.boolean :show_public_context_messages
t.text :syllabus_body, limit: 16_777_215
t.boolean :allow_student_forum_attachments, default: false
t.string :default_wiki_editing_roles, limit: 255
t.references :wiki, foreign_key: true, index: { where: "wiki_id IS NOT NULL" }
t.boolean :allow_student_organized_groups, default: true
t.string :course_code, limit: 255
t.string :default_view, limit: 255
t.references :abstract_course, foreign_key: true, index: { where: "abstract_course_id IS NOT NULL" }
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.references :enrollment_term, null: false, foreign_key: { deferrable: :immediate }
t.string :sis_source_id, limit: 255
t.references :sis_batch, foreign_key: true, index: { where: "sis_batch_id IS NOT NULL" }
t.boolean :open_enrollment
t.bigint :storage_quota
t.text :tab_configuration
t.boolean :allow_wiki_comments
t.text :turnitin_comments
t.boolean :self_enrollment
t.string :license, limit: 255
t.boolean :indexed
t.boolean :restrict_enrollments_to_course_dates
t.references :template_course, foreign_key: { to_table: :courses }
t.string :locale, limit: 255
t.text :settings
t.references :replacement_course, index: false
t.text :stuck_sis_fields
t.text :public_description
t.string :self_enrollment_code, limit: 255, index: { unique: true, where: "self_enrollment_code IS NOT NULL" }
t.integer :self_enrollment_limit
t.string :integration_id, limit: 255
t.string :time_zone, limit: 255
t.string :lti_context_id, limit: 255, index: { unique: true }
t.bigint :turnitin_id, unique: true
t.boolean :show_announcements_on_home_page
t.integer :home_page_announcement_limit
t.references :latest_outcome_import,
foreign_key: { to_table: :outcome_imports },
index: { where: "latest_outcome_import_id IS NOT NULL" }
t.string :grade_passback_setting, limit: 255
t.boolean :template, default: false, null: false
t.boolean :homeroom_course, default: false, null: false, index: { where: "homeroom_course" }
t.boolean :sync_enrollments_from_homeroom, default: false, null: false, index: { where: "sync_enrollments_from_homeroom" }
t.references :homeroom_course, index: { where: "homeroom_course_id IS NOT NULL" }
t.timestamp :deleted_at, precision: 6
t.replica_identity_index
t.index [:sis_source_id, :root_account_id], where: "sis_source_id IS NOT NULL", unique: true
t.index :grading_standard_id, name: "index_courses_on_grading_standard"
if (trgm = connection.extension(:pg_trgm)&.schema)
t.index "(
coalesce(lower(name), '') || ' ' ||
coalesce(lower(sis_source_id), '') || ' ' ||
coalesce(lower(course_code), '')
) #{trgm}.gin_trgm_ops",
name: "index_gin_trgm_courses_composite_search",
using: :gin
t.index [:integration_id, :root_account_id],
unique: true,
name: "index_courses_on_integration_id",
where: "integration_id IS NOT NULL"
end
end
create_table :sis_batches do |t|
t.references :account, null: false, index: false
t.timestamp :ended_at
t.string :workflow_state, null: false, limit: 255
t.text :data
t.timestamps precision: nil
t.references :attachment
t.integer :progress
t.text :processing_errors, limit: 16_777_215
t.text :processing_warnings, limit: 16_777_215
t.boolean :batch_mode
t.references :batch_mode_term,
foreign_key: { to_table: :enrollment_terms },
index: { where: "batch_mode_term_id IS NOT NULL" }
t.text :options
t.references :user, foreign_key: true, index: { where: "user_id IS NOT NULL" }
t.timestamp :started_at
t.string :diffing_data_set_identifier, limit: 255
t.boolean :diffing_remaster
t.references :generated_diff, index: false
t.references :errors_attachment, foreign_key: { to_table: :attachments }
t.integer :change_threshold
t.boolean :diffing_threshold_exceeded, default: false, null: false
t.bigint :job_ids, array: true, default: [], null: false
t.index [:account_id, :created_at], name: "index_sis_batches_account_id_created_at"
t.index %i[account_id diffing_data_set_identifier created_at],
name: "index_sis_batches_diffing"
t.index %i[account_id workflow_state created_at], name: "index_sis_batches_workflow_state_for_accounts"
end
create_table :users do |t|
t.string :name, limit: 255
t.string :sortable_name, limit: 255
t.string :workflow_state, null: false, limit: 255, index: true
t.string :time_zone, limit: 255
t.string :uuid, limit: 255, index: { unique: true, name: "index_users_on_unique_uuid" }
t.timestamps precision: nil
t.string :avatar_image_url, limit: 255
t.string :avatar_image_source, limit: 255
t.timestamp :avatar_image_updated_at
t.string :phone, limit: 255
t.string :school_name, limit: 255
t.string :school_position, limit: 255
t.string :short_name, limit: 255
t.timestamp :deleted_at
t.boolean :show_user_services, default: true
t.integer :page_views_count, default: 0
t.integer :reminder_time_for_due_dates, default: 172_800
t.integer :reminder_time_for_grading, default: 0
t.bigint :storage_quota
t.string :visible_inbox_types, limit: 255
t.timestamp :last_user_note
t.boolean :subscribe_to_emails
t.text :features_used
t.text :preferences
t.string :avatar_state, limit: 255
t.string :locale, limit: 255
t.string :browser_locale, limit: 255
t.integer :unread_conversations_count, default: 0
t.text :stuck_sis_fields
t.boolean :public
t.string :otp_secret_key_enc, limit: 255
t.string :otp_secret_key_salt, limit: 255
t.references :otp_communication_channel, index: false
t.string :initial_enrollment_type, limit: 255
t.integer :crocodoc_id
t.timestamp :last_logged_out
t.string :lti_context_id, limit: 255, index: { unique: true }
t.bigint :turnitin_id, index: { unique: true, where: "turnitin_id IS NOT NULL" }
t.text :lti_id, index: { unique: true, name: "index_users_on_unique_lti_id" }
t.string :pronouns
t.bigint :root_account_ids, array: true, null: false, default: []
t.references :merged_into_user,
foreign_key: { to_table: :users },
index: { where: "merged_into_user_id IS NOT NULL" }
t.replica_identity_index :root_account_ids
t.index [:avatar_state, :avatar_image_updated_at]
if (trgm = connection.extension(:pg_trgm)&.schema)
t.index "lower(name) #{trgm}.gin_trgm_ops", name: "index_gin_trgm_users_name", using: :gin
t.index "LOWER(short_name) #{trgm}.gin_trgm_ops", name: "index_gin_trgm_users_short_name", using: :gin
t.index "LOWER(name) #{trgm}.gin_trgm_ops",
name: "index_gin_trgm_users_name_active_only",
using: :gin,
where: "workflow_state IN ('registered', 'pre_registered')"
end
t.index "(sortable_name COLLATE public.\"und-u-kn-true\"), id",
name: :index_users_on_sortable_name
t.index :id, where: "workflow_state <> 'deleted'", name: "index_active_users_on_id"
end
# most the rest is alphabetical, with a "natural" sort order (so that for example quizzes comes before quiz_submissions)
create_table :abstract_courses do |t|
t.string :sis_source_id, limit: 255, index: true
t.references :sis_batch, foreign_key: true, index: { where: "sis_batch_id IS NOT NULL" }
t.references :account, null: false, foreign_key: true
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.string :short_name, limit: 255
t.string :name, limit: 255
t.timestamps precision: nil
t.references :enrollment_term, null: false, foreign_key: true
t.string :workflow_state, null: false, limit: 255
t.text :stuck_sis_fields
t.index [:root_account_id, :sis_source_id]
end
create_table :access_tokens do |t|
t.references :developer_key, null: false, index: false
t.references :user, foreign_key: true
t.timestamp :last_used_at
t.timestamp :expires_at
t.string :purpose, limit: 255
t.timestamps precision: nil
t.string :crypted_token, limit: 255, index: { unique: true }
t.string :token_hint, limit: 255
t.text :scopes
t.boolean :remember_access
t.string :crypted_refresh_token, limit: 255, index: { unique: true }
t.string :workflow_state, default: "active", null: false, index: true
t.references :root_account, null: false, index: false
t.references :real_user,
foreign_key: { to_table: :users },
index: { where: "real_user_id IS NOT NULL" }
t.timestamp :permanent_expires_at
t.replica_identity_index
t.index [:developer_key_id, :last_used_at]
end
create_table :account_notifications do |t|
t.string :subject, limit: 255
t.string :icon, default: "warning", limit: 255
t.text :message
t.references :account, null: false, foreign_key: true, index: false
t.references :user, null: false, foreign_key: true
t.timestamp :start_at, null: false
t.timestamp :end_at, null: false
t.timestamps precision: nil
t.string :required_account_service, limit: 255
t.integer :months_in_display_cycle
t.boolean :domain_specific, default: false, null: false
t.boolean :send_message, default: false, null: false
t.timestamp :messages_sent_at
t.index %i[account_id end_at start_at], name: "index_account_notifications_by_account_and_timespan"
end
create_table :account_notification_roles do |t|
t.references :account_notification, null: false, foreign_key: true, index: false
t.references :role,
foreign_key: true,
index: { where: "role_id IS NOT NULL", name: "index_account_notification_roles_only_on_role_id" }
t.index [:account_notification_id, :role_id],
unique: true,
name: "index_account_notification_roles_on_role_id"
end
create_table :account_reports do |t|
t.references :user, null: false, foreign_key: true
t.text :message
t.references :account, null: false, foreign_key: true, index: false
t.references :attachment, foreign_key: true
t.string :workflow_state, default: "created", null: false, limit: 255
t.string :report_type, limit: 255
t.integer :progress
t.timestamps precision: nil
t.text :parameters
t.integer :current_line
t.integer :total_lines
t.timestamp :start_at
t.timestamp :end_at
t.bigint :job_ids, array: true, default: [], null: false
t.index %i[account_id report_type created_at],
order: { created_at: :desc },
name: "index_account_reports_latest_of_type_per_account"
end
create_table :account_report_runners do |t|
t.references :account_report, null: false, foreign_key: true
t.string :workflow_state, null: false, default: "created", limit: 255
t.string :batch_items, array: true, default: []
t.timestamps precision: nil
t.timestamp :started_at
t.timestamp :ended_at
t.bigint :job_ids, array: true, default: [], null: false
end
create_table :account_report_rows do |t|
t.references :account_report, null: false, foreign_key: true
t.references :account_report_runner, null: false, foreign_key: true
t.integer :row_number
t.string :row, array: true, default: []
t.timestamp :created_at, null: false, index: true
t.string :file, index: true
end
create_table :account_users do |t|
t.references :account, null: false, foreign_key: true
t.references :user, null: false, foreign_key: true
t.timestamps precision: nil
t.references :role, null: false, foreign_key: true
t.string :workflow_state, default: "active", null: false, index: true
t.references :sis_batch, index: { where: "sis_batch_id IS NOT NULL" }
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.replica_identity_index
end
create_table :alerts do |t|
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.text :recipients, null: false
t.integer :repetition
t.timestamps precision: nil
end
create_table :alert_criteria do |t|
t.references :alert, foreign_key: true, index: { where: "alert_id IS NOT NULL" }
t.string :criterion_type, limit: 255
t.float :threshold
end
create_table :anonymous_or_moderation_events do |t|
t.references :assignment, null: false, foreign_key: true
t.references :user, foreign_key: true
t.references :submission, foreign_key: true
t.references :canvadoc, foreign_key: true
t.string :event_type, null: false
t.jsonb :payload, null: false, default: {}
t.timestamps precision: nil
t.references :context_external_tool,
foreign_key: { name: "fk_rails_f492821432" },
index: { where: "context_external_tool_id IS NOT NULL",
name: "index_ame_on_context_external_tool_id" }
t.references :quiz,
foreign_key: { name: "fk_rails_a862303024" },
index: { where: "quiz_id IS NOT NULL" }
end
create_table :appointment_groups do |t|
t.string :title, limit: 255
t.text :description
t.string :location_name, limit: 255
t.string :location_address, limit: 255
t.string :context_code, limit: 255
t.string :sub_context_code, limit: 255
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.timestamp :start_at
t.timestamp :end_at
t.integer :participants_per_appointment
t.integer :max_appointments_per_participant # nil means no limit
t.integer :min_appointments_per_participant, default: 0
t.string :participant_visibility, limit: 255
t.boolean :allow_observer_signup, null: false, default: false
end
create_table :appointment_group_contexts do |t|
t.references :appointment_group
t.string :context_code, limit: 255
t.bigint :context_id
t.string :context_type, limit: 255
t.timestamps precision: nil
end
create_table :appointment_group_sub_contexts do |t|
t.references :appointment_group
t.bigint :sub_context_id
t.string :sub_context_type, limit: 255
t.string :sub_context_code, limit: 255
t.timestamps precision: nil
end
create_table :assessment_question_banks do |t|
t.bigint :context_id
t.string :context_type, limit: 255
t.text :title
t.string :workflow_state, null: false, limit: 255
t.timestamp :deleted_at
t.timestamps precision: nil
t.string :migration_id, limit: 255
t.references :root_account, foreign_key: { to_table: :accounts }
t.index %i[context_id context_type title id],
name: "index_aqb_context_and_title"
end
create_table :assessment_question_bank_users do |t|
t.references :assessment_question_bank, null: false, index: { name: "assessment_qbu_aqb_id" }
t.references :user, null: false, index: { name: "assessment_qbu_u_id" }
t.timestamps precision: nil
end
create_table :assessment_questions do |t|
t.text :name
t.text :question_data
t.bigint :context_id
t.string :context_type, limit: 255
t.string :workflow_state, limit: 255
t.timestamps null: true, precision: nil
t.references :assessment_question_bank, index: false
t.timestamp :deleted_at
t.string :migration_id, limit: 255
t.integer :position
t.references :root_account, foreign_key: { to_table: :accounts }
t.index [:assessment_question_bank_id, :position], name: "question_bank_id_and_position"
end
create_table :assessment_requests do |t|
t.references :rubric_assessment
t.references :user, null: false, foreign_key: true
t.references :asset, null: false, foreign_key: { to_table: :submissions }, index: false
t.string :asset_type, null: false, limit: 255
t.bigint :assessor_asset_id, null: false
t.string :assessor_asset_type, null: false, limit: 255
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.string :uuid, limit: 255
t.references :rubric_association, foreign_key: true
t.references :assessor, null: false, foreign_key: { to_table: :users }
t.index [:assessor_asset_id, :assessor_asset_type], name: "aa_id_and_aa_type"
t.index [:asset_id, :asset_type]
end
create_table :asset_user_accesses do |t|
t.string :asset_code, limit: 255
t.string :asset_group_code, limit: 255
t.references :user, index: false
t.bigint :context_id
t.string :context_type, limit: 255
t.timestamp :last_access
t.timestamps null: true, precision: nil
t.string :asset_category, limit: 255
t.float :view_score
t.float :participate_score
t.string :action_level, limit: 255
t.text :display_name
t.string :membership_type, limit: 255
t.references :root_account, index: false, null: false
t.replica_identity_index
t.index [:user_id, :asset_code]
t.index %i[context_id context_type user_id updated_at], name: "index_asset_user_accesses_on_ci_ct_ui_ua"
t.index %i[user_id context_id asset_code id],
name: "index_asset_user_accesses_on_user_id_context_id_asset_code"
end
# one table for each day of week, they'll periodically
# be compacted and truncated. This prevents having to
# create and drop true partitions at a high rate
(0..6).each { |i| create_aua_log_partition(i) }
create_table :assignment_groups do |t|
t.string :name, limit: 255
t.text :rules
t.string :default_assignment_name, limit: 255
t.integer :position
t.string :assignment_weighting_scheme, limit: 255
t.float :group_weight
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.references :cloned_item, foreign_key: true, index: { where: "cloned_item_id IS NOT NULL" }
t.string :context_code, limit: 255
t.string :migration_id, limit: 255
t.string :sis_source_id, limit: 255
t.text :integration_data
t.references :root_account, foreign_key: { to_table: :accounts }
t.index [:context_id, :context_type]
end
create_table :assignments do |t|
t.string :title, limit: 255
t.text :description, limit: 16_777_215
t.timestamp :due_at
t.timestamp :unlock_at
t.timestamp :lock_at
t.float :points_possible
t.float :min_score
t.float :max_score
t.float :mastery_score
t.string :grading_type, limit: 255
t.string :submission_types, limit: 255
t.string :workflow_state, null: false, limit: 255, default: "published", index: true
t.bigint :context_id, null: false, index: { where: "context_type='Course' AND workflow_state<>'deleted'",
name: "index_assignments_active" }
t.string :context_type, null: false, limit: 255
t.references :assignment_group
t.references :grading_standard
t.timestamps null: true, precision: nil
t.string :group_category, limit: 255
t.integer :submissions_downloads, default: 0
t.integer :peer_review_count, default: 0
t.timestamp :peer_reviews_due_at
t.boolean :peer_reviews_assigned, default: false, null: false
t.boolean :peer_reviews, default: false, null: false
t.boolean :automatic_peer_reviews, default: false, null: false
t.boolean :all_day, default: false, null: false
t.date :all_day_date
t.boolean :could_be_locked, default: false, null: false
t.references :cloned_item, foreign_key: true, index: { where: "cloned_item_id IS NOT NULL" }
t.integer :position
t.string :migration_id, limit: 255
t.boolean :grade_group_students_individually, default: false, null: false
t.boolean :anonymous_peer_reviews, default: false, null: false
t.string :time_zone_edited, limit: 255
t.boolean :turnitin_enabled, default: false, null: false
t.string :allowed_extensions, limit: 255
t.text :turnitin_settings
t.boolean :muted, default: false, null: false
t.references :group_category, foreign_key: true, index: { where: "group_category_id IS NOT NULL" }
t.boolean :freeze_on_copy, default: false, null: false
t.boolean :copied, default: false, null: false
t.boolean :only_visible_to_overrides, default: false, null: false
t.boolean :post_to_sis, default: false, null: false
t.string :integration_id, limit: 255
t.text :integration_data
t.bigint :turnitin_id, index: { unique: true, where: "turnitin_id IS NOT NULL" }
t.boolean :moderated_grading, default: false, null: false
t.timestamp :grades_published_at
t.boolean :omit_from_final_grade, default: false, null: false
t.boolean :vericite_enabled, default: false, null: false
t.boolean :intra_group_peer_reviews, default: false, null: false
t.string :lti_context_id, index: { unique: true }
t.boolean :anonymous_instructor_annotations, default: false, null: false
t.references :duplicate_of,
index: { where: "duplicate_of_id IS NOT NULL" },
foreign_key: { to_table: :assignments }
t.boolean :anonymous_grading, default: false
t.boolean :graders_anonymous_to_graders, default: false
t.integer :grader_count, default: 0
t.boolean :grader_comments_visible_to_graders, default: true
t.references :grader_section,
foreign_key: { to_table: :course_sections },
index: { where: "grader_section_id IS NOT NULL" }
t.references :final_grader,
foreign_key: { to_table: :users },
index: { where: "final_grader_id IS NOT NULL" }
t.boolean :grader_names_visible_to_final_grader, default: true
t.timestamp :duplication_started_at, index: { where: "duplication_started_at IS NOT NULL AND workflow_state = 'duplicating'" }
t.timestamp :importing_started_at, index: { where: "importing_started_at IS NOT NULL AND workflow_state = 'importing'" }
t.integer :allowed_attempts
t.references :root_account, null: false, foreign_key: { to_table: :accounts }
t.string :sis_source_id
t.references :migrate_from,
foreign_key: { to_table: :quizzes },
index: { where: "migrate_from_id IS NOT NULL" }
t.jsonb :settings
t.references :annotatable_attachment,
foreign_key: { to_table: :attachments },
index: { where: "annotatable_attachment_id IS NOT NULL" }
t.boolean :important_dates, default: false, null: false, index: { where: "important_dates" }
t.boolean :hide_in_gradebook, default: false, null: false
t.string :ab_guid, array: true, default: [], null: false
t.references :parent_assignment, foreign_key: { to_table: :assignments }
t.string :type, null: false, limit: 255, default: "Assignment"
t.string :sub_assignment_tag, limit: 255
t.boolean :has_sub_assignments, null: false, default: false
t.index [:context_id, :context_type]
t.index [:sis_source_id, :root_account_id], where: "sis_source_id IS NOT NULL", unique: true
t.index :duplication_started_at,
where: "workflow_state = 'migrating' AND duplication_started_at IS NOT NULL",
name: "index_assignments_duplicating_on_started_at"
t.index %i[context_id grading_standard_id grading_type],
name: "index_assignments_on_context_grading_standard_grading_type"
end
create_table :assignment_configuration_tool_lookups do |t|
t.references :assignment, null: false, foreign_key: true
t.bigint :tool_id
t.string :tool_type, null: false, limit: 255
t.string :subscription_id
t.string :tool_product_code
t.string :tool_vendor_code
t.string :tool_resource_type_code
t.string :context_type, default: "Account", null: false
t.index %i[tool_id tool_type assignment_id], unique: true, name: "index_tool_lookup_on_tool_assignment_id"
t.index %i[tool_product_code tool_vendor_code tool_resource_type_code], name: "index_resource_codes_on_assignment_configuration_tool_lookups"
end
create_table :assignment_overrides do |t|
t.timestamps precision: nil
# generic info
t.references :assignment, foreign_key: true
t.integer :assignment_version
t.references :set,
polymorphic: { limit: 255 },
index: { name: "index_assignment_overrides_on_set_type_and_set_id" }
t.string :title, null: false, limit: 255
t.string :workflow_state, null: false, limit: 255
# due at override
t.boolean :due_at_overridden, default: false, null: false
t.timestamp :due_at, index: { where: "due_at_overridden", name: "index_assignment_overrides_due_at_when_overridden" }
t.boolean :all_day
t.date :all_day_date
# unlock at override
t.boolean :unlock_at_overridden, default: false, null: false
t.timestamp :unlock_at
# lock at override
t.boolean :lock_at_overridden, default: false, null: false
t.timestamp :lock_at
t.references :quiz, foreign_key: true
t.integer :quiz_version
t.references :root_account, foreign_key: { to_table: :accounts }
t.references :context_module, foreign_key: true, index: { where: "context_module_id IS NOT NULL" }
t.boolean :unassign_item, default: false, null: false
t.references :wiki_page, foreign_key: true, index: { where: "wiki_page_id IS NOT NULL" }
t.references :discussion_topic, foreign_key: true, index: { where: "discussion_topic_id IS NOT NULL" }
t.references :attachment, foreign_key: true, index: { where: "attachment_id IS NOT NULL" }
t.check_constraint <<~SQL.squish, name: "require_association"
workflow_state='deleted' OR
assignment_id IS NOT NULL OR
quiz_id IS NOT NULL OR context_module_id IS NOT NULL OR
wiki_page_id IS NOT NULL OR
discussion_topic_id IS NOT NULL OR
attachment_id IS NOT NULL
SQL
t.index %i[assignment_id set_type set_id],
name: "index_assignment_overrides_on_assignment_and_set",
unique: true,
where: "workflow_state='active' and set_id is not null"
t.index %i[context_module_id set_id set_type],
unique: true,
where: "context_module_id IS NOT NULL AND workflow_state = 'active' AND set_id IS NOT NULL",
name: "index_assignment_overrides_on_context_module_id_and_set"
t.index %i[wiki_page_id set_id set_type],
unique: true,
where: "wiki_page_id IS NOT NULL AND workflow_state = 'active' AND set_id IS NOT NULL",
name: "index_assignment_overrides_on_wiki_page_id_and_set"
t.index %i[discussion_topic_id set_id set_type],
unique: true,
where: "discussion_topic_id IS NOT NULL AND workflow_state = 'active' AND set_id IS NOT NULL",
name: "index_assignment_overrides_on_discussion_topic_id_and_set"
t.index %i[attachment_id set_id set_type],
unique: true,
where: "attachment_id IS NOT NULL AND workflow_state = 'active' AND set_id IS NOT NULL",
name: "index_assignment_overrides_on_attachment_id_and_set"
end
create_table :assignment_override_students do |t|
t.timestamps precision: nil
t.references :assignment, foreign_key: true
t.references :assignment_override, null: false, foreign_key: true
t.references :user, null: false, foreign_key: { deferrable: :immediate }
t.references :quiz, foreign_key: true
t.string :workflow_state, default: "active", null: false, index: true
t.references :root_account, foreign_key: { to_table: :accounts }
t.references :context_module, foreign_key: true, index: false
t.references :wiki_page, foreign_key: true, index: false
t.references :discussion_topic, foreign_key: true, index: false
t.references :attachment, foreign_key: true, index: false
t.index [:assignment_id, :user_id], unique: true, where: "workflow_state = 'active'"
t.index [:user_id, :quiz_id]
t.index [:context_module_id, :user_id],
where: "context_module_id IS NOT NULL",
unique: true,
name: "index_assignment_override_students_on_context_module_and_user"
t.index [:wiki_page_id, :user_id], unique: true, where: "wiki_page_id IS NOT NULL"
t.index [:discussion_topic_id, :user_id],
unique: true,
where: "discussion_topic_id IS NOT NULL",
name: "index_assignment_override_students_on_discussion_topic_and_user"
t.index [:attachment_id, :user_id],
unique: true,
where: "attachment_id IS NOT NULL"
end
create_table :attachments do |t|
t.bigint :context_id
t.string :context_type, limit: 255
t.bigint :size
t.references :folder, index: false
t.string :content_type, limit: 255
t.text :filename
t.string :uuid, limit: 255
t.text :display_name
t.timestamps null: true, precision: nil
t.string :workflow_state, limit: 255
t.references :user
t.boolean :locked, default: false
t.string :file_state, limit: 255
t.timestamp :deleted_at
t.integer :position
t.timestamp :lock_at
t.timestamp :unlock_at
t.boolean :could_be_locked
t.references :root_attachment,
foreign_key: { to_table: :attachments },
index: { where: "root_attachment_id IS NOT NULL", name: "index_attachments_on_root_attachment_id_not_null" }
t.references :cloned_item
t.string :migration_id, limit: 255
t.string :namespace, limit: 255, index: true
t.string :media_entry_id, limit: 255, index: true
t.string :md5, limit: 255
t.string :encoding, limit: 255
t.boolean :need_notify, index: { where: "need_notify" }
t.text :upload_error_message
t.references :replacement_attachment,
foreign_key: { to_table: :attachments },
index: { where: "replacement_attachment_id IS NOT NULL" }
t.references :usage_rights, foreign_key: true, index: { where: "usage_rights_id IS NOT NULL" }
t.timestamp :modified_at
t.timestamp :viewed_at
t.string :instfs_uuid, index: { where: "instfs_uuid IS NOT NULL" }
t.references :root_account
t.string :category, default: "uncategorized", null: false
t.integer :word_count
t.string :visibility_level, limit: 32, default: "inherit", null: false
t.boolean :only_visible_to_overrides, null: false, default: false
t.index [:context_id, :context_type]
t.index [:md5, :namespace]
t.index [:workflow_state, :updated_at]
t.index %i[folder_id file_state position]
t.index [:folder_id, :position], where: "folder_id IS NOT NULL"
t.index %i[context_id context_type migration_id],
where: "migration_id IS NOT NULL",
name: "index_attachments_on_context_and_migration_id"
t.index %i[md5 namespace content_type],
where: "root_attachment_id IS NULL and filename IS NOT NULL"
t.index %i[context_id context_type migration_id],
opclass: { migration_id: :text_pattern_ops },
where: "migration_id IS NOT NULL",
name: "index_attachments_on_context_and_migration_id_pattern_ops"
t.index :created_at, where: "context_type IN ('ContentExport', 'ContentMigration') and file_state NOT IN ('deleted', 'broken') and root_attachment_id is null"
t.index :context_type, where: "workflow_state = 'deleted' and file_state = 'deleted'"
t.index "folder_id, file_state, (display_name COLLATE public.\"und-u-kn-true\")",
name: :index_attachments_on_folder_id_and_file_state_and_display_name,
where: "folder_id IS NOT NULL"
end
execute(<<~SQL) # rubocop:disable Rails/SquishedSQLHeredocs
CREATE FUNCTION #{connection.quote_table_name("attachment_before_insert_verify_active_folder__tr_fn")} () RETURNS trigger AS $$
DECLARE
folder_state text;
BEGIN
SELECT workflow_state INTO folder_state FROM folders WHERE folders.id = NEW.folder_id FOR SHARE;
if folder_state = 'deleted' then
RAISE EXCEPTION 'Cannot create attachments in deleted folders --> %', NEW.folder_id;
end if;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
SQL
set_search_path("attachment_before_insert_verify_active_folder__tr_fn")
execute(<<~SQL.squish)
CREATE TRIGGER attachment_before_insert_verify_active_folder__tr
BEFORE INSERT ON #{Attachment.quoted_table_name}
FOR EACH ROW
EXECUTE PROCEDURE #{connection.quote_table_name("attachment_before_insert_verify_active_folder__tr_fn")}()
SQL
create_table :attachment_associations do |t|
t.references :attachment
t.bigint :context_id
t.string :context_type, limit: 255
t.references :root_account
t.index [:context_id, :context_type], name: "attachment_associations_a_id_a_type"
end
create_table :attachment_upload_statuses do |t|
t.references :attachment, null: false, foreign_key: true
t.text :error, null: false
t.timestamp :created_at, null: false
end
create_table :auditor_authentication_records do |t|
t.string :uuid, null: false, index: { unique: true, name: "index_auth_audits_on_unique_uuid" }
t.references :account, null: false, foreign_key: true
t.string :event_type, null: false
t.references :pseudonym, null: false, foreign_key: true
t.string :request_id, null: false
t.references :user, null: false, foreign_key: true
t.timestamp :created_at, null: false
end
create_table :auditor_course_records do |t|
t.string :uuid, null: false, index: { unique: true, name: "index_course_audits_on_unique_uuid" }
t.references :account, null: false, foreign_key: true
t.references :course, null: false, foreign_key: true
t.text :data
t.string :event_source, null: false
t.string :event_type, null: false
t.string :request_id, null: false
t.references :sis_batch
t.references :user, foreign_key: true
t.timestamp :created_at, null: false
end
create_table :auditor_feature_flag_records do |t|
t.string :uuid, null: false, index: true
t.references :feature_flag, null: false
t.references :root_account, null: false, foreign_key: { to_table: :accounts }
t.references :context, polymorphic: true, index: false
t.string :feature_name
t.string :event_type, null: false
t.string :state_before, null: false
t.string :state_after, null: false
t.string :request_id, null: false
t.references :user, foreign_key: true
t.timestamp :created_at, null: false
end
create_table :auditor_grade_change_records do |t|
t.string :uuid, null: false, index: { unique: true, name: "index_grade_audits_on_unique_uuid" }
t.references :account, null: false, foreign_key: true
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.references :assignment, foreign_key: true
t.bigint :context_id, null: false
t.string :context_type, null: false
t.string :event_type, null: false
t.boolean :excused_after, null: false
t.boolean :excused_before, null: false
t.string :grade_after
t.string :grade_before
t.boolean :graded_anonymously
t.references :grader, foreign_key: { to_table: :users }
t.float :points_possible_after
t.float :points_possible_before
t.string :request_id, null: false
t.float :score_after
t.float :score_before
t.references :student, null: false, foreign_key: { to_table: :users }
t.references :submission, foreign_key: true
t.integer :submission_version_number
t.timestamp :created_at, null: false
t.references :grading_period, foreign_key: true, index: { where: "grading_period_id IS NOT NULL" }
t.index %i[context_type context_id assignment_id], name: "index_auditor_grades_by_course_and_assignment"
t.index [:root_account_id, :grader_id], name: "index_auditor_grades_by_account_and_grader"
t.index [:root_account_id, :student_id], name: "index_auditor_grades_by_account_and_student"
t.index %i[context_type context_id grader_id], name: "index_auditor_grades_by_course_and_grader"
t.index %i[context_type context_id student_id], name: "index_auditor_grades_by_course_and_student"
end
create_table :auditor_pseudonym_records do |t|
t.references :pseudonym, null: false, foreign_key: true
t.references :root_account, null: false, foreign_key: { to_table: :accounts }
t.references :performing_user, null: false, index: false
t.string :action, null: false
t.string :hostname, null: false
t.string :pid, null: false
t.string :uuid, null: false, index: true
t.string :event_type, null: false
t.string :request_id
t.timestamp :created_at, null: false
end
create_table :authentication_providers do |t|
t.references :account, null: false, foreign_key: true
t.integer :auth_port
t.string :auth_host, limit: 255
t.string :auth_base, limit: 255
t.string :auth_username, limit: 255
t.string :auth_crypted_password, limit: 2048
t.string :auth_password_salt, limit: 255
t.string :auth_type, limit: 255
t.string :auth_over_tls, limit: 255, default: "start_tls"
t.timestamps precision: nil
t.string :log_in_url, limit: 255
t.string :log_out_url, limit: 255
t.string :identifier_format, limit: 255
t.text :certificate_fingerprint
t.string :entity_id, limit: 255
t.text :auth_filter
t.string :requested_authn_context, limit: 255
t.timestamp :last_timeout_failure
t.text :login_attribute
t.string :idp_entity_id, limit: 255
t.integer :position
t.boolean :parent_registration, default: false, null: false
t.string :workflow_state, default: "active", null: false, limit: 255, index: true
t.boolean :jit_provisioning, default: false, null: false
t.string :metadata_uri, limit: 255, index: { where: "metadata_uri IS NOT NULL" }
t.json :settings, default: {}, null: false
t.text :internal_ca
# this field will be removed after VERIFY_NONE is removed entirely
t.boolean :verify_tls_cert_opt_in, default: false, null: false
end
create_table :blackout_dates do |t|
t.references :context, polymorphic: true, index: { name: "index_blackout_dates_on_context_type_and_context_id" }, null: false
t.date :start_date, :end_date, null: false
t.string :event_title, limit: 255, null: false
t.timestamps precision: 6
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.replica_identity_index
end
create_table :block_editors do |t|
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.references :context, polymorphic: true, null: false
t.bigint :time
t.jsonb :blocks, default: [], null: false
t.string :editor_version
t.timestamps
t.replica_identity_index
end
create_table :bookmarks_bookmarks do |t|
t.references :user, null: false, foreign_key: true
t.text :name, null: false
t.text :url, null: false
t.integer :position
t.text :json
end
create_table :brand_configs, id: false do |t|
t.primary_keys [:md5]
t.string :md5, limit: 32, null: false, unique: true
t.text :variables
t.boolean :share, default: false, null: false, index: true
t.string :name, limit: 255
t.timestamp :created_at, null: false
t.text :js_overrides
t.text :css_overrides
t.text :mobile_js_overrides
t.text :mobile_css_overrides
t.string :parent_md5, limit: 255
end
create_table :calendar_events do |t|
t.string :title, limit: 255
t.text :description, limit: 16_777_215
t.text :location_name
t.text :location_address
t.timestamp :start_at, index: { where: "workflow_state<>'deleted'" }
t.timestamp :end_at
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.references :user, foreign_key: true
t.boolean :all_day
t.date :all_day_date
t.timestamp :deleted_at
t.references :cloned_item, foreign_key: true, index: { where: "cloned_item_id IS NOT NULL" }
t.string :context_code, limit: 255, index: true
t.string :migration_id, limit: 255
t.string :time_zone_edited, limit: 255
t.references :parent_calendar_event, foreign_key: { to_table: :calendar_events }
t.string :effective_context_code, limit: 255, index: { where: "effective_context_code IS NOT NULL" }
t.integer :participants_per_appointment
t.boolean :override_participants_per_appointment
t.text :comments
t.string :timetable_code, limit: 255
t.references :web_conference, foreign_key: true, index: { where: "web_conference_id IS NOT NULL" }
t.references :root_account, foreign_key: { to_table: :accounts }
t.boolean :important_dates, default: false, null: false, index: { where: "important_dates" }
t.string :rrule, limit: 255
t.uuid :series_uuid, index: true
t.boolean :series_head
t.boolean :blackout_date, default: false, null: false
t.index [:context_id, :context_type]
t.index %i[context_id context_type timetable_code], where: "timetable_code IS NOT NULL", unique: true, name: "index_calendar_events_on_context_and_timetable_code"
end
create_table :canvadocs do |t|
t.string :document_id, limit: 255, index: { unique: true }
t.string :process_state, limit: 255
t.references :attachment, null: false, foreign_key: true
t.timestamps precision: nil
t.boolean :has_annotations
end
create_table :canvadocs_annotation_contexts do |t|
t.references :attachment, foreign_key: true, index: false, null: false
t.references :submission, null: false, foreign_key: true
t.references :root_account, null: false, foreign_key: { to_table: :accounts }
t.string :launch_id, null: false
t.integer :submission_attempt
t.timestamps precision: nil
t.index %i[attachment_id submission_attempt submission_id],
name: "index_attachment_attempt_submission",
unique: true
t.index [:attachment_id, :submission_id],
where: "submission_attempt IS NULL",
name: "index_attachment_submission",
unique: true
end
create_table :canvadocs_submissions do |t|
t.references :canvadoc
t.references :crocodoc_document, index: { where: "crocodoc_document_id IS NOT NULL" }
t.references :submission, null: false
t.index [:submission_id, :canvadoc_id],
where: "canvadoc_id IS NOT NULL",
name: "unique_submissions_and_canvadocs",
unique: true
t.index [:submission_id, :crocodoc_document_id],
where: "crocodoc_document_id IS NOT NULL",
name: "unique_submissions_and_crocodocs",
unique: true
end
create_table :canvas_metadata do |t|
t.string :key, null: false, index: { unique: true }
t.jsonb :payload, null: false
t.timestamps precision: nil
end
create_table :collaborations do |t|
t.string :collaboration_type, limit: 255
t.string :document_id, limit: 255
t.references :user, foreign_key: true
t.bigint :context_id
t.string :context_type, limit: 255
t.string :url, limit: 255
t.string :uuid, limit: 255
t.text :data
t.timestamps precision: nil
t.text :description
t.string :title, null: false, limit: 255
t.string :workflow_state, default: "active", null: false, limit: 255
t.timestamp :deleted_at
t.string :context_code, limit: 255
t.string :type, limit: 255
t.uuid :resource_link_lookup_uuid
t.index [:context_id, :context_type]
end
create_table :collaborators do |t|
t.references :user, foreign_key: true
t.references :collaboration, foreign_key: true
t.timestamps precision: nil
t.string :authorized_service_user_id, limit: 255
t.references :group, foreign_key: true
end
create_table :comment_bank_items do |t|
t.references :course, null: false, foreign_key: true
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.references :user, null: false, foreign_key: true
t.text :comment, null: false
t.timestamps precision: 6
t.string :workflow_state, null: false, default: "active"
t.index :user_id,
where: "workflow_state <> 'deleted'",
name: "index_active_comment_bank_items_on_user"
t.replica_identity_index
end
create_table :communication_channels do |t|
t.string :path, null: false, limit: 255
t.string :path_type, default: "email", null: false, limit: 255
t.integer :position
t.references :user, null: false, foreign_key: true, index: false
t.references :pseudonym, index: false
t.integer :bounce_count, default: 0
t.string :workflow_state, null: false, limit: 255
t.string :confirmation_code, limit: 255, index: true
t.timestamps precision: nil
t.boolean :build_pseudonym_on_confirm
t.timestamp :last_bounce_at, index: { where: "bounce_count > 0" }
# last_bounce_details was originally intended to have limit: 32768, but
# it was typoed as "length" instead of "limit" so it did not apply
t.text :last_bounce_details
t.timestamp :last_suppression_bounce_at
t.timestamp :last_transient_bounce_at
# last_transient_bounce_details was originally intended to have limit:
# 32768, but it was typoed as "length" instead of "limit" so it did not apply
t.text :last_transient_bounce_details
t.timestamp :confirmation_code_expires_at
t.integer :confirmation_sent_count, default: 0, null: false
t.bigint :root_account_ids, array: true
t.string :confirmation_redirect
t.index [:pseudonym_id, :position]
t.index [:user_id, :position]
t.index "LOWER(path), path_type", name: "index_communication_channels_on_path_and_path_type"
if (trgm = connection.extension(:pg_trgm)&.schema)
t.index "lower(path) #{trgm}.gin_trgm_ops", name: "index_gin_trgm_communication_channels_path", using: :gin
t.index "user_id, LOWER(path), path_type",
unique: true,
name: "index_communication_channels_on_user_id_and_path_and_path_type"
end
end
create_table :conditional_release_rules do |t|
t.references :course, foreign_key: true
t.references :trigger_assignment, foreign_key: { to_table: :assignments }
t.timestamp :deleted_at
t.references :root_account,
foreign_key: { to_table: :accounts },
null: false,
index: { name: "index_cr_rules_on_root_account_id" }
t.timestamps precision: nil
t.index [:root_account_id, :course_id], where: "deleted_at IS NULL", name: "index_cr_rules_on_account_and_course"
end
create_table :conditional_release_scoring_ranges do |t|
t.references :rule,
foreign_key: { to_table: :conditional_release_rules },
index: { where: "deleted_at IS NULL", name: "index_cr_scoring_ranges_on_rule_id" },
null: false
t.decimal :lower_bound
t.decimal :upper_bound
t.integer :position
t.timestamp :deleted_at
t.references :root_account,
foreign_key: { to_table: :accounts },
null: false,
index: { name: "index_cr_scoring_ranges_on_root_account_id" }
t.timestamps precision: nil
t.index :rule_id
end
create_table :conditional_release_assignment_sets do |t|
t.references :scoring_range,
foreign_key: { to_table: :conditional_release_scoring_ranges },
index: { where: "deleted_at IS NULL", name: "index_cr_assignment_sets_on_scoring_range_id" },
null: false
t.integer :position
t.timestamp :deleted_at
t.references :root_account,
foreign_key: { to_table: :accounts },
null: false,
index: { name: "index_cr_assignment_sets_on_root_account_id" }
t.timestamps precision: nil
t.index :scoring_range_id
end
create_table :conditional_release_assignment_set_associations do |t|
t.references :assignment_set,
foreign_key: { to_table: :conditional_release_assignment_sets },
index: { name: "index_crasa_on_assignment_set_id", where: "assignment_set_id IS NOT NULL" }
t.references :assignment,
foreign_key: true,
index: { where: "deleted_at IS NULL", name: "index_cr_assignment_set_associations_on_set" }
t.integer :position
t.timestamp :deleted_at
t.references :root_account,
foreign_key: { to_table: :accounts },
null: false,
index: { name: "index_cr_assignment_set_associations_on_root_account_id" }
t.timestamps precision: nil
t.index [:assignment_id, :assignment_set_id],
unique: true,
where: "deleted_at IS NULL",
name: "index_cr_assignment_set_associations_on_assignment_and_set"
t.index :assignment_id, name: "index_crasa_on_assignment_id", where: "assignment_id IS NOT NULL"
end
create_table :conditional_release_assignment_set_actions do |t|
t.string :action, null: false
t.string :source, null: false
t.references :student, null: false, index: false
t.references :actor, null: false, index: false
t.references :assignment_set, index: false
t.timestamp :deleted_at
t.references :root_account,
foreign_key: { to_table: :accounts },
null: false,
index: { name: "index_cr_assignment_set_actions_on_root_account_id" }
t.timestamps precision: nil
t.index :assignment_set_id,
where: "deleted_at IS NULL",
name: "index_cr_assignment_set_actions_on_assignment_set_id"
t.index %i[assignment_set_id student_id created_at],
order: { created_at: :desc },
where: "deleted_at IS NULL",
name: "index_cr_assignment_set_actions_on_set_and_student"
end
create_table :content_exports do |t|
t.references :user, foreign_key: true, index: { where: "user_id IS NOT NULL" }
t.references :attachment, foreign_key: true
t.string :export_type, limit: 255
t.text :settings
t.float :progress
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.references :content_migration
t.references :context, polymorphic: { limit: 255 }, index: false
t.boolean :global_identifiers, default: false, null: false
t.index [:context_id, :context_type]
end
create_table :content_migrations do |t|
t.bigint :context_id, null: false
t.references :user, foreign_key: true, index: { where: "user_id IS NOT NULL" }
t.string :workflow_state, null: false, limit: 255
t.text :migration_settings
t.timestamp :started_at
t.timestamp :finished_at
t.timestamps precision: nil
t.float :progress
t.string :context_type, limit: 255
t.references :attachment, index: { where: "attachment_id IS NOT NULL" }
t.references :overview_attachment,
foreign_key: { to_table: :attachments },
index: { where: "overview_attachment_id IS NOT NULL" }
t.references :exported_attachment,
foreign_key: { to_table: :attachments },
index: { where: "exported_attachment_id IS NOT NULL" }
t.references :source_course, index: { where: "source_course_id IS NOT NULL" }
t.string :migration_type, limit: 255
t.references :child_subscription,
foreign_key: { to_table: :master_courses_child_subscriptions },
index: { where: "child_subscription_id IS NOT NULL" }
t.references :root_account, foreign_key: { to_table: :accounts }
t.references :asset_map_attachment, index: { where: "asset_map_attachment_id IS NOT NULL" }, foreign_key: { to_table: :attachments }
t.index [:context_id, :id], name: "index_content_migrations_on_context_id_and_id_no_clause"
t.index [:context_id, :id], where: "workflow_state='queued'"
t.index [:context_id, :started_at],
name: "index_content_migrations_blocked_migrations",
where: "started_at IS NOT NULL"
end
create_table :content_participations do |t|
t.references :content, polymorphic: { limit: 255 }, null: false, index: false
t.references :user, null: false, foreign_key: true
t.string :workflow_state, null: false, limit: 255
t.references :root_account, foreign_key: { to_table: :accounts }
t.string :content_item, null: false, default: "grade"
t.index %i[content_id content_type user_id content_item],
name: "index_content_participations_by_type_uniquely",
unique: true
t.index :user_id,
name: "index_content_participations_on_user_id_unread",
where: "workflow_state = 'unread'"
end
create_table :content_participation_counts do |t|
t.string :content_type, limit: 255
t.references :context, polymorphic: { limit: 255 }, index: false
t.references :user, index: { where: "user_id IS NOT NULL" }
t.integer :unread_count, default: 0
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }
t.index %i[context_id context_type user_id content_type], name: "index_content_participation_counts_uniquely", unique: true
end
create_table :content_shares do |t|
t.text :name, null: false
t.timestamps precision: nil
t.references :user, null: false, foreign_key: true, index: false
t.references :content_export, null: false
t.references :sender, foreign_key: { to_table: :users }, index: { where: "sender_id IS NOT NULL" }
t.string :read_state, limit: 255, null: false
t.string :type, limit: 255, null: false
t.references :root_account
t.index %i[user_id content_export_id sender_id],
unique: true,
name: "index_content_shares_on_user_and_content_export_and_sender_ids"
end
create_table :content_tags do |t|
t.bigint :content_id
t.string :content_type, limit: 255
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.text :title
t.string :tag, limit: 255
t.text :url
t.timestamps precision: nil
t.text :comments
t.string :tag_type, default: "default", limit: 255
t.references :context_module, foreign_key: true
t.integer :position
t.integer :indent
t.string :migration_id, limit: 255
t.references :learning_outcome, foreign_key: true, index: { where: "learning_outcome_id IS NOT NULL" }
t.string :context_code, limit: 255
t.float :mastery_score
t.references :rubric_association, index: false
t.string :workflow_state, default: "active", null: false, limit: 255
t.references :cloned_item, foreign_key: true, index: { where: "cloned_item_id IS NOT NULL" }
t.bigint :associated_asset_id
t.string :associated_asset_type, limit: 255
t.boolean :new_tab
t.jsonb :link_settings
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.jsonb :external_data
t.replica_identity_index
t.index [:content_id, :content_type]
t.index [:context_id, :context_type]
t.index [:associated_asset_id, :associated_asset_type], name: "index_content_tags_on_associated_asset"
t.index %i[context_id context_type content_type],
where: "workflow_state = 'active'",
name: "index_content_tags_on_context_when_active"
t.index %i[content_type context_type context_id],
where: "workflow_state<>'deleted'",
name: "index_content_tags_for_due_date_cacher"
end
create_table :context_external_tools do |t|
t.bigint :context_id
t.string :context_type, limit: 255
t.string :domain, limit: 255
t.string :url, limit: 4.kilobytes
t.text :shared_secret, null: false
t.text :consumer_key, null: false, index: true
t.string :name, null: false, limit: 255
t.text :description
t.text :settings
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.string :migration_id, limit: 255
t.references :cloned_item, foreign_key: true, index: { where: "cloned_item_id IS NOT NULL" }
t.string :tool_id, limit: 255, index: true
t.boolean :not_selectable
t.string :app_center_id, limit: 255
t.boolean :allow_membership_service_access, default: false, null: false
t.references :developer_key
t.references :root_account, null: false, index: false
t.boolean :is_rce_favorite, default: false, null: false
t.string :identity_hash, limit: 64, index: { where: "identity_hash <> 'duplicate'" }
t.text :lti_version, null: false, limit: 8, default: "1.1"
t.replica_identity_index
t.index [:context_id, :context_type]
t.index %i[context_id context_type migration_id], where: "migration_id IS NOT NULL", name: "index_external_tools_on_context_and_migration_id"
end
create_table :context_external_tool_placements do |t|
t.string :placement_type, limit: 255
t.references :context_external_tool, null: false, foreign_key: true, index: { name: "external_tool_placements_tool_id" }
t.index [:placement_type, :context_external_tool_id], unique: true, name: "external_tool_placements_type_and_tool_id"
end
create_table :context_modules do |t|
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.text :name
t.integer :position
t.text :prerequisites
t.text :completion_requirements
t.timestamps precision: nil
t.string :workflow_state, default: "active", null: false, limit: 255
t.timestamp :deleted_at
t.timestamp :unlock_at
t.string :migration_id, limit: 255
t.boolean :require_sequential_progress
t.references :cloned_item, foreign_key: true, index: { where: "cloned_item_id IS NOT NULL" }
t.text :completion_events
t.integer :requirement_count
t.references :root_account, foreign_key: { to_table: :accounts }
t.index [:context_id, :context_type]
end
create_table :context_module_progressions do |t|
t.references :context_module, foreign_key: true
t.references :user, foreign_key: true, index: false
t.text :requirements_met
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.boolean :collapsed
t.integer :current_position
t.timestamp :completed_at
t.boolean :current
t.integer :lock_version, default: 0, null: false
t.timestamp :evaluated_at
t.text :incomplete_requirements
t.references :root_account, foreign_key: { to_table: :accounts }
t.index [:user_id, :context_module_id], unique: true, name: "index_cmp_on_user_id_and_module_id"
end
create_table :conversations do |t|
# for quick lookups so we know whether or not we need to create a new one
t.string :private_hash, limit: 255, index: { unique: true }
t.boolean :has_attachments, default: false, null: false
t.boolean :has_media_objects, default: false, null: false
t.text :tags
t.text :root_account_ids
t.string :subject, limit: 255
t.references :context, polymorphic: { limit: 255 }, index: false
t.timestamp :updated_at
end
create_table :conversation_batches do |t|
t.string :workflow_state, null: false, limit: 255
t.references :user, null: false, foreign_key: true, index: false
t.text :recipient_ids
t.references :root_conversation_message, null: false, foreign_key: { to_table: :conversation_messages }
t.text :conversation_message_ids
t.text :tags
t.timestamps precision: nil
t.references :context, polymorphic: { limit: 255 }, index: false
t.string :subject, limit: 255
t.boolean :group
t.boolean :generate_user_note
t.index [:user_id, :workflow_state]
end
create_table :conversation_messages do |t|
t.references :conversation, foreign_key: true, index: false
t.references :author
t.timestamp :created_at
t.boolean :generated
t.text :body
t.text :forwarded_message_ids
t.string :media_comment_id, limit: 255
t.string :media_comment_type, limit: 255
t.bigint :context_id
t.string :context_type, limit: 255
t.bigint :asset_id
t.string :asset_type, limit: 255
t.text :attachment_ids
t.boolean :has_attachments
t.boolean :has_media_objects
t.text :root_account_ids
t.index [:conversation_id, :created_at]
end
create_table :conversation_message_participants do |t|
t.references :conversation_message,
foreign_key: true,
index: { name: "index_conversation_message_participants_on_message_id" }
t.references :conversation_participant, index: false
t.text :tags
t.references :user, index: false
t.string :workflow_state, limit: 255
t.timestamp :deleted_at, index: true
t.text :root_account_ids
t.index [:conversation_participant_id, :conversation_message_id], name: "index_cmp_on_cpi_and_cmi"
t.index [:user_id, :conversation_message_id], name: "index_conversation_message_participants_on_uid_and_message_id", unique: true
end
create_table :conversation_participants do |t|
t.references :conversation, null: false, index: false
t.references :user, null: false, index: {
where: "workflow_state = 'unread'",
name: "index_conversation_participants_unread_on_user_id"
}
t.timestamp :last_message_at
t.boolean :subscribed, default: true
t.string :workflow_state, null: false, limit: 255
t.timestamp :last_authored_at
t.boolean :has_attachments, default: false, null: false
t.boolean :has_media_objects, default: false, null: false
t.integer :message_count, default: 0
t.string :label, limit: 255
t.text :tags
t.timestamp :visible_last_authored_at
t.text :root_account_ids
t.string :private_hash, limit: 255
t.timestamp :updated_at
t.index [:user_id, :last_message_at]
t.index [:conversation_id, :user_id], unique: true
t.index [:private_hash, :user_id], where: "private_hash IS NOT NULL", unique: true
end
create_table :course_account_associations do |t|
t.references :course, null: false, foreign_key: true, index: false
t.references :account, null: false, foreign_key: true, index: false
t.integer :depth, null: false
t.timestamps precision: nil
t.references :course_section, foreign_key: true
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.index [:account_id, :depth], name: "index_course_account_associations_on_account_id_and_depth_id"
t.index %i[course_id course_section_id account_id], unique: true, name: "index_caa_on_course_id_and_section_id_and_account_id"
end
create_table :course_paces do |t|
t.references :course, null: false, foreign_key: true
t.references :course_section, index: false
t.references :user, index: false
t.string :workflow_state, default: "unpublished", null: false, limit: 255
t.date :end_date
t.boolean :exclude_weekends, null: false, default: true
t.boolean :hard_end_dates, null: false, default: false
t.timestamps precision: 6
t.timestamp :published_at
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.string :migration_id
t.replica_identity_index
t.index :course_id, unique: true, where: "course_section_id IS NULL AND user_id IS NULL AND workflow_state='active'", name: "course_paces_unique_primary_plan_index"
t.index :course_section_id, unique: true, where: "workflow_state='active'"
t.index [:course_id, :user_id], unique: true, where: "workflow_state='active'"
end
create_table :course_pace_module_items do |t|
t.references :course_pace, foreign_key: true
t.integer :duration, null: false, default: 0
t.references :module_item, foreign_key: { to_table: :content_tags }
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.timestamps precision: 6
t.string :migration_id
t.replica_identity_index
end
create_table :course_score_statistics do |t|
t.references :course, null: false, foreign_key: true, index: { unique: true }
t.decimal :average, precision: 8, scale: 2, null: false
t.integer :score_count, null: false
t.timestamps precision: nil
end
create_table :course_sections do |t|
t.string :sis_source_id, limit: 255
t.references :sis_batch, foreign_key: true, index: { where: "sis_batch_id IS NOT NULL" }
t.references :course, null: false, foreign_key: true
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.references :enrollment_term, foreign_key: true
t.string :name, null: false, limit: 255
t.boolean :default_section
t.boolean :accepting_enrollments
t.boolean :can_manually_enroll
t.timestamp :start_at
t.timestamp :end_at
t.timestamps precision: nil
t.string :workflow_state, default: "active", null: false, limit: 255
t.boolean :restrict_enrollments_to_section_dates
t.references :nonxlist_course,
foreign_key: { to_table: :courses },
index: { where: "nonxlist_course_id IS NOT NULL",
name: "index_course_sections_on_nonxlist_course" }
t.text :stuck_sis_fields
t.string :integration_id, limit: 255
t.replica_identity_index
t.index [:sis_source_id, :root_account_id], where: "sis_source_id IS NOT NULL", unique: true
t.index [:integration_id, :root_account_id],
unique: true,
name: "index_sections_on_integration_id",
where: "integration_id IS NOT NULL"
t.index :course_id,
unique: true,
where: "default_section = 't' AND workflow_state <> 'deleted'",
name: "index_course_sections_unique_default_section"
end
create_table :crocodoc_documents do |t|
t.string :uuid, limit: 255, index: true
t.string :process_state, limit: 255, index: true
t.references :attachment
t.timestamps null: true, precision: nil
end
create_table :csp_domains do |t|
t.references :account, null: false, foreign_key: true, index: false
t.string :domain, null: false, limit: 255
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.index [:account_id, :domain], unique: true
t.index [:account_id, :workflow_state]
end
create_table :custom_data do |t|
t.text :data
t.string :namespace, limit: 255
t.references :user, index: false
t.timestamps precision: nil
t.index [:user_id, :namespace], unique: true
end
create_table :custom_grade_statuses do |t|
t.string :color, limit: 7, null: false
t.string :name, null: false, limit: 14
t.string :workflow_state, null: false, default: "active", limit: 255
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.references :created_by, null: false, foreign_key: { to_table: :users }
t.references :deleted_by, foreign_key: { to_table: :users }
t.timestamps precision: 6
t.boolean :applies_to_submissions, null: false, default: true
t.boolean :applies_to_final_grade, null: false, default: true
t.boolean :allow_final_grade_value, null: false, default: true
t.replica_identity_index
end
create_table :custom_gradebook_columns do |t|
t.string :title, null: false, limit: 255
t.integer :position, null: false
t.string :workflow_state, default: "active", null: false, limit: 255
t.references :course, null: false, foreign_key: { dependent: true }
t.timestamps precision: nil
t.boolean :teacher_notes, default: false, null: false
t.boolean :read_only, default: false, null: false
t.references :root_account, foreign_key: { to_table: :accounts }
end
create_table :custom_gradebook_column_data do |t|
t.string :content, null: false, limit: 255
t.references :user, null: false, foreign_key: true
t.references :custom_gradebook_column, null: false, foreign_key: true, index: false
t.references :root_account, foreign_key: { to_table: :accounts }
t.index [:custom_gradebook_column_id, :user_id],
unique: true,
name: "index_custom_gradebook_column_data_unique_column_and_user"
end
create_table :delayed_messages do |t|
t.references :notification, index: false
t.references :notification_policy, foreign_key: true
t.bigint :context_id
t.string :context_type, limit: 255
t.references :communication_channel, foreign_key: true, index: false
t.string :frequency, limit: 255
t.string :workflow_state, limit: 255
t.timestamp :batched_at
t.timestamps null: true, precision: nil
t.timestamp :send_at, index: { name: "by_sent_at" }
t.text :link
t.text :name_of_topic
t.text :summary
t.references :root_account, index: false
t.references :notification_policy_override,
foreign_key: true,
index: { where: "notification_policy_override_id IS NOT NULL" }
t.index [:workflow_state, :send_at], name: "ws_sa"
t.index %i[communication_channel_id root_account_id workflow_state send_at], name: "ccid_raid_ws_sa"
t.index :send_at, where: "workflow_state = 'pending'", name: "index_delayed_messages_pending"
end
create_table :delayed_notifications do |t|
t.references :notification, null: false, index: false
t.bigint :asset_id, null: false
t.string :asset_type, null: false, limit: 255
t.text :recipient_keys
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
end
create_table :developer_keys do |t|
t.string :api_key, limit: 255
t.string :email, limit: 255
t.string :user_name, limit: 255
t.references :account, index: false
t.timestamps precision: nil
t.references :user, index: false
t.string :name, limit: 255
t.string :redirect_uri, limit: 255
t.string :icon_url, limit: 255
t.string :sns_arn, limit: 255
t.boolean :trusted
t.boolean :force_token_reuse
t.string :workflow_state, default: "active", null: false, limit: 255
t.boolean :replace_tokens
t.boolean :auto_expire_tokens, default: false, null: false
t.string :redirect_uris, array: true, default: [], null: false, limit: 4096
t.text :notes
t.integer :access_token_count, default: 0, null: false
t.string :vendor_code, index: true
t.boolean :visible, default: false, null: false
t.text :scopes
t.boolean :require_scopes, default: false, null: false
t.boolean :test_cluster_only, default: false, null: false
t.jsonb :public_jwk
t.boolean :internal_service, default: false, null: false
t.text :oidc_initiation_url
t.string :public_jwk_url
t.boolean :is_lti_key, default: false, null: false
t.boolean :allow_includes, default: false, null: false
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.string :client_credentials_audience
t.references :service_user, foreign_key: { to_table: :users }, index: { where: "service_user_id IS NOT NULL" }
t.replica_identity_index
end
create_table :developer_key_account_bindings do |t|
t.references :account, null: false, foreign_key: true, index: false
t.references :developer_key, null: false
t.string :workflow_state, null: false, default: "off"
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.index %i[account_id developer_key_id], name: "index_dev_key_bindings_on_account_id_and_developer_key_id", unique: true
end
create_table :discussion_entries do |t|
t.text :message
t.references :discussion_topic, foreign_key: true, index: false
t.references :user, foreign_key: true
t.references :parent, foreign_key: { to_table: :discussion_entries }
t.timestamps precision: nil
t.references :attachment, index: false
t.string :workflow_state, default: "active", limit: 255
t.timestamp :deleted_at
t.string :migration_id, limit: 255
t.references :editor, foreign_key: { to_table: :users }, index: { where: "editor_id IS NOT NULL" }
t.references :root_entry, foreign_key: { to_table: :discussion_entries }, index: false
t.integer :depth
t.integer :rating_count
t.integer :rating_sum
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.boolean :legacy, default: true, null: false
t.boolean :include_reply_preview, default: false, null: false
t.boolean :is_anonymous_author, default: false, null: false
t.references :quoted_entry, foreign_key: { to_table: :discussion_entries }
t.replica_identity_index
t.index %i[root_entry_id workflow_state created_at], name: "index_discussion_entries_root_entry"
t.index %i[discussion_topic_id updated_at created_at], name: "index_discussion_entries_for_topic"
t.index [:user_id, :discussion_topic_id],
where: "workflow_state <> 'deleted'",
name: "index_discussion_entries_active_on_user_id_and_topic"
end
create_table :discussion_entry_drafts do |t|
t.references :discussion_topic, null: false, foreign_key: true
t.references :discussion_entry, foreign_key: true, index: false
t.references :root_entry, foreign_key: { to_table: :discussion_entries }
t.references :parent, foreign_key: { to_table: :discussion_entries }
t.references :attachment, foreign_key: true
t.references :user, null: false, foreign_key: true
t.text :message
t.boolean :include_reply_preview, null: false, default: false
t.timestamps precision: 6
t.index %i[discussion_topic_id user_id],
name: "unique_index_on_topic_and_user",
where: "discussion_entry_id IS NULL AND root_entry_id IS NULL",
unique: true
t.index %i[root_entry_id user_id],
name: "unique_index_on_root_entry_and_user",
where: "discussion_entry_id IS NULL",
unique: true
t.index %i[discussion_entry_id user_id],
name: "unique_index_on_entry_and_user",
unique: true
end
create_table :discussion_entry_participants do |t|
t.references :discussion_entry, null: false, foreign_key: true, index: false
t.references :user, null: false, foreign_key: true
t.string :workflow_state, null: false, limit: 255
t.boolean :forced_read_state
t.integer :rating
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.string :report_type, limit: 255
t.timestamp :read_at
t.replica_identity_index
t.index [:discussion_entry_id, :user_id], name: "index_entry_participant_on_entry_id_and_user_id", unique: true
end
create_table :discussion_entry_versions do |t|
t.references :discussion_entry, null: false, foreign_key: true
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.references :user, foreign_key: true
t.bigint :version
t.text :message
t.timestamps precision: 6
t.replica_identity_index
end
create_table :discussion_topics do |t|
t.string :title, limit: 255
t.text :message, limit: 16_777_215
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.string :type, limit: 255
t.references :user, foreign_key: true
t.string :workflow_state, null: false, limit: 255, index: true
t.timestamp :last_reply_at
t.timestamps precision: nil
t.timestamp :delayed_post_at
t.timestamp :posted_at
t.references :assignment, foreign_key: true
t.references :attachment, foreign_key: true, index: { where: "attachment_id IS NOT NULL" }
t.timestamp :deleted_at
t.references :root_topic, foreign_key: { to_table: :discussion_topics }
t.boolean :could_be_locked, default: false, null: false
t.references :cloned_item, foreign_key: true, index: { where: "cloned_item_id IS NOT NULL" }
t.string :context_code, limit: 255
t.integer :position
t.string :migration_id, limit: 255
t.references :old_assignment, foreign_key: { to_table: :assignments }, index: { where: "old_assignment_id IS NOT NULL" }
t.timestamp :subtopics_refreshed_at
t.references :last_assignment, index: false
t.references :external_feed, foreign_key: true, index: { where: "external_feed_id IS NOT NULL" }
t.references :editor, foreign_key: { to_table: :users }, index: { where: "editor_id IS NOT NULL" }
t.boolean :podcast_enabled, default: false, null: false
t.boolean :podcast_has_student_posts, default: false, null: false
t.boolean :require_initial_post, default: false, null: false
t.string :discussion_type, limit: 255
t.timestamp :lock_at
t.boolean :pinned, default: false, null: false
t.boolean :locked, default: false, null: false
t.references :group_category, foreign_key: true, index: { where: "group_category_id IS NOT NULL" }
t.boolean :allow_rating, default: false, null: false
t.boolean :only_graders_can_rate, default: false, null: false
t.boolean :sort_by_rating, default: false, null: false
t.timestamp :todo_date
t.boolean :is_section_specific, default: false, null: false
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.string :anonymous_state, limit: 255
t.boolean :is_anonymous_author, default: false, null: false
t.integer :reply_to_entry_required_count, null: false, default: 0
t.timestamp :unlock_at, precision: 6
t.boolean :only_visible_to_overrides, null: false, default: false
t.replica_identity_index
t.index [:context_id, :position]
t.index [:id, :type]
t.index %i[context_id context_type root_topic_id], unique: true, name: "index_discussion_topics_unique_subtopic_per_context"
t.index [:context_id, :last_reply_at], name: "index_discussion_topics_on_context_and_last_reply_at"
if (trgm = connection.extension(:pg_trgm)&.schema)
t.index "LOWER(title) #{trgm}.gin_trgm_ops", name: "index_gin_trgm_discussion_topics_title", using: :gin
end
end
create_table :discussion_topic_materialized_views, id: false do |t|
t.primary_keys [:discussion_topic_id]
t.references :discussion_topic, null: false, foreign_key: true, index: false
t.text :json_structure
t.text :participants_array
t.text :entry_ids_array
t.timestamps precision: nil
t.timestamp :generation_started_at
end
create_table :discussion_topic_participants do |t|
t.references :discussion_topic, null: false, foreign_key: true, index: false
t.references :user, null: false, foreign_key: true
t.integer :unread_entry_count, default: 0, null: false
t.string :workflow_state, null: false, limit: 255
t.boolean :subscribed
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.index [:discussion_topic_id, :user_id], name: "index_topic_participant_on_topic_id_and_user_id", unique: true
end
create_table :discussion_topic_section_visibilities do |t|
t.references :discussion_topic, null: false, foreign_key: true, index: { name: "idx_discussion_topic_section_visibility_on_topic" }
t.references :course_section, null: false, foreign_key: true, index: { name: "idx_discussion_topic_section_visibility_on_section" }
t.timestamps precision: nil
t.string :workflow_state, null: false, limit: 255
end
create_table :enrollment_dates_overrides do |t|
t.references :enrollment_term, foreign_key: true
t.string :enrollment_type, limit: 255
t.bigint :context_id, null: false
t.string :context_type, limit: 255
t.timestamp :start_at
t.timestamp :end_at
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }
t.text :stuck_sis_fields
t.replica_identity_index :context_id
end
create_table :enrollment_states, id: false do |t|
t.primary_keys [:enrollment_id]
t.references :enrollment, null: false, foreign_key: true, index: false
t.string :state, limit: 255, index: true
t.boolean :state_is_current, null: false, default: false
t.timestamp :state_started_at
t.timestamp :state_valid_until, index: true
t.boolean :restricted_access, null: false, default: false
t.boolean :access_is_current, null: false, default: false
t.integer :lock_version, default: 0, null: false
t.timestamp :updated_at
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.index [:state_is_current, :access_is_current], name: "index_enrollment_states_on_currents"
end
create_table :enrollment_terms do |t|
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.string :name, limit: 255
t.string :term_code, limit: 255
t.string :sis_source_id, limit: 255
t.references :sis_batch, foreign_key: true, index: { where: "sis_batch_id IS NOT NULL" }
t.timestamp :start_at
t.timestamp :end_at
t.boolean :accepting_enrollments
t.boolean :can_manually_enroll
t.timestamps precision: nil
t.string :workflow_state, default: "active", null: false, limit: 255
t.text :stuck_sis_fields
t.string :integration_id, limit: 255
t.references :grading_period_group, foreign_key: true
t.replica_identity_index
t.index [:sis_source_id, :root_account_id], where: "sis_source_id IS NOT NULL", unique: true
t.index [:integration_id, :root_account_id],
unique: true,
name: "index_terms_on_integration_id",
where: "integration_id IS NOT NULL"
end
create_table :enrollments do |t|
t.references :user, null: false, foreign_key: true
t.references :course,
foreign_key: true,
null: false,
index: { where: "workflow_state = 'active'", name: "index_enrollments_on_course_when_active" }
t.string :type, null: false, limit: 255
t.string :uuid, limit: 255, index: true
t.string :workflow_state, null: false, limit: 255, index: true
t.timestamps precision: nil
t.references :associated_user, foreign_key: { to_table: :users }, index: { where: "associated_user_id IS NOT NULL" }
t.references :sis_batch, foreign_key: true, index: { where: "sis_batch_id IS NOT NULL" }
t.timestamp :start_at
t.timestamp :end_at
t.references :course_section, null: false, foreign_key: true, index: false
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.timestamp :completed_at
t.boolean :self_enrolled
t.string :grade_publishing_status, default: "unpublished", limit: 255
t.timestamp :last_publish_attempt_at
t.text :stuck_sis_fields
t.text :grade_publishing_message
t.boolean :limit_privileges_to_course_section, default: false, null: false
t.timestamp :last_activity_at
t.integer :total_activity_time
t.references :role, null: false, foreign_key: true, index: false
t.timestamp :graded_at
t.references :sis_pseudonym
t.timestamp :last_attended_at
t.references :temporary_enrollment_source_user, foreign_key: { to_table: :users }, index: false
t.references :temporary_enrollment_pairing, foreign_key: true, index: { where: "temporary_enrollment_pairing_id IS NOT NULL" }
t.replica_identity_index
t.index [:course_id, :workflow_state]
t.index [:root_account_id, :course_id]
t.index %i[user_id type role_id course_section_id associated_user_id],
where: "associated_user_id IS NOT NULL",
name: "index_enrollments_on_user_type_role_section_associated_user",
unique: true
t.index %i[user_id type role_id course_section_id],
where: "associated_user_id IS NULL ",
name: "index_enrollments_on_user_type_role_section",
unique: true
t.index [:course_id, :user_id]
t.index [:role_id, :user_id]
t.index [:course_section_id, :id]
t.index [:course_id, :id]
t.index %i[temporary_enrollment_source_user_id user_id type role_id course_section_id],
where: "temporary_enrollment_source_user_id IS NOT NULL",
name: "index_enrollments_on_temp_enrollment_user_type_role_section",
unique: true
end
create_table :eportfolios do |t|
t.references :user, null: false, foreign_key: true
t.string :name, limit: 255
t.boolean :public
t.timestamps precision: nil
t.string :uuid, limit: 255
t.string :workflow_state, default: "active", null: false, limit: 255
t.timestamp :deleted_at
t.string :spam_status, index: true
end
create_table :eportfolio_categories do |t|
t.references :eportfolio, null: false, foreign_key: true
t.string :name, limit: 255
t.integer :position
t.string :slug, limit: 255
t.timestamps precision: nil
end
create_table :eportfolio_entries do |t|
t.references :eportfolio, null: false, foreign_key: true
t.references :eportfolio_category, null: false, foreign_key: true
t.integer :position
t.string :name, limit: 255
t.boolean :allow_comments
t.boolean :show_comments
t.string :slug, limit: 255
t.text :content, limit: 16_777_215
t.timestamps precision: nil
end
create_table :epub_exports do |t|
t.references :content_export, :course, :user, foreign_key: true
t.string :workflow_state, default: "created", limit: 255
t.timestamps precision: nil
t.string :type, limit: 255
end
create_table :error_reports do |t|
t.text :backtrace
t.text :url
t.text :message
t.text :comments
t.references :user, index: { where: "user_id IS NOT NULL" }
t.timestamps null: true, precision: nil
t.string :email, limit: 255
t.boolean :during_tests, default: false
t.text :user_agent
t.string :request_method, limit: 255
t.text :http_env, limit: 16_777_215
t.text :subject
t.string :request_context_id, limit: 255
t.references :account, index: false
t.bigint :zendesk_ticket_id, index: true
t.text :data
t.string :category, limit: 255, index: true
t.index :created_at, name: "error_reports_created_at"
end
create_table :event_stream_failures do |t|
t.string :operation, null: false, limit: 255
t.string :event_stream, null: false, limit: 255
t.string :record_id, null: false, limit: 255
t.text :payload, null: false
t.text :exception
t.text :backtrace
t.timestamps precision: nil
end
create_table :external_feeds do |t|
t.references :user, foreign_key: true, index: { where: "user_id IS NOT NULL" }
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.integer :consecutive_failures
t.integer :failures
t.timestamp :refresh_at
t.string :title, limit: 255
t.string :url, null: false, limit: 255
t.string :header_match, limit: 255
t.timestamps precision: nil
t.string :verbosity, limit: 255
t.string :migration_id, limit: 255
t.index [:context_id, :context_type]
t.index %i[context_id context_type url verbosity], unique: true, where: "header_match IS NULL", name: "index_external_feeds_uniquely_1"
t.index %i[context_id context_type url header_match verbosity], unique: true, where: "header_match IS NOT NULL", name: "index_external_feeds_uniquely_2"
end
create_table :external_feed_entries do |t|
t.references :user, foreign_key: true, index: { where: "user_id IS NOT NULL" }
t.references :external_feed, null: false, foreign_key: true
t.text :title
t.text :message
t.string :source_name, limit: 255
t.text :source_url
t.timestamp :posted_at
t.string :workflow_state, null: false, limit: 255
t.text :url, index: true
t.string :author_name, limit: 255
t.string :author_email, limit: 255
t.text :author_url
t.bigint :asset_id
t.string :asset_type, limit: 255
t.string :uuid, limit: 255, index: true
t.timestamps precision: nil
end
create_table :external_integration_keys do |t|
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.string :key_value, null: false, limit: 255
t.string :key_type, null: false, limit: 255
t.timestamps precision: nil
t.index %i[context_id context_type key_type], name: "index_external_integration_keys_unique", unique: true
end
create_table :favorites do |t|
t.references :user, foreign_key: true, index: false
t.bigint :context_id
t.string :context_type, limit: 255
t.timestamps precision: nil
t.references :root_account, index: false, null: false
t.replica_identity_index
t.index %i[user_id context_id context_type], unique: true, name: "index_favorites_unique_user_object"
end
create_table :feature_flags do |t|
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.string :feature, null: false, limit: 255
t.string :state, default: "allowed", null: false, limit: 255
t.timestamps precision: nil
t.index %i[context_id context_type feature], unique: true, name: "index_feature_flags_on_context_and_feature"
end
create_table :folders do |t|
t.string :name, limit: 255
t.text :full_name
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.references :parent_folder, foreign_key: { to_table: :folders }
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.timestamp :deleted_at
t.boolean :locked
t.timestamp :lock_at
t.timestamp :unlock_at
t.references :cloned_item
t.integer :position
t.string :submission_context_code, limit: 255
t.string :unique_type
t.references :root_account, index: false, null: false
t.replica_identity_index
t.index [:context_id, :context_type]
t.index [:context_id, :context_type], unique: true, name: "index_folders_on_context_id_and_context_type_for_root_folders", where: "parent_folder_id IS NULL AND workflow_state<>'deleted'"
t.index [:submission_context_code, :parent_folder_id], unique: true
t.index %i[unique_type context_id context_type],
unique: true,
where: "unique_type IS NOT NULL AND workflow_state <> 'deleted'"
end
execute(<<~SQL) # rubocop:disable Rails/SquishedSQLHeredocs
CREATE FUNCTION #{connection.quote_table_name("folder_before_insert_verify_active_parent_folder__tr_fn")} () RETURNS trigger AS $$
DECLARE
parent_state text;
BEGIN
SELECT workflow_state INTO parent_state FROM folders WHERE folders.id = NEW.parent_folder_id FOR SHARE;
if parent_state = 'deleted' then
RAISE EXCEPTION 'Cannot create sub-folders in deleted folders --> %', NEW.parent_folder_id;
end if;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
SQL
set_search_path("folder_before_insert_verify_active_parent_folder__tr_fn")
execute(<<~SQL.squish)
CREATE TRIGGER folder_before_insert_verify_active_parent_folder__tr
BEFORE INSERT ON #{Folder.quoted_table_name}
FOR EACH ROW
EXECUTE PROCEDURE #{connection.quote_table_name("folder_before_insert_verify_active_parent_folder__tr_fn")}()
SQL
create_table :gradebook_csvs do |t|
t.references :user, null: false, foreign_key: true, index: false
t.references :attachment, null: false, index: false
t.references :progress, null: false, foreign_key: true
t.references :course, null: false, foreign_key: true
t.index [:user_id, :course_id]
end
create_table :gradebook_filters do |t|
t.references :course, null: false, foreign_key: true, index: false
t.references :user, null: false, foreign_key: true
t.string :name, limit: 255, null: false
t.jsonb :payload, null: false, default: {}
t.timestamps precision: 6
t.index [:course_id, :user_id]
end
create_table :gradebook_uploads do |t|
t.timestamps precision: nil
t.references :course, null: false, foreign_key: true, index: false
t.references :user, null: false, foreign_key: true
t.references :progress, null: false, foreign_key: true
t.text :gradebook
t.index [:course_id, :user_id], unique: true
end
create_table :grading_period_groups do |t|
t.references :course, foreign_key: true
t.references :account, foreign_key: true
t.timestamps precision: nil
t.string :workflow_state, default: "active", null: false, limit: 255, index: true
t.string :title, limit: 255
t.boolean :weighted
t.boolean :display_totals_for_all_grading_periods, default: false, null: false
t.references :root_account, foreign_key: { to_table: :accounts }, index: { where: "root_account_id IS NOT NULL" }
end
create_table :grading_periods do |t|
t.float :weight
t.timestamp :start_date, null: false
t.timestamp :end_date, null: false
t.timestamps precision: nil
t.string :title, limit: 255
t.string :workflow_state, default: "active", null: false, limit: 255, index: true
t.references :grading_period_group, null: false, foreign_key: true
t.timestamp :close_date
t.references :root_account, foreign_key: { to_table: :accounts }
end
create_table :grading_standards do |t|
t.string :title, limit: 255
t.text :data
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.timestamps precision: nil
t.references :user, foreign_key: true, index: { where: "user_id IS NOT NULL" }
t.integer :usage_count
t.string :context_code, limit: 255, index: true
t.string :workflow_state, null: false, limit: 255
t.string :migration_id, limit: 255
t.integer :version
t.references :root_account, foreign_key: { to_table: :accounts }
t.boolean :points_based, default: false, null: false
t.decimal :scaling_factor, precision: 5, scale: 2, default: 1.0, null: false
t.index [:context_id, :context_type]
end
create_table :group_categories do |t|
t.bigint :context_id
t.string :context_type, limit: 255
t.string :name, limit: 255
t.string :role, limit: 255, index: true
t.timestamp :deleted_at
t.string :self_signup, limit: 255
t.integer :group_limit
t.string :auto_leader, limit: 255
t.timestamps null: true, precision: nil
t.string :sis_source_id
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.references :sis_batch, foreign_key: true
t.replica_identity_index
t.index [:context_id, :context_type], name: "index_group_categories_on_context"
t.index [:root_account_id, :sis_source_id], where: "sis_source_id IS NOT NULL", unique: true
end
create_table :groups do |t|
t.string :name, limit: 255
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.string :category, limit: 255
t.integer :max_membership
t.boolean :is_public
t.references :account, null: false, foreign_key: true
t.references :wiki, foreign_key: true, index: { where: "wiki_id IS NOT NULL" }
t.timestamp :deleted_at
t.string :join_level, limit: 255
t.string :default_view, default: "feed", limit: 255
t.string :migration_id, limit: 255
t.bigint :storage_quota
t.string :uuid, null: false, limit: 255, index: { unique: true }
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.string :sis_source_id, limit: 255
t.references :sis_batch, foreign_key: true, index: { where: "sis_batch_id IS NOT NULL" }
t.text :stuck_sis_fields
t.references :group_category, foreign_key: true
t.text :description
t.references :avatar_attachment, index: false
t.references :leader, foreign_key: { to_table: :users }, index: { where: "leader_id IS NOT NULL" }
t.string :lti_context_id, limit: 255
t.replica_identity_index
t.index [:context_id, :context_type]
t.index [:sis_source_id, :root_account_id], where: "sis_source_id IS NOT NULL", unique: true
end
create_table :group_memberships do |t|
t.references :group, null: false, foreign_key: true
t.string :workflow_state, null: false, limit: 255, index: true
t.timestamps precision: nil
t.references :user, null: false, foreign_key: true
t.string :uuid, null: false, limit: 255, index: { unique: true }
t.references :sis_batch, foreign_key: true, index: { where: "sis_batch_id IS NOT NULL" }
t.boolean :moderator
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.index [:group_id, :user_id], unique: true, where: "workflow_state <> 'deleted'"
end
create_table :group_and_membership_importers do |t|
t.references :group_category, null: false, foreign_key: true
t.references :attachment, foreign_key: true, index: { where: "attachment_id IS NOT NULL" }
t.string :workflow_state, null: false, default: "active"
t.timestamps precision: nil
end
create_table :inbox_settings do |t|
t.string :user_id, index: true, null: false
t.boolean :use_signature, default: false, null: false
t.string :signature, limit: 255
t.boolean :use_out_of_office, default: false, null: false
t.datetime :out_of_office_first_date
t.datetime :out_of_office_last_date
t.string :out_of_office_subject, limit: 255
t.string :out_of_office_message, limit: 255
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.timestamps
end
create_table :ignores do |t|
t.references :asset, polymorphic: { limit: 255 }, null: false, index: false
t.references :user, null: false, foreign_key: true
t.string :purpose, null: false, limit: 255
t.boolean :permanent, null: false, default: false
t.timestamps precision: nil
t.index %i[asset_id asset_type user_id purpose], unique: true, name: "index_ignores_on_asset_and_user_id_and_purpose"
end
create_table :late_policies do |t|
t.references :course, null: false, foreign_key: true, index: { unique: true }
t.boolean :missing_submission_deduction_enabled, null: false, default: false
t.decimal :missing_submission_deduction, precision: 5, scale: 2, null: false, default: 100
t.boolean :late_submission_deduction_enabled, null: false, default: false
t.decimal :late_submission_deduction, precision: 5, scale: 2, null: false, default: 0
t.string :late_submission_interval, limit: 16, null: false, default: "day"
t.boolean :late_submission_minimum_percent_enabled, null: false, default: false
t.decimal :late_submission_minimum_percent, precision: 5, scale: 2, null: false, default: 0
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }
end
create_table :learning_outcomes do |t|
t.bigint :context_id
t.string :context_type, limit: 255
t.string :short_description, null: false, limit: 255
t.string :context_code, limit: 255
t.text :description
t.text :data
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.string :migration_id, limit: 255
t.string :vendor_guid, limit: 255, index: true
t.string :low_grade, limit: 255
t.string :high_grade, limit: 255
t.string :display_name, limit: 255
t.string :calculation_method, limit: 255
t.integer :calculation_int, limit: 2
t.string :vendor_guid_2, limit: 255, index: true
t.string :migration_id_2, limit: 255
t.references :outcome_import, index: false
t.bigint :root_account_ids, array: true, index: { using: :gin }
t.references :copied_from_outcome, index: { where: "copied_from_outcome_id IS NOT NULL" }
t.timestamp :archived_at, precision: 6, default: nil
t.index [:context_id, :context_type]
end
create_table :learning_outcome_groups do |t|
t.bigint :context_id
t.string :context_type, limit: 255
t.string :title, null: false, limit: 255
t.references :learning_outcome_group, foreign_key: true, index: { where: "learning_outcome_group_id IS NOT NULL" }
t.references :root_learning_outcome_group,
foreign_key: { to_table: :learning_outcome_groups },
index: { where: "root_learning_outcome_group_id IS NOT NULL" }
t.string :workflow_state, null: false, limit: 255
t.text :description
t.timestamps precision: nil
t.string :migration_id, limit: 255
t.string :vendor_guid, limit: 255, index: true
t.string :low_grade, limit: 255
t.string :high_grade, limit: 255
t.string :vendor_guid_2, limit: 255, index: true
t.string :migration_id_2, limit: 255
t.references :outcome_import, index: false
t.references :root_account, foreign_key: { to_table: :accounts }
t.references :source_outcome_group,
index: { where: "source_outcome_group_id IS NOT NULL" },
foreign_key: { to_table: :learning_outcome_groups }
t.timestamp :archived_at, precision: 6, default: nil
t.index [:context_id, :context_type]
t.index %i[context_type context_id vendor_guid_2], name: "index_learning_outcome_groups_on_context_and_vendor_guid"
end
create_table :learning_outcome_question_results do |t|
t.references :learning_outcome_result, index: { name: "index_LOQR_on_learning_outcome_result_id" }
t.references :learning_outcome
t.bigint :associated_asset_id
t.string :associated_asset_type, limit: 255
t.float :score
t.float :possible
t.boolean :mastery
t.float :percent
t.integer :attempt
t.text :title
t.float :original_score
t.float :original_possible
t.boolean :original_mastery
t.timestamp :assessed_at
t.timestamps precision: nil
t.timestamp :submitted_at
t.references :root_account, foreign_key: { to_table: :accounts }
end
create_table :learning_outcome_results do |t|
t.bigint :context_id
t.string :context_type, limit: 255
t.string :context_code, limit: 255
t.bigint :association_id
t.string :association_type, limit: 255
t.references :content_tag, foreign_key: true
t.references :learning_outcome, foreign_key: true, index: { where: "learning_outcome_id IS NOT NULL" }
t.boolean :mastery
t.references :user, foreign_key: true, index: false
t.float :score
t.timestamps precision: nil
t.integer :attempt
t.float :possible
t.float :original_score
t.float :original_possible
t.boolean :original_mastery
t.bigint :artifact_id
t.string :artifact_type, limit: 255
t.timestamp :assessed_at
t.string :title, limit: 255
t.float :percent
t.bigint :associated_asset_id
t.string :associated_asset_type, limit: 255
t.timestamp :submitted_at
t.boolean :hide_points, default: false, null: false
t.boolean :hidden, default: false, null: false
t.string :user_uuid, limit: 255
t.references :root_account, foreign_key: { to_table: :accounts }
t.string :workflow_state, default: "active", null: false
t.index %i[user_id content_tag_id association_id association_type associated_asset_id associated_asset_type],
unique: true,
name: "index_learning_outcome_results_association"
t.index [:artifact_id, :artifact_type], name: "lor_artifact_id_idx"
end
create_table :live_assessments_assessments do |t|
t.string :key, null: false, limit: 255
t.string :title, null: false, limit: 255
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.timestamps precision: nil
t.index %i[context_id context_type key], unique: true, name: "index_live_assessments"
end
create_table :live_assessments_results do |t|
t.references :user, null: false
t.references :assessor, null: false, foreign_key: { to_table: :users }
t.references :assessment, null: false, foreign_key: { to_table: :live_assessments_assessments }, index: false
t.boolean :passed, null: false
t.timestamp :assessed_at, null: false
t.index [:assessment_id, :user_id]
end
create_table :live_assessments_submissions do |t|
t.references :user, null: false, foreign_key: true
t.references :assessment, null: false, foreign_key: { to_table: :live_assessments_assessments }, index: false
t.float :possible
t.float :score
t.timestamp :assessed_at
t.timestamps precision: nil
t.index [:assessment_id, :user_id], unique: true
end
create_table :lti_ims_registrations do |t|
t.jsonb :lti_tool_configuration, null: false
t.references :developer_key, null: false, foreign_key: true
t.string :application_type, null: false
t.text :grant_types, array: true, default: [], null: false
t.text :response_types, array: true, default: [], null: false
t.text :redirect_uris, array: true, default: [], null: false
t.text :initiate_login_uri, null: false
t.string :client_name, null: false
t.text :jwks_uri, null: false
t.text :logo_uri
t.string :token_endpoint_auth_method, null: false
t.string :contacts, array: true, default: [], null: false, limit: 255
t.text :client_uri
t.text :policy_uri
t.text :tos_uri
t.text :scopes, array: true, default: [], null: false
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.timestamps precision: 6
t.string :guid
t.jsonb :registration_overlay, default: {}
t.replica_identity_index
end
create_table :lti_line_items do |t|
t.float :score_maximum, null: false
t.string :label, null: false
t.string :resource_id, index: true
t.string :tag, index: true
t.references :lti_resource_link, foreign_key: true
t.references :assignment, null: false, foreign_key: true
t.timestamps precision: nil
t.bigint :client_id, null: false, index: true
t.string :workflow_state, default: "active", null: false, index: true
t.jsonb :extensions, default: {}
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.boolean :coupled, default: true, null: false
t.timestamp :end_date_time
t.datetime :start_date_time
t.replica_identity_index
end
create_table :lti_links do |t|
t.string :resource_link_id, null: false, index: { unique: true }
t.string :vendor_code, null: false
t.string :product_code, null: false
t.string :resource_type_code, null: false
t.bigint :linkable_id
t.string :linkable_type
t.text :custom_parameters
t.text :resource_url
t.timestamps precision: nil
t.index [:linkable_id, :linkable_type]
end
create_table :lti_message_handlers do |t|
t.string :message_type, null: false, limit: 255
t.string :launch_path, null: false, limit: 255
t.text :capabilities
t.text :parameters
t.references :resource_handler,
null: false,
foreign_key: { to_table: :lti_resource_handlers },
index: false
t.timestamps precision: nil
t.references :tool_proxy, foreign_key: { to_table: :lti_tool_proxies }
t.index [:resource_handler_id, :message_type],
name: "index_lti_message_handlers_on_resource_handler_and_type",
unique: true
end
create_table :lti_product_families do |t|
t.string :vendor_code, null: false, limit: 255
t.string :product_code, null: false, limit: 255
t.string :vendor_name, null: false, limit: 255
t.text :vendor_description
t.string :website, limit: 255
t.string :vendor_email, limit: 255
t.references :root_account, null: false, foreign_key: { to_table: :accounts }
t.timestamps precision: nil
t.references :developer_key
t.index %i[product_code vendor_code root_account_id developer_key_id],
unique: true,
name: "product_family_uniqueness"
end
create_table :lti_resource_handlers do |t|
t.string :resource_type_code, null: false, limit: 255
t.string :placements, limit: 255
t.string :name, null: false, limit: 255
t.text :description
t.text :icon_info
t.references :tool_proxy,
null: false,
foreign_key: { to_table: :lti_tool_proxies },
index: false
t.timestamps precision: nil
t.index [:tool_proxy_id, :resource_type_code],
name: "index_lti_resource_handlers_on_tool_proxy_and_type_code",
unique: true
end
create_table :lti_resource_links do |t|
t.timestamps precision: nil
t.references :context_external_tool, null: false, foreign_key: { to_table: :context_external_tools }
t.string :workflow_state, default: "active", null: false, index: true
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.bigint :context_id, null: false
t.string :context_type, limit: 255, null: false
t.jsonb :custom
t.uuid :lookup_uuid, null: false
t.uuid :resource_link_uuid, null: false, index: { unique: true }
t.string :url
t.string :lti_1_1_id
t.string :title
t.replica_identity_index
t.index [:context_id, :context_type], name: "index_lti_resource_links_by_context_id_context_type"
t.index %i[lookup_uuid context_id context_type],
unique: true,
name: "index_lti_resource_links_unique_lookup_uuid_on_context"
end
create_table :lti_resource_placements do |t|
t.string :placement, null: false, limit: 255
t.timestamps precision: nil
t.references :message_handler,
foreign_key: { to_table: :lti_message_handlers },
index: { where: "message_handler_id IS NOT NULL" }
t.index [:placement, :message_handler_id],
unique: true,
where: "message_handler_id IS NOT NULL",
name: "index_resource_placements_on_placement_and_message_handler"
end
create_table :lti_results do |t|
t.float :result_score
t.float :result_maximum
t.text :comment
t.string :activity_progress
t.string :grading_progress
t.references :lti_line_item, null: false, foreign_key: true
t.references :submission, foreign_key: true
t.references :user, null: false, foreign_key: true
t.timestamps precision: nil
t.string :workflow_state, default: "active", null: false, index: true
t.jsonb :extensions, default: {}
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.index %i[lti_line_item_id user_id], unique: true
end
create_table :lti_tool_configurations do |t|
t.references :developer_key, null: false, foreign_key: true, index: { unique: true }
t.jsonb :settings, null: false
t.timestamps precision: nil
t.string :disabled_placements, array: true, default: []
t.string :privacy_level
end
create_table :lti_tool_consumer_profiles do |t|
t.text :services
t.text :capabilities
t.string :uuid, null: false, index: { unique: true }
t.references :developer_key, null: false, foreign_key: true, index: { unique: true }
t.timestamps precision: nil
end
create_table :lti_tool_proxies do |t|
t.text :shared_secret, null: false
t.string :guid, null: false, limit: 255, index: true
t.string :product_version, null: false, limit: 255
t.string :lti_version, null: false, limit: 255
t.references :product_family, null: false, foreign_key: { to_table: :lti_product_families }
t.bigint :context_id, null: false
t.string :workflow_state, null: false, limit: 255
t.text :raw_data, null: false
t.timestamps precision: nil
# NOTE: I think the original migration didn't want this to remain the
# default, but they didn't remove it properly, so it still is.
t.string :context_type, null: false, default: "Account", limit: 255
t.string :name, limit: 255
t.text :description
t.text :update_payload
t.text :registration_url
t.string :subscription_id
end
create_table :lti_tool_proxy_bindings do |t|
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.references :tool_proxy, null: false, foreign_key: { to_table: :lti_tool_proxies }
t.timestamps precision: nil
t.boolean :enabled, null: false, default: true
t.index %i[context_id context_type tool_proxy_id], name: "index_lti_tool_proxy_bindings_on_context_and_tool_proxy", unique: true
end
create_table :lti_tool_settings do |t|
t.references :tool_proxy, index: false
t.bigint :context_id
t.string :context_type, limit: 255
t.text :resource_link_id
t.text :custom
t.timestamps precision: nil
t.string :product_code
t.string :vendor_code
t.string :resource_type_code
t.text :custom_parameters
t.text :resource_url
t.index %i[resource_link_id context_type context_id tool_proxy_id], name: "index_lti_tool_settings_on_link_context_and_tool_proxy", unique: true
end
create_table :master_courses_child_content_tags do |t|
# mainly for bulk loading on import
t.references :child_subscription,
null: false,
foreign_key: { to_table: :master_courses_child_subscriptions },
index: { name: "index_child_content_tags_on_subscription" }
t.references :content,
polymorphic: { limit: 255 },
null: false,
index: { unique: true, name: "index_child_content_tags_on_content" }
t.text :downstream_changes
t.string :migration_id, index: { name: "index_child_content_tags_on_migration_id" }
t.references :root_account, foreign_key: { to_table: :accounts }
t.index [:child_subscription_id, :migration_id],
opclass: { migration_id: :text_pattern_ops },
name: "index_mc_child_content_tags_on_sub_and_migration_id_pattern_ops"
end
create_table :master_courses_child_subscriptions do |t|
t.references :master_template, null: false, foreign_key: { to_table: :master_courses_master_templates }
# we may have to drop this foreign key at some point for cross-shard subscriptions
t.references :child_course,
foreign_key: { to_table: :courses },
null: false,
index: { name: "index_child_subscriptions_on_child_course_id" }
t.string :workflow_state, null: false, limit: 255
# we can use this to keep track of which subscriptions are new
# vs. which ones have been getting regular updates and we can use a selective copy for
t.boolean :use_selective_copy, null: false, default: false
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }
t.index [:master_template_id, :child_course_id],
unique: true,
where: "workflow_state <> 'deleted'",
name: "index_mc_child_subscriptions_on_template_id_and_course_id"
end
create_table :master_courses_master_content_tags do |t|
t.references :master_template, null: false, foreign_key: { to_table: :master_courses_master_templates }
# should we add a workflow state and make this soft-deletable?
# maybe someday if we decide to use these to define the template content aets
t.references :content, polymorphic: { limit: 255 }, null: false, index: false
# when we export an object for a master migration we'll set this column on the tag
# when we update the content we'll erase this
# so now we'll know what's been updated since the last successful export
t.references :current_migration,
foreign_key: { to_table: :master_courses_master_migrations },
index: { where: "current_migration_id IS NOT NULL",
name: "index_master_content_tags_on_current_migration_id" }
t.text :restrictions # we might not leave this at settings/content
t.string :migration_id, index: { unique: true, name: "index_master_content_tags_on_migration_id" }
t.boolean :use_default_restrictions, default: false, null: false
t.references :root_account, foreign_key: { to_table: :accounts }
t.index %i[master_template_id content_type content_id],
unique: true,
name: "index_master_content_tags_on_template_id_and_content"
end
create_table :master_courses_master_migrations do |t|
t.references :master_template, null: false, foreign_key: { to_table: :master_courses_master_templates }
t.references :user, index: false # exports use a bunch of terrible user-dependent stuff
# we can just use serialized columns here to store the rest of the data
# instead of a million rows
# since we won't really be needing any of it separately
t.text :export_results # we can store the initial export details here
t.timestamp :exports_started_at
t.timestamp :imports_queued_at
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.timestamp :imports_completed_at
t.text :comment
t.boolean :send_notification, default: false, null: false
t.text :migration_settings
t.references :root_account, foreign_key: { to_table: :accounts }
end
create_table :master_courses_master_templates do |t|
t.references :course, null: false, foreign_key: true
t.boolean :full_course, null: false, default: true # we may not ever get around to allowing selective collection sets out but just in case
t.string :workflow_state, limit: 255
t.timestamps precision: nil
# due to paranoia about race conditions around trying to make multiple migrations at once
# we'll lock the template before we create the migration
# and mark this column with the new migration unless there's already a currently running one, in which case we'll abort
t.references :active_migration,
foreign_key: { to_table: :master_courses_master_migrations },
index: { where: "active_migration_id IS NOT NULL" }
t.text :default_restrictions
t.boolean :use_default_restrictions_by_type, default: false, null: false
t.text :default_restrictions_by_type
t.references :root_account, foreign_key: { to_table: :accounts }
t.index :course_id,
unique: true,
where: "full_course AND workflow_state <> 'deleted'",
name: "index_master_templates_unique_on_course_and_full"
end
create_table :master_courses_migration_results do |t|
t.references :master_migration,
null: false,
foreign_key: { to_table: :master_courses_master_migrations },
index: false
t.references :content_migration, null: false, foreign_key: true
t.references :child_subscription, null: false, foreign_key: { to_table: :master_courses_child_subscriptions }
t.string :import_type, null: false
t.string :state, null: false
t.text :results
t.references :root_account, foreign_key: { to_table: :accounts }
t.index [:master_migration_id, :state],
name: "index_mc_migration_results_on_master_mig_id_and_state"
t.index [:master_migration_id, :content_migration_id],
unique: true,
name: "index_mc_migration_results_on_master_and_content_migration_ids"
end
create_table :media_objects do |t|
t.references :user, foreign_key: true, index: { where: "user_id IS NOT NULL" }
t.bigint :context_id
t.string :context_type, limit: 255
t.string :workflow_state, null: false, limit: 255
t.string :user_type, limit: 255
t.string :title, limit: 255
t.string :user_entered_title, limit: 255
t.string :media_id, null: false, limit: 255, index: true
t.string :media_type, limit: 255
t.integer :duration
t.integer :max_size
t.references :root_account, foreign_key: { to_table: :accounts }
t.text :data
t.timestamps precision: nil
t.references :attachment
t.integer :total_size
t.string :old_media_id, limit: 255, index: true
t.index [:context_id, :context_type]
end
create_table :media_tracks do |t|
t.references :user, index: false
t.references :media_object, null: false, index: false
t.string :kind, default: "subtitles", limit: 255
t.string :locale, default: "en", limit: 255
t.text :content, null: false
t.timestamps precision: nil
t.text :webvtt_content
t.references :attachment, index: false
t.index [:media_object_id, :locale], name: "media_object_id_locale"
t.index [:attachment_id, :locale], where: "attachment_id IS NOT NULL", unique: true
end
create_table :mentions do |t|
t.references :discussion_entry, null: false, foreign_key: true
t.references :user, null: false, foreign_key: true
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.string :workflow_state, default: "active", null: false, limit: 255
t.timestamps precision: 6
t.replica_identity_index
end
create_table :messages do |t|
t.text :to
t.text :from
t.text :subject
t.text :body
t.integer :delay_for, default: 120
t.timestamp :dispatch_at
t.timestamp :sent_at, index: { where: "sent_at IS NOT NULL" }
t.string :workflow_state, limit: 255
t.text :transmission_errors
t.boolean :is_bounced
t.references :notification
t.references :communication_channel
t.bigint :context_id
t.string :context_type, limit: 255
t.references :user, index: false
t.timestamps null: true, precision: nil
t.string :notification_name, limit: 255
t.text :url
t.string :path_type, limit: 255
t.text :from_name
t.boolean :to_email
t.text :html_body
t.references :root_account
t.string :reply_to_name, limit: 255
t.index %i[context_id context_type notification_name to user_id], name: "existing_undispatched_message"
t.index %i[user_id to_email dispatch_at], name: "index_messages_user_id_dispatch_at_to_email"
t.index :created_at
end
create_table :microsoft_sync_groups do |t|
t.references :course, foreign_key: true, index: { unique: true }, null: false
t.string :workflow_state, null: false, default: "pending"
t.string :job_state
t.timestamp :last_synced_at
t.timestamp :last_manually_synced_at
t.text :last_error
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.timestamps precision: 6
t.string :ms_group_id
t.references :last_error_report, index: false
t.text :debug_info
t.replica_identity_index
end
create_table :microsoft_sync_partial_sync_changes do |t|
t.references :course, null: false, foreign_key: true
t.references :user, null: false, foreign_key: true
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.string :enrollment_type, null: false
t.timestamps precision: 6
t.replica_identity_index
t.index %i[course_id user_id enrollment_type],
unique: true,
name: "index_microsoft_sync_partial_sync_changes_course_user_enroll"
end
create_table :microsoft_sync_user_mappings do |t|
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.references :user, null: false, foreign_key: true, index: false
t.string :aad_id
t.timestamps precision: 6
t.boolean :needs_updating, default: false, null: false
t.replica_identity_index
t.index [:user_id, :root_account_id], unique: true, name: "index_microsoft_sync_user_mappings_ra_id_user_id"
end
create_table :migration_issues do |t|
t.references :content_migration, null: false, foreign_key: true
t.text :description
t.string :workflow_state, null: false, limit: 255
t.text :fix_issue_html_url
t.string :issue_type, null: false, limit: 255
t.references :error_report, index: false
t.text :error_message
t.timestamps precision: nil
end
create_table :moderation_graders do |t|
t.string :anonymous_id, limit: 5, null: false
t.references :assignment, null: false, foreign_key: true
t.references :user, null: false, foreign_key: true, index: false
t.timestamps precision: nil
t.boolean :slot_taken, default: true, null: false
t.index [:assignment_id, :anonymous_id], unique: true
t.index [:user_id, :assignment_id], unique: true
end
create_table :moderated_grading_provisional_grades do |t|
t.string :grade, limit: 255
t.float :score
t.timestamp :graded_at
t.references :scorer, null: false, foreign_key: { to_table: :users }
t.references :submission, null: false, foreign_key: true
t.timestamps precision: nil
t.boolean :final, null: false, default: false
t.references :source_provisional_grade,
foreign_key: { to_table: :moderated_grading_provisional_grades,
name: "provisional_grades_source_provisional_grade_fk" },
index: { where: "source_provisional_grade_id IS NOT NULL",
name: "index_provisional_grades_on_source_grade" }
t.boolean :graded_anonymously
t.index :submission_id,
unique: true,
where: "final = TRUE",
name: "idx_mg_provisional_grades_unique_submission_when_final"
t.index [:submission_id, :scorer_id],
unique: true,
name: "idx_mg_provisional_grades_unique_sub_scorer_when_not_final",
where: "final = FALSE"
end
create_table :moderated_grading_selections do |t|
t.references :assignment, null: false, foreign_key: true, index: false
t.references :student, null: false, foreign_key: { to_table: :users }
t.references :selected_provisional_grade,
foreign_key: { to_table: :moderated_grading_provisional_grades },
index: { where: "selected_provisional_grade_id IS NOT NULL",
name: "index_moderated_grading_selections_on_selected_grade" }
t.timestamps precision: nil
t.index [:assignment_id, :student_id],
unique: true,
name: "idx_mg_selections_unique_on_assignment_and_student"
end
create_table :notification_endpoints do |t|
t.references :access_token, null: false, foreign_key: true
t.string :token, null: false, limit: 255
t.string :arn, null: false, limit: 255
t.timestamps precision: nil
t.string :workflow_state, default: "active", null: false, index: true
t.index [:access_token_id, :arn],
where: "workflow_state='active'",
unique: true
end
create_table :notifications do |t|
t.string :name, limit: 255, index: { unique: true, name: "index_notifications_unique_on_name" }
t.string :subject, limit: 255
t.string :category, limit: 255
t.integer :delay_for, default: 120
t.timestamps precision: nil
t.string :main_link, limit: 255
t.boolean :priority, default: false, null: false
end
create_table :notification_policies do |t|
t.references :notification
t.references :communication_channel, null: false, foreign_key: true, index: false
t.string :frequency, default: "immediately", null: false, limit: 255
t.timestamps precision: nil
t.index [:communication_channel_id, :notification_id], unique: true, name: "index_notification_policies_on_cc_and_notification_id"
end
create_table :notification_policy_overrides do |t|
t.references :context,
polymorphic: { default: "Course" },
null: false,
index: { name: "index_notification_policy_overrides_on_context" }
t.references :communication_channel, null: false, foreign_key: true
t.references :notification
t.string :workflow_state, default: "active", null: false
t.string :frequency
t.timestamps precision: nil
t.index %i[communication_channel_id notification_id],
name: "index_notification_policies_overrides_on_cc_id_and_notification"
t.index %i[context_id context_type communication_channel_id notification_id],
where: "notification_id IS NOT NULL",
unique: true,
name: "index_notification_policies_overrides_uniq_context_notification"
t.index %i[context_id context_type communication_channel_id],
where: "notification_id IS NULL",
unique: true,
name: "index_notification_policies_overrides_uniq_context_and_cc"
end
create_table :oauth_requests do |t|
t.string :token, limit: 255
t.string :secret, limit: 255
t.string :user_secret, limit: 255
t.string :return_url, limit: 4.kilobytes
t.string :workflow_state, limit: 255
t.references :user, foreign_key: true, index: { where: "user_id IS NOT NULL" }
t.string :original_host_with_port, limit: 255
t.string :service, limit: 255
t.timestamps precision: nil
end
create_table :observer_alert_thresholds do |t|
t.string :alert_type, null: false
t.string :threshold
t.string :workflow_state, default: "active", null: false
t.timestamps precision: nil
t.references :user, null: false, foreign_key: true
t.references :observer, null: false, foreign_key: { to_table: :users }
t.index %i[alert_type user_id observer_id], unique: true, name: "observer_alert_thresholds_on_alert_type_and_observer_and_user"
end
create_table :observer_alerts do |t|
t.references :observer_alert_threshold, null: false, foreign_key: true
t.references :context, polymorphic: true, index: { name: "index_observer_alerts_on_context_type_and_context_id" }
t.string :alert_type, null: false
t.string :workflow_state, default: "unread", null: false, index: true
t.timestamp :action_date, null: false
t.string :title, null: false
t.timestamps precision: nil
t.references :user, null: false, foreign_key: true
t.references :observer, null: false, foreign_key: { to_table: :users }
end
create_table :observer_pairing_codes do |t|
t.references :user, null: false, foreign_key: true
t.string :code, null: false, limit: 10
t.timestamp :expires_at, null: false, index: true
t.string :workflow_state, default: "active", null: false
t.timestamps precision: nil
end
create_table :one_time_passwords do |t|
t.references :user, null: false, foreign_key: true, index: false
t.string :code, null: false
t.boolean :used, null: false, default: false
t.timestamps precision: nil
t.index [:user_id, :code], unique: true
end
create_table :originality_reports do |t|
t.references :attachment
t.float :originality_score
t.references :originality_report_attachment
t.text :originality_report_url
t.text :originality_report_lti_url
t.timestamps precision: nil
t.references :submission, null: false, foreign_key: true
t.string :workflow_state, null: false, default: "pending", index: true
t.text :link_id
t.text :error_message
t.timestamp :submission_time, index: true
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
end
create_table :outcome_calculation_methods do |t|
t.references :context,
polymorphic: { limit: 255 },
null: false,
index: { unique: true, name: "index_outcome_calculation_methods_on_context" }
t.integer :calculation_int, limit: 2
t.string :calculation_method, null: false, limit: 255
t.string :workflow_state, null: false, default: "active"
t.references :root_account, null: false, foreign_key: { to_table: :accounts }
t.timestamps precision: nil
end
create_table :outcome_friendly_descriptions do |t|
t.references :context, polymorphic: { limit: 255 }, null: false, index: false
t.string :workflow_state, null: false, default: "active"
t.references :root_account, foreign_key: { to_table: :accounts }
t.text :description, null: false
t.timestamps precision: nil
t.references :learning_outcome, null: false, foreign_key: true
t.index %i[context_type context_id learning_outcome_id], unique: true, name: "index_outcome_friendly_description_on_context_and_outcome"
end
create_table :outcome_imports do |t|
t.string :workflow_state, null: false
t.bigint :context_id, null: false
t.string :context_type, null: false
t.references :user, foreign_key: true
t.references :attachment, foreign_key: true
t.integer :progress
t.timestamp :ended_at
t.timestamps precision: nil
t.json :data
t.references :learning_outcome_group
t.index %i[context_type context_id]
end
create_table :outcome_import_errors do |t|
t.references :outcome_import, null: false, foreign_key: true
t.string :message, null: false, limit: 255
t.timestamps precision: nil
t.integer :row
t.boolean :failure, default: false, null: false
end
create_table :outcome_proficiencies do |t|
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }
t.bigint :context_id, null: false
t.string :context_type, limit: 255, null: false
t.string :workflow_state, default: "active", null: false
t.index [:context_id, :context_type],
unique: true,
where: "context_id IS NOT NULL"
end
create_table :outcome_proficiency_ratings do |t|
t.references :outcome_proficiency, null: false, foreign_key: true
t.string :description, null: false, limit: 255
t.float :points, null: false
t.boolean :mastery, null: false
t.string :color, null: false
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }
t.string :workflow_state, default: "active", null: false
t.index [:outcome_proficiency_id, :points],
name: "index_outcome_proficiency_ratings_on_proficiency_and_points"
end
create_table :page_comments do |t|
t.text :message
t.bigint :page_id
t.string :page_type, limit: 255
t.references :user, foreign_key: true
t.timestamps precision: nil
t.index [:page_id, :page_type]
end
create_table :page_views, id: false do |t|
t.primary_keys [:request_id]
t.string :request_id, limit: 255
t.string :session_id, limit: 255
t.references :user, null: false, foreign_key: true, index: false
t.text :url
t.bigint :context_id
t.string :context_type, limit: 255
t.bigint :asset_id
t.string :asset_type, limit: 255
t.string :controller, limit: 255
t.string :action, limit: 255
t.float :interaction_seconds
t.timestamps precision: nil
t.references :developer_key, index: false
t.boolean :user_request
t.float :render_time
t.text :user_agent
t.references :asset_user_access, index: { name: "index_page_views_asset_user_access_id" }
t.boolean :participated
t.boolean :summarized
t.references :account, index: false
t.references :real_user, foreign_key: { to_table: :users }, index: { where: "real_user_id IS NOT NULL" }
t.string :http_method, limit: 255
t.string :remote_ip, limit: 255
t.index [:account_id, :created_at]
t.index [:context_type, :context_id]
t.index [:summarized, :created_at], name: "index_page_views_summarized_created_at"
t.index [:user_id, :created_at]
end
create_table :parallel_importers do |t|
t.references :sis_batch, null: false, foreign_key: true
t.string :workflow_state, null: false, limit: 255
t.bigint :index, null: false
t.bigint :batch_size, null: false
t.timestamps precision: nil
t.timestamp :started_at
t.timestamp :ended_at
t.string :importer_type, null: false, limit: 255
t.references :attachment, null: false, foreign_key: true
t.integer :rows_processed, default: 0, null: false
t.bigint :job_ids, array: true, default: [], null: false
end
create_table :planner_notes do |t|
t.timestamp :todo_date, null: false
t.string :title, null: false
t.text :details
t.references :user, null: false, foreign_key: true
t.references :course, index: false
t.string :workflow_state, null: false
t.timestamps precision: nil
t.references :linked_object, polymorphic: true, index: false
t.index %i[user_id linked_object_id linked_object_type],
where: "linked_object_id IS NOT NULL AND workflow_state<>'deleted'",
unique: true,
name: "index_planner_notes_on_user_id_and_linked_object"
end
create_table :planner_overrides do |t|
t.references :plannable, polymorphic: true, null: false, index: false
t.references :user, null: false, foreign_key: true
t.string :workflow_state
t.boolean :marked_complete, null: false, default: false
t.timestamp :deleted_at
t.timestamps precision: nil
t.boolean :dismissed, default: false, null: false
t.index %i[plannable_type plannable_id user_id], unique: true, name: "index_planner_overrides_on_plannable_and_user"
end
create_table :plugin_settings do |t|
t.string :name, default: "", null: false, limit: 255, index: true
t.text :settings
t.timestamps precision: nil
t.boolean :disabled
end
create_table :polling_polls do |t|
t.string :question, limit: 255
t.string :description, limit: 255
t.timestamps precision: nil
t.references :user, null: false, foreign_key: true
end
create_table :polling_poll_choices do |t|
t.string :text, limit: 255
t.boolean :is_correct, null: false, default: false
t.references :poll, null: false, foreign_key: { to_table: :polling_polls }
t.timestamps precision: nil
t.integer :position
end
create_table :polling_poll_sessions do |t|
t.boolean :is_published, null: false, default: false
t.boolean :has_public_results, null: false, default: false
t.references :course, null: false, foreign_key: true
t.references :course_section, foreign_key: true
t.references :poll, null: false
t.timestamps precision: nil
end
create_table :polling_poll_submissions do |t|
t.references :poll, null: false, foreign_key: { to_table: :polling_polls }
t.references :poll_choice, null: false, foreign_key: { to_table: :polling_poll_choices }
t.references :user, null: false, foreign_key: true
t.timestamps precision: nil
t.references :poll_session, null: false, foreign_key: { to_table: :polling_poll_sessions }
end
create_table :post_policies do |t|
t.boolean :post_manually, null: false, default: false
t.references :course, foreign_key: true
t.references :assignment, foreign_key: true
t.index [:course_id, :assignment_id], unique: true
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }
end
create_table :profiles do |t|
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.references :context,
polymorphic: { limit: 255 },
null: false,
index: { unique: true, name: "index_profiles_on_context_type_and_context_id" }
t.string :title, limit: 255
t.string :path, limit: 255
t.text :description
t.text :data
t.string :visibility, limit: 255
t.integer :position
t.index [:root_account_id, :path], unique: true
end
create_table :progresses do |t|
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.references :user, index: false
t.string :tag, null: false, limit: 255
t.float :completion
t.string :delayed_job_id, limit: 255
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.text :message
t.string :cache_key_context, limit: 255
t.text :results
t.index [:context_id, :context_type]
end
create_table :pseudonyms do |t|
t.references :user, null: false, foreign_key: true
t.references :account, null: false, foreign_key: true, index: false
t.string :workflow_state, null: false, limit: 255
t.string :unique_id, null: false, limit: 255
t.string :crypted_password, null: false, limit: 255
t.string :password_salt, null: false, limit: 255
t.string :persistence_token, null: false, limit: 255, index: true
t.string :single_access_token, null: false, limit: 255, index: true
t.string :perishable_token, null: false, limit: 255
t.integer :login_count, default: 0, null: false
t.integer :failed_login_count, default: 0, null: false
t.timestamp :last_request_at
t.timestamp :last_login_at
t.timestamp :current_login_at
t.string :last_login_ip, limit: 255
t.string :current_login_ip, limit: 255
t.string :reset_password_token, default: "", null: false, limit: 255
t.integer :position
t.timestamps precision: nil
t.boolean :password_auto_generated
t.timestamp :deleted_at
t.references :sis_batch, foreign_key: true, index: { where: "sis_batch_id IS NOT NULL" }
t.string :sis_user_id, limit: 255
t.string :sis_ssha, limit: 255
t.references :communication_channel, index: false
t.references :sis_communication_channel
t.text :stuck_sis_fields
t.string :integration_id, limit: 255
t.references :authentication_provider, foreign_key: true, index: { where: "authentication_provider_id IS NOT NULL" }
t.string :declared_user_type, limit: 255
t.replica_identity_index :account_id
if (trgm = connection.extension(:pg_trgm)&.schema)
t.index "lower(sis_user_id) #{trgm}.gin_trgm_ops", name: "index_gin_trgm_pseudonyms_sis_user_id", using: :gin
t.index "lower(unique_id) #{trgm}.gin_trgm_ops", name: "index_gin_trgm_pseudonyms_unique_id", using: :gin
end
t.index [:sis_user_id, :account_id], where: "sis_user_id IS NOT NULL", unique: true
t.index [:integration_id, :account_id],
unique: true,
name: "index_pseudonyms_on_integration_id",
where: "integration_id IS NOT NULL"
t.index "LOWER(unique_id), account_id, authentication_provider_id",
name: "index_pseudonyms_unique_with_auth_provider",
unique: true,
where: "workflow_state IN ('active', 'suspended')"
t.index "LOWER(unique_id), account_id",
name: "index_pseudonyms_unique_without_auth_provider",
unique: true,
where: "workflow_state IN ('active', 'suspended') AND authentication_provider_id IS NULL"
t.index "LOWER(unique_id), account_id", name: "index_pseudonyms_on_unique_id_and_account_id"
end
create_table :purgatories do |t|
t.references :attachment, null: false, foreign_key: true, index: { unique: true }
t.references :deleted_by_user, foreign_key: { to_table: :users }
t.timestamps precision: nil
t.string :workflow_state, null: false, default: "active", index: true
t.string :old_filename, null: false
t.string :old_display_name, limit: 255
t.string :old_content_type, limit: 255
t.string :new_instfs_uuid
t.string :old_file_state
t.string :old_workflow_state
end
create_table :quizzes do |t|
t.string :title, limit: 255
t.text :description, limit: 16_777_215
t.text :quiz_data, limit: 16_777_215
t.float :points_possible
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.references :assignment, foreign_key: true, index: { unique: true }
t.string :workflow_state, null: false, limit: 255
t.boolean :shuffle_answers, default: false, null: false
t.boolean :show_correct_answers, default: true, null: false
t.integer :time_limit
t.integer :allowed_attempts
t.string :scoring_policy, limit: 255
t.string :quiz_type, limit: 255
t.timestamps precision: nil
t.timestamp :lock_at
t.timestamp :unlock_at
t.timestamp :deleted_at
t.boolean :could_be_locked, default: false, null: false
t.references :cloned_item, foreign_key: true, index: { where: "cloned_item_id IS NOT NULL" }
t.string :access_code, limit: 255
t.string :migration_id, limit: 255
t.integer :unpublished_question_count, default: 0
t.timestamp :due_at
t.integer :question_count
t.references :last_assignment, index: false
t.timestamp :published_at
t.timestamp :last_edited_at
t.boolean :anonymous_submissions, default: false, null: false
t.references :assignment_group, index: false
t.string :hide_results, limit: 255
t.string :ip_filter, limit: 255
t.boolean :require_lockdown_browser, default: false, null: false
t.boolean :require_lockdown_browser_for_results, default: false, null: false
t.boolean :one_question_at_a_time, default: false, null: false
t.boolean :cant_go_back, default: false, null: false
t.timestamp :show_correct_answers_at
t.timestamp :hide_correct_answers_at
t.boolean :require_lockdown_browser_monitor, default: false, null: false
t.text :lockdown_browser_monitor_data
t.boolean :only_visible_to_overrides, default: false, null: false
t.boolean :one_time_results, default: false, null: false
t.boolean :show_correct_answers_last_attempt, default: false, null: false
t.references :root_account, foreign_key: { to_table: :accounts }
t.boolean :disable_timer_autosubmission, default: false, null: false
t.index [:context_id, :context_type]
end
create_table :quiz_groups do |t|
t.references :quiz, null: false
t.string :name, limit: 255
t.integer :pick_count
t.float :question_points
t.integer :position
t.timestamps precision: nil
t.string :migration_id, limit: 255
t.references :assessment_question_bank, index: false
t.references :root_account, foreign_key: { to_table: :accounts }
end
create_table :quiz_migration_alerts do |t|
t.references :migration, polymorphic: true, index: { name: "index_quiz_migration_alerts_on_migration_type_and_migration_id" }
t.references :user, null: false, foreign_key: true
t.references :course, null: false, foreign_key: true
t.timestamps precision: 6
end
create_table :quiz_questions do |t|
t.references :quiz, index: false
t.references :quiz_group, index: { name: "quiz_questions_quiz_group_id" }
t.references :assessment_question, index: { where: "assessment_question_id IS NOT NULL" }
t.text :question_data
t.integer :assessment_question_version
t.integer :position
t.timestamps null: true, precision: nil
t.string :migration_id, limit: 255
t.string :workflow_state, limit: 255
t.integer :duplicate_index
t.references :root_account
t.index [:quiz_id, :assessment_question_id], name: "idx_qqs_on_quiz_and_aq_ids"
t.index %i[assessment_question_id quiz_group_id duplicate_index],
name: "index_generated_quiz_questions",
where: "assessment_question_id IS NOT NULL AND quiz_group_id IS NOT NULL AND workflow_state='generated'",
unique: true
end
create_table :quiz_regrades do |t|
t.references :user, null: false, foreign_key: true
t.references :quiz, null: false, foreign_key: true, index: false
t.integer :quiz_version, null: false
t.timestamps precision: nil
t.index [:quiz_id, :quiz_version], unique: true
end
create_table :quiz_question_regrades do |t|
t.references :quiz_regrade, null: false, foreign_key: true, index: false
t.references :quiz_question, null: false, foreign_key: true, index: { name: "index_qqr_on_qq_id" }
t.string :regrade_option, null: false, limit: 255
t.timestamps precision: nil
t.index [:quiz_regrade_id, :quiz_question_id], unique: true, name: "index_qqr_on_qr_id_and_qq_id"
end
create_table :quiz_regrade_runs do |t|
t.references :quiz_regrade, null: false, foreign_key: true
t.timestamp :started_at
t.timestamp :finished_at
t.timestamps precision: nil
end
create_table :quiz_statistics do |t|
t.references :quiz, foreign_key: true, index: false
t.boolean :includes_all_versions
t.boolean :anonymous
t.timestamps precision: nil
t.string :report_type, limit: 255
t.boolean :includes_sis_ids
t.index [:quiz_id, :report_type]
end
create_table :quiz_submissions do |t|
t.references :quiz, null: false, foreign_key: true, index: false
t.integer :quiz_version
t.references :user, foreign_key: { deferrable: :immediate }
t.text :submission_data, limit: 16_777_215
t.references :submission
t.float :score
t.float :kept_score
t.text :quiz_data, limit: 16_777_215
t.timestamp :started_at
t.timestamp :end_at
t.timestamp :finished_at
t.integer :attempt
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.integer :fudge_points, default: 0
t.float :quiz_points_possible
t.integer :extra_attempts
t.string :temporary_user_code, limit: 255, index: true
t.integer :extra_time
t.boolean :manually_unlocked
t.boolean :manually_scored
t.string :validation_token, limit: 255
t.float :score_before_regrade
t.boolean :was_preview
t.boolean :has_seen_results
t.boolean :question_references_fixed
t.references :root_account, foreign_key: { to_table: :accounts }
t.index [:quiz_id, :user_id], unique: true
end
# If the column is created as a float with default 0, it becomes 0.0, which
# would be fine, but it's easier to compare schema consistency this way.
change_column :quiz_submissions, :fudge_points, :float
create_table :quiz_submission_events do |t|
t.integer :attempt, null: false
t.string :event_type, null: false, limit: 255
t.references :quiz_submission, null: false, foreign_key: true, index: false
t.text :event_data
t.timestamp :created_at, null: false, index: true
t.timestamp :client_timestamp
t.references :root_account, foreign_key: { to_table: :accounts }
t.index %i[quiz_submission_id attempt created_at],
name: "event_predecessor_locator_index"
end
create_table :quiz_submission_snapshots do |t|
t.references :quiz_submission
t.integer :attempt
t.text :data
t.timestamps null: true, precision: nil
end
create_table :report_snapshots do |t|
t.string :report_type, limit: 255
t.text :data, limit: 16_777_215
t.timestamps precision: nil
t.references :account, foreign_key: true, index: { where: "account_id IS NOT NULL" }
t.index %i[report_type account_id created_at], name: "index_on_report_snapshots"
end
create_table :roles do |t|
t.string :name, null: false, limit: 255, index: true
t.string :base_role_type, null: false, limit: 255
t.references :account, foreign_key: true
t.string :workflow_state, null: false, limit: 255
t.timestamps precision: nil
t.timestamp :deleted_at
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.replica_identity_index
t.index [:account_id, :name], unique: true, name: "index_roles_unique_account_name_where_active", where: "workflow_state = 'active'"
end
create_table :role_overrides do |t|
t.string :permission, limit: 255
t.boolean :enabled, default: true, null: false
t.boolean :locked, default: false, null: false
t.references :context, null: false, foreign_key: { to_table: :accounts }, index: false
t.string :context_type, limit: 255, null: false
t.timestamps null: true, precision: nil
t.boolean :applies_to_self, default: true, null: false
t.boolean :applies_to_descendants, default: true, null: false
t.references :role, null: false, foreign_key: true
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.index %i[context_id context_type role_id permission],
unique: true,
name: "index_role_overrides_on_context_role_permission"
end
create_table :rubrics do |t|
t.references :user, foreign_key: true
t.references :rubric, foreign_key: true, index: { where: "rubric_id IS NOT NULL" }
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.text :data
t.float :points_possible
t.string :title, limit: 255
t.text :description
t.timestamps precision: nil
t.boolean :reusable, default: false
t.boolean :public, default: false
t.boolean :read_only, default: false
t.integer :association_count, default: 0
t.boolean :free_form_criterion_comments
t.string :context_code, limit: 255
t.string :migration_id, limit: 255
t.boolean :hide_score_total
t.string :workflow_state, default: "active", null: false, limit: 255
t.references :root_account, foreign_key: { to_table: :accounts }
t.boolean :hide_points, default: false, null: false
t.string :rating_order, default: "descending", null: false
t.string :button_display, default: "numeric", null: false
t.check_constraint "rating_order IN ('descending', 'ascending')", name: "check_rating_order"
t.check_constraint "button_display IN ('numeric', 'emoji', 'letter')", name: "check_button_display"
t.index [:context_id, :context_type]
end
create_table :rubric_associations do |t|
t.references :rubric, null: false, foreign_key: true
t.bigint :association_id, null: false
t.string :association_type, null: false, limit: 255
t.boolean :use_for_grading
t.timestamps precision: nil
t.string :title, limit: 255
t.text :summary_data
t.string :purpose, null: false, limit: 255
t.string :url, limit: 255
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.boolean :hide_score_total
t.boolean :bookmarked, default: true
t.string :context_code, limit: 255, index: true
t.boolean :hide_points, default: false
t.boolean :hide_outcome_results, default: false
t.references :root_account
t.string :workflow_state, default: "active", null: false
t.index [:association_id, :association_type], name: "index_rubric_associations_on_aid_and_atype"
t.index [:context_id, :context_type]
end
create_table :rubric_assessments do |t|
t.references :user, foreign_key: true
t.references :rubric, null: false, foreign_key: true
t.references :rubric_association, foreign_key: true
t.float :score
t.text :data
t.timestamps precision: nil
t.bigint :artifact_id, null: false
t.string :artifact_type, null: false, limit: 255
t.string :assessment_type, null: false, limit: 255
t.references :assessor, foreign_key: { to_table: :users }
t.integer :artifact_attempt
t.boolean :hide_points, default: false, null: false
t.references :root_account, foreign_key: { to_table: :accounts }
t.index [:artifact_id, :artifact_type]
end
create_table :rubric_criteria do |t|
t.references :rubric, null: false, foreign_key: { to_table: :rubrics }
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.text :description
t.text :long_description
t.integer :order, null: false
t.decimal :points, null: false
t.boolean :criterion_use_range, null: false, default: false
t.references :learning_outcome, foreign_key: { to_table: :learning_outcomes }
t.decimal :mastery_points
t.boolean :ignore_for_scoring, null: false, default: false
t.string :workflow_state, null: false, default: "active", limit: 255
t.references :created_by, null: false, foreign_key: { to_table: :users }
t.references :deleted_by, foreign_key: { to_table: :users }
t.timestamps
t.replica_identity_index
end
create_table :scheduled_smart_alerts do |t|
t.string :context_type, null: false
t.string :alert_type, null: false
t.bigint :context_id, null: false
t.timestamp :due_at, null: false, index: true
t.references :root_account, null: false, foreign_key: { to_table: :accounts }
t.timestamps precision: nil
t.index %i[context_type context_id alert_type root_account_id], name: "index_unique_scheduled_smart_alert"
end
create_table :scores do |t|
t.references :enrollment, null: false, foreign_key: true, index: { name: "index_enrollment_scores" }
t.references :grading_period, foreign_key: true, index: { where: "grading_period_id IS NOT NULL" }
t.string :workflow_state, default: :active, null: false, limit: 255
t.float :current_score
t.float :final_score
t.timestamps null: true, precision: nil
t.references :assignment_group
t.boolean :course_score, default: false, null: false
t.float :unposted_current_score
t.float :unposted_final_score
t.float :current_points
t.float :unposted_current_points
t.float :final_points
t.float :unposted_final_points
t.float :override_score
t.references :root_account, foreign_key: { to_table: :accounts }
t.references :custom_grade_status, foreign_key: true, index: { where: "custom_grade_status_id IS NOT NULL" }
t.index %i[enrollment_id grading_period_id],
unique: true,
where: "grading_period_id IS NOT NULL",
name: "index_grading_period_scores"
t.index %i[enrollment_id assignment_group_id],
unique: true,
where: "assignment_group_id IS NOT NULL",
name: "index_assignment_group_scores"
t.index :enrollment_id, unique: true, where: "course_score", name: "index_course_scores"
end
create_table :score_metadata do |t|
t.references :score, null: false, foreign_key: true, index: { unique: true }
t.json :calculation_details, default: {}, null: false
t.timestamps precision: nil
t.string :workflow_state, default: "active", null: false
end
create_table :score_statistics do |t|
t.references :assignment, null: false, index: { unique: true }, foreign_key: true
t.float :minimum, null: false
t.float :maximum, null: false
t.float :mean, null: false
t.integer :count, null: false
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }
t.float :lower_q
t.float :median
t.float :upper_q
end
create_table :sessions do |t|
t.string :session_id, null: false, limit: 255, index: true
t.text :data
t.timestamps precision: nil
t.index :updated_at
end
create_table :session_persistence_tokens do |t|
t.string :token_salt, null: false, limit: 255
t.string :crypted_token, null: false, limit: 255
t.references :pseudonym, null: false, foreign_key: true
t.timestamps precision: nil
end
create_table :settings do |t|
t.string :name, limit: 255, index: { unique: true }
t.text :value
t.timestamps precision: nil
t.boolean :secret, default: false, null: false
end
create_table :shared_brand_configs do |t|
t.string :name, limit: 255
t.references :account, foreign_key: true
t.string :brand_config_md5, limit: 32, null: false, index: true
t.timestamps precision: nil
t.foreign_key :brand_configs, column: :brand_config_md5, primary_key: :md5
end
create_table :sis_batch_errors do |t|
t.references :sis_batch, null: false, foreign_key: true
t.references :root_account, null: false, foreign_key: { to_table: :accounts }
t.text :message, null: false
t.text :backtrace
t.string :file, limit: 255
t.boolean :failure, default: false, null: false
t.integer :row
t.timestamp :created_at, null: false, index: true
t.text :row_info
end
create_table :sis_batch_roll_back_data do |t|
t.references :sis_batch, null: false, foreign_key: true
t.references :context, polymorphic: { limit: 255 }, null: false, index: false
t.string :previous_workflow_state, null: false, limit: 255
t.string :updated_workflow_state, null: false, limit: 255
t.boolean :batch_mode_delete, null: false, default: false
t.string :workflow_state, null: false, limit: 255, default: "active", index: true
t.timestamps precision: nil
t.index %i[updated_workflow_state previous_workflow_state],
name: "index_sis_batch_roll_back_context_workflow_states"
end
create_table :sis_post_grades_statuses do |t|
t.references :course, null: false, foreign_key: true
t.references :course_section, foreign_key: true
t.references :user, foreign_key: true
t.string :status, null: false, limit: 255
t.string :message, null: false, limit: 255
t.timestamp :grades_posted_at, null: false
t.timestamps precision: nil
end
create_table :standard_grade_statuses do |t|
t.string :color, limit: 7, null: false
t.string :status_name, null: false
t.boolean :hidden, default: false, null: false
t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false
t.timestamps precision: 6
t.replica_identity_index
t.index [:status_name, :root_account_id], unique: true, name: "index_standard_status_on_name_and_root_account_id"
end
create_table :stream_items do |t|
t.text :data, null: false
t.timestamps precision: nil
t.references :context, polymorphic: { limit: 255 }, index: false
t.string :asset_type, null: false, limit: 255
t.bigint :asset_id
t.string :notification_category, limit: 255
t.index [:asset_type, :asset_id], unique: true
t.index :updated_at
end
create_table :stream_item_instances do |t|
t.references :user, null: false, foreign_key: true, index: false
t.references :stream_item, null: false
t.boolean :hidden, default: false, null: false
t.string :workflow_state, limit: 255
t.references :context, polymorphic: { limit: 255 }, index: { name: "index_stream_item_instances_on_context_type_and_context_id" }
t.index %i[user_id hidden id stream_item_id], name: "index_stream_item_instances_global"
t.index [:stream_item_id, :user_id], unique: true
end
create_table :submissions do |t|
t.text :body, limit: 16_777_215
t.string :url, limit: 255
t.references :attachment, index: false
t.string :grade, limit: 255
t.float :score
t.timestamp :submitted_at, index: true
t.references :assignment, null: false, foreign_key: true, index: false
t.references :user, null: false, foreign_key: { deferrable: :immediate }, index: false
t.string :submission_type, limit: 255
t.string :workflow_state, null: false, limit: 255
t.timestamps null: true, precision: nil
t.references :group, foreign_key: true, index: { where: "group_id IS NOT NULL" }
t.text :attachment_ids
t.boolean :processed
t.boolean :grade_matches_current_submission
t.float :published_score
t.string :published_grade, limit: 255
t.timestamp :graded_at, index: { using: :brin }
t.float :student_entered_score
t.references :grader, index: false
t.string :media_comment_id, limit: 255
t.string :media_comment_type, limit: 255
t.references :quiz_submission, foreign_key: true, index: { where: "quiz_submission_id IS NOT NULL" }
t.integer :submission_comments_count
t.integer :attempt
t.references :media_object, foreign_key: true, index: { where: "media_object_id IS NOT NULL" }
t.text :turnitin_data
t.timestamp :cached_due_date, index: true
t.boolean :excused
t.boolean :graded_anonymously
t.string :late_policy_status, limit: 16, index: { where: "workflow_state<>'deleted' AND late_policy_status IS NOT NULL" }
t.decimal :points_deducted, precision: 6, scale: 2
t.references :grading_period, foreign_key: true, index: { where: "grading_period_id IS NOT NULL" }
t.bigint :seconds_late_override
t.string :lti_user_id
t.string :anonymous_id, limit: 5
t.timestamp :last_comment_at
t.integer :extra_attempts
t.timestamp :posted_at
t.boolean :cached_quiz_lti, default: false, null: false, index: true
t.string :cached_tardiness, limit: 16
t.references :course, foreign_key: true, index: false, null: false
t.references :root_account, foreign_key: { to_table: :accounts }
t.boolean :redo_request, default: false, null: false
t.uuid :resource_link_lookup_uuid
t.references :proxy_submitter, foreign_key: { to_table: :users }
t.references :custom_grade_status, foreign_key: true, index: { where: "custom_grade_status_id IS NOT NULL" }
t.string :sticker, limit: 255
t.index [:assignment_id, :submission_type]
t.index [:user_id, :assignment_id], unique: true
t.index [:assignment_id, :user_id]
t.index :assignment_id, name: "index_submissions_needs_grading", where: <<~SQL.squish
submissions.submission_type IS NOT NULL
AND (submissions.excused = 'f' OR submissions.excused IS NULL)
AND (submissions.workflow_state = 'pending_review'
OR (submissions.workflow_state IN ('submitted', 'graded')
AND (submissions.score IS NULL OR NOT submissions.grade_matches_current_submission)
)
)
SQL
t.index [:assignment_id, :grading_period_id],
name: "index_active_submissions",
where: "workflow_state <> 'deleted'"
t.index [:assignment_id, :grading_period_id],
where: "workflow_state<>'deleted' AND grading_period_id IS NOT NULL",
name: "index_active_submissions_gp"
t.index %i[assignment_id anonymous_id], unique: true, where: "anonymous_id IS NOT NULL"
t.index "user_id, GREATEST(submitted_at, created_at)", name: "index_submissions_on_user_and_greatest_dates"
t.index :user_id,
where: "(score IS NOT NULL AND workflow_state = 'graded') OR excused = TRUE",
name: "index_submissions_graded_or_excused_on_user_id"
t.index :assignment_id,
where: "workflow_state <> 'deleted' AND ((score IS NOT NULL AND workflow_state = 'graded') OR excused = TRUE)",
name: "index_submissions_graded_or_excused_on_assignment_id"
t.index [:user_id, :cached_due_date]
t.index [:user_id, :course_id]
t.index [:user_id, :course_id],
where: "(score IS NOT NULL OR grade IS NOT NULL) AND workflow_state<>'deleted'",
name: "index_submissions_with_grade"
t.index [:course_id, :cached_due_date]
t.index :user_id, where: "late_policy_status='missing'", name: "index_on_submissions_missing_for_user"
t.index :assignment_id, name: "index_graded_submissions_on_assignments", where: "workflow_state='graded'"
end
create_table :submission_comments do |t|
t.text :comment
t.references :submission, foreign_key: true
t.references :author, foreign_key: { to_table: :users }
t.string :author_name, limit: 255
t.string :group_comment_id, limit: 255
t.timestamps precision: nil
t.text :attachment_ids
t.references :assessment_request, index: false
t.string :media_comment_id, limit: 255
t.string :media_comment_type, limit: 255
t.bigint :context_id
t.string :context_type, limit: 255
t.text :cached_attachments
t.boolean :anonymous
t.boolean :teacher_only_comment, default: false
t.boolean :hidden, default: false
t.references :provisional_grade, foreign_key: { to_table: :moderated_grading_provisional_grades }, index: { where: "provisional_grade_id IS NOT NULL" }
t.boolean :draft, default: false, null: false, index: true
t.timestamp :edited_at
t.integer :attempt, index: true
t.references :root_account, foreign_key: { to_table: :accounts }
t.string :workflow_state, default: "active", null: false
t.index [:context_id, :context_type]
end
create_table :submission_drafts do |t|
t.references :submission, null: false, foreign_key: true
t.integer :submission_attempt, index: true, null: false
t.text :body
t.text :url
t.string :active_submission_type
# This is actually the media_id e.g. m-123456 rather than the media_object.id
t.string :media_object_id
t.references :context_external_tool, index: false
t.text :lti_launch_url
t.uuid :resource_link_lookup_uuid
end
create_table :submission_draft_attachments do |t|
t.references :submission_draft, null: false
t.references :attachment, null: false
t.index [:submission_draft_id, :attachment_id],
name: "index_submission_draft_and_attachment_unique",
unique: true
end
create_table :submission_versions do |t|
t.bigint :context_id
t.string :context_type, limit: 255
t.references :version
t.references :user, index: false
t.references :assignment, index: false
t.references :root_account, foreign_key: { to_table: :accounts }
t.index %i[context_id version_id user_id assignment_id],
name: "index_submission_versions",
where: "context_type='Course'",
unique: true
end
# rubocop:disable Rails/SquishedSQLHeredocs
execute(<<~SQL)
CREATE FUNCTION #{connection.quote_table_name("submission_comment_after_save_set_last_comment_at__tr_fn")} () RETURNS trigger AS $$
BEGIN
UPDATE submissions
SET last_comment_at = (
SELECT MAX(submission_comments.created_at) FROM submission_comments
WHERE submission_comments.submission_id=submissions.id AND
submission_comments.author_id <> submissions.user_id AND
submission_comments.draft <> 't' AND
submission_comments.provisional_grade_id IS NULL
) WHERE id = NEW.submission_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
SQL
execute(<<~SQL)
CREATE FUNCTION #{connection.quote_table_name("submission_comment_after_delete_set_last_comment_at__tr_fn")} () RETURNS trigger AS $$
BEGIN
UPDATE submissions
SET last_comment_at = (
SELECT MAX(submission_comments.created_at) FROM submission_comments
WHERE submission_comments.submission_id=submissions.id AND
submission_comments.author_id <> submissions.user_id AND
submission_comments.draft <> 't' AND
submission_comments.provisional_grade_id IS NULL
) WHERE id = OLD.submission_id;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
SQL
# rubocop:enable Rails/SquishedSQLHeredocs
set_search_path("submission_comment_after_save_set_last_comment_at__tr_fn", "()")
set_search_path("submission_comment_after_delete_set_last_comment_at__tr_fn", "()")
execute(<<~SQL.squish)
CREATE TRIGGER submission_comment_after_insert_set_last_comment_at__tr
AFTER INSERT ON #{SubmissionComment.quoted_table_name}
FOR EACH ROW
WHEN (NEW.draft <> 't' AND NEW.provisional_grade_id IS NULL)
EXECUTE PROCEDURE #{connection.quote_table_name("submission_comment_after_save_set_last_comment_at__tr_fn")}()
SQL
execute(<<~SQL.squish)
CREATE TRIGGER submission_comment_after_update_set_last_comment_at__tr
AFTER UPDATE OF draft, provisional_grade_id ON #{SubmissionComment.quoted_table_name}
FOR EACH ROW
EXECUTE PROCEDURE #{connection.quote_table_name("submission_comment_after_save_set_last_comment_at__tr_fn")}()
SQL
execute(<<~SQL.squish)
CREATE TRIGGER submission_comment_after_delete_set_last_comment_at__tr
AFTER DELETE ON #{SubmissionComment.quoted_table_name}
FOR EACH ROW
WHEN (OLD.draft <> 't' AND OLD.provisional_grade_id IS NULL)
EXECUTE PROCEDURE #{connection.quote_table_name("submission_comment_after_delete_set_last_comment_at__tr_fn")}()
SQL
create_table :switchman_shards do |t|
t.string :name, limit: 255
t.string :database_server_id, limit: 255, index: { unique: true,
where: "name IS NULL",
name: "index_switchman_shards_unique_primary_shard" }
t.boolean :default, default: false, null: false, index: { unique: true, where: '"default"' }
t.text :settings
t.references :delayed_jobs_shard,
foreign_key: { to_table: :switchman_shards },
index: { where: "delayed_jobs_shard_id IS NOT NULL" }
t.timestamps precision: nil
t.boolean :block_stranded, default: false
t.boolean :jobs_held, default: false
t.index [:database_server_id, :name], unique: true
t.index "(true)",
unique: true,
where: "database_server_id IS NULL AND name IS NULL",
name: "index_switchman_shards_unique_primary_db_and_shard"
end
create_table :temporary_enrollment_pairings, if_not_exists: true do |t|
t.references :root_account, foreign_key: { to_table: :accounts }, null: false, index: false
t.string :workflow_state, null: false, default: "active", limit: 255
t.timestamps
t.references :created_by, foreign_key: { to_table: :users }
t.references :deleted_by, foreign_key: { to_table: :users }
t.text :ending_enrollment_state
t.replica_identity_index
end
create_table :terms_of_service_contents do |t|
t.text :content, null: false
t.timestamps precision: nil
t.timestamp :terms_updated_at, null: false
t.string :workflow_state, null: false
t.references :account, foreign_key: true, index: { unique: true }
end
create_table :terms_of_services do |t|
t.string :terms_type, null: false, default: "default"
t.boolean :passive, null: false, default: true
t.references :terms_of_service_content, index: false
t.references :account, null: false, foreign_key: true, index: { unique: true }
t.timestamps precision: nil
t.string :workflow_state, null: false
end
create_table :thumbnails do |t|
t.references :parent
t.string :content_type, null: false, limit: 255
t.string :filename, null: false, limit: 255
t.string :thumbnail, limit: 255
t.integer :size, null: false
t.integer :width
t.integer :height
t.timestamps precision: nil
t.string :uuid, limit: 255
t.string :namespace, limit: 255
t.index [:parent_id, :thumbnail], unique: true, name: "index_thumbnails_size"
end
create_table :usage_rights do |t|
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.string :use_justification, null: false, limit: 255
t.string :license, null: false, limit: 255
t.text :legal_copyright
t.index [:context_id, :context_type], name: "usage_rights_context_idx"
end
create_table :user_account_associations do |t|
t.references :user, null: false, foreign_key: true, index: false
t.references :account, null: false, foreign_key: true
t.integer :depth
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.index [:user_id, :account_id], unique: true
end
create_table :user_lmgb_outcome_orderings do |t|
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.references :user, null: false, foreign_key: true
t.references :course, null: false, foreign_key: true
t.references :learning_outcome, null: false, foreign_key: true
t.integer :position, null: false
t.timestamps precision: 6
t.replica_identity_index
t.index %i[learning_outcome_id user_id course_id],
unique: true,
name: "index_user_lmgb_outcome_orderings"
end
create_table :user_merge_data do |t|
t.references :user, null: false, foreign_key: true
t.references :from_user, null: false
t.timestamps precision: nil
t.string :workflow_state, null: false, default: "active", limit: 255
end
create_table :user_merge_data_items do |t|
t.references :user_merge_data, null: false, foreign_key: true
t.references :user, null: false, foreign_key: true
t.string :item_type, null: false, limit: 255
t.text :item, null: false
end
create_table :user_merge_data_records do |t|
t.references :user_merge_data, null: false, foreign_key: true
t.bigint :context_id, null: false
t.references :previous_user, null: false, index: false
t.string :context_type, null: false, limit: 255
t.string :previous_workflow_state, limit: 255
t.index %i[context_id context_type user_merge_data_id previous_user_id],
name: "index_user_merge_data_records_on_context_id_and_context_type"
end
create_table :user_notes do |t|
t.references :user, foreign_key: true, index: false
t.text :note
t.string :title, limit: 255
t.references :created_by, foreign_key: { to_table: :users }
t.string :workflow_state, default: "active", null: false, limit: 255
t.timestamp :deleted_at
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.index [:user_id, :workflow_state]
end
create_table :user_observers do |t|
t.references :user, null: false, foreign_key: true, index: false
t.references :observer, null: false, foreign_key: { to_table: :users }
t.string :workflow_state, default: "active", null: false, limit: 255, index: true
t.timestamps precision: nil
t.references :sis_batch, index: { where: "sis_batch_id IS NOT NULL" }
t.references :root_account, null: false, index: false
t.index %i[user_id observer_id root_account_id],
unique: true,
name: "index_user_observers_on_user_id_and_observer_id_and_ra"
end
create_table :user_past_lti_ids do |t|
t.references :user, null: false, foreign_key: true
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.string :user_uuid, null: false, limit: 255, index: true
t.text :user_lti_id, null: false
t.string :user_lti_context_id, limit: 255, index: true
t.index %i[user_id context_id context_type], name: "user_past_lti_ids_index", unique: true
end
create_table :user_preference_values do |t|
t.references :user, null: false, foreign_key: true, index: false
t.string :key, null: false
t.string :sub_key
t.text :value
t.index %i[user_id key sub_key], unique: true, name: "index_user_preference_values_on_keys"
t.index [:user_id, :key],
unique: true,
where: "sub_key IS NULL",
name: "index_user_preference_values_on_key_no_sub_key"
end
create_table :user_profiles do |t|
t.text :bio
t.string :title, limit: 255
t.references :user, foreign_key: true, null: false
end
create_table :user_profile_links do |t|
t.string :url, limit: 4.kilobytes
t.string :title, limit: 255
t.references :user_profile, foreign_key: true, index: { where: "user_profile_id IS NOT NULL" }
t.timestamps precision: nil
end
create_table :user_services do |t|
t.references :user, null: false, foreign_key: true
t.text :token
t.string :secret, limit: 255
t.string :protocol, limit: 255
t.string :service, null: false, limit: 255
t.timestamps precision: nil
t.string :service_user_url, limit: 255
t.string :service_user_id, null: false, limit: 255
t.string :service_user_name, limit: 255
t.string :service_domain, limit: 255
t.string :crypted_password, limit: 255
t.string :password_salt, limit: 255
t.string :type, limit: 255
t.string :workflow_state, null: false, limit: 255
t.string :last_result_id, limit: 255
t.timestamp :refresh_at
t.boolean :visible
end
create_table :versions do |t|
t.bigint :versionable_id
t.string :versionable_type, limit: 255
t.integer :number
t.text :yaml, limit: 16_777_215
t.timestamp :created_at
t.index %i[versionable_id versionable_type number], unique: true, name: "index_versions_on_versionable_object_and_number"
end
create_table :viewed_submission_comments do |t|
t.references :user, null: false, foreign_key: true, index: false
t.references :submission_comment, null: false, foreign_key: true
t.timestamp :viewed_at, null: false
t.index [:user_id, :submission_comment_id], name: "index_viewed_submission_comments_user_comment", unique: true
end
create_table :web_conferences do |t|
t.string :title, null: false, limit: 255
t.string :conference_type, null: false, limit: 255
t.string :conference_key, limit: 255
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.string :user_ids, limit: 255
t.string :added_user_ids, limit: 255
t.references :user, null: false, foreign_key: true
t.timestamp :started_at
t.text :description
t.float :duration
t.timestamps precision: nil
t.string :uuid, limit: 255
t.string :invited_user_ids, limit: 255
t.timestamp :ended_at
t.timestamp :start_at
t.timestamp :end_at
t.string :context_code, limit: 255
t.string :type, limit: 255
t.text :settings
t.boolean :recording_ready
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
t.index [:context_id, :context_type]
end
create_table :web_conference_participants do |t|
t.references :user, foreign_key: true
t.references :web_conference, foreign_key: true
t.string :participation_type, limit: 255
t.timestamps precision: nil
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.replica_identity_index
end
create_table :wikis do |t|
t.string :title, limit: 255
t.timestamps precision: nil
t.text :front_page_url
t.boolean :has_no_front_page
t.references :root_account, foreign_key: { to_table: :accounts }
end
create_table :wiki_pages do |t|
t.references :wiki, null: false, foreign_key: true
t.string :title, limit: 255
t.text :body, limit: 16_777_215
t.string :workflow_state, null: false, limit: 255
t.references :user, foreign_key: true
t.timestamps precision: nil
t.text :url
t.boolean :protected_editing, default: false
t.string :editing_roles, limit: 255
t.timestamp :revised_at
t.boolean :could_be_locked
t.references :cloned_item, foreign_key: true, index: { where: "cloned_item_id IS NOT NULL" }
t.string :migration_id, limit: 255
t.references :assignment, foreign_key: true
t.references :old_assignment, foreign_key: { to_table: :assignments }
t.timestamp :todo_date
t.bigint :context_id, null: false
t.string :context_type, null: false
t.references :root_account, foreign_key: { to_table: :accounts }
t.timestamp :publish_at
t.references :current_lookup, foreign_key: { to_table: :wiki_page_lookups }
t.timestamp :unlock_at, precision: 6
t.timestamp :lock_at, precision: 6
t.boolean :only_visible_to_overrides, null: false, default: false
t.index [:context_id, :context_type]
t.index [:wiki_id, :todo_date], where: "todo_date IS NOT NULL"
end
create_table :wiki_page_lookups do |t|
t.text :slug, null: false, index: false
t.references :wiki_page, null: false, foreign_key: { deferrable: :deferred, on_delete: :cascade }
t.references :root_account, foreign_key: { to_table: :accounts }, index: false, null: false
t.bigint :context_id, null: false
t.string :context_type, null: false, limit: 255
t.timestamps precision: 6
t.replica_identity_index
t.index %i[context_id context_type slug],
name: "unique_index_on_context_and_slug",
unique: true
end
unless Rails.env.production?
# this user is *not* used in production! it's only used to simulate a read-only secondary database in dev/test
# the user is cluster-wide ...
unless readonly_user_exists?
execute("CREATE USER canvas_readonly_user")
end
quoted_schema = connection.quote_local_table_name(Shard.current.name)
# ... but needs permissions on each shard's schema
execute("GRANT USAGE ON SCHEMA #{quoted_schema} TO canvas_readonly_user")
execute("GRANT SELECT ON ALL TABLES IN SCHEMA #{quoted_schema} TO canvas_readonly_user")
execute("ALTER DEFAULT PRIVILEGES IN SCHEMA #{quoted_schema} GRANT SELECT ON TABLES TO canvas_readonly_user")
end
change_column :schema_migrations, :version, :string, limit: 255
execute(<<~SQL.squish)
CREATE VIEW #{connection.quote_table_name("assignment_student_visibilities")} AS
SELECT DISTINCT a.id as assignment_id,
e.user_id as user_id,
e.course_id as course_id
FROM #{Assignment.quoted_table_name} a
JOIN #{Enrollment.quoted_table_name} e
ON e.course_id = a.context_id
AND a.context_type = 'Course'
AND e.type IN ('StudentEnrollment', 'StudentViewEnrollment')
AND e.workflow_state NOT IN ('deleted', 'rejected', 'inactive')
WHERE a.workflow_state NOT IN ('deleted','unpublished')
AND COALESCE(a.only_visible_to_overrides, 'false') = 'false'
UNION
SELECT DISTINCT a.id as assignment_id,
e.user_id as user_id,
e.course_id as course_id
FROM #{Assignment.quoted_table_name} a
JOIN #{Enrollment.quoted_table_name} e
ON e.course_id = a.context_id
AND a.context_type = 'Course'
AND e.type IN ('StudentEnrollment', 'StudentViewEnrollment')
AND e.workflow_state NOT IN ('deleted', 'rejected', 'inactive')
INNER JOIN #{AssignmentOverride.quoted_table_name} ao
ON a.id = ao.assignment_id
AND ao.set_type = 'ADHOC'
INNER JOIN #{AssignmentOverrideStudent.quoted_table_name} aos
ON ao.id = aos.assignment_override_id
AND aos.user_id = e.user_id
WHERE ao.workflow_state = 'active'
AND aos.workflow_state <> 'deleted'
AND a.workflow_state NOT IN ('deleted','unpublished')
AND a.only_visible_to_overrides = 'true'
UNION
SELECT DISTINCT a.id as assignment_id,
e.user_id as user_id,
e.course_id as course_id
FROM #{Assignment.quoted_table_name} a
JOIN #{Enrollment.quoted_table_name} e
ON e.course_id = a.context_id
AND a.context_type = 'Course'
AND e.type IN ('StudentEnrollment', 'StudentViewEnrollment')
AND e.workflow_state NOT IN ('deleted', 'rejected', 'inactive')
INNER JOIN #{AssignmentOverride.quoted_table_name} ao
ON a.id = ao.assignment_id
AND ao.set_type = 'Group'
INNER JOIN #{Group.quoted_table_name} g
ON g.id = ao.set_id
INNER JOIN #{GroupMembership.quoted_table_name} gm
ON gm.group_id = g.id
AND gm.user_id = e.user_id
WHERE gm.workflow_state <> 'deleted'
AND g.workflow_state <> 'deleted'
AND ao.workflow_state = 'active'
AND a.workflow_state NOT IN ('deleted','unpublished')
AND a.only_visible_to_overrides = 'true'
UNION
SELECT DISTINCT a.id as assignment_id,
e.user_id as user_id,
e.course_id as course_id
FROM #{Assignment.quoted_table_name} a
JOIN #{Enrollment.quoted_table_name} e
ON e.course_id = a.context_id
AND a.context_type = 'Course'
AND e.type IN ('StudentEnrollment', 'StudentViewEnrollment')
AND e.workflow_state NOT IN ('deleted', 'rejected', 'inactive')
INNER JOIN #{AssignmentOverride.quoted_table_name} ao
ON e.course_section_id = ao.set_id
AND ao.set_type = 'CourseSection'
AND ao.assignment_id = a.id
WHERE a.workflow_state NOT IN ('deleted','unpublished')
AND a.only_visible_to_overrides = 'true'
AND ao.workflow_state = 'active'
SQL
execute(<<~SQL.squish)
CREATE VIEW #{connection.quote_table_name("quiz_student_visibilities")} AS
SELECT DISTINCT q.id as quiz_id,
e.user_id as user_id,
e.course_id as course_id
FROM #{Quizzes::Quiz.quoted_table_name} q
JOIN #{Enrollment.quoted_table_name} e
ON e.course_id = q.context_id
AND q.context_type = 'Course'
AND e.type IN ('StudentEnrollment', 'StudentViewEnrollment')
AND e.workflow_state NOT IN ('deleted', 'rejected', 'inactive')
WHERE q.workflow_state NOT IN ('deleted','unpublished')
AND COALESCE(q.only_visible_to_overrides, 'false') = 'false'
UNION
SELECT DISTINCT q.id as quiz_id,
e.user_id as user_id,
e.course_id as course_id
FROM #{Quizzes::Quiz.quoted_table_name} q
JOIN #{Enrollment.quoted_table_name} e
ON e.course_id = q.context_id
AND q.context_type = 'Course'
AND e.type IN ('StudentEnrollment', 'StudentViewEnrollment')
AND e.workflow_state NOT IN ('deleted', 'rejected', 'inactive')
INNER JOIN #{AssignmentOverride.quoted_table_name} ao
ON q.id = ao.quiz_id
AND ao.set_type = 'ADHOC'
INNER JOIN #{AssignmentOverrideStudent.quoted_table_name} aos
ON ao.id = aos.assignment_override_id
AND aos.user_id = e.user_id
WHERE ao.workflow_state = 'active'
AND aos.workflow_state <> 'deleted'
AND q.workflow_state NOT IN ('deleted','unpublished')
AND q.only_visible_to_overrides = 'true'
UNION
SELECT DISTINCT q.id as quiz_id,
e.user_id as user_id,
e.course_id as course_id
FROM #{Quizzes::Quiz.quoted_table_name} q
JOIN #{Enrollment.quoted_table_name} e
ON e.course_id = q.context_id
AND q.context_type = 'Course'
AND e.type IN ('StudentEnrollment', 'StudentViewEnrollment')
AND e.workflow_state NOT IN ('deleted', 'rejected', 'inactive')
INNER JOIN #{AssignmentOverride.quoted_table_name} ao
ON e.course_section_id = ao.set_id
AND ao.set_type = 'CourseSection'
AND ao.quiz_id = q.id
WHERE q.workflow_state NOT IN ('deleted','unpublished')
AND q.only_visible_to_overrides = 'true'
AND ao.workflow_state = 'active'
SQL
execute(MigrationHelpers::StudentVisibilities::StudentVisibilitiesV6.new(connection.quote_table_name("module_student_visibilities"), ContextModule).view_sql)
execute(MigrationHelpers::StudentVisibilities::StudentVisibilitiesV6.new(connection.quote_table_name("assignment_student_visibilities_v2"), Assignment).view_sql)
execute(MigrationHelpers::StudentVisibilities::StudentVisibilitiesV6.new(connection.quote_table_name("quiz_student_visibilities_v2"), Quizzes::Quiz).view_sql)
end
def readonly_user_exists?
!!connection.select_value("SELECT 1 AS one FROM pg_roles WHERE rolname='canvas_readonly_user'")
end
def down
raise ActiveRecord::IrreversibleMigration
end
end
# rubocop:enable Migration/AddIndex, Migration/ChangeColumn, Migration/Execute, Migration/IdColumn
# rubocop:enable Migration/PrimaryKey, Migration/RootAccountId, Rails/CreateTableWithTimestamps
# rubocop:enable Rails/ThreeStateBooleanColumn