Ejemplo n.º 1
0
    def _get_languages_from_http(self):
        """
        Reads an Accept HTTP header and returns an array of Media Type string in descending weighted order
        :return: List of URIs of accept profiles in descending request order
        :rtype: list
        """
        if hasattr(self.request, 'headers'):
            if self.request.headers.get('Accept-Language') is not None:
                try:
                    # split the header into individual URIs, with weights still attached
                    languages = self.request.headers['Accept-Language'].split(',')
                    # remove \s
                    languages = [x.strip() for x in languages]

                    # split off any weights and sort by them with default weight = 1
                    languages = [
                        (float(x.split(';')[1].replace('q=', ''))
                         if len(x.split(';')) == 2 else 1, x.split(';')[0]) for x in languages
                    ]

                    # sort profiles by weight, heaviest first
                    languages.sort(reverse=True)

                    # return only the orderd list of languages, not weights
                    return[x[1] for x in languages]
                except Exception:
                    raise ViewsFormatsException(
                        'You have requested a language using an Accept-Language header that is incorrectly formatted.')

        return None
Ejemplo n.º 2
0
    def _get_accept_profiles_in_order(self):
        """
        Reads an Accept-Profile HTTP header and returns an array of Profile URIs in descending weighted order

        :return: List of URIs of accept profiles in descending request order
        :rtype: list
        """
        try:
            # split the header into individual URIs, with weights still attached
            profiles = self.request.headers['Accept-Profile'].split(',')
            # remove <, >, and \s
            profiles = [
                x.replace('<', '').replace('>', '').replace(' ', '').strip()
                for x in profiles
            ]

            # split off any weights and sort by them with default weight = 1
            profiles = [(float(x.split(';')[1].replace('q=', '')) if len(
                x.split(';')) == 2 else 1, x.split(';')[0]) for x in profiles]

            # sort profiles by weight, heaviest first
            profiles.sort(reverse=True)

            return [x[1] for x in profiles]
        except Exception as e:
            raise ViewsFormatsException(
                'You have requested a profile using an Accept-Profile header that is incorrectly formatted.'
            )
Ejemplo n.º 3
0
    def _get_mediatypes_from_http(self):
        """Returns a list of Media Type tokens from an Accept HTTP header in descending weighted order
        :return: List of URIs of accept profiles in descending request order
        :rtype: list
        """
        if hasattr(self.request, 'headers'):
            if self.request.headers.get('Accept') is not None:
                try:
                    # Chrome breaking Accept header variable by adding v=b3
                    # Issue https://github.com/RDFLib/pyLDAPI/issues/21
                    mediatypes_string = re.sub('v=(.*);', '', self.request.headers['Accept'])

                    # split the header into individual URIs, with weights still attached
                    mediatypes = mediatypes_string.split(',')

                    # remove \s
                    mediatypes = [x.strip() for x in mediatypes]

                    # split off any weights and sort by them with default weight = 1
                    mediatypes = [
                        (float(x.split(';')[1].replace('q=', ''))
                         if ";q=" in x else 1, x.split(';')[0]) for x in mediatypes
                    ]

                    # sort profiles by weight, heaviest first
                    mediatypes.sort(reverse=True)

                    # return only the orderd list of mediatypes, not weights
                    return[x[1] for x in mediatypes]
                except Exception:
                    raise ViewsFormatsException(
                        'You have requested a Media Type using an Accept header that is incorrectly formatted.')

        return None
Ejemplo n.º 4
0
 def _get_profiles_from_http(self):
     """
     Reads an Accept-Profile HTTP header and returns a list of Profile tokens in descending weighted order
     Ref: https://www.w3.org/TR/dx-prof-conneg/#http-getresourcebyprofile
     :return: List of URIs of accept profiles in descending request order
     :rtype: list
     """
     if self.request.headers.get('Accept-Profile') is not None:
         try:
             ap = connegp.AcceptProfileHeaderParser(self.request.headers.get('Accept-Profile'))
             if ap.valid:
                 profiles = []
                 for profile in ap.profiles:
                     # convert this valid URI/URN to a token
                     for token, view in self.profiles.items():
                         if view.namespace == profile['profile']:
                             profiles.append(token)
                 if len(profiles) == 0:
                     return None
                 else:
                     return profiles
             else:
                 return None
         except Exception:
             msg = 'You have requested a profile using an Accept-Profile header that is incorrectly formatted.'
             raise ViewsFormatsException(msg)
     else:
         return None
