Пример #1
0
def url_for(endpoint, default=DEFAULT_ENDPOINT, **values):
    """Looks up the API URL for the given endpoint

    :param endpoint: The name of the registered route (aka endpoint)
    :type endpoint: string
    :returns: External URL for this endpoint
    :rtype: string/None
    """

    try:
        return router.url_for(endpoint, force_external=True, values=values)
    except Exception:
        logger.warn("Could not build API URL for endpoint '%s'. "
                    "No route provider registered?" % endpoint)
        # build generic API URL
        return router.url_for(default, force_external=True, values=values)
Пример #2
0
def url_for(endpoint, default=DEFAULT_ENDPOINT, **values):
    """Looks up the API URL for the given endpoint

    :param endpoint: The name of the registered route (aka endpoint)
    :type endpoint: string
    :returns: External URL for this endpoint
    :rtype: string/None
    """

    try:
        return router.url_for(endpoint, force_external=True, values=values)
    except Exception:
        logger.warn("Could not build API URL for endpoint '%s'. "
                    "No route provider registered?" % endpoint)
        # build generic API URL
        return router.url_for(default, force_external=True, values=values)
Пример #3
0
def url_for(endpoint, default="senaite.jsonapi.get", **values):
    """Looks up the API URL for the given endpoint

    :param endpoint: The name of the registered route (aka endpoint)
    :type endpoint: string
    :returns: External URL for this endpoint
    :rtype: string/None
    """

    try:
        return router.url_for(endpoint, force_external=True, values=values)
    except Exception:
        # XXX plone.jsonapi.core should catch the BuildError of Werkzeug and
        #     throw another error which can be handled here.
        logger.debug("Could not build API URL for endpoint '%s'. "
                     "No route provider registered?" % endpoint)

        # build generic API URL
        return router.url_for(default, force_external=True, values=values)
Пример #4
0
def url_for(endpoint, **values):
    """ returns the api url
    """
    try:
        return router.url_for(endpoint, force_external=True, values=values)
    except:
        # XXX plone.jsonapi.core should catch the BuildError of Werkzeug and
        #     throw another error which can be handled here.
        logger.warn("Could not build API URL for endpoint '%s'. No route provider registered?" % endpoint)
        return None
Пример #5
0
def url_for(endpoint, default="bika.lims.jsonapi.get", **values):
    """Looks up the API URL for the given endpoint

    :param endpoint: The name of the registered route (aka endpoint)
    :type endpoint: string
    :returns: External URL for this endpoint
    :rtype: string/None
    """

    try:
        return router.url_for(endpoint, force_external=True, values=values)
    except Exception:
        # XXX plone.jsonapi.core should catch the BuildError of Werkzeug and
        #     throw another error which can be handled here.
        logger.debug("Could not build API URL for endpoint '%s'. "
                     "No route provider registered?" % endpoint)

        # build generic API URL
        return router.url_for(default, force_external=True, values=values)
Пример #6
0
    def remove(self, context, request):
        """/@@API/remove: Remove existing object

        Required parameters:

            - UID: UID for the object.

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
        }

        So.

        >>> portal = layer['portal']
        >>> portal_url = portal.absolute_url()
        >>> from plone.app.testing import SITE_OWNER_NAME
        >>> from plone.app.testing import SITE_OWNER_PASSWORD
        >>> contact = portal.clients["client-1"]["contact-1"]
        >>> uid = contact.UID()

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/remove?UID="+uid)
        >>> browser.contents
        '{..."success": true...}'
        """

        savepoint = transaction.savepoint()
        uc = getToolByName(context, 'uid_catalog')

        _uid = request.get('UID', '')
        if not _uid:
            raise BadRequest("No UID specified in request")

        ret = {
            "url": router.url_for("remove", force_external=True),
            "success": True,
            "error": False,
        }

        data = uc(UID=_uid)
        if not data:
            raise BadRequest("No objects found")

        for proxy in data:
            try:
                parent = proxy.getObject().aq_parent
                parent.manage_delObjects([proxy.id])
            except Exception as e:
                savepoint.rollback()
                msg = "Cannot delete '{0}' because ({1})".format(
                    _uid, e.message)
                raise BadRequest(msg)
        return ret
Пример #7
0
    def remove(self, context, request):
        """/@@API/remove: Remove existing object

        Required parameters:

            - UID: UID for the object.

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
        }

        So.

        >>> portal = layer['portal']
        >>> portal_url = portal.absolute_url()
        >>> from plone.app.testing import SITE_OWNER_NAME
        >>> from plone.app.testing import SITE_OWNER_PASSWORD
        >>> contact = portal.clients["client-1"]["contact-1"]
        >>> uid = contact.UID()

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/remove?UID="+uid)
        >>> browser.contents
        '{..."success": true...}'
        """

        savepoint = transaction.savepoint()
        uc = getToolByName(context, 'uid_catalog')

        _uid = request.get('UID', '')
        if not _uid:
            raise BadRequest("No UID specified in request")

        ret = {
            "url": router.url_for("remove", force_external=True),
            "success": True,
            "error": False,
        }

        data = uc(UID=_uid)
        if not data:
            raise BadRequest("No objects found")

        for proxy in data:
            try:
                parent = proxy.getObject().aq_parent
                parent.manage_delObjects([proxy.id])
            except Exception as e:
                savepoint.rollback()
                msg = "Cannot delete '{0}' because ({1})".format(_uid, e.message)
                raise BadRequest(msg)
        return ret
Пример #8
0
def url_for(endpoint, **values):
    """ returns the api url
    """
    try:
        return router.url_for(endpoint, force_external=True, values=values)
    except:
        # XXX plone.jsonapi.core should catch the BuildError of Werkzeug and
        #     throw another error which can be handled here.
        logger.warn(
            "Could not build API URL for endpoint '%s'. No route provider registered?"
            % endpoint)
        return None
Пример #9
0
    def getusers(self, context, request):
        """/@@API/getusers: Return users belonging to specified roles

        Required parameters:

            - roles: The role of which users to return

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
            users: list of dictionaries: {fullname: fullname, userid: userid}
        }
        
        >>> portal = layer['portal']
        >>> portal_url = portal.absolute_url()
        >>> from plone.app.testing import SITE_OWNER_NAME
        >>> from plone.app.testing import SITE_OWNER_PASSWORD

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/getusers?roles:list=Manager&roles:list=Analyst")
        >>> browser.contents
        '{...test_labmanager1...}'
        >>> browser.contents
        '{...test_analyst1...}'
        
        >>> browser.open(portal_url+"/@@API/getusers")
        >>> browser.contents
        'No roles specified'
        """
        roles = request.get('roles', '')

        if len(roles) == 0:
            raise BadRequest("No roles specified")

        mtool = getToolByName(context, 'portal_membership')
        users = []
        for user in mtool.searchForMembers(roles=roles):
            uid = user.getId()
            fullname = user.getProperty('fullname')
            if not fullname:
                fullname = uid
            users.append({'fullname': fullname, 'userid': uid})

        ret = {
            "url": router.url_for("remove", force_external=True),
            "success": True,
            "error": False,
            'users': users,
        }
        return ret
