def test_json_for_individual(self):
        individual = Individual.objects.first()
        json = _get_json_for_individual(individual)

        individual_fields = {
            'projectGuid',
            'familyGuid',
            'individualGuid',
            'caseReviewStatusLastModifiedBy',
            'phenotipsData',
            'individualId',
            'paternalId',
            'maternalId',
            'sex',
            'affected',
            'displayName',
            'notes',
            'phenotipsPatientId',
            'phenotipsData',
            'createdDate',
            'lastModifiedDate',
            'paternalGuid',
            'maternalGuid',
        }
        self.assertSetEqual(set(json.keys()), individual_fields)

        individual_fields.update({
            'caseReviewStatus',
            'caseReviewDiscussion',
            'caseReviewStatusLastModifiedDate',
            'caseReviewStatusLastModifiedBy',
        })
        user = User.objects.filter(is_staff=True).first()
        json = _get_json_for_individual(individual, user)
        self.assertSetEqual(set(json.keys()), individual_fields)
    def test_json_for_individual(self):
        individual = Individual.objects.first()
        json = _get_json_for_individual(individual)
        self.assertSetEqual(set(json.keys()), INDIVIDUAL_FIELDS)

        user = User.objects.filter(is_staff=True).first()
        json = _get_json_for_individual(individual, user)
        self.assertSetEqual(set(json.keys()), INTERNAL_INDIVIDUAL_FIELDS)
Exemple #3
0
def update_individual_handler(request, individual_guid):
    """Updates a single field in an Individual record.

    Args:
        request (object): Django HTTP Request object.
        individual_guid (string): GUID of the Individual.

    Request:
        body should be a json dictionary like: { 'value': xxx }

    Response:
        json dictionary representing the updated individual like:
            {
                <individualGuid> : {
                    individualId: xxx,
                    sex: xxx,
                    affected: xxx,
                    ...
                }
            }
    """

    individual = Individual.objects.get(guid=individual_guid)

    project = individual.family.project

    check_permissions(project, request.user, CAN_EDIT)

    request_json = json.loads(request.body)

    update_individual_from_json(individual, request_json, user=request.user, allow_unknown_keys=True)

    return create_json_response({
        individual.guid: _get_json_for_individual(individual, request.user)
    })
Exemple #4
0
def update_individual_field_handler(request, individual_guid, field_name):
    """Updates an Individual record.

    Args:
        individual_guid (string): GUID of the individual.
        field_name (string): Name of Individual record field to update (eg. "affected").
    """

    individual = Individual.objects.get(guid=individual_guid)

    # check permission
    project = individual.family.project
    if not request.user.is_staff and not request.user.has_perm(
            CAN_EDIT, project):
        raise PermissionDenied("%s does not have EDIT permissions for %s" %
                               (request.user, project))

    request_json = json.loads(request.body)
    if "value" not in request_json:
        raise ValueError("Request is missing 'value' key")

    individual_json = {field_name: request_json['value']}
    update_individual_from_json(individual, individual_json)

    return create_json_response(
        {individual.guid: _get_json_for_individual(individual, request.user)})
Exemple #5
0
def update_individual_hpo_terms(request, individual_guid):
    """Updates features fields for the given Individual
    """

    individual = Individual.objects.get(guid=individual_guid)

    project = individual.family.project

    check_project_permissions(project, request.user, can_edit=True)

    request_json = json.loads(request.body)

    for feature_key in [
            'features', 'absentFeatures', 'nonstandardFeatures',
            'absentNonstandardFeatures'
    ]:
        orm_key = _to_snake_case(feature_key)
        value = [get_parsed_feature(feature) for feature in request_json[feature_key]] \
            if request_json.get(feature_key) else None
        setattr(individual, orm_key, value)
    individual.save()

    return create_json_response({
        individual.guid:
        _get_json_for_individual(individual,
                                 request.user,
                                 add_hpo_details=True)
    })
