chore: init commit

in worker.../repo/GITFOLDER.zip is the .git folder.
This commit is contained in:
2026-08-11 14:44:09 -04:00
parent 0012380fd3
commit 392781f7aa
781 changed files with 65944 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
class CreateSchools < ActiveRecord::Migration[7.1]
def change
create_table :schools do |t|
t.string :name
t.timestamps
end
end
end

View File

@@ -0,0 +1,11 @@
class CreateYears < ActiveRecord::Migration[7.1]
def change
create_table :years do |t|
t.integer :year
t.timestamps
end
add_index :years, :year, unique: true
end
end

View File

@@ -0,0 +1,12 @@
class CreateSchoolYears < ActiveRecord::Migration[7.1]
def change
create_table :school_years do |t|
t.references :school, null: false, foreign_key: true
t.references :year, null: false, foreign_key: true
t.timestamps
end
add_index :school_years, [:school_id, :year_id], unique: true
end
end

View File

@@ -0,0 +1,12 @@
class CreateClassrooms < ActiveRecord::Migration[7.1]
def change
create_table :classrooms do |t|
t.string :name
t.references :year, null: false, foreign_key: true
t.references :school, null: false, foreign_key: true
t.string :grade
t.timestamps
end
end
end

View File

@@ -0,0 +1,45 @@
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[7.1]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: true, default: ""
t.string :username, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.string :current_sign_in_ip
# t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :username, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end

View File

@@ -0,0 +1,51 @@
class CreateTableRelations < ActiveRecord::Migration[7.1]
def change
add_reference :users, :classroom, foreign_key: true
create_table :portfolios do |t|
t.references :user, null: false, foreign_key: true
t.float :cash_balance
t.float :current_position
t.json :transactions
t.timestamps
end
create_table :portfolio_transactions do |t|
t.references :portfolio, null: false, foreign_key: true
t.integer :actor_id, null: false
t.integer :transaction_type
t.timestamps
end
create_table :companies do |t|
t.string :company_name
t.json :company_info
t.timestamps
end
add_index :companies, :company_name, unique: true
create_table :stocks do |t|
t.string :ticker
t.json :price_info
t.references :company, null: false, foreign_key: true
t.timestamps
end
add_index :stocks, :ticker, unique: true
create_table :portfolio_stocks do |t|
t.references :portfolio, null: false, foreign_key: true
t.references :stock, null: false, foreign_key: true
t.float :shares
t.float :purchase_price
t.timestamps
end
add_index :portfolio_stocks, [:portfolio_id, :stock_id], unique: true, name: "index_portfolio_stocks_on_portfolio_and_stock"
end
end

View File

@@ -0,0 +1,5 @@
class AddStockToCompanies < ActiveRecord::Migration[7.1]
def change
add_reference :companies, :stock, foreign_key: true
end
end

View File

@@ -0,0 +1,28 @@
# standard:disable Rails/ReversibleMigration
class RemoveCompanyAndAddToStock < ActiveRecord::Migration[7.1]
def change
remove_reference :stocks, :company, foreign_key: true
drop_table :companies
add_column :stocks, :stock_exchange, :string
add_column :stocks, :company_name, :string
add_column :stocks, :company_website, :string
add_column :stocks, :description, :text
add_column :stocks, :industry, :string
add_column :stocks, :management, :text
add_column :stocks, :employees, :integer
add_column :stocks, :competitor_names, :text
add_column :stocks, :sales_growth, :decimal, precision: 15, scale: 2
add_column :stocks, :industry_avg_sales_growth, :decimal, precision: 15, scale: 2
add_column :stocks, :debt_to_equity, :decimal, precision: 15, scale: 2
add_column :stocks, :industry_avg_debt_to_equity, :decimal, precision: 15, scale: 2
add_column :stocks, :profit_margin, :decimal, precision: 15, scale: 2
add_column :stocks, :industry_avg_profit_margin, :decimal, precision: 15, scale: 2
add_column :stocks, :cash_flow, :decimal, precision: 15, scale: 2
add_column :stocks, :debt, :decimal, precision: 15, scale: 2
end
end
# rubocop:enable Rails/ReversibleMigration

View File

@@ -0,0 +1,5 @@
class RemoveUniqueIndexFromEmail < ActiveRecord::Migration[7.1]
def change
remove_index :users, :email
end
end

View File

@@ -0,0 +1,5 @@
class RemoveActorIdFromPortfolioTransactions < ActiveRecord::Migration[7.1]
def change
remove_reference :portfolio_transactions, :actor
end
end

View File

@@ -0,0 +1,5 @@
class AddAmountToPortfolioTransactions < ActiveRecord::Migration[7.1]
def change
add_column :portfolio_transactions, :amount, :decimal, precision: 8, scale: 2, null: false
end
end

View File

@@ -0,0 +1,7 @@
# standard:disable Rails/ReversibleMigration
class RemoveCashBalanceFromPortfolios < ActiveRecord::Migration[7.1]
def change
remove_column :portfolios, :cash_balance
end
end
# rubocop:enable Rails/ReversibleMigration

View File

@@ -0,0 +1,12 @@
class CreateOrders < ActiveRecord::Migration[7.1]
def change
create_table :orders do |t|
t.references :user, null: false, foreign_key: true
t.references :stock, null: false, foreign_key: true
t.decimal :shares
t.integer :status
t.timestamps
end
end
end

View File

@@ -0,0 +1,5 @@
class RemovePriceInfoFromStocks < ActiveRecord::Migration[7.1]
def change
remove_column :stocks, :price_info, :json
end
end

View File

@@ -0,0 +1,5 @@
class AddPriceToStocks < ActiveRecord::Migration[7.1]
def change
add_column :stocks, :price, :decimal
end
end

View File

@@ -0,0 +1,24 @@
# standard:disable Rails/ReversibleMigrationMethodDefinition
class CreateDelayedJobs < ActiveRecord::Migration[7.1]
def self.up
create_table :delayed_jobs do |table|
table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue
table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually.
table.text :handler, null: false # YAML-encoded string of the object that will do work
table.text :last_error # reason for last failure (See Note below)
table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
table.datetime :locked_at # Set when a client is working on this object
table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
table.string :locked_by # Who is working on this object (if locked)
table.string :queue # The name of the queue this job is in
table.timestamps null: true
end
add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority"
end
def self.down
drop_table :delayed_jobs
end
end
# rubocop:enable Rails/ReversibleMigrationMethodDefinition

View File

@@ -0,0 +1,9 @@
# standard:disable Rails/ReversibleMigration
class ChangeFloatToBigDecimalPortfolioStocks < ActiveRecord::Migration[7.1]
def change
change_column :portfolio_stocks, :shares, :decimal, precision: 15, scale: 2
change_column :portfolio_stocks, :purchase_price, :decimal, precision: 15, scale: 2
end
end
# rubocop:enable Rails/ReversibleMigration

View File

@@ -0,0 +1,6 @@
class AddPortfolioStockAndTransactionToOrder < ActiveRecord::Migration[7.1]
def change
add_reference :orders, :portfolio_stock, null: true, foreign_key: true
add_reference :orders, :portfolio_transaction, null: true, foreign_key: true
end
end

View File

@@ -0,0 +1,9 @@
# standard:disable Rails/ReversibleMigration
class RemovePortfolioStocksIndex < ActiveRecord::Migration[7.1]
def change
remove_index :portfolio_stocks, name: "index_portfolio_stocks_on_portfolio_and_stock"
add_index :portfolio_stocks, [:portfolio_id, :stock_id], name: "index_portfolio_stocks_on_portfolio_and_stock"
end
end
# rubocop:enable Rails/ReversibleMigration

View File

@@ -0,0 +1,5 @@
class AddAdminToUsers < ActiveRecord::Migration[7.1]
def change
add_column :users, :admin, :boolean, default: false
end
end

View File

@@ -0,0 +1,5 @@
class AddNotNullConstraintToYearOnYears < ActiveRecord::Migration[7.1]
def change
change_column_null :years, :year, false
end
end

View File

@@ -0,0 +1,27 @@
class RenameAmountToAmountCentsOnPortfolioTransactions < ActiveRecord::Migration[7.2]
def change
reversible do |dir|
dir.up do
rename_column(:portfolio_transactions, :amount, :amount_cents)
change_column(
:portfolio_transactions,
:amount_cents,
:integer,
null: false
)
end
dir.down do
rename_column(:portfolio_transactions, :amount_cents, :amount)
change_column(
:portfolio_transactions,
:amount,
:decimal,
precision: 15,
scale: 2,
null: false
)
end
end
end
end

View File

@@ -0,0 +1,5 @@
class AddNotNullConstraintToTransactionTypeOnProtfolioTransactions < ActiveRecord::Migration[7.2]
def change
change_column_null(:portfolio_transactions, :transaction_type, false)
end
end

View File

@@ -0,0 +1,15 @@
class ChangePriceToPriceCentsOnStocks < ActiveRecord::Migration[7.2]
def change
reversible do |dir|
dir.up do
rename_column :stocks, :price, :price_cents
change_column :stocks, :price_cents, :integer
end
dir.down do
rename_column :stocks, :price_cents, :price
change_column :stocks, :price, :decimal
end
end
end
end

View File

