|
6 | 6 | # produce different external_match_id values for the same physical game. This |
7 | 7 | # module derives a source-agnostic fingerprint so duplicates are caught before |
8 | 8 | # 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? |
20 | 23 |
|
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 |
25 | 28 |
|
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? |
37 | 40 |
|
38 | | - organization.competitive_matches.where(game_fingerprint: fp).exists? |
| 41 | + organization.competitive_matches.where(game_fingerprint: fp).exists? |
| 42 | + end |
| 43 | + end |
39 | 44 | end |
40 | 45 | end |
0 commit comments