Skip to content

Commit db93aa9

Browse files
committed
refactor: Remove dependencies from edx-platform in extracted LTI block
- Defined constants for anonymous user ID and user role. - Replaced HTML/Text helpers with markupsafe.
1 parent 1f00bf5 commit db93aa9

File tree

2 files changed

+49
-31
lines changed

2 files changed

+49
-31
lines changed

xblocks_contrib/lti/lti.py

+27-28
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import datetime
6060
import hashlib
6161
import logging
62+
import markupsafe
6263
import textwrap
6364
from xml.sax.saxutils import escape
6465
from unittest import mock
@@ -81,13 +82,12 @@
8182
from xblockutils.resources import ResourceLoader
8283
from xblockutils.studio_editable import StudioEditableXBlockMixin
8384

84-
from openedx.core.djangolib.markup import HTML, Text
8585
from .lti_2_util import LTI20BlockMixin, LTIError
8686

87-
from common.djangoapps.xblock_django.constants import (
88-
ATTR_KEY_ANONYMOUS_USER_ID,
89-
ATTR_KEY_USER_ROLE,
90-
)
87+
# The anonymous user ID for the user in the course.
88+
ATTR_KEY_ANONYMOUS_USER_ID = 'edx-platform.anonymous_user_id'
89+
# The user's role in the course ('staff', 'instructor', or 'student').
90+
ATTR_KEY_USER_ROLE = 'edx-platform.user_role'
9191

9292
resource_loader = ResourceLoader(__name__)
9393

