def print(status: int = 200, data: str = "") -> json: """ Makes a json object to return to send. @param status (int): HTTP status code: https://nl.wikipedia.org/wiki/Lijst_van_HTTP-statuscodes. Default = 200 @param data (json): object what has to be send back. Default = "" @return formatted json. input: @code Api.print(200, { "result": 1 }) @endcode Result: @code { "description": "Request was successful.", "status": 200, "data": { "result": 1 }, "message": "OK" } @endcode """ return { "status": Status(status).code, "message": Status(status).name, "description": Status(status).description, "data": data }
def __call__(self, environ, start_response): url_params = None current_url_handler, url_params = self.get_handler(environ) response_text, status_code, extra_headers = current_url_handler( environ, url_params) status_code_message = '{} {}'.format( Status(status_code).code, Status(status_code).name, ) headers = { 'Content-Type': 'text/html', } headers.update(extra_headers) if isinstance(response_text, (list, dict)): response_text = json.dumps(response_text) headers['Content-Type'] = 'text/json' start_response( status_code_message, list(headers.items()), ) return [response_text.encode('utf-8')]
def images(taxon=None, service=None, limit=None, offset=None): """Specimen link retrieval.""" from ..elc import subreq desc_obj = dict() t0 = time() sub_query = 'Specimen images for {0:s} retrieved through {1:s}'.format( taxon.capitalize(), 'iDigBio' if service == 'idigbio' else 'ePANDDA') try: options = params.set_options(req_args=connexion.request.args, endpoint='images') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Call parse function to check for parameter errors try: payload = params.parse(req_args=connexion.request.args, options=options, db='pbdb', endpoint='images') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Query media aggregators try: media = subreq.images_req(payload=payload, options=options) except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Build returned metadata object desc_obj.update(aux.build_meta(options)) desc_obj.update(aux.build_meta_sub(source=sub_query, t0=t0, sub_tag='media_links', options=options, data=media)) # Return data structure to client return jsonify(metadata=desc_obj, records=media)
def mobile(taxon=None, bbox=None): """Lightweight custom response.""" from ..elc import config, subreq return_obj = list() # Set runtime options try: options = params.set_options(req_args=connexion.request.args, endpoint='occ') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # This query only applies to Neotoma and PBDB for db in ['neotoma', 'pbdb']: # Configure parameter payload for api subquery try: payload = params.parse(req_args=connexion.request.args, options=options, db=db, endpoint='occ') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Database API call url_path = ''.join([config.get('resource_api', db), config.get('db_occ_endpt', db)]) try: return_obj = subreq.mobile_req(return_obj=return_obj, url_path=url_path, payload=payload, db=db) except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Return composite data structure to client return jsonify(return_obj)
def test_undefined_code(self): HTTP_Status = Status(code=self.undefined_code) self.assertEqual(HTTP_Status.name, self.default_name_fail) self.assertEqual(HTTP_Status.name, HTTP_Status.name_fail) self.assertEqual(HTTP_Status.description, self.default_description_fail) self.assertEqual(HTTP_Status.description, HTTP_Status.description_fail)
def test_alt_name_description(self): HTTP_Status = Status(code=self.undefined_code, name_fail=self.alt_name_fail, description_fail=self.alt_description_fail) self.assertEqual(HTTP_Status.name, self.alt_name_fail) self.assertEqual(HTTP_Status.name, HTTP_Status.name_fail) self.assertEqual(HTTP_Status.description, self.alt_description_fail) self.assertEqual(HTTP_Status.description, HTTP_Status.description_fail)
def _generate_error(error_message: str, start_response: Callable) -> List[bytes]: """ Generates an error. :param error_message: error message :param start_response: header handler :return: error body """ start_response(_code_and_description(Status(400)), []) return [error_message.encode("utf-8")]
def _endpoint(environment: Dict, start_response: Callable) -> List[bytes]: """ Endpoint handler. :param environment: environment of call :param start_response: header handler :return: body """ path = environment["PATH_INFO"][1:] try: raw_status = int(path) except ValueError: return Server._generate_error(f"Unknown status code: \"{path}\"", start_response) try: status_information = Status(raw_status) except InvalidHttpCode: return Server._generate_error(f"Invalid status code: {raw_status}", start_response) code_and_description = _code_and_description(status_information) start_response(code_and_description, []) return [f"{code_and_description}".encode("utf-8")]
def get_url(url, **kwargs): """Gets a url, handles all the icky requests stuff.""" try: if 'timeout' not in kwargs: kwargs['timeout'] = 20 r = requests.get(url, **kwargs) r.status = Status(r.status_code) if not r.ok: return 'HTTP Error - {code} {name} - {description}'.format( **{ 'code': r.status.code, 'name': r.status.name, 'description': r.status.description }) except requests.exceptions.Timeout: return 'Connection timed out' except requests.exceptions.RequestException as x: return '{}'.format(x.__class__.__name__) return r
def paleocoords(coords=None, age=None, ageunits=None): """ Return paleocoordinates for a given age and modern lat/lon. :param coords: Comma separated latitude,longitude :type coords: str :param age: Numerical or geologic age :param age: str :param ageunits: Units of measure for the ages specified (and returned) :type ageunits: str ('ybp', 'ka' or 'ma') """ t0 = time() desc_obj = dict() # Set runtime options try: options = params.set_options(req_args=connexion.request.args, endpoint='misc') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Call parse function to check for parameter errors try: params.parse(req_args=connexion.request.args, options=options, db='pbdb', endpoint='paleocoords') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Determine paleocoordinates and resolve geologic age if necessary try: paleo, modern, geog_ref = geog.get_geog(coords=coords, age=age, options=options) except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Build returned metadata object desc_obj.update(aux.build_meta(options)) desc_obj.update(aux.build_meta_sub(source=geog_ref, t0=t0, sub_tag='paleo_coord', options=options)) # Return data structure to client return_obj = {'paleo_lat': paleo[1], 'paleo_lon': paleo[0], 'modern_lat': modern[0], 'modern_lon': modern[1], 'age': age} return jsonify(metadata=desc_obj, records=return_obj)
def subtaxa(taxon=None, synonyms=True): """ Retrieve the related lower taxonomy for a given taxon name. :param taxon: taxonomic name :type taxon: str :param synonyms: optionally include synonymous names :type synonyms: bool (default true) """ t0 = time() desc_obj = dict() sub_query = '{0:s} {1:s} synonyms (PBDB systematics)'.format( taxon.capitalize(), 'including' if synonyms else 'excluding') # Set runtime options try: options = params.set_options(req_args=connexion.request.args, endpoint='misc') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Call parse function to check for parameter errors try: params.parse(req_args=connexion.request.args, options=options, db='pbdb', endpoint='subtaxa') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Retrieve lower taxa try: lower_taxa = taxa.get_subtaxa(taxon=taxon, inc_syn=synonyms) except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Build returned metadata object desc_obj.update(aux.build_meta(options)) desc_obj.update(aux.build_meta_sub(source=sub_query, t0=t0, sub_tag='subtaxa', options=options, data=lower_taxa)) # Return data structure to client return jsonify(metadata=desc_obj, records=lower_taxa)
def test_code_match(self): HTTP_NoneStatus = NoneStatus(code=self.correct_code) HTTP_Status = Status(code=self.correct_code) self.assertEqual(HTTP_NoneStatus.code, HTTP_Status.code)
def test__unicode__(self): HTTP_Status = Status(code=self.correct_code) self.assertEqual(HTTP_Status.__unicode__(verbose=True), self.correct_unicode_verbose) self.assertEqual(HTTP_Status.__unicode__(verbose=False), self.correct_unicode)
def test_non_numeric_code(self): with self.assertRaises(Invalid): Status(code=self.non_numeric_code)
def ref(idlist=None, show=None, output=None, run=None): """ Literature references/publications. Accepts the following dataset types: occ, col, dst, ref :param idlist: List of formatted ids [dbname]:[datasettype]:[number] :type idlist: str :param show: Return identifiers or stats (defult=full, idx, poll) :type show: str :param output: Response format (defult=bibjson, csv) :type output: str """ return_obj = list() desc_obj = dict() # Set runtime options try: options = params.set_options(req_args=connexion.request.args, endpoint='ref') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') run_list = aux.get_run_list(connexion.request.args.get('run')) # Cycle through external databases for db in run_list: t0 = time() options.update(skip=False) # Configure parameter payload for api subquery try: payload = params.parse(req_args=connexion.request.args, options=options, db=db, endpoint='ref') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # skip this database if no ids specified if options.get('skip'): continue # Database API call url_path = ''.join( [config.get('resource_api', db), config.get('db_ref_endpt', db)]) try: resp_json, api_call = subreq.trigger(url_path, payload, db) except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Parse database response return_obj = router.response_decode(resp_json=resp_json, return_obj=return_obj, options=options, db=db, endpoint='ref') # Build returned metadata object desc_obj.update(aux.build_meta(options)) desc_obj.update( aux.build_meta_sub(data=return_obj, source=api_call, t0=t0, sub_tag=db, options=options)) # Return composite data structure to client if options.get('output') in ['json', 'bibjson']: if options.get('show') == 'poll': return jsonify(desc_obj) if options.get('show') == 'idx': return jsonify(aux.get_id_numbers(data=return_obj, endpoint='ref')) else: if options.get('output') == 'bibjson': return jsonify(metadata=desc_obj, records=formatter.type_bibjson(return_obj)) else: return jsonify(metadata=desc_obj, records=return_obj) elif options.get('output') == 'json': if options.get('show') == 'poll': return jsonify(desc_obj) if options.get('show') == 'idx': return jsonify(aux.get_id_numbers(data=return_obj, endpoint='ref')) else: return jsonify(metadata=desc_obj, records=return_obj) elif options.get('output') == 'csv': if return_obj: filename = aux.build_filename(endpoint='ref', data=return_obj) return flask_csv.send_csv(return_obj, filename, return_obj[0].keys()) else: msg = 'Unable to generate CSV file. Search returned no records.' return jsonify(status=204, title=Status(204).name, detail=msg, type='about:blank')
def test_exceeds_max_code(self): with self.assertRaises(Invalid): Status(code=self.exceeds_max_code)
def test__unicode__(self): HTTP_Status = Status(code=self.correct_code) self.assertEqual(HTTP_Status.__unicode__(verbose=True), self.correct_unicode_verbose) self.assertEqual(HTTP_Status.__unicode__(verbose=False), self.correct_unicode)
def test_correct_code(self): HTTP_Status = Status(code=self.correct_code) self.assertEqual(HTTP_Status.name, self.correct_name) self.assertEqual(HTTP_Status.description, self.correct_description)
def test_default_code(self): HTTP_Status = Status() self.assertEqual(HTTP_Status.code, self.default_code)
def loc(idlist=None, bbox=None, agerange=None, ageunits=None, timerule=None, coordtype=None, limit=None, offset=None, show=None, output=None, run=None): """Return locale identifiers: collections, datasets etc.""" return_obj = list() desc_obj = dict() # Set runtime options try: options = params.set_options(req_args=connexion.request.args, endpoint='loc') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') run_list = aux.get_run_list(connexion.request.args.get('run')) # Cycle through external databases for db in run_list: t0 = time() options.update(skip=False) # Configure parameter payload for api subquery try: payload = params.parse(req_args=connexion.request.args, options=options, db=db, endpoint='loc') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # skip this database if no ids specified if options.get('skip'): continue # Database API call url_path = ''.join( [config.get('resource_api', db), config.get('db_loc_endpt', db)]) try: resp_json, api_call = subreq.trigger(url_path, payload, db) except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Parse database response return_obj = router.response_decode(resp_json=resp_json, return_obj=return_obj, options=options, db=db, endpoint='loc') # Build returned metadata object desc_obj.update(aux.build_meta(options)) desc_obj.update( aux.build_meta_sub(data=return_obj, source=api_call, t0=t0, sub_tag=db, options=options)) # Return composite data structure to client if options.get('output') == 'json': if options.get('show') == 'poll': return jsonify(desc_obj) if options.get('show') == 'idx': return jsonify( aux.get_id_numbers(data=return_obj, endpoint='locale')) else: return jsonify(metadata=desc_obj, records=return_obj) elif options.get('output') == 'csv': if return_obj: tab_data = formatter.type_csv(return_obj) return Response((x for x in tab_data), mimetype='text/csv') else: msg = 'Unable to generate CSV file. Search returned no records.' return jsonify(status=204, title=Status(204).name, detail=msg, type='about:blank') elif options.get('output') == 'file': import flask_csv if return_obj: filename = aux.build_filename(endpoint='loc', data=return_obj) return flask_csv.send_csv(return_obj, filename, return_obj[0].keys()) else: msg = 'Unable to generate CSV file. Search returned no records.' return jsonify(status=204, title=Status(204).name, detail=msg, type='about:blank')
def timebound(agerange=None, ageunits=None): """ Return min and max ages given a specified bound and units. :param agerange: Comma separated numerical or textual geologic ages :type agerange: str :param ageunits: Units of measure for the ages specified (and returned) :type ageunits: str ('ybp', 'ka' or 'ma') """ t0 = time() desc_obj = dict() # Set runtime options try: options = params.set_options(req_args=connexion.request.args, endpoint='misc') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Call parse function to check for parameter errors try: params.parse(req_args=connexion.request.args, options=options, db='pbdb', endpoint='timebound') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Determine time bounds and resolve geologic age if necessary try: early_age, late_age, \ col_hex, age_ref = ages.get_age(age_range=agerange, options=options) except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Build returned metadata object desc_obj.update(aux.build_meta(options)) desc_obj.update(aux.build_meta_sub(source=age_ref, t0=t0, sub_tag='geo_age', options=options)) # Return data structure to client return_obj = {'early_age': early_age, 'late_age': late_age, 'ics_color': col_hex} return jsonify(metadata=desc_obj, records=return_obj)
def occ(bbox=None, agerange=None, ageuits=None, timerule=None, taxon=None, includelower=None, coordtype=None, limit=None, offset=None, show=None, output=None, run=None): """Paleobiological occurrences in a specific place and time.""" return_obj = list() desc_obj = dict() # Set runtime options try: options = params.set_options(req_args=connexion.request.args, endpoint='occ') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') run_list = aux.get_run_list(connexion.request.args.get('run')) # Cycle through external databases for db in run_list: t0 = time() # Configure parameter payload for api subquery try: payload = params.parse(req_args=connexion.request.args, options=options, db=db, endpoint='occ') except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') url_path = ''.join( [config.get('resource_api', db), config.get('db_occ_endpt', db)]) # Database API call try: resp_json, api_call = subreq.trigger(url_path, payload, db) except ValueError as err: return connexion.problem(status=err.args[0], title=Status(err.args[0]).name, detail=err.args[1], type='about:blank') # Parse database response return_obj = router.response_decode(resp_json=resp_json, return_obj=return_obj, options=options, db=db, endpoint='occ') # Build returned metadata object desc_obj.update(aux.build_meta(options)) desc_obj.update( aux.build_meta_sub(data=return_obj, source=api_call, t0=t0, sub_tag=db, options=options)) # Return composite data structure to client if options.get('output') == 'json': if options.get('show') == 'poll': return jsonify(desc_obj) if options.get('show') == 'idx': return jsonify(aux.get_id_numbers(data=return_obj, endpoint='occ')) else: return jsonify(metadata=desc_obj, records=return_obj) elif options.get('output') == 'csv': if return_obj: filename = aux.build_filename(endpoint='occ', data=return_obj) return flask_csv.send_csv(return_obj, filename, return_obj[0].keys()) else: msg = 'Unable to generate CSV file. Search returned no records.' return jsonify(status=204, title=Status(204).name, detail=msg, type='about:blank')
def test_below_min_code(self): with self.assertRaises(Invalid): Status(code=self.below_min_code)