Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- `email` and `additionalContacts` in API reference json schema (#380)
### Changed
- Updated versions of external actions and MongoDB in GitHub workflows (#374)
- Allow updating contact's email and href separately using the command line (#381)
### Fixed
- Fixed typo on conda installation docs (#373)

Expand Down
1 change: 1 addition & 0 deletions patientMatcher/cli/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import click
from flask.cli import current_app, with_appcontext

from patientMatcher.resources import path_to_benchmark_patients
from patientMatcher.utils.add import add_node, load_demo_patients
from patientMatcher.utils.delete import drop_all_collections
Expand Down
1 change: 1 addition & 0 deletions patientMatcher/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from flask import Flask, current_app
from flask.cli import FlaskGroup, routes_command, run_command, with_appcontext
from flask_mail import Message

from patientMatcher import __version__
from patientMatcher.server import create_app

Expand Down
1 change: 1 addition & 0 deletions patientMatcher/cli/remove.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import click
from flask.cli import current_app, with_appcontext

from patientMatcher.utils.delete import delete_by_query


Expand Down
87 changes: 52 additions & 35 deletions patientMatcher/cli/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

import click
from flask.cli import current_app, with_appcontext

from patientMatcher.parse.patient import EMAIL_REGEX, href_validate
from patientMatcher.utils.patient import patients
from patientMatcher.utils.update import update_resources

LOG = logging.getLogger(__name__)
HREF_FIELD = "contact.href"


@click.group()
Expand All @@ -19,54 +21,69 @@ def update():

@update.command()
@with_appcontext
@click.option(
"-o", "--old-href", type=click.STRING, nargs=1, required=True, help="Old contact href"
)
@click.option("-h", "--href", type=click.STRING, nargs=1, required=True, help="New contact href")
@click.option("-n", "--name", type=click.STRING, nargs=1, required=True, help="New contact name")
@click.option(
"--institution", type=click.STRING, nargs=1, required=False, help="New contact institution"
)
def contact(old_href, href, name, institution):
"""Update contact person for a group of patients"""

# If new contact is a simple email, add "mailto" schema
if bool(EMAIL_REGEX.match(href)) is True and "mailto:" not in href:
href = ":".join(["mailto", href])

if href_validate(href) is False:
LOG.error(
"Provided href does not have a valid schema. Provide either a URL (http://.., https://..) or an email address (mailto:..)"
@click.option("--href", type=click.STRING, required=False, help="Contact's href")
@click.option("--email", type=click.STRING, required=False, help="Contact's email")
@click.option("--new-href", type=click.STRING, required=False, help="New href")
@click.option("--new-email", type=click.STRING, required=False, help="New email")
@click.option("--new-name", type=click.STRING, required=False, help="New name")
@click.option("--new-institution", type=click.STRING, required=False, help="New institution")
def contact(href, email, new_href, new_email, new_name, new_institution):
"""Update contact person for a group of patients."""

# Validate exactly one identifier
if bool(href) == bool(email):
raise click.UsageError("You must provide EITHER --href or --email")

# Validate at least one field to update
if not any([new_href, new_email, new_name, new_institution]):
click.echo(
"Provide at least a field you wish to update: "
"--new-href / --new-email / --new-name / --new-institution"
)
return

database = current_app.db
query = {"contact.href": {"$regex": old_href}}
# Build query
query = {HREF_FIELD: {"$regex": href}} if href else {"contact.email": email}

# Retrieving all patients matching the given old_href
old_contact_patients = patients(database=database, match_query=query)
# Retriving unique contacts for the above patients
match_contacts = list(old_contact_patients.distinct("contact.href"))
database = current_app.db
matching_patients = patients(database=database, match_query=query)
matching_contacts = list(matching_patients.distinct(HREF_FIELD))

if len(match_contacts) == 0:
click.echo(f"No patients found with contact URI '{old_href}'")
if not matching_contacts:
click.echo(f"No patients found with query '{query}'")
return
if len(match_contacts) > 1:
if len(matching_contacts) > 1:
click.echo(
f"Your search for contact url '{old_href}' is returning more than one patients' contact: {match_contacts}.\nPlease restrict your search by typing a different href."
f"Your search for contact query '{query}' is returning more than one patients' contact.\n"
"Please restrict your search by typing a different href/email."
)
return
# Search is returning only one contact, it's OK to use it for updating patients
matches = list(old_contact_patients)
new_contact = dict(href=href, name=name)
if institution:
new_contact["institution"] = institution

# Build update dict
set_options = {}
if new_href:
if EMAIL_REGEX.match(new_href) and not new_href.startswith("mailto:"):
new_href = f"mailto:{new_href}"
if not href_validate(new_href):
LOG.error(
"Provided href does not have a valid schema. Provide either a URL (http://.., https://..) or an email address (mailto:..)"
)
return
set_options[HREF_FIELD] = new_href
if new_email:
set_options["contact.email"] = new_email
if new_name:
set_options["contact.name"] = new_name
if new_institution:
set_options["contact.institution"] = new_institution

# Confirm and update
patient_count = len(list(matching_patients))
if click.confirm(
f"{len(matches)} patients with the old contact href '{matches[0]['contact']['href']}' will be updated with contact info:{new_contact}. Confirm?",
f"{patient_count} patients will be updated with contact info: {set_options}. Confirm?",
abort=True,
):
result = database.patients.update_many(query, {"$set": {"contact": new_contact}})
result = database.patients.update_many(query, {"$set": set_options})
click.echo(f"Contact information was updated for {result.modified_count} patients.")


Expand Down
3 changes: 2 additions & 1 deletion patientMatcher/match/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
import logging

import requests
from werkzeug.datastructures import Headers

from patientMatcher.match.genotype_matcher import match as genomatch
from patientMatcher.match.phenotype_matcher import match as phenomatch
from patientMatcher.parse.patient import json_patient
from werkzeug.datastructures import Headers

LOG = logging.getLogger(__name__)

Expand Down
1 change: 1 addition & 0 deletions patientMatcher/parse/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from urllib.parse import urlparse

from jsonschema import FormatChecker, RefResolver, validate

from patientMatcher.utils.gene import ensembl_to_symbol, entrez_to_symbol, symbol_to_ensembl
from patientMatcher.utils.variant import liftover

Expand Down
6 changes: 1 addition & 5 deletions patientMatcher/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@
from flask import Flask
from flask_mail import Mail
from pymongo import MongoClient
from pymongo.errors import (
ConnectionFailure,
OperationFailure,
ServerSelectionTimeoutError,
)
from pymongo.errors import ConnectionFailure, OperationFailure, ServerSelectionTimeoutError

from patientMatcher.resources import path_to_hpo_terms, path_to_phenotype_annotations
from patientMatcher.utils.notify import TlsSMTPHandler, admins_email_format
Expand Down
1 change: 1 addition & 0 deletions patientMatcher/server/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from flask import current_app, jsonify
from jsonschema import ValidationError

from patientMatcher.__version__ import __version__
from patientMatcher.auth.auth import authorize
from patientMatcher.constants import STATUS_CODES
Expand Down
1 change: 1 addition & 0 deletions patientMatcher/server/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from bson import json_util
from flask import Blueprint, current_app, jsonify, render_template, request, send_from_directory
from flask_negotiate import consumes, produces

from patientMatcher.auth.auth import authorize
from patientMatcher.match.handler import internal_matcher, patient_matches
from patientMatcher.utils.add import backend_add_patient
Expand Down
3 changes: 2 additions & 1 deletion patientMatcher/utils/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
import sys

import enlighten
from pymongo import MongoClient

from patientMatcher.match.handler import external_matcher
from patientMatcher.parse.patient import mme_patient
from pymongo import MongoClient

LOG = logging.getLogger(__name__)

Expand Down
1 change: 1 addition & 0 deletions patientMatcher/utils/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from logging.handlers import SMTPHandler

from flask_mail import Message

from patientMatcher.__version__ import __version__

LOG = logging.getLogger(__name__)
Expand Down
3 changes: 0 additions & 3 deletions patientMatcher/utils/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,7 @@ def patients(database, ids=None, match_query=None):
if ids: # if only specified patients should be returned
query["_id"] = {"$in": ids}
elif match_query:
LOG.info("Return patients matching query {}".format(match_query))
query = match_query
else:
LOG.info("Return all patients in database")

results = database["patients"].find(query)
return results
1 change: 1 addition & 0 deletions patientMatcher/utils/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import requests
from clint.textui import progress

from patientMatcher.constants import PHENOTYPE_TERMS

LOG = logging.getLogger(__name__)
Expand Down
1 change: 1 addition & 0 deletions tests/backend/test_backend_patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os

import pytest

from patientMatcher.parse.patient import mme_patient
from patientMatcher.utils.add import backend_add_patient, load_demo_patients
from patientMatcher.utils.delete import delete_by_query
Expand Down
1 change: 1 addition & 0 deletions tests/cli/test_commands.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask_mail import Message

from patientMatcher.cli.commands import cli


Expand Down
Loading
Loading