Skip to main content

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