def wait_for_rate_limit_reset(self): utc_now = pd.Timestamp.utcnow() timedelta_to_wait = self.rate_limit_reset_time - utc_now timedelta_to_wait += timedelta(minutes=3) # Just for safety secs_to_wait = timedelta_to_wait.total_seconds() retry_time_utc = utc_now + timedelta_to_wait _print_and_log('Waiting {:.0f} seconds. Will retry at {}'.format( secs_to_wait, retry_time_utc)) time.sleep(secs_to_wait)
def _api_query(self, service: str, api_params: Dict, wait_if_rate_limit_exceeded: bool = False, use_data_service: bool = False) -> str: """Send API request to PVOutput.org and return content text. Args: service: string, e.g. 'search' or 'getstatus' api_params: dict wait_if_rate_limit_exceeded: bool use_data_service: bool Raises: NoStatusFound RateLimitExceeded """ get_response_func = (self._get_data_service_response if use_data_service else self._get_api_response) try: response = get_response_func(service, api_params) except Exception as e: _LOG.exception(e) raise try: return self._process_api_response(response) except RateLimitExceeded: msg = ("PVOutput.org API rate limit exceeded!" " Rate limit will be reset at {}".format( self.rate_limit_reset_time)) _print_and_log(msg) if wait_if_rate_limit_exceeded: self.wait_for_rate_limit_reset() return self._api_query(service, api_params, wait_if_rate_limit_exceeded=False) raise RateLimitExceeded(response, msg)
def get_batch_status(self, pv_system_id: int, date_to: Optional[Union[str, datetime]] = None, max_retries: Optional[int] = 1000, **kwargs) -> Union[None, pd.DataFrame]: """Get batch PV system status (e.g. power generation). The returned DataFrame will be empty if the PVOutput API returns 'status 400: No status found'. Data returned is limited to the last 366 days per request. To retrieve older data, use the date_to parameter. The PVOutput getbatchstatus API is asynchronous. When it's first called, it replies to say 'accepted'. This function will then wait a minute and call the API again to see if the data is ready. Set `max_retries` to 1 if you want to return immediately, even if data isn't ready yet (and hence this function will return None) https://pvoutput.org/help.html#dataservice-getbatchstatus Args: pv_system_id: int date_to: str in format YYYYMMDD; or datetime (localtime of the PV system). The returned timeseries will include 366 days of data: from YYYY-1MMDD to YYYYMMDD inclusive max_retries: int, number of times to retry after receiving a '202 Accepted' request. Set `max_retries` to 1 if you want to return immediately, even if data isn't ready yet (and hence this function will return None). Returns: None (if data isn't ready after retrying max_retries times) or pd.DataFrame: index: datetime (DatetimeIndex, localtime of the PV system) columns: (all np.float64): cumulative_energy_gen_Wh, instantaneous_power_gen_W, temperature_C, voltage """ api_params = {'sid1': pv_system_id} _set_date_param(date_to, api_params, 'dt') for retry in range(max_retries): try: pv_system_status_text = self._api_query( service='getbatchstatus', api_params=api_params, use_data_service=True, **kwargs) except NoStatusFound: _LOG.info('system_id %d: No status found for date_to %s', pv_system_id, date_to) pv_system_status_text = "" break if 'Accepted 202' in pv_system_status_text: if retry == 0: _print_and_log('Request accepted.') if retry < max_retries - 1: _print_and_log('Sleeping for 1 minute.') time.sleep(60) else: _print_and_log( 'Call get_batch_status again in a minute to see if' ' results are ready.') else: break else: return return _process_batch_status(pv_system_status_text)