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

Get an instance of an API backend from the given information. If the backend type is not explicitly provided, this function will attempt to guess it from other information.

def guess_backend_type( server: Optional[str] = None, backend_type: Optional[str] = None, **kwargs: Any) -> Optional[str]:
48def guess_backend_type(
49        server: typing.Union[str, None] = None,
50        backend_type: typing.Union[str, None] = None,
51        **kwargs: typing.Any) -> typing.Union[str, None]:
52    """
53    Attempt to guess the backend type from a server.
54    This function will return None it cannot guess the backend type.
55    """
56
57    if (backend_type is not None):
58        return backend_type
59
60    if (server is None):
61        return None
62
63    # Try looking at the URL string itself.
64    backend_type = guess_backend_type_from_url(server)
65    if (backend_type is not None):
66        return backend_type
67
68    # Make a request to the server and examine the response.
69    backend_type = guess_backend_type_from_request(server)
70    if (backend_type is not None):
71        return backend_type
72
73    return None

Attempt to guess the backend type from a server. This function will return None it cannot guess the backend type.

def guess_backend_type_from_request(server: str, timeout_secs: float = 10.0) -> Optional[str]:
 75def guess_backend_type_from_request(server: str, timeout_secs: float = edq.net.request.DEFAULT_REQUEST_TIMEOUT_SECS) -> typing.Union[str, None]:
 76    """
 77    Attempt to guess the backend type by pinging the server.
 78    This function will not do any lexical analysis on the server string.
 79    """
 80
 81    options = {
 82        'allow_redirects': False,
 83    }
 84
 85    try:
 86        response, _ = edq.net.request.make_get(server,
 87                raise_for_status = False,
 88                timeout_secs = timeout_secs,
 89                additional_requests_options = options)
 90    except requests.exceptions.ConnectionError:
 91        return None
 92    except requests.exceptions.Timeout:
 93        return None
 94
 95    header_keys = [key.lower() for key in response.headers.keys()]
 96
 97    # Blackboard sends a special header.
 98    if ('x-blackboard-product' in header_keys):
 99        return lms.model.constants.BACKEND_TYPE_BLACKBOARD
100
101    # Canvas sends a special header.
102    if ('x-canvas-meta' in header_keys):
103        return lms.model.constants.BACKEND_TYPE_CANVAS
104
105    # Canvas requests that a specific cookie is set.
106    if ('_normandy_session' in response.headers.get('set-cookie', '')):
107        return lms.model.constants.BACKEND_TYPE_CANVAS
108
109    # Moodle will try to redirect with a special header.
110    if (response.headers.get('x-redirect-by', '').lower() == 'moodle'):
111        return lms.model.constants.BACKEND_TYPE_MOODLE
112
113    # Moodle requests that a specific cookie is set.
114    if ('MoodleSession' in response.headers.get('set-cookie', '')):
115        return lms.model.constants.BACKEND_TYPE_MOODLE
116
117    return None

Attempt to guess the backend type by pinging the server. This function will not do any lexical analysis on the server string.

def guess_backend_type_from_url(server: str) -> Optional[str]:
119def guess_backend_type_from_url(server: str) -> typing.Union[str, None]:
120    """
121    Attempt to guess the backend type only from a string server URL.
122    This function will only do lexical analysis on the string (no HTTP requests will be made).
123    """
124
125    server = server.lower().strip()
126
127    if ('canvas' in server):
128        return lms.model.constants.BACKEND_TYPE_CANVAS
129
130    if ('moodle' in server):
131        return lms.model.constants.BACKEND_TYPE_MOODLE
132
133    if ('blackboard' in server):
134        return lms.model.constants.BACKEND_TYPE_BLACKBOARD
135
136    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).