Exemplo n.º 1
0
def create_classification_entry(request):
    """
    Create a new classification entry with a primary synonym in the current language.
    """
    parameters = request.data

    parent = None
    if parameters.get('parent'):
        parent_id = int_arg(parameters['parent'])
        parent = get_object_or_404(ClassificationEntry, id=parent_id)

    rank_id = int_arg(parameters['rank'])
    language = parameters['language']
    layout = request.data.get('layout')
    descriptors = request.data.get('descriptors')

    if not Language.objects.filter(code=language).exists():
        raise SuspiciousOperation(_("The language is not supported"))

    if layout is not None and descriptors is not None:
        layout_id = int_arg(layout)

        content_type = get_object_or_404(ContentType,
                                         app_label="classification",
                                         model="classificationentry")
        layout = get_object_or_404(Layout, id=layout_id, target=content_type)
    else:
        # @todo do we allow that ?
        layout = None

    classification_entry = None

    try:
        with transaction.atomic():
            classification_entry = ClassificationEntryManager.create_classification_entry(
                parameters['name'], rank_id, parent, language, layout,
                descriptors)
    except IntegrityError as e:
        Descriptor.integrity_except(ClassificationEntry, e)

    response = {
        'id': classification_entry.id,
        'name': classification_entry.name,
        'rank': classification_entry.rank_id,
        'parent': classification_entry.parent_id if parent else None,
        'parent_list': classification_entry.parent_list,
        'synonyms': [],
        'layout': classification_entry.layout_id if layout else None,
        'descriptors': classification_entry.descriptors
    }

    for s in classification_entry.synonyms.all():
        response['synonyms'].append({
            'id': s.id,
            'name': s.name,
            'synonym_type': s.synonym_type_id,
            'language': s.language
        })

    return HttpResponseRest(request, response)
Exemplo n.º 2
0
def patch_accession(request, acc_id):
    accession = get_object_or_404(Accession, id=int(acc_id))

    entity_status = request.data.get("entity_status")
    descriptors = request.data.get("descriptors")

    result = {'id': accession.id}

    try:
        with transaction.atomic():
            if 'primary_classification_entry' in request.data:
                primary_classification_entry_id = int(
                    request.data['primary_classification_entry'])
                primary_classification_entry = get_object_or_404(
                    ClassificationEntry, id=primary_classification_entry_id)

                # update FK
                accession.primary_classification_entry = primary_classification_entry
                result[
                    'primary_classification_entry'] = primary_classification_entry.id

                # and replace from classification entry M2M previous primary
                accession_classification_entry = get_object_or_404(
                    AccessionClassificationEntry,
                    accession=accession,
                    primary=True)

                accession_classification_entry.classification_entry = primary_classification_entry
                accession_classification_entry.save()

                accession.update_field('primary_classification_entry')

            if entity_status is not None and accession.entity_status != entity_status:
                accession.set_status(entity_status)
                result['entity_status'] = entity_status
                accession.update_field('entity_status')

            if descriptors is not None:
                # update descriptors
                descriptors_builder = DescriptorsBuilder(accession)

                descriptors_builder.check_and_update(accession.layout,
                                                     descriptors)

                accession.descriptors = descriptors_builder.descriptors
                result['descriptors'] = accession.descriptors

                descriptors_builder.update_associations()

                accession.update_descriptors(
                    descriptors_builder.changed_descriptors())
                accession.update_field('descriptors')

            accession.save()
    except IntegrityError as e:
        Descriptor.integrity_except(Accession, e)

    return HttpResponseRest(request, result)
Exemplo n.º 3
0
def patch_organisation(request, org_id):
    organisation = get_object_or_404(Organisation, id=int(org_id))

    organisation_name = request.data.get("name")
    organisation_type = request.data.get("type")
    entity_status = request.data.get("entity_status")
    descriptors = request.data.get("descriptors")

    result = {'id': organisation.id}

    try:
        with transaction.atomic():
            if organisation_name is not None:
                organisation.name = organisation_name
                result['name'] = organisation_name

                organisation.update_field('name')

            if organisation_type is not None:
                organisation.type = organisation_type
                result['type'] = organisation_type

                organisation.update_field('type')

            if entity_status is not None and organisation.entity_status != entity_status:
                organisation.set_status(entity_status)
                result['entity_status'] = entity_status

            if descriptors is not None:
                # update descriptors
                descriptors_builder = DescriptorsBuilder(organisation)

                descriptors_builder.check_and_update(organisation.layout,
                                                     descriptors)

                organisation.descriptors = descriptors_builder.descriptors
                result['descriptors'] = organisation.descriptors

                descriptors_builder.update_associations()

                organisation.update_descriptors(
                    descriptors_builder.changed_descriptors())
                organisation.update_field('descriptors')

            organisation.save()
    except IntegrityError as e:
        Descriptor.integrity_except(Organisation, e)

    return HttpResponseRest(request, result)