Ejemplo n.º 5
0
    def _get_requested_view(self):
        # if a particular _view is requested, if it's available, return it
        # the _view selector, coming first (before profile neg) will override profile neg, if both are set
        # if nothing is set, return default view (not HTTP 406)
        if self.request.values.get('_view') is not None:
            if self.views.get(self.request.values.get('_view')) is not None:
                return self.request.values.get('_view')
            else:
                raise ViewsFormatsException(
                    'The requested view is not available for the resource for which it was requested'
                )
        elif hasattr(self.request, 'headers'):
            if self.request.headers.get('Accept-Profile') is not None:
                h = self._get_best_accept_profile()
                return h if h is not None else self.default_view_token

        return self.default_view_token
Ejemplo n.º 6
0
    def _get_accept_profiles_in_order(self):
        """
        Reads an Accept-Profile HTTP header and returns an array of Profile URIs in descending weighted order

        :return: List of URIs of accept profiles in descending request order
        :rtype: list
        """
        try:
            # split the header into individual URIs, with weights still attached
            profiles = self.request.headers['Accept-Profile'].split(',')
            # remove <, >, and \s
            profiles = [
                x.replace('<', '').replace('>', '').replace(' ', '').strip()
                for x in profiles
            ]

            # split off any weights and sort by them with default weight = 1
            weighted_profiles = []
            for mtype in profiles:
                mtype_parts = iter(mtype.split(";"))
                mimetype = next(mtype_parts)
                try:
                    qweight = 0.0
                    while True:
                        part = next(mtype_parts)
                        if part.startswith("q="):
                            qweight = float(part.replace("q=", ""))
                            break
                except StopIteration:
                    qweight = 1.0
                weighted_profiles.append((qweight, mimetype))

            # sort profiles by weight, heaviest first
            weighted_profiles.sort(reverse=True)

            return [x[1] for x in weighted_profiles]
        except Exception as e:
            raise ViewsFormatsException(
                'You have requested a profile using an Accept-Profile header that is incorrectly formatted.'
            )
Ejemplo n.º 7
0
    def _get_requested_view(self):
        # if a particular _view is requested, if it's available, return it
        # the _view selector, coming first (before profile neg) will override profile neg, if both are set
        # if nothing is set, return default view (not HTTP 406)
        query_view = self.request.values.get('_view', None)
        if query_view is not None:
            requested_view = str(query_view).replace(' ', '+')
            if requested_view == "_internal":
                return requested_view
            if self.views.get(requested_view, None) is not None:
                return requested_view
            else:
                # TODO: determine whether or not to
                # silently return default view
                raise ViewsFormatsException(
                    'The requested view is not available for the resource for which it was requested')
        elif hasattr(self.request, 'headers'):
            if self.request.headers.get('Accept-Profile') is not None:
                h = self._get_best_accept_profile()
                return h if h is not None else self.default_view_token

        return self.default_view_token
Ejemplo n.º 8
0
    def __init__(self,
                 request,
                 uri,
                 views,
                 default_view_token,
                 alternates_template=None):
        """
        Init function for class
        :param request: the Flask request object that triggered this class object creation
        :type request: Flask request object
        :param uri: the URI called that triggered this API endpoint (can be via redirects but the main URI is needed)
        :type uri: string
        :param views: a list of views available for this resource
        :type views: list (of View class objects)
        :param default_view_token: the ID of the default view (key of a view in the list of Views)
        :type default_view_token: string (key in views)
        :param alternates_template: the jinja template to use for rendering the HTML alternates view
        :type alternates_template: string | None
        """
        self.request = request
        self.uri = uri

        # ensure alternates isn't hogged by user
        for k, v in views.items():
            if k == 'alternates':
                raise ViewsFormatsException(
                    'You must not manually add a view with token \'alternates\' as this is auto-created'
                )
        self.views = views

        # ensure that the default view is actually a given view
        if default_view_token not in self.views.keys():
            raise ViewsFormatsException(
                'The view token you specified for the default view is not in the list of views you supplied'
            )
        self.default_view_token = default_view_token
        self.alternates_template = alternates_template
        # auto-add in an Alternates view
        self.views['alternates'] = View(
            'Alternates',
            'The view that lists all other views',
            ['text/html', 'application/json', '_internal'] +
            self.RDF_MIMETYPES,
            'text/html',
            languages=['en'],  # default 'en' only for now
            namespace='https://promsns.org/def/alt')

        # get view & format for this request, flag any errors but do not except out
        try:
            self.view = self._get_requested_view()
            try:
                self.format = self._get_requested_format()
                if self.format is None:
                    self.format = self.views[self.view].default_format

                self.language = self._get_requested_language()
                if self.language is None:
                    self.language = self.views[self.view].default_language
            except ViewsFormatsException as e:
                print(e)
                self.vf_error = str(e)
        except ViewsFormatsException as e:
            print(e)
            self.vf_error = str(e)

        self.headers = dict()
