Example #1
0
def _assign_disease_by_morpho_topo(instance, icdo_morpho, icdo_topo_list, disease_field='disease'):
    """
    Given an instance with a disease:models.ForeignKey(Disease) field (specified by 'disease_field'),
    an icdo_morpho id, and an optional set of icdo_topo IDs in icdo_topo_list,
    creates a new disease matching the morpho, topo spec, or retrieves it if it already exists.

    Assigns the new or retrieved disease to the instance's disease field if successful.
    """

    # approach: now that Disease is mutable, we can't simply point
    # multiple curation entries at the same Disease; instead, we need
    # to either look up a Disease that matches the morpho code + topo set
    # in this entry, or create it if it doesn't exist.

    # note that this produces a trail of Diseases that are *potentially*
    # no longer in use if we ever change the morpho code or topo codes.
    # we can either clean up these unused entries after updating, or
    # 'vacuum' them on a schedule with a script. this will require
    # identifying every model that refers to a Disease

    with transaction.atomic():
        if not icdo_morpho:
            # we can't assign a disease if they didn't specify a morpho field
            setattr(instance, disease_field, None)
            instance.save()
            return None

        instance_disease = getattr(instance, disease_field)

        if instance_disease:
            # if there was previously a disease, check if it's in use and delete it if not
            collector = NestedObjects(using='default')  # or specific database
            collector.collect([instance_disease])

            if collector.can_fast_delete():
                print("Fast-deleting old disease")
                instance_disease.delete()
            else:
                print("Can't fast-delete old disease: %s" % collector.nested())

        # build a Q-object that's all the topo entries related to the target Disease ANDed together
        q_objects = Q(icd_o_morpho=icdo_morpho['id'])

        if icdo_topo_list:
            for x in icdo_topo_list:
                q_objects &= Q(icdotopoapidisease__icd_o_topo__id=x['id'])
        else:
            # we need to search for a morpho that has no associated topo codes
            q_objects &= Q(icdotopoapidisease=None)

        # either retrieve an existing disease that matches the description, or create it
        candidate = Disease.objects.filter(q_objects).first()

        if not candidate:
            candidate = Disease.objects.create(
                icd_o_morpho=IcdOMorpho.objects.get(id=icdo_morpho['id']))
            IcdOTopoApiDisease.objects.bulk_create([
                IcdOTopoApiDisease(api_disease=candidate,
                                   icd_o_topo=IcdOTopo.objects.get(id=x['id']))
                for x in (icdo_topo_list if icdo_topo_list else [])
            ])
            candidate.save()

        setattr(instance, disease_field, candidate)
        instance.save()