2025-08-01 22:10:59 +02:00
|
|
|
import random
|
2025-08-01 18:46:38 +02:00
|
|
|
from dataclasses import dataclass
|
|
|
|
|
2025-08-02 07:59:50 +02:00
|
|
|
from song import Song
|
2025-08-01 18:46:38 +02:00
|
|
|
|
2025-08-01 19:13:09 +02:00
|
|
|
USER_SCORE_WEIGHT = 0.7
|
2025-08-01 19:31:47 +02:00
|
|
|
ARTIST_WEIGHT = 0.1
|
|
|
|
TAG_WEIGHT = 0.1
|
2025-08-01 19:13:09 +02:00
|
|
|
RANDOM_WEIGHT = 0.1
|
|
|
|
RECENT_PENALTY = 0.5
|
|
|
|
RECENT_COUNT = 10
|
2025-08-01 22:48:38 +02:00
|
|
|
QUEUE_SIZE = 3
|
2025-08-01 19:13:09 +02:00
|
|
|
|
|
|
|
|
2025-08-01 19:31:47 +02:00
|
|
|
type UserScoredSong = tuple[Song, int]
|
2025-08-01 18:46:38 +02:00
|
|
|
|
|
|
|
|
2025-08-01 22:10:59 +02:00
|
|
|
@dataclass
|
|
|
|
class Rank:
|
|
|
|
user_score: float
|
|
|
|
tag: float
|
|
|
|
artist: float
|
|
|
|
random: float
|
|
|
|
recent_penalty: float
|
|
|
|
|
|
|
|
def total(self) -> float:
|
|
|
|
return self.user_score + self.tag + self.artist + self.random - self.recent_penalty
|
|
|
|
|
|
|
|
|
2025-08-01 18:46:38 +02:00
|
|
|
@dataclass
|
|
|
|
class Room:
|
|
|
|
id: int
|
|
|
|
coord: tuple[float, float]
|
|
|
|
name: str
|
|
|
|
pin: int | None
|
|
|
|
tags: set[str]
|
2025-08-02 01:14:58 +02:00
|
|
|
range_size: int # in meters ??
|
2025-08-02 08:43:41 +02:00
|
|
|
distance: float
|
2025-08-01 18:46:38 +02:00
|
|
|
|
2025-08-01 22:48:38 +02:00
|
|
|
songs: dict[str, UserScoredSong] # all songs + user score (the playlist)
|
|
|
|
history: list[Song] # all songs previously played
|
|
|
|
|
2025-08-02 00:19:07 +02:00
|
|
|
## playing queue info
|
|
|
|
playing: list[Song]
|
2025-08-01 22:07:39 +02:00
|
|
|
playing_idx: int
|
2025-08-01 19:13:09 +02:00
|
|
|
|
2025-08-01 22:48:38 +02:00
|
|
|
def renew_queue(self):
|
|
|
|
for song in self.playing:
|
|
|
|
self.history.append(song)
|
|
|
|
self.playing.clear()
|
|
|
|
self.playing_idx = 0
|
|
|
|
|
|
|
|
rankings: dict[str, float] = {}
|
|
|
|
for id, (song, user_score) in self.songs.items():
|
|
|
|
rankings[id] = self.rank_song(song, user_score).total()
|
|
|
|
|
|
|
|
# sort dict items by their values and pick the highest 3
|
|
|
|
top_items = sorted(rankings.items(), key=lambda item: item[1], reverse=True)[:QUEUE_SIZE] # [:3]
|
|
|
|
self.playing = list(map(lambda x: self.songs[x[0]][0], top_items)) # remove the ranking and take only the songs
|
|
|
|
|
2025-08-01 22:10:59 +02:00
|
|
|
def rank_song_from_id(self, id: str) -> Rank:
|
|
|
|
scored = self.songs[id]
|
|
|
|
return self.rank_song(scored[0], scored[1])
|
|
|
|
|
|
|
|
def rank_song(self, song: Song, user_score: int) -> Rank:
|
2025-08-01 19:13:09 +02:00
|
|
|
song_items = self.songs.items()
|
|
|
|
|
2025-08-01 19:31:47 +02:00
|
|
|
lowest_score = min(song_items, key=lambda item: item[1][1])[1][1]
|
2025-08-01 22:10:59 +02:00
|
|
|
highest_score = max(song_items, key=lambda item: item[1][1])[1][1]
|
2025-08-01 19:31:47 +02:00
|
|
|
|
2025-08-01 22:10:59 +02:00
|
|
|
score_rank = translate(user_score, lowest_score, highest_score, 0.0, USER_SCORE_WEIGHT)
|
2025-08-01 19:31:47 +02:00
|
|
|
|
|
|
|
recent_songs = self.history[-RECENT_COUNT:]
|
|
|
|
|
|
|
|
tag_counts = {}
|
|
|
|
artist_counts = {}
|
2025-08-01 22:10:59 +02:00
|
|
|
for recent_song in recent_songs:
|
|
|
|
for tag in recent_song.tags:
|
2025-08-01 19:31:47 +02:00
|
|
|
if tag not in tag_counts:
|
|
|
|
tag_counts[tag] = 0
|
|
|
|
tag_counts[tag] += 1
|
2025-08-01 22:10:59 +02:00
|
|
|
if recent_song.artist not in artist_counts:
|
|
|
|
artist_counts[recent_song.artist] = 0
|
|
|
|
artist_counts[recent_song.artist] += 1
|
2025-08-01 19:31:47 +02:00
|
|
|
|
|
|
|
tag_total = 0
|
|
|
|
for tag in song.tags:
|
|
|
|
if tag in tag_counts:
|
|
|
|
tag_total += tag_counts[tag]
|
2025-08-01 19:13:09 +02:00
|
|
|
|
2025-08-01 22:10:59 +02:00
|
|
|
artist_total = artist_counts[song.artist] if song.artist in artist_counts else 0
|
2025-08-01 19:13:09 +02:00
|
|
|
|
2025-08-01 22:10:59 +02:00
|
|
|
tag_value = min(RECENT_COUNT, len(self.history)) - tag_total
|
|
|
|
artist_value = min(RECENT_COUNT, len(self.history)) - artist_total
|
2025-08-01 19:13:09 +02:00
|
|
|
|
2025-08-01 22:10:59 +02:00
|
|
|
tag_rank = translate(tag_value, 0, min(RECENT_COUNT, len(self.history)), 0, TAG_WEIGHT)
|
|
|
|
artist_rank = translate(artist_value, 0, min(RECENT_COUNT, len(self.history)), 0, ARTIST_WEIGHT)
|
|
|
|
|
|
|
|
random_rank = translate(random.random(), 0, 1, 0.0, RANDOM_WEIGHT)
|
|
|
|
|
|
|
|
recent_penalty = RECENT_PENALTY if song in recent_songs else 0
|
|
|
|
|
|
|
|
return Rank(score_rank, tag_rank, artist_rank, random_rank, recent_penalty)
|
2025-08-01 19:13:09 +02:00
|
|
|
|
|
|
|
|
|
|
|
def translate(value: float, in_min: float, in_max: float, out_min: float, out_max: float):
|
2025-08-01 22:10:59 +02:00
|
|
|
if in_max == in_min:
|
|
|
|
return out_min
|
2025-08-01 19:13:09 +02:00
|
|
|
return (value - in_min) / (in_max - in_min) * (out_max - out_min) + out_min
|
2025-08-01 22:10:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_algo():
|
|
|
|
songs = [
|
|
|
|
Song("paulham", "Io e i miei banchi", "Paul Ham", ["pop"], "", ""),
|
|
|
|
Song("cisco", "CiscoPT", "Cantarex", ["rap"], "", ""),
|
|
|
|
Song("vpn", "VPN", "Cantarex", ["rap"], "", ""),
|
|
|
|
Song("gang", "Gang Gang Gang", "Cantarex", ["rap"], "", ""),
|
|
|
|
Song("bertha1", "Rindondantha", "Berthanetti", ["rock"], "", ""),
|
|
|
|
Song("bertha2", "Ragatthi", "Berthanetti", ["rock"], "", ""),
|
|
|
|
Song("bertha3", "Tranthathione", "Berthanetti", ["rock"], "", ""),
|
|
|
|
Song("cexx", "Spritz", "Cex", ["kpop"], "", ""),
|
|
|
|
]
|
|
|
|
|
|
|
|
room = Room(
|
|
|
|
123,
|
|
|
|
(0.0, 0.0),
|
|
|
|
"test",
|
|
|
|
None,
|
|
|
|
set(["rock", "rap"]),
|
2025-08-02 01:14:58 +02:00
|
|
|
100,
|
2025-08-01 22:10:59 +02:00
|
|
|
{
|
2025-08-01 22:48:38 +02:00
|
|
|
"paulham": (songs[0], 7),
|
|
|
|
"cisco": (songs[1], 5),
|
|
|
|
"vpn": (songs[2], 11),
|
|
|
|
"gang": (songs[3], 10),
|
|
|
|
"bertha1": (songs[4], 4),
|
|
|
|
"bertha2": (songs[5], 5),
|
|
|
|
"bertha3": (songs[6], -4),
|
|
|
|
"cexx": (songs[7], 12),
|
2025-08-01 22:10:59 +02:00
|
|
|
},
|
2025-08-01 22:48:38 +02:00
|
|
|
[],
|
|
|
|
[songs[2], songs[0], songs[1]],
|
|
|
|
1,
|
2025-08-01 22:10:59 +02:00
|
|
|
)
|
2025-08-01 22:48:38 +02:00
|
|
|
room.renew_queue()
|
|
|
|
print(room.playing)
|