Пример #1
0
def create_object(container, portal_type, **data):
    """Creates an object slug

    :returns: The new created content object
    :rtype: object
    """

    if "id" in data:
        # always omit the id as Bika LIMS generates a proper one
        id = data.pop("id")
        logger.warn("Passed in ID '{}' omitted! Bika LIMS "
                    "generates a proper ID for you".format(id))

    try:
        if portal_type == "Sample":
            obj = create_sample(container, **data)
            return obj
        elif portal_type == "SampleType":
            obj = create_sample_type(container, portal_type, **data)
            return obj
        elif portal_type == "Project":
            obj = create_project(container, portal_type, **data)
            return obj
        elif portal_type == "StorageUnit" or \
             portal_type == "ManagedStorage" or \
             portal_type == "UnmanagedStorage":
            obj = create_storage(container, portal_type, **data)
            return obj

    except Unauthorized:
        fail(401, "You are not allowed to create this content")

    obj = bika_create_object(container, portal_type, **data)

    return obj
Пример #2
0
def action(context, request, action=None, resource=None, uid=None):
    """Various HTTP POST actions
    """

    # Fetch and call the action function of the API
    func_name = "{}_items".format(action)
    action_func = getattr(api, func_name, None)
    if action_func is None:
        api.fail(500, "API has no member named '{}'".format(func_name))

    portal_type = api.resource_to_portal_type(resource)
    items = action_func(portal_type=portal_type, uid=uid)

    return {
        "count": len(items),
        "items": items,
        "url": api.url_for("bika.lims.jsonapi.v2.action", action=action),
    }
Пример #3
0
def login(context, request):
    """ Login Route

    Login route to authenticate a user against Plone.
    """
    # extract the data
    __ac_name = request.get("__ac_name", None)
    __ac_password = request.get("__ac_password", None)

    logger.info("*** LOGIN %s ***" % __ac_name)

    if __ac_name is None:
        api.fail(400, "__ac_name is missing")
    if __ac_password is None:
        api.fail(400, "__ac_password is missing")

    acl_users = api.get_tool("acl_users")

    # XXX hard coded
    acl_users.credentials_cookie_auth.login()

    # XXX amin user won't be logged in if I use this approach
    # acl_users.login()
    # response = request.response
    # acl_users.updateCredentials(request, response, __ac_name, __ac_password)

    if api.is_anonymous():
        api.fail(401, "Invalid Credentials")

    # return the JSON in the same format like the user route
    return get(context, request, username=__ac_name)
Пример #4
0
def login(context, request):
    """ Login Route

    Login route to authenticate a user against Plone.
    """
    # extract the data
    __ac_name = request.get("__ac_name", None)
    __ac_password = request.get("__ac_password", None)

    logger.info("*** LOGIN %s ***" % __ac_name)

    if __ac_name is None:
        api.fail(400, "__ac_name is missing")
    if __ac_password is None:
        api.fail(400, "__ac_password is missing")

    acl_users = api.get_tool("acl_users")

    # XXX hard coded
    acl_users.credentials_cookie_auth.login()

    # XXX amin user won't be logged in if I use this approach
    # acl_users.login()
    # response = request.response
    # acl_users.updateCredentials(request, response, __ac_name, __ac_password)

    if api.is_anonymous():
        api.fail(401, "Invalid Credentials")

    # return the JSON in the same format like the user route
    return get(context, request, username=__ac_name)
Пример #5
0
def action(context, request, action=None, resource=None, uid=None):
    """Various HTTP POST actions

    Case 1: /<uid>
    -> Return the full object immediately in the root of the JSON API response
    <Bika-Site>/@@API/v2/<uid>

    Case 2: /<action>/<uid>
    -> The actions (update, delete) will performed on the object identified by <uid>
    -> The actions (create) will use the <uid> as the parent folder
    <Bika-Site>/@@API/v2/<action>/<uid>

    Case 3: <resource>/<action>
    -> The "target" object will be located by a location given in the request body (uid, path, parent_path + id)
    -> The actions (cut, copy, update, delete) will performed on the target object
    -> The actions (create) will use the target object as the container
    <Bika-Site>/@@API/v2/<resource>/<action>

    Case 4: <resource>/<action>/<uid>
    -> The actions (cut, copy, update, delete) will performed on the object identified by <uid>
    -> The actions (create) will use the <uid> as the parent folder
    <Bika-Site>/@@API/v2/<resource>/<action>
    """

    # Fetch and call the action function of the API
    func_name = "{}_items".format(action)
    action_func = getattr(api, func_name, None)
    if action_func is None:
        api.fail(500, "API has no member named '{}'".format(func_name))

    portal_type = api.resource_to_portal_type(resource)
    items = action_func(portal_type=portal_type, uid=uid)

    return {
        "count": len(items),
        "items": items,
        "url": api.url_for("bika.lims.jsonapi.v2.action", action=action),
    }
