diff --git a/app/assets/javascripts/autograder.js b/app/assets/javascripts/autograder.js new file mode 100644 index 000000000..da2dfab6d --- /dev/null +++ b/app/assets/javascripts/autograder.js @@ -0,0 +1,41 @@ +;(function() { + $(document).ready(function () { + function access_key_callback() { + const checked = $(this).prop('checked'); + const $access_key_field = $('#autograder_access_key'); + const $access_key_id_field = $('#autograder_access_key_id'); + $access_key_field.prop('disabled', !checked); + $access_key_id_field.prop('disabled', !checked); + if (!checked) { + $access_key_field.val('', checked); + $access_key_id_field.val('', checked); + } + } + + $('#autograder_use_access_key').on('change', access_key_callback); + access_key_callback.call($('#autograder_use_access_key')); + + function initializeEC2Dropdown() { + if ($.fn.tooltip) { + $('.browser-default[data-tooltip]').tooltip({ + enterDelay: 300, + exitDelay: 200, + position: 'top' + }); + } + + $('#autograder_instance_type option').hover( + function() { $(this).addClass('highlighted-option'); }, + function() { $(this).removeClass('highlighted-option'); } + ); + + $('#autograder_instance_type').on('change.ec2-instance', function() { + const selectedInstance = $(this).val(); + console.log('Selected EC2 instance type:', selectedInstance); + }); + } + + // Initialize the EC2 dropdown functionality + initializeEC2Dropdown(); + }); +})(); \ No newline at end of file diff --git a/app/controllers/autograders_controller.rb b/app/controllers/autograders_controller.rb index a71787f3f..51c27ff86 100755 --- a/app/controllers/autograders_controller.rb +++ b/app/controllers/autograders_controller.rb @@ -16,6 +16,9 @@ def create a.autograde_timeout = 180 a.autograde_image = "autograding_image" a.release_score = true + a.access_key_id = "" + a.access_key = "" + a.instance_type = "t2.micro" end if @autograder.save flash[:success] = "Autograder created." @@ -112,7 +115,8 @@ def set_autograder end def autograder_params - params[:autograder].permit(:autograde_timeout, :autograde_image, :release_score) + params[:autograder].permit(:autograde_timeout, :autograde_image, :release_score, :access_key, + :access_key_id, :instance_type) end def assessment_params diff --git a/app/helpers/assessment_autograde_core.rb b/app/helpers/assessment_autograde_core.rb index 41c100ac8..ac90d2a9b 100644 --- a/app/helpers/assessment_autograde_core.rb +++ b/app/helpers/assessment_autograde_core.rb @@ -166,7 +166,21 @@ def tango_add_job(course, assessment, upload_file_list, callback_url, job_name, "timeout" => @autograde_prop.autograde_timeout, "callback_url" => callback_url, "jobName" => job_name, - "disable_network" => assessment.disable_network }.to_json + "disable_network" => assessment.disable_network} + if Rails.configuration.x.ec2_ssh.present? + job_properties["ec2Vmms"] = true + if @autograde_prop.use_access_key? + job_properties["accessKey"] = @autograde_prop.access_key + job_properties["accessKeyId"] = @autograde_prop.access_key_id + else + job_properties["accessKey"] = "" + job_properties["accessKeyId"] = "" + end + job_properties["instanceType"] = @autograde_prop.instance_type + end + + job_properties = job_properties.to_json + begin response = TangoClient.addjob("#{course.name}-#{assessment.name}", job_properties) rescue TangoClient::TangoException => e diff --git a/app/views/autograders/_basic_settings.html.erb b/app/views/autograders/_basic_settings.html.erb new file mode 100644 index 000000000..0ee1ac71a --- /dev/null +++ b/app/views/autograders/_basic_settings.html.erb @@ -0,0 +1,36 @@ +<%= f.text_field :autograde_image, display_name: "VM Image", + help_text: "VM image for autograding (e.g. rhel.img). #{link_to 'Click here', tango_status_course_jobs_path} to view the list of VM images and pools currently being used".html_safe + + (Rails.configuration.x.docker_image_upload_enabled.presence ? ", or #{link_to 'click here', course_dockers_path} to upload a new docker image.".html_safe : "."), + required: true, maxlength: 64 %> + +<%= f.number_field :autograde_timeout, display_name: "Timeout", + help_text: "Timeout for autograding jobs (secs). Must be between 10s and 900s, inclusive.", min: 10, max: 900 %> +<%= f.check_box :release_score, + display_name: "Release Scores?", + help_text: "Check to release autograded scores to students immediately after autograding (strongly recommended)." %> + +<% help_tar_text = "Tar file exists, upload a file to override." %> +<% help_makefile_text = "Makefile exists, upload a file to override." %> +<%= f.file_field :makefile, label_text: "Autograder Makefile", action: :upload, file_exists_text: help_makefile_text, class: "form-file-field", file_exists: @makefile_exists %> +<%= f.file_field :tar, label_text: "Autograder Tar", action: :upload, file_exists_text: help_tar_text, class: "form-file-field", file_exists: @tar_exists %> +

+ Both of the above files will be renamed upon upload. +

+ +<%= f.fields_for :assessment, @assessment do |af| %> + <%= af.check_box :disable_network, help_text: "Disable network access for autograding containers." %> +<% end %> + +<%= f.submit "Save Settings" %> + +<%= link_to "Delete Autograder", course_assessment_autograder_path(@course, @assessment), + method: :delete, class: "btn danger", + data: { confirm: "Are you sure you want to delete the Autograder for this assesssment?" } %> + +<% unless @makefile_exists.nil? %> + <%= link_to "Download Makefile", download_file_course_assessment_autograder_path(file_path: @makefile_exists, file_key: 'makefile'), class: "btn" %> +<% end %> + +<% unless @tar_exists.nil? %> + <%= link_to "Download Tar", download_file_course_assessment_autograder_path(file_path: @tar_exists, file_key: 'tar'), class: "btn" %> +<% end %> diff --git a/app/views/autograders/_ec2_settings.html.erb b/app/views/autograders/_ec2_settings.html.erb new file mode 100755 index 000000000..cfb6bf6f0 --- /dev/null +++ b/app/views/autograders/_ec2_settings.html.erb @@ -0,0 +1,84 @@ +<% content_for :javascripts do %> + <%= javascript_include_tag "autograder" %> +<% end %> + +

EC2 Settings

+<%= f.check_box :use_access_key, + display_name: "Enable Access Key", + help_text: "(Optional) Use your own provided access key to authenticate to different EC2 instances than the default one on Tango" %> +<%= f.text_field :access_key, display_name: "Access Key" %> +<%= f.text_field :access_key_id, display_name: "Access Key ID" %> + +<% + # Group EC2 instance options by category for better organization + ec2_instance_options = [ + # General Purpose - T2 (burstable) + ['T2 - General Purpose (Burstable)', [ + ['t2.nano - 1 vCPU, 0.5 GiB RAM (minimal, lowest cost)', 't2.nano'], + ['t2.micro - 1 vCPU, 1 GiB RAM (lowest cost, suitable for small jobs)', 't2.micro'], + ['t2.small - 1 vCPU, 2 GiB RAM (low cost, better for memory-intensive tasks)', 't2.small'], + ['t2.medium - 2 vCPU, 4 GiB RAM (balanced, good for most autograding tasks)', 't2.medium'], + ['t2.large - 2 vCPU, 8 GiB RAM (better for memory-heavy workloads)', 't2.large'], + ['t2.xlarge - 4 vCPU, 16 GiB RAM (good for parallel processing)', 't2.xlarge'], + ['t2.2xlarge - 8 vCPU, 32 GiB RAM (high performance, burstable)', 't2.2xlarge'] + ]], + # General Purpose - T3 (newer generation) + ['T3 - General Purpose (Newer Generation)', [ + ['t3.micro - 2 vCPU, 1 GiB RAM (better performance than t2.micro)', 't3.micro'], + ['t3.small - 2 vCPU, 2 GiB RAM (improved over t2.small)', 't3.small'], + ['t3.medium - 2 vCPU, 4 GiB RAM (better CPU performance than t2)', 't3.medium'], + ['t3.large - 2 vCPU, 8 GiB RAM (improved networking)', 't3.large'] + ]], + # Compute Optimized + ['C - Compute Optimized', [ + ['c5.large - 2 vCPU, 4 GiB RAM (compute-optimized, faster CPU)', 'c5.large'], + ['c5.xlarge - 4 vCPU, 8 GiB RAM (high compute performance)', 'c5.xlarge'], + ['c5.2xlarge - 8 vCPU, 16 GiB RAM (very high compute performance)', 'c5.2xlarge'] + ]], + # Memory Optimized + ['R - Memory Optimized', [ + ['r5.large - 2 vCPU, 16 GiB RAM (memory-optimized, for large datasets)', 'r5.large'], + ['r5.xlarge - 4 vCPU, 32 GiB RAM (high memory capacity)', 'r5.xlarge'], + ['r5.2xlarge - 8 vCPU, 64 GiB RAM (very high memory capacity)', 'r5.2xlarge'] + ]] + ] +%> + +
EC2 Instance Type
+ +<%= f.select :instance_type, + grouped_options_for_select(ec2_instance_options, @autograder.instance_type), + { include_blank: false, label: "EC2 Instance Type" }, + { class: 'browser-default', + data: { tooltip: "Select an EC2 instance type based on your autograding needs. Larger instances cost more but run faster." }, + display_name: "EC2 Instance Type" } %> +
+ Choose an instance type based on your autograding requirements: + +
+ Recommendations: + +
+ Note: Larger instances incur higher AWS costs. View EC2 pricing +
+ +<%= f.submit "Save Settings" %> + +<%= link_to "Delete Autograder", course_assessment_autograder_path(@course, @assessment), + method: :delete, class: "btn danger", + data: { confirm: "Are you sure you want to delete the Autograder for this assesssment?" } %> diff --git a/app/views/autograders/_form.html.erb b/app/views/autograders/_form.html.erb index 1a7c985e1..07e2f1e3d 100755 --- a/app/views/autograders/_form.html.erb +++ b/app/views/autograders/_form.html.erb @@ -1,40 +1,34 @@ -<%= form_for @autograder, url: course_assessment_autograder_path(@course, @assessment, @autograder), - builder: FormBuilderWithDateTimeInput, - html: { multipart: true } do |f| %> - <%= f.text_field :autograde_image, display_name: "VM Image", - help_text: "VM image for autograding (e.g. rhel.img). #{link_to 'Click here', tango_status_course_jobs_path} to view the list of VM images and pools currently being used".html_safe + - (Rails.configuration.x.docker_image_upload_enabled.presence ? ", or #{link_to 'click here', course_dockers_path} to upload a new docker image.".html_safe : "."), - required: true, maxlength: 64 %> +
+
+ +
+
- <%= f.number_field :autograde_timeout, display_name: "Timeout", - help_text: "Timeout for autograding jobs (secs). Must be between 10s and 900s, inclusive.", min: 10, max: 900 %> - <%= f.check_box :release_score, - display_name: "Release Scores?", - help_text: "Check to release autograded scores to students immediately after autograding (strongly recommended)." %> - - <% help_tar_text = "Tar file exists, upload a file to override." %> - <% help_makefile_text = "Makefile exists, upload a file to override." %> - <%= f.file_field :makefile, label_text: "Autograder Makefile", action: :upload, file_exists_text: help_makefile_text, class: "form-file-field", file_exists: @makefile_exists %> - <%= f.file_field :tar, label_text: "Autograder Tar", action: :upload, file_exists_text: help_tar_text, class: "form-file-field", file_exists: @tar_exists %> -

- Both of the above files will be renamed upon upload. -

- - <%= f.fields_for :assessment, @assessment do |af| %> - <%= af.check_box :disable_network, help_text: "Disable network access for autograding containers." %> - <% end %> - - <%= f.submit "Save Settings" %> - - <%= link_to "Delete Autograder", course_assessment_autograder_path(@course, @assessment), - method: :delete, class: "btn danger", - data: { confirm: "Are you sure you want to delete the Autograder for this assesssment?" } %> - - <% unless @makefile_exists.nil? %> - <%= link_to "Download Makefile", download_file_course_assessment_autograder_path(file_path: @makefile_exists, file_key: 'makefile'), class: "btn" %> - <% end %> - - <% unless @tar_exists.nil? %> - <%= link_to "Download Tar", download_file_course_assessment_autograder_path(file_path: @tar_exists, file_key: 'tar'), class: "btn" %> - <% end %> -<% end %> +
+
+ <%= form_for @autograder, url: course_assessment_autograder_path(@course, @assessment, @autograder), + builder: FormBuilderWithDateTimeInput, + html: { multipart: true } do |f| %> +
+ <%= render "basic_settings", f: %> +
+ <% if Rails.configuration.x.ec2_ssh.presence %> +
+ <%= render "ec2_settings", f: %> +
+ <% end %> + <% end %> +
+
diff --git a/config/environments/development.rb b/config/environments/development.rb index 33ab24e31..f38aed721 100755 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -74,6 +74,9 @@ # Feature flag for docker image upload config.x.docker_image_upload_enabled = true + # Feature flag for EC2 autograder + config.x.ec2_ssh = true + # Uncomment if you wish to allow Action Cable access from any origin. # config.action_cable.disable_request_forgery_protection = true diff --git a/config/environments/production.rb.template b/config/environments/production.rb.template index 0d624c445..25d342ee3 100755 --- a/config/environments/production.rb.template +++ b/config/environments/production.rb.template @@ -88,6 +88,9 @@ Autolab3::Application.configure do # Feature flag for docker image upload config.x.docker_image_upload_enabled = false + # Feature flag for EC2 autograder + config.x.ec2_ssh = false + # ID for Heap Analytics config.x.analytics_id = nil diff --git a/db/migrate/20241205233214_add_ec2_ssh_fields_to_autograders.rb b/db/migrate/20241205233214_add_ec2_ssh_fields_to_autograders.rb new file mode 100644 index 000000000..2f6052824 --- /dev/null +++ b/db/migrate/20241205233214_add_ec2_ssh_fields_to_autograders.rb @@ -0,0 +1,7 @@ +class AddEc2SshFieldsToAutograders < ActiveRecord::Migration[6.1] + def change + add_column :autograders, :instance_type, :string, default: "" + add_column :autograders, :access_key, :string, default: "" + add_column :autograders, :access_key_id, :string, default: "" + end +end diff --git a/db/migrate/20241211042124_add_use_access_key_to_autograder.rb b/db/migrate/20241211042124_add_use_access_key_to_autograder.rb new file mode 100644 index 000000000..8d84eaf2d --- /dev/null +++ b/db/migrate/20241211042124_add_use_access_key_to_autograder.rb @@ -0,0 +1,5 @@ +class AddUseAccessKeyToAutograder < ActiveRecord::Migration[6.1] + def change + add_column :autograders, :use_access_key, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index b212ea0a3..659fad556 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,9 +10,9 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2024_04_06_174050) do +ActiveRecord::Schema.define(version: 2024_12_11_042124) do - create_table "active_storage_attachments", force: :cascade do |t| + create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false t.bigint "record_id", null: false @@ -22,7 +22,7 @@ t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true end - create_table "active_storage_blobs", force: :cascade do |t| + create_table "active_storage_blobs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "key", null: false t.string "filename", null: false t.string "content_type" @@ -34,13 +34,13 @@ t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true end - create_table "active_storage_variant_records", force: :cascade do |t| + create_table "active_storage_variant_records", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "blob_id", null: false t.string "variation_digest", null: false t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true end - create_table "annotations", force: :cascade do |t| + create_table "annotations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "submission_id" t.string "filename" t.integer "position" @@ -56,11 +56,11 @@ t.boolean "global_comment", default: false end - create_table "announcements", force: :cascade do |t| + create_table "announcements", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "title" t.text "description" - t.datetime "start_date" - t.datetime "end_date" + t.timestamp "start_date" + t.timestamp "end_date" t.integer "course_user_datum_id" t.integer "course_id" t.datetime "created_at" @@ -69,7 +69,7 @@ t.boolean "system", default: false, null: false end - create_table "assessment_user_data", force: :cascade do |t| + create_table "assessment_user_data", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "course_user_datum_id", null: false t.integer "assessment_id", null: false t.integer "latest_submission_id" @@ -85,10 +85,10 @@ t.index ["latest_submission_id"], name: "index_assessment_user_data_on_latest_submission_id", unique: true end - create_table "assessments", force: :cascade do |t| - t.datetime "due_at" - t.datetime "end_at" - t.datetime "start_at" + create_table "assessments", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.timestamp "due_at" + t.timestamp "end_at" + t.timestamp "start_at" t.string "name" t.text "description" t.datetime "created_at" @@ -121,7 +121,7 @@ t.boolean "disable_network", default: false end - create_table "attachments", force: :cascade do |t| + create_table "attachments", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "filename" t.string "mime_type" t.string "name" @@ -136,7 +136,7 @@ t.index ["slug"], name: "index_attachments_on_slug", unique: true end - create_table "authentications", force: :cascade do |t| + create_table "authentications", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "provider", null: false t.string "uid", null: false t.integer "user_id" @@ -144,14 +144,18 @@ t.datetime "updated_at" end - create_table "autograders", force: :cascade do |t| + create_table "autograders", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "assessment_id" t.integer "autograde_timeout" t.string "autograde_image" t.boolean "release_score" + t.string "instance_type", default: "" + t.string "access_key", default: "" + t.string "access_key_id", default: "" + t.boolean "use_access_key", default: false end - create_table "course_user_data", force: :cascade do |t| + create_table "course_user_data", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "lecture" t.string "section", default: "" t.string "grade_policy", default: "" @@ -167,7 +171,7 @@ t.string "course_number", default: "" end - create_table "courses", force: :cascade do |t| + create_table "courses", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.string "semester" t.integer "late_slack" @@ -187,14 +191,14 @@ t.boolean "disable_on_end", default: false end - create_table "extensions", force: :cascade do |t| + create_table "extensions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "course_user_datum_id" t.integer "assessment_id" t.integer "days" t.boolean "infinite", default: false, null: false end - create_table "friendly_id_slugs", charset: "utf8mb3", force: :cascade do |t| + create_table "friendly_id_slugs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "slug", null: false t.integer "sluggable_id", null: false t.string "sluggable_type", limit: 50 @@ -205,7 +209,7 @@ t.index ["sluggable_type", "sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_type_and_sluggable_id" end - create_table "github_integrations", force: :cascade do |t| + create_table "github_integrations", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "oauth_state" t.text "access_token_ciphertext" t.integer "user_id" @@ -215,37 +219,37 @@ t.index ["user_id"], name: "index_github_integrations_on_user_id", unique: true end - create_table "groups", force: :cascade do |t| + create_table "groups", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false end - create_table "lti_course_data", force: :cascade do |t| + create_table "lti_course_data", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "context_id" t.integer "course_id" t.datetime "last_synced" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false t.string "membership_url" t.string "platform" t.boolean "auto_sync", default: false t.boolean "drop_missing_students", default: false end - create_table "module_data", force: :cascade do |t| + create_table "module_data", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "field_id" t.integer "data_id" t.binary "data" end - create_table "module_fields", force: :cascade do |t| + create_table "module_fields", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "user_module_id" t.string "name" t.string "data_type" end - create_table "oauth_access_grants", force: :cascade do |t| + create_table "oauth_access_grants", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "resource_owner_id", null: false t.integer "application_id", null: false t.string "token", null: false @@ -254,10 +258,11 @@ t.datetime "created_at", null: false t.datetime "revoked_at" t.string "scopes" + t.index ["application_id"], name: "fk_rails_b4b53e07b8" t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true end - create_table "oauth_access_tokens", force: :cascade do |t| + create_table "oauth_access_tokens", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "resource_owner_id" t.integer "application_id" t.string "token", null: false @@ -267,12 +272,13 @@ t.datetime "created_at", null: false t.string "scopes" t.string "previous_refresh_token", default: "", null: false + t.index ["application_id"], name: "fk_rails_732cb83ab7" t.index ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true t.index ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id" t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true end - create_table "oauth_applications", force: :cascade do |t| + create_table "oauth_applications", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name", null: false t.string "uid", null: false t.string "secret", null: false @@ -284,7 +290,7 @@ t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true end - create_table "oauth_device_flow_requests", force: :cascade do |t| + create_table "oauth_device_flow_requests", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "application_id", null: false t.string "scopes", default: "", null: false t.string "device_code", null: false @@ -294,11 +300,12 @@ t.datetime "resolved_at" t.integer "resource_owner_id" t.string "access_code" + t.index ["application_id"], name: "fk_rails_4035c6e0ed" t.index ["device_code"], name: "index_oauth_device_flow_requests_on_device_code", unique: true t.index ["user_code"], name: "index_oauth_device_flow_requests_on_user_code", unique: true end - create_table "problems", force: :cascade do |t| + create_table "problems", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.text "description" t.integer "assessment_id" @@ -310,7 +317,7 @@ t.index ["assessment_id", "name"], name: "problem_uniq", unique: true end - create_table "risk_conditions", force: :cascade do |t| + create_table "risk_conditions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "condition_type" t.text "parameters" t.integer "version" @@ -319,9 +326,9 @@ t.integer "course_id" end - create_table "scheduler", force: :cascade do |t| + create_table "scheduler", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "action" - t.datetime "next" + t.timestamp "next" t.integer "interval" t.integer "course_id" t.datetime "created_at" @@ -330,20 +337,20 @@ t.boolean "disabled", default: false end - create_table "score_adjustments", force: :cascade do |t| + create_table "score_adjustments", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "kind", null: false t.float "value", null: false t.string "type", default: "Tweak", null: false end - create_table "scoreboards", force: :cascade do |t| + create_table "scoreboards", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "assessment_id" t.text "banner" t.text "colspec" t.boolean "include_instructors", default: false end - create_table "scores", force: :cascade do |t| + create_table "scores", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "submission_id" t.float "score" t.text "feedback", size: :medium @@ -356,7 +363,7 @@ t.index ["submission_id"], name: "index_scores_on_submission_id" end - create_table "submissions", force: :cascade do |t| + create_table "submissions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "version" t.integer "course_user_datum_id" t.integer "assessment_id" @@ -382,7 +389,7 @@ t.index ["course_user_datum_id"], name: "index_submissions_on_course_user_datum_id" end - create_table "users", force: :cascade do |t| + create_table "users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "email", default: "", null: false t.string "first_name", default: "", null: false t.string "last_name", default: "", null: false @@ -411,20 +418,20 @@ t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end - create_table "watchlist_configurations", force: :cascade do |t| + create_table "watchlist_configurations", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.json "category_blocklist" t.json "assessment_blocklist" - t.integer "course_id" + t.bigint "course_id" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.boolean "allow_ca", default: false t.index ["course_id"], name: "index_watchlist_configurations_on_course_id" end - create_table "watchlist_instances", force: :cascade do |t| - t.integer "course_user_datum_id" - t.integer "course_id" - t.integer "risk_condition_id" + create_table "watchlist_instances", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.bigint "course_user_datum_id" + t.bigint "course_id" + t.bigint "risk_condition_id" t.integer "status", default: 0 t.boolean "archived", default: false t.datetime "created_at", null: false @@ -437,4 +444,8 @@ add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" + add_foreign_key "github_integrations", "users" + add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id" + add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id" + add_foreign_key "oauth_device_flow_requests", "oauth_applications", column: "application_id" end