@@ -0,0 +1,5 @@
class AddSchoolYearRefToClassrooms < ActiveRecord::Migration[7.2]
def change
add_reference :classrooms, :school_year, foreign_key: true
end
end

View File

@@ -0,0 +1,6 @@
class RemoveYearAndSchoolFromClassrooms < ActiveRecord::Migration[7.2]
def change
remove_reference :classrooms, :year, foreign_key: true
remove_reference :classrooms, :school, foreign_key: true
end
end

View File

@@ -0,0 +1,5 @@
class AddUniqueIndexToUsersEmail < ActiveRecord::Migration[7.2]
def change
add_index :users, :email, unique: true
end
end

View File

@@ -0,0 +1,11 @@
class ChangeYearColumnToNameAndUpdateType < ActiveRecord::Migration[7.2]
def up
rename_column :years, :year, :name
change_column :years, :name, :string, null: false
end
def down
change_column :years, :name, :integer
rename_column :years, :name, :year
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
# Adds a type column to the users table to support STI
class AddTypeToUsers < ActiveRecord::Migration[8.0]
def change
add_column :users, :type, :string, default: "User", null: false
change_column_null :users, :type, false
end
end

View File

@@ -0,0 +1,29 @@
class FixStudentEmailConstraints < ActiveRecord::Migration[8.0]
def up
# Remove the existing unique index on email
remove_index :users, :email if index_exists?(:users, :email)
# Change email column to allow null and remove default empty string
change_column :users, :email, :string, null: true, default: nil
# Update existing empty string emails to null for students
User.where(email: "", type: "Student").update_all(email: nil)
# Create a partial unique index that only applies to non-null, non-empty emails
add_index :users, :email, unique: true, where: "email IS NOT NULL AND email != ''"
end
def down
# Remove the partial unique index
remove_index :users, :email if index_exists?(:users, :email)
# Restore the original email column settings
change_column :users, :email, :string, null: false, default: ""
# Update null emails back to empty strings
User.where(email: nil).update_all(email: "")
# Restore the original unique index
add_index :users, :email, unique: true
end
end

View File

@@ -0,0 +1,5 @@
class RemoveTransactionsFromPortfolios < ActiveRecord::Migration[8.0]
def change
safety_assured { remove_column :portfolios, :transactions, :json }
end
end

View File

@@ -0,0 +1,10 @@
class CreateQuarters < ActiveRecord::Migration[8.0]
def change
create_table :quarters do |t|
t.string :name
t.references :school_year, null: false, foreign_key: true
t.timestamps
end
end
end

View File

@@ -0,0 +1,13 @@
class CreateGradeBooks < ActiveRecord::Migration[8.0]
def change
create_table :grade_books do |t|
t.references :quarter, null: false, foreign_key: true
t.references :classroom, null: false, foreign_key: true
t.string :status, null: false, default: 'draft'
t.timestamps
end
add_index :grade_books, [:quarter_id, :classroom_id], unique: true
end
end

View File

@@ -0,0 +1,16 @@
class CreateGradeEntries < ActiveRecord::Migration[8.0]
def change
create_table :grade_entries do |t|
t.references :grade_book, null: false, foreign_key: true
t.references :user, null: false, foreign_key: true
t.string :math_grade, null: true
t.string :reading_grade, null: true
t.bigint :days_missed, null: false, default: 0
t.timestamps
end
add_index :grade_entries, [:grade_book_id, :user_id], unique: true
end
end

View File

@@ -0,0 +1,5 @@
class AddNumberToQuarters < ActiveRecord::Migration[8.0]
def change
add_column :quarters, :number, :integer, null: false
end
end

View File

@@ -0,0 +1,7 @@
class AddIndexToNumberOnQuarters < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def change
add_index :quarters, [:school_year_id, :number], unique: true, algorithm: :concurrently
end
end

View File

@@ -0,0 +1,12 @@
class CreateTeacherClassrooms < ActiveRecord::Migration[8.0]
def change
create_table :teacher_classrooms do |t|
t.references :teacher, null: false, foreign_key: { to_table: :users }
t.references :classroom, null: false, foreign_key: true
t.timestamps
end
add_index :teacher_classrooms, [:teacher_id, :classroom_id], unique: true
end
end

View File

@@ -0,0 +1,5 @@
class SetStatusDefaulOnOrders < ActiveRecord::Migration[8.0]
def change
change_column_default :orders, :status, 0
end
end

View File

@@ -0,0 +1,5 @@
class AddPerfectWeeksToGradeEntries < ActiveRecord::Migration[8.0]
def change
add_column :grade_entries, :perfect_weeks, :bigint
end
end

View File

@@ -0,0 +1,5 @@
class RemoveMissedDaysFromGradeEntries < ActiveRecord::Migration[8.0]
def change
safety_assured { remove_column :grade_entries, :days_missed, :bigint }
end
end

View File

@@ -0,0 +1,5 @@
class AddAttendanceDaysToGradeEntries < ActiveRecord::Migration[8.0]
def change
add_column :grade_entries, :attendance_days, :bigint
end
end

View File

@@ -0,0 +1,5 @@
class RemovePerfectWeeksFromGradeEntries < ActiveRecord::Migration[8.0]
def change
safety_assured { remove_column :grade_entries, :perfect_weeks, :bigint }
end
end

View File

@@ -0,0 +1,7 @@
class AddActionToOrders < ActiveRecord::Migration[8.0]
def change
add_column :orders, :action, :string, null: false, default: "sell"
change_column_default :orders, :action, from: "sell", to: nil
end
end

View File

@@ -0,0 +1,5 @@
class AddArchivedToStocks < ActiveRecord::Migration[8.0]
def change
add_column :stocks, :archived, :boolean, default: false, null: false
end
end

View File

@@ -0,0 +1,6 @@
class ConvertGradeToInteger < ActiveRecord::Migration[8.0]
def change
safety_assured { remove_column :classrooms, :grade, :string }
add_column :classrooms, :grade, :integer
end
end

View File

@@ -0,0 +1,5 @@
class AddTransactionFeeToOrders < ActiveRecord::Migration[8.0]
def change
add_column :orders, :transaction_fee_cents, :integer, null: false, default: 0
end
end

View File

@@ -0,0 +1,5 @@
class AddReasonToPortfolioTransaction < ActiveRecord::Migration[8.0]
def change
add_column :portfolio_transactions, :reason, :string
end
end

View File

@@ -0,0 +1,5 @@
class AddDiscardedAtToUsers < ActiveRecord::Migration[8.0]
def change
add_column :users, :discarded_at, :datetime
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddIndexDiscardedAtToUsers < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def change
add_index :users, :discarded_at, algorithm: :concurrently
end
end

View File

@@ -0,0 +1,5 @@
class AddIsPerfectAttendanceToGradeEntries < ActiveRecord::Migration[8.0]
def change
add_column :grade_entries, :is_perfect_attendance, :boolean, default: false, null: false
end
end

View File

@@ -0,0 +1,7 @@
class DropTransactionFeeFromOrders < ActiveRecord::Migration[8.0]
def change
safety_assured do
remove_column :orders, :transaction_fee_cents, :integer, default: 0, null: false
end
end
end

View File

@@ -0,0 +1,5 @@
class AddYesterdayPriceCentsToStocks < ActiveRecord::Migration[8.0]
def change
add_column :stocks, :yesterday_price_cents, :integer
end
end

View File

@@ -0,0 +1,5 @@
class AddNameToUsers < ActiveRecord::Migration[8.0]
def change
add_column :users, :name, :string
end
end

View File

@@ -0,0 +1,13 @@
class CreatePortfolioSnapshots < ActiveRecord::Migration[8.0]
def change
create_table :portfolio_snapshots do |t|
t.references :portfolio, null: false, foreign_key: true
t.date :date, null: false
t.integer :worth_cents, null: false
t.timestamps
end
add_index :portfolio_snapshots, [:portfolio_id, :date], unique: true
end
end

View File

@@ -0,0 +1,10 @@
class CreateAnnouncments < ActiveRecord::Migration[8.0]
def change
create_table :announcements do |t|
t.string :title
t.text :body
t.timestamps
end
end
end

View File

@@ -0,0 +1,57 @@
# This migration comes from active_storage (originally 20170806125915)
class CreateActiveStorageTables < ActiveRecord::Migration[7.0]
def change
# Use Active Record's configured type for primary and foreign keys
primary_key_type, foreign_key_type = primary_and_foreign_key_types
create_table :active_storage_blobs, id: primary_key_type do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.string :service_name, null: false
t.bigint :byte_size, null: false
t.string :checksum
if connection.supports_datetime_with_precision?
t.datetime :created_at, precision: 6, null: false
else
t.datetime :created_at, null: false
end
t.index [ :key ], unique: true
end
create_table :active_storage_attachments, id: primary_key_type do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
t.references :blob, null: false, type: foreign_key_type
if connection.supports_datetime_with_precision?
t.datetime :created_at, precision: 6, null: false
else
t.datetime :created_at, null: false
end
t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
create_table :active_storage_variant_records, id: primary_key_type do |t|
t.belongs_to :blob, null: false, index: false, type: foreign_key_type
t.string :variation_digest, null: false
t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
private
def primary_and_foreign_key_types
config = Rails.configuration.generators
setting = config.options[config.orm][:primary_key_type]
primary_key_type = setting || :primary_key
foreign_key_type = setting || :bigint
[ primary_key_type, foreign_key_type ]
end
end