Exemplo n.º 4
0
def create_establishment(request):
    """
    Create a new establishment.
    """
    name = request.data['name']
    descriptors = request.data['descriptors']
    org_id = request.data['organisation']

    # check existence of the organisation
    organisation = get_object_or_404(Organisation, id=int(org_id))

    # check uniqueness of the name
    if Establishment.objects.filter(name=name).exists():
        raise SuspiciousOperation(_("The name of the establishment is already used"))

    content_type = get_object_or_404(ContentType, app_label="organisation", model="establishment")
    layout = get_object_or_404(Layout, name="establishment", target=content_type)

    try:
        with transaction.atomic():
            # common properties
            establishment = Establishment()
            establishment.name = request.data['name']
            establishment.layout = layout
            establishment.organisation = organisation

            # descriptors
            descriptors_builder = DescriptorsBuilder(organisation)

            descriptors_builder.check_and_update(layout, descriptors)
            establishment.descriptors = descriptors_builder.descriptors

            establishment.save()

            # update owner on external descriptors
            descriptors_builder.update_associations()
    except IntegrityError as e:
        Descriptor.integrity_except(Organisation, e)

    response = {
        'id': establishment.id,
        'name': establishment.name,
        'organisation': organisation.id,
        'layout': layout.id,
        'descriptors': establishment.descriptors
    }

    return HttpResponseRest(request, response)
Exemplo n.º 5
0
def patch_establishment(request, est_id):
    establishment = get_object_or_404(Establishment, id=int(est_id))

    organisation_name = request.data.get("name")
    entity_status = request.data.get("entity_status")
    descriptors = request.data.get("descriptors")

    result = {
        'id': establishment.id
    }

    try:
        with transaction.atomic():
            if organisation_name is not None:
                establishment.name = organisation_name
                result['name'] = organisation_name

                establishment.update_field('name')

            if entity_status is not None and establishment.entity_status != entity_status:
                establishment.set_status(entity_status)
                result['entity_status'] = entity_status

            if descriptors is not None:
                # update descriptors
                descriptors_builder = DescriptorsBuilder(establishment)

                descriptors_builder.check_and_update(establishment.layout, descriptors)

                establishment.descriptors = descriptors_builder.descriptors
                result['descriptors'] = establishment.descriptors

                descriptors_builder.update_associations()

                establishment.update_descriptors(descriptors_builder.changed_descriptors())
                establishment.update_field('descriptors')

            establishment.save()
    except IntegrityError as e:
        Descriptor.integrity_except(Organisation, e)

    return HttpResponseRest(request, result)
