mirror of
https://github.com/ryankazokas/turbovault-app.git
synced 2026-04-16 22:12:53 +00:00
149 lines
4.3 KiB
Ruby
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
|