Spaces:
Sleeping
Sleeping
| """ | |
| Command-line runner for the Music Recommender Simulation with 4 Advanced Challenges. | |
| Run from the project root: | |
| python -m src.main | |
| Challenges implemented: | |
| 1. Advanced Song Features: popularity, release_decade, detailed_mood_tags | |
| 2. Multiple Scoring Modes: genre-first, mood-first, energy-focused, popularity-aware | |
| 3. Diversity Penalty: prevents same artist from appearing multiple times | |
| 4. Visual Summary Table: tabulate-based formatted output (Challenge 4) | |
| """ | |
| from src.recommender import load_songs, recommend_songs | |
| # Try to import tabulate for Challenge 4 (visual tables) | |
| try: | |
| from tabulate import tabulate | |
| TABULATE_AVAILABLE = True | |
| except ImportError: | |
| TABULATE_AVAILABLE = False | |
| print("Note: Install 'tabulate' for prettier table output: pip install tabulate") | |
| # ββ User profiles ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| PROFILES = { | |
| "High-Energy Pop Fan": { | |
| "genre": "pop", | |
| "mood": "happy", | |
| "energy": 0.90, | |
| "target_valence": 0.85, | |
| "likes_acoustic": False, | |
| }, | |
| "Chill Lofi Student": { | |
| "genre": "lofi", | |
| "mood": "chill", | |
| "energy": 0.38, | |
| "target_valence": 0.58, | |
| "likes_acoustic": True, | |
| }, | |
| "Deep Intense Rock": { | |
| "genre": "rock", | |
| "mood": "intense", | |
| "energy": 0.92, | |
| "target_valence": 0.45, | |
| "likes_acoustic": False, | |
| }, | |
| "Adversarial: High-Energy Blues (Conflicting)": { | |
| "genre": "blues", | |
| "mood": "sad", | |
| "energy": 0.90, | |
| "target_valence": 0.20, | |
| "likes_acoustic": True, | |
| }, | |
| "Edge Case: Genre Not in Catalog (Reggae)": { | |
| "genre": "reggae", | |
| "mood": "relaxed", | |
| "energy": 0.60, | |
| "target_valence": 0.75, | |
| "likes_acoustic": False, | |
| }, | |
| } | |
| # Challenge 1: New profile that uses detailed mood tags (advanced feature) | |
| MOOD_TAG_PROFILE = { | |
| "genre": "lofi", | |
| "mood": "chill", | |
| "energy": 0.40, | |
| "target_valence": 0.60, | |
| "likes_acoustic": True, | |
| "mood_tags": ["nostalgic", "dreamy", "peaceful"], # Challenge 1: Uses new feature | |
| } | |
| EXPERIMENT_WEIGHTS = { | |
| "genre": 1.0, | |
| "mood": 1.0, | |
| "energy": 2.0, | |
| "valence": 0.5, | |
| "acoustic": 0.5, | |
| } | |
| def print_results(profile_name: str, user_prefs: dict, results: list, mode: str = "default") -> None: | |
| """Prints a formatted block of recommendations with optional Challenge 4 table.""" | |
| print() | |
| print("=" * 70) | |
| print(f" {profile_name} (Mode: {mode})") | |
| print("=" * 70) | |
| print( | |
| f" genre={user_prefs.get('genre')} | " | |
| f"mood={user_prefs.get('mood')} | " | |
| f"energy={user_prefs.get('energy')}" | |
| ) | |
| print("-" * 70) | |
| # Challenge 4: Visual Summary Table | |
| if TABULATE_AVAILABLE and results: | |
| table_data = [] | |
| for rank, (song, score, reasons) in enumerate(results, start=1): | |
| table_data.append([ | |
| rank, | |
| song['title'], | |
| song['artist'], | |
| song['genre'], | |
| song['mood'], | |
| f"{score:.2f}", | |
| "; ".join(reasons[:1]), # First reason only for table | |
| ]) | |
| headers = ["#", "Title", "Artist", "Genre", "Mood", "Score", "Why"] | |
| print(tabulate(table_data, headers=headers, tablefmt="grid")) | |
| else: | |
| # Fallback to original bullet format | |
| for rank, (song, score, reasons) in enumerate(results, start=1): | |
| print(f"\n #{rank} {song['title']} β {song['artist']}") | |
| print(f" Score : {score:.2f}") | |
| print(f" Genre : {song['genre']} Mood : {song['mood']} Energy : {song['energy']}") | |
| print(" Why :") | |
| for reason in reasons: | |
| print(f" β’ {reason}") | |
| print() | |
| def main() -> None: | |
| """Demonstrates all 4 challenges.""" | |
| songs = load_songs("data/songs.csv") | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CHALLENGE 1: Advanced Song Features | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "β" * 70) | |
| print(" CHALLENGE 1 β Advanced Song Features (Popularity, Decade, Mood Tags)") | |
| print("β" * 70) | |
| print("\n Songs now have:") | |
| print(" β’ Popularity (0-100): " + ", ".join([f"{s['title']} ({s['popularity']})" for s in songs[:3]])) | |
| print(" β’ Release Decade: " + ", ".join([f"{s['title']} ({s['release_decade']})" for s in songs[:3]])) | |
| print(" β’ Detailed Mood Tags: " + f"{songs[0]['title']} {songs[0]['detailed_mood_tags']}") | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CHALLENGE 2: Multiple Scoring Modes | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "β" * 70) | |
| print(" CHALLENGE 2 β Multiple Scoring Modes") | |
| print("β" * 70) | |
| pop_prefs = PROFILES["High-Energy Pop Fan"] | |
| print("\n Mode 1: DEFAULT (Genre-First)") | |
| default_results = recommend_songs(pop_prefs, songs, k=5, mode="default") | |
| print_results("DEFAULT MODE", pop_prefs, default_results, mode="default") | |
| print(" Mode 2: MOOD-FIRST (Mood is primary signal)") | |
| mood_first_results = recommend_songs(pop_prefs, songs, k=5, mode="mood_first") | |
| print_results("MOOD-FIRST MODE", pop_prefs, mood_first_results, mode="mood_first") | |
| print(" Mode 3: ENERGY-FOCUSED (Energy dominates)") | |
| energy_results = recommend_songs(pop_prefs, songs, k=5, mode="energy_focused") | |
| print_results("ENERGY-FOCUSED MODE", pop_prefs, energy_results, mode="energy_focused") | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CHALLENGE 3: Diversity Penalty | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "β" * 70) | |
| print(" CHALLENGE 3 β Diversity Penalty (Prevents Duplicate Artists)") | |
| print("β" * 70) | |
| print("\n WITHOUT Diversity Penalty:") | |
| results_no_diversity = recommend_songs(pop_prefs, songs, k=5, diversity_penalty=False) | |
| for rank, (song, score, _) in enumerate(results_no_diversity, 1): | |
| print(f" #{rank} {song['title']} by {song['artist']} [{score:.2f}]") | |
| print("\n WITH Diversity Penalty (penalizes duplicate artists):") | |
| results_with_diversity = recommend_songs(pop_prefs, songs, k=5, diversity_penalty=True) | |
| for rank, (song, score, _) in enumerate(results_with_diversity, 1): | |
| print(f" #{rank} {song['title']} by {song['artist']} [{score:.2f}]") | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CHALLENGE 4: Visual Summary Table (already shown above with tabulate) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "β" * 70) | |
| print(" CHALLENGE 4 β Visual Summary Tables (shown above with tabulate)") | |
| print("β" * 70) | |
| print("\n Tables display rank, title, artist, genre, mood, score, and reasons.") | |
| if not TABULATE_AVAILABLE: | |
| print(" Install tabulate for better formatting: pip install tabulate") | |
| print() | |
| if __name__ == "__main__": | |
| main() | |