Пример #6
0
def action(context, request, action=None, resource=None, uid=None):
    """Various HTTP POST actions

    Case 1: /<uid>
    -> Return the full object immediately in the root of the JSON API response
    <Bika-Site>/@@API/v2/<uid>

    Case 2: /<action>/<uid>
    -> The actions (update, delete) will performed on the object identified by <uid>
    -> The actions (create) will use the <uid> as the parent folder
    <Bika-Site>/@@API/v2/<action>/<uid>

    Case 3: <resource>/<action>
    -> The "target" object will be located by a location given in the request body (uid, path, parent_path + id)
    -> The actions (cut, copy, update, delete) will performed on the target object
    -> The actions (create) will use the target object as the container
    <Bika-Site>/@@API/v2/<resource>/<action>

    Case 4: <resource>/<action>/<uid>
    -> The actions (cut, copy, update, delete) will performed on the object identified by <uid>
    -> The actions (create) will use the <uid> as the parent folder
    <Bika-Site>/@@API/v2/<resource>/<action>
    """

    # Fetch and call the action function of the API
    func_name = "{}_items".format(action)
    action_func = getattr(api, func_name, None)
    if action_func is None:
        api.fail(500, "API has no member named '{}'".format(func_name))

    portal_type = api.resource_to_portal_type(resource)
    items = action_func(portal_type=portal_type, uid=uid)

    return {
        "count": len(items),
        "items": items,
        "url": api.url_for("bika.lims.jsonapi.v2.action", action=action),
    }
Пример #7
0
def get_object_by_id(id, default=None):
    """Find an object by a given ID

    :param id: The ID of the object to find
    :type id: string
    :returns: Found Object or None
    """

    # nothing to do here
    if not id:
        if default is not _marker:
            return default
        fail("get_object_by_id requires ID as first argument; got {} instead".
             format(id))

    # we defined the portal object UID to be '0'::
    if id == '0':
        return api.get_portal()

    # we try to find the object with both catalogs
    pc = api.get_portal_catalog()
    # uc = api.get_tool("uid_catalog")

    # try to find the object with the reference catalog first
    brains = pc(id=id)
    if brains:
        return brains[0].getObject()

    # try to find the object with the portal catalog
    res = pc(ID=id)
    if not res:
        if default is not _marker:
            return default
        fail("No object found for UID {}".format(id))

    return api.get_object(res[0])
Пример #8
0
def create_project(container, portal_type, **data):
    """
        Create a project via API
    """

    container = get_object(container)
    title = data.get("title", "")
    if not title:
        fail(404, "Title is required.")

    obj = bika_create_object(container, portal_type, **data)

    st_titles = data.get("SampleType", "")

    st_uids = []
    if st_titles and type(st_titles) is list:
        for st_title in st_titles:
            brains = search(portal_type="SampleType", title=st_title)
            if brains:
                st_uids.append(brains[0].UID)
        if st_uids:
            obj.setSampleType(st_uids)

    return obj
Пример #9
0
def create_items(portal_type=None, uid=None, endpoint=None, **kw):
    """ create items

    1. If the uid is given, get the object and create the content in there
       (assumed that it is folderish)
    2. If the uid is 0, the target folder is assumed the portal.
    3. If there is no uid given, the payload is checked for either a key
        - `parent_uid`  specifies the *uid* of the target folder
        - `parent_path` specifies the *physical path* of the target folder
    """
    # import pdb;pdb.set_trace()
    # disable CSRF
    req.disable_csrf_protection()

    # destination where to create the content
    container = uid and api.get_object_by_uid(uid) or None

    # extract the data from the request
    records = req.get_request_data()

    results = []
    for record in records:

        # get the portal_type
        if portal_type is None:
            # try to fetch the portal type out of the request data
            portal_type = record.pop("portal_type", None)

        # check if it is allowed to create the portal_type
        if not is_creation_allowed(portal_type):
            fail(401, "Creation of '{}' is not allowed".format(portal_type))

        if container is None:
            # find the container for content creation
            container = find_target_container(portal_type, record)

        # Check if we have a container and a portal_type
        if not all([container, portal_type]):
            fail(400, "Please provide a container path/uid and portal_type")

        # create the object and pass in the record data
        obj = create_object(container, portal_type, **record)
        if type(obj) is list:
            results.extend(obj)
        else:
            results.append(obj)

    if not results:
        fail(400, "No Objects could be created")

    return make_items_for(results, endpoint=endpoint)