View File

@@ -0,0 +1,26 @@
# This migration comes from action_text (originally 20180528164100)
class CreateActionTextTables < ActiveRecord::Migration[6.0]
def change
# Use Active Record's configured type for primary and foreign keys
primary_key_type, foreign_key_type = primary_and_foreign_key_types
create_table :action_text_rich_texts, id: primary_key_type do |t|
t.string :name, null: false
t.text :body, size: :long
t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
t.timestamps
t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true
end
end
private
def primary_and_foreign_key_types
config = Rails.configuration.generators
setting = config.options[config.orm][:primary_key_type]
primary_key_type = setting || :primary_key
foreign_key_type = setting || :bigint
[ primary_key_type, foreign_key_type ]
end
end

View File

@@ -0,0 +1,24 @@
# frozen_string_literal: true
class DropDelayedJobsTable < ActiveRecord::Migration[8.0]
def up
drop_table :delayed_jobs, if_exists: true
end
def down
create_table "delayed_jobs" do |t|
t.integer "priority", default: 0, null: false
t.integer "attempts", default: 0, null: false
t.text "handler", null: false
t.text "last_error"
t.datetime "run_at"
t.datetime "locked_at"
t.datetime "failed_at"
t.string "locked_by"
t.string "queue"
t.timestamps null: true
t.index ["priority", "run_at"], name: "delayed_jobs_priority"
end
end
end

View File

@@ -0,0 +1,7 @@
class AddIndexToAnnouncementsCreatedAt < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def change
add_index :announcements, :created_at, order: { created_at: :desc }, algorithm: :concurrently
end
end

View File

@@ -0,0 +1,131 @@
class CreateSolidQueue < ActiveRecord::Migration[8.0]
safety_assured do
create_table "solid_queue_blocked_executions", force: :cascade do |t|
t.bigint "job_id", null: false
t.string "queue_name", null: false
t.integer "priority", default: 0, null: false
t.string "concurrency_key", null: false
t.datetime "expires_at", null: false
t.datetime "created_at", null: false
t.index [ "concurrency_key", "priority", "job_id" ], name: "index_solid_queue_blocked_executions_for_release"
t.index [ "expires_at", "concurrency_key" ], name: "index_solid_queue_blocked_executions_for_maintenance"
t.index [ "job_id" ], name: "index_solid_queue_blocked_executions_on_job_id", unique: true
end
create_table "solid_queue_claimed_executions", force: :cascade do |t|
t.bigint "job_id", null: false
t.bigint "process_id"
t.datetime "created_at", null: false
t.index [ "job_id" ], name: "index_solid_queue_claimed_executions_on_job_id", unique: true
t.index [ "process_id", "job_id" ], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id"
end
create_table "solid_queue_failed_executions", force: :cascade do |t|
t.bigint "job_id", null: false
t.text "error"
t.datetime "created_at", null: false
t.index [ "job_id" ], name: "index_solid_queue_failed_executions_on_job_id", unique: true
end
create_table "solid_queue_jobs", force: :cascade do |t|
t.string "queue_name", null: false
t.string "class_name", null: false
t.text "arguments"
t.integer "priority", default: 0, null: false
t.string "active_job_id"
t.datetime "scheduled_at"
t.datetime "finished_at"
t.string "concurrency_key"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index [ "active_job_id" ], name: "index_solid_queue_jobs_on_active_job_id"
t.index [ "class_name" ], name: "index_solid_queue_jobs_on_class_name"
t.index [ "finished_at" ], name: "index_solid_queue_jobs_on_finished_at"
t.index [ "queue_name", "finished_at" ], name: "index_solid_queue_jobs_for_filtering"
t.index [ "scheduled_at", "finished_at" ], name: "index_solid_queue_jobs_for_alerting"
end
create_table "solid_queue_pauses", force: :cascade do |t|
t.string "queue_name", null: false
t.datetime "created_at", null: false
t.index [ "queue_name" ], name: "index_solid_queue_pauses_on_queue_name", unique: true
end
create_table "solid_queue_processes", force: :cascade do |t|
t.string "kind", null: false
t.datetime "last_heartbeat_at", null: false
t.bigint "supervisor_id"
t.integer "pid", null: false
t.string "hostname"
t.text "metadata"
t.datetime "created_at", null: false
t.string "name", null: false
t.index [ "last_heartbeat_at" ], name: "index_solid_queue_processes_on_last_heartbeat_at"
t.index [ "name", "supervisor_id" ], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true
t.index [ "supervisor_id" ], name: "index_solid_queue_processes_on_supervisor_id"
end
create_table "solid_queue_ready_executions", force: :cascade do |t|
t.bigint "job_id", null: false
t.string "queue_name", null: false
t.integer "priority", default: 0, null: false
t.datetime "created_at", null: false
t.index [ "job_id" ], name: "index_solid_queue_ready_executions_on_job_id", unique: true
t.index [ "priority", "job_id" ], name: "index_solid_queue_poll_all"
t.index [ "queue_name", "priority", "job_id" ], name: "index_solid_queue_poll_by_queue"
end
create_table "solid_queue_recurring_executions", force: :cascade do |t|
t.bigint "job_id", null: false
t.string "task_key", null: false
t.datetime "run_at", null: false
t.datetime "created_at", null: false
t.index [ "job_id" ], name: "index_solid_queue_recurring_executions_on_job_id", unique: true
t.index [ "task_key", "run_at" ], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true
end
create_table "solid_queue_recurring_tasks", force: :cascade do |t|
t.string "key", null: false
t.string "schedule", null: false
t.string "command", limit: 2048
t.string "class_name"
t.text "arguments"
t.string "queue_name"
t.integer "priority", default: 0
t.boolean "static", default: true, null: false
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index [ "key" ], name: "index_solid_queue_recurring_tasks_on_key", unique: true
t.index [ "static" ], name: "index_solid_queue_recurring_tasks_on_static"
end
create_table "solid_queue_scheduled_executions", force: :cascade do |t|
t.bigint "job_id", null: false
t.string "queue_name", null: false
t.integer "priority", default: 0, null: false
t.datetime "scheduled_at", null: false
t.datetime "created_at", null: false
t.index [ "job_id" ], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true
t.index [ "scheduled_at", "priority", "job_id" ], name: "index_solid_queue_dispatch_all"
end
create_table "solid_queue_semaphores", force: :cascade do |t|
t.string "key", null: false
t.integer "value", default: 1, null: false
t.datetime "expires_at", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index [ "expires_at" ], name: "index_solid_queue_semaphores_on_expires_at"
t.index [ "key", "value" ], name: "index_solid_queue_semaphores_on_key_and_value"
t.index [ "key" ], name: "index_solid_queue_semaphores_on_key", unique: true
end
add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
end
end

View File

@@ -0,0 +1,5 @@
class AddDescriptionToPortfolioTransactions < ActiveRecord::Migration[8.0]
def change
add_column :portfolio_transactions, :description, :text
end
end

View File

@@ -0,0 +1,5 @@
class AddArchivedToClassrooms < ActiveRecord::Migration[8.0]
def change
add_column :classrooms, :archived, :boolean, default: false, null: false
end
end

View File

@@ -0,0 +1,5 @@
class AddTradingEnabledToClassrooms < ActiveRecord::Migration[8.0]
def change
add_column :classrooms, :trading_enabled, :boolean, default: false, null: false
end
end

View File

@@ -0,0 +1,53 @@
class ConvertPortfolioTransactionReasonToEnum < ActiveRecord::Migration[8.1]
def up
add_column :portfolio_transactions, :reason_enum, :integer
safety_assured do
execute <<-SQL
UPDATE portfolio_transactions SET reason_enum = 0 WHERE reason = 'Earnings from Math';
UPDATE portfolio_transactions SET reason_enum = 1 WHERE reason = 'Earnings from Reading';
UPDATE portfolio_transactions SET reason_enum = 2 WHERE reason = 'Earnings from Attendance';
UPDATE portfolio_transactions SET reason_enum = 3 WHERE reason = 'Earnings from Grades';
UPDATE portfolio_transactions SET reason_enum = 4 WHERE reason = 'Transaction Fees';
UPDATE portfolio_transactions SET reason_enum = 5 WHERE reason = 'Award';
UPDATE portfolio_transactions SET reason_enum = 6 WHERE reason = 'Administrative Adjustment';
SQL
end
if PortfolioTransaction.where.not(reason: nil).where(reason_enum: nil).exists?
raise "Migration Failed: Some records were not migrated! Check for unmapped 'reason' strings."
end
safety_assured do
remove_column :portfolio_transactions, :reason
end
safety_assured do
rename_column :portfolio_transactions, :reason_enum, :reason
end
end
def down
add_column :portfolio_transactions, :reason_string, :string
safety_assured do
execute <<-SQL
UPDATE portfolio_transactions SET reason_string = 'Earnings from Math' WHERE reason = 0;
UPDATE portfolio_transactions SET reason_string = 'Earnings from Reading' WHERE reason = 1;
UPDATE portfolio_transactions SET reason_string = 'Earnings from Attendance' WHERE reason = 2;
UPDATE portfolio_transactions SET reason_string = 'Earnings from Grades' WHERE reason = 3;
UPDATE portfolio_transactions SET reason_string = 'Transaction Fees' WHERE reason = 4;
UPDATE portfolio_transactions SET reason_string = 'Award' WHERE reason = 5;
UPDATE portfolio_transactions SET reason_string = 'Administrative Adjustment' WHERE reason = 6;
SQL
end
safety_assured do
remove_column :portfolio_transactions, :reason
end
safety_assured do
rename_column :portfolio_transactions, :reason_string, :reason
end
end
end

