Files
turbovault-app/app/models/igdb_match_suggestion.rb
2026-03-29 02:37:49 -04:00

149 lines
4.3 KiB
Ruby

class IgdbMatchSuggestion < ApplicationRecord
# Associations
belongs_to :game
belongs_to :igdb_game, optional: true
# Enums
enum :status, {
pending: "pending",
approved: "approved",
rejected: "rejected"
}, prefix: true
# Validations
validates :igdb_id, presence: true
validates :igdb_name, presence: true
validates :game_id, uniqueness: { scope: :igdb_id }
# Scopes
scope :pending_review, -> { status_pending.order(confidence_score: :desc, created_at: :asc) }
scope :for_user, ->(user) { joins(:game).where(games: { user_id: user.id }) }
scope :high_confidence, -> { where("confidence_score >= ?", 80.0) }
# Instance methods
def approve!
transaction do
update!(status: "approved", reviewed_at: Time.current)
# Update the game with the matched IGDB ID
game.update!(
igdb_id: igdb_id,
igdb_matched_at: Time.current,
igdb_match_status: "matched",
igdb_match_confidence: confidence_score
)
# Find or create the IgdbGame record with full data
igdb_game_record = IgdbGame.find_or_create_by!(igdb_id: igdb_id) do |ig|
ig.name = igdb_name
ig.slug = igdb_slug
ig.cover_url = igdb_cover_url
ig.summary = igdb_summary
ig.first_release_date = igdb_release_date
ig.last_synced_at = Time.current
end
# Update summary if it wasn't already set
if igdb_game_record.summary.blank? && igdb_summary.present?
igdb_game_record.update(summary: igdb_summary)
end
igdb_game_record.increment_match_count!
# Map and assign IGDB genres to the game
sync_genres_to_game if igdb_genres.present?
# Reject all other pending suggestions for this game
game.igdb_match_suggestions
.where.not(id: id)
.status_pending
.update_all(
status: "rejected",
reviewed_at: Time.current
)
end
end
def reject!
transaction do
update!(
status: "rejected",
reviewed_at: Time.current
)
# Reject all other pending suggestions for this game
game.igdb_match_suggestions
.where.not(id: id)
.status_pending
.update_all(
status: "rejected",
reviewed_at: Time.current
)
# Mark game as manually reviewed with no match (only if all suggestions rejected)
if game.igdb_match_suggestions.status_pending.none?
game.update!(
igdb_match_status: "no_match",
igdb_matched_at: Time.current
)
end
end
end
def cover_image_url(size = "cover_big")
return nil unless igdb_cover_url.present?
"https://images.igdb.com/igdb/image/upload/t_#{size}/#{igdb_cover_url}.jpg"
end
private
def sync_genres_to_game
return unless igdb_genres.is_a?(Array) && igdb_genres.any?
# Genre mapping: IGDB genre names to our genre names
genre_mappings = {
"Role-playing (RPG)" => "RPG",
"Fighting" => "Fighting",
"Shooter" => "Shooter",
"Music" => "Music",
"Platform" => "Platformer",
"Puzzle" => "Puzzle",
"Racing" => "Racing",
"Real Time Strategy (RTS)" => "Strategy",
"Simulator" => "Simulation",
"Sport" => "Sports",
"Strategy" => "Strategy",
"Turn-based strategy (TBS)" => "Strategy",
"Tactical" => "Strategy",
"Hack and slash/Beat 'em up" => "Action",
"Quiz/Trivia" => "Puzzle",
"Pinball" => "Arcade",
"Adventure" => "Adventure",
"Indie" => "Indie",
"Arcade" => "Arcade",
"Visual Novel" => "Adventure",
"Card & Board Game" => "Puzzle",
"MOBA" => "Strategy",
"Point-and-click" => "Adventure"
}
# Find or create matching genres
igdb_genres.each do |igdb_genre_name|
# Try exact match first
local_genre = Genre.find_by("LOWER(name) = ?", igdb_genre_name.downcase)
# Try mapped name
if local_genre.nil? && genre_mappings[igdb_genre_name]
mapped_name = genre_mappings[igdb_genre_name]
local_genre = Genre.find_by("LOWER(name) = ?", mapped_name.downcase)
end
# Add genre to game if found and not already assigned
if local_genre && !game.genres.include?(local_genre)
game.genres << local_genre
Rails.logger.info("Added genre '#{local_genre.name}' to game #{game.id} from IGDB")
end
end
end
end