# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[8.0].define(version: 2025_07_13_114125) do
  # These are extensions that must be enabled in order to support this database
  enable_extension "pg_trgm"
  enable_extension "pgcrypto"
  enable_extension "plpgsql"

  create_table "anomaly_notifications", id: :serial, force: :cascade do |t|
    t.integer "contributor_id", null: false
    t.string "contributor_type", null: false
    t.integer "exercise_id", null: false
    t.integer "exercise_collection_id", null: false
    t.jsonb "reason"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.index ["contributor_type", "contributor_id"], name: "index_anomaly_notifications_on_contributor"
    t.index ["exercise_collection_id"], name: "index_anomaly_notifications_on_exercise_collection_id"
    t.index ["exercise_id"], name: "index_anomaly_notifications_on_exercise_id"
  end

  create_table "authentication_tokens", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
    t.string "shared_secret", null: false
    t.string "user_type", null: false
    t.bigint "user_id", null: false
    t.datetime "expire_at", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.bigint "study_group_id"
    t.index ["shared_secret"], name: "index_authentication_tokens_on_shared_secret", unique: true
    t.index ["study_group_id"], name: "index_authentication_tokens_on_study_group_id"
    t.index ["user_type", "user_id"], name: "index_authentication_tokens_on_user"
  end

  create_table "codeharbor_links", id: :serial, force: :cascade do |t|
    t.string "api_key"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer "user_id"
    t.string "push_url"
    t.string "check_uuid_url"
    t.string "user_type"
    t.index ["user_type", "user_id"], name: "index_codeharbor_links_on_user_type_and_user_id"
  end

  create_table "comments", id: :serial, force: :cascade do |t|
    t.integer "user_id"
    t.integer "file_id"
    t.string "user_type"
    t.integer "row"
    t.integer "column"
    t.text "text"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.index ["file_id"], name: "index_comments_on_file_id"
    t.index ["user_id"], name: "index_comments_on_user_id"
  end

  create_table "community_solution_contributions", force: :cascade do |t|
    t.bigint "community_solution_id", null: false
    t.bigint "study_group_id"
    t.string "user_type", null: false
    t.bigint "user_id", null: false
    t.bigint "community_solution_lock_id", null: false
    t.boolean "proposed_changes", null: false
    t.boolean "timely_contribution", null: false
    t.boolean "autosave", null: false
    t.interval "working_time", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["community_solution_id", "timely_contribution", "autosave", "proposed_changes"], name: "index_community_solution_valid_contributions"
    t.index ["community_solution_lock_id"], name: "index_community_solution_contributions_lock"
    t.index ["user_type", "user_id"], name: "index_community_solution_contributions_on_user"
  end

  create_table "community_solution_locks", force: :cascade do |t|
    t.bigint "community_solution_id", null: false
    t.string "user_type", null: false
    t.bigint "user_id", null: false
    t.datetime "locked_until"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["community_solution_id", "locked_until"], name: "index_community_solution_locks_until", unique: true
    t.index ["user_type", "user_id"], name: "index_community_solution_locks_on_user"
  end

  create_table "community_solutions", force: :cascade do |t|
    t.bigint "exercise_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["exercise_id"], name: "index_community_solutions_on_exercise_id"
  end

  create_table "consumers", id: :serial, force: :cascade do |t|
    t.string "name"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "oauth_key"
    t.string "oauth_secret"
    t.integer "rfc_visibility", limit: 2, default: 0, null: false, comment: "Used as enum in Rails"
  end

  create_table "error_template_attributes", id: :serial, force: :cascade do |t|
    t.string "key"
    t.string "regex"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.text "description"
    t.boolean "important"
  end

  create_table "error_template_attributes_templates", id: false, force: :cascade do |t|
    t.integer "error_template_id", null: false
    t.integer "error_template_attribute_id", null: false
  end

  create_table "error_templates", id: :serial, force: :cascade do |t|
    t.integer "execution_environment_id"
    t.string "name"
    t.string "signature"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.text "description"
    t.text "hint"
  end

  create_table "events", id: :serial, force: :cascade do |t|
    t.string "category"
    t.string "data"
    t.integer "user_id"
    t.string "user_type"
    t.integer "exercise_id"
    t.integer "file_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.bigint "programming_group_id"
    t.bigint "study_group_id"
    t.index ["programming_group_id"], name: "index_events_on_programming_group_id"
    t.index ["study_group_id"], name: "index_events_on_study_group_id"
  end

  create_table "events_synchronized_editor", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
    t.bigint "programming_group_id", null: false
    t.bigint "study_group_id", null: false
    t.string "user_type", null: false
    t.bigint "user_id", null: false
    t.integer "action", limit: 2, default: 0, null: false, comment: "Used as enum in Rails"
    t.integer "status", limit: 2, comment: "Used as enum in Rails"
    t.bigint "file_id"
    t.integer "editor_action", limit: 2, comment: "Used as enum in Rails"
    t.integer "range_start_row"
    t.integer "range_start_column"
    t.integer "range_end_row"
    t.integer "range_end_column"
    t.text "lines", array: true
    t.jsonb "data"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.uuid "session_id", null: false
    t.index ["file_id"], name: "index_events_synchronized_editor_on_file_id"
    t.index ["programming_group_id"], name: "index_events_synchronized_editor_on_programming_group_id"
    t.index ["study_group_id"], name: "index_events_synchronized_editor_on_study_group_id"
    t.index ["user_type", "user_id"], name: "index_events_synchronized_editor_on_user"
  end

  create_table "execution_environments", id: :serial, force: :cascade do |t|
    t.string "docker_image"
    t.string "name"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "run_command"
    t.string "test_command"
    t.string "testing_framework"
    t.text "help"
    t.integer "permitted_execution_time"
    t.integer "user_id"
    t.string "user_type"
    t.integer "pool_size"
    t.integer "file_type_id"
    t.integer "memory_limit"
    t.boolean "network_enabled"
    t.integer "cpu_limit", default: 20, null: false
    t.integer "exposed_ports", default: [], array: true
    t.boolean "privileged_execution", default: false, null: false
  end

  create_table "exercise_collection_items", id: :serial, force: :cascade do |t|
    t.integer "exercise_collection_id"
    t.integer "exercise_id"
    t.integer "position", default: 0, null: false
    t.index ["exercise_collection_id"], name: "index_exercise_collection_items_on_exercise_collection_id"
    t.index ["exercise_id"], name: "index_exercise_collection_items_on_exercise_id"
  end

  create_table "exercise_collections", id: :serial, force: :cascade do |t|
    t.string "name"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.boolean "use_anomaly_detection", default: false
    t.integer "user_id"
    t.string "user_type"
    t.index ["user_type", "user_id"], name: "index_exercise_collections_on_user_type_and_user_id"
  end

  create_table "exercise_tags", id: :serial, force: :cascade do |t|
    t.integer "exercise_id"
    t.integer "tag_id"
    t.integer "factor", default: 1
  end

  create_table "exercise_tips", force: :cascade do |t|
    t.bigint "exercise_id", null: false
    t.bigint "tip_id", null: false
    t.integer "rank", null: false
    t.bigint "parent_exercise_tip_id"
    t.index ["exercise_id", "rank"], name: "index_exercise_tips_on_exercise_id_and_rank"
    t.index ["exercise_id"], name: "index_exercise_tips_on_exercise_id"
    t.index ["parent_exercise_tip_id"], name: "index_exercise_tips_on_parent_exercise_tip_id"
    t.index ["tip_id"], name: "index_exercise_tips_on_tip_id"
  end

  create_table "exercises", id: :serial, force: :cascade do |t|
    t.text "description"
    t.integer "execution_environment_id"
    t.string "title"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer "user_id"
    t.text "instructions"
    t.boolean "public"
    t.string "user_type"
    t.string "token"
    t.boolean "hide_file_tree"
    t.boolean "allow_file_creation"
    t.boolean "allow_auto_completion", default: false
    t.integer "expected_difficulty", default: 1
    t.uuid "uuid"
    t.boolean "unpublished", default: false
    t.datetime "submission_deadline"
    t.datetime "late_submission_deadline"
    t.string "internal_title"
    t.index ["id"], name: "index_exercises_on_id"
    t.index ["id"], name: "index_unpublished_exercises", where: "(NOT unpublished)"
    t.index ["internal_title"], name: "index_exercises_on_internal_title", opclass: :gin_trgm_ops, using: :gin
    t.index ["title"], name: "index_exercises_on_title", opclass: :gin_trgm_ops, using: :gin
  end

  create_table "exercises_proxy_exercises", id: false, force: :cascade do |t|
    t.integer "proxy_exercise_id"
    t.integer "exercise_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.index ["exercise_id"], name: "index_exercises_proxy_exercises_on_exercise_id"
    t.index ["proxy_exercise_id"], name: "index_exercises_proxy_exercises_on_proxy_exercise_id"
  end

  create_table "external_users", id: :serial, force: :cascade do |t|
    t.integer "consumer_id"
    t.string "email"
    t.string "external_id", null: false
    t.string "name"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.boolean "platform_admin", default: false, null: false
    t.string "webauthn_user_id"
    t.index ["external_id", "consumer_id"], name: "index_external_users_on_external_id_and_consumer_id", unique: true
  end

  create_table "file_templates", id: :serial, force: :cascade do |t|
    t.string "name"
    t.text "content"
    t.integer "file_type_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "file_types", id: :serial, force: :cascade do |t|
    t.string "editor_mode"
    t.string "file_extension"
    t.integer "indent_size"
    t.string "name"
    t.integer "user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.boolean "executable"
    t.boolean "renderable"
    t.string "user_type"
    t.boolean "binary"
  end

  create_table "files", id: :serial, force: :cascade do |t|
    t.text "content"
    t.integer "context_id"
    t.string "context_type"
    t.integer "file_id"
    t.integer "file_type_id"
    t.boolean "hidden"
    t.string "name"
    t.boolean "read_only"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "native_file"
    t.string "role"
    t.string "hashed_content"
    t.string "feedback_message"
    t.float "weight"
    t.string "path"
    t.integer "file_template_id"
    t.boolean "hidden_feedback", default: false, null: false
    t.string "xml_id_path", default: [], array: true
    t.index ["context_id", "context_type"], name: "index_files_on_context_id_and_context_type"
  end

  create_table "internal_users", id: :serial, force: :cascade do |t|
    t.integer "consumer_id"
    t.string "email"
    t.string "name"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "crypted_password"
    t.string "salt"
    t.integer "failed_logins_count", default: 0
    t.datetime "lock_expires_at"
    t.string "unlock_token"
    t.string "remember_me_token"
    t.datetime "remember_me_token_expires_at"
    t.string "reset_password_token"
    t.datetime "reset_password_token_expires_at"
    t.datetime "reset_password_email_sent_at"
    t.string "activation_state"
    t.string "activation_token"
    t.datetime "activation_token_expires_at"
    t.boolean "platform_admin", default: false, null: false
    t.string "webauthn_user_id"
    t.index ["activation_token"], name: "index_internal_users_on_activation_token"
    t.index ["email"], name: "index_internal_users_on_email", unique: true
    t.index ["remember_me_token"], name: "index_internal_users_on_remember_me_token"
    t.index ["reset_password_token"], name: "index_internal_users_on_reset_password_token"
  end

  create_table "interventions", id: :serial, force: :cascade do |t|
    t.string "name"
    t.text "markup"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "linter_check_runs", force: :cascade do |t|
    t.bigint "linter_check_id", null: false
    t.string "scope"
    t.integer "line"
    t.text "result"
    t.bigint "testrun_id", null: false
    t.bigint "file_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["file_id"], name: "index_linter_check_runs_on_file_id"
    t.index ["linter_check_id"], name: "index_linter_check_runs_on_linter_check_id"
    t.index ["testrun_id"], name: "index_linter_check_runs_on_testrun_id"
  end

  create_table "linter_checks", force: :cascade do |t|
    t.string "name", null: false
    t.string "code", null: false
    t.string "severity"
  end

  create_table "lti_parameters", force: :cascade do |t|
    t.integer "external_user_id", null: false
    t.integer "exercise_id", null: false
    t.jsonb "lti_parameters", default: {}, null: false
    t.datetime "created_at"
    t.datetime "updated_at"
    t.bigint "study_group_id"
    t.index ["external_user_id", "study_group_id", "exercise_id"], name: "index_lti_params_on_external_user_and_study_group_and_exercise", unique: true
    t.index ["external_user_id"], name: "index_lti_parameters_on_external_user_id"
    t.index ["study_group_id"], name: "index_lti_parameters_on_study_group_id"
  end

  create_table "pair_programming_exercise_feedbacks", force: :cascade do |t|
    t.bigint "exercise_id", null: false
    t.bigint "submission_id", null: false
    t.string "user_type", null: false
    t.bigint "user_id", null: false
    t.bigint "programming_group_id"
    t.bigint "study_group_id", null: false
    t.integer "difficulty"
    t.integer "user_estimated_worktime"
    t.integer "reason_work_alone"
    t.float "normalized_score"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["exercise_id"], name: "index_pair_programming_exercise_feedbacks_on_exercise_id"
    t.index ["programming_group_id"], name: "pp_feedback_programming_group"
    t.index ["study_group_id"], name: "index_pair_programming_exercise_feedbacks_on_study_group_id"
    t.index ["submission_id"], name: "index_pair_programming_exercise_feedbacks_on_submission_id"
    t.index ["user_type", "user_id"], name: "index_pair_programming_exercise_feedbacks_on_user"
  end

  create_table "pair_programming_waiting_users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
    t.string "user_type", null: false
    t.bigint "user_id", null: false
    t.bigint "exercise_id", null: false
    t.bigint "programming_group_id"
    t.integer "status", limit: 2, null: false, comment: "Used as enum in Rails"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["exercise_id"], name: "index_pair_programming_waiting_users_on_exercise_id"
    t.index ["programming_group_id"], name: "index_pair_programming_waiting_users_on_programming_group_id"
    t.index ["user_type", "user_id"], name: "index_pair_programming_waiting_users_on_user"
  end

  create_table "programming_group_memberships", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
    t.bigint "programming_group_id", null: false
    t.string "user_type", null: false
    t.bigint "user_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["programming_group_id"], name: "index_programming_group_memberships_on_programming_group_id"
    t.index ["user_type", "user_id"], name: "index_programming_group_memberships_on_user"
  end

  create_table "programming_groups", force: :cascade do |t|
    t.bigint "exercise_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["exercise_id"], name: "index_programming_groups_on_exercise_id"
  end

  create_table "proxy_exercises", id: :serial, force: :cascade do |t|
    t.string "title"
    t.string "description"
    t.string "token"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "user_type"
    t.bigint "user_id"
    t.boolean "public", default: false, null: false
    t.integer "algorithm", limit: 2, default: 0, null: false, comment: "Used as enum in Rails"
    t.index ["user_type", "user_id"], name: "index_proxy_exercises_on_user_type_and_user_id"
  end

  create_table "remote_evaluation_mappings", id: :serial, force: :cascade do |t|
    t.integer "user_id", null: false
    t.integer "exercise_id", null: false
    t.string "validation_token", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "user_type"
    t.bigint "study_group_id"
    t.bigint "programming_group_id"
    t.index ["programming_group_id"], name: "index_remote_evaluation_mappings_on_programming_group_id"
    t.index ["study_group_id"], name: "index_remote_evaluation_mappings_on_study_group_id"
  end

  create_table "request_for_comments", id: :serial, force: :cascade do |t|
    t.integer "user_id", null: false
    t.integer "exercise_id", null: false
    t.integer "file_id", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "user_type"
    t.text "question"
    t.boolean "solved", default: false
    t.integer "submission_id"
    t.text "thank_you_note"
    t.boolean "full_score_reached", default: false
    t.integer "times_featured", default: 0
    t.index ["exercise_id", "created_at"], name: "index_unresolved_recommended_rfcs", where: "(((NOT solved) OR (solved IS NULL)) AND ((question IS NOT NULL) AND (question <> ''::text)))"
    t.index ["exercise_id"], name: "index_request_for_comments_on_exercise_id"
    t.index ["submission_id"], name: "index_request_for_comments_on_submission_id"
    t.index ["user_id", "user_type", "created_at"], name: "index_rfc_on_user_and_created_at", order: { created_at: :desc }
  end

  create_table "runners", force: :cascade do |t|
    t.string "runner_id", null: false
    t.bigint "execution_environment_id", null: false
    t.string "contributor_type", null: false
    t.bigint "contributor_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "reserved_until"
    t.index ["contributor_type", "contributor_id"], name: "index_runners_on_user"
    t.index ["execution_environment_id", "contributor_id", "contributor_type"], name: "idx_on_execution_environment_id_contributor_id_cont_74611ea4a2", unique: true
    t.index ["execution_environment_id"], name: "index_runners_on_execution_environment_id"
    t.index ["runner_id"], name: "index_runners_on_runner_id", unique: true
  end

  create_table "searches", id: :serial, force: :cascade do |t|
    t.integer "exercise_id", null: false
    t.integer "user_id", null: false
    t.string "user_type", null: false
    t.string "search"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "structured_error_attributes", id: :serial, force: :cascade do |t|
    t.integer "structured_error_id"
    t.integer "error_template_attribute_id"
    t.string "value"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "match"
    t.index ["structured_error_id"], name: "index_structured_error_attributes_on_structured_error_id"
  end

  create_table "structured_errors", id: :serial, force: :cascade do |t|
    t.integer "error_template_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "submission_id"
    t.index ["submission_id"], name: "index_structured_errors_on_submission_id"
  end

  create_table "study_group_memberships", force: :cascade do |t|
    t.bigint "study_group_id"
    t.string "user_type"
    t.bigint "user_id"
    t.integer "role", limit: 2, default: 0, null: false, comment: "Used as enum in Rails"
    t.index ["study_group_id"], name: "index_study_group_memberships_on_study_group_id"
    t.index ["user_type", "user_id"], name: "index_study_group_memberships_on_user"
  end

  create_table "study_groups", force: :cascade do |t|
    t.string "name"
    t.string "external_id"
    t.bigint "consumer_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["consumer_id"], name: "index_study_groups_on_consumer_id"
    t.index ["external_id", "consumer_id"], name: "index_study_groups_on_external_id_and_consumer_id", unique: true, nulls_not_distinct: true
  end

  create_table "submissions", id: :serial, force: :cascade do |t|
    t.integer "exercise_id"
    t.float "score"
    t.integer "contributor_id", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "cause"
    t.string "contributor_type", null: false
    t.bigint "study_group_id"
    t.index ["cause"], name: "index_submissions_on_cause", opclass: :gin_trgm_ops, using: :gin
    t.index ["contributor_id"], name: "index_submissions_on_contributor_id"
    t.index ["created_at"], name: "index_submissions_on_created_at", order: :desc
    t.index ["exercise_id"], name: "index_submissions_on_exercise_id"
    t.index ["study_group_id"], name: "index_submissions_on_study_group_id"
  end

  create_table "subscriptions", id: :serial, force: :cascade do |t|
    t.integer "user_id"
    t.string "user_type"
    t.integer "request_for_comment_id"
    t.string "subscription_type"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "deleted"
    t.bigint "study_group_id"
    t.index ["study_group_id"], name: "index_subscriptions_on_study_group_id"
  end

  create_table "tags", id: :serial, force: :cascade do |t|
    t.string "name", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "testrun_execution_environments", force: :cascade do |t|
    t.bigint "testrun_id", null: false
    t.bigint "execution_environment_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["execution_environment_id"], name: "index_testrun_execution_environments"
    t.index ["testrun_id"], name: "index_testrun_execution_environments_on_testrun_id"
  end

  create_table "testrun_messages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
    t.bigint "testrun_id", null: false
    t.interval "timestamp", default: "PT0S", null: false
    t.integer "cmd", limit: 2, default: 1, null: false, comment: "Used as enum in Rails"
    t.integer "stream", limit: 2, comment: "Used as enum in Rails"
    t.text "log"
    t.jsonb "data"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["testrun_id"], name: "index_testrun_messages_on_testrun_id"
    t.check_constraint "log IS NULL OR data IS NULL", name: "either_data_or_log"
  end

  create_table "testruns", id: :serial, force: :cascade do |t|
    t.boolean "passed"
    t.text "output"
    t.integer "file_id"
    t.integer "submission_id", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "cause"
    t.interval "container_execution_time"
    t.interval "waiting_for_container_time"
    t.integer "exit_code", limit: 2, comment: "No exit code is available in case of a timeout"
    t.integer "status", limit: 2, default: 0, null: false, comment: "Used as enum in Rails"
    t.string "user_type", null: false
    t.bigint "user_id", null: false
    t.index ["submission_id"], name: "index_testruns_on_submission_id"
    t.index ["user_type", "user_id"], name: "index_testruns_on_user"
    t.check_constraint "exit_code >= 0 AND exit_code <= 255", name: "exit_code_constraint"
  end

  create_table "tips", force: :cascade do |t|
    t.string "title"
    t.text "description"
    t.text "example"
    t.bigint "file_type_id"
    t.string "user_type", null: false
    t.bigint "user_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["file_type_id"], name: "index_tips_on_file_type_id"
    t.index ["user_type", "user_id"], name: "index_tips_on_user"
  end

  create_table "user_exercise_feedbacks", id: :serial, force: :cascade do |t|
    t.integer "exercise_id", null: false
    t.integer "user_id", null: false
    t.string "user_type", null: false
    t.integer "difficulty"
    t.integer "working_time_seconds"
    t.string "feedback_text"
    t.integer "user_estimated_worktime"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.float "normalized_score"
    t.bigint "submission_id"
    t.index ["submission_id"], name: "index_user_exercise_feedbacks_on_submission_id"
  end

  create_table "user_exercise_interventions", id: :serial, force: :cascade do |t|
    t.integer "contributor_id", null: false
    t.string "contributor_type", null: false
    t.integer "exercise_id", null: false
    t.integer "intervention_id", null: false
    t.integer "accumulated_worktime_s"
    t.text "reason"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.index ["contributor_type", "contributor_id"], name: "idx_on_contributor_type_contributor_id_10a7504bcc"
    t.index ["exercise_id"], name: "index_user_exercise_interventions_on_exercise_id"
    t.index ["intervention_id"], name: "index_user_exercise_interventions_on_intervention_id"
  end

  create_table "user_proxy_exercise_exercises", id: :serial, force: :cascade do |t|
    t.integer "user_id", null: false
    t.string "user_type", null: false
    t.integer "proxy_exercise_id", null: false
    t.integer "exercise_id", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "reason"
    t.index ["exercise_id"], name: "index_user_proxy_exercise_exercises_on_exercise_id"
    t.index ["proxy_exercise_id"], name: "index_user_proxy_exercise_exercises_on_proxy_exercise_id"
    t.index ["user_id", "user_type", "proxy_exercise_id"], name: "idx_on_user_id_user_type_proxy_exercise_id_50cfc18ee8", unique: true
    t.index ["user_type", "user_id"], name: "index_user_proxy_exercise_exercises_on_user"
  end

  create_table "webauthn_credentials", force: :cascade do |t|
    t.string "external_id", null: false
    t.string "public_key", null: false
    t.string "label", null: false
    t.bigint "sign_count", default: 0, null: false
    t.string "transports", default: [], array: true
    t.string "user_type", null: false
    t.bigint "user_id", null: false
    t.datetime "last_used_at", precision: nil
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["external_id"], name: "index_webauthn_credentials_on_external_id", unique: true
    t.index ["user_type", "user_id"], name: "index_webauthn_credentials_on_user"
  end

  add_foreign_key "anomaly_notifications", "exercise_collections"
  add_foreign_key "anomaly_notifications", "exercises"
  add_foreign_key "authentication_tokens", "study_groups"
  add_foreign_key "community_solution_contributions", "community_solution_locks"
  add_foreign_key "community_solution_contributions", "community_solutions"
  add_foreign_key "community_solution_contributions", "study_groups"
  add_foreign_key "community_solution_locks", "community_solutions"
  add_foreign_key "community_solutions", "exercises"
  add_foreign_key "events", "programming_groups"
  add_foreign_key "events", "study_groups"
  add_foreign_key "events_synchronized_editor", "files"
  add_foreign_key "events_synchronized_editor", "programming_groups"
  add_foreign_key "events_synchronized_editor", "study_groups"
  add_foreign_key "exercise_tips", "exercise_tips", column: "parent_exercise_tip_id"
  add_foreign_key "exercise_tips", "exercises"
  add_foreign_key "exercise_tips", "tips"
  add_foreign_key "lti_parameters", "exercises"
  add_foreign_key "lti_parameters", "external_users"
  add_foreign_key "lti_parameters", "study_groups"
  add_foreign_key "pair_programming_exercise_feedbacks", "exercises"
  add_foreign_key "pair_programming_exercise_feedbacks", "programming_groups"
  add_foreign_key "pair_programming_exercise_feedbacks", "study_groups"
  add_foreign_key "pair_programming_exercise_feedbacks", "submissions"
  add_foreign_key "pair_programming_waiting_users", "exercises"
  add_foreign_key "pair_programming_waiting_users", "programming_groups"
  add_foreign_key "programming_group_memberships", "programming_groups"
  add_foreign_key "programming_groups", "exercises"
  add_foreign_key "remote_evaluation_mappings", "programming_groups"
  add_foreign_key "remote_evaluation_mappings", "study_groups"
  add_foreign_key "runners", "execution_environments"
  add_foreign_key "structured_error_attributes", "error_template_attributes"
  add_foreign_key "structured_error_attributes", "structured_errors"
  add_foreign_key "structured_errors", "error_templates"
  add_foreign_key "structured_errors", "submissions"
  add_foreign_key "submissions", "study_groups"
  add_foreign_key "subscriptions", "study_groups"
  add_foreign_key "testrun_execution_environments", "execution_environments"
  add_foreign_key "testrun_execution_environments", "testruns"
  add_foreign_key "testrun_messages", "testruns"
  add_foreign_key "testruns", "submissions"
  add_foreign_key "tips", "file_types"
  add_foreign_key "user_exercise_feedbacks", "submissions"
  add_foreign_key "user_exercise_interventions", "exercises"
  add_foreign_key "user_exercise_interventions", "interventions"
  add_foreign_key "user_proxy_exercise_exercises", "exercises"
  add_foreign_key "user_proxy_exercise_exercises", "proxy_exercises"
end
