Skip to content

Commit

Permalink
refactor: Remove dependencies from edx-platform in extracted LTI block
Browse files Browse the repository at this point in the history
- Defined constants for anonymous user ID and user role.
- Replaced HTML/Text helpers with markupsafe.
  • Loading branch information
ttqureshi committed Nov 18, 2024
1 parent 1f00bf5 commit db93aa9
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 31 deletions.
55 changes: 27 additions & 28 deletions xblocks_contrib/lti/lti.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import datetime
import hashlib
import logging
import markupsafe
import textwrap
from xml.sax.saxutils import escape
from unittest import mock
Expand All @@ -81,13 +82,12 @@
from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin

from openedx.core.djangolib.markup import HTML, Text
from .lti_2_util import LTI20BlockMixin, LTIError

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

resource_loader = ResourceLoader(__name__)

Expand Down Expand Up @@ -228,47 +228,47 @@ class LTIBlock(

lti_id = String(
display_name=_("LTI ID"),
help=Text(_(
help=markupsafe.escape(_(
"Enter the LTI ID for the external LTI provider. "
"This value must be the same LTI ID that you entered in the "
"LTI Passports setting on the Advanced Settings page."
"{break_tag}See {docs_anchor_open}the edX LTI documentation{anchor_close} for more details on this setting."
)).format(
break_tag=HTML(BREAK_TAG),
docs_anchor_open=HTML(DOCS_ANCHOR_TAG_OPEN),
anchor_close=HTML("</a>")
break_tag=markupsafe.Markup(BREAK_TAG),
docs_anchor_open=markupsafe.Markup(DOCS_ANCHOR_TAG_OPEN),
anchor_close=markupsafe.Markup("</a>")
),
default='',
scope=Scope.settings
)

launch_url = String(
display_name=_("LTI URL"),
help=Text(_(
help=markupsafe.escape(_(
"Enter the URL of the external tool that this component launches. "
"This setting is only used when Hide External Tool is set to False."
"{break_tag}See {docs_anchor_open}the edX LTI documentation{anchor_close} for more details on this setting."
)).format(
break_tag=HTML(BREAK_TAG),
docs_anchor_open=HTML(DOCS_ANCHOR_TAG_OPEN),
anchor_close=HTML("</a>")
break_tag=markupsafe.Markup(BREAK_TAG),
docs_anchor_open=markupsafe.Markup(DOCS_ANCHOR_TAG_OPEN),
anchor_close=markupsafe.Markup("</a>")
),
default='http://www.example.com',
scope=Scope.settings)

custom_parameters = List(
display_name=_("Custom Parameters"),
help=Text(_(
help=markupsafe.escape(_(
"Add the key/value pair for any custom parameters, such as the page your e-book should open to or "
"the background color for this component."
"{break_tag}See {docs_anchor_open}the edX LTI documentation{anchor_close} for more details on this setting."
)).format(
break_tag=HTML(BREAK_TAG),
docs_anchor_open=HTML(DOCS_ANCHOR_TAG_OPEN),
anchor_close=HTML("</a>")
break_tag=markupsafe.Markup(BREAK_TAG),
docs_anchor_open=markupsafe.Markup(DOCS_ANCHOR_TAG_OPEN),
anchor_close=markupsafe.Markup("</a>")
),
scope=Scope.settings)

open_in_a_new_page = Boolean(
display_name=_("Open in New Page"),
help=_(
Expand Down Expand Up @@ -333,7 +333,7 @@ class LTIBlock(
default=False,
scope=Scope.settings
)

ask_to_send_email = Boolean(
display_name=_("Request user's email"),
# Translators: This is used to request the user's email for a third party service.
Expand Down Expand Up @@ -367,7 +367,7 @@ class LTIBlock(
default=True,
scope=Scope.settings
)

editable_fields = (
"accept_grades_past_due", "button_text", "custom_parameters", "display_name",
"hide_launch", "description", "lti_id", "launch_url", "open_in_a_new_page",
Expand Down Expand Up @@ -959,12 +959,12 @@ def verify_oauth_body_sign(self, request, content_type='application/x-www-form-u

if (not signature.verify_hmac_sha1(mock_request_lti_1, client_secret) and not
signature.verify_hmac_sha1(mock_request_lti_2, client_secret)):
log.error("OAuth signature verification failed, for "
"headers:{} url:{} method:{}".format(
oauth_headers,
self.get_outcome_service_url(),
str(request.method)
))
log.error(
"OAuth signature verification failed, for "
"headers:{} url:{} method:{}".format(
oauth_headers, self.get_outcome_service_url(), str(request.method)
)
)
raise LTIError("OAuth signature verification has failed.")

def get_client_key_secret(self):
Expand Down Expand Up @@ -996,4 +996,3 @@ def is_past_due(self):
else:
close_date = due_date
return close_date is not None and datetime.datetime.now(UTC) > close_date

25 changes: 22 additions & 3 deletions xblocks_contrib/lti/lti_2_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import hashlib
import json
import logging
import math
import re
from unittest import mock
from urllib import parse
Expand All @@ -17,8 +18,6 @@
from webob import Response
from xblock.core import XBlock

from openedx.core.lib.grade_utils import round_away_from_zero

log = logging.getLogger(__name__)

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

def _round_away_from_zero(number, digits=0):
"""
Round numbers using the 'away from zero' strategy as opposed to the
'Banker's rounding strategy.' The strategy refers to how we round when
a number is half way between two numbers. eg. 0.5, 1.5, etc. In python 3
numbers round towards even. So 0.5 would round to 0 but 1.5 would round to 2.
See here for more on floating point rounding strategies:
https://en.wikipedia.org/wiki/IEEE_754#Rounding_rules
We want to continue to round away from zero so that student grades remain
consistent and don't suddenly change.
"""
p = 10.0 ** digits

if number >= 0:
return float(math.floor((number * p) + 0.5)) / p
else:
return float(math.ceil((number * p) - 0.5)) / p

def _lti_2_0_result_get_handler(self, request, real_user):
"""
Helper request handler for GET requests to LTI 2.0 result endpoint
Expand All @@ -176,7 +195,7 @@ def _lti_2_0_result_get_handler(self, request, real_user):
return Response(json.dumps(base_json_obj).encode('utf-8'), content_type=LTI_2_0_JSON_CONTENT_TYPE)

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

Expand Down

0 comments on commit db93aa9

Please sign in to comment.