示例#1
0
    def request(self,
                method,
                endpoint=None,
                headers=None,
                use_auth=True,
                _url=None,
                **kwargs):
        """
        Make a request to the Canvas API and return the response.

        :param method: The HTTP method for the request.
        :type method: str
        :param endpoint: The endpoint to call.
        :type endpoint: str
        :param headers: Optional HTTP headers to be sent with the request.
        :type headers: dict
        :param use_auth: Optional flag to remove the authentication header from the request.
        :type use_auth: bool
        :param _url: Optional argument to send a request to a URL outside of the Canvas API. \
                    If this is selected and an endpoint is provided, the endpoint will be \
                    ignored and only the _url argument will be used.
        :type _url: str
        :rtype: str
        """
        full_url = _url if _url else "%s%s" % (self.base_url, endpoint)

        if not headers:
            headers = {}

        if use_auth:
            auth_header = {'Authorization': 'Bearer %s' % (self.access_token)}
            headers.update(auth_header)

        if method == 'GET':
            req_method = self._get_request
        elif method == 'POST':
            req_method = self._post_request
        elif method == 'DELETE':
            req_method = self._delete_request
        elif method == 'PUT':
            req_method = self._put_request

        response = req_method(full_url, headers, kwargs)

        if response.status_code == 400:
            raise BadRequest(response.json())
        elif response.status_code == 401:
            if 'WWW-Authenticate' in response.headers:
                raise InvalidAccessToken(response.json())
            else:
                raise Unauthorized(response.json())
        elif response.status_code == 404:
            raise ResourceDoesNotExist('Not Found')
        elif response.status_code == 500:
            raise CanvasException(
                "API encountered an error processing your request")

        return response
示例#2
0
    def submit(self, submission, file=None, **kwargs):
        """
        Makes a submission for an assignment.

        :calls: `POST /api/v1/courses/:course_id/assignments/:assignment_id/submissions \
        <https://canvas.instructure.com/doc/api/submissions.html#method.submissions.create>`_

        :param submission: The attributes of the submission.
        :type submission: dict
        :param file: A file to upload with the submission. (Optional,
            defaults to `None`. Submission type must be `online_upload`)
        :type file: file or str

        :rtype: :class:`canvasapi.submission.Submission`
        """
        if isinstance(submission, dict) and "submission_type" in submission:
            kwargs["submission"] = submission
        else:
            raise RequiredFieldMissing(
                "Dictionary with key 'submission_type' is required.")

        if file:
            if submission.get("submission_type") != "online_upload":
                raise ValueError(
                    "To upload a file, `submission['submission_type']` must be `online_upload`."
                )

            upload_response = self.upload_to_submission(file, **kwargs)
            if upload_response[0]:
                kwargs["submission"]["file_ids"] = [upload_response[1]["id"]]
            else:
                raise CanvasException("File upload failed. Not submitting.")

        response = self._requester.request(
            "POST",
            "courses/{}/assignments/{}/submissions".format(
                self.course_id, self.id),
            _kwargs=combine_kwargs(**kwargs),
        )
        response_json = response.json()
        response_json.update(course_id=self.course_id)

        return Submission(self._requester, response_json)
示例#3
0
    def get_sessionless_launch_url(self, **kwargs):
        """
        Return a sessionless launch url for an external tool.

        :calls: `GET /api/v1/courses/:course_id/external_tools/sessionless_launch
            <https://canvas.instructure.com/doc/api/external_tools.html#method.external_tools.generate_sessionless_launch>`_
            or `GET /api/v1/accounts/:account_id/external_tools/sessionless_launch \
            <https://canvas.instructure.com/doc/api/external_tools.html#method.external_tools.generate_sessionless_launch>`_

        :rtype: str
        """
        kwargs['id'] = self.id
        response = self._requester.request(
            'GET', '%ss/%s/external_tools/sessionless_launch' %
            (self.parent_type, self.parent_id), **combine_kwargs(**kwargs))
        try:
            return response.json()['url']
        except KeyError:
            raise CanvasException('Canvas did not respond with a valid URL')
