lms.backend.instance
1import typing 2 3import edq.net.request 4import requests 5 6import lms.backend.blackboard.backend 7import lms.backend.canvas.backend 8import lms.backend.moodle.backend 9import lms.model.config 10import lms.model.constants 11import lms.model.backend 12 13def get_backend( 14 config: lms.model.config.Config, 15 **kwargs: typing.Any) -> lms.model.backend.APIBackend: 16 """ 17 Get an instance of an API backend from the given config information. 18 If the backend type is not explicitly provided, 19 this function will attempt to guess it from other information. 20 21 This function may modify the config and will pass ownership to the backend instance. 22 """ 23 24 if (config.server is None): 25 raise ValueError("No LMS server address provided.") 26 27 config.server = config.server.strip() 28 if (not config.server.startswith('http')): 29 config.server = 'http://' + config.server 30 31 guess_backend_type(config) 32 if (config.backend_type is None): 33 raise ValueError(f"Unable to guess backend type from server: '{config.server}'.") 34 35 if (config.backend_type == lms.model.constants.BackendType.CANVAS): 36 return lms.backend.canvas.backend.CanvasBackend(config = config, **kwargs) 37 elif (config.backend_type == lms.model.constants.BackendType.MOODLE): 38 return lms.backend.moodle.backend.MoodleBackend(config = config, **kwargs) 39 elif (config.backend_type == lms.model.constants.BackendType.BLACKBOARD): 40 return lms.backend.blackboard.backend.BlackboardBackend(config = config, **kwargs) 41 elif (config.backend_type not in lms.model.constants.BackendType): 42 raise ValueError(f"Instance creation not yet supported for backend type: '{config.backend_type.value}'.") 43 else: 44 raise ValueError((f"Unknown backend type: '{config.backend_type.value}'.", 45 + f" Known backend types: {[choice.value for choice in lms.model.constants.OutputFormat]}.")) 46 47def guess_backend_type( 48 config: lms.model.config.Config, 49 **kwargs: typing.Any) -> None: 50 """ 51 Attempt to guess the backend type from a server. 52 The result of the guess (which may be None) will be placed in the passed-in config. 53 """ 54 55 if (config.backend_type is not None): 56 return 57 58 if (config.server is None): 59 return 60 61 # Try looking at the URL string itself. 62 config.backend_type = guess_backend_type_from_url(config.server) 63 if (config.backend_type is not None): 64 return 65 66 # Finally, make a request to the server and examine the response. 67 config.backend_type = guess_backend_type_from_request(config.server) 68 69def guess_backend_type_from_request( 70 server: str, 71 timeout_secs: typing.Union[float, None] = None, 72 ) -> typing.Union[lms.model.constants.BackendType, None]: 73 """ 74 Attempt to guess the backend type by pinging the server. 75 This function will not do any lexical analysis on the server string. 76 """ 77 78 options = { 79 'allow_redirects': False, 80 } 81 82 try: 83 response, _ = edq.net.request.make_get(server, 84 raise_for_status = False, 85 timeout_secs = timeout_secs, 86 additional_requests_options = options) 87 except requests.exceptions.ConnectionError: 88 return None 89 except requests.exceptions.Timeout: 90 return None 91 92 header_keys = [key.lower() for key in response.headers.keys()] 93 94 # Blackboard sends a special header. 95 if ('x-blackboard-product' in header_keys): 96 return lms.model.constants.BackendType.BLACKBOARD 97 98 # Canvas sends a special header. 99 if ('x-canvas-meta' in header_keys): 100 return lms.model.constants.BackendType.CANVAS 101 102 # Canvas requests that a specific cookie is set. 103 if ('_normandy_session' in response.headers.get('set-cookie', '')): 104 return lms.model.constants.BackendType.CANVAS 105 106 # Moodle will try to redirect with a special header. 107 if (response.headers.get('x-redirect-by', '').lower() == 'moodle'): 108 return lms.model.constants.BackendType.MOODLE 109 110 # Moodle requests that a specific cookie is set. 111 if ('MoodleSession' in response.headers.get('set-cookie', '')): 112 return lms.model.constants.BackendType.MOODLE 113 114 return None 115 116def guess_backend_type_from_url(server: str) -> typing.Union[lms.model.constants.BackendType, None]: 117 """ 118 Attempt to guess the backend type only from a string server URL. 119 This function will only do lexical analysis on the string (no HTTP requests will be made). 120 """ 121 122 server = server.lower().strip() 123 124 if ('canvas' in server): 125 return lms.model.constants.BackendType.CANVAS 126 127 if ('moodle' in server): 128 return lms.model.constants.BackendType.MOODLE 129 130 if ('blackboard' in server): 131 return lms.model.constants.BackendType.BLACKBOARD 132 133 return None
14def get_backend( 15 config: lms.model.config.Config, 16 **kwargs: typing.Any) -> lms.model.backend.APIBackend: 17 """ 18 Get an instance of an API backend from the given config information. 19 If the backend type is not explicitly provided, 20 this function will attempt to guess it from other information. 21 22 This function may modify the config and will pass ownership to the backend instance. 23 """ 24 25 if (config.server is None): 26 raise ValueError("No LMS server address provided.") 27 28 config.server = config.server.strip() 29 if (not config.server.startswith('http')): 30 config.server = 'http://' + config.server 31 32 guess_backend_type(config) 33 if (config.backend_type is None): 34 raise ValueError(f"Unable to guess backend type from server: '{config.server}'.") 35 36 if (config.backend_type == lms.model.constants.BackendType.CANVAS): 37 return lms.backend.canvas.backend.CanvasBackend(config = config, **kwargs) 38 elif (config.backend_type == lms.model.constants.BackendType.MOODLE): 39 return lms.backend.moodle.backend.MoodleBackend(config = config, **kwargs) 40 elif (config.backend_type == lms.model.constants.BackendType.BLACKBOARD): 41 return lms.backend.blackboard.backend.BlackboardBackend(config = config, **kwargs) 42 elif (config.backend_type not in lms.model.constants.BackendType): 43 raise ValueError(f"Instance creation not yet supported for backend type: '{config.backend_type.value}'.") 44 else: 45 raise ValueError((f"Unknown backend type: '{config.backend_type.value}'.", 46 + f" Known backend types: {[choice.value for choice in lms.model.constants.OutputFormat]}."))
Get an instance of an API backend from the given config information. If the backend type is not explicitly provided, this function will attempt to guess it from other information.
This function may modify the config and will pass ownership to the backend instance.
48def guess_backend_type( 49 config: lms.model.config.Config, 50 **kwargs: typing.Any) -> None: 51 """ 52 Attempt to guess the backend type from a server. 53 The result of the guess (which may be None) will be placed in the passed-in config. 54 """ 55 56 if (config.backend_type is not None): 57 return 58 59 if (config.server is None): 60 return 61 62 # Try looking at the URL string itself. 63 config.backend_type = guess_backend_type_from_url(config.server) 64 if (config.backend_type is not None): 65 return 66 67 # Finally, make a request to the server and examine the response. 68 config.backend_type = guess_backend_type_from_request(config.server)
Attempt to guess the backend type from a server. The result of the guess (which may be None) will be placed in the passed-in config.
def
guess_backend_type_from_request( server: str, timeout_secs: Optional[float] = None) -> Optional[lms.model.constants.BackendType]:
70def guess_backend_type_from_request( 71 server: str, 72 timeout_secs: typing.Union[float, None] = None, 73 ) -> typing.Union[lms.model.constants.BackendType, None]: 74 """ 75 Attempt to guess the backend type by pinging the server. 76 This function will not do any lexical analysis on the server string. 77 """ 78 79 options = { 80 'allow_redirects': False, 81 } 82 83 try: 84 response, _ = edq.net.request.make_get(server, 85 raise_for_status = False, 86 timeout_secs = timeout_secs, 87 additional_requests_options = options) 88 except requests.exceptions.ConnectionError: 89 return None 90 except requests.exceptions.Timeout: 91 return None 92 93 header_keys = [key.lower() for key in response.headers.keys()] 94 95 # Blackboard sends a special header. 96 if ('x-blackboard-product' in header_keys): 97 return lms.model.constants.BackendType.BLACKBOARD 98 99 # Canvas sends a special header. 100 if ('x-canvas-meta' in header_keys): 101 return lms.model.constants.BackendType.CANVAS 102 103 # Canvas requests that a specific cookie is set. 104 if ('_normandy_session' in response.headers.get('set-cookie', '')): 105 return lms.model.constants.BackendType.CANVAS 106 107 # Moodle will try to redirect with a special header. 108 if (response.headers.get('x-redirect-by', '').lower() == 'moodle'): 109 return lms.model.constants.BackendType.MOODLE 110 111 # Moodle requests that a specific cookie is set. 112 if ('MoodleSession' in response.headers.get('set-cookie', '')): 113 return lms.model.constants.BackendType.MOODLE 114 115 return None
Attempt to guess the backend type by pinging the server. This function will not do any lexical analysis on the server string.
117def guess_backend_type_from_url(server: str) -> typing.Union[lms.model.constants.BackendType, None]: 118 """ 119 Attempt to guess the backend type only from a string server URL. 120 This function will only do lexical analysis on the string (no HTTP requests will be made). 121 """ 122 123 server = server.lower().strip() 124 125 if ('canvas' in server): 126 return lms.model.constants.BackendType.CANVAS 127 128 if ('moodle' in server): 129 return lms.model.constants.BackendType.MOODLE 130 131 if ('blackboard' in server): 132 return lms.model.constants.BackendType.BLACKBOARD 133 134 return None
Attempt to guess the backend type only from a string server URL. This function will only do lexical analysis on the string (no HTTP requests will be made).