Exemple #6
0
def update_individual_hpo_terms(request, individual_guid):
    """Updates features fields for the given Individual
    """

    individual = Individual.objects.get(guid=individual_guid)

    project = individual.family.project

    check_project_permissions(project, request.user, can_edit=True)

    request_json = json.loads(request.body)

    update_json = {
        key:
        [get_parsed_feature(feature)
         for feature in request_json[key]] if request_json.get(key) else None
        for key in [
            'features', 'absentFeatures', 'nonstandardFeatures',
            'absentNonstandardFeatures'
        ]
    }
    update_model_from_json(individual, update_json, user=request.user)

    return create_json_response({
        individual.guid:
        _get_json_for_individual(individual,
                                 request.user,
                                 add_hpo_details=True)
    })
    def test_json_for_individual(self, mock_analyst_group):
        individual = Individual.objects.first()
        json = _get_json_for_individual(individual)
        self.assertSetEqual(set(json.keys()), INDIVIDUAL_FIELDS_NO_FEATURES)

        json = _get_json_for_individual(individual, add_hpo_details=True)
        self.assertSetEqual(set(json.keys()), INDIVIDUAL_FIELDS)

        user = User.objects.get(username='******')
        json = _get_json_for_individual(individual, user, add_hpo_details=True)
        self.assertSetEqual(set(json.keys()), INDIVIDUAL_FIELDS)

        mock_analyst_group.__bool__.return_value = True
        mock_analyst_group.resolve_expression.return_value = 'analysts'
        json = _get_json_for_individual(individual, user, add_hpo_details=True)
        self.assertSetEqual(set(json.keys()), INTERNAL_INDIVIDUAL_FIELDS)
    def test_json_for_individual(self):
        individual = Individual.objects.first()
        json = _get_json_for_individual(individual)

        individual_fields = {
            'projectGuid', 'familyGuid', 'individualGuid', 'caseReviewStatusLastModifiedBy', 'phenotipsData',
            'individualId', 'paternalId', 'maternalId', 'sex', 'affected', 'displayName', 'notes',
            'phenotipsPatientId', 'phenotipsData', 'createdDate', 'lastModifiedDate', 'paternalGuid', 'maternalGuid',
            'mmeSubmittedDate', 'mmeDeletedDate',
        }
        self.assertSetEqual(set(json.keys()), individual_fields)

        individual_fields.update({
            'caseReviewStatus', 'caseReviewDiscussion',
            'caseReviewStatusLastModifiedDate', 'caseReviewStatusLastModifiedBy',
        })
        user = User.objects.filter(is_staff=True).first()
        json = _get_json_for_individual(individual, user)
        self.assertSetEqual(set(json.keys()), individual_fields)
Exemple #9
0
def case_review_page_data(request, project_guid):
    """Returns a JSON object containing information used by the case review page:
    ::

      json_response = {
         'user': {..},
         'project': {..},
         'familiesByGuid': {..},
         'individualsByGuid': {..},
       }
    Args:
        project_guid (string): GUID of the project being case-reviewed.
    """

    # get all families in this project
    project = get_project_and_check_permissions(project_guid, request.user)

    json_response = {
        'user': _get_json_for_user(request.user),
        'project': _get_json_for_project(project, request.user),
        'familiesByGuid': {},
        'individualsByGuid': {},
    }

    for i in Individual.objects.select_related('family').filter(
            family__project=project):

        # filter out individuals that were never in case review
        if not i.case_review_status:
            continue

        # process family record if it hasn't been added already
        family = i.family
        if family.guid not in json_response['familiesByGuid']:
            json_response['familiesByGuid'][
                family.guid] = _get_json_for_family(family, request.user)
            json_response['familiesByGuid'][
                family.guid]['individualGuids'] = []

        json_response['individualsByGuid'][i.guid] = _get_json_for_individual(
            i, request.user)
        json_response['familiesByGuid'][family.guid]['individualGuids'].append(
            i.guid)

    return create_json_response(json_response)