Пример #10
0
def update_object_with_data(content, record):
    """Update the content with the record data

    :param content: A single folderish catalog brain or content object
    :type content: ATContentType/DexterityContentType/CatalogBrain
    :param record: The data to update
    :type record: dict
    :returns: The updated content object
    :rtype: object
    :raises:
        APIError,
        :class:`~plone.jsonapi.routes.exceptions.APIError`
    """

    # ensure we have a full content object
    content = get_object(content)

    # get the proper data manager
    dm = IDataManager(content)

    if dm is None:
        fail(400, "Update for this object is not allowed")

    if content.portal_type == 'Sample':
        content = update_sample(content, record)
        record = u.omit(record, "SampleType", "StorageLocation")

    # Iterate through record items
    for k, v in record.items():
        try:
            success = dm.set(k, v, **record)  #starting point
        except Unauthorized:
            fail(401, "Not allowed to set the field '%s'" % k)
        except ValueError, exc:
            fail(400, str(exc))

        if not success:
            logger.warn("update_object_with_data::skipping key=%r", k)
            continue

        logger.debug("update_object_with_data::field %r updated", k)
Пример #11
0
def create_sample(container, **data):
    """
    create a sample from here that doesnt go via api create
    :param container:
    :param data:
    :return: Sample object
    """
    container = get_object(container)
    request = req.get_request()
    # we need to resolve the SampleType to a full object
    sample_type = data.get("SampleType", None)
    if sample_type is None:
        fail(400, "Please provide a SampleType")

    # TODO We should handle the same values as in the DataManager for this field
    #      (UID, path, objects, dictionaries ...)
    sample_type_results = search(portal_type="SampleType", title=sample_type)
    if not sample_type_results:
        sample_type_results = search(portal_type="SampleType", uid=sample_type)

    # StorageLocation
    storage_location = data.get("StorageLocation", None)
    if storage_location is None:
        fail(400, "Please provide a StorageLocation")

    linked_sample_list = search(portal_type="Sample",
                                title=data.get('LinkedSample', ''))
    linked_sample = linked_sample_list and linked_sample_list[0].getObject(
    ) or None

    try:
        volume = str(data.get('Volume'))
        float_volume = float(volume)
        if not float_volume:
            fail(400, "Please provide a correct Volume")
    except:
        raise

    # TODO We should handle the same values as in the DataManager for this field
    #      (UID, path, objects, dictionaries ...)
    storage_location_results = search(portal_type='StoragePosition',
                                      Title=storage_location)
    if not storage_location_results:
        storage_location_results = search(portal_type='StoragePosition',
                                          uid=storage_location)

    # set the values and call the create function
    values = {
        "title":
        data.get('title', ''),
        "description":
        data.get("description", ""),
        "Project":
        container,  #because the container is in fact the project this sample belongs to.
        "AllowSharing":
        data.get('AllowSharing', 0),
        "StorageLocation":
        storage_location_results and get_object(storage_location_results[0])
        or None,
        "SampleType":
        sample_type_results and get_object(sample_type_results[0]) or None,
        "SubjectID":
        data.get("SubjectID", ""),
        "Barcode":
        data.get("Barcode", ""),
        "Volume":
        volume,
        "Unit":
        data.get("Unit", ""),
        "LinkedSample":
        linked_sample,
        "DateCreated":
        data.get("DateCreated", ""),
    }

    api_source = data.get('APISource', None)
    if api_source:
        values['APISource'] = api_source

    return create_smp(container, request, values)
