class CredentialedGirderTask(Task): """ Provide a task with a requests session via self.session, this is the default task. This base task should always be used in conjunction with setting bind=True in order to access the session. """ def __call__(self, *args, **kwargs): """ Create a token and configure a requests session object with it. The child class overrides run, so __call__ must be used to hook in before a task is executed. """ # TODO: Revoke token in post task signal self.token = Token().createToken( user=getAdminUser(), days=1, scope=[TokenScope.DATA_READ, TokenScope.DATA_WRITE]) self.session = BaseUrlSession(settings.ISIC_API_URL) self.session.headers.update({'Girder-Token': str(self.token['_id'])}) retry = Retry(total=10, read=10, connect=10, backoff_factor=.2, method_whitelist=False, status_forcelist=[500, 502, 503, 504]) adapter = HTTPAdapter(max_retries=retry) self.session.mount('http://', adapter) self.session.mount('https://', adapter) # super(CredentialedGirderTask, self).__call__(*args, **kwargs)
def __init__(self, api_token: str, account_id: int) -> None: self.account_id = account_id # Rather than assigning directly to `self`, this is the recommended idiom so atexit.register behaves nicely with GC. session = BaseUrlSession( base_url=f"{self.api_domain}/{self.api_version}/{account_id}/") session.auth = (api_token, "") session.headers.update({"User-Agent": self.drip_py_ua}) session.mount(self.api_domain, GzipAdapter()) register(session.close) self.session = session
def test_connection_keepalive(simple_wsgi_server): """Test the connection keepalive works (duh).""" session = Session(base_url=simple_wsgi_server['url']) pooled = requests.adapters.HTTPAdapter( pool_connections=1, pool_maxsize=1000, ) session.mount('http://', pooled) def do_request(): with ExceptionTrap(requests.exceptions.ConnectionError) as trap: resp = session.get('info') resp.raise_for_status() return bool(trap) with ThreadPoolExecutor(max_workers=50) as pool: tasks = [ pool.submit(do_request) for n in range(1000) ] failures = sum(task.result() for task in tasks) assert not failures
class MyTurnCA: """Main API class""" def __init__(self, api_key: str): self.logger = logging.getLogger(__name__) self.session = BaseUrlSession(base_url=MY_TURN_URL) self.session.mount('https://', HTTPAdapter(max_retries=DEFAULT_RETRY_STRATEGY)) self.session.headers.update({**REQUEST_HEADERS, GOOD_BOT_HEADER: api_key}) self.vaccine_data = self._get_vaccine_data() def _get_vaccine_data(self) -> str: """Retrieve initial vaccine data""" response = self._send_request(url=ELIGIBILITY_URL, body=ELIGIBLE_REQUEST_BODY).json() if response['eligible'] is False: raise RuntimeError('something is wrong, default /eligibility body returned \'eligible\' = False') return response['vaccineData'] def get_locations(self, latitude: float, longitude: float) -> List[Location]: """Gets available locations near the given coordinates""" body = { 'location': { 'lat': latitude, 'lng': longitude }, 'fromDate': datetime.now(tz=pytz.timezone('US/Pacific')).strftime('%Y-%m-%d'), 'vaccineData': self.vaccine_data, 'locationQuery': { 'includePools': LOCATION_POOLS, 'excludeTags': [], 'includeTags': [] } } response = self._send_request(url=LOCATIONS_URL, body=body) try: return [Location(location_id=x['extId'], name=x['name'], address=x['displayAddress'], booking_type=x['type'], vaccine_data=x['vaccineData'], distance=x['distanceInMeters']) for x in response.json()['locations']] except json.JSONDecodeError: self.logger.error(JSON_DECODE_ERROR_MSG.format(body=response.text)) return [] def get_availability(self, location: Location, start_date: date, end_date: date) -> LocationAvailability: """Gets a given vaccination location's availability""" body = { 'startDate': start_date.strftime('%Y-%m-%d'), 'endDate': end_date.strftime('%Y-%m-%d'), 'vaccineData': location.vaccine_data, 'doseNumber': 1 } response = self._send_request(url=LOCATION_AVAILABILITY_URL.format(location_id=location.location_id), body=body) try: return LocationAvailability(location=location, dates_available=[datetime.strptime(x['date'], '%Y-%m-%d').date() for x in response.json()['availability'] if x['available'] is True]) except json.JSONDecodeError: self.logger.error(JSON_DECODE_ERROR_MSG.format(body=response.text)) return LocationAvailability(location=location, dates_available=[]) def get_slots(self, location: Location, start_date: date) -> LocationAvailabilitySlots: """Gets a given location's available appointments""" body = { 'vaccineData': location.vaccine_data } response = self._send_request(url=LOCATION_AVAILABILITY_SLOTS_URL.format(location_id=location.location_id, start_date=start_date.strftime('%Y-%m-%d')), body=body) try: return LocationAvailabilitySlots(location=location, slots=[self._combine_date_and_time(start_date, x['localStartTime']) for x in response.json()['slotsWithAvailability'] if self._combine_date_and_time(start_date, x['localStartTime']) > datetime.now(tz=pytz.timezone('US/Pacific'))]) except json.JSONDecodeError: self.logger.error(JSON_DECODE_ERROR_MSG.format(body=response.text)) return LocationAvailabilitySlots(location=location, slots=[]) def get_appointments(self, latitude: float, longitude: float, start_date: date, end_date: date) -> List[LocationAvailabilitySlots]: """Retrieves available appointments from all vaccination locations near the given coordinates""" locations = self.get_locations(latitude=latitude, longitude=longitude) if not locations: return [] if start_date > end_date: raise ValueError('Provided start_date must be before end_date') appointments = [] for location in locations: days_available = self.get_availability(location=location, start_date=start_date, end_date=end_date).dates_available if not days_available: continue location_appointments = [location_appointment for location_appointment in [self.get_slots(location=location, start_date=day_available) for day_available in days_available] if location_appointment.slots] if not location_appointments: continue # combines appointments on different days for the same location appointments.append(LocationAvailabilitySlots(location=location_appointments[0].location, slots=functools.reduce(operator.add, [location_appointment.slots for location_appointment in location_appointments]))) return appointments @staticmethod def _combine_date_and_time(start_date: date, timestamp: str) -> datetime: """Private helper function to combine a date and timestamp""" return datetime.combine(start_date, datetime.strptime(timestamp, '%H:%M:%S').time(), tzinfo=pytz.timezone('US/Pacific')) def _send_request(self, url: str, body: dict) -> Response: """Private helper function to make HTTP POST requests""" self.logger.info(f'sending request to {MY_TURN_URL}{url} with body - {body}') response = self.session.post(url=url, json=body) self.logger.info(f'got response from /{url} - {response.__dict__}') return response