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!
This commit is contained in:
Emi Matchu 2024-05-07 17:40:14 -07:00
parent 1f157b49da
commit b6e18e10a5
5 changed files with 91 additions and 3 deletions

View file

@ -0,0 +1,3 @@
class NCMallRecord < ApplicationRecord
belongs_to :item
end

View file

@ -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

View file

@ -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

View file

@ -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

60
lib/tasks/nc_mall.rake Normal file
View file

@ -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 || "<item not found>"
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