diff --git a/backend/fix_tours.py b/backend/fix_tours.py
new file mode 100644
index 0000000..9ef8995
--- /dev/null
+++ b/backend/fix_tours.py
@@ -0,0 +1,29 @@
+from sqlmodel import Session, select, func
+from database import engine
+from models import Tour, Show
+
+def fix_tour_dates():
+ with Session(engine) as session:
+ tours = session.exec(select(Tour)).all()
+ print(f"Fixing dates for {len(tours)} tours...")
+
+ for tour in tours:
+ # Find min/max show date
+ result = session.exec(
+ select(func.min(Show.date), func.max(Show.date))
+ .where(Show.tour_id == tour.id)
+ ).first()
+
+ if result and result[0]:
+ tour.start_date = result[0]
+ tour.end_date = result[1]
+ session.add(tour)
+ # print(f" {tour.name}: {tour.start_date.date()} - {tour.end_date.date()}")
+ # else:
+ # print(f" {tour.name}: No shows found")
+
+ session.commit()
+ print("Done.")
+
+if __name__ == "__main__":
+ fix_tour_dates()
diff --git a/backend/import_elgoose.py b/backend/import_elgoose.py
index cc89a0d..a275947 100644
--- a/backend/import_elgoose.py
+++ b/backend/import_elgoose.py
@@ -252,57 +252,63 @@ def import_shows(session, vertical_id, venue_map):
return show_map, tour_map
def import_setlists(session, show_map, song_map):
- """Import setlists for all shows"""
+ """Import setlists for all shows (Paginated)"""
print("\nš Importing setlists...")
- # Fetch all setlists (this gets all performances across all shows)
- setlists_data = fetch_all_json("setlists")
- if not setlists_data:
- print("ā No setlist data found")
- return
-
- # Filter for Goose shows
- goose_setlists = [
- s for s in setlists_data
- if s.get('show_id') in show_map
- ]
-
+ page = 1
+ total_processed = 0
performance_count = 0
- for perf_data in goose_setlists:
- # Map to our show and song IDs
- our_show_id = show_map.get(perf_data['show_id'])
- our_song_id = song_map.get(perf_data['song_id'])
-
- if not our_show_id or not our_song_id:
- continue
-
- # Check existing performance
- existing_perf = session.exec(
- select(Performance).where(
- Performance.show_id == our_show_id,
- Performance.song_id == our_song_id,
- Performance.position == perf_data.get('position', 0)
- )
- ).first()
-
- if not existing_perf:
- perf = Performance(
- show_id=our_show_id,
- song_id=our_song_id,
- position=perf_data.get('position', 0),
- set_name=perf_data.get('set'),
- segue=bool(perf_data.get('segue', 0)),
- notes=perf_data.get('notes')
- )
- session.add(perf)
- performance_count += 1
-
- if performance_count % 100 == 0:
- session.commit()
- print(f" Progress: {performance_count} performances...")
+ params = {}
- session.commit()
- print(f"ā Imported {performance_count} performances")
+ while True:
+ params['page'] = page
+ print(f" Fetching setlists page {page}...", end="", flush=True)
+
+ data = fetch_json("setlists", params)
+ if not data:
+ print(" Done (No Data/End).")
+ break
+
+ print(f" Processing {len(data)} items...", end="", flush=True)
+
+ count_in_page = 0
+ for perf_data in data:
+ # Map to our show and song IDs
+ our_show_id = show_map.get(perf_data.get('show_id'))
+ our_song_id = song_map.get(perf_data.get('song_id'))
+
+ if not our_show_id or not our_song_id:
+ continue
+
+ # Check existing performance validation is expensive.
+ # We trust idempotency or assume clean run?
+ # User idempotency request requires checking.
+ existing_perf = session.exec(
+ select(Performance).where(
+ Performance.show_id == our_show_id,
+ Performance.song_id == our_song_id,
+ Performance.position == perf_data.get('position', 0)
+ )
+ ).first()
+
+ if not existing_perf:
+ perf = Performance(
+ show_id=our_show_id,
+ song_id=our_song_id,
+ position=perf_data.get('position', 0),
+ set_name=perf_data.get('set'),
+ segue=bool(perf_data.get('segue', 0)),
+ notes=perf_data.get('notes')
+ )
+ session.add(perf)
+ count_in_page += 1
+ performance_count += 1
+
+ session.commit()
+ print(f" Validated/Added {count_in_page} items.")
+ page += 1
+
+ print(f"ā Imported {performance_count} new performances")
def main():
print("="*60)
diff --git a/backend/routers/shows.py b/backend/routers/shows.py
index c7e37bf..9dd4e57 100644
--- a/backend/routers/shows.py
+++ b/backend/routers/shows.py
@@ -30,11 +30,9 @@ def read_shows(
query = query.where(Show.venue_id == venue_id)
if tour_id:
query = query.where(Show.tour_id == tour_id)
- # if year:
- # # SQLite/Postgres specific year extraction might differ,
- # # but usually we can filter by date range or extract year.
- # # For simplicity let's skip year for now or use a range if needed.
- # pass
+ if year:
+ from sqlalchemy import extract
+ query = query.where(extract('year', Show.date) == year)
shows = session.exec(query.offset(offset).limit(limit)).all()
return shows
diff --git a/frontend/app/about/page.tsx b/frontend/app/about/page.tsx
index 85e6f2c..3f5b721 100644
--- a/frontend/app/about/page.tsx
+++ b/frontend/app/about/page.tsx
@@ -1,30 +1,17 @@
export default function AboutPage() {
return (
-
-
About Elmeg
-
-
- Elmeg is the definitive fan archive for Goose, built by fans for fans.
-
-
- Our mission is to track every show, every song, and every stat. Whether you're chasing your first Arcadia or looking for that deep cut Factory Fiction, Elmeg has the data you need.
-
-
-
-
Features
-
-
Comprehensive Show Archive
-
Detailed Setlists & Song Stats
-
Community Ratings & Reviews
-
Venue Leaderboards
-
Personal Attendance Tracking
-
-
-
-
- Elmeg is a demo project showcasing advanced full-stack capabilities. Powered by FastAPI, Next.js, and SQLModel.
-
-
+
+
About Elmeg
+
+ Elmeg is a community-driven archive for Goose, dedicated to documenting every show, song, and performance.
+
+
+ Founded in 2025, Elmeg aims to provide the most comprehensive stats and the highest quality data for fans.
+
+
The Archives
+
+ Our data includes setlists, reviews, and community ratings for shows dating back to 2014.
+
- );
+ )
}
diff --git a/frontend/app/archive/page.tsx b/frontend/app/archive/page.tsx
index c8437cd..d5c6110 100644
--- a/frontend/app/archive/page.tsx
+++ b/frontend/app/archive/page.tsx
@@ -1,28 +1,24 @@
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+import { Card, CardContent } from "@/components/ui/card"
import Link from "next/link"
+import { Calendar } from "lucide-react"
-// Mock data for now - will fetch from API later
-const recentShows = [
- { id: 1, date: "2023-12-31", venue: "Madison Square Garden", location: "New York, NY", band: "Phish" },
- { id: 2, date: "2023-12-30", venue: "Madison Square Garden", location: "New York, NY", band: "Phish" },
- { id: 3, date: "2023-12-29", venue: "Madison Square Garden", location: "New York, NY", band: "Phish" },
-]
+const years = Array.from({ length: 2025 - 2014 + 1 }, (_, i) => 2025 - i)
export default function ArchivePage() {
return (