View File

@@ -0,0 +1,5 @@
class AddFeaturedToAnnouncements < ActiveRecord::Migration[8.1]
def change
add_column :announcements, :featured, :boolean, default: false, null: false
end
end

View File

@@ -0,0 +1,37 @@
class CreateGrades < ActiveRecord::Migration[8.1]
def change
create_table :grades do |t|
t.string :name, null: false
t.integer :level, null: false
t.timestamps
end
add_index :grades, :level, unique: true
add_index :grades, :name, unique: true
reversible do |dir|
dir.up do
grades = [
{ name: "Kindergarten", level: 0 },
{ name: "1st Grade", level: 1 },
{ name: "2nd Grade", level: 2 },
{ name: "3rd Grade", level: 3 },
{ name: "4th Grade", level: 4 },
{ name: "5th Grade", level: 5 },
{ name: "6th Grade", level: 6 },
{ name: "7th Grade", level: 7 },
{ name: "8th Grade", level: 8 },
{ name: "9th Grade", level: 9 },
{ name: "10th Grade", level: 10 },
{ name: "11th Grade", level: 11 },
{ name: "12th Grade", level: 12 },
]
Grade.upsert_all(
grades,
unique_by: :level
)
end
end
end
end

View File

@@ -0,0 +1,12 @@
class CreateClassroomGrades < ActiveRecord::Migration[8.1]
def change
create_table :classroom_grades do |t|
t.references :classroom, null: false, foreign_key: true
t.references :grade, null: false, foreign_key: true
t.timestamps
end
add_index :classroom_grades, [:classroom_id, :grade_id], unique: true
end
end

View File

@@ -0,0 +1,5 @@
class AddLastTradingDayToStocks < ActiveRecord::Migration[8.1]
def change
add_column :stocks, :last_trading_day, :date
end
end

View File

@@ -0,0 +1,39 @@
class MigrateClassroomGradeToClassroomGrades < ActiveRecord::Migration[8.1]
class Classroom < ActiveRecord::Base
self.table_name = "classrooms"
end
class Grade < ActiveRecord::Base
self.table_name = "grades"
end
class ClassroomGrade < ActiveRecord::Base
self.table_name = "classroom_grades"
end
def up
Classroom.reset_column_information
Classroom.find_each do |classroom|
grade_number = classroom.grade
raise "Grade not found for classroom #{classroom.id}" unless grade_number
grade = Grade.find_by(level: grade_number)
raise "Grade with level #{grade_number} not found" unless grade
classroom_grade = ClassroomGrade.create!(grade_id: grade.id, classroom_id: classroom.id)
end
safety_assured { remove_column :classrooms, :grade }
end
def down
add_column :classrooms, :grade, :integer
Classroom.reset_column_information
Classroom.find_each do |classroom|
level = classroom.classroom_grade&.grade&.level
classroom.update_column(:grade, level) if level
end
end
end

View File

@@ -0,0 +1,19 @@
class CreateClassroomEnrollments < ActiveRecord::Migration[8.1]
def change
create_table :classroom_enrollments do |t|
t.references :student, null: false, foreign_key: { to_table: :users }
t.references :classroom, null: false, foreign_key: true
t.datetime :enrolled_at, null: false
t.datetime :unenrolled_at
t.boolean :primary, default: false, null: false
t.timestamps
end
add_index :classroom_enrollments, [:student_id, :classroom_id, :enrolled_at],
name: "index_enrollments_on_student_classroom_enrolled"
add_index :classroom_enrollments, [:student_id, :primary],
where: '"primary" = true',
name: "index_enrollments_on_student_primary"
end
end

View File

@@ -0,0 +1,5 @@
class RemoveCurrentPositionFromPortfolios < ActiveRecord::Migration[8.1]
def change
safety_assured { remove_column :portfolios, :current_position, :float }
end
end

View File

@@ -0,0 +1,11 @@
class BackfillTeacherUsernameFromEmail < ActiveRecord::Migration[8.1]
def up
User.where(type: "Teacher").find_each do |teacher|
teacher.update_column(:username, teacher.email) if teacher.email.present?
end
end
def down
# No safe way to reverse — username values before migration are unknown
end
end

View File

@@ -0,0 +1,8 @@
class AddCascadeDeleteToQuartersSchoolYear < ActiveRecord::Migration[8.1]
def change
safety_assured do
remove_foreign_key :quarters, :school_years
add_foreign_key :quarters, :school_years, on_delete: :cascade
end
end
end

View File