Exemple #10
0
def update_individual_field_handler(request, individual_guid, field_name):
    """Updates a single field in an Individual record.

    Args:
        request (object): Django HTTP Request object.
        individual_guid (string): GUID of the Individual.
        field_name (string): Name of the field to update (eg. "maternalId").

    Request:
        body should be a json dictionary like: { 'value': xxx }

    Response:
        json dictionary representing the updated individual like:
            {
                <individualGuid> : {
                    individualId: xxx,
                    maternalId: xxx,
                    affected: xxx,
                    ...
                }
            }
    """

    individual = Individual.objects.get(guid=individual_guid)

    project = individual.family.project

    check_permissions(project, request.user, CAN_EDIT)

    request_json = json.loads(request.body)
    if "value" not in request_json:
        raise ValueError("Request is missing 'value' key: %s" %
                         (request.body, ))

    individual_json = {field_name: request_json['value']}
    update_individual_from_json(individual, individual_json)

    return create_json_response(
        {individual.guid: _get_json_for_individual(individual, request.user)})
Exemple #11
0
def save_individuals_table_handler(request, project_guid, upload_file_id):
    """Handler for 'save' requests to apply Individual tables previously uploaded through receive_individuals_table(..)

    Args:
        request (object): Django request object
        project_guid (string): project GUID
        uploadedFileId (string): a token sent to the client by receive_individuals_table(..)
    """
    project = get_project_and_check_permissions(project_guid, request.user)

    serialized_file_path = _compute_serialized_file_path(upload_file_id)
    with gzip.open(serialized_file_path) as f:
        json_records = json.load(f)

    updated_families, updated_individuals = add_or_update_individuals_and_families(
        project, individual_records=json_records)

    os.remove(serialized_file_path)

    # edit individuals
    individuals_by_guid = {
        individual.guid: _get_json_for_individual(individual, request.user)
        for individual in updated_individuals
    }

    families_by_guid = {
        family.guid: _get_json_for_family(family,
                                          request.user,
                                          add_individual_guids_field=True)
        for family in updated_families
    }  # families whose list of individuals may have changed

    updated_families_and_individuals_by_guid = {
        'individualsByGuid': individuals_by_guid,
        'familiesByGuid': families_by_guid,
    }

    return create_json_response(updated_families_and_individuals_by_guid)
Exemple #12
0
def update_individual_field_handler(request, individual_guid, field_name):
    """Updates an Individual record.

    Args:
        individual_guid (string): GUID of the individual.
        field_name (string): Name of Individual record field to update (eg. "affected").
    """

    individual = Individual.objects.get(guid=individual_guid)

    project = individual.family.project

    check_permissions(project, request.user, CAN_EDIT)

    request_json = json.loads(request.body)
    if "value" not in request_json:
        raise ValueError("Request is missing 'value' key: %s" %
                         (request.body, ))

    individual_json = {field_name: request_json['value']}
    update_individual_from_json(individual, individual_json)

    return create_json_response(
        {individual.guid: _get_json_for_individual(individual, request.user)})