Exemplo n.º 6
0
def patch_classification_entry(request, cls_id):
    classification_entry = get_object_or_404(ClassificationEntry,
                                             id=int(cls_id))

    result = {}

    if 'parent' in request.data:
        if request.data['parent'] is None:
            classification_entry.parent = None
            classification_entry.parent_list = []

            result['parent'] = None
            result['parent_list'] = []
            result['parent_details'] = []
        else:
            pcls_id = int(request.data['parent'])

            parent = get_object_or_404(ClassificationEntry, id=pcls_id)

            classification_entry.parent = parent

            # @todo rank level
            if parent.rank_id >= classification_entry.rank_id:
                raise SuspiciousOperation(
                    _("The rank of the parent must be lowest than the classification entry itself"
                      ))

            # make parent list
            ClassificationEntryManager.update_parents(classification_entry,
                                                      parent)

            # query for parents
            parents = []
            parents_cls = ClassificationEntry.objects.filter(
                id__in=classification_entry.parent_list)

            for parent in parents_cls:
                parents.insert(
                    0, {
                        'id': parent.id,
                        'name': parent.name,
                        'rank': parent.rank_id,
                        'parent': parent.parent_id
                    })

            result['parent'] = parent.id
            result['parent_list'] = parents
            result['parent_details'] = parents

        classification_entry.update_field(['parent', 'parent_list'])

    try:
        with transaction.atomic():
            # update layout of descriptors and descriptors
            if 'layout' in request.data:
                layout_id = request.data["layout"]

                # changing of layout erase all previous descriptors values
                if layout_id is None and classification_entry.layout is not None:
                    # clean previous descriptors and owns
                    descriptors_builder = DescriptorsBuilder(
                        classification_entry)

                    descriptors_builder.clear(classification_entry.layout)

                    classification_entry.layout = None
                    classification_entry.descriptors = {}

                    descriptors_builder.update_associations()

                    result['layout'] = None
                    result['descriptors'] = {}

                elif layout_id is not None:
                    # existing descriptors and new layout is different : first clean previous descriptors
                    if (classification_entry.layout is not None
                            and classification_entry.layout.pk != layout_id):

                        # clean previous descriptors and owns
                        descriptors_builder = DescriptorsBuilder(
                            classification_entry)

                        descriptors_builder.clear(classification_entry.layout)

                        classification_entry.layout = None
                        classification_entry.descriptors = {}

                        descriptors_builder.update_associations()

                    # and set the new one
                    content_type = get_object_or_404(
                        ContentType,
                        app_label="classification",
                        model="classificationentry")
                    layout = get_object_or_404(Layout,
                                               id=layout_id,
                                               target=content_type)

                    classification_entry.layout = layout
                    classification_entry.descriptors = {}

                    result['layout'] = layout.id
                    result['descriptors'] = {}

                    classification_entry.update_field(
                        ['layout', 'descriptors'])

            # update descriptors
            if 'descriptors' in request.data:
                descriptors = request.data["descriptors"]

                descriptors_builder = DescriptorsBuilder(classification_entry)

                descriptors_builder.check_and_update(
                    classification_entry.layout, descriptors)
                classification_entry.descriptors = descriptors_builder.descriptors

                descriptors_builder.update_associations()

                result['descriptors'] = classification_entry.descriptors

                classification_entry.update_descriptors(
                    descriptors_builder.changed_descriptors())
                classification_entry.update_field('descriptors')

            classification_entry.save()

    except IntegrityError as e:
        Descriptor.integrity_except(ClassificationEntry, e)

    return HttpResponseRest(request, result)
Exemplo n.º 7
0
def create_organisation(request):
    """
    Create a new organisation.
    """
    name = request.data['name']
    organisation_type = request.data['type']
    descriptors = request.data['descriptors']

    # check uniqueness of the name
    if Organisation.objects.filter(name=name).exists():
        raise SuspiciousOperation(
            _("The name of the organisation is already used"))

    # check that type is in the values of descriptor
    if not Organisation.is_type(organisation_type):
        raise SuspiciousOperation(_("Unsupported type of organisation"))

    content_type = get_object_or_404(ContentType,
                                     app_label="organisation",
                                     model="organisation")
    layout = get_object_or_404(Layout,
                               name="organisation",
                               target=content_type)

    try:
        with transaction.atomic():
            # common properties
            organisation = Organisation()
            organisation.name = request.data['name']
            organisation.type = organisation_type
            organisation.layout = layout

            # descriptors
            descriptors_builder = DescriptorsBuilder(organisation)

            descriptors_builder.check_and_update(layout, descriptors)
            organisation.descriptors = descriptors_builder.descriptors

            organisation.save()

            # update owner on external descriptors
            descriptors_builder.update_associations()

            # add to GRC as partner
            if request.data['grc'] is True:
                grc = GRC.objects.get_unique_grc()
                grc.organisations.add(organisation)

    except IntegrityError as e:
        Descriptor.integrity_except(Organisation, e)

    response = {
        'id': organisation.id,
        'name': organisation.name,
        'type': organisation.type,
        'grc': request.data['grc'],
        'layout': layout.id,
        'descriptors': organisation.descriptors
    }

    return HttpResponseRest(request, response)