@@ -0,0 +1,432 @@
# 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.1].define(version: 2026_06_09_141805) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
create_table "action_text_rich_texts", force: :cascade do |t|
t.text "body"
t.datetime "created_at", null: false
t.string "name", null: false
t.bigint "record_id", null: false
t.string "record_type", null: false
t.datetime "updated_at", null: false
t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true
end
create_table "active_storage_attachments", force: :cascade do |t|
t.bigint "blob_id", null: false
t.datetime "created_at", null: false
t.string "name", null: false
t.bigint "record_id", null: false
t.string "record_type", null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
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|
t.bigint "byte_size", null: false
t.string "checksum"
t.string "content_type"
t.datetime "created_at", null: false
t.string "filename", null: false
t.string "key", null: false
t.text "metadata"
t.string "service_name", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
create_table "active_storage_variant_records", 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 "announcements", force: :cascade do |t|
t.text "body"
t.datetime "created_at", null: false
t.boolean "featured", default: false, null: false
t.string "title"
t.datetime "updated_at", null: false
t.index ["created_at"], name: "index_announcements_on_created_at", order: :desc
end
create_table "classroom_enrollments", force: :cascade do |t|
t.bigint "classroom_id", null: false
t.datetime "created_at", null: false
t.datetime "enrolled_at", null: false
t.boolean "primary", default: false, null: false
t.bigint "student_id", null: false
t.datetime "unenrolled_at"
t.datetime "updated_at", null: false
t.index ["classroom_id"], name: "index_classroom_enrollments_on_classroom_id"
t.index ["student_id", "classroom_id", "enrolled_at"], name: "index_enrollments_on_student_classroom_enrolled"
t.index ["student_id", "primary"], name: "index_enrollments_on_student_primary", where: "(\"primary\" = true)"
t.index ["student_id"], name: "index_classroom_enrollments_on_student_id"
end
create_table "classroom_grades", force: :cascade do |t|
t.bigint "classroom_id", null: false
t.datetime "created_at", null: false
t.bigint "grade_id", null: false
t.datetime "updated_at", null: false
t.index ["classroom_id", "grade_id"], name: "index_classroom_grades_on_classroom_id_and_grade_id", unique: true
t.index ["classroom_id"], name: "index_classroom_grades_on_classroom_id"
t.index ["grade_id"], name: "index_classroom_grades_on_grade_id"
end
create_table "classrooms", force: :cascade do |t|
t.boolean "archived", default: false, null: false
t.datetime "created_at", null: false
t.string "name"
t.bigint "school_year_id"
t.boolean "trading_enabled", default: false, null: false
t.datetime "updated_at", null: false
t.index ["school_year_id"], name: "index_classrooms_on_school_year_id"
end
create_table "grade_books", force: :cascade do |t|
t.bigint "classroom_id", null: false
t.datetime "created_at", null: false
t.bigint "quarter_id", null: false
t.string "status", default: "draft", null: false
t.datetime "updated_at", null: false
t.index ["classroom_id"], name: "index_grade_books_on_classroom_id"
t.index ["quarter_id", "classroom_id"], name: "index_grade_books_on_quarter_id_and_classroom_id", unique: true
t.index ["quarter_id"], name: "index_grade_books_on_quarter_id"
end
create_table "grade_entries", force: :cascade do |t|
t.bigint "attendance_days"
t.datetime "created_at", null: false
t.bigint "grade_book_id", null: false
t.boolean "is_perfect_attendance", default: false, null: false
t.string "math_grade"
t.string "reading_grade"
t.datetime "updated_at", null: false
t.bigint "user_id", null: false
t.index ["grade_book_id", "user_id"], name: "index_grade_entries_on_grade_book_id_and_user_id", unique: true
t.index ["grade_book_id"], name: "index_grade_entries_on_grade_book_id"
t.index ["user_id"], name: "index_grade_entries_on_user_id"
end
create_table "grades", force: :cascade do |t|
t.datetime "created_at", null: false
t.integer "level", null: false
t.string "name", null: false
t.datetime "updated_at", null: false
t.index ["level"], name: "index_grades_on_level", unique: true
t.index ["name"], name: "index_grades_on_name", unique: true
end
create_table "orders", force: :cascade do |t|
t.string "action", null: false
t.datetime "created_at", null: false
t.bigint "portfolio_stock_id"
t.bigint "portfolio_transaction_id"
t.decimal "shares"
t.integer "status", default: 0
t.bigint "stock_id", null: false
t.datetime "updated_at", null: false
t.bigint "user_id", null: false
t.index ["portfolio_stock_id"], name: "index_orders_on_portfolio_stock_id"
t.index ["portfolio_transaction_id"], name: "index_orders_on_portfolio_transaction_id"
t.index ["stock_id"], name: "index_orders_on_stock_id"
t.index ["user_id"], name: "index_orders_on_user_id"
end
create_table "portfolio_snapshots", force: :cascade do |t|
t.datetime "created_at", null: false
t.date "date", null: false
t.bigint "portfolio_id", null: false
t.datetime "updated_at", null: false
t.integer "worth_cents", null: false
t.index ["portfolio_id", "date"], name: "index_portfolio_snapshots_on_portfolio_id_and_date", unique: true
t.index ["portfolio_id"], name: "index_portfolio_snapshots_on_portfolio_id"
end
create_table "portfolio_stocks", force: :cascade do |t|
t.datetime "created_at", null: false
t.bigint "portfolio_id", null: false
t.decimal "purchase_price", precision: 15, scale: 2
t.decimal "shares", precision: 15, scale: 2
t.bigint "stock_id", null: false
t.datetime "updated_at", null: false
t.index ["portfolio_id", "stock_id"], name: "index_portfolio_stocks_on_portfolio_and_stock"
t.index ["portfolio_id"], name: "index_portfolio_stocks_on_portfolio_id"
t.index ["stock_id"], name: "index_portfolio_stocks_on_stock_id"
end
create_table "portfolio_transactions", force: :cascade do |t|
t.integer "amount_cents", null: false
t.datetime "created_at", null: false
t.text "description"
t.bigint "portfolio_id", null: false
t.integer "reason"
t.integer "transaction_type", null: false
t.datetime "updated_at", null: false
t.index ["portfolio_id"], name: "index_portfolio_transactions_on_portfolio_id"
end
create_table "portfolios", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "user_id", null: false
t.index ["user_id"], name: "index_portfolios_on_user_id"
end
create_table "quarters", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "name"
t.integer "number", null: false
t.bigint "school_year_id", null: false
t.datetime "updated_at", null: false
t.index ["school_year_id", "number"], name: "index_quarters_on_school_year_id_and_number", unique: true
t.index ["school_year_id"], name: "index_quarters_on_school_year_id"
end
create_table "school_years", force: :cascade do |t|
t.datetime "created_at", null: false
t.bigint "school_id", null: false
t.datetime "updated_at", null: false
t.bigint "year_id", null: false
t.index ["school_id", "year_id"], name: "index_school_years_on_school_id_and_year_id", unique: true
t.index ["school_id"], name: "index_school_years_on_school_id"
t.index ["year_id"], name: "index_school_years_on_year_id"
end
create_table "schools", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "name"
t.datetime "updated_at", null: false
end
create_table "solid_queue_blocked_executions", force: :cascade do |t|
t.string "concurrency_key", null: false
t.datetime "created_at", null: false
t.datetime "expires_at", null: false
t.bigint "job_id", null: false
t.integer "priority", default: 0, null: false
t.string "queue_name", null: false
t.index ["concurrency_key", "priority", "job_id"], name: "index_solid_queue_blocked_executions_for_release"
t.index ["expires_at", "concurrency_key"], name: "index_solid_queue_blocked_executions_for_maintenance"
t.index ["job_id"], name: "index_solid_queue_blocked_executions_on_job_id", unique: true
end
create_table "solid_queue_claimed_executions", force: :cascade do |t|
t.datetime "created_at", null: false
t.bigint "job_id", null: false
t.bigint "process_id"
t.index ["job_id"], name: "index_solid_queue_claimed_executions_on_job_id", unique: true
t.index ["process_id", "job_id"], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id"
end
create_table "solid_queue_failed_executions", force: :cascade do |t|
t.datetime "created_at", null: false
t.text "error"
t.bigint "job_id", null: false
t.index ["job_id"], name: "index_solid_queue_failed_executions_on_job_id", unique: true
end
create_table "solid_queue_jobs", force: :cascade do |t|
t.string "active_job_id"
t.text "arguments"
t.string "class_name", null: false
t.string "concurrency_key"
t.datetime "created_at", null: false
t.datetime "finished_at"
t.integer "priority", default: 0, null: false
t.string "queue_name", null: false
t.datetime "scheduled_at"
t.datetime "updated_at", null: false
t.index ["active_job_id"], name: "index_solid_queue_jobs_on_active_job_id"
t.index ["class_name"], name: "index_solid_queue_jobs_on_class_name"
t.index ["finished_at"], name: "index_solid_queue_jobs_on_finished_at"
t.index ["queue_name", "finished_at"], name: "index_solid_queue_jobs_for_filtering"
t.index ["scheduled_at", "finished_at"], name: "index_solid_queue_jobs_for_alerting"
end
create_table "solid_queue_pauses", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "queue_name", null: false
t.index ["queue_name"], name: "index_solid_queue_pauses_on_queue_name", unique: true
end
create_table "solid_queue_processes", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "hostname"
t.string "kind", null: false
t.datetime "last_heartbeat_at", null: false
t.text "metadata"
t.string "name", null: false
t.integer "pid", null: false
t.bigint "supervisor_id"
t.index ["last_heartbeat_at"], name: "index_solid_queue_processes_on_last_heartbeat_at"
t.index ["name", "supervisor_id"], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true
t.index ["supervisor_id"], name: "index_solid_queue_processes_on_supervisor_id"
end
create_table "solid_queue_ready_executions", force: :cascade do |t|
t.datetime "created_at", null: false
t.bigint "job_id", null: false
t.integer "priority", default: 0, null: false
t.string "queue_name", null: false
t.index ["job_id"], name: "index_solid_queue_ready_executions_on_job_id", unique: true
t.index ["priority", "job_id"], name: "index_solid_queue_poll_all"
t.index ["queue_name", "priority", "job_id"], name: "index_solid_queue_poll_by_queue"
end
create_table "solid_queue_recurring_executions", force: :cascade do |t|
t.datetime "created_at", null: false
t.bigint "job_id", null: false
t.datetime "run_at", null: false
t.string "task_key", null: false
t.index ["job_id"], name: "index_solid_queue_recurring_executions_on_job_id", unique: true
t.index ["task_key", "run_at"], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true
end
create_table "solid_queue_recurring_tasks", force: :cascade do |t|
t.text "arguments"
t.string "class_name"
t.string "command", limit: 2048
t.datetime "created_at", null: false
t.text "description"
t.string "key", null: false
t.integer "priority", default: 0
t.string "queue_name"
t.string "schedule", null: false
t.boolean "static", default: true, null: false
t.datetime "updated_at", null: false
t.index ["key"], name: "index_solid_queue_recurring_tasks_on_key", unique: true
t.index ["static"], name: "index_solid_queue_recurring_tasks_on_static"
end
create_table "solid_queue_scheduled_executions", force: :cascade do |t|
t.datetime "created_at", null: false
t.bigint "job_id", null: false
t.integer "priority", default: 0, null: false
t.string "queue_name", null: false
t.datetime "scheduled_at", null: false
t.index ["job_id"], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true
t.index ["scheduled_at", "priority", "job_id"], name: "index_solid_queue_dispatch_all"
end
create_table "solid_queue_semaphores", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "expires_at", null: false
t.string "key", null: false
t.datetime "updated_at", null: false
t.integer "value", default: 1, null: false
t.index ["expires_at"], name: "index_solid_queue_semaphores_on_expires_at"
t.index ["key", "value"], name: "index_solid_queue_semaphores_on_key_and_value"
t.index ["key"], name: "index_solid_queue_semaphores_on_key", unique: true
end
create_table "stocks", force: :cascade do |t|
t.boolean "archived", default: false, null: false
t.decimal "cash_flow", precision: 15, scale: 2
t.string "company_name"
t.string "company_website"
t.text "competitor_names"
t.datetime "created_at", null: false
t.decimal "debt", precision: 15, scale: 2
t.decimal "debt_to_equity", precision: 15, scale: 2
t.text "description"
t.integer "employees"
t.string "industry"
t.decimal "industry_avg_debt_to_equity", precision: 15, scale: 2
t.decimal "industry_avg_profit_margin", precision: 15, scale: 2
t.decimal "industry_avg_sales_growth", precision: 15, scale: 2
t.date "last_trading_day"
t.text "management"
t.integer "price_cents"
t.decimal "profit_margin", precision: 15, scale: 2
t.decimal "sales_growth", precision: 15, scale: 2
t.string "stock_exchange"
t.string "ticker"
t.datetime "updated_at", null: false
t.integer "yesterday_price_cents"
t.index ["ticker"], name: "index_stocks_on_ticker", unique: true
end
create_table "teacher_classrooms", force: :cascade do |t|
t.bigint "classroom_id", null: false
t.datetime "created_at", null: false
t.bigint "teacher_id", null: false
t.datetime "updated_at", null: false
t.index ["classroom_id"], name: "index_teacher_classrooms_on_classroom_id"
t.index ["teacher_id", "classroom_id"], name: "index_teacher_classrooms_on_teacher_id_and_classroom_id", unique: true
t.index ["teacher_id"], name: "index_teacher_classrooms_on_teacher_id"
end
create_table "users", force: :cascade do |t|
t.boolean "admin", default: false
t.bigint "classroom_id"
t.datetime "created_at", null: false
t.datetime "discarded_at"
t.string "email"
t.string "encrypted_password", default: "", null: false
t.string "name"
t.datetime "remember_created_at"
t.datetime "reset_password_sent_at"
t.string "reset_password_token"
t.string "type", default: "User", null: false
t.datetime "updated_at", null: false
t.string "username", default: "", null: false
t.index ["classroom_id"], name: "index_users_on_classroom_id"
t.index ["discarded_at"], name: "index_users_on_discarded_at"
t.index ["email"], name: "index_users_on_email", unique: true, where: "((email IS NOT NULL) AND ((email)::text <> ''::text))"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["username"], name: "index_users_on_username", unique: true
end
create_table "years", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "name", null: false
t.datetime "updated_at", null: false
t.index ["name"], name: "index_years_on_name", unique: true
end
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 "classroom_enrollments", "classrooms"
add_foreign_key "classroom_enrollments", "users", column: "student_id"
add_foreign_key "classroom_grades", "classrooms"
add_foreign_key "classroom_grades", "grades"
add_foreign_key "classrooms", "school_years"
add_foreign_key "grade_books", "classrooms"
add_foreign_key "grade_books", "quarters"
add_foreign_key "grade_entries", "grade_books"
add_foreign_key "grade_entries", "users"
add_foreign_key "orders", "portfolio_stocks"
add_foreign_key "orders", "portfolio_transactions"
add_foreign_key "orders", "stocks"
add_foreign_key "orders", "users"
add_foreign_key "portfolio_snapshots", "portfolios"
add_foreign_key "portfolio_stocks", "portfolios"
add_foreign_key "portfolio_stocks", "stocks"
add_foreign_key "portfolio_transactions", "portfolios"
add_foreign_key "portfolios", "users"
add_foreign_key "quarters", "school_years", on_delete: :cascade
add_foreign_key "school_years", "schools"
add_foreign_key "school_years", "years"
add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "teacher_classrooms", "classrooms"
add_foreign_key "teacher_classrooms", "users", column: "teacher_id"
add_foreign_key "users", "classrooms"
end