示例#4
0
    def delete(self):
        """
        Delete the current account

        Note: Cannot delete an account with active courses or active
        sub accounts. Cannot delete a root account.

        :calls: `DELETE /api/v1/accounts/:account_id/sub_accounts/:id \
        <https://canvas.beta.instructure.com/doc/api/accounts.html#method.sub_accounts.destroy>`_

        :returns: True if successfully deleted; False otherwise.
        :rtype: bool
        """
        if not hasattr(self, 'parent_account_id') or not self.parent_account_id:
            raise CanvasException("Cannot delete a root account.")

        response = self._requester.request(
            'DELETE',
            'accounts/{}/sub_accounts/{}'.format(self.parent_account_id, self.id)
        )

        return response.json().get('workflow_state') == 'deleted'
示例#5
0
    def get_sessionless_launch_url(self, **kwargs):
        """
        Return a sessionless launch url for an external tool.

        :calls: `GET /api/v1/courses/:course_id/external_tools/sessionless_launch
            <https://canvas.instructure.com/doc/api/external_tools.html#method.external_tools.generate_sessionless_launch>`_
            or `GET /api/v1/accounts/:account_id/external_tools/sessionless_launch \
            <https://canvas.instructure.com/doc/api/external_tools.html#method.external_tools.generate_sessionless_launch>`_

        :rtype: str
        """
        kwargs["id"] = self.id
        response = self._requester.request(
            "GET",
            "{}s/{}/external_tools/sessionless_launch".format(
                self.parent_type, self.parent_id),
            _kwargs=combine_kwargs(**kwargs),
        )
        try:
            return response.json()["url"]
        except KeyError:
            raise CanvasException("Canvas did not respond with a valid URL")
示例#6
0
    def request(self,
                method,
                endpoint=None,
                headers=None,
                use_auth=True,
                _url=None,
                _kwargs=None,
                json=False,
                **kwargs):
        """
        Make a request to the Canvas API and return the response.

        :param method: The HTTP method for the request.
        :type method: str
        :param endpoint: The endpoint to call.
        :type endpoint: str
        :param headers: Optional HTTP headers to be sent with the request.
        :type headers: dict
        :param use_auth: Optional flag to remove the authentication
            header from the request.
        :type use_auth: bool
        :param _url: Optional argument to send a request to a URL
            outside of the Canvas API. If this is selected and an
            endpoint is provided, the endpoint will be ignored and
            only the _url argument will be used.
        :type _url: str
        :param _kwargs: A list of 2-tuples representing processed
            keyword arguments to be sent to Canvas as params or data.
        :type _kwargs: `list`
        :param json: Whether or not to treat the data as json instead of form data.
            currently only the POST request of GraphQL is using this parameter.
            For all other methods it's just passed and ignored.
        :type json: `bool`
        :rtype: str
        """
        full_url = _url if _url else "{}{}".format(self.base_url, endpoint)

        if not headers:
            headers = {}

        if use_auth:
            auth_header = {
                "Authorization": "Bearer {}".format(self.access_token)
            }
            headers.update(auth_header)

        # Convert kwargs into list of 2-tuples and combine with _kwargs.
        _kwargs = _kwargs or []
        _kwargs.extend(kwargs.items())

        # Do any final argument processing before sending to request method.
        for i, kwarg in enumerate(_kwargs):
            kw, arg = kwarg

            # Convert boolean objects to a lowercase string.
            if isinstance(arg, bool):
                _kwargs[i] = (kw, str(arg).lower())

            # Convert any datetime objects into ISO 8601 formatted strings.
            elif isinstance(arg, datetime):
                _kwargs[i] = (kw, arg.isoformat())

        # Determine the appropriate request method.
        if method == "GET":
            req_method = self._get_request
        elif method == "POST":
            req_method = self._post_request
        elif method == "DELETE":
            req_method = self._delete_request
        elif method == "PUT":
            req_method = self._put_request
        elif method == "PATCH":
            req_method = self._patch_request

        # Call the request method
        logger.info("Request: {method} {url}".format(method=method,
                                                     url=full_url))
        logger.debug("Headers: {headers}".format(
            headers=pformat(clean_headers(headers))))

        if _kwargs:
            logger.debug("Data: {data}".format(data=pformat(_kwargs)))

        response = req_method(full_url, headers, _kwargs, json=json)
        logger.info("Response: {method} {url} {status}".format(
            method=method, url=full_url, status=response.status_code))
        logger.debug("Headers: {headers}".format(
            headers=pformat(clean_headers(response.headers))))

        try:
            logger.debug("Data: {data}".format(data=pformat(response.json())))
        except ValueError:
            logger.debug("Data: {data}".format(data=pformat(response.text)))

        # Add response to internal cache
        if len(self._cache) > 4:
            self._cache.pop()

        self._cache.insert(0, response)

        # Raise for status codes
        if response.status_code == 400:
            raise BadRequest(response.text)
        elif response.status_code == 401:
            if "WWW-Authenticate" in response.headers:
                raise InvalidAccessToken(response.json())
            else:
                raise Unauthorized(response.json())
        elif response.status_code == 403:
            raise Forbidden(response.text)
        elif response.status_code == 404:
            raise ResourceDoesNotExist("Not Found")
        elif response.status_code == 409:
            raise Conflict(response.text)
        elif response.status_code == 422:
            raise UnprocessableEntity(response.text)
        elif response.status_code > 400:
            # generic catch-all for error codes
            raise CanvasException(
                "Encountered an error: status code {}".format(
                    response.status_code))

        return response
