Metadata-Version: 1.1
Name: django-dataporten
Version: 0.4.0
Summary: A simple Django app to fetch and parse data from Dataporten.
Home-page: https://github.com/JakobGM/django-dataporten
Author: Jakob Gerhard Martinussen
Author-email: jakobgm@gmail.com
License: MIT License
Description: =================
        Django-dataporten
        =================
        
        Django-dataporten is a simple Django app which fetches data from dataporten and
        attaches it to your Django user objects. It implements the dataporten groups
        API, allowing you to easily access group memberships through a pythonic API, without
        worrying about parsing raw JSON content.
        
        Setup
        =====
        
        1. Add "dataporten" to your :code:`INSTALLED_APPS` setting like this
        
        .. code:: python
        
            INSTALLED_APPS = [
                ...
                'dataporten',
                ...
            ]
        
        
        2. Run `python manage.py migrate` to create the dataporten proxy models.
        
        3. In your `settings.py` file, add the variable :code:`DATAPORTEN_TOKEN_FUNCTION`,
        which should be a dotted path to the function that will retrieve user tokens.
        Dataporten uses this "importable string" in order to retrieve the OAuth2
        authentication token for a given user. For instance,
        
        .. code:: python
        
            DATAPORTEN_TOKEN_FUNCTION = 'myapp.oauth.allauth_token'
        
        The function should accept a :code:`User` and return a :code:`str`, if the
        token exists, else :code:`None`.
        Here is a python3.6/3.7 example that will work if you use `django-allauth`_:
        
        .. code:: python
        
            def allauth_token(user: User) -> Optional[str]:
                try:
                    return SocialToken.objects.get(
                        account__user=user,
                        account__provider='dataporten',
                    ).token
                except SocialToken.DoesNotExist:
                    return None
        
        4. Add the dataporten middleware. This middleware adds a :code:`dataporten`
        attribute to :code:`request.user` for users with an associated
        dataporten token. Take care to place it after
        :code:`django.contrib.auth.middleware.AuthenticationMiddleware`.
        
        .. code:: python
        
            MIDDLEWARE = (
                ...
                'django.contrib.auth.middleware.AuthenticationMiddleware',
                ...
                'dataporten.middleware.DataportenGroupsMiddleware',
                ...
            )
        
        5. Optionally, enable caching for API queries. Take care to create the directory
        set in :code:`DATAPORTEN_CACHE_PATH` before starting the Django server.
        
        .. code:: python
        
            # Cache requests to the dataporten API
            DATAPORTEN_CACHE_REQUESTS = True
        
            # Where to save the sqlite3 cache backend
            DATAPORTEN_CACHE_PATH = 'tmp/'
        
        .. _django-allauth: https://github.com/pennersr/django-allauth:
        
        
        Usage
        =====
        
        The :code:`DataportenGroupsMiddleware` adds an instance of
        :code:`DataportenGroupsManager` assigned to :code:`request.user.dataporten` for
        every valid dataporten user making a request. This object contains attributes
        for accessing different types of group memberships, such as courses, organization
        units, study programmes, main profiles, generic groups, and *all* groups.
        
        
        Groups
        ------
        
        *All* groups are accessible through :code:`request.user.dataporten.groups`.
        This is a dictionary keyed by group ids, with :code:`Group` objects as values.
        Let's use the Applied Physics and Mathematics master degree at NTNU as an example
        for common attributes available for all group types
        
        .. code:: python
        
            uid = 'fc:fs:fs:prg:ntnu.no:MTFYMA'
            group = request.user.dataporten.groups[uid]
            assert group.uid == uid
            assert group.name == 'Fysikk og matematikk - masterstudium (5-\u00e5rig)'
            assert group.url == 'http://www.ntnu.no/studier/mtfyma'
            assert group.group_type == 'fc:fs:prg'
        
        Membership objects
        ------------------
        
        All groups have an associated :code:`Membership` object which can be used for
        further querying of membership properties for that particular group.
        The original membership JSON can be accessed from the :code:`Membership.json`
        attribute:
        
        .. code:: python
        
            group = request.user.dataporten.groups[uid]
            membership = group.membership
        
            print(membership.json)
            >>> {
            >>>     'title': ['fast ansatt'],
            >>>     'affiliation': ['employee', 'member', 'affiliate', 'student'],
            >>>     'primaryAffiliation': 'employee',
            >>>     'basic': 'admin',
            >>>     'displayName': 'Ansatt',
            >>> }
        
        
        Some additional, common properties are available:
        
        .. code:: python
        
            # Membership objects are "truthy" if they are considered active
            assert membership
        
            # Not all group memberships have a set end time
            assert isinstance(membership.end_time, [datetime.datetime, None])
        
            # The displayName value is used as the membership string representation
            assert str(membership) == 'Ansatt'
        
            # Primary affiliation to the group
            assert membership.primary_affiliation == 'employee'
        
            # And all affiliations to the group
            assert membership.affiliations == [
                'employee',
                'member',
                'affiliate',
                'student',
            ]
        
        
        Group membership checks
        ~~~~~~~~~~~~~~~~~~~~~~~
        
        You can also check if a user is an **active** member of a specific dataporten group
        by providing the group :code:`id` to the :code:`DataportenGroupsManager.is_member_of`
        method. This is offered as a more ergonomic alternative to
        :code:`bool(request.user.dataporten.groups[uid].membership)`. For instance,
        
        .. code:: python
        
            assert request.user.dataporten.is_member_of(
                uid='fc:org:ntnu.no:unit:167500',
                active=True,
            )
        
        If :code:`active` is set to :code:`False`, the method only checks if the user
        has been a member of the group at any time, not necessarily if the user is
        an **active** member.
        
        Semester objects
        ----------------
        
        Membership objects also have an associated :code:`Semester` object which
        can be used to determine the year and season of the membership.
        
        .. code:: python
        
            from dataporten.parsers import Semester
        
            semester = request.user.groups[uid].membership.semester
            assert semester.year == 2019
            assert semester.season in (Semester.SPRING, Semester.AUTUMN)
        
        The :code:`Semester` class also implements :code:`__sub__`, which
        returns "semester delta" between two semesters. For instance,
        the spring semester of 2019 minus the autumn semester of 2017 would
        return :code:`3`.
        
        Courses
        -------
        
        Course enrollment can be queryed from the :code:`CourseManager` object, attributed to
        :code:`request.user.dataporten.course`.
        
        You can check if a user has an affiliation to a course, only given
        its course code, and not its dataporten ID,
        
        .. code:: python
        
            # Already finished the course
            assert 'TMA4150' in request.user.dataporten.courses.finished
        
            # Currently enrolled in the course
            assert 'TMA4150' in request.user.dataporten.courses.active
        
            # Either
            assert 'TMA4150' in request.user.dataporten.courses.all
        
        
        More
        ----
        
        There is still lots of more undocumented (but well tested!) attributes of
        :code:`DataportenGroupsManager`. Take a look at :code:`dataporten/parsers.py`.
        Each parser has a class variable :code:`NAME`, and they are attached to
        the user as :code:`request.user.dataporten.NAME`.
        
        If you have a specific usecase, please open a GitHub issue, and I will
        document and/or implement it for you.
        
        Run tests
        =========
        
        .. code:: bash
        
            export DJANGO_SETTINGS_MODULE=dataporten.settings
            pytest
        
Platform: UNKNOWN
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 1.11
Classifier: Framework :: Django :: 2.0
Classifier: Framework :: Django :: 2.1
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware
