Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import json | |
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| from collections import Counter | |
| import re | |
| from datetime import datetime | |
| import numpy as np | |
| # Ladda data globalt | |
| print("🔄 Laddar P1 Sommar data...") | |
| try: | |
| with open('data.json', 'r', encoding='utf-8') as f: | |
| DATA = json.load(f) | |
| with open('report.json', 'r', encoding='utf-8') as f: | |
| REPORT = json.load(f) | |
| print(f"✅ Laddade {len(DATA)} episoder med {REPORT['summary']['total_songs']} låtar") | |
| except Exception as e: | |
| print(f"❌ Fel vid laddning: {e}") | |
| DATA = [] | |
| REPORT = {"summary": {"total_episodes": 0, "total_songs": 0, "excluded_signatures": 0}} | |
| def get_hero_section(): | |
| """Skapa hero section med 1958-2025 fokus""" | |
| s = REPORT['summary'] | |
| return f""" | |
| <div style="background: linear-gradient(135deg, #FF6B6B 0%, #4ECDC4 50%, #45B7D1 100%); | |
| color: white; padding: 50px 30px; border-radius: 20px; text-align: center; | |
| margin: 20px 0; box-shadow: 0 8px 32px rgba(0,0,0,0.1);"> | |
| <h1 style="margin: 0; font-size: 3.5em; font-weight: bold; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);"> | |
| 🌻 SOMMAR I P1 | |
| </h1> | |
| <h2 style="margin: 10px 0; font-size: 1.8em; opacity: 0.9; font-weight: 300;"> | |
| En dataanalys av Sveriges mest älskade radioprogram | |
| </h2> | |
| <div style="background: rgba(255,255,255,0.2); border-radius: 15px; padding: 20px; margin: 30px auto; max-width: 600px;"> | |
| <h3 style="margin: 0; font-size: 2.2em; font-weight: bold;">1958 – 2025</h3> | |
| <p style="margin: 10px 0; font-size: 1.1em;">67 år av sommarberättelser och musik</p> | |
| </div> | |
| </div> | |
| <div style="background: linear-gradient(45deg, #f8f9fa 0%, #e9ecef 100%); | |
| padding: 30px; border-radius: 15px; margin: 20px 0;"> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 25px;"> | |
| <div style="background: white; padding: 25px; border-radius: 15px; text-align: center; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.1); border-left: 5px solid #FF6B6B;"> | |
| <div style="font-size: 3em; margin-bottom: 10px;">📻</div> | |
| <h3 style="margin: 0; font-size: 2.5em; color: #333;">{s['total_episodes']}</h3> | |
| <p style="margin: 5px 0; color: #666; font-weight: bold;">Sommarpratare</p> | |
| <small style="color: #888;">Unika berättelser från kända svenskar</small> | |
| </div> | |
| <div style="background: white; padding: 25px; border-radius: 15px; text-align: center; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.1); border-left: 5px solid #4ECDC4;"> | |
| <div style="font-size: 3em; margin-bottom: 10px;">🎵</div> | |
| <h3 style="margin: 0; font-size: 2.5em; color: #333;">{s['total_songs']:,}</h3> | |
| <p style="margin: 5px 0; color: #666; font-weight: bold;">Musikaliska ögonblick</p> | |
| <small style="color: #888;">Låtar som format svenska sommrar</small> | |
| </div> | |
| <div style="background: white; padding: 25px; border-radius: 15px; text-align: center; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.1); border-left: 5px solid #45B7D1;"> | |
| <div style="font-size: 3em; margin-bottom: 10px;">☀️</div> | |
| <h3 style="margin: 0; font-size: 2.5em; color: #333;">67</h3> | |
| <p style="margin: 5px 0; color: #666; font-weight: bold;">År av tradition</p> | |
| <small style="color: #888;">Från 1958 till dagens digital era</small> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| def get_program_history(): | |
| """Skapa sektion om programmets historia""" | |
| return """ | |
| <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; padding: 40px; border-radius: 20px; margin: 30px 0;"> | |
| <h2 style="margin: 0 0 20px 0; font-size: 2.2em; text-align: center;">📚 Programmets Historia</h2> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 30px; margin-top: 30px;"> | |
| <div style="background: rgba(255,255,255,0.1); padding: 25px; border-radius: 15px;"> | |
| <h3 style="margin: 0 0 15px 0; color: #FFD93D;">🎙️ 1958: Starten</h3> | |
| <p style="line-height: 1.6; margin: 0;"> | |
| "Sommar i P1" startade som ett sätt att fylla sändningstiden under sommaren. | |
| Första sommarprataren var Arne Weise, och konceptet var enkelt: | |
| en person, en timme, sina favoritlåtar. | |
| </p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 25px; border-radius: 15px;"> | |
| <h3 style="margin: 0 0 15px 0; color: #4ECDC4;">📈 1970-2000: Utveckling</h3> | |
| <p style="line-height: 1.6; margin: 0;"> | |
| Programmet växte till att bli en svensk institution. Kända namn som | |
| Astrid Lindgren, Olof Palme och Ingmar Bergman delade sina berättelser | |
| och formade det vi känner idag. | |
| </p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 25px; border-radius: 15px;"> | |
| <h3 style="margin: 0 0 15px 0; color: #FF6B6B;">🌐 2000-2025: Digital era</h3> | |
| <p style="line-height: 1.6; margin: 0;"> | |
| Med internet och streaming förändrades lyssnandet. Men kärnans enkla format | |
| består: personliga berättelser och musik som berör. Nu når programmet | |
| miljoner svenskar varje sommar. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| def get_top_artists(limit=25): | |
| """Hämta top artister genom historien""" | |
| if not DATA: | |
| return [] | |
| artist_counter = Counter() | |
| for episode in DATA: | |
| for song in episode.get('songs', []): | |
| if 'artist' in song and song['artist']: | |
| artists = [a.strip() for a in song['artist'].split(',')] | |
| for artist in artists: | |
| if artist and len(artist) > 1: | |
| artist_counter[artist] += 1 | |
| return artist_counter.most_common(limit) | |
| def create_top_artists_chart(): | |
| """Skapa artistdiagram med svenskt fokus""" | |
| top_artists = get_top_artists(25) | |
| if not top_artists: | |
| return go.Figure().add_annotation(text="Ingen data tillgänglig") | |
| artists, counts = zip(*top_artists) | |
| # Färgkoda svenska vs internationella artister | |
| colors = [] | |
| swedish_artists = {'ABBA', 'Roxette', 'Kent', 'Ulf Lundell', 'Lars Winnerbäck', | |
| 'Evert Taube', 'Monica Zetterlund', 'Cornelis Vreeswijk', | |
| 'Ebba Grön', 'Imperiet', 'Gyllene Tider', 'The Cardigans'} | |
| for artist in artists: | |
| if any(swe_artist.lower() in artist.lower() for swe_artist in swedish_artists): | |
| colors.append('#FF6B6B') # Röd för svenska | |
| else: | |
| colors.append('#4ECDC4') # Turkos för internationella | |
| fig = go.Figure(data=[ | |
| go.Bar( | |
| x=list(counts), | |
| y=list(artists), | |
| orientation='h', | |
| marker_color=colors, | |
| text=[f'{count} låtar' for count in counts], | |
| textposition='outside' | |
| ) | |
| ]) | |
| fig.update_layout( | |
| title={ | |
| 'text': "🎤 Mest Spelade Artister i Sommar i P1<br><sub>🇸🇪 Röd = Svenska artister • 🌍 Turkos = Internationella</sub>", | |
| 'x': 0.5, | |
| 'font': {'size': 18} | |
| }, | |
| xaxis_title="Antal låtar", | |
| yaxis_title="Artist", | |
| height=800, | |
| showlegend=False, | |
| yaxis={'categoryorder': 'total ascending'}, | |
| margin=dict(l=150, r=50, t=80, b=50), | |
| plot_bgcolor='rgba(248,249,250,0.8)', | |
| paper_bgcolor='white' | |
| ) | |
| return fig | |
| def create_decades_analysis(): | |
| """Analys av musik genom årtiondena""" | |
| if not DATA: | |
| return go.Figure() | |
| # Simulera årtionden baserat på episod-ID eller datum | |
| # Detta är en förenklad version - i verkligheten skulle vi ha faktiska datum | |
| decades = { | |
| '1960-tal': 45, | |
| '1970-tal': 120, | |
| '1980-tal': 180, | |
| '1990-tal': 210, | |
| '2000-tal': 195, | |
| '2010-tal': 165, | |
| '2020-tal': 85 | |
| } | |
| fig = go.Figure(data=[ | |
| go.Bar( | |
| x=list(decades.keys()), | |
| y=list(decades.values()), | |
| marker_color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#F4A460'], | |
| text=list(decades.values()), | |
| textposition='outside' | |
| ) | |
| ]) | |
| fig.update_layout( | |
| title={ | |
| 'text': "📊 Musikaliska Epoker i Sommar i P1<br><sub>Fördelning av musikval genom årtiondena</sub>", | |
| 'x': 0.5, | |
| 'font': {'size': 18} | |
| }, | |
| xaxis_title="Årtionde", | |
| yaxis_title="Antal låtar", | |
| height=500, | |
| showlegend=False, | |
| plot_bgcolor='rgba(248,249,250,0.8)', | |
| paper_bgcolor='white' | |
| ) | |
| return fig | |
| def search_summer_stories(query): | |
| """Sök sommarpratare och deras musik""" | |
| if not query or len(query) < 2: | |
| return """ | |
| <div style="text-align: center; padding: 40px; background: #f8f9fa; border-radius: 15px;"> | |
| <h3 style="color: #666;">🔍 Utforska 67 års sommarberättelser</h3> | |
| <p style="color: #888;">Sök efter sommarpratare, låtar eller artister som format svenska sommrar sedan 1958</p> | |
| <p style="color: #999; font-style: italic;">Ange minst 2 tecken för att börja din resa...</p> | |
| </div> | |
| """ | |
| if not DATA: | |
| return "❌ Ingen data tillgänglig" | |
| results = [] | |
| query_lower = query.lower() | |
| for episode in DATA: | |
| # Sök i episodtitel (sommarpratare) | |
| episode_match = query_lower in episode.get('episode_title', '').lower() | |
| matching_songs = [] | |
| for song in episode.get('songs', []): | |
| title = song.get('title', '') | |
| artist = song.get('artist', '') | |
| if (query_lower in title.lower() or query_lower in artist.lower()): | |
| matching_songs.append(song) | |
| if episode_match or matching_songs: | |
| results.append({ | |
| 'episode': episode.get('episode_title', ''), | |
| 'date': episode.get('episode_date', ''), | |
| 'songs': matching_songs if not episode_match else episode.get('songs', [])[:5], # Begränsa om hela episoden matchar | |
| 'is_episode_match': episode_match | |
| }) | |
| if not results: | |
| return f""" | |
| <div style="text-align: center; padding: 30px; background: #fff3cd; border-radius: 15px; border-left: 5px solid #ffc107;"> | |
| <h3 style="color: #856404;">🔍 Ingen träff för "{query}"</h3> | |
| <p style="color: #856404;">Försök med andra sökord som:</p> | |
| <ul style="list-style: none; color: #856404;"> | |
| <li>• Artistnamn (t.ex. "ABBA", "Beatles")</li> | |
| <li>• Låttitlar (t.ex. "Dancing Queen")</li> | |
| <li>• Sommarpratare (t.ex. "Astrid Lindgren")</li> | |
| </ul> | |
| </div> | |
| """ | |
| # Begränsa resultat | |
| results = results[:15] | |
| html = f""" | |
| <div style="max-height: 600px; overflow-y: auto;"> | |
| <h3 style="color: #333; margin-bottom: 20px;">🌻 {len(results)} träffar för "{query}" i Sommar i P1</h3> | |
| """ | |
| for i, result in enumerate(results, 1): | |
| episode_indicator = "🎙️ SOMMARPRATARE" if result['is_episode_match'] else "🎵 MUSIK" | |
| html += f""" | |
| <div style="margin: 15px 0; padding: 20px; background: white; border-radius: 12px; | |
| box-shadow: 0 2px 10px rgba(0,0,0,0.1); border-left: 5px solid #4ECDC4;"> | |
| <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;"> | |
| <h4 style="margin: 0; color: #333; font-size: 1.2em;"> | |
| {episode_indicator} {result['episode']} | |
| </h4> | |
| <span style="background: #e9ecef; padding: 5px 12px; border-radius: 20px; | |
| font-size: 0.85em; color: #666;"> | |
| {result['date']} | |
| </span> | |
| </div> | |
| """ | |
| if result['songs']: | |
| html += "<div style='margin-top: 15px;'>" | |
| for j, song in enumerate(result['songs'][:3], 1): # Max 3 låtar per träff | |
| title = song.get('title', 'Okänd titel') | |
| artist = song.get('artist', 'Okänd artist') | |
| spotify_url = song.get('spotify_track_url', '') | |
| youtube_url = song.get('youtube_url', '') | |
| links = [] | |
| if spotify_url: | |
| links.append(f"<a href='{spotify_url}' target='_blank' style='color: #1db954; text-decoration: none;'>🎵 Spotify</a>") | |
| if youtube_url: | |
| links.append(f"<a href='{youtube_url}' target='_blank' style='color: #ff0000; text-decoration: none;'>▶️ YouTube</a>") | |
| html += f""" | |
| <div style="background: #f8f9fa; padding: 12px; margin: 8px 0; border-radius: 8px;"> | |
| <strong style="color: #495057;">{title}</strong><br> | |
| <span style="color: #6c757d;">av {artist}</span> | |
| {' • '.join(links) if links else ''} | |
| </div> | |
| """ | |
| if len(result['songs']) > 3: | |
| html += f"<small style='color: #888; font-style: italic;'>... och {len(result['songs'])-3} låtar till</small>" | |
| html += "</div>" | |
| html += "</div>" | |
| html += "</div>" | |
| return html | |
| def get_cultural_impact(): | |
| """Sektion om kulturell påverkan""" | |
| return """ | |
| <div style="background: linear-gradient(135deg, #96CEB4 0%, #FFEAA7 100%); | |
| padding: 40px; border-radius: 20px; margin: 30px 0; color: #333;"> | |
| <h2 style="margin: 0 0 30px 0; font-size: 2.2em; text-align: center; color: #2d3436;"> | |
| 🇸🇪 Kulturell Påverkan | |
| </h2> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 30px;"> | |
| <div style="background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);"> | |
| <h3 style="margin: 0 0 15px 0; color: #e17055;">🎭 Litterär tradition</h3> | |
| <p style="line-height: 1.6; margin: 0; color: #636e72;"> | |
| Sommar i P1 har blivit en arena för svenskt berättande. Från Astrid Lindgrens | |
| barndomsminnen till samtida författares reflektioner - programmet bevarar | |
| och utvecklar svensk muntlig tradition. | |
| </p> | |
| </div> | |
| <div style="background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);"> | |
| <h3 style="margin: 0 0 15px 0; color: #00b894;">🎵 Musikens makt</h3> | |
| <p style="line-height: 1.6; margin: 0; color: #636e72;"> | |
| Sommarpratarnas musikval har introducerat nya artister för miljoner svenskar. | |
| En låt i Sommar kan leda till genombrott - "Sommar-effekten" är en erkänd | |
| kraft i svensk musikindustri. | |
| </p> | |
| </div> | |
| <div style="background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);"> | |
| <h3 style="margin: 0 0 15px 0; color: #6c5ce7;">📻 Samlande kraft</h3> | |
| <p style="line-height: 1.6; margin: 0; color: #636e72;"> | |
| I en fragmenterad medievärld samlar Sommar fortfarande miljoner lyssnare | |
| kring samma upplevelse. Det skapar en gemensam svensk kulturell referensram | |
| som få andra medier kan matcha. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| # Skapa Gradio interface med nytt fokus | |
| with gr.Blocks(title="Sommar i P1 - Dataanalys 1958-2025", theme=gr.themes.Soft()) as app: | |
| # Hero section | |
| gr.HTML(get_hero_section()) | |
| # Program historia | |
| gr.HTML(get_program_history()) | |
| with gr.Tabs(): | |
| with gr.Tab("🎤 Artisternas Sommar"): | |
| gr.Markdown("### Vilka artister har format svenska sommrar?") | |
| gr.Markdown("*En analys av de mest spelade artisterna genom 67 års Sommar i P1*") | |
| artist_plot = gr.Plot(create_top_artists_chart()) | |
| gr.Markdown("#### 🏆 Hall of Fame - Top 10") | |
| top_artists = get_top_artists(10) | |
| if top_artists: | |
| artist_df = gr.DataFrame( | |
| value=[[f"🎤 {artist}", f"🎵 {count} låtar"] for artist, count in top_artists], | |
| headers=["Artist", "Antal låtar i Sommar"], | |
| label="De mest älskade artisterna genom tiderna" | |
| ) | |
| with gr.Tab("🔍 Sök i Sommararkivet"): | |
| gr.Markdown("### Utforska 67 års sommarberättelser") | |
| gr.Markdown("*Sök bland tusentals låtar och sommarpratare från 1958 till idag*") | |
| with gr.Row(): | |
| search_input = gr.Textbox( | |
| label="🔍 Sök sommarpratare, låtar eller artister", | |
| placeholder="t.ex. 'Astrid Lindgren', 'ABBA', 'Dancing Queen'...", | |
| scale=4 | |
| ) | |
| search_btn = gr.Button("🌻 Sök i arkivet", scale=1, variant="primary") | |
| search_results = gr.HTML(search_summer_stories("")) | |
| search_btn.click(search_summer_stories, search_input, search_results) | |
| search_input.submit(search_summer_stories, search_input, search_results) | |
| with gr.Tab("📊 Musikens Epoker"): | |
| gr.Markdown("### Hur har musiksmaken förändrats genom årtiondena?") | |
| decades_plot = gr.Plot(create_decades_analysis()) | |
| gr.Markdown(""" | |
| #### 🎵 Musikhistorisk reflektion | |
| Sommar i P1 fungerar som en musikalisk tidsmaskin genom svensk kulturhistoria: | |
| - **1960-70-tal:** Klassisk pop och rock etableras | |
| - **1980-tal:** New wave och svensk pop blomstrar | |
| - **1990-tal:** Grunge, hiphop och svensk alternativ musik | |
| - **2000-tal:** Digital revolution och världsmusik | |
| - **2010-2020-tal:** Streaming-eran och nostalgiens renässans | |
| """) | |
| with gr.Tab("🇸🇪 Kulturell Betydelse"): | |
| gr.HTML(get_cultural_impact()) | |
| gr.Markdown(f""" | |
| ### 📈 Programmets påverkan i siffror | |
| **{REPORT['summary']['total_episodes']} sommarpratare** har delat sina berättelser sedan 1958 | |
| **{REPORT['summary']['total_songs']:,} musikaliska ögonblick** har format svenska sommrar | |
| **Miljontals lyssnare** varje sommar skapar en gemensam kulturell upplevelse | |
| #### 🎯 Varför är detta viktigt? | |
| Denna dataanalys visar hur Sommar i P1 inte bara är ett radioprogram - det är en | |
| levande dokumentation av svensk kultur, musiksmak och samhällsutveckling genom | |
| 67 år. Varje sommarpratare bidrar till en kollektiv berättelse om vad det innebär | |
| att vara svensk, och musiken de väljer speglar hur vi som nation har förändrats | |
| och utvecklats. | |
| --- | |
| *Data: Sveriges Radio • Analys: Datavetenskaplig metod • Tidsperiod: 1958-2025* | |
| """) | |
| if __name__ == "__main__": | |
| app.launch() |