Exemplo n.º 8
0
def create_accession(request):
    name = request.data['name']
    naming_options = request.data['naming_options']
    # code = request.data['code']
    layout_id = int_arg(request.data['layout'])
    primary_classification_entry_id = int_arg(
        request.data['primary_classification_entry'])
    descriptors = request.data['descriptors']
    language = request.data['language']

    if not Language.objects.filter(code=language).exists():
        raise SuspiciousOperation(_("The language is not supported"))

    naming_variables = {}
    code = NameBuilderManager.get(NameBuilderManager.GLOBAL_ACCESSION).pick(
        naming_variables, naming_options)

    # check uniqueness of the code for any type of synonym
    if AccessionSynonym.objects.filter(name=code).exists():
        raise SuspiciousOperation(
            _("The code of the accession is already used as a synonym name"))

    # check uniqueness of the code
    if Accession.objects.filter(code=code).exists():
        raise SuspiciousOperation(
            _("The code of the accession is already used"))

    if name == code:
        raise SuspiciousOperation(
            _("The code and the name of the accession must be different"))

    content_type = get_object_or_404(ContentType,
                                     app_label="accession",
                                     model="accession")
    layout = get_object_or_404(Layout, id=layout_id, target=content_type)

    try:
        with transaction.atomic():
            # common properties
            accession = Accession()
            accession.name = name
            accession.code = code
            accession.layout = layout

            # primary classification entry
            primary_classification_entry = get_object_or_404(
                ClassificationEntry, id=primary_classification_entry_id)
            accession.primary_classification_entry = primary_classification_entry

            # descriptors
            descriptors_builder = DescriptorsBuilder(accession)

            descriptors_builder.check_and_update(layout, descriptors)
            accession.descriptors = descriptors_builder.descriptors

            accession.save()

            # update owner on external descriptors
            descriptors_builder.update_associations()

            # primary classifications in M2M
            AccessionClassificationEntry.objects.create(
                accession=accession,
                classification_entry=primary_classification_entry,
                primary=True)

            # initial synonym GRC code
            grc_code = AccessionSynonym(
                entity=accession,
                name=code,
                synonym_type_id=localsettings.synonym_type_accession_code,
                language='en')
            grc_code.save()

            # primary synonym if defined
            primary_name = AccessionSynonym(
                entity=accession,
                name=name,
                synonym_type_id=localsettings.synonym_type_accession_name,
                language=language)
            primary_name.save()

            accession.synonyms.add(grc_code)
            accession.synonyms.add(primary_name)

            # add related classification entries
            classification_entry_bulk = []
            for classification_entry in primary_classification_entry.related.all(
            ):
                ace = AccessionClassificationEntry(
                    primary=False,
                    accession=accession,
                    classification_entry=classification_entry)

                classification_entry_bulk.append(ace)

            AccessionClassificationEntry.objects.bulk_create(
                classification_entry_bulk)

    except IntegrityError as e:
        Descriptor.integrity_except(Accession, e)

    response = {
        'id':
        accession.pk,
        'name':
        accession.name,
        'code':
        accession.code,
        'layout':
        layout.id,
        'primary_classification_entry':
        primary_classification_entry.id,
        'descriptors':
        accession.descriptors,
        'synonyms': [{
            'id': grc_code.id,
            'name': grc_code.name,
            'synonym_type': grc_code.synonym_type_id,
            'language': grc_code.language
        }, {
            'id': primary_name.id,
            'name': primary_name.name,
            'synonym_type': primary_name.synonym_type_id,
            'language': primary_name.language
        }]
    }

    return HttpResponseRest(request, response)
Exemplo n.º 9
0
def modify_panel_accessions(request, panel_id):
    action = request.data['action']
    selection = request.data['selection']['select']
    acc_panel = AccessionPanel.objects.get(id=int_arg(panel_id))

    from main.cursor import CursorQuery
    cq = CursorQuery(Accession)

    if request.data['selection'].get('search'):
        search = json.loads(request.GET['search'])
        cq.filter(request.data['selection'].get('search'))

    if request.data['selection'].get('filters'):
        filters = json.loads(request.GET['filters'])
        cq.filter(request.data['selection'].get('filters'))

    cq.m2m_to_array_field(relationship=AccessionPanel.accessions,
                          selected_field='accessionpanel_id',
                          from_related_field='id',
                          to_related_field='accession_id',
                          alias='panels')

    cq.m2m_to_array_field(relationship=Accession.classifications_entries,
                          selected_field='classification_entry_id',
                          from_related_field='id',
                          to_related_field='accession_id',
                          alias='classifications')

    cq.set_synonym_model(AccessionSynonym)

    if request.data['selection'].get('from'):
        related_entity = request.data['selection']['from']
        label, model = related_entity['content_type'].split('.')
        content_type = get_object_or_404(ContentType,
                                         app_label=label,
                                         model=model)
        model_class = content_type.model_class()
        cq.inner_join(model_class, **{model: int_arg(related_entity['id'])})

    if action == 'remove':
        try:
            with transaction.atomic():
                if isinstance(selection, bool):
                    if selection is True:
                        acc_panel.accessions.remove(*cq)

                elif selection['op'] == 'in':
                    acc_panel.accessions.remove(*cq.filter(
                        id__in=selection['value']))

                elif selection['op'] == 'notin':
                    acc_panel.accessions.remove(*cq.filter(
                        id__notin=selection['value']))

                acc_panel.save()

        except IntegrityError as e:
            Descriptor.integrity_except(AccessionPanel, e)

    elif action == 'add':
        try:
            with transaction.atomic():
                if isinstance(selection, bool):
                    if selection is True:
                        acc_panel.accessions.add(*cq)

                elif selection['op'] == 'in':
                    acc_panel.accessions.add(*cq.filter(
                        id__in=selection['value']))

                elif selection['op'] == 'notin':
                    acc_panel.accessions.add(*cq.filter(
                        id__notin=selection['value']))

                acc_panel.save()

        except IntegrityError as e:
            Descriptor.integrity_except(AccessionPanel, e)
    else:
        raise SuspiciousOperation('Invalid action')

    return HttpResponseRest(request, {})