Пример #10
0
    def getusers(self, context, request):
        """/@@API/getusers: Return users belonging to specified roles

        Required parameters:

            - roles: The role of which users to return

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
            users: list of dictionaries: {fullname: fullname, userid: userid}
        }
        
        >>> portal = layer['portal']
        >>> portal_url = portal.absolute_url()
        >>> from plone.app.testing import SITE_OWNER_NAME
        >>> from plone.app.testing import SITE_OWNER_PASSWORD

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/getusers?roles:list=Manager&roles:list=Analyst")
        >>> browser.contents
        '{...test_labmanager1...}'
        >>> browser.contents
        '{...test_analyst1...}'
        
        >>> browser.open(portal_url+"/@@API/getusers")
        >>> browser.contents
        'No roles specified'
        """
        roles = request.get('roles','')

        if len(roles) == 0:
            raise BadRequest("No roles specified")
        
        mtool = getToolByName(context, 'portal_membership') 
        users = []
        for user in mtool.searchForMembers(roles=roles):
            uid = user.getId()
            fullname = user.getProperty('fullname')
            if not fullname:
                fullname = uid
            users.append({'fullname':fullname, 'userid': uid})
            
        ret = {
            "url": router.url_for("remove", force_external=True),
            "success": True,
            "error": False,
            'users': users,
        }
        return ret
Пример #11
0
    def allowed_transitions_for_many(self, context, request):
        """/@@API/allowedTransitionsFor_many: Returns a list of dictionaries. Each
        dictionary has the following structure:
            {'uid': <uid_object_passed_in>,
             'transitions': [{'id': action_id, 'title': 'Action Title'}, 
                             {...}]
        
        Required parameters:
            - uid: uids of the objects to get the allowed transitions from
        """
        wftool = getToolByName(context, "portal_workflow")
        uc = getToolByName(context, 'uid_catalog')
        uids = json.loads(request.get('uid', '[]'))
        if not uids:
            raise BadRequest("No object UID specified in request")

        allowed_transitions = []
        try:
            brains = uc(UID=uids)
            for brain in brains:
                obj = brain.getObject()
                trans = [{
                    'id': t['id'],
                    'title': t['title']
                } for t in wftool.getTransitionsFor(obj)]
                allowed_transitions.append({
                    'uid': obj.UID(),
                    'transitions': trans
                })
        except Exception as e:
            msg = "Cannot get the allowed transitions ({})".format(e.message)
            raise BadRequest(msg)

        ret = {
            "url":
            router.url_for("allowedTransitionsFor_many", force_external=True),
            "success":
            True,
            "error":
            False,
            "transitions":
            allowed_transitions
        }
        return ret
Пример #12
0
    def do_action_for(self, context, request):
        """/@@API/doActionFor: Perform workflow transition on values returned
        by jsonapi "read" function.

        Required parameters:

            - action: The workflow transition to apply to found objects.

        Parameters used to locate objects are the same as used for the "read"
        method.

        """
        savepoint = transaction.savepoint()
        workflow = getToolByName(context, 'portal_workflow')
        uc = getToolByName(context, 'uid_catalog')

        action = request.get('action', '')
        if not action:
            raise BadRequest("No action specified in request")

        ret = {
            "url": router.url_for("doActionFor", force_external=True),
            "success": True,
            "error": False,
        }

        data = read(context, request)
        objects = data.get('objects', [])
        if len(objects) == 0:
            raise BadRequest("No matching objects found")
        for obj_dict in objects:
            try:
                obj = uc(UID=obj_dict['UID'])[0].getObject()
                workflow.doActionFor(obj, action)
                obj.reindexObject()
            except Exception as e:
                savepoint.rollback()
                msg = "Cannot execute '{0}' on {1} ({2})".format(
                    action, obj, e.message)
                msg = msg.replace("${action_id}", action)
                raise BadRequest(msg)
        return ret
Пример #13
0
    def do_action_for(self, context, request):
        """/@@API/doActionFor: Perform workflow transition on values returned
        by jsonapi "read" function.

        Required parameters:

            - action: The workflow transition to apply to found objects.

        Parameters used to locate objects are the same as used for the "read"
        method.

        """
        savepoint = transaction.savepoint()
        workflow = getToolByName(context, 'portal_workflow')
        uc = getToolByName(context, 'uid_catalog')

        action = request.get('action', '')
        if not action:
            raise BadRequest("No action specified in request")

        ret = {
            "url": router.url_for("doActionFor", force_external=True),
            "success": True,
            "error": False,
        }

        data = read(context, request)
        objects = data.get('objects', [])
        if len(objects) == 0:
            raise BadRequest("No matching objects found")
        for obj_dict in objects:
            try:
                obj = uc(UID=obj_dict['UID'])[0].getObject()
                workflow.doActionFor(obj, action)
                obj.reindexObject()
            except Exception as e:
                savepoint.rollback()
                msg = "Cannot execute '{0}' on {1} ({2})".format(
                    action, obj, e.message)
                msg = msg.replace("${action_id}", action)
                raise BadRequest(msg)
        return ret
Пример #14
0
    def do_action_for_many(self, context, request):
        """/@@API/doActionFor: Perform workflow transition on a list of objects.

        required parameters:

            - obj_paths: a json encoded list of objects to transition.
            - action: the id of the transition

        """
        savepoint = transaction.savepoint()
        workflow = getToolByName(context, 'portal_workflow')
        site_path = request['PATH_INFO'].replace("/@@API/doActionFor_many", "")

        obj_paths = json.loads(request.get('f', '[]'))
        action = request.get('action', '')
        if not action:
            raise BadRequest("No action specified in request")

        ret = {
            "url": router.url_for("doActionFor_many", force_external=True),
            "success": True,
            "error": False,
        }

        for obj_path in obj_paths:
            if not obj_path.startswith("/"):
                obj_path = "/" + obj_path
            obj = context.restrictedTraverse(str(site_path + obj_path))
            if obj_path.startswith(site_path):
                obj_path = obj_path[len(site_path):]
            try:
                workflow.doActionFor(obj, action)
                obj.reindexObject()
            except Exception as e:
                savepoint.rollback()
                msg = "Cannot execute '{0}' on {1} ({2})".format(
                    action, obj, e.message)
                msg = msg.replace("${action_id}", action)
                raise BadRequest(msg)
        return ret
Пример #15
0
    def do_action_for_many(self, context, request):
        """/@@API/doActionFor: Perform workflow transition on a list of objects.

        required parameters:

            - obj_paths: a json encoded list of objects to transition.
            - action: the id of the transition

        """
        savepoint = transaction.savepoint()
        workflow = getToolByName(context, 'portal_workflow')
        site_path = request['PATH_INFO'].replace("/@@API/doActionFor_many", "")

        obj_paths = json.loads(request.get('f', '[]'))
        action = request.get('action', '')
        if not action:
            raise BadRequest("No action specified in request")

        ret = {
            "url": router.url_for("doActionFor_many", force_external=True),
            "success": True,
            "error": False,
        }

        for obj_path in obj_paths:
            if not obj_path.startswith("/"):
                obj_path = "/" + obj_path
            obj = context.restrictedTraverse(str(site_path + obj_path))
            if obj_path.startswith(site_path):
                obj_path = obj_path[len(site_path):]
            try:
                workflow.doActionFor(obj, action)
                obj.reindexObject()
            except Exception as e:
                savepoint.rollback()
                msg = "Cannot execute '{0}' on {1} ({2})".format(
                    action, obj, e.message)
                msg = msg.replace("${action_id}", action)
                raise BadRequest(msg)
        return ret