Exemple #13
0
def edit_individuals_handler(request, project_guid):
    """Modify one or more Individual records.

    Args:
        request (object): Django HTTP Request object.
        project_guid (string): GUID of project that contains these individuals.

    Request:
        body should be a json dictionary that contains a 'individuals' list that includes the individuals to update,
         represented by dictionaries of their guid and fields to update -
        for example:
            {
                'individuals': [
                    { 'individualGuid': <individualGuid1>, 'paternalId': <paternalId>, 'affected': 'A' },
                    { 'individualGuid': <individualGuid1>, 'sex': 'U' },
                    ...
                [
            }

    Response:
        json dictionary representing the updated individual(s) like:
            {
                <individualGuid1> : { individualId: xxx, sex: xxx, affected: xxx, ...},
                <individualGuid2> : { individualId: xxx, sex: xxx, affected: xxx, ...},
                ...
            }
    """

    project = get_project_and_check_permissions(project_guid, request.user, CAN_EDIT)

    request_json = json.loads(request.body)

    modified_individuals_list = request_json.get('individuals')
    if modified_individuals_list is None:
        return create_json_response(
            {}, status=400, reason="'individuals' not specified")

    update_individuals = {ind['individualGuid']: ind for ind in modified_individuals_list}
    update_individual_models = {ind.guid: ind for ind in Individual.objects.filter(guid__in=update_individuals.keys())}
    for modified_ind in modified_individuals_list:
        model = update_individual_models[modified_ind['individualGuid']]
        if modified_ind[JsonConstants.INDIVIDUAL_ID_COLUMN] != model.individual_id:
            modified_ind[JsonConstants.PREVIOUS_INDIVIDUAL_ID_COLUMN] = model.individual_id

    modified_family_ids = {ind.get('familyId') or ind['family']['familyId'] for ind in modified_individuals_list}
    modified_family_ids.update({ind.family.family_id for ind in update_individual_models.values()})
    related_individuals = Individual.objects.filter(
        family__family_id__in=modified_family_ids, family__project=project).exclude(guid__in=update_individuals.keys())
    related_individuals_json = _get_json_for_individuals(related_individuals, project_guid=project_guid, family_fields=['family_id'])
    individuals_list = modified_individuals_list + related_individuals_json

    # TODO more validation?
    errors, warnings = validate_fam_file_records(individuals_list, fail_on_warnings=True)
    if errors:
        return create_json_response({'errors': errors, 'warnings': warnings}, status=400, reason='Invalid updates')

    try:
        updated_families, updated_individuals = add_or_update_individuals_and_families(
            project, modified_individuals_list, user=request.user
        )
    except Exception as e:
        return create_json_response({'errors': [e.message]}, status=400, reason='Invalid updates')

    individuals_by_guid = {
        individual.guid: _get_json_for_individual(individual, request.user) for individual in updated_individuals
    }
    families_by_guid = {
        family.guid: _get_json_for_family(family, request.user, add_individual_guids_field=True)
        for family in updated_families
    }

    return create_json_response({
        'individualsByGuid': individuals_by_guid,
        'familiesByGuid': families_by_guid,
    })