View File

@@ -0,0 +1,18 @@
# Do not add any seed command to this file. Instead, go to /db/seeds/ where you
# will find:
# - development.rb
# - production.rb
#
# Add your seed code into one of those depending on the environment where you wish
# for it to execute.
#
# The intent is to keep seeds separated by environment to avoid any accidental
# execution of code in Production that was meant for Development.
#
# In addition, seeds are split into multiple files (partials) to keep things
# organised. For more information, see the docoumentation at: docs/seeds.md
puts "Starting database seed."
puts "Current environment is: #{Rails.env.downcase}"
load(Rails.root.join('db', 'seeds', "#{Rails.env.downcase}.rb"))

View File

@@ -0,0 +1,75 @@
# Add the seed actions for the development environment to this file.
puts "Development seed running."
start_time = Time.now
puts ""
# ---------------------- Add your seed code in this block ----------------------
# The order of these seed files can be important due to model dependencies.
# Create years
puts "Creating years..."
load(Rails.root.join("db", "seeds", "partials", "years.rb"))
puts "...done."
# Create schools
puts "Creating schools..."
load(Rails.root.join("db", "seeds", "partials", "schools.rb"))
puts "...done."
# Create school years
puts "Creating school years..."
load(Rails.root.join("db", "seeds", "partials", "school_years.rb"))
puts "...done."
# Create grades
puts "Creating grades..."
load(Rails.root.join("db", "seeds", "partials", "grades.rb"))
puts "...done."
# Create quarters
puts "Creating quarters..."
load(Rails.root.join("db", "seeds", "partials", "quarters.rb"))
puts "...done."
# Create classrooms
puts "Creating classrooms..."
load(Rails.root.join("db", "seeds", "partials", "classrooms.rb"))
puts "...done."
# Create users
puts "Creating users..."
load(Rails.root.join("db", "seeds", "partials", "users.rb"))
puts "...done."
# Create stocks
puts "Creating stocks..."
load(Rails.root.join("db", "seeds", "partials", "stocks.rb"))
puts "...done."
# Create portfolio transactions
puts "Creating portfolio transactions..."
load(Rails.root.join("db", "seeds", "partials", "portfolio_transactions.rb"))
puts "...done."
# Create grade books and grade entries
puts "Creating grade books and entries..."
load(Rails.root.join("db", "seeds", "partials", "grade_books_entries.rb"))
puts "...done."
# Create orders
puts "Creating orders..."
load(Rails.root.join("db", "seeds", "partials", "orders.rb"))
puts "...done."
# Create portfolio snapshots
puts "Creating portfolio snapshots..."
load(Rails.root.join("db", "seeds", "partials", "portfolio_snapshots.rb"))
puts "...done."
# ------------------------------------------------------------------------------
end_time = Time.now
run_time = end_time - start_time
puts "Development seed completed in #{run_time} seconds."

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
grade = Grade.find_by!(level: 6)
classroom = Classroom.find_or_initialize_by(name: "Smith's Sixth Grade", school_year: SchoolYear.first)
classroom.trading_enabled = true
classroom.grades << grade unless classroom.grades.include?(grade)
classroom.save!

View File

@@ -0,0 +1,28 @@
# For each quarter, spin up a GradeBook for this classroom
school_years = SchoolYear.first
classroom = Classroom.first
Quarter
.where(school_year: school_years)
.order(:number)
.each do |quarter|
book = GradeBook.find_or_create_by!(
quarter: quarter,
classroom: classroom
)
puts "Seeded GradeBook for #{quarter.name}"
Rails.logger.info "Seeded GradeBook for #{quarter.name}"
# And for each student in that classroom, create a blank entry
classroom
.users
.where(type: "Student")
.find_each do |student|
GradeEntry.find_or_create_by!(
grade_book: book,
user: student
)
puts "Seeded GradeEntry for student #{student.username}"
Rails.logger.info "Seeded GradeEntry for student #{student.username}"
end
end

View File

@@ -0,0 +1,14 @@
# Create grade levels 5-8
Classroom::GRADE_RANGE.each do |level|
# Create the grade name with proper ordinal (5th, 6th, 7th, etc.)
grade_name = "#{level.ordinalize} Grade"
grade = Grade.find_or_create_by(level: level) do |g|
g.name = grade_name
end
# Update name if it's not following the convention
if grade.persisted? && grade.name != grade_name
grade.update!(name: grade_name)
end
end

View File

@@ -0,0 +1,129 @@
# frozen_string_literal: true
# Pending buy orders
student = User.find_by(email: "student@example.com")
mike = User.find_by(email: "mike@example.com")
students = [student, mike].compact
if students.any?
stocks = Stock.limit(5)
students.each do |student_user|
if student_user&.portfolio
portfolio_balance = student_user.portfolio.cash_balance || 0
if portfolio_balance > 100
2.times do |i|
stock = stocks[i]
if stock
next unless stock.price_cents
shares = 1
cost = stock.price_cents / 100.0
if cost * shares <= portfolio_balance
existing_order = Order.find_by(
user: student_user,
stock: stock,
shares: shares,
status: :pending,
action: :buy
)
unless existing_order
Order.create!(
user: student_user,
stock: stock,
shares: shares,
status: :pending,
action: :buy
)
puts "Created pending buy order: #{student_user.email} - #{shares} shares of #{stock.ticker}"
Rails.logger.info "Seeded pending buy order for #{student_user.email}: #{shares} shares of #{stock.ticker}"
else
puts "Pending buy order already exists: #{student_user.email} - #{shares} shares of #{stock.ticker}"
end
end
end
end
end
end
end
end
# Pending sell orders
students.each do |student_user|
if student_user&.portfolio
existing_portfolio_stock = PortfolioStock.joins(:portfolio)
.where(portfolio: { user: student_user })
.first
if existing_portfolio_stock && existing_portfolio_stock.shares > 0
safe_shares = [existing_portfolio_stock.shares.to_i, 1].min
if safe_shares > 0
existing_order = Order.find_by(
user: student_user,
stock: existing_portfolio_stock.stock,
shares: safe_shares,
status: :pending,
action: :sell
)
unless existing_order
Order.create!(
user: student_user,
stock: existing_portfolio_stock.stock,
shares: safe_shares,
status: :pending,
action: :sell
)
puts "Created pending sell order: #{student_user.email} - #{safe_shares} shares of #{existing_portfolio_stock.stock.ticker}"
Rails.logger.info "Seeded pending sell order for #{student_user.email}: #{safe_shares} shares of #{existing_portfolio_stock.stock.ticker}"
else
puts "Pending sell order already exists: #{student_user.email} - #{safe_shares} shares of #{existing_portfolio_stock.stock.ticker}"
end
end
end
end
end
# Canceled orders
if students.any?
stocks = Stock.limit(5)
students.each do |student_user|
if student_user&.portfolio
[1, 2].sample.times do |i|
stock = stocks[i + 2]
if stock
shares = [1, 2].sample
existing_order = Order.find_by(
user: student_user,
stock: stock,
shares: shares,
status: :canceled,
action: :sell
)
unless existing_order
order = Order.new(
user: student_user,
stock: stock,
shares: shares,
action: :sell
)
order.status = :canceled
order.save!(validate: false)
puts "Created canceled order: #{student_user.email} - #{shares} shares of #{stock.ticker}"
Rails.logger.info "Seeded canceled order for #{student_user.email}: #{shares} shares of #{stock.ticker}"
else
puts "Canceled order already exists: #{student_user.email} - #{shares} shares of #{stock.ticker}"
end
end
end
end
end
else
puts "No student users found. Skipping orders seeding."
Rails.logger.warn "No student users found. Skipping orders seeding."
end