Пример #16
0
    def allowed_transitions_for_many(self, context, request):
        """/@@API/allowedTransitionsFor_many: Returns a list of dictionaries. Each
        dictionary has the following structure:
            {'uid': <uid_object_passed_in>,
             'transitions': [{'id': action_id, 'title': 'Action Title'}, 
                             {...}]
        
        Required parameters:
            - uid: uids of the objects to get the allowed transitions from
        """
        wftool = getToolByName(context, "portal_workflow")
        uc = getToolByName(context, 'uid_catalog')
        uids = json.loads(request.get('uid', '[]'))
        if not uids:
            raise BadRequest("No object UID specified in request")

        allowed_transitions = []
        try:
            brains = uc(UID=uids)
            for brain in brains:
                obj = brain.getObject()
                trans = [{'id': t['id'], 'title': t['title']} for t in
                         wftool.getTransitionsFor(obj)]
                allowed_transitions.append(
                    {'uid': obj.UID(), 'transitions': trans})
        except Exception as e:
            msg = "Cannot get the allowed transitions ({})".format(e.message)
            raise BadRequest(msg)

        ret = {
            "url": router.url_for("allowedTransitionsFor_many",
                                  force_external=True),
            "success": True,
            "error": False,
            "transitions": allowed_transitions
        }
        return ret
Пример #17
0
 def test_version(self):
     self.assertEqual(router.url_for("apiversion"), "/plone/@@API/version")
Пример #18
0
    def update_many(self, context, request):
        """/@@API/update_many: Update existing object values

        This is a wrapper around the update method, allowing multiple updates
        to be combined into a single request.

        required parameters:

            - input_values: A json-encoded dictionary.
              Each key is an obj_path, and each value is a dictionary
              containing key/value pairs to be set on the object.

        Return value:

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
            updates: return values from update
        }
        """
        self.context = context
        self.request = request
        self.unused = [x for x in self.request.form.keys()]
        self.used("form.submitted")
        self.used("__ac_name")
        self.used("__ac_password")
        ret = {
            "url": router.url_for("update_many", force_external=True),
            "success": False,
            "error": True,
            "updates": [],
        }

        input_values = json.loads(request.get('input_values', '[]'))
        if not input_values:
            raise BadRequest("missing input_values")
        site_path = request['PATH_INFO'].replace("/@@API/update_many", "")

        for obj_path, i in input_values.items():
            savepoint = transaction.savepoint()
            if not obj_path.startswith("/"):
                obj_path = "/" + obj_path
            if obj_path.startswith(site_path):
                obj_path = obj_path[len(site_path):]
            obj = context.restrictedTraverse(str(site_path + obj_path))
            this_ret = {
                "url": router.url_for("update_many", force_external=True),
                "success": False,
                "error": True,
            }
            try:
                set_fields_from_request(obj, i)
            except:
                savepoint.rollback()
                raise
            this_ret['success'] = True
            this_ret['error'] = False
            ret['updates'].append(this_ret)
        ret['success'] = True
        ret['error'] = False
        return ret
Пример #19
0
    def update(self, context, request):
        """/@@API/update: Update existing object values

        Required parameters:

            - obj_path: path to the object, relative to plone site root.
            - fields: json value, dict: key:value = fieldname:value.

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
            <fieldname>: <current value>
            ...
        }

        So.

        >>> portal = layer['portal']
        >>> portal_url = portal.absolute_url()
        >>> from plone.app.testing import SITE_OWNER_NAME
        >>> from plone.app.testing import SITE_OWNER_PASSWORD

        Update a client's existing address:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/update?", "&".join([
        ... "obj_path=/clients/client-1",
        ... "title=Test",
        ... "PostalAddress={'address': '1 Wendy Way', 'city': 'Johannesburg', 'zip': '9000', 'state': 'Gauteng', 'district': '', 'country':'South Africa'}"
        ... ]))
        >>> browser.contents
        '{..."success": true...}'

        quickly check that it saved:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/read?", "&".join([
        ... "Title=Happy Hills",
        ... "include_fields=PostalAddress",
        ... ]))
        >>> browser.contents
        '{...1 Wendy Way...}'

        Try the same with a nonsense fieldname:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/update?", "&".join([
        ... "obj_path=/clients/client-1",
        ... "Thing=Fish",
        ... ]))
        >>> browser.contents
        '{...The following request fields were not used: ...Thing...}'

        Setting the value of a RefereceField to "" or None (null) should not cause
        an error; setting an empty value should clear the field

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/update?", "&".join([
        ... "obj_path=/clients/client-1",
        ... "DefaultCategories=\"\"",
        ... ]))
        >>> browser.contents
        '{..."success": true...}'

        """
        savepoint = transaction.savepoint()
        self.context = context
        self.request = request
        self.unused = [x for x in self.request.form.keys()]
        self.used("form.submitted")
        self.used("__ac_name")
        self.used("__ac_password")
        ret = {
            "url": router.url_for("update", force_external=True),
            "success": False,
            "error": True,
        }
        # always require obj_path
        self.require("obj_path")
        obj_path = self.request['obj_path']
        self.used("obj_path")

        site_path = request['PATH_INFO'].replace("/@@API/update", "")
        obj = context.restrictedTraverse(str(site_path + obj_path))

        try:
            fields = set_fields_from_request(obj, request)
            for field in fields:
                self.used(field)
        except:
            savepoint.rollback()
            raise

        ret['success'] = True
        ret['error'] = False

        if self.unused:
            raise BadRequest("The following request fields were not used: %s.  Request aborted." % self.unused)

        return ret