Exemplo n.º 10
0
def modify_panel(request, panel_id):
    acc_panel = get_object_or_404(AccessionPanel, id=int(panel_id))
    # entity_status = request.data.get("entity_status")
    descriptors = request.data.get("descriptors")

    result = {'id': acc_panel.id}

    try:
        with transaction.atomic():
            # if entity_status is not None and panel.entity_status != entity_status:
            #     panel.set_status(entity_status)
            #     result['entity_status'] = entity_status

            if 'name' in request.data:
                name = request.data['name']

                if AccessionPanel.objects.filter(name=name).exists():
                    raise SuspiciousOperation(
                        _("The name of the panel is already used"))

                acc_panel.name = name
                result['name'] = name

            if 'layout' in request.data:
                layout_id = request.data["layout"]

                # changing of layout erase all previous descriptors values
                if layout_id is None and acc_panel.layout is not None:
                    # clean previous descriptors and owns
                    descriptors_builder = DescriptorsBuilder(acc_panel)

                    descriptors_builder.clear(acc_panel.layout)

                    acc_panel.layout = None
                    acc_panel.descriptors = {}

                    descriptors_builder.update_associations()

                    result['layout'] = None
                    result['descriptors'] = {}

                elif layout_id is not None:
                    # existing descriptors and new layout is different : first clean previous descriptors
                    if acc_panel.layout is not None and acc_panel.layout.pk != layout_id:
                        # clean previous descriptors and owns
                        descriptors_builder = DescriptorsBuilder(acc_panel)

                        descriptors_builder.clear(acc_panel.layout)

                        acc_panel.layout = None
                        acc_panel.descriptors = {}

                        descriptors_builder.update_associations()

                    # and set the new one
                    content_type = get_object_or_404(ContentType,
                                                     app_label="accession",
                                                     model="accessionpanel")
                    layout = get_object_or_404(Layout,
                                               id=layout_id,
                                               target=content_type)

                    acc_panel.layout = layout
                    acc_panel.descriptors = {}

                    result['layout'] = layout.id
                    result['descriptors'] = {}

                    acc_panel.update_field(['layout', 'descriptors'])

            if descriptors is not None:
                # update descriptors
                descriptors_builder = DescriptorsBuilder(acc_panel)

                descriptors_builder.check_and_update(acc_panel.layout,
                                                     descriptors)

                acc_panel.descriptors = descriptors_builder.descriptors
                result['descriptors'] = acc_panel.descriptors

                descriptors_builder.update_associations()

                acc_panel.update_descriptors(
                    descriptors_builder.changed_descriptors())
                acc_panel.update_field('descriptors')

            acc_panel.save()
    except IntegrityError as e:
        Descriptor.integrity_except(Accession, e)

    return HttpResponseRest(request, result)