Ejemplo n.º 9
0
    def __init__(self,
                 request,
                 uri,
                 views,
                 default_view_token,
                 alternates_template=None):
        """
        Constructor

        :param request: Flask request object that triggered this class object's creation.
        :type request: :class:`flask.request`
        :param uri: The URI that triggered this API endpoint (can be via redirects but the main URI is needed).
        :type uri: str
        :param views: A dictionary of views available for this resource.
        :type views: dict (of :class:`.View` class objects)
        :param default_view_token: The ID of the default view (key of a view in the dictionary of :class:`.View` objects)
        :type default_view_token: str (a key in views)
        :param alternates_template: The Jinja2 template to use for rendering the HTML *alternates view*. If None, then it will default to try and use a template called :code:`alternates.html`.
        :type alternates_template: str

        .. seealso:: See the :class:`.View` class on how to create a dictionary of views.

        """
        self.request = request
        self.uri = uri

        # ensure alternates isn't hogged by user
        for k, v in views.items():
            if k == 'alternates':
                raise ViewsFormatsException(
                    'You must not manually add a view with token \'alternates\' as this is auto-created'
                )
        self.views = views

        # ensure that the default view is actually a given view
        if default_view_token not in self.views.keys():
            raise ViewsFormatsException(
                'The view token you specified for the default view is not in the list of views you supplied'
            )
        self.default_view_token = default_view_token
        self.alternates_template = alternates_template
        # auto-add in an Alternates view
        self.views['alternates'] = View(
            'Alternates',
            'The view that lists all other views',
            ['text/html', 'application/json', '_internal'] +
            self.RDF_MIMETYPES,
            'text/html',
            languages=['en'],  # default 'en' only for now
            namespace='https://promsns.org/def/alt')

        # get view & format for this request, flag any errors but do not except out
        try:
            self.view = self._get_requested_view()
            try:
                self.format = self._get_requested_format()
                if self.format is None:
                    self.format = self.views[self.view].default_format

                self.language = self._get_requested_language()
                if self.language is None:
                    self.language = self.views[self.view].default_language
            except ViewsFormatsException as e:
                print(e)
                self.vf_error = str(e)
        except ViewsFormatsException as e:
            print(e)
            self.vf_error = str(e)

        self.headers = dict()