Пример #20
0
 def testTopicEndpoint(self):
     endpoint = "topics"
     self.assertEqual(router.url_for(endpoint),
                      "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #21
0
 def testNewsItemsEndpoint(self):
     endpoint = "newsitems"
     self.assertEqual(router.url_for(endpoint),
                      "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #22
0
    def update(self, context, request):
        """/@@API/update: Update existing object values

        Required parameters:

            - obj_path: path to the object, relative to plone site root.
            - fields: json value, dict: key:value = fieldname:value.

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
            <fieldname>: <current value>
            ...
        }

        So.

        >>> portal = layer['portal']
        >>> portal_url = portal.absolute_url()
        >>> from plone.app.testing import SITE_OWNER_NAME
        >>> from plone.app.testing import SITE_OWNER_PASSWORD

        Update a client's existing address:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/update?", "&".join([
        ... "obj_path=/clients/client-1",
        ... "title=Test",
        ... "PostalAddress={'address': '1 Wendy Way', 'city': 'Johannesburg', 'zip': '9000', 'state': 'Gauteng', 'district': '', 'country':'South Africa'}"
        ... ]))
        >>> browser.contents
        '{..."success": true...}'

        quickly check that it saved:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/read?", "&".join([
        ... "id=client-1",
        ... "include_fields=PostalAddress",
        ... ]))
        >>> browser.contents
        '{...1 Wendy Way...}'

        Try the same with a nonsense fieldname:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/update?", "&".join([
        ... "obj_path=/clients/client-1",
        ... "Thing=Fish",
        ... ]))
        >>> browser.contents
        '{...The following request fields were not used: ...Thing...}'

        Setting the value of a RefereceField to "" or None (null) should not cause
        an error; setting an empty value should clear the field

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/update?", "&".join([
        ... "obj_path=/clients/client-1",
        ... 'DefaultCategories=',
        ... ]))
        >>> browser.contents
        '{..."success": true...}'

        """
        savepoint = transaction.savepoint()
        self.context = context
        self.request = request
        self.unused = [x for x in self.request.form.keys()]
        self.used("form.submitted")
        self.used("__ac_name")
        self.used("__ac_password")
        ret = {
            "url": router.url_for("update", force_external=True),
            "success": False,
            "error": True,
        }
        obj = None
        # Find out if the object can be retrieved via UID or via
        # traversing.
        if self.request.get('obj_uid', ''):
            uc = getToolByName(self.context, 'uid_catalog')
            brain = uc(UID=self.request.get('obj_uid', ''))
            obj = brain[0].getObject() if brain else None
        if self.request.get('obj_path', '') and not obj:
            obj_path = self.request['obj_path'].split("?")[0]
            site_path = context.portal_url.getPortalObject().getPhysicalPath()
            if site_path and isinstance(site_path, basestring):
                site_path = site_path if site_path.startswith(
                    '/') else '/' + site_path
                obj = context.restrictedTraverse(site_path + obj_path)
            elif site_path and len(site_path) > 1:
                site_path = site_path[1]
                site_path = site_path if site_path.startswith(
                    '/') else '/' + site_path
                obj = context.restrictedTraverse(site_path + obj_path)

        if obj:
            self.used('obj_uid')
            self.used('obj_path')
        else:
            ret['success'] = False
            ret['error'] = True
            return ret

        try:
            fields = set_fields_from_request(obj, request)
            if not fields:
                return ret
            for field in fields:
                self.used(field)
        except:
            savepoint.rollback()
            raise

        ret['success'] = True
        ret['error'] = False

        if self.unused:
            raise BadRequest(
                "The following request fields were not used: %s.  Request aborted."
                % self.unused)

        return ret
Пример #23
0
 def testCRUDEndpoint(self):
     endpoint = "get"
     self.assertEqual(router.url_for(endpoint), "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #24
0
 def test_version(self):
     self.assertEqual(router.url_for("apiversion"), "/plone/@@API/version")
Пример #25
0
 def testUsersEndpoint(self):
     endpoint = "users"
     namespace_endpoint = ".".join([NAMESPACE, endpoint])
     self.assertEqual(router.url_for(namespace_endpoint),
                      "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #26
0
 def testSearchEndpoint(self):
     endpoint = "search"
     self.assertEqual(router.url_for(endpoint), "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #27
0
 def testCollectionsEndpoint(self):
     endpoint = "collections"
     self.assertEqual(router.url_for(endpoint),
                      "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #28
0
 def testTopicEndpoint(self):
     endpoint = "topics"
     self.assertEqual(router.url_for(endpoint), "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #29
0
 def testUsersEndpoint(self):
     endpoint = "users"
     self.assertEqual(router.url_for(endpoint), "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #30
0
 def testUsersEndpoint(self):
     endpoint = "users"
     self.assertEqual(router.url_for(endpoint),
                      "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #31
0
 def testDocumentsEndpoint(self):
     endpoint = "documents"
     self.assertEqual(router.url_for(endpoint), "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #32
0
def url_for(endpoint, **values):
    """ returns the api url
    """
    return router.url_for(endpoint, force_external=True, values=values)
Пример #33
0
    def update_many(self, context, request):
        """/@@API/update_many: Update existing object values

        This is a wrapper around the update method, allowing multiple updates
        to be combined into a single request.

        required parameters:

            - input_values: A json-encoded dictionary.
              Each key is an obj_path, and each value is a dictionary
              containing key/value pairs to be set on the object.

        Return value:

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
            updates: return values from update
        }
        """
        self.context = context
        self.request = request
        self.unused = [x for x in self.request.form.keys()]
        self.used("form.submitted")
        self.used("__ac_name")
        self.used("__ac_password")
        ret = {
            "url": router.url_for("update_many", force_external=True),
            "success": False,
            "error": True,
            "updates": [],
        }

        input_values = json.loads(request.get('input_values', '[]'))
        if not input_values:
            raise BadRequest("missing input_values")
        site_path = request['PATH_INFO'].replace("/@@API/update_many", "")

        for obj_path, i in input_values.items():
            savepoint = transaction.savepoint()
            if not obj_path.startswith("/"):
                obj_path = "/" + obj_path
            if obj_path.startswith(site_path):
                obj_path = obj_path[len(site_path):]
            obj = context.restrictedTraverse(str(site_path + obj_path))
            this_ret = {
                "url": router.url_for("update_many", force_external=True),
                "success": False,
                "error": True,
            }
            try:
                fields = set_fields_from_request(obj, i)
                if not fields:
                    this_ret['success'] = False
                    this_ret['error'] = True
                else:
                    this_ret['success'] = True
                    this_ret['error'] = False
            except:
                savepoint.rollback()
                raise
            ret['updates'].append(this_ret)
        ret['success'] = True
        ret['error'] = False
        return ret
Пример #34
0
 def testUsersEndpoint(self):
     endpoint = "users"
     namespace_endpoint = ".".join([NAMESPACE, endpoint])
     self.assertEqual(router.url_for(namespace_endpoint),
                      "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #35
0
 def testCollectionsEndpoint(self):
     endpoint = "collections"
     self.assertEqual(router.url_for(endpoint), "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #36
0
def read(context, request):
    tag = AuthenticatorView(context, request).authenticator()
    pattern = '<input .*name="(\w+)".*value="(\w+)"'
    _authenticator = re.match(pattern, tag).groups()[1]

    ret = {
        "url": router.url_for("read", force_external=True),
        "success": True,
        "error": False,
        "objects": [],
        "_authenticator": _authenticator,
    }
    debug_mode = App.config.getConfiguration().debug_mode
    catalog_name = request.get("catalog_name", "portal_catalog")
    if not catalog_name:
        raise ValueError("bad or missing catalog_name: " + catalog_name)
    catalog = getToolByName(context, catalog_name)
    indexes = catalog.indexes()

    contentFilter = {}
    for index in indexes:
        if index in request:
            if index == 'review_state' and "{" in request[index]:
                continue
            contentFilter[index] = safe_unicode(request[index])
        if "%s[]"%index in request:
            value = request["%s[]"%index]
            if type(value) in (list, tuple):
                contentFilter[index] = [safe_unicode(v) for v in value]
            else:
                contentFilter[index] = value

    if 'limit' in request:
        try:
            contentFilter['sort_limit'] = int(request["limit"])
        except ValueError:
            pass
    sort_on = request.get('sort_on', 'id')
    contentFilter['sort_on'] = sort_on
    # sort order
    sort_order = request.get('sort_order', '')
    if sort_order:
        contentFilter['sort_order'] = sort_order
    else:
        sort_order = 'ascending'
        contentFilter['sort_order'] = 'ascending'

    include_fields = get_include_fields(request)
    if debug_mode:
        logger.info("contentFilter: " + str(contentFilter))

    # Get matching objects from catalog
    proxies = catalog(**contentFilter)

    # batching items
    page_nr = int(request.get("page_nr", 0))
    try:
        page_size = int(request.get("page_size", 10))
    except ValueError:
        page_size = 10
    # page_size == 0: show all
    if page_size == 0:
        page_size = len(proxies)
    first_item_nr = page_size * page_nr
    if first_item_nr > len(proxies):
        first_item_nr = 0
    page_proxies = proxies[first_item_nr:first_item_nr + page_size]
    for proxy in page_proxies:
        obj_data = {}

        # Place all proxy attributes into the result.
        obj_data.update(load_brain_metadata(proxy, include_fields))

        # Place all schema fields ino the result.
        obj = proxy.getObject()
        obj_data.update(load_field_values(obj, include_fields))

        obj_data['path'] = "/".join(obj.getPhysicalPath())

        # call any adapters that care to modify this data.
        adapters = getAdapters((obj, ), IJSONReadExtender)
        for name, adapter in adapters:
            adapter(request, obj_data)

        ret['objects'].append(obj_data)

    ret['total_objects'] = len(proxies)
    ret['first_object_nr'] = first_item_nr
    last_object_nr = first_item_nr + len(page_proxies)
    if last_object_nr > ret['total_objects']:
        last_object_nr = ret['total_objects']
    ret['last_object_nr'] = last_object_nr

    if debug_mode:
        logger.info("{0} objects returned".format(len(ret['objects'])))
    return ret
Пример #37
0
def read(context, request):
    tag = AuthenticatorView(context, request).authenticator()
    pattern = '<input .*name="(\w+)".*value="(\w+)"'
    _authenticator = re.match(pattern, tag).groups()[1]

    ret = {
        "url": router.url_for("read", force_external=True),
        "success": True,
        "error": False,
        "objects": [],
        "_authenticator": _authenticator,
    }
    debug_mode = App.config.getConfiguration().debug_mode
    catalog_name = request.get("catalog_name", "portal_catalog")
    if not catalog_name:
        raise ValueError("bad or missing catalog_name: " + catalog_name)
    catalog = getToolByName(context, catalog_name)
    indexes = catalog.indexes()

    contentFilter = {}
    for index in indexes:
        if index in request:
            if index == 'review_state' and "{" in request[index]:
                continue
            contentFilter[index] = safe_unicode(request[index])
        if "%s[]"%index in request:
            value = request["%s[]"%index]
            if type(value) in (list, tuple):
                contentFilter[index] = [safe_unicode(v) for v in value]
            else:
                contentFilter[index] = value

    if 'limit' in request:
        try:
            contentFilter['sort_limit'] = int(request["limit"])
        except ValueError:
            pass
    sort_on = request.get('sort_on', 'id')
    contentFilter['sort_on'] = sort_on
    # sort order
    sort_order = request.get('sort_order', '')
    if sort_order:
        contentFilter['sort_order'] = sort_order
    else:
        sort_order = 'ascending'
        contentFilter['sort_order'] = 'ascending'

    include_fields = get_include_fields(request)
    if debug_mode:
        logger.info("contentFilter: " + str(contentFilter))

    # Get matching objects from catalog
    proxies = catalog(**contentFilter)
    # batching items
    page_nr = int(request.get("page_nr", 0))
    try:
        page_size = int(request.get("page_size", 10))
    except ValueError:
        page_size = 10
    # page_size == 0: show all
    if page_size == 0:
        page_size = len(proxies)
    first_item_nr = page_size * page_nr
    if first_item_nr > len(proxies):
        first_item_nr = 0
    page_proxies = proxies[first_item_nr:first_item_nr + page_size]

    for proxy in page_proxies:
        obj_data = {}

        # Place all proxy attributes into the result.
        obj_data.update(load_brain_metadata(proxy, include_fields))

        # Place all schema fields ino the result.
        obj = proxy.getObject()
        obj_data.update(load_field_values(obj, include_fields))

        obj_data['path'] = "/".join(obj.getPhysicalPath())

        # call any adapters that care to modify this data.
        adapters = getAdapters((obj, ), IJSONReadExtender)
        for name, adapter in adapters:
            adapter(request, obj_data)

        ret['objects'].append(obj_data)

    ret['total_objects'] = len(proxies)
    ret['first_object_nr'] = first_item_nr
    last_object_nr = first_item_nr + len(page_proxies)
    if last_object_nr > ret['total_objects']:
        last_object_nr = ret['total_objects']
    ret['last_object_nr'] = last_object_nr

    if debug_mode:
        logger.info("{0} objects returned".format(len(ret['objects'])))
    return ret
Пример #38
0
 def testSearchEndpoint(self):
     endpoint = "search"
     self.assertEqual(router.url_for(endpoint),
                      "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #39
0
    def calculate_partitions(self, context, request):
        """Calculate the size and type of sample partitions required to perform
        analysis services on a certain sample type.

        Required fields

            - services: A list of Analysis Services
            - sampletype: The sample type in use for this sample

        Example usage:

            @@API/calculate_partitions
                ?sampletype=portal_type:SampleType,title=Cattle Feed
                &services:list=portal_type:AnalysisService,title:Ash
                &services:list=portal_type:AnalysisService,title:Moisture
        """

        ret = {
            "url": router.url_for("calculate_partitions", force_external=True),
            "success": False,
            "error": True,
        }
        parts = []

        uc = getToolByName(context, 'uid_catalog')

        services = []
        _services = request.get('services', '').split(",")
        if not _services:
            raise BadRequest("services are not present in request")
        for uid in _services:
            if not uid:
                continue
            try:
                services.append(uc(UID=uid)[0].getObject())
            except:
                raise BadRequest("analysis service uid %s is invalid" % uid)

        _sampletype = request.get('sampletype', '')
        if not _sampletype:
            raise BadRequest("sampletype is not present in the request")
        try:
            sampletype = uc(UID=_sampletype)[0].getObject()
        except:
            raise BadRequest("sample type %s is invalid" % _sampletype)

        for service in services:
            partsetup = [
                ps for ps in service.getPartitionSetup()
                if ps['sampletype'] == sampletype.UID()
            ]
            if partsetup:
                # User values set for each SampleType on each service
                separate = bool(partsetup[0]['separate'])
                containers = [
                    uc(UID=uid)[0].getObject()
                    for uid in partsetup[0]['container']
                ]
                preservations = [
                    uc(UID=uid)[0].getObject()
                    for uid in partsetup[0]['preservation']
                ]
                minvol = mg(partsetup[0]['vol'])
            else:
                # read default values from service
                separate = service.getSeparate()
                containers = service.getContainer()
                if not containers:
                    containers = []
                if containers and not type(containers) in (list, tuple):
                    containers = [
                        containers,
                    ]
                preservations = service.getPreservation()
                if not preservations:
                    preservations = []
                if preservations and not type(preservations) in (list, tuple):
                    preservations = [
                        preservations,
                    ]
                minvol = mg(sampletype.getMinimumVolume())

            # ContainerTypes may also be selected in the UI.
            # we want a list of actual containers which this analysis can use
            _containers = []
            for c in containers:
                if c.portal_type == 'Container':
                    _containers.append(c)
                else:
                    _containers.extend(c.getContainers())
            containers = _containers

            containers.sort(
                lambda a, b: cmp(mg(a.getCapacity()), mg(b.getCapacity())))

            if separate:
                parts.append({
                    'services': [
                        service,
                    ],
                    'separate': True,
                    'container': containers,
                    'preservation': preservations,
                    'minvol': minvol,
                })
            else:
                # find partition this analysis can use or create new entry.
                for x, part in enumerate(parts):
                    if part['separate']:
                        continue

                    # our container must match this part.
                    _containers = []
                    if part['container']:
                        _containers = [
                            c for c in part['container'] if c in containers
                        ]
                        if not _containers:
                            continue

                    # our preservation must match this part
                    _preservations = []
                    if part['preservation']:
                        _preservations = [
                            p for p in part['preservation']
                            if p in preservations
                        ]
                        if not _preservations:
                            continue

                    # filter containers on capacoty
                    _required_vol = part['minvol'] + minvol
                    if _containers:
                        _containers = [
                            c for c in _containers
                            if mg(c.getCapacity()) > _required_vol
                        ]
                        if not _containers:
                            continue

                    # all the conditions passed:
                    # this partition can host our analysis
                    part['minvol'] = _required_vol
                    part['services'].append(service)
                    part['container'] = _containers
                    part['preservation'] = _preservations
                    parts[x] = part
                    break
                # no partition found to share this analysis: create new.
                else:
                    parts.append({
                        'services': [
                            service,
                        ],
                        'separate': False,
                        'container': containers,
                        'preservation': preservations,
                        'minvol': minvol
                    })

        # Convert objects to UIDs
        for x, part in enumerate(parts):
            parts[x]['service_titles'] = [s.Title() for s in part['services']]
            parts[x]['services'] = [s.UID() for s in part['services']]
            parts[x]['container_titles'] = [
                c.Title() for c in part['container']
            ]
            parts[x]['container'] = [c.UID() for c in part['container']]
            parts[x]['preservation_titles'] = [
                p.Title() for p in part['preservation']
            ]
            parts[x]['preservation'] = [p.UID() for p in part['preservation']]
            parts[x]['minvol'] = str(part['minvol'])

        ret['success'] = True
        ret['error'] = False
        ret['parts'] = parts

        return ret
Пример #40
0
    def create(self, context, request):
        """/@@API/create: Create new object.

        Required parameters:

            - obj_type = portal_type of new object.
            - obj_path = path of new object, from plone site root. - Not required for
             obj_type=AnalysisRequest

        Optionally:

            - obj_id = ID of new object.

        All other parameters in the request are matched against the object's
        Schema.  If a matching field is found in the schema, then the value is
        taken from the request and sent to the field's mutator.

        Reference fields may have their target value(s) specified with a
        delimited string query syntax, containing the portal_catalog search:

            <FieldName>=index1:value1|index2:value2

        eg to set the Client of a batch:

            ...@@API/update?obj_path=<path>...
            ...&Client=title:<client_title>&...

        And, to set a multi-valued reference, these both work:

            ...@@API/update?obj_path=<path>...
            ...&InheritedObjects:list=title:AR1...
            ...&InheritedObjects:list=title:AR2...

            ...@@API/update?obj_path=<path>...
            ...&InheritedObjects[]=title:AR1...
            ...&InheritedObjects[]=title:AR2...

        The Analysis_Specification parameter is special, it mimics
        the format of the python dictionaries, and only service Keyword
        can be used to reference services.  Even if the keyword is not
        actively required, it must be supplied:

            <service_keyword>:min:max:error tolerance

        The function returns a dictionary as a json string:

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
        }

        >>> portal = layer['portal']
        >>> portal_url = portal.absolute_url()
        >>> from plone.app.testing import SITE_OWNER_NAME
        >>> from plone.app.testing import SITE_OWNER_PASSWORD

        Simple AR creation, no obj_path parameter is required:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/create", "&".join([
        ... "obj_type=AnalysisRequest",
        ... "Client=portal_type:Client|id:client-1",
        ... "SampleType=portal_type:SampleType|title:Apple Pulp",
        ... "Contact=portal_type:Contact|getFullname:Rita Mohale",
        ... "Services:list=portal_type:AnalysisService|title:Calcium",
        ... "Services:list=portal_type:AnalysisService|title:Copper",
        ... "Services:list=portal_type:AnalysisService|title:Magnesium",
        ... "SamplingDate=2013-09-29",
        ... "Specification=portal_type:AnalysisSpec|title:Apple Pulp",
        ... ]))
        >>> browser.contents
        '{..."success": true...}'

        If some parameters are specified and are not located as existing fields or properties
        of the created instance, the create should fail:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/create?", "&".join([
        ... "obj_type=Batch",
        ... "obj_path=/batches",
        ... "title=Test",
        ... "Thing=Fish"
        ... ]))
        >>> browser.contents
        '{...The following request fields were not used: ...Thing...}'

        Now we test that the AR create also fails if some fields are spelled wrong

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/create", "&".join([
        ... "obj_type=AnalysisRequest",
        ... "thing=Fish",
        ... "Client=portal_type:Client|id:client-1",
        ... "SampleType=portal_type:SampleType|title:Apple Pulp",
        ... "Contact=portal_type:Contact|getFullname:Rita Mohale",
        ... "Services:list=portal_type:AnalysisService|title:Calcium",
        ... "Services:list=portal_type:AnalysisService|title:Copper",
        ... "Services:list=portal_type:AnalysisService|title:Magnesium",
        ... "SamplingDate=2013-09-29"
        ... ]))
        >>> browser.contents
        '{...The following request fields were not used: ...thing...}'

        """
        savepoint = transaction.savepoint()
        self.context = context
        self.request = request
        self.unused = [x for x in self.request.form.keys()]
        self.used("form.submitted")
        self.used("__ac_name")
        self.used("__ac_password")
        # always require obj_type
        self.require("obj_type")
        obj_type = self.request['obj_type']
        self.used("obj_type")
        # AnalysisRequest shortcut: creates Sample, Partition, AR, Analyses.
        if obj_type == "AnalysisRequest":
            try:
                return self._create_ar(context, request)
            except:
                savepoint.rollback()
                raise
        # Other object types require explicit path as their parent
        self.require("obj_path")
        obj_path = self.request['obj_path']
        if not obj_path.startswith("/"):
            obj_path = "/" + obj_path
        self.used("obj_path")
        site_path = request['PATH_INFO'].replace("/@@API/create", "")
        parent = context.restrictedTraverse(str(site_path + obj_path))
        # normal permissions still apply for this user
        if not getSecurityManager().checkPermission(AccessJSONAPI, parent):
            msg = "You don't have the '{0}' permission on {1}".format(
                AccessJSONAPI, parent.absolute_url())
            raise Unauthorized(msg)

        obj_id = request.get("obj_id", "")
        _renameAfterCreation = False
        if not obj_id:
            _renameAfterCreation = True
            obj_id = tmpID()
        self.used(obj_id)

        ret = {
            "url": router.url_for("create", force_external=True),
            "success": True,
            "error": False,
        }

        try:
            obj = _createObjectByType(obj_type, parent, obj_id)
            obj.unmarkCreationFlag()
            if _renameAfterCreation:
                renameAfterCreation(obj)
            ret['obj_id'] = obj.getId()
            used_fields = set_fields_from_request(obj, request)
            for field in used_fields:
                self.used(field)
            obj.reindexObject()
            obj.aq_parent.reindexObject()
            event.notify(ObjectInitializedEvent(obj))
            obj.at_post_create_script()
        except:
            savepoint.rollback()
            raise

        if self.unused:
            raise BadRequest("The following request fields were not used: %s.  Request aborted." % self.unused)

        return ret
Пример #41
0
 def testCRUDEndpoint(self):
     endpoint = "get"
     self.assertEqual(router.url_for(endpoint),
                      "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #42
0
 def testDocumentsEndpoint(self):
     endpoint = "documents"
     self.assertEqual(router.url_for(endpoint),
                      "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #43
0
    def _create_ar(self, context, request):
        """Creates AnalysisRequest object, with supporting Sample, Partition
        and Analysis objects.  The client is retrieved from the obj_path
        key in the request.

        Required request parameters:

            - Contact: One client contact Fullname.  The contact must exist
              in the specified client.  The first Contact with the specified
              value in it's Fullname field will be used.

            - SampleType_<index> - Must be an existing sample type.

        Optional request parameters:

        - CCContacts: A list of contact Fullnames, which will be copied on
          all messages related to this AR and it's sample or results.

        - CCEmails: A list of email addresses to include as above.

        - Sample_id: Create a secondary AR with an existing sample.  If
          unspecified, a new sample is created.

        - Specification: a lookup to set Analysis specs default values
          for all analyses

        - Analysis_Specification: specs (or overrides) per analysis, using
          a special lookup format.

            &Analysis_Specification:list=<Keyword>:min:max:error&...


        """

        wftool = getToolByName(context, 'portal_workflow')
        bc = getToolByName(context, 'bika_catalog')
        bsc = getToolByName(context, 'bika_setup_catalog')
        pc = getToolByName(context, 'portal_catalog')
        ret = {
            "url": router.url_for("create", force_external=True),
            "success": True,
            "error": False,
        }
        SamplingWorkflowEnabled = context.bika_setup.getSamplingWorkflowEnabled()
        for field in [
            'Client',
            'SampleType',
            'Contact',
            'SamplingDate',
            'Services']:
            self.require(field)
            self.used(field)

        try:
            client = resolve_request_lookup(context, request, 'Client')[0].getObject()
        except IndexError:
            raise Exception("Client not found")

        # Sample_id
        if 'Sample' in request:
            try:
                sample = resolve_request_lookup(context, request, 'Sample')[0].getObject()
            except IndexError:
                raise Exception("Sample not found")
        else:
            # Primary AR
            sample = _createObjectByType("Sample", client, tmpID())
            sample.unmarkCreationFlag()
            fields = set_fields_from_request(sample, request)
            for field in fields:
                self.used(field)
            sample._renameAfterCreation()
            sample.setSampleID(sample.getId())
            event.notify(ObjectInitializedEvent(sample))
            sample.at_post_create_script()

            if SamplingWorkflowEnabled:
                wftool.doActionFor(sample, 'sampling_workflow')
            else:
                wftool.doActionFor(sample, 'no_sampling_workflow')

        ret['sample_id'] = sample.getId()

        parts = [{'services': [],
                  'container': [],
                  'preservation': '',
                  'separate': False}]

        specs = self.get_specs_from_request()
        ar = _createObjectByType("AnalysisRequest", client, tmpID())
        ar.unmarkCreationFlag()
        fields = set_fields_from_request(ar, request)
        for field in fields:
            self.used(field)
        ar.setSample(sample.UID())
        ar._renameAfterCreation()
        ret['ar_id'] = ar.getId()
        brains = resolve_request_lookup(context, request, 'Services')
        service_uids = [p.UID for p in brains]
        new_analyses = ar.setAnalyses(service_uids, specs=specs)
        ar.setRequestID(ar.getId())
        ar.reindexObject()
        event.notify(ObjectInitializedEvent(ar))
        ar.at_post_create_script()

        # Create sample partitions
        parts_and_services = {}
        for _i in range(len(parts)):
            p = parts[_i]
            part_prefix = sample.getId() + "-P"
            if '%s%s' % (part_prefix, _i + 1) in sample.objectIds():
                parts[_i]['object'] = sample['%s%s' % (part_prefix, _i + 1)]
                parts_and_services['%s%s' % (part_prefix, _i + 1)] = p['services']
                part = parts[_i]['object']
            else:
                part = _createObjectByType("SamplePartition", sample, tmpID())
                parts[_i]['object'] = part
                container = None
                preservation = p['preservation']
                parts[_i]['prepreserved'] = False
                part.edit(
                    Container=container,
                    Preservation=preservation,
                )
                part.processForm()
                if SamplingWorkflowEnabled:
                    wftool.doActionFor(part, 'sampling_workflow')
                else:
                    wftool.doActionFor(part, 'no_sampling_workflow')
                parts_and_services[part.id] = p['services']

        if SamplingWorkflowEnabled:
            wftool.doActionFor(ar, 'sampling_workflow')
        else:
            wftool.doActionFor(ar, 'no_sampling_workflow')

        # Add analyses to sample partitions
        # XXX jsonapi create AR: right now, all new analyses are linked to the first samplepartition
        if new_analyses:
            analyses = list(part.getAnalyses())
            analyses.extend(new_analyses)
            part.edit(
                Analyses=analyses,
            )
            for analysis in new_analyses:
                analysis.setSamplePartition(part)

        # If Preservation is required for some partitions,
        # and the SamplingWorkflow is disabled, we need
        # to transition to to_be_preserved manually.
        if not SamplingWorkflowEnabled:
            to_be_preserved = []
            sample_due = []
            lowest_state = 'sample_due'
            for p in sample.objectValues('SamplePartition'):
                if p.getPreservation():
                    lowest_state = 'to_be_preserved'
                    to_be_preserved.append(p)
                else:
                    sample_due.append(p)
            for p in to_be_preserved:
                doActionFor(p, 'to_be_preserved')
            for p in sample_due:
                doActionFor(p, 'sample_due')
            doActionFor(sample, lowest_state)
            for analysis in ar.objectValues('Analysis'):
                doActionFor(analysis, lowest_state)
            doActionFor(ar, lowest_state)

        # receive secondary AR
        if request.get('Sample_id', ''):
            doActionFor(ar, 'sampled')
            doActionFor(ar, 'sample_due')
            not_receive = ['to_be_sampled', 'sample_due', 'sampled',
                           'to_be_preserved']
            sample_state = wftool.getInfoFor(sample, 'review_state')
            if sample_state not in not_receive:
                doActionFor(ar, 'receive')
            for analysis in ar.getAnalyses(full_objects=1):
                doActionFor(analysis, 'sampled')
                doActionFor(analysis, 'sample_due')
                if sample_state not in not_receive:
                    doActionFor(analysis, 'receive')

        if self.unused:
            raise BadRequest("The following request fields were not used: %s.  Request aborted." % self.unused)

        return ret
Пример #44
0
    def calculate_partitions(self, context, request):
        """Calculate the size and type of sample partitions required to perform
        analysis services on a certain sample type.

        Required fields

            - services: A list of Analysis Services
            - sampletype: The sample type in use for this sample

        Example usage:

            @@API/calculate_partitions
                ?sampletype=portal_type:SampleType,title=Cattle Feed
                &services:list=portal_type:AnalysisService,title:Ash
                &services:list=portal_type:AnalysisService,title:Moisture
        """

        ret = {
            "url": router.url_for("calculate_partitions", force_external=True),
            "success": False,
            "error": True,
        }
        parts = []

        uc = getToolByName(context, 'uid_catalog')

        services = []
        _services = request.get('services', '').split(",")
        if not _services:
            raise BadRequest("services are not present in request")
        for uid in _services:
            if not uid:
                continue
            try:
                services.append(uc(UID=uid)[0].getObject())
            except:
                raise BadRequest("analysis service uid %s is invalid" % uid)

        _sampletype = request.get('sampletype', '')
        if not _sampletype:
            raise BadRequest("sampletype is not present in the request")
        try:
            sampletype = uc(UID=_sampletype)[0].getObject()
        except:
            raise BadRequest("sample type %s is invalid" % _sampletype)

        for service in services:
            partsetup = [ps for ps in service.getPartitionSetup()
                         if ps['sampletype'] == sampletype.UID()]
            if partsetup:
                # User values set for each SampleType on each service
                separate = bool(partsetup[0]['separate'])
                containers = [uc(UID=uid)[0].getObject() for uid in partsetup[0]['container']]
                preservations = [uc(UID=uid)[0].getObject() for uid in partsetup[0]['preservation']]
                minvol = mg(partsetup[0]['vol'])
            else:
                # read default values from service
                separate = service.getSeparate()
                containers = service.getContainer()
                if not containers:
                    containers = []
                if containers and not type(containers) in (list, tuple):
                    containers = [containers, ]
                preservations = service.getPreservation()
                if not preservations:
                    preservations = []
                if preservations and not type(preservations) in (list, tuple):
                    preservations = [preservations, ]
                minvol = mg(sampletype.getMinimumVolume())

            # ContainerTypes may also be selected in the UI.
            # we want a list of actual containers which this analysis can use
            _containers = []
            for c in containers:
                if c.portal_type == 'Container':
                    _containers.append(c)
                else:
                    _containers.extend(c.getContainers())
            containers = _containers

            containers.sort(lambda a, b: cmp(mg(a.getCapacity()),
                                             mg(b.getCapacity())))

            if separate:
                parts.append({
                    'services': [service, ],
                    'separate': True,
                    'container': containers,
                    'preservation': preservations,
                    'minvol': minvol,
                })
            else:
                # find partition this analysis can use or create new entry.
                for x, part in enumerate(parts):
                    if part['separate']:
                        continue

                    # our container must match this part.
                    _containers = []
                    if part['container']:
                        _containers = [c for c in part['container']
                                       if c in containers]
                        if not _containers:
                            continue

                    # our preservation must match this part
                    _preservations = []
                    if part['preservation']:
                        _preservations = [p for p in part['preservation']
                                          if p in preservations]
                        if not _preservations:
                            continue

                    # filter containers on capacoty
                    _required_vol = part['minvol'] + minvol
                    if _containers:
                        _containers = [c for c in _containers
                                       if mg(c.getCapacity()) > _required_vol]
                        if not _containers:
                            continue

                    # all the conditions passed:
                    # this partition can host our analysis
                    part['minvol'] = _required_vol
                    part['services'].append(service)
                    part['container'] = _containers
                    part['preservation'] = _preservations
                    parts[x] = part
                    break
                # no partition found to share this analysis: create new.
                else:
                    parts.append({
                        'services': [service, ],
                        'separate': False,
                        'container': containers,
                        'preservation': preservations,
                        'minvol': minvol
                    })

        # Convert objects to UIDs
        for x, part in enumerate(parts):
            parts[x]['service_titles'] = [s.Title() for s in part['services']]
            parts[x]['services'] = [s.UID() for s in part['services']]
            parts[x]['container_titles'] = [c.Title() for c in part['container']]
            parts[x]['container'] = [c.UID() for c in part['container']]
            parts[x]['preservation_titles'] = [p.Title() for p in part['preservation']]
            parts[x]['preservation'] = [p.UID() for p in part['preservation']]
            parts[x]['minvol'] = str(part['minvol'])

        ret['success'] = True
        ret['error'] = False
        ret['parts'] = parts

        return ret
Пример #45
0
 def testNewsItemsEndpoint(self):
     endpoint = "newsitems"
     self.assertEqual(router.url_for(endpoint), "/plone/@@API/plone/api/1.0/%s" % endpoint)
Пример #46
0
    def update(self, context, request):
        """/@@API/update: Update existing object values

        Required parameters:

            - obj_path: path to the object, relative to plone site root.
            - fields: json value, dict: key:value = fieldname:value.

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
            <fieldname>: <current value>
            ...
        }

        So.

        >>> portal = layer['portal']
        >>> portal_url = portal.absolute_url()
        >>> from plone.app.testing import SITE_OWNER_NAME
        >>> from plone.app.testing import SITE_OWNER_PASSWORD

        Update a client's existing address:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/update?", "&".join([
        ... "obj_path=/clients/client-1",
        ... "title=Test",
        ... "PostalAddress={'address': '1 Wendy Way', 'city': 'Johannesburg', 'zip': '9000', 'state': 'Gauteng', 'district': '', 'country':'South Africa'}"
        ... ]))
        >>> browser.contents
        '{..."success": true...}'

        quickly check that it saved:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/read?", "&".join([
        ... "id=client-1",
        ... "include_fields=PostalAddress",
        ... ]))
        >>> browser.contents
        '{...1 Wendy Way...}'

        Try the same with a nonsense fieldname:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/update?", "&".join([
        ... "obj_path=/clients/client-1",
        ... "Thing=Fish",
        ... ]))
        >>> browser.contents
        '{...The following request fields were not used: ...Thing...}'

        Setting the value of a RefereceField to "" or None (null) should not cause
        an error; setting an empty value should clear the field

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/update?", "&".join([
        ... "obj_path=/clients/client-1",
        ... 'DefaultCategories=',
        ... ]))
        >>> browser.contents
        '{..."success": true...}'

        """
        savepoint = transaction.savepoint()
        self.context = context
        self.request = request
        self.unused = [x for x in self.request.form.keys()]
        self.used("form.submitted")
        self.used("__ac_name")
        self.used("__ac_password")
        ret = {
            "url": router.url_for("update", force_external=True),
            "success": False,
            "error": True,
        }
        obj = None
        # Find out if the object can be retrieved via UID or via
        # traversing.
        if self.request.get('obj_uid', ''):
            uc = getToolByName(self.context, 'uid_catalog')
            brain = uc(UID=self.request.get('obj_uid', ''))
            obj = brain[0].getObject() if brain else None
        if self.request.get('obj_path', '') and not obj:
            obj_path = self.request['obj_path']
            site_path = context.portal_url.getPortalObject().getPhysicalPath()
            if site_path and isinstance(site_path, basestring):
                site_path = site_path if site_path.startswith('/') else '/' + site_path
                obj = context.restrictedTraverse(site_path + obj_path)
            elif site_path and len(site_path) > 1:
                site_path = site_path[1]
                site_path = site_path if site_path.startswith('/') else '/' + site_path
                obj = context.restrictedTraverse(site_path + obj_path)

        if obj:
            self.used('obj_uid')
            self.used('obj_path')
        else:
            ret['success'] = False
            ret['error'] = True
            return ret

        try:
            fields = set_fields_from_request(obj, request)
            for field in fields:
                self.used(field)
        except:
            savepoint.rollback()
            raise

        ret['success'] = True
        ret['error'] = False

        if self.unused:
            raise BadRequest("The following request fields were not used: %s.  Request aborted." % self.unused)

        return ret