View File

@@ -0,0 +1,42 @@
# frozen_string_literal: true
mike = User.find_by(email: "mike@example.com")
if mike
portfolio = Portfolio.find_by(user: mike)
if portfolio
# Create 6 months of historical snapshots with realistic value progression
# Starting 5 months ago and ending with current month
base_value = 150_00 # $150.00 starting value
snapshots_data = [
{ months_ago: 5, worth_cents: base_value },
{ months_ago: 4, worth_cents: base_value + 50_00 }, # +$50
{ months_ago: 3, worth_cents: base_value + 120_00 }, # +$70
{ months_ago: 2, worth_cents: base_value + 80_00 }, # -$40 (dip)
{ months_ago: 1, worth_cents: base_value + 180_00 }, # +$100 (recovery)
{ months_ago: 0, worth_cents: base_value + 250_00 } # +$70 (growth)
]
snapshots_data.each do |data|
snapshot_date = data[:months_ago].months.ago.beginning_of_month.to_date
PortfolioSnapshot.find_or_create_by!(
portfolio: portfolio,
date: snapshot_date
) do |snapshot|
snapshot.worth_cents = data[:worth_cents]
end
end
puts "Seeded #{snapshots_data.length} portfolio snapshots for Mike's portfolio"
Rails.logger.info "Seeded #{snapshots_data.length} portfolio snapshots for Mike's portfolio"
else
puts "Portfolio not found for Mike. Skipping portfolio snapshots seeding."
Rails.logger.warn "Portfolio not found for Mike. Skipping portfolio snapshots seeding."
end
else
puts "Student user 'Mike' not found. Skipping portfolio snapshots seeding."
Rails.logger.warn "Student user 'Mike' not found. Skipping portfolio snapshots seeding."
end

View File

@@ -0,0 +1,142 @@
# frozen_string_literal: true
# Create transactions for Student user
student = User.find_by(email: "student@example.com")
if student
portfolio = Portfolio.find_or_create_by(user: student)
# Initial deposit to give student starting balance
PortfolioTransaction.create(
portfolio: portfolio,
transaction_type: :deposit,
amount_cents: 10_000_00
)
# Add earnings transactions with reasons for testing
PortfolioTransaction.create(
portfolio: portfolio,
transaction_type: :deposit,
amount_cents: 500_00,
reason: :attendance_earnings
)
PortfolioTransaction.create(
portfolio: portfolio,
transaction_type: :deposit,
amount_cents: 500_00,
reason: :reading_earnings
)
PortfolioTransaction.create(
portfolio: portfolio,
transaction_type: :deposit,
amount_cents: 100_00,
reason: :math_earnings
)
PortfolioTransaction.create(
portfolio: portfolio,
transaction_type: :deposit,
amount_cents: 50_00,
reason: :awards
)
PortfolioTransaction.create(
portfolio: portfolio,
transaction_type: :deposit,
amount_cents: 25_00,
reason: :transaction_fees
)
puts "Seeded portfolio transactions for Student user"
Rails.logger.info "Seeded portfolio transactions for Student user"
else
puts "Student user not found. Skipping Student portfolio transactions seeding."
Rails.logger.warn "Student user not found. Skipping Student portfolio transactions seeding."
end
# Create transactions for Mike user
mike = User.find_by(email: "mike@example.com")
if mike
portfolio = Portfolio.find_or_create_by(user: mike)
pt = PortfolioTransaction.create(
portfolio: portfolio,
transaction_type: :deposit,
amount_cents: 15_000_00
)
stocks = Stock.limit(3)
shares = 2
stocks.each do |stock|
next unless stock.price_cents
total_price_cents = (stock.price_cents * shares)
order = Order.create!(
user: mike,
stock: stock,
shares: shares,
status: :pending,
portfolio_transaction: pt,
action: :buy
)
ExecuteOrder.execute(order)
end
shares = 1
stocks.each do |stock|
next unless stock.price_cents
total_price_cents = (stock.price_cents * shares)
order = Order.create!(
user: mike,
stock: stock,
shares: shares,
status: :pending,
portfolio_transaction: pt,
action: :sell
)
ExecuteOrder.execute(order)
end
pt = PortfolioTransaction.create(
portfolio: portfolio,
transaction_type: :withdrawal,
amount_cents: 100_00
)
pt = PortfolioTransaction.create(
portfolio: portfolio,
transaction_type: :deposit,
amount_cents: 10_000_00
)
existing_stock_ids = Order.where(user: mike, action: :buy).pluck(:stock_id).uniq
stocks = Stock.where.not(id: existing_stock_ids).limit(1)
stocks.each do |stock|
next unless stock.price_cents
shares = 1
total_price_cents = (stock.price_cents * shares)
order = Order.create!(
user: mike,
stock: stock,
shares: shares,
status: :pending,
portfolio_transaction: pt,
action: :buy
)
ExecuteOrder.execute(order)
end
puts "Seeded three completed orders and transactions for the Student user 'Mike'"
Rails.logger.info "Seeded three completed orders and transactions for the Student user 'Mike"
else
puts "Student user 'Mike' not found. Skipping portfolio transactions seeding."
Rails.logger.warn "Student user 'Mike' not found. Skipping portfolio transactions seeding."
end

View File

@@ -0,0 +1,17 @@
# Create 4 quarters for the first school year
school_year = SchoolYear.find_by(school: School.first, year: Year.first)
if school_year
(1..4).each do |n|
Quarter.find_or_create_by!(
school_year: school_year,
number: n
) do |q|
q.update(name: "Quarter #{n}")
end
end
puts "Quarters created for school year: #{school_year.year.name} at #{school_year.school.name}."
Rails.logger.info "Created 4 quarters for school year: #{school_year.year.name} at #{school_year.school.name}."
else
puts "No current school year found. Skipping quarter creation."
end

View File

@@ -0,0 +1 @@
SchoolYear.find_or_create_by!(school: School.first, year: Year.first)

View File

@@ -0,0 +1 @@
School.find_or_create_by(name: "Test School")

View File

