game_manager_lib\services\recommendation/
profile.rs

1//! Cálculo de Perfil de Usuário
2//!
3//! Este módulo é responsável por calcular o vetor de preferências do usuário
4//! baseado em sua biblioteca de jogos.
5
6use super::core::*;
7use crate::utils::tag_utils::{combined_multiplier, TagKey, TagRole};
8use std::collections::{HashMap, HashSet};
9
10/// Calcula o perfil de preferências do usuário baseado em sua biblioteca
11pub fn calculate_user_profile(
12    games: &[GameWithDetails],
13    ignored_ids: &HashSet<String>,
14) -> UserPreferenceVector {
15    let mut genres: HashMap<String, f32> = HashMap::new();
16    let mut tags: HashMap<TagKey, f32> = HashMap::new();
17    let mut series: HashMap<String, f32> = HashMap::new();
18    let mut total_playtime = 0;
19    let total_games = games.len() as i32;
20
21    for game_data in games {
22        if ignored_ids.contains(&game_data.game.id) {
23            continue;
24        }
25
26        let game = &game_data.game;
27        let weight = calculate_game_weight(game);
28        total_playtime += game.playtime.unwrap_or(0);
29
30        // Acumular pesos para gêneros
31        accumulate_genres(&mut genres, &game_data.genres, weight);
32
33        // Acumular pesos para tags
34        accumulate_tags(&mut tags, &game_data.tags, weight);
35
36        // Acumular pesos para séries
37        accumulate_series(&mut series, &game_data.series, weight);
38    }
39
40    UserPreferenceVector {
41        genres,
42        tags,
43        series,
44        total_playtime,
45        total_games,
46    }
47}
48
49// === FUNÇÕES AUXILIARES ===
50
51fn accumulate_genres(genres: &mut HashMap<String, f32>, game_genres: &[String], weight: f32) {
52    for genre in game_genres {
53        *genres.entry(genre.clone()).or_insert(0.0) += weight * WEIGHT_GENRE;
54    }
55}
56
57fn accumulate_tags(
58    tags: &mut HashMap<TagKey, f32>,
59    game_tags: &[crate::models::GameTag],
60    weight: f32,
61) {
62    for tag in game_tags {
63        let key = TagKey::new(tag.category.clone(), tag.slug.clone());
64
65        // Tags de filtro não contribuem para o perfil
66        if tag.role == TagRole::Filter {
67            continue;
68        }
69
70        let tag_weight = weight * tag.relevance * combined_multiplier(&tag.category, &tag.role);
71
72        *tags.entry(key).or_insert(0.0) += tag_weight;
73    }
74}
75
76fn accumulate_series(series: &mut HashMap<String, f32>, game_series: &Option<String>, weight: f32) {
77    if let Some(series_name) = game_series {
78        *series.entry(series_name.clone()).or_insert(0.0) += weight * WEIGHT_SERIES;
79    }
80}