def _generate_notification_for_seqr_match(submission, results): """ Generate a notifcation to say that a match happened initiated from a seqr user. """ matches = [] hpo_terms_by_id, genes_by_id, _ = get_mme_genes_phenotypes_for_results(results) for result in results: patient = result['patient'] gene_message = '' if patient.get('genomicFeatures'): gene_symbols = set() for gene in patient['genomicFeatures']: gene_symbol = gene['gene']['id'] if gene_symbol.startswith('ENSG'): gene_symbol = genes_by_id.get(gene_symbol, {}).get('geneSymbol', gene_symbol) gene_symbols.add(gene_symbol) gene_message = ' with genes {}'.format(' '.join(sorted(gene_symbols))) phenotypes_message = '' if patient.get('features'): phenotypes_message = ' with phenotypes {}'.format(' '.join( ['{} ({})'.format(feature['id'], hpo_terms_by_id.get(feature['id'])) for feature in patient['features']] )) matches.append(' - From {contact} at institution {institution}{gene_message}{phenotypes_message}.'.format( contact=patient['contact'].get('name', '(none given)'), institution=patient['contact'].get('institution', '(none given)'), gene_message=gene_message, phenotypes_message=phenotypes_message, )) individual = submission.individual project = individual.family.project message = u""" A search from a seqr user from project {project} individual {individual_id} had the following new match(es): {matches} {host}project/{project_guid}/family_page/{family_guid}/matchmaker_exchange """.format( project=project.name, individual_id=individual.individual_id, matches='\n\n'.join(matches), host=BASE_URL, project_guid=project.guid, family_guid=submission.individual.family.guid, ) post_to_slack(MME_SLACK_SEQR_MATCH_NOTIFICATION_CHANNEL, message) emails = map(lambda s: s.strip().split('mailto:')[-1], submission.contact_href.split(',')) email_message = EmailMessage( subject=u'New matches found for MME submission {} (project: {})'.format(individual.individual_id, project.name), body=message, to=[email for email in emails if email != MME_DEFAULT_CONTACT_EMAIL], from_email=MME_DEFAULT_CONTACT_EMAIL, ) email_message.send()
def _search_external_matches(nodes_to_query, patient_data): body = {'_disclaimer': MME_DISCLAIMER} body.update(patient_data) external_results = [] submission_gene_ids = set() for feature in patient_data['patient'].get('genomicFeatures', []): submission_gene_ids.update(get_gene_ids_for_feature(feature, {})) for node in nodes_to_query: headers = { 'X-Auth-Token': node['token'], 'Accept': MME_ACCEPT_HEADER, 'Content-Type': MME_ACCEPT_HEADER, 'Content-Language': 'en-US', } try: external_result = requests.post(url=node['url'], headers=headers, data=json.dumps(body)) if external_result.status_code != 200: try: message = external_result.json().get('message') except Exception: message = external_result.content error_message = '{} ({})'.format(message or 'Error', external_result.status_code) raise Exception(error_message) node_results = external_result.json()['results'] logger.info('Found {} matches from {}'.format( len(node_results), node['name'])) if node_results: _, _, gene_symbols_to_ids = get_mme_genes_phenotypes_for_results( node_results) invalid_results = [] for result in node_results: if (not submission_gene_ids) or \ _is_valid_external_match(result, submission_gene_ids, gene_symbols_to_ids): external_results.append(result) else: invalid_results.append(result) if invalid_results: error_message = 'Received {} invalid matches from {}'.format( len(invalid_results), node['name']) logger.error(error_message) except Exception as e: error_message = 'Error searching in {}: {}\n(Patient info: {})'.format( node['name'], e.message, json.dumps(patient_data)) logger.error(error_message) post_to_slack(MME_SLACK_ALERT_NOTIFICATION_CHANNEL, error_message) return external_results
def _parse_mme_results(submission, saved_results, user, additional_genes=None, response_json=None): results = [] contact_institutions = set() prefetch_related_objects(saved_results, 'originating_submission__individual__family__project') for result_model in saved_results: result = result_model.result_data result['matchStatus'] = _get_json_for_model(result_model) if result_model.originating_submission: originating_family = result_model.originating_submission.individual.family if has_project_permissions(originating_family.project, user): result['originatingSubmission'] = { 'originatingSubmissionGuid': result_model.originating_submission.guid, 'familyGuid': originating_family.guid, 'projectGuid': originating_family.project.guid, } results.append(result) contact_institutions.add(result['patient']['contact'].get('institution', '').strip().lower()) additional_hpo_ids = {feature['id'] for feature in (submission.features or []) if feature.get('id')} if not additional_genes: additional_genes = set() additional_genes.update({gene_feature['gene']['id'] for gene_feature in (submission.genomic_features or [])}) hpo_terms_by_id, genes_by_id, gene_symbols_to_ids = get_mme_genes_phenotypes_for_results( results, additional_genes=additional_genes, additional_hpo_ids=additional_hpo_ids) parsed_results = [_parse_mme_result(res, hpo_terms_by_id, gene_symbols_to_ids, submission.guid) for res in results] parsed_results_gy_guid = {result['matchStatus']['matchmakerResultGuid']: result for result in parsed_results} contact_notes = {note.institution: _get_json_for_model(note, user=user) for note in MatchmakerContactNotes.objects.filter(institution__in=contact_institutions)} submission_json = get_json_for_matchmaker_submission( submission, individual_guid=submission.individual.guid, additional_model_fields=['contact_name', 'contact_href', 'submission_id'] ) submission_json.update({ 'mmeResultGuids': list(parsed_results_gy_guid.keys()), 'phenotypes': parse_mme_features(submission.features, hpo_terms_by_id), 'geneVariants': parse_mme_gene_variants(submission.genomic_features, gene_symbols_to_ids), }) response = { 'mmeResultsByGuid': parsed_results_gy_guid, 'mmeContactNotes': contact_notes, 'mmeSubmissionsByGuid': {submission.guid: submission_json}, 'individualsByGuid': {submission.individual.guid: {'mmeSubmissionGuid': submission.guid}}, 'genesById': genes_by_id, } if response_json: response.update(response_json) return create_json_response(response)
def _generate_notification_for_incoming_match(results, incoming_query, incoming_request_node, incoming_patient): """ Generate a SLACK notifcation to say that a VALID match request came in and the following results were sent back. If Slack is not supported, a message is not sent, but details persisted. Args: response_from_matchbox (python requests object): contains the response from matchbox incoming_request (Django request object): The request that came into the view incoming_patient (JSON): The query patient JSON structure from outside MME node that was matched with """ incoming_patient_id = incoming_patient['patient']['id'] logger.info('{} MME matches found for patient {} from {}'.format( len(results), incoming_patient_id, incoming_request_node)) institution = incoming_patient['patient']['contact'].get( 'institution', incoming_request_node) contact_href = incoming_patient['patient']['contact'].get( 'href', '(sorry I was not able to read the information given for URL)') if not results: message_template = """A match request for {patient_id} came in from {institution} today. The contact information given was: {contact}. We didn't find any individuals in matchbox that matched that query well, *so no results were sent back*.""" post_to_slack( MME_SLACK_MATCH_NOTIFICATION_CHANNEL, message_template.format(institution=institution, patient_id=incoming_patient_id, contact=contact_href)) return new_matched_results = MatchmakerResult.objects.filter( originating_query=incoming_query).prefetch_related('submission') if not new_matched_results: message_template = """A match request for {patient_id} came in from {institution} today. The contact information given was: {contact}. We found {existing_results} existing matching individuals but no new ones, *so no results were sent back*.""" post_to_slack( MME_SLACK_MATCH_NOTIFICATION_CHANNEL, message_template.format(institution=institution, patient_id=incoming_patient_id, contact=contact_href, existing_results=len(results))) return hpo_terms_by_id, genes_by_id, _ = get_mme_genes_phenotypes_for_results( [incoming_patient]) match_results = [] all_emails = set() for result in new_matched_results: submission = result.submission individual = submission.individual project = individual.family.project result_text = u"""seqr ID {individual_id} from project {project_name} in family {family_id} inserted into matchbox on {insertion_date}, with seqr link {host}project/{project_guid}/family_page/{family_guid}/matchmaker_exchange""".replace( '\n', ' ').format( individual_id=individual.individual_id, project_guid=project.guid, project_name=project.name, family_guid=individual.family.guid, family_id=individual.family.family_id, insertion_date=submission.created_date.strftime('%b %d, %Y'), host=BASE_URL) emails = [ email.strip() for email in submission.contact_href.replace( 'mailto:', '').split(',') if email.strip() != MME_DEFAULT_CONTACT_EMAIL ] all_emails.update(emails) match_results.append((result_text, emails)) match_results = sorted(match_results, key=lambda result_tuple: result_tuple[0]) base_message = u"""Dear collaborators, matchbox found a match between a patient from {query_institution} and the following {number_of_results} case(s) in matchbox. The following information was included with the query, genes: {incoming_query_genes} phenotypes: {incoming_query_phenotypes} contact: {incoming_query_contact_name} email: {incoming_query_contact_url} We sent back the following: """.format( query_institution=institution, number_of_results=len(results), incoming_query_genes=', '.join( sorted([gene['geneSymbol'] for gene in genes_by_id.values()])), incoming_query_phenotypes=', '.join([ '{} ({})'.format(hpo_id, term) for hpo_id, term in hpo_terms_by_id.items() ]), incoming_query_contact_url=contact_href, incoming_query_contact_name=incoming_patient['patient']['contact'].get( 'name', '(sorry I was not able to read the information given for name'), ) message_template = u"""{base_message}{match_results} We sent this email alert to: {email_addresses_alert_sent_to}\n{footer}.""" post_to_slack( MME_SLACK_MATCH_NOTIFICATION_CHANNEL, message_template.format( base_message=base_message, match_results='\n'.join([text for text, _ in match_results]), email_addresses_alert_sent_to=', '.join(sorted(all_emails)), footer=MME_EMAIL_FOOTER)) for result_text, emails in match_results: email_message = EmailMessage( subject='Received new MME match', body=message_template.format( base_message=base_message, match_results=result_text, email_addresses_alert_sent_to=', '.join(emails), footer=MME_EMAIL_FOOTER), to=emails, from_email=MME_DEFAULT_CONTACT_EMAIL, ) email_message.send()