Skip to content

Commit c3667b8

Browse files
committed
fix: solve match fingerprint mismatch
1 parent 1e36c3b commit c3667b8

4 files changed

Lines changed: 35 additions & 30 deletions

File tree

app/modules/competitive/concerns/match_fingerprint.rb

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,40 @@
66
# produce different external_match_id values for the same physical game. This
77
# module derives a source-agnostic fingerprint so duplicates are caught before
88
# they are persisted.
9-
module MatchFingerprint
10-
# Generates a stable fingerprint for a physical game based on attributes that
11-
# are source-agnostic.
12-
#
13-
# @param org_id [String] organization UUID
14-
# @param match_date [DateTime, nil]
15-
# @param game_number [Integer, nil] game within the series (1-5)
16-
# @param opponent_name [String, nil]
17-
# @return [String, nil] SHA-256 hex string, or nil when inputs are insufficient
18-
def generate_fingerprint(org_id, match_date, game_number, opponent_name)
19-
return nil if match_date.nil? || opponent_name.nil? || opponent_name.strip.empty?
9+
module Competitive
10+
module Concerns
11+
# Shared fingerprinting logic for deduplicating competitive match imports.
12+
module MatchFingerprint
13+
# Generates a stable fingerprint for a physical game based on attributes that
14+
# are source-agnostic.
15+
#
16+
# @param org_id [String] organization UUID
17+
# @param match_date [DateTime, nil]
18+
# @param game_number [Integer, nil] game within the series (1-5)
19+
# @param opponent_name [String, nil]
20+
# @return [String, nil] SHA-256 hex string, or nil when inputs are insufficient
21+
def generate_fingerprint(org_id, match_date, game_number, opponent_name)
22+
return nil if match_date.nil? || opponent_name.nil? || opponent_name.strip.empty?
2023

21-
day = match_date.to_date.to_s
22-
normalized = opponent_name.strip.downcase
23-
Digest::SHA256.hexdigest("#{org_id}|#{day}|#{game_number || 1}|#{normalized}")
24-
end
24+
day = match_date.to_date.to_s
25+
normalized = opponent_name.strip.downcase
26+
Digest::SHA256.hexdigest("#{org_id}|#{day}|#{game_number || 1}|#{normalized}")
27+
end
2528

26-
# Returns true if a record with this fingerprint already exists for the org.
27-
# Skips the check when the fingerprint cannot be computed (missing inputs).
28-
#
29-
# @param organization [Organization]
30-
# @param match_date [DateTime, nil]
31-
# @param game_number [Integer, nil]
32-
# @param opponent_name [String, nil]
33-
# @return [Boolean]
34-
def duplicate_by_fingerprint?(organization, match_date, game_number, opponent_name)
35-
fp = generate_fingerprint(organization.id, match_date, game_number, opponent_name)
36-
return false if fp.nil?
29+
# Returns true if a record with this fingerprint already exists for the org.
30+
# Skips the check when the fingerprint cannot be computed (missing inputs).
31+
#
32+
# @param organization [Organization]
33+
# @param match_date [DateTime, nil]
34+
# @param game_number [Integer, nil]
35+
# @param opponent_name [String, nil]
36+
# @return [Boolean]
37+
def duplicate_by_fingerprint?(organization, match_date, game_number, opponent_name)
38+
fp = generate_fingerprint(organization.id, match_date, game_number, opponent_name)
39+
return false if fp.nil?
3740

38-
organization.competitive_matches.where(game_fingerprint: fp).exists?
41+
organization.competitive_matches.where(game_fingerprint: fp).exists?
42+
end
43+
end
3944
end
4045
end

app/modules/competitive/services/leaguepedia_recovery_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# # => { recovered: 1, already_present: 12, errors: 0, skipped_no_players: 0 }
2121
#
2222
class LeaguepediaRecoveryService
23-
include MatchFingerprint
23+
include Competitive::Concerns::MatchFingerprint
2424

2525
CARGO_BASE_URL = 'https://lol.fandom.com/api.php'
2626
CACHE_TTL = 30.minutes

app/modules/competitive/services/scraper_importer_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# # => { imported: 5, skipped_duplicate: 3, skipped_unenriched: 2, errors: 0 }
1313
#
1414
class ScraperImporterService
15-
include MatchFingerprint
15+
include Competitive::Concerns::MatchFingerprint
1616

1717
# Leaguepedia role values mapped to our internal lowercase convention
1818
ROLE_MAP = {

config/application.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class Application < Rails::Application
4444
# Serializers, policies, channels, and services keep their original flat class names.
4545
# Adding their dirs as roots (same pattern as models) avoids renaming every
4646
# constant: PlayerSerializer, PlayerPolicy, RiotApiService, etc. stay as-is.
47-
%w[serializers policies channels services concerns].each do |layer|
47+
%w[serializers policies channels services].each do |layer|
4848
Dir[root.join("app/modules/*/#{layer}")].each do |path|
4949
config.autoload_paths << path
5050
config.eager_load_paths << path

0 commit comments

Comments
 (0)