From b6e18e10a56c0a6a148d99ce6b9a134296b53e76 Mon Sep 17 00:00:00 2001 From: Matchu Date: Tue, 7 May 2024 17:40:14 -0700 Subject: [PATCH] Add bare-bones `rails nc_mall:sync` task, incl. NCMallRecord model Currently we only load the homepage, so there's only actually one wearable item to sync up! But here's the task to do it! To do this, we also created the backing model NCMallRecord, where we'll save the current NC Mall state! --- app/models/nc_mall_record.rb | 3 + app/services/nc_mall.rb | 4 +- .../20240507235742_create_nc_mall_records.rb | 13 ++++ db/schema.rb | 14 ++++- lib/tasks/nc_mall.rake | 60 +++++++++++++++++++ 5 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 app/models/nc_mall_record.rb create mode 100644 db/migrate/20240507235742_create_nc_mall_records.rb create mode 100644 lib/tasks/nc_mall.rake 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