@@ -228,47 +228,47 @@ class LTIBlock(
228228

229229
lti_id = String(
230230
display_name=_("LTI ID"),
231-
help=Text(_(
231+
help=markupsafe.escape(_(
232232
"Enter the LTI ID for the external LTI provider. "
233233
"This value must be the same LTI ID that you entered in the "
234234
"LTI Passports setting on the Advanced Settings page."
235235
"{break_tag}See {docs_anchor_open}the edX LTI documentation{anchor_close} for more details on this setting."
236236
)).format(
237-
break_tag=HTML(BREAK_TAG),
238-
docs_anchor_open=HTML(DOCS_ANCHOR_TAG_OPEN),
239-
anchor_close=HTML("</a>")
237+
break_tag=markupsafe.Markup(BREAK_TAG),
238+
docs_anchor_open=markupsafe.Markup(DOCS_ANCHOR_TAG_OPEN),
239+
anchor_close=markupsafe.Markup("</a>")
240240
),
241241
default='',
242242
scope=Scope.settings
243243
)
244244

245245
launch_url = String(
246246
display_name=_("LTI URL"),
247-
help=Text(_(
247+
help=markupsafe.escape(_(
248248
"Enter the URL of the external tool that this component launches. "
249249
"This setting is only used when Hide External Tool is set to False."
250250
"{break_tag}See {docs_anchor_open}the edX LTI documentation{anchor_close} for more details on this setting."
251251
)).format(
252-
break_tag=HTML(BREAK_TAG),
253-
docs_anchor_open=HTML(DOCS_ANCHOR_TAG_OPEN),
254-
anchor_close=HTML("</a>")
252+
break_tag=markupsafe.Markup(BREAK_TAG),
253+
docs_anchor_open=markupsafe.Markup(DOCS_ANCHOR_TAG_OPEN),
254+
anchor_close=markupsafe.Markup("</a>")
255255
),
256256
default='http://www.example.com',
257257
scope=Scope.settings)
258-
258+
259259
custom_parameters = List(
260260
display_name=_("Custom Parameters"),
261-
help=Text(_(
261+
help=markupsafe.escape(_(
262262
"Add the key/value pair for any custom parameters, such as the page your e-book should open to or "
263263
"the background color for this component."
264264
"{break_tag}See {docs_anchor_open}the edX LTI documentation{anchor_close} for more details on this setting."
265265
)).format(
266-
break_tag=HTML(BREAK_TAG),
267-
docs_anchor_open=HTML(DOCS_ANCHOR_TAG_OPEN),
268-
anchor_close=HTML("</a>")
266+
break_tag=markupsafe.Markup(BREAK_TAG),
267+
docs_anchor_open=markupsafe.Markup(DOCS_ANCHOR_TAG_OPEN),
268+
anchor_close=markupsafe.Markup("</a>")
269269
),
270270
scope=Scope.settings)
271-
271+
272272
open_in_a_new_page = Boolean(
273273
display_name=_("Open in New Page"),
274274
help=_(
@@ -333,7 +333,7 @@ class LTIBlock(
333333
default=False,
334334
scope=Scope.settings
335335
)
336-
336+
337337
ask_to_send_email = Boolean(
338338
display_name=_("Request user's email"),
339339
# Translators: This is used to request the user's email for a third party service.
@@ -367,7 +367,7 @@ class LTIBlock(
367367
default=True,
368368
scope=Scope.settings
369369
)
370-
370+
371371
editable_fields = (
372372
"accept_grades_past_due", "button_text", "custom_parameters", "display_name",
373373
"hide_launch", "description", "lti_id", "launch_url", "open_in_a_new_page",
@@ -959,12 +959,12 @@ def verify_oauth_body_sign(self, request, content_type='application/x-www-form-u
959959

960960
if (not signature.verify_hmac_sha1(mock_request_lti_1, client_secret) and not
961961
signature.verify_hmac_sha1(mock_request_lti_2, client_secret)):
962-
log.error("OAuth signature verification failed, for "
963-
"headers:{} url:{} method:{}".format(
964-
oauth_headers,
965-
self.get_outcome_service_url(),
966-
str(request.method)
967-
))
962+
log.error(
963+
"OAuth signature verification failed, for "
964+
"headers:{} url:{} method:{}".format(
965+
oauth_headers, self.get_outcome_service_url(), str(request.method)
966+
)
967+
)
968968
raise LTIError("OAuth signature verification has failed.")
969969

970970
def get_client_key_secret(self):
@@ -996,4 +996,3 @@ def is_past_due(self):
996996
else:
997997
close_date = due_date
998998
return close_date is not None and datetime.datetime.now(UTC) > close_date
999-

xblocks_contrib/lti/lti_2_util.py

+22-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import hashlib
99
import json
1010
import logging
11+
import math
1112
import re
1213
from unittest import mock
1314
from urllib import parse
@@ -17,8 +18,6 @@
1718
from webob import Response
1819
from xblock.core import XBlock
1920

20-
from openedx.core.lib.grade_utils import round_away_from_zero
21-
2221
log = logging.getLogger(__name__)
2322

2423
LTI_2_0_REST_SUFFIX_PARSER = re.compile(r"^user/(?P<anon_id>\w+)", re.UNICODE)
@@ -154,6 +153,26 @@ def parse_lti_2_0_handler_suffix(self, suffix):
154153
log.info(f"[LTI]: {msg}")
155154
raise LTIError(msg)
156155

156+
def _round_away_from_zero(number, digits=0):
157+
"""
158+
Round numbers using the 'away from zero' strategy as opposed to the
159+
'Banker's rounding strategy.' The strategy refers to how we round when
160+
a number is half way between two numbers. eg. 0.5, 1.5, etc. In python 3
161+
numbers round towards even. So 0.5 would round to 0 but 1.5 would round to 2.
162+
163+
See here for more on floating point rounding strategies:
164+
https://en.wikipedia.org/wiki/IEEE_754#Rounding_rules
165+
166+
We want to continue to round away from zero so that student grades remain
167+
consistent and don't suddenly change.
168+
"""
169+
p = 10.0 ** digits
170+
171+
if number >= 0:
172+
return float(math.floor((number * p) + 0.5)) / p
173+
else:
174+
return float(math.ceil((number * p) - 0.5)) / p
175+
157176
def _lti_2_0_result_get_handler(self, request, real_user):
158177
"""
159178
Helper request handler for GET requests to LTI 2.0 result endpoint
@@ -176,7 +195,7 @@ def _lti_2_0_result_get_handler(self, request, real_user):
176195
return Response(json.dumps(base_json_obj).encode('utf-8'), content_type=LTI_2_0_JSON_CONTENT_TYPE)
177196

178197
# Fall through to returning grade and comment
179-
base_json_obj['resultScore'] = round_away_from_zero(self.module_score, 2)
198+
base_json_obj['resultScore'] = self._round_away_from_zero(self.module_score, 2)
180199
base_json_obj['comment'] = self.score_comment
181200
return Response(json.dumps(base_json_obj).encode('utf-8'), content_type=LTI_2_0_JSON_CONTENT_TYPE)
182201

0 commit comments

Comments
 (0)