コード例 #1
0
def test_filter_categories(client: MoodleClient, setenv_category: t.Callable):
    '''
    Does client mix well categories filtering and filters keywords?
    '''

    setenv_category('1', '2')

    with patch.object(client, '_get_courses') as get_courses:

        courses = get_courses.return_value = [
            JsonDict(course_id='foo', category=1),
            JsonDict(course_id='bar', category=2),
            JsonDict(course_id='baz', category=5),
            JsonDict(course_id='spam', category=10),
        ]

        client.use_categories()

        client.load_courses()

        assert client.courses == courses[:2]

        client.courses = []

        client.load_courses(course_id='foo')

        assert client.courses == courses[:1]

        client.courses = []

        client.load_courses(course_id=('spam', 'baz'))

        assert client.courses == []
コード例 #2
0
ファイル: helper.py プロジェクト: antibagr/ncsu-jupyterhub
    def format_course(cls, course: JsonType) -> Course:
        '''Format raw json response to convinient dictionary.

        Args:
            course (JsonType): Raw Json from LMS containing course data.

        Returns:
            Course: JsonDict with course information

        '''

        return JsonDict({
            'id':
            course['id'],
            'course_id':
            cls.format_string(course['shortname']),
            'title':
            course['displayname'],
            'category':
            course['categoryid'],
            'need_nbgrader':
            course['categoryid'] == int(
                os.environ['MOODLE_NBGRADER_CATEGORY_ID']),
            'instructors': [],
            'students': [],
            'graders': [],
            'lms_lineitems_endpoint':
            f'{os.environ["MOODLE_BASE_URL"]}/mod/lti/services.php/{course["id"]}/lineitems'
        })
コード例 #3
0
ファイル: manager.py プロジェクト: antibagr/ncsu-jupyterhub
    def process_data(
        self,
        courses: t.Optional[Course] = None,
        json_path: t.Optional[PathLike] = None,
        **filters: Filters,
    ) -> None:
        '''Iterates through json file and calls self.process_course for
        every course found in a file.

        Args:
            json_path (t.Optional[PathLike]):
                Custom path to json. Defaults to None.
            filters (t.Dict[str, t.Union[t.Sequence[t.AnyStr], t.AnyStr]]):
                key-value pairs where value can be both single value or list
                of valid items.
        '''

        if courses is None:
            courses = (JsonDict(crs) for crs in load_moodle_courses(json_path))

        for course in courses:

            if self.helper.skip_course(course, filters):
                logger.debug(f'Skipping course {course.title!r}')
                continue

            logger.debug(f'Processing course {course.title!r}')

            self.courses.append(course)

            self.process_course(course)
コード例 #4
0
def test_filter_courses(client: MoodleClient):

    with patch.object(client, '_get_courses') as get_courses:

        courses = get_courses.return_value = [
            JsonDict(course_id='foo_course'),
            JsonDict(course_id='bar_course'),
        ]

        client.load_courses(course_id='foo_course')

        assert client.courses == courses[:1]

        # Filtering with multiple options

        courses = get_courses.return_value = [
            JsonDict(course_id='foo'),
            JsonDict(course_id='bar'),
            JsonDict(course_id='egg'),
            JsonDict(course_id='baz'),
        ]

        for seq in (list, tuple, set, frozenset):

            client.courses = []

            client.load_courses(course_id=seq(('foo', 'bar')))

            assert client.courses == courses[:2]

        # Filtering by multiple fields

        courses = get_courses.return_value = [
            JsonDict(title='The Foo', course_id='baz'),
            JsonDict(title='The Bar', course_id='baz'),
            JsonDict(title='The Spam', course_id='spam'),
        ]

        client.courses = []

        client.load_courses(title=('The Foo', 'The Spam'), course_id='baz')

        assert client.courses == [courses[0]]

        # Empty sequence is not allowed
        with pytest.raises(ValueError):
            client.load_courses(title=(), course_id='baz')

        # Invalid key for the course
        with pytest.raises(KeyError):
            client.load_courses(foo='bar')
コード例 #5
0
def test_get_categories(client: MoodleClient):
    '''
    Does get categories returns courses in valid format?
    '''

    courses = [
        JsonDict(title='Foo Course', course_id='foo_course', category=1)
    ]

    with patch.object(client, '_get_courses', return_value=[]) as get_course:

        assert client.get_categories() == []

        get_course.return_value = courses

        assert client.get_categories() == [('Foo Course', 'foo_course', 1)]
