diff --git a/app/models/nc_mall_record.rb b/app/models/nc_mall_record.rb new file mode 100644 index 00000000..657e7379 --- /dev/null +++ b/app/models/nc_mall_record.rb @@ -0,0 +1,3 @@ +class NCMallRecord < ApplicationRecord + belongs_to :item +end diff --git a/app/services/nc_mall.rb b/app/services/nc_mall.rb index a2216e58..b4168a1d 100644 --- a/app/services/nc_mall.rb +++ b/app/services/nc_mall.rb @@ -72,8 +72,8 @@ module NCMall { price: discount_price, - start: item_info["discountBegin"], - end: item_info["discountEnd"], + begins_at: item_info["discountBegin"], + ends_at: item_info["discountEnd"], } end diff --git a/db/migrate/20240507235742_create_nc_mall_records.rb b/db/migrate/20240507235742_create_nc_mall_records.rb new file mode 100644 index 00000000..ffcd2232 --- /dev/null +++ b/db/migrate/20240507235742_create_nc_mall_records.rb @@ -0,0 +1,13 @@ +class CreateNCMallRecords < ActiveRecord::Migration[7.1] + def change + create_table :nc_mall_records do |t| + t.references :item, type: :integer, null: false, foreign_key: true + t.integer :price, null: false + t.integer :discount_price + t.datetime :discount_begins_at + t.datetime :discount_ends_at + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 50954354..5fbefe21 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_05_02_195157) do +ActiveRecord::Schema[7.1].define(version: 2024_05_07_235742) do create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t| t.integer "species_id", null: false t.integer "color_id", null: false @@ -154,6 +154,17 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_02_195157) do t.string "pet_name", limit: 128, null: false end + create_table "nc_mall_records", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| + t.integer "item_id", null: false + t.integer "price", null: false + t.integer "discount_price" + t.datetime "discount_begins_at" + t.datetime "discount_ends_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["item_id"], name: "index_nc_mall_records_on_item_id" + end + create_table "neopets_connections", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t| t.integer "user_id" t.string "neopets_username" @@ -278,5 +289,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_02_195157) do add_foreign_key "alt_styles", "colors" add_foreign_key "alt_styles", "species" + add_foreign_key "nc_mall_records", "items" add_foreign_key "outfits", "alt_styles" end diff --git a/lib/tasks/nc_mall.rake b/lib/tasks/nc_mall.rake new file mode 100644 index 00000000..85a3e8bc --- /dev/null +++ b/lib/tasks/nc_mall.rake @@ -0,0 +1,60 @@ +namespace :nc_mall do + desc "Sync our NCMallRecord table with the live NC Mall" + task :sync => :environment do + # Log to STDOUT. + Rails.logger = Logger.new(STDOUT) + + # First, load all records of what's being sold in the live NC Mall. + # TODO: Load from other pages, too! + live_item_records = NCMall.load_home_page[:items] + + # Then, get the existing NC Mall records in our database. (We include the + # items, to be able to output the item name during logging.) + existing_records = NCMallRecord.includes(:item).all + existing_records_by_item_id = existing_records.to_h { |r| [r.item_id, r] } + + # Additionally, check which of the item IDs in the live records are items + # we've seen before. (We'll skip records for items we don't know.) + live_item_ids = live_item_records.map { |r| r[:id] } + recognized_item_ids = Item.where(id: live_item_ids).pluck(:id).to_set + Rails.logger.debug "We recognize #{recognized_item_ids.size} of these items" + + # For each record in the live NC Mall, check if there's an existing record. + # If so, update it, and remove it from the existing records hash. If not, + # create it. + live_item_records.each do |record_data| + # If we don't recognize this item ID in our database already, skip it. + next unless recognized_item_ids.include?(record_data[:id]) + + record = existing_records_by_item_id.delete(record_data[:id]) || + NCMallRecord.new + record.item_id = record_data[:id] + record.price = record_data[:price] + record.discount_price = record_data.dig(:discount, :price) + record.discount_begins_at = record_data.dig(:discount, :begins_at) + record.discount_ends_at = record_data.dig(:discount, :ends_at) + if record.save + Rails.logger.info "Saved record for item #{record_data[:name]}" + else + Rails.logger.error "Failed to save record for item " + + "#{record_data[:name]}: " + + "#{record.errors.full_messages.join("; ")}: " + + "#{record.inspect}" + end + end + + # For each existing record remaining in the existing records hash, this + # means there was no live record corresponding to it during this sync. + # Delete it! + existing_records_by_item_id.values.each do |record| + item_name = record.item&.name || "" + if record.destroy + Rails.logger.info "Destroyed record #{record.id} for item " + + "#{item_name}" + else + Rails.logger.error "Failed to destroy record #{record.id} for " + + "item #{item_name}: #{record.inspect}" + end + end + end +end