diff --git a/.env.example b/.env.example index c129082..a2d209c 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,7 @@ MYSQL_ROOT_PASSWORD=rootsecret TICKETMASTER_API_KEY= JAMBASE_API_KEY= JAMBASE_USER_AGENT=eventlens/0.1 +JAMBASE_SYNC_INTERVAL_HOURS=72 BANDSINTOWN_APP_ID= EVENTIM_ENABLED=true POLL_INTERVAL_HOURS=6 diff --git a/README.md b/README.md index f2bc3f1..fff8b9b 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ API-Statusinfo findest du unter `http://127.0.0.1:8001/api`. - `TICKETMASTER_API_KEY`: Ticketmaster Discovery API - `JAMBASE_API_KEY`: JamBase Data API Bearer Token - `JAMBASE_USER_AGENT`: eigene App-Kennung fuer JamBase-Requests +- `JAMBASE_SYNC_INTERVAL_HOURS`: Mindestabstand fuer JamBase-API-Laeufe, Standard `72`; `0` deaktiviert die Bremse - `BANDSINTOWN_APP_ID`: echte Bandsintown App-ID fuer Artist-Events - `EVENTIM_ENABLED`: aktiviert den Eventim-Website-Provider - `EVENTLENS_AUTH_USERNAME`, `EVENTLENS_AUTH_PASSWORD`: optionaler Passwortschutz fuer Webfrontend und API @@ -61,6 +62,13 @@ curl -X POST http://127.0.0.1:8001/watch-items \ curl -X POST http://127.0.0.1:8001/sync ``` +JamBase wird dabei nur abgefragt, wenn der letzte erfolgreiche JamBase-Lauf aelter als +`JAMBASE_SYNC_INTERVAL_HOURS` ist. Fuer einen sofortigen Provider-Lauf: + +```bash +curl -X POST 'http://127.0.0.1:8001/sync?force_providers=true' +``` + 3. Events abfragen: ```bash diff --git a/backend/app/config.py b/backend/app/config.py index 870c70b..c13934c 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -14,6 +14,7 @@ class Settings: ticketmaster_api_key = os.getenv("TICKETMASTER_API_KEY", "") jambase_api_key = os.getenv("JAMBASE_API_KEY", "") jambase_user_agent = os.getenv("JAMBASE_USER_AGENT", "eventlens/0.1") + jambase_sync_interval_hours = int(os.getenv("JAMBASE_SYNC_INTERVAL_HOURS", "72")) bandsintown_app_id = os.getenv("BANDSINTOWN_APP_ID", "") eventim_enabled = os.getenv("EVENTIM_ENABLED", "true").lower() == "true" poll_interval_hours = int(os.getenv("POLL_INTERVAL_HOURS", "6")) diff --git a/backend/app/main.py b/backend/app/main.py index dd51fc5..7f51e33 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -251,5 +251,5 @@ def get_provider_statuses(db: Session = Depends(get_db)): @app.post("/sync", response_model=SyncResult) -def trigger_sync(db: Session = Depends(get_db)): - return run_sync(db) +def trigger_sync(force_providers: bool = False, db: Session = Depends(get_db)): + return run_sync(db, force_providers=force_providers) diff --git a/backend/app/providers/jambase.py b/backend/app/providers/jambase.py index 25962b7..93e39a8 100644 --- a/backend/app/providers/jambase.py +++ b/backend/app/providers/jambase.py @@ -176,14 +176,7 @@ class JamBaseProvider: "geoRadiusAmount": 35, "geoRadiusUnits": "km", "geoCountryIso2": "DE", - }, - base_params - | { - "geoLatitude": 53.5511, - "geoLongitude": 9.9937, - "geoRadiusAmount": 35, - "geoRadiusUnits": "km", - }, + } ] if region_scope == RegionScope.germany: diff --git a/backend/app/services.py b/backend/app/services.py index cae1883..b80d429 100644 --- a/backend/app/services.py +++ b/backend/app/services.py @@ -19,6 +19,7 @@ from app.notifications import send_email_notification from app.providers.registry import get_providers from app.schemas import SyncResult from app.source_scanner import SourceScanner +from app.config import settings logger = logging.getLogger(__name__) @@ -38,6 +39,12 @@ PROVIDER_STATUS_PRIORITY = { } +def get_provider_sync_interval_hours(provider_name: str) -> int: + if provider_name == "jambase": + return settings.jambase_sync_interval_hours + return 0 + + def list_watch_items(db: Session) -> list[WatchItem]: return list(db.scalars(select(WatchItem).order_by(WatchItem.name))) @@ -96,6 +103,7 @@ def update_provider_status( provider_name: str, status: ProviderStatusType, message: str, + mark_success: bool = True, ): provider_status = db.scalar( select(ProviderStatus).where(ProviderStatus.provider_name == provider_name) @@ -116,7 +124,7 @@ def update_provider_status( provider_status.status = status provider_status.message = message provider_status.last_checked_at = now - if status == ProviderStatusType.ok: + if status == ProviderStatusType.ok and mark_success: provider_status.last_success_at = now @@ -343,12 +351,52 @@ def prune_stale_source_events( return len(stale_events) -def run_sync(db: Session) -> SyncResult: +def get_provider_skip_message(db: Session, provider_name: str, force_providers: bool) -> str | None: + if force_providers: + return None + + interval_hours = get_provider_sync_interval_hours(provider_name) + if interval_hours <= 0: + return None + + provider_status = db.scalar( + select(ProviderStatus).where(ProviderStatus.provider_name == provider_name) + ) + if provider_status is None or provider_status.last_success_at is None: + return None + + next_allowed_at = provider_status.last_success_at + timedelta(hours=interval_hours) + if datetime.utcnow() >= next_allowed_at: + return None + + return ( + f"{provider_name} uebersprungen: letzter erfolgreicher API-Lauf ist weniger " + f"als {interval_hours} Stunden her. Fuer sofortigen Lauf /sync?force_providers=true nutzen." + ) + + +def run_sync(db: Session, force_providers: bool = False) -> SyncResult: providers = get_providers() source_scanner = SourceScanner() + skipped_provider_messages = { + provider.source_name: skip_message + for provider in providers + if ( + skip_message := get_provider_skip_message( + db=db, + provider_name=provider.source_name, + force_providers=force_providers, + ) + ) + } + providers_to_sync = [ + provider + for provider in providers + if provider.source_name not in skipped_provider_messages + ] provider_states = { provider.source_name: init_provider_sync_state(provider.source_name) - for provider in providers + for provider in providers_to_sync } active_items = list( db.scalars(select(WatchItem).where(WatchItem.is_active.is_(True)).order_by(WatchItem.name)) @@ -433,7 +481,7 @@ def run_sync(db: Session) -> SyncResult: prune_stale_source_events(db, watch_item, source_name, seen_source_event_ids) db.commit() - for provider in providers: + for provider in providers_to_sync: try: events = provider.search_events( term=watch_item.name, @@ -526,6 +574,14 @@ def run_sync(db: Session) -> SyncResult: status=state["status"], message=build_provider_status_message(state), ) + for provider_name, message in skipped_provider_messages.items(): + update_provider_status( + db=db, + provider_name=provider_name, + status=ProviderStatusType.ok, + message=message, + mark_success=False, + ) db.commit() return SyncResult(