コード例 #6
0
ファイル: conftest.py プロジェクト: antibagr/ncsu-jupyterhub
    def _course_fabric(
        *,
        id: t.Optional[int] = None,
        title: t.Optional[str] = None,
        course_id: t.Optional[str] = None,
        category: t.Optional[int] = None,
        instructors: t.Optional[t.List[User]] = None,
        students: t.Optional[t.List[User]] = None,
        graders: t.Optional[t.List[User]] = None,
    ) -> Course:
        '''
        Create new course. Keyword-only arguments allowed.
        '''

        if id and not isinstance(id, int):
            raise TypeError('User id must be int.')

        if course_id and re.findall(r'\W+', course_id):
            raise ValueError('Short name contains invalid characters: %s' %
                             course_id)

        # Python does strange things
        # with lists in default values ...
        instructors = instructors or []
        students = students or []
        graders = graders or []

        for user in instructors + students + graders:
            assert tuple(user.keys()) == ('id', 'username', 'email',
                                          'first_name', 'last_name', 'roles')

        return JsonDict({
            'id':
            id or random.randint(1, 100),
            'title':
            title or random_string(20),
            'course_id':
            course_id or random_string(20),
            'category':
            category or random.randint(0, 100),
            'instructors': []
            or [user_fabric() for _ in range(random.randint(0, 5))],
            'students': []
            or [user_fabric() for _ in range(random.randint(0, 5))],
            'graders': []
            or [user_fabric() for _ in range(random.randint(0, 5))],
        })
コード例 #7
0
ファイル: conftest.py プロジェクト: antibagr/ncsu-jupyterhub
    def _user_fabric(
        *,
        id: t.Optional[int] = None,
        username: t.Optional[str] = None,
        email: t.Optional[str] = None,
        first_name: t.Optional[str] = None,
        last_name: t.Optional[str] = None,
        roles: t.Optional[t.List[Role]] = None,
    ) -> User:
        '''
        Create new user. Keyword-only arguments allowed.
        '''

        if id and not isinstance(id, int):
            raise TypeError('User id must be int.')

        if email and not valid_email(email):
            raise ValueError('Invalid email: %s' % email)

        if username and re.findall(r'\W+', username):
            raise ValueError('Username contains invalid characters: %s' %
                             username)

        if roles:
            for role in roles:
                if role not in ROLES:
                    raise ValueError(
                        'Role [%s] does not set in moodle.settings.ROLES tuple.'
                        % role)

        return JsonDict({
            'id':
            id or random.randint(0, 100),
            'username':
            username or random_string(10),
            'email':
            email or f'{random_string(10)}@mail.com',
            'first_name':
            first_name or random_string(10),
            'last_name':
            last_name or random_string(10),
            'roles':
            roles
            or [random.choice(ROLES) for _ in range(random.randint(0, 5))],
        })
コード例 #8
0
ファイル: helper.py プロジェクト: antibagr/ncsu-jupyterhub
    def format_user(cls, user: JsonType) -> User:
        '''Format raw json response to convinient dictionary.

        Args:
            user (JsonType): Raw Json from LMS containing user data.

        Returns:
            User: JsonDict with user information

        '''

        return JsonDict({
            'id': user['id'],
            'first_name': user['firstname'],
            'last_name': user['lastname'],
            'username': cls.format_string(user['username']),
            'email': user['email'],
            'roles': [role['shortname'] for role in user['roles']],
        })
コード例 #9
0
def test_load_users(client: MoodleClient, student: User, teacher: User,
                    course: Course):
    '''
    Test transforming data process.
    '''

    # reset groups
    course['graders'] = []
    course['instructors'] = []
    course['students'] = []

    client.courses = [
        course,
    ]

    # we have teacher and two repeated students.
    with patch.object(client, '_get_users') as mocked_get_users:

        mocked_get_users.return_value = [
            JsonDict(u) for u in (student, student, teacher)
        ]

        client.load_users()

        # do the same work client does with users
        # to compare data
        student.role = student.pop('roles')[0]
        teacher.role = teacher.pop('roles')[0]

        mocked_get_users.assert_called_once_with(course)

        assert client.courses == [{
            **course, 'graders': [teacher],
            'students': [student, student]
        }]

        assert client.users == {
            teacher.username: teacher,
            student.username: student
        }
コード例 #10
0
    def create_service(course_id: str,
                       api_token: str,
                       port: int = 0) -> JsonDict:
        '''
        Fills service template with provided data.

        Args:
            course_id (str): Normalized course's name.
            api_token (str): Jupyterhub Service API token.
            port (int): Port to run service on. Defaults to 0.

        Returns:
            JsonDict: Dict that you can add to services in jupyterhub_config.py
        '''

        return JsonDict({
            'name':
            course_id,
            'admin':
            True,
            'url':
            f'http://127.0.0.1:{9000 + port}',
            'command': [
                'jupyterhub-singleuser',
                f'--group=formgrade-{course_id}',
                '--debug',
                '--allow-root',
            ],
            'user':
            f'grader-{course_id}',
            'cwd':
            f'/home/grader-{course_id}',
            'api_token':
            api_token,
            'environment': {
                'JUPYTERHUB_SERVICE_USER': f'grader-{course_id}'
            }
        })
コード例 #11
0
ファイル: manager.py プロジェクト: antibagr/ncsu-jupyterhub
 def _format(users: JsonType) -> t.List[User]:
     return [JsonDict(user) for user in users]