lms.backend.blackboard.backend
1# pylint: disable=abstract-method 2 3import typing 4 5import edq.net.request 6 7import lms.backend.blackboard.model 8import lms.model.backend 9import lms.model.constants 10import lms.model.courses 11import lms.model.users 12import lms.util.net 13import lms.util.parse 14 15class BlackboardBackend(lms.model.backend.APIBackend): 16 """ An API backend for the Blackboard Learn LMS. """ 17 18 def __init__(self, 19 server: str, 20 auth_user: typing.Union[str, None] = None, 21 auth_password: typing.Union[str, None] = None, 22 **kwargs: typing.Any) -> None: 23 super().__init__(server, lms.model.constants.BACKEND_TYPE_BLACKBOARD, **kwargs) 24 25 if (auth_user is None): 26 raise ValueError("Blackboard backends require a username.") 27 28 if (auth_password is None): 29 raise ValueError("Blackboard backends require a password.") 30 31 self._username = auth_user 32 """ The username to authenticate with. """ 33 34 self._password = auth_password 35 """ The password to authenticate with. """ 36 37 self._session_headers: typing.Union[typing.Dict[str, typing.Any], None] = None 38 """ The headers (e.g., cookies) for our logged in Blackboard session. """ 39 40 def _login(self) -> None: 41 """ Try to login to the Blackboard server. """ 42 43 # Check if we are already logged in. 44 if (self._session_headers is not None): 45 return 46 47 response, _ = edq.net.request.make_get(self.server) 48 cookies, router_params = self._parse_bb_cookies(response.headers.get('set-cookie', None)) 49 50 new_cookies = { 51 'BbRouter': cookies['bbrouter'] 52 } 53 54 text_cookies = '; '.join(['='.join(items) for items in new_cookies.items()]) 55 56 headers = { 57 'cookie': text_cookies, 58 } 59 data = { 60 'user_id': self._username, 61 'password': self._password, 62 'blackboard.platform.security.NonceUtil.nonce.ajax': router_params['xsrf'], 63 } 64 65 response, _ = edq.net.request.make_post(self.server + '/webapps/login/', 66 headers = headers, data = data, 67 # Don't store the nonce in exchanges. 68 params_to_skip = ['blackboard.platform.security.NonceUtil.nonce.ajax'], 69 allow_redirects = False) 70 71 cookies, router_params = self._parse_bb_cookies(response.headers.get('set-cookie', None)) 72 if ('sessionId' not in router_params): 73 raise ValueError(f"Could not log into Blackboard server ({self.server}) with user '{self._username}'. Is username/password correct?") 74 75 self._session_headers = { 76 'cookie': response.headers.get('set-cookie', None), 77 'x-blackboard-xsrf': router_params['xsrf'], 78 # Insert a header to identify the user. 79 'edq-lms-blackboard-user': self._username, 80 } 81 82 def _parse_bb_cookies(self, text_cookies: typing.Union[str, None]) -> typing.Tuple[typing.Dict[str, typing.Any], typing.Dict[str, str]]: 83 """ 84 Parse out the Blackboard cookies and return two dicts: 85 - All cookies (with lower case keys). 86 - The router params/cookies (specific to Blackboard). 87 """ 88 89 cookies: typing.Dict[str, typing.Any] = {} 90 router_params: typing.Dict[str, str] = {} 91 92 # If we are testing, return fake cookies. 93 if (self.is_testing()): 94 cookies['bbrouter'] = 'xsrf:abc,sessionId:9999999' 95 router_params['xsrf'] = 'abc' 96 router_params['sessionId'] = '9999999' 97 98 return cookies, router_params 99 100 cookies = lms.util.net.parse_cookies(text_cookies) 101 102 router_text = cookies.get('bbrouter', None) 103 if (isinstance(router_text, str)): 104 for text in router_text.split(','): 105 key, value = text.split(':', maxsplit = 1) 106 router_params[key] = value 107 108 return cookies, router_params 109 110 def courses_list(self, 111 **kwargs: typing.Any) -> typing.List[lms.model.courses.Course]: 112 self._login() 113 114 url = self.server + '/learn/api/public/v3/courses' 115 data = { 116 'availability.available': 'Yes', 117 } 118 response, _ = edq.net.request.make_get(url, headers = self._session_headers, data = data) 119 120 courses = [] 121 for raw_course in response.json().get('results', []): 122 courses.append(lms.backend.blackboard.model.course(raw_course)) 123 124 courses.sort() 125 126 return courses 127 128 def courses_users_list(self, 129 course_id: str, 130 **kwargs: typing.Any) -> typing.List[lms.model.users.CourseUser]: 131 parsed_course_id = lms.util.parse.required_int(course_id, 'course_id') 132 133 self._login() 134 135 url = self.server + '/learn/api/public/v1/courses/{course_id}/users' 136 url = url.format(course_id = lms.backend.blackboard.model.format_id(parsed_course_id)) 137 138 data = { 139 'expand': 'user', 140 } 141 142 response, _ = edq.net.request.make_get(url, headers = self._session_headers, data = data) 143 144 users = [] 145 for raw_user in response.json().get('results', []): 146 users.append(lms.backend.blackboard.model.course_user(raw_user)) 147 148 users.sort() 149 150 return users
16class BlackboardBackend(lms.model.backend.APIBackend): 17 """ An API backend for the Blackboard Learn LMS. """ 18 19 def __init__(self, 20 server: str, 21 auth_user: typing.Union[str, None] = None, 22 auth_password: typing.Union[str, None] = None, 23 **kwargs: typing.Any) -> None: 24 super().__init__(server, lms.model.constants.BACKEND_TYPE_BLACKBOARD, **kwargs) 25 26 if (auth_user is None): 27 raise ValueError("Blackboard backends require a username.") 28 29 if (auth_password is None): 30 raise ValueError("Blackboard backends require a password.") 31 32 self._username = auth_user 33 """ The username to authenticate with. """ 34 35 self._password = auth_password 36 """ The password to authenticate with. """ 37 38 self._session_headers: typing.Union[typing.Dict[str, typing.Any], None] = None 39 """ The headers (e.g., cookies) for our logged in Blackboard session. """ 40 41 def _login(self) -> None: 42 """ Try to login to the Blackboard server. """ 43 44 # Check if we are already logged in. 45 if (self._session_headers is not None): 46 return 47 48 response, _ = edq.net.request.make_get(self.server) 49 cookies, router_params = self._parse_bb_cookies(response.headers.get('set-cookie', None)) 50 51 new_cookies = { 52 'BbRouter': cookies['bbrouter'] 53 } 54 55 text_cookies = '; '.join(['='.join(items) for items in new_cookies.items()]) 56 57 headers = { 58 'cookie': text_cookies, 59 } 60 data = { 61 'user_id': self._username, 62 'password': self._password, 63 'blackboard.platform.security.NonceUtil.nonce.ajax': router_params['xsrf'], 64 } 65 66 response, _ = edq.net.request.make_post(self.server + '/webapps/login/', 67 headers = headers, data = data, 68 # Don't store the nonce in exchanges. 69 params_to_skip = ['blackboard.platform.security.NonceUtil.nonce.ajax'], 70 allow_redirects = False) 71 72 cookies, router_params = self._parse_bb_cookies(response.headers.get('set-cookie', None)) 73 if ('sessionId' not in router_params): 74 raise ValueError(f"Could not log into Blackboard server ({self.server}) with user '{self._username}'. Is username/password correct?") 75 76 self._session_headers = { 77 'cookie': response.headers.get('set-cookie', None), 78 'x-blackboard-xsrf': router_params['xsrf'], 79 # Insert a header to identify the user. 80 'edq-lms-blackboard-user': self._username, 81 } 82 83 def _parse_bb_cookies(self, text_cookies: typing.Union[str, None]) -> typing.Tuple[typing.Dict[str, typing.Any], typing.Dict[str, str]]: 84 """ 85 Parse out the Blackboard cookies and return two dicts: 86 - All cookies (with lower case keys). 87 - The router params/cookies (specific to Blackboard). 88 """ 89 90 cookies: typing.Dict[str, typing.Any] = {} 91 router_params: typing.Dict[str, str] = {} 92 93 # If we are testing, return fake cookies. 94 if (self.is_testing()): 95 cookies['bbrouter'] = 'xsrf:abc,sessionId:9999999' 96 router_params['xsrf'] = 'abc' 97 router_params['sessionId'] = '9999999' 98 99 return cookies, router_params 100 101 cookies = lms.util.net.parse_cookies(text_cookies) 102 103 router_text = cookies.get('bbrouter', None) 104 if (isinstance(router_text, str)): 105 for text in router_text.split(','): 106 key, value = text.split(':', maxsplit = 1) 107 router_params[key] = value 108 109 return cookies, router_params 110 111 def courses_list(self, 112 **kwargs: typing.Any) -> typing.List[lms.model.courses.Course]: 113 self._login() 114 115 url = self.server + '/learn/api/public/v3/courses' 116 data = { 117 'availability.available': 'Yes', 118 } 119 response, _ = edq.net.request.make_get(url, headers = self._session_headers, data = data) 120 121 courses = [] 122 for raw_course in response.json().get('results', []): 123 courses.append(lms.backend.blackboard.model.course(raw_course)) 124 125 courses.sort() 126 127 return courses 128 129 def courses_users_list(self, 130 course_id: str, 131 **kwargs: typing.Any) -> typing.List[lms.model.users.CourseUser]: 132 parsed_course_id = lms.util.parse.required_int(course_id, 'course_id') 133 134 self._login() 135 136 url = self.server + '/learn/api/public/v1/courses/{course_id}/users' 137 url = url.format(course_id = lms.backend.blackboard.model.format_id(parsed_course_id)) 138 139 data = { 140 'expand': 'user', 141 } 142 143 response, _ = edq.net.request.make_get(url, headers = self._session_headers, data = data) 144 145 users = [] 146 for raw_user in response.json().get('results', []): 147 users.append(lms.backend.blackboard.model.course_user(raw_user)) 148 149 users.sort() 150 151 return users
An API backend for the Blackboard Learn LMS.
BlackboardBackend( server: str, auth_user: Optional[str] = None, auth_password: Optional[str] = None, **kwargs: Any)
19 def __init__(self, 20 server: str, 21 auth_user: typing.Union[str, None] = None, 22 auth_password: typing.Union[str, None] = None, 23 **kwargs: typing.Any) -> None: 24 super().__init__(server, lms.model.constants.BACKEND_TYPE_BLACKBOARD, **kwargs) 25 26 if (auth_user is None): 27 raise ValueError("Blackboard backends require a username.") 28 29 if (auth_password is None): 30 raise ValueError("Blackboard backends require a password.") 31 32 self._username = auth_user 33 """ The username to authenticate with. """ 34 35 self._password = auth_password 36 """ The password to authenticate with. """ 37 38 self._session_headers: typing.Union[typing.Dict[str, typing.Any], None] = None 39 """ The headers (e.g., cookies) for our logged in Blackboard session. """
111 def courses_list(self, 112 **kwargs: typing.Any) -> typing.List[lms.model.courses.Course]: 113 self._login() 114 115 url = self.server + '/learn/api/public/v3/courses' 116 data = { 117 'availability.available': 'Yes', 118 } 119 response, _ = edq.net.request.make_get(url, headers = self._session_headers, data = data) 120 121 courses = [] 122 for raw_course in response.json().get('results', []): 123 courses.append(lms.backend.blackboard.model.course(raw_course)) 124 125 courses.sort() 126 127 return courses
List the courses associated with the context user.
129 def courses_users_list(self, 130 course_id: str, 131 **kwargs: typing.Any) -> typing.List[lms.model.users.CourseUser]: 132 parsed_course_id = lms.util.parse.required_int(course_id, 'course_id') 133 134 self._login() 135 136 url = self.server + '/learn/api/public/v1/courses/{course_id}/users' 137 url = url.format(course_id = lms.backend.blackboard.model.format_id(parsed_course_id)) 138 139 data = { 140 'expand': 'user', 141 } 142 143 response, _ = edq.net.request.make_get(url, headers = self._session_headers, data = data) 144 145 users = [] 146 for raw_user in response.json().get('results', []): 147 users.append(lms.backend.blackboard.model.course_user(raw_user)) 148 149 users.sort() 150 151 return users
List the users associated with the given course.
Inherited Members
- lms.model.backend.APIBackend
- server
- backend_type
- testing
- is_testing
- get_standard_headers
- not_found
- courses_get
- courses_fetch
- courses_assignments_get
- courses_assignments_fetch
- courses_assignments_list
- courses_assignments_resolve_and_list
- courses_assignments_scores_get
- courses_assignments_scores_fetch
- courses_assignments_scores_list
- courses_assignments_scores_resolve_and_list
- courses_assignments_scores_resolve_and_upload
- courses_assignments_scores_upload
- courses_gradebook_get
- courses_gradebook_fetch
- courses_gradebook_list
- courses_gradebook_resolve_and_list
- courses_gradebook_resolve_and_upload
- courses_gradebook_upload
- courses_groupsets_create
- courses_groupsets_resolve_and_create
- courses_groupsets_delete
- courses_groupsets_resolve_and_delete
- courses_groupsets_get
- courses_groupsets_fetch
- courses_groupsets_list
- courses_groupsets_resolve_and_list
- courses_groupsets_memberships_resolve_and_add
- courses_groupsets_memberships_resolve_and_set
- courses_groupsets_memberships_resolve_and_subtract
- courses_groupsets_memberships_list
- courses_groupsets_memberships_resolve_and_list
- courses_groups_create
- courses_groups_resolve_and_create
- courses_groups_delete
- courses_groups_resolve_and_delete
- courses_groups_get
- courses_groups_fetch
- courses_groups_list
- courses_groups_resolve_and_list
- courses_groups_memberships_add
- courses_groups_memberships_resolve_and_add
- courses_groups_memberships_list
- courses_groups_memberships_resolve_and_list
- courses_groups_memberships_resolve_and_set
- courses_groups_memberships_subtract
- courses_groups_memberships_resolve_and_subtract
- courses_syllabus_fetch
- courses_syllabus_get
- courses_users_get
- courses_users_fetch
- courses_users_resolve_and_list
- courses_users_scores_get
- courses_users_scores_fetch
- courses_users_scores_list
- courses_users_scores_resolve_and_list
- parse_assignment_query
- parse_assignment_queries
- parse_course_query
- parse_course_queries
- parse_groupset_query
- parse_groupset_queries
- parse_group_query
- parse_group_queries
- parse_user_query
- parse_user_queries
- resolve_assignment_query
- resolve_assignment_queries
- resolve_course_query
- resolve_course_queries
- resolve_group_queries
- resolve_group_query
- resolve_groupset_queries
- resolve_groupset_query
- resolve_user_queries