Ejemplo n.º 10
0
    def __init__(self, request, uri, label, comment, register_items,
                 contained_item_classes, register_total_count, *args,
                 views=None, default_view_token=None, super_register=None,
                 page_size_max=1000, register_template=None, **kwargs):
        """
        Constructor

        :param request: The Flask request object triggering this class object's creation.
        :type request: :class:`.flask.request`
        :param uri: The URI requested.
        :type uri: str
        :param label: The label of the Register.
        :type label: str
        :param comment: A description of the Register.
        :type comment: str
        :param register_items: The items within this register as a list of URI strings or tuples with string elements like (URI, label). They can also be tuples like (URI, URI, label) if you want to manually specify an item's class.
        :type register_items: list
        :param contained_item_classes: The list of URI strings of each distinct class of item contained in this Register.
        :type contained_item_classes: list
        :param register_total_count: The total number of items in this Register (not of a page but the register as a whole).
        :type register_total_count: int
        :param views: A dictionary of named :class:`.View` objects available for this Register, apart from 'reg' which is auto-created.
        :type views: dict
        :param default_view_token: The ID of the default :class:`.View` (key of a view in the list of Views).
        :type default_view_token: str
        :param super_register: A super-Register URI for this register. Can be within this API or external.
        :type super_register: str
        :param register_template: The Jinja2 template to use for rendering the HTML view of the register. If None, then it will default to try and use a template called :code:`alternates.html`.
        :type register_template: str or None
        """
        if views is None:
            views = {}
        for k, v in views.items():
            if k == 'reg':
                raise ViewsFormatsException(
                    'You must not manually add a view with token \'reg\' as this is auto-created'
                )
        views.update(self._add_standard_reg_view())
        if default_view_token is None:
            default_view_token = 'reg'
        super(RegisterRenderer, self).__init__(request, uri, views,
                                               default_view_token, **kwargs)
        self.label = label
        self.comment = comment
        if register_items is not None:
            self.register_items = register_items
        else:
            self.register_items = []
        self.contained_item_classes = contained_item_classes
        self.register_total_count = register_total_count
        self.per_page = request.args.get('per_page', type=int, default=20)
        self.page = request.args.get('page', type=int, default=1)
        self.super_register = super_register
        self.page_size_max = page_size_max
        self.register_template = register_template
        self.paging_error = self._paging()

        try:
            self.format = self._get_requested_format()
        except ViewsFormatsException as e:
            self.vf_error = str(e)
    def __init__(self,
                 request,
                 instance_uri,
                 label,
                 comment,
                 parent_container_uri,
                 parent_container_label,
                 members,
                 members_total_count,
                 *args,
                 profiles=None,
                 default_profile_token=None,
                 super_register=None,
                 page_size_max=1000,
                 register_template=None,
                 **kwargs):
        """
        Constructor

        :param request: The Flask request object triggering this class object's creation.
        :type request: :class:`.flask.request`
        :param instance_uri: The URI requested.
        :type instance_uri: str
        :param label: The label of the Register.
        :type label: str
        :param comment: A description of the Register.
        :type comment: str
        :param members: The items within this register as a list of URI strings or tuples with string elements
        like (URI, label). They can also be tuples like (URI, URI, label) if you want to manually specify an item's
        class.
        :type members: list
        :param contained_item_classes: The list of URI strings of each distinct class of item contained in this
        Register.
        :type contained_item_classes: list
        :param members_total_count: The total number of items in this Register (not of a page but the register as a
        whole).
        :type members_total_count: int
        :param profiles: A dictionary of named :class:`.View` objects available for this Register, apart from 'reg'
        which is auto-created.
        :type profiles: dict
        :param default_profile_token: The ID of the default :class:`.View` (key of a profile in the list of Views).
        :type default_profile_token: str
        :param super_register: A super-Register URI for this register. Can be within this API or external.
        :type super_register: str
        :param register_template: The Jinja2 template to use for rendering the HTML profile of the register. If None,
        then it will default to try and use a template called :code:`alt.html`.
        :type register_template: str or None
        :param per_page: Number of items to show per page if not specified in request. If None, then it will default to
        RegisterRenderer.DEFAULT_ITEMS_PER_PAGE.
        :type per_page: int or None
        """
        if profiles is None:
            profiles = {}
        for k, v in profiles.items():
            if k == 'mem':
                raise ViewsFormatsException(
                    'You must not manually add a profile with token \'mem\' as this is auto-created'
                )
        profiles.update({
            'mem':
            Profile(
                'Members Profile',
                'A very basic RDF data model-only profile that lists the sub-items (members) of collections (rdf:Bag)',
                ['text/html'] + Renderer.RDF_MEDIA_TYPES,
                'text/html',
                profile_uri='https://w3id.org/profile/mem')
        })
        if default_profile_token is None:
            default_profile_token = 'mem'
        super(ContainerRenderer,
              self).__init__(request, instance_uri, profiles,
                             default_profile_token, **kwargs)
        if self.vf_error is None:
            self.label = label
            self.comment = comment
            self.parent_container_uri = parent_container_uri
            self.parent_container_label = parent_container_label
            if members is not None:
                self.members = members
            else:
                self.members = []
            self.members_total_count = members_total_count
            self.per_page = request.args.get(
                'per_page',
                type=int,
                default=ContainerRenderer.DEFAULT_ITEMS_PER_PAGE)
            self.page = request.args.get('page', type=int, default=1)
            self.super_register = super_register
            self.page_size_max = page_size_max
            self.members_template = register_template
            self.paging_error = self._paging()
            self.template_extras = kwargs