import logging from datetime import datetime from threading import Thread, Event import pytz import requests from core.constants import HTTP_HEADERS from spotproviders.spot_provider import SpotProvider class HTTPSpotProvider(SpotProvider): """Generic spot provider class for providers that request data via HTTP(S). Just for convenience to avoid code duplication. Subclasses of this query the individual APIs for data.""" def __init__(self, provider_config, url, poll_interval): super().__init__(provider_config) self._url = url self._poll_interval = poll_interval self._thread = None self._stop_event = Event() def start(self): # Fire off the polling thread. It will poll immediately on startup, then sleep for poll_interval between # subsequent polls, so start() returns immediately and the application can continue starting. logging.info("Set up query of " + self.name + " spot API every " + str(self._poll_interval) + " seconds.") self._thread = Thread(target=self._run, daemon=True) self._thread.start() def stop(self): self._stop_event.set() def _run(self): while True: self._poll() if self._stop_event.wait(timeout=self._poll_interval): break def _poll(self): try: # Request data from API logging.debug("Polling " + self.name + " spot API...") http_response = requests.get(self._url, headers=HTTP_HEADERS) # Pass off to the subclass for processing new_spots = self._http_response_to_spots(http_response) # Submit the new spots for processing. There might not be any spots for the less popular programs. if new_spots: self._submit_batch(new_spots) self.status = "OK" self.last_update_time = datetime.now(pytz.UTC) logging.debug("Received data from " + self.name + " spot API.") except Exception: self.status = "Error" logging.exception("Exception in HTTP JSON Spot Provider (" + self.name + ")") self._stop_event.wait(timeout=1) def _http_response_to_spots(self, http_response): """Convert an HTTP response returned by the API into spot data. The whole response is provided here so the subclass implementations can check for HTTP status codes if necessary, and handle the response as JSON, XML, text, whatever the API actually provides.""" raise NotImplementedError("Subclasses must implement this method")