Exemple #14
0
def update_pedigree_image(family):
    """Uses HaploPainter to (re)generate the pedigree image for the given family.

    Args:
         family (object): seqr Family model.
    """
    family_id = family.family_id

    individuals = Individual.objects.filter(family=family)

    if len(individuals) < 2:
        update_seqr_model(family, pedigree_image=None)
        return

    # convert individuals to json
    individual_records = {}
    for i in individuals:
        individual_records[i.individual_id] = _get_json_for_individual(i)
        individual_records[i.individual_id]['familyId'] = i.family.family_id

    # compute a map of parent ids to list of children
    parent_ids_to_children_map = collections.defaultdict(list)
    for individual_id, individual_json in individual_records.items():
        if not individual_json['paternalId'] and not individual_json[
                'maternalId']:
            continue
        key = (individual_json['paternalId'], individual_json['maternalId'])
        parent_ids_to_children_map[key].append(individual_json)

    # generate placeholder individuals as needed, since HaploPainter1.043.pl doesn't support families with only 1 parent
    for ((paternal_id, maternal_id),
         children) in parent_ids_to_children_map.items():

        for parent_id_key, parent_id, sex in [('paternalId', paternal_id, 'M'),
                                              ('maternalId', maternal_id, 'F')
                                              ]:

            if not parent_id or parent_id not in individual_records:
                placeholder_parent_id = 'placeholder_%s' % _random_string(10)
                placeholder_parent_json = {
                    'familyId': family_id,
                    'individualId': placeholder_parent_id,  # fake indiv id
                    'paternalId': '',
                    'maternalId': '',
                    'sex': sex,
                    'affected':
                    'INVISIBLE',  # use a special value to tell HaploPainter to draw this individual as '?'
                }

                for child_json in children:
                    child_json[parent_id_key] = placeholder_parent_id

                individual_records[
                    placeholder_parent_id] = placeholder_parent_json

    # convert to FAM file values
    SEX_TO_FAM_FILE_VALUE = {"M": "1", "F": "2", "U": "0"}
    AFFECTED_STATUS_TO_FAM_FILE_VALUE = {
        "A": "2",
        "N": "1",
        "U": "0",
        "INVISIBLE": "9"
    }  # HaploPainter1.043.pl has been modified to hide individuals with affected-status='9'
    for individual_json in individual_records.values():
        if not individual_json['paternalId']:
            individual_json['paternalId'] = '0'
        if not individual_json['maternalId']:
            individual_json['maternalId'] = '0'
        individual_json['sex'] = SEX_TO_FAM_FILE_VALUE[individual_json['sex']]
        individual_json['affected'] = AFFECTED_STATUS_TO_FAM_FILE_VALUE[
            individual_json['affected']]

    # run HaploPainter to generate the pedigree image
    png_file_path = os.path.join(tempfile.gettempdir(),
                                 "pedigree_image_%s.png" % _random_string(10))
    with tempfile.NamedTemporaryFile('w', suffix=".fam",
                                     delete=True) as fam_file:

        # columns: family, individual id, paternal id, maternal id, sex, affected
        for i in individual_records.values():
            row = [
                i[key] for key in [
                    'familyId', 'individualId', 'paternalId', 'maternalId',
                    'sex', 'affected'
                ]
            ]
            fam_file.write("\t".join(row).encode('UTF-8'))
            fam_file.write("\n")
        fam_file.flush()

        fam_file_path = fam_file.name
        haplopainter_command = "/usr/bin/perl " + os.path.join(
            BASE_DIR,
            "xbrowse_server/base/management/commands/HaploPainter1.043.pl")
        haplopainter_command += " -b -outformat png -pedfile %(fam_file_path)s -family %(family_id)s -outfile %(png_file_path)s" % locals(
        )
        os.system(haplopainter_command)

    if not os.path.isfile(png_file_path):
        logger.error("Failed to generated pedigree image for family: %s" %
                     family_id)
        update_seqr_model(family, pedigree_image=None)
        return

    _save_pedigree_image_file(family, png_file_path)

    os.remove(png_file_path)
