def mme_match_proxy(request): """ -This is a proxy URL for backend MME server as per MME spec. -Looks for matches for the given individual ONLY in the local MME DB. -Expects a single patient (as per MME spec) in the POST Args: None, all data in POST under key "patient_data" Returns: Status code and results (as per MME spec), returns raw results from MME Server NOTES: 1. login is not required, since AUTH is handled by MME server, hence missing decorator @login_required """ query_patient_data = '' for line in request.readlines(): query_patient_data = query_patient_data + ' ' + line response = proxy_request(request, MME_LOCAL_MATCH_URL, data=query_patient_data) if response.status_code == 200: try: _generate_notification_for_incoming_match(response, request, query_patient_data) except Exception: logger.error( 'Unable to create notification for incoming MME match request') return response
def _handle_phenotips_save_request(request, patient_id): """Update the seqr SQL database record for this patient with the just-saved phenotype data.""" url = '/rest/patients/%s' % patient_id cookie_header = request.META.get('HTTP_COOKIE') http_headers = {'Cookie': cookie_header} if cookie_header else {} response = proxy_request(request, url, headers=http_headers, method='GET', scheme='http', host=settings.PHENOTIPS_SERVER) if response.status_code != 200: logger.error("ERROR: unable to retrieve patient json. %s %s %s" % ( url, response.status_code, response.reason_phrase)) return patient_json = json.loads(response.content) try: if patient_json.get('external_id'): # prefer to use the external id for legacy reasons: some projects shared phenotips # records by sharing the phenotips internal id, so in rare cases, the # Individual.objects.get(phenotips_patient_id=...) may match multiple Individual records individual = Individual.objects.get(phenotips_eid=patient_json['external_id']) else: individual = Individual.objects.get(phenotips_patient_id=patient_json['id']) except ObjectDoesNotExist as e: logger.error("ERROR: PhenoTips patient id %s not found in seqr Individuals." % patient_json['id']) return _update_individual_phenotips_data(individual, patient_json)
def phenotips_edit_handler(request, project_guid, patient_id): """Request the PhenoTips Edit page for the given patient_id, and forwards PhenoTips' response to the client. Args: request: Django HTTP request object project_guid (string): project GUID for the seqr project containing this individual patient_id (string): PhenoTips internal patient id """ # query string forwarding needed for PedigreeEditor button query_string = request.META["QUERY_STRING"] url = "/bin/edit/data/{patient_id}?{query_string}".format( patient_id=patient_id, query_string=query_string) project = Project.objects.get(guid=project_guid) check_permissions(project, request.user, CAN_EDIT) auth_tuple = _get_phenotips_username_and_password( request.user, project, permissions_level=CAN_EDIT) return proxy_request(request, url, headers={}, auth_tuple=auth_tuple, host=settings.PHENOTIPS_SERVER)
def mme_match_proxy(request): """ -This is a proxy URL for backend MME server as per MME spec. -Looks for matches for the given individual ONLY in the local MME DB. -Expects a single patient (as per MME spec) in the POST Args: None, all data in POST under key "patient_data" Returns: Status code and results (as per MME spec), returns raw results from MME Server NOTES: 1. login is not required, since AUTH is handled by MME server, hence missing decorator @login_required """ query_patient_data = '' for line in request.readlines(): query_patient_data = query_patient_data + ' ' + line response = proxy_request(request, MME_LOCAL_MATCH_URL, data=query_patient_data) if response.status_code == 200: try: _generate_notification_for_incoming_match(response, request, query_patient_data) except Exception: logger.error('Unable to create slack notification for incoming MME match request') return response
def _phenotips_view_handler(request, project_guid, individual_guid, url_template, permission_level=CAN_VIEW): """Requests the PhenoTips PDF for the given patient_id, and forwards PhenoTips' response to the client. Args: request: Django HTTP request object project_guid (string): project GUID for the seqr project containing this individual individual_guid (string): individual GUID for the seqr individual corresponding to the desired patient """ project = Project.objects.get(guid=project_guid) check_permissions(project, request.user, CAN_VIEW) individual = Individual.objects.get(guid=individual_guid) _create_patient_if_missing(project, individual) _set_phenotips_patient_id_if_missing(project, individual) # query string forwarding needed for PedigreeEditor button query_string = request.META["QUERY_STRING"] url = url_template.format(patient_id=individual.phenotips_patient_id, query_string=query_string) auth_tuple = _get_phenotips_username_and_password( request.user, project, permissions_level=permission_level) return proxy_request(request, url, headers={}, auth_tuple=auth_tuple, host=settings.PHENOTIPS_SERVER)
def fetch_igv_track(request, project_guid, igv_track_path): get_project_and_check_permissions(project_guid, request.user) logger.info("Proxy Request: %s %s" % (request.method, igv_track_path)) is_cram = igv_track_path.split('?')[0].endswith('.cram') if is_cram: absolute_path = "/alignments?reference=igvjs/static/data/public/Homo_sapiens_assembly38.fasta&file=igvjs/static/data/readviz-mounts/{0}&options={1}®ion={2}".format( igv_track_path, request.GET.get('options', ''), request.GET.get('region', '')) request_kwargs = { 'host': settings.READ_VIZ_CRAM_PATH, 'scheme': 'http', 'filter_request_headers': True, 'stream': True } else: absolute_path = os.path.join(settings.READ_VIZ_BAM_PATH, igv_track_path) request_kwargs = { 'auth_tuple': ('xbrowse-bams', 'xbrowse-bams'), 'verify': False } return proxy_request(request, absolute_path, **request_kwargs)
def proxy_to_phenotips(request): """This django view accepts GET and POST requests and forwards them to PhenoTips""" url = request.get_full_path() if any([k for k in DO_NOT_PROXY_URL_KEYWORDS if k.lower() in url.lower()]): logger.warn("Blocked proxy url: " + str(url)) return HttpResponse(status=204) logger.info("Proxying url: " + str(url)) # Some PhenoTips endpoints that use HTTP redirects lose the phenotips JSESSION auth cookie # along the way, and don't proxy correctly. Using a Session object as below to store the cookies # provides a work-around. phenotips_session = requests.Session() for key, value in request.COOKIES.items(): phenotips_session.cookies.set(key, value) http_response = proxy_request(request, url, data=request.body, session=phenotips_session, host=settings.PHENOTIPS_SERVER, filter_request_headers=True) # if this is the 'Quick Save' request, also save a copy of phenotips data in the seqr SQL db. match = re.match(PHENOTIPS_QUICK_SAVE_URL_REGEX, url) if match: _handle_phenotips_save_request(request, patient_id=match.group(1)) return http_response
def _handle_phenotips_save_request(request, patient_id): """Update the seqr SQL database record for this patient with the just-saved phenotype data.""" url = '/rest/patients/%s' % patient_id cookie_header = request.META.get('HTTP_COOKIE') http_headers = {'Cookie': cookie_header} if cookie_header else {} response = proxy_request(request, url, headers=http_headers, method='GET', scheme='http', host=PHENOTIPS_SERVER) if response.status_code != 200: logger.error("ERROR: unable to retrieve patient json. %s %s %s" % ( url, response.status_code, response.reason_phrase)) return patient_json = json.loads(response.content) try: if patient_json.get('external_id'): # prefer to use the external id for legacy reasons: some projects shared phenotips # records by sharing the phenotips internal id, so in rare cases, the # Individual.objects.get(phenotips_patient_id=...) may match multiple Individual records individual = Individual.objects.get(phenotips_eid=patient_json['external_id']) else: individual = Individual.objects.get(phenotips_patient_id=patient_json['id']) except ObjectDoesNotExist as e: logger.error("ERROR: PhenoTips patient id %s not found in seqr Individuals." % patient_json['id']) return _update_individual_phenotips_data(individual, patient_json)
def proxy_to_phenotips(request): """This django view accepts GET and POST requests and forwards them to PhenoTips""" url = request.get_full_path() if any([k for k in DO_NOT_PROXY_URL_KEYWORDS if k.lower() in url.lower()]): logger.warn("Blocked proxy url: " + str(url)) return HttpResponse(status=204) logger.info("Proxying url: " + str(url)) # Some PhenoTips endpoints that use HTTP redirects lose the phenotips JSESSION auth cookie # along the way, and don't proxy correctly. Using a Session object as below to store the cookies # provides a work-around. phenotips_session = requests.Session() for key, value in request.COOKIES.items(): phenotips_session.cookies.set(key, value) http_response = proxy_request(request, url, data=request.body, session=phenotips_session, host=settings.PHENOTIPS_SERVER, filter_request_headers=True) # if this is the 'Quick Save' request, also save a copy of phenotips data in the seqr SQL db. match = re.match(PHENOTIPS_QUICK_SAVE_URL_REGEX, url) if match: _handle_phenotips_save_request(request, patient_id=match.group(1)) return http_response
def _make_api_call(method, url, http_headers={}, data=None, auth_tuple=None, expected_status_code=200, parse_json_resonse=True, verbose=False): """Utility method for making an API call and then parsing & returning the json response. Args: method (string): 'GET' or 'POST' url (string): url path, starting with '/' (eg. '/bin/edit/data/P0000001') data (string): request body - used for POST, PUT, and other such requests. auth_tuple (tuple): ("username", "password") pair expected_status_code (int or list): expected server response code parse_json_resonse (bool): whether to parse and return the json response verbose (bool): whether to print details about the request & response Returns: json object or None if response content is empty """ try: response = proxy_request(None, url, headers=http_headers, method=method, scheme='http', data=data, auth_tuple=auth_tuple, host=settings.PHENOTIPS_SERVER, verbose=verbose) except requests.exceptions.RequestException as e: raise PhenotipsException(e.message) if (isinstance(expected_status_code, int) and response.status_code != expected_status_code) or ( isinstance(expected_status_code, list) and response.status_code not in expected_status_code): raise PhenotipsException( "Unable to retrieve %s. response code = %s: %s" % (url, response.status_code, response.reason_phrase)) if parse_json_resonse: if not response.content: return {} try: return json.loads(response.content) except ValueError as e: logger.error( "Unable to parse PhenoTips response for %s request to %s" % (method, url)) raise PhenotipsException("Unable to parse response for %s:\n%s" % (url, e)) else: return dict(response.items())
def proxy_to_kibana(request): try: return proxy_request(request, host=KIBANA_SERVER, url=request.get_full_path(), data=request.body, stream=True) # use stream=True because kibana returns gziped responses, and this prevents the requests module from # automatically unziping them except ConnectionError as e: logger.error(e) return HttpResponse("Error: Unable to connect to Kibana")
def proxy_to_kibana(request): try: return proxy_request(request, host=KIBANA_SERVER, url=request.get_full_path(), data=request.body, stream=True) # use stream=True because kibana returns gziped responses, and this prevents the requests module from # automatically unziping them except ConnectionError as e: logger.error(e) return HttpResponse("Error: Unable to connect to Kibana")
def proxy_to_igv(igv_track_path, params, request=None, **request_kwargs): is_cram = igv_track_path.split('?')[0].endswith('.cram') if is_cram: absolute_path = "/alignments?reference=igvjs/static/data/public/Homo_sapiens_assembly38.fasta&file=igvjs/static/data/readviz-mounts/{0}&options={1}®ion={2}".format( igv_track_path, params.get('options', ''), params.get('region', '')) request_kwargs.update({'host': settings.READ_VIZ_CRAM_PATH, 'stream': True}) else: absolute_path = os.path.join(settings.READ_VIZ_BAM_PATH, igv_track_path) request_kwargs.update({'auth_tuple': ('xbrowse-bams', 'xbrowse-bams'), 'verify': False}) return proxy_request(request, absolute_path, **request_kwargs)
def mme_metrics_proxy(request): """ -This is a proxy URL for backend MME server as per MME spec. -Proxies public metrics endpoint Args: None, all data in POST under key "patient_data" Returns: Metric JSON from matchbox NOTES: 1. seqr login IS NOT required, since AUTH via toke in POST is handled by MME server, hence no decorator @login_required. This is a PUBLIC endpoint """ return proxy_request(request, MME_MATCHBOX_PUBLIC_METRICS_URL)
def mme_metrics_proxy(request): """ -This is a proxy URL for backend MME server as per MME spec. -Proxies public metrics endpoint Args: None, all data in POST under key "patient_data" Returns: Metric JSON from matchbox NOTES: 1. seqr login IS NOT required, since AUTH via toke in POST is handled by MME server, hence no decorator @login_required. This is a PUBLIC endpoint """ return proxy_request(request, MME_MATCHBOX_PUBLIC_METRICS_URL)
def _make_api_call( method, url, http_headers={}, data=None, auth_tuple=None, expected_status_code=200, parse_json_resonse=True, verbose=False): """Utility method for making an API call and then parsing & returning the json response. Args: method (string): 'GET' or 'POST' url (string): url path, starting with '/' (eg. '/bin/edit/data/P0000001') data (string): request body - used for POST, PUT, and other such requests. auth_tuple (tuple): ("username", "password") pair expected_status_code (int or list): expected server response code parse_json_resonse (bool): whether to parse and return the json response verbose (bool): whether to print details about the request & response Returns: json object or None if response content is empty """ try: response = proxy_request(None, url, headers=http_headers, method=method, scheme='http', data=data, auth_tuple=auth_tuple, host=settings.PHENOTIPS_SERVER, verbose=verbose) except requests.exceptions.RequestException as e: raise PhenotipsException(e.message) if (isinstance(expected_status_code, int) and response.status_code != expected_status_code) or ( isinstance(expected_status_code, list) and response.status_code not in expected_status_code): raise PhenotipsException("Unable to retrieve %s. response code = %s: %s" % ( url, response.status_code, response.reason_phrase)) if parse_json_resonse: if not response.content: return {} try: return json.loads(response.content) except ValueError as e: logger.error("Unable to parse PhenoTips response for %s request to %s" % (method, url)) raise PhenotipsException("Unable to parse response for %s:\n%s" % (url, e)) else: return dict(response.items())
def proxy_to_igv(igv_track_path, params, request=None, **request_kwargs): is_cram = igv_track_path.split('?')[0].endswith('.cram') if is_cram: absolute_path = "/alignments?reference=igvjs/static/data/public/Homo_sapiens_assembly38.fasta&file=igvjs/static/data/readviz-mounts/{0}&options={1}®ion={2}".format( igv_track_path, params.get('options', ''), params.get('region', '')) request_kwargs.update({ 'host': settings.READ_VIZ_CRAM_PATH, 'stream': True }) else: absolute_path = os.path.join(settings.READ_VIZ_BAM_PATH, igv_track_path) request_kwargs.update({ 'auth_tuple': ('xbrowse-bams', 'xbrowse-bams'), 'verify': False }) return proxy_request(request, absolute_path, **request_kwargs)
def phenotips_pdf_handler(request, project_guid, patient_id): """Requests the PhenoTips PDF for the given patient_id, and forwards PhenoTips' response to the client. Args: request: Django HTTP request object project_guid (string): project GUID for the seqr project containing this individual patient_id (string): PhenoTips internal patient id """ url = "/bin/export/data/{patient_id}?format=pdf&pdfcover=0&pdftoc=0&pdftemplate=PhenoTips.PatientSheetCode".format( patient_id=patient_id) project = Project.objects.get(guid=project_guid) check_permissions(project, request.user, CAN_VIEW) auth_tuple = _get_phenotips_username_and_password( request.user, project, permissions_level=CAN_VIEW) return proxy_request(request, url, headers={}, auth_tuple=auth_tuple, host=settings.PHENOTIPS_SERVER)
def _phenotips_view_handler(request, project_guid, individual_guid, url_template, permission_level=CAN_VIEW): """Requests the PhenoTips PDF for the given patient_id, and forwards PhenoTips' response to the client. Args: request: Django HTTP request object project_guid (string): project GUID for the seqr project containing this individual individual_guid (string): individual GUID for the seqr individual corresponding to the desired patient """ project = Project.objects.get(guid=project_guid) check_permissions(project, request.user, CAN_VIEW) individual = Individual.objects.get(guid=individual_guid) _create_patient_if_missing(project, individual) _set_phenotips_patient_id_if_missing(project, individual) # query string forwarding needed for PedigreeEditor button query_string = request.META["QUERY_STRING"] url = url_template.format(patient_id=individual.phenotips_patient_id, query_string=query_string) auth_tuple = _get_phenotips_username_and_password(request.user, project, permissions_level=permission_level) return proxy_request(request, url, headers={}, auth_tuple=auth_tuple, host=settings.PHENOTIPS_SERVER)
def mme_metrics_proxy(request): return proxy_request(request, MME_MATCHBOX_METRICS_URL, headers=MME_HEADERS)
def mme_metrics_proxy(request): return proxy_request(request, MME_MATCHBOX_METRICS_URL, headers=MME_HEADERS)