Пример #12
0
def create_storage(container, portal_type, **data):
    """
        Create a storage unit and (un-)managed storage via API
    """
    def set_inputs_into_schema(instance, temperature, department, unit_type):
        # Set field values across each object if possible
        schema = instance.Schema()
        if temperature and 'Temperature' in schema:
            instance.Schema()['Temperature'].set(instance, temperature)
        if department and 'Department' in schema:
            instance.Schema()['Department'].set(instance, department)
        if unit_type and 'UnitType' in schema:
            instance.Schema()['UnitType'].set(instance, unit_type)

    def set_storage_types(instance, storage_interfaces):
        schema = instance.Schema()
        if storage_interfaces and 'StorageTypes' in schema:
            instance.Schema()['StorageTypes'].set(instance, storage_interfaces)

        for storage_interface in storage_interfaces:
            inter = resolve(storage_interface)
            alsoProvides(instance, inter)

    container = get_object(container)

    # variables for storage unit
    department_title = data.get("department", "")
    temperature = data.get("temperature", "")
    unit_type = data.get("unit_type", "")

    department = None
    if container.portal_type == "StorageUnit":
        department = container.getDepartment()
        temperature = container.getTemperature()
    else:
        brains = search(portal_type="Department", title=department_title)
        if not brains:
            department = brains[0].getObject()

    # variables for managed storage
    if portal_type == "ManagedStorage":
        number_positions = data.get("number_positions", "")
        x_axis = data.get("x_axis", "")
        y_axis = data.get("y_axis", "")
        try:
            x_axis = x_axis and int(x_axis) or ''
            y_axis = y_axis and int(y_axis) or ''
            number_positions = number_positions and int(number_positions) or ''
        except ValueError:
            fail(401, "Number positions, X axis and Y axis must be integers.")

        if not number_positions or not x_axis or not y_axis:
            fail(
                400,
                "Number positions, X axis and Y axis are required to create storage positions."
            )

    # common variables
    prefix = data.get("prefix", "")
    leading_zeros = data.get("leading_zeros", "")
    if not prefix or not leading_zeros:
        fail(
            400,
            "Prefix and leading_zeros are required to construct storage unit title and Id."
        )

    number_items = data.get("number_items", "")
    try:
        number_items = number_items and int(number_items) or 1
    except ValueError:
        fail(401, "Number items must be integer.")

    seq_start = data.get("seq_start", "")
    try:
        seq_start = seq_start and int(seq_start) or 1
    except ValueError:
        fail(401, "Id sequence start must be integer.")

    sequence = range(seq_start, seq_start + number_items)

    units = []
    for x in sequence:
        id_obj = prefix + '-' + str(x).zfill(len(leading_zeros) + 1)
        title_obj = prefix + ' ' + str(x).zfill(len(leading_zeros) + 1)

        instance = api.content.create(container=container,
                                      type=portal_type,
                                      id=id_obj,
                                      title=title_obj)

        if instance.portal_type == "StorageUnit":
            set_inputs_into_schema(instance, temperature, department,
                                   unit_type)
        elif instance.portal_type == "UnmanagedStorage":
            set_storage_types(instance,
                              ["baobab.lims.interfaces.IStockItemStorage"])
        elif instance.portal_type == "ManagedStorage":
            instance.setXAxis(x_axis)
            instance.setYAxis(y_axis)
            set_storage_types(
                instance, ["baobab.lims.interfaces.ISampleStorageLocation"])
            positions = storage_positions(instance, number_positions)
            for position in positions:
                set_storage_types(
                    position,
                    ["baobab.lims.interfaces.ISampleStorageLocation"])
                position.reindexObject()

        instance.reindexObject()

        units.append(instance)

    return units
Пример #13
0
            success = dm.set(k, v, **record)  #starting point
        except Unauthorized:
            fail(401, "Not allowed to set the field '%s'" % k)
        except ValueError, exc:
            fail(400, str(exc))

        if not success:
            logger.warn("update_object_with_data::skipping key=%r", k)
            continue

        logger.debug("update_object_with_data::field %r updated", k)

    # Validate the entire content object
    invalid = validate_object(content, record)
    if invalid:
        fail(400, u.to_json(invalid))

    # do a wf transition
    if record.get("transition", None):
        t = record.get("transition")
        logger.debug(">>> Do Transition '%s' for Object %s", t,
                     content.getId())
        do_transition_for(content, t)

    # reindex the object
    content.reindexObject()
    return content


# TODO: MOVE TO BAOBAB (QUINTON)
def update_sample(content, record):