@@ -0,0 +1,35 @@
# frozen_string_literal: true
active_seed_stocks = [
{ ticker: "BAC", stock_exchange: "NYSE", company_name: "Bank of America Corporation", company_website: "https://www.bankofamerica.com", description: "Bank of America Corporation operates as a bank holding company for Bank of America, N.A., which provides various banking and financial products and services.", industry: "Banks", management: "Brian Moynihan (CEO), Alastair Borthwick (CFO)", employees: 216_823, competitor_names: "JPMorgan Chase, Wells Fargo, Citigroup, Goldman Sachs", sales_growth: 2.5, industry_avg_sales_growth: 4.1, debt_to_equity: 1.15, industry_avg_debt_to_equity: 1.25, profit_margin: 32.1, industry_avg_profit_margin: 28.5, cash_flow: 31_200_000_000.00, debt: 294_000_000_000.00, price_cents: 4_512 },
{ ticker: "CCL", stock_exchange: "NYSE", company_name: "Carnival Corporation & plc", company_website: "https://www.carnival.com", description: "Carnival Corporation & plc operates as a leisure travel company providing cruise vacations and complementary vacation services.", industry: "Travel Services", management: "Josh Weinstein (CEO), David Bernstein (CFO)", employees: 120_000, competitor_names: "Royal Caribbean, Norwegian Cruise Line, MSC Cruises", sales_growth: 15.2, industry_avg_sales_growth: 8.7, debt_to_equity: 3.45, industry_avg_debt_to_equity: 1.85, profit_margin: -2.1, industry_avg_profit_margin: 5.8, cash_flow: 3_800_000_000.00, debt: 28_500_000_000.00, price_cents: 2_185 },
{ ticker: "F", stock_exchange: "NYSE", company_name: "Ford Motor Company", company_website: "https://www.ford.com", description: "Ford Motor Company designs, manufactures, markets, and services a full line of Ford trucks, utility vehicles, and cars worldwide.", industry: "Automobiles", management: "Jim Farley (CEO), John Lawler (CFO)", employees: 190_000, competitor_names: "General Motors, Tesla, Toyota, Stellantis", sales_growth: 11.8, industry_avg_sales_growth: 6.4, debt_to_equity: 1.85, industry_avg_debt_to_equity: 0.95, profit_margin: 3.7, industry_avg_profit_margin: 7.2, cash_flow: 11_500_000_000.00, debt: 96_000_000_000.00, price_cents: 1_177 },
{ ticker: "IBIT", stock_exchange: "NASDAQ", company_name: "iShares Bitcoin Trust ETF", company_website: "https://www.ishares.com", description: "iShares Bitcoin Trust provides exposure to bitcoin by holding bitcoin and seeks to reflect the performance of bitcoin.", industry: "Exchange Traded Funds", management: "BlackRock Fund Advisors (Sponsor), Samara Cohen (Chief Investment Officer)", employees: 0, competitor_names: "Grayscale Bitcoin Trust, Fidelity Wise Origin Bitcoin Fund, ARK 21Shares Bitcoin ETF", sales_growth: 0.0, industry_avg_sales_growth: 0.0, debt_to_equity: 0.0, industry_avg_debt_to_equity: 0.0, profit_margin: 0.0, industry_avg_profit_margin: 0.0, cash_flow: 0.00, debt: 0.00, price_cents: 4_125 },
{ ticker: "KO", stock_exchange: "NYSE", company_name: "The Coca-Cola Company", company_website: "https://www.coca-colacompany.com", description: "The Coca-Cola Company manufactures, retails, and markets nonalcoholic beverage concentrates and syrups worldwide.", industry: "Beverages", management: "James Quincey (CEO), John Murphy (CFO)", employees: 86_200, competitor_names: "PepsiCo, Dr Pepper Snapple Group, Monster Beverage", sales_growth: 11.2, industry_avg_sales_growth: 5.50, debt_to_equity: 1.65, industry_avg_debt_to_equity: 0.85, profit_margin: 25.4, industry_avg_profit_margin: 12.50, cash_flow: 10_800_000_000.00, debt: 37_000_000_000.00, price_cents: 6_899 },
{ ticker: "KHC", stock_exchange: "NASDAQ", company_name: "The Kraft Heinz Company", company_website: "https://www.kraftheinzcompany.com", description: "The Kraft Heinz Company manufactures and markets food and beverage products in the United States and internationally.", industry: "Food Products", management: "Carlos Abrams-Rivera (CEO), Andre Maciel (CFO)", employees: 38_000, competitor_names: "Unilever, Nestle, General Mills, Campbell Soup", sales_growth: -1.2, industry_avg_sales_growth: 3.8, debt_to_equity: 0.87, industry_avg_debt_to_equity: 0.65, profit_margin: 8.9, industry_avg_profit_margin: 11.2, cash_flow: 4_200_000_000.00, debt: 27_800_000_000.00, price_cents: 3_654 },
{ ticker: "NKE", stock_exchange: "NYSE", company_name: "Nike, Inc.", company_website: "https://www.nike.com", description: "Nike, Inc. designs, develops, markets, and sells athletic footwear, apparel, equipment, and accessories worldwide.", industry: "Footwear & Accessories", management: "John Donahoe (CEO), Matthew Friend (CFO)", employees: 83_700, competitor_names: "Adidas, Under Armour, Puma, New Balance", sales_growth: 0.3, industry_avg_sales_growth: 4.2, debt_to_equity: 0.68, industry_avg_debt_to_equity: 0.55, profit_margin: 12.9, industry_avg_profit_margin: 9.8, cash_flow: 6_900_000_000.00, debt: 9_600_000_000.00, price_cents: 7_521 },
{ ticker: "MAT", stock_exchange: "NASDAQ", company_name: "Mattel, Inc.", company_website: "https://www.mattel.com", description: "Mattel, Inc. designs, manufactures, and markets various toy products worldwide under the Mattel, Fisher-Price, American Girl, Thomas & Friends, UNO, and Hot Wheels brands.", industry: "Leisure Products", management: "Ynon Kreiz (CEO), Anthony DiSilvestro (CFO)", employees: 33_000, competitor_names: "Hasbro, LEGO, Spin Master, MGA Entertainment", sales_growth: -1.5, industry_avg_sales_growth: 2.8, debt_to_equity: 2.85, industry_avg_debt_to_equity: 1.25, profit_margin: 17.2, industry_avg_profit_margin: 8.9, cash_flow: 1_100_000_000.00, debt: 2_900_000_000.00, price_cents: 1_892 },
{ ticker: "SONY", stock_exchange: "NYSE", company_name: "Sony Group Corporation", company_website: "https://www.sony.com", description: "Sony Group Corporation operates as a technology and media company that develops, produces, manufactures, and sells various products worldwide.", industry: "Consumer Electronics", management: "Kenichiro Yoshida (CEO), Hiroki Totoki (CFO)", employees: 108_900, competitor_names: "Samsung, Apple, Nintendo, Microsoft", sales_growth: 4.8, industry_avg_sales_growth: 6.2, debt_to_equity: 0.35, industry_avg_debt_to_equity: 0.45, profit_margin: 13.2, industry_avg_profit_margin: 8.9, cash_flow: 8_500_000_000.00, debt: 15_200_000_000.00, price_cents: 2_752 },
{ ticker: "VZ", stock_exchange: "NYSE", company_name: "Verizon Communications Inc.", company_website: "https://www.verizon.com", description: "Verizon Communications Inc., through its subsidiaries, offers communications, technology, information, and entertainment products and services to consumers, businesses, and governmental entities worldwide.", industry: "Telecommunications Services", management: "Hans Vestberg (CEO), Matt Ellis (CFO)", employees: 117_100, competitor_names: "AT&T, T-Mobile, Comcast, Charter Communications", sales_growth: 2.1, industry_avg_sales_growth: 3.8, debt_to_equity: 1.8, industry_avg_debt_to_equity: 1.5, profit_margin: 17.2, industry_avg_profit_margin: 12.4, cash_flow: 23_700_000_000.00, debt: 176_200_000_000.00, price_cents: 4_423 },
]
active_seed_stocks.each do |stock_data|
stock = Stock.find_or_create_by(ticker: stock_data[:ticker])
stock.update!(stock_data)
end
archived_seed_stocks = [
{ ticker: "DIS", company_name: "Disney" },
{ ticker: "EA", company_name: "Electronic Arts" },
{ ticker: "GAP", company_name: "Gap" },
{ ticker: "LUV", company_name: "Southwest Airlines" },
{ ticker: "META", company_name: "Facebook" },
{ ticker: "SIRI", company_name: "SiriusXM" },
{ ticker: "TWX", company_name: "Time Warner" },
{ ticker: "UA", company_name: "Under Armour" },
]
archived_seed_stocks.each do |stock_data|
stock = Stock.find_or_create_by(ticker: stock_data[:ticker])
stock.update!(stock_data.merge(archived: true))
end

View File

@@ -0,0 +1,72 @@
# Teacher
teacher = User.find_or_initialize_by(email: "teacher@example.com")
unless teacher.persisted?
teacher.attributes = {
name: "Teacher Name",
password: "password",
password_confirmation: "password",
admin: false,
type: "Teacher",
classroom: Classroom.first
}
teacher.save
puts "Created Teacher user: #{teacher.email}"
Rails.logger.info "Seeded Teacher user: #{teacher.email}"
else
puts "Teacher user already exists: #{teacher.email}"
end
# Student
student = User.find_or_initialize_by(email: "student@example.com")
unless student.persisted?
student.attributes = {
username: "Student",
password: "password",
password_confirmation: "password",
admin: false,
type: "Student",
classroom: Classroom.first
}
student.save
puts "Created Student user: #{student.email}"
Rails.logger.info "Seeded Student user: #{student.email}"
else
puts "Student user already exists: #{student.email}"
end
# Admin
admin = User.find_or_initialize_by(email: "admin@example.com")
unless admin.persisted?
admin.attributes = {
username: "Admin",
password: "password",
password_confirmation: "password",
admin: true,
type: "User",
classroom: Classroom.first
}
admin.save
puts "Created Admin user: #{admin.email}"
Rails.logger.info "Seeded Admin user: #{admin.email}"
else
puts "Admin user already exists: #{admin.email}"
end
# Student "mike" to who the portfolio transactions will belong
mike = User.find_or_initialize_by(email: "mike@example.com")
unless mike.persisted?
mike.attributes = {
username: "mike",
email: "mike@example.com",
password: "password",
password_confirmation: "password",
admin: false,
type: "Student",
classroom: Classroom.first
}
mike.save
puts "Created portfolio transactions user: #{mike.email}"
Rails.logger.info "Seeded user: #{mike.email}"
else
puts "Mike already exists: #{mike.email}"
end

View File

@@ -0,0 +1,8 @@
current_year = Date.current.year
start_year = current_year - 3
end_year = current_year + 10
(start_year..end_year).each do |i|
year = Year.find_or_create_by(name: "#{i} - #{i + 1}")
year.save!
end

View File

@@ -0,0 +1,28 @@
# Add the seed actions for the production environment to this file.
puts "Production seed running."
start_time = Time.now
puts ""
# ---------------------- Add your seed code in this block ----------------------
# Create years
puts "Creating years..."
load(Rails.root.join("db", "seeds", "partials", "years.rb"))
puts "...done."
# Create grades
puts "Creating grades..."
load(Rails.root.join("db", "seeds", "partials", "grades.rb"))
puts "...done."
# Create stocks
puts "Creating stocks..."
load(Rails.root.join("db", "seeds", "partials", "stocks.rb"))
puts "...done."
# ------------------------------------------------------------------------------
end_time = Time.now
run_time = end_time - start_time
puts "Production seed completed in #{run_time} seconds."

View File

@@ -0,0 +1,3 @@
# Staging environment uses the same seeds as development
puts "Staging environment detected - loading development seeds"
load(Rails.root.join('db', 'seeds', 'development.rb'))

View File

@@ -0,0 +1 @@
puts "No seeds added for this environment."