示例#7
0
    def request(
            self, method, endpoint=None, headers=None, use_auth=True,
            _url=None, _kwargs=None, **kwargs):
        """
        Make a request to the Canvas API and return the response.

        :param method: The HTTP method for the request.
        :type method: str
        :param endpoint: The endpoint to call.
        :type endpoint: str
        :param headers: Optional HTTP headers to be sent with the request.
        :type headers: dict
        :param use_auth: Optional flag to remove the authentication
            header from the request.
        :type use_auth: bool
        :param _url: Optional argument to send a request to a URL
            outside of the Canvas API. If this is selected and an
            endpoint is provided, the endpoint will be ignored and
            only the _url argument will be used.
        :type _url: str
        :param _kwargs: A list of 2-tuples representing processed
            keyword arguments to be sent to Canvas as params or data.
        :type _kwargs: `list`
        :rtype: str
        """
        full_url = _url if _url else "{}{}".format(self.base_url, endpoint)

        if not headers:
            headers = {}

        if use_auth:
            auth_header = {'Authorization': 'Bearer {}'.format(self.access_token)}
            headers.update(auth_header)

        # Convert kwargs into list of 2-tuples and combine with _kwargs.
        _kwargs = _kwargs or []
        _kwargs.extend(kwargs.items())

        # Do any final argument processing before sending to request method.
        for i, kwarg in enumerate(_kwargs):
            kw, arg = kwarg

            # Convert boolean objects to a lowercase string.
            if isinstance(arg, bool):
                _kwargs[i] = (kw, str(arg).lower())

            # Convert any datetime objects into ISO 8601 formatted strings.
            elif isinstance(arg, datetime):
                _kwargs[i] = (kw, arg.isoformat())

        # Determine the appropriate request method.
        if method == 'GET':
            req_method = self._get_request
        elif method == 'POST':
            req_method = self._post_request
        elif method == 'DELETE':
            req_method = self._delete_request
        elif method == 'PUT':
            req_method = self._put_request

        # Call the request method
        response = req_method(full_url, headers, _kwargs)

        # Add response to internal cache
        if len(self._cache) > 4:
            self._cache.pop()

        self._cache.insert(0, response)

        # Raise for status codes
        if response.status_code == 400:
            raise BadRequest(response.text)
        elif response.status_code == 401:
            if 'WWW-Authenticate' in response.headers:
                raise InvalidAccessToken(response.json())
            else:
                raise Unauthorized(response.json())
        elif response.status_code == 403:
            raise Forbidden(response.text)
        elif response.status_code == 404:
            raise ResourceDoesNotExist('Not Found')
        elif response.status_code == 500:
            raise CanvasException("API encountered an error processing your request")

        return response