Exemplo n.º 11
0
def create_panel(request):
    name = request.data['name']
    selection = request.data['selection']['select']
    related_entity = request.data['selection']['from']
    search = request.data['selection']['search']
    filters = request.data['selection']['filters']
    layout_id = request.data['layout']
    descriptors = request.data['descriptors']

    layout = None

    # check uniqueness of the name
    if AccessionPanel.objects.filter(name=name).exists():
        raise SuspiciousOperation(_("The name of the panel is already used"))

    if layout_id is not None:
        content_type = get_object_or_404(ContentType,
                                         app_label="accession",
                                         model="accessionpanel")
        layout = get_object_or_404(Layout,
                                   id=int_arg(layout_id),
                                   target=content_type)

    from main.cursor import CursorQuery
    cq = CursorQuery(Accession)

    if search:
        cq.filter(search)

    if filters:
        cq.filter(filters)

    cq.m2m_to_array_field(relationship=AccessionPanel.accessions,
                          selected_field='accessionpanel_id',
                          from_related_field='id',
                          to_related_field='accession_id',
                          alias='panels')

    cq.m2m_to_array_field(relationship=Accession.classifications_entries,
                          selected_field='classification_entry_id',
                          from_related_field='id',
                          to_related_field='accession_id',
                          alias='classifications')

    cq.set_synonym_model(AccessionSynonym)

    if related_entity:
        label, model = related_entity['content_type'].split('.')
        content_type = get_object_or_404(ContentType,
                                         app_label=label,
                                         model=model)
        model_class = content_type.model_class()
        cq.inner_join(model_class, **{model: int_arg(related_entity['id'])})

    try:
        with transaction.atomic():
            acc_panel = AccessionPanel(name=name)
            acc_panel.layout = layout
            acc_panel.count = 0

            # descriptors
            descriptors_builder = DescriptorsBuilder(acc_panel)

            if layout:
                descriptors_builder.check_and_update(layout, descriptors)
                acc_panel.descriptors = descriptors_builder.descriptors

            acc_panel.save()

            # update owner on external descriptors
            descriptors_builder.update_associations()

            if isinstance(selection, bool):
                if selection is True:
                    acc_panel.accessions.add(*cq)
                    acc_panel.count = cq.count()

            elif selection['op'] == 'in':
                acc_panel.accessions.add(*cq.filter(id__in=selection['value']))
                acc_panel.count = cq.filter(id__in=selection['value']).count()

            elif selection['op'] == 'notin':
                acc_panel.accessions.add(*cq.filter(
                    id__notin=selection['value']))
                acc_panel.count = cq.filter(
                    id__notin=selection['value']).count()

    except IntegrityError as e:
        Descriptor.integrity_except(AccessionPanel, e)

    response = {
        'id': acc_panel.pk,
        'name': acc_panel.name,
        'layout': acc_panel.layout.pk if acc_panel.layout else None,
        'descriptors': acc_panel.descriptors,
        'accessions_amount': acc_panel.count
    }

    return HttpResponseRest(request, response)
Exemplo n.º 12
0
def modify_panel_batches(request, panel_id):
    action = request.data['action']
    selection = request.data['selection']['select']
    panel = BatchPanel.objects.get(id=int_arg(panel_id))

    from main.cursor import CursorQuery
    cq = CursorQuery(Batch)

    if request.data['selection'].get('filters'):
        cq.filter(request.data['selection'].get('filters'))

    if request.data['selection'].get('search'):
        cq.filter(request.data['selection'].get('search'))

    if request.data['selection'].get('from'):
        related_entity = request.data['selection']['from']
        label, model = related_entity['content_type'].split('.')
        content_type = get_object_or_404(ContentType,
                                         app_label=label,
                                         model=model)
        model_class = content_type.model_class()
        cq.inner_join(model_class, **{model: int_arg(related_entity['id'])})

    if action == 'remove':
        try:
            with transaction.atomic():
                if isinstance(selection, bool):
                    if selection is True:
                        panel.batches.remove(*cq)

                elif selection['op'] == 'in':
                    panel.batches.remove(*cq.filter(id__in=selection['value']))

                elif selection['op'] == 'notin':
                    panel.batches.remove(*cq.filter(
                        id__notin=selection['value']))

                panel.save()

        except IntegrityError as e:
            Descriptor.integrity_except(BatchPanel, e)

    elif action == 'add':
        try:
            with transaction.atomic():
                if isinstance(selection, bool):
                    if selection is True:
                        panel.batches.add(*cq)

                elif selection['op'] == 'in':
                    panel.batches.add(*cq.filter(id__in=selection['value']))

                elif selection['op'] == 'notin':
                    panel.batches.add(*cq.filter(id__notin=selection['value']))

                panel.save()

        except IntegrityError as e:
            Descriptor.integrity_except(BatchPanel, e)
    else:
        raise SuspiciousOperation('Invalid action')

    return HttpResponseRest(request, {})