Exemple #15
0
def save_case_review_status(request):
    """Updates the `case_review_status` of one or more individuals.

    HTTP POST
        Request body - should contain json:
            {
                form: {
                    <individualGuid1> : <case review status>,
                    <individualGuid2> : <case review status>,
                    ..
                }
            }

        Response body - will be json with the following structure, representing the created project:
            {
                <individualGuid1> : { ... <individual key-value pairs> ... },
            }

    """

    request_json = json.loads(request.body)
    if "form" not in request_json:
        raise ValueError("Request is missing 'value' key: %s" %
                         (request.body, ))

    response_json = {}
    for individual_guid, case_review_status_change in request_json[
            'form'].items():
        i = Individual.objects.get(guid=individual_guid)

        # keep new seqr.Project model in sync with existing xbrowse_server.base.models - TODO remove this code after transition to new schema is finished
        base_project = BaseProject.objects.filter(
            project_id=i.family.project.deprecated_project_id)
        if base_project:
            base_project = base_project[0]
            base_i = BaseIndividual.objects.get(family__project=base_project,
                                                indiv_id=i.individual_id)

        value = case_review_status_change.get('value')
        action = case_review_status_change.get('action')
        if action == 'UPDATE_CASE_REVIEW_STATUS':
            if i.case_review_status == value:
                continue

            # keep new seqr.Project model in sync with existing xbrowse_server.base.models - TODO remove this code after transition to new schema is finished
            i.case_review_status = value
            base_i.case_review_status = i.case_review_status
        elif action == 'ADD_ACCEPTED_FOR':
            if i.case_review_status_accepted_for and (
                    value in i.case_review_status_accepted_for):
                continue

            # keep new seqr.Project model in sync with existing xbrowse_server.base.models - TODO remove this code after transition to new schema is finished
            i.case_review_status_accepted_for = "".join(
                sorted(set((i.case_review_status_accepted_for or "") + value)))
            base_i.case_review_status_accepted_for = i.case_review_status_accepted_for
        elif action == 'REMOVE_ACCEPTED_FOR':
            if not i.case_review_status_accepted_for or (
                    value not in i.case_review_status_accepted_for):
                continue

            i.case_review_status_accepted_for = i.case_review_status_accepted_for.replace(
                value, "")
            base_i.case_review_status_accepted_for = i.case_review_status_accepted_for
        else:
            raise ValueError("Unexpected action param: {0}".format(
                case_review_status_change.get('action')))

        print("Saving individual: %s %s %s" %
              (i.individual_id, i.case_review_status,
               i.case_review_status_accepted_for))
        i.case_review_status_last_modified_by = request.user
        i.case_review_status_last_modified_date = timezone.now()
        i.save()
        base_i.save()

        response_json[i.guid] = _get_json_for_individual(i, request.user)

    return create_json_response(response_json)
Exemple #16
0
def edit_individuals_handler(request, project_guid):
    """Modify one or more Individual records.

    Args:
        request (object): Django HTTP Request object.
        project_guid (string): GUID of project that contains these individuals.

    Request:
        body should be a json dictionary that contains a 'modifiedIndividuals' dictionary that
        includes the individuals to update, mapped to the specific fields that should be updated
        for each individual - for example:
            {
                'form': {
                    'modifiedIndividuals': {
                        <individualGuid1>: { 'paternalId': <paternalId>, 'affected': 'A' },
                        <individualGuid2>: { 'sex': 'U' },
                        ...
                    }
                }
            }

    Response:
        json dictionary representing the updated individual(s) like:
            {
                <individualGuid1> : { individualId: xxx, maternalId: xxx, paternalId: xxx, ...},
                <individualGuid2> : { individualId: xxx, maternalId: xxx, paternalId: xxx, ...},
                ...
            }
    """

    project = get_project_and_check_permissions(project_guid, request.user,
                                                CAN_EDIT)

    request_json = json.loads(request.body)

    if 'form' not in request_json:
        return create_json_response(
            {}, status=400, reason="Invalid request: 'form' key not specified")

    form_data = request_json['form']

    modified_individuals_by_guid = form_data.get('modifiedIndividuals')
    if modified_individuals_by_guid is None:
        return create_json_response(
            {}, status=400, reason="'modifiedIndividuals' not specified")

    # edit individuals
    modified_individuals_list = list(modified_individuals_by_guid.values())

    # TODO more validation
    errors, warnings = validate_fam_file_records(modified_individuals_list)
    if errors:
        return create_json_response({'errors': errors, 'warnings': warnings})

    updated_families, updated_individuals = add_or_update_individuals_and_families(
        project, modified_individuals_list)

    individuals_by_guid = {
        individual.guid: _get_json_for_individual(individual, request.user)
        for individual in updated_individuals
    }

    families_by_guid = {
        family.guid: _get_json_for_family(family,
                                          request.user,
                                          add_individual_guids_field=True)
        for family in updated_families
    }  # families whose list of individuals may have changed

    updated_individuals_by_guid = {
        'individualsByGuid': individuals_by_guid,
        'familiesByGuid': families_by_guid,
    }

    return create_json_response(updated_individuals_by_guid)