Skip to content
This repository was archived by the owner on Sep 5, 2023. It is now read-only.

Commit

Permalink
feat: Automated assignment creation with LTI11 (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
netoisc authored Aug 7, 2020
1 parent 6b769be commit beadb21
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 102 deletions.
2 changes: 1 addition & 1 deletion src/illumidesk/apis/nbgrader_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,5 @@ def create_assignment_in_nbgrader(self, assignment_name: str, **kwargs: dict) ->
shutil.chown(str(Path(assignment_dir).parent), user=self.uid, group=self.gid)
shutil.chown(str(assignment_dir), user=self.uid, group=self.gid)
except InvalidEntry as e:
logger.debug('Error during adding assignment to gradebook: %s' % e)
logger.debug('Error ocurred by adding assignment to gradebook: %s' % e)
return assignment
55 changes: 33 additions & 22 deletions src/illumidesk/authenticators/authenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async def setup_course_hook(
username = lti_utils.normalize_string(authentication['name'])
lms_user_id = authentication['auth_state']['lms_user_id']
user_role = authentication['auth_state']['user_role']
# register the user (it doesn't matter if is a student or instructor) with her/his lms_user_id in nbgrader
# register the user (it doesn't matter if it is a student or instructor) with her/his lms_user_id in nbgrader
await jupyterhub_api.add_user_to_nbgrader_gradebook(course_id, username, lms_user_id)
# TODO: verify the logic to simplify groups creation and membership
if user_is_a_student(user_role):
Expand Down Expand Up @@ -189,9 +189,6 @@ async def authenticate(self, handler: BaseHandler, data: Dict[str, str] = None)
# Assign the user_id. Check the tool consumer (lms) vendor. If canvas use their
# custom user id extension by default, else use standar lti values.
username = ''
# GRADES-SENDER: retrieve assignment_name from standard property vs custom lms
# properties, such as custom_canvas_...
assignment_name = args['resource_link_title'] if 'resource_link_title' in args else 'unknown'
if lms_vendor == 'canvas':
login_id = ''
user_id = ''
Expand All @@ -210,10 +207,6 @@ async def authenticate(self, handler: BaseHandler, data: Dict[str, str] = None)
user_id = lti_utils.email_to_username(custom_canvas_user_id)
self.log.debug('using custom_canvas_user_id for username')
username = f'{login_id}-{user_id}'
# GRADES-SENDER >>>> retrieve assignment_name from custom property
assignment_name = (
args['custom_canvas_assignment_title'] if 'custom_canvas_assignment_title' in args else 'unknown'
)
if (
not username
and 'lis_person_contact_email_primary' in args
Expand Down Expand Up @@ -243,22 +236,40 @@ async def authenticate(self, handler: BaseHandler, data: Dict[str, str] = None)
# then default to the username
lms_user_id = args['user_id'] if 'user_id' in args else username

# with all info extracted from lms request, register info for grades sender only if the user has
# the Learner role
# GRADES-SENDER: fetch the information needed to register assignments within the control file
# retrieve assignment_name from standard property vs custom lms properties
assignment_name = ''
# the next fields must come in args
if 'custom_canvas_assignment_title' in args and args['custom_canvas_assignment_title']:
assignment_name = lti_utils.normalize_string(args['custom_canvas_assignment_title'])
# this requires adding a the assignment_title as a custom parameter in the tool consumer (lms)
elif 'custom_assignment_title' in args and args['custom_assignment_title']:
assignment_name = lti_utils.normalize_string(args['custom_assignment_title'])
elif 'resource_link_title' in args and args['resource_link_title']:
assignment_name = lti_utils.normalize_string(args['resource_link_title'])
elif 'resource_link_id' in args and args['resource_link_id']:
assignment_name = lti_utils.normalize_string(args['resource_link_id'])

# Get lis_outcome_service_url and lis_result_sourcedid values that will help us to submit grades later
lis_outcome_service_url = ''
lis_result_sourcedid = ''
if user_is_a_student(user_role):
# the next fields must come in args
if 'lis_outcome_service_url' in args and args['lis_outcome_service_url']:
lis_outcome_service_url = args['lis_outcome_service_url']
if 'lis_result_sourcedid' in args and args['lis_result_sourcedid']:
lis_result_sourcedid = args['lis_result_sourcedid']
# only if both values exist we can register them to submit grades later
if lis_outcome_service_url and lis_result_sourcedid:
control_file = LTIGradesSenderControlFile(f'/home/grader-{course_id}/{course_id}')
control_file.register_data(
assignment_name, lis_outcome_service_url, lms_user_id, lis_result_sourcedid
)

# the next fields must come in args
if 'lis_outcome_service_url' in args and args['lis_outcome_service_url']:
lis_outcome_service_url = args['lis_outcome_service_url']
if 'lis_result_sourcedid' in args and args['lis_result_sourcedid']:
lis_result_sourcedid = args['lis_result_sourcedid']
# only if both values exist we can register them to submit grades later
if lis_outcome_service_url and lis_result_sourcedid:
control_file = LTIGradesSenderControlFile(f'/home/grader-{course_id}/{course_id}')
control_file.register_data(assignment_name, lis_outcome_service_url, lms_user_id, lis_result_sourcedid)
# Assignment creation
if assignment_name:
nbgrader_service = NbGraderServiceHelper(course_id)
self.log.debug(
'Creating a new assignment from the Authentication flow with title %s' % assignment_name
)
nbgrader_service.create_assignment_in_nbgrader(assignment_name)
# ensure the user name is normalized
username_normalized = lti_utils.normalize_string(username)
self.log.debug('Assigned username is: %s' % username_normalized)
Expand Down
22 changes: 11 additions & 11 deletions src/illumidesk/authenticators/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,17 +116,17 @@ def convert_request_to_dict(self, arguments: Dict[str, List[bytes]]) -> Dict[str


def user_is_a_student(user_role: str) -> Boolean:
if not user_role:
raise ValueError('user_role must have a value')
return user_role.lower() in DEFAULT_ROLE_NAMES_FOR_STUDENT
if not user_role:
raise ValueError('user_role must have a value')
return user_role.lower() in DEFAULT_ROLE_NAMES_FOR_STUDENT


def user_is_an_instructor(user_role: str) -> Boolean:
if not user_role:
raise ValueError('user_role must have a value')
# find the extra role names to recognize an instructor (to be added in the grader group)
extra_roles = os.environ.get('EXTRA_ROLE_NAMES_FOR_INSTRUCTOR') or []
if extra_roles:
extra_roles = extra_roles.lower().split(',')
DEFAULT_ROLE_NAMES_FOR_INSTRUCTOR.extend(extra_roles)
return user_role.lower() in DEFAULT_ROLE_NAMES_FOR_INSTRUCTOR
if not user_role:
raise ValueError('user_role must have a value')
# find the extra role names to recognize an instructor (to be added in the grader group)
extra_roles = os.environ.get('EXTRA_ROLE_NAMES_FOR_INSTRUCTOR') or []
if extra_roles:
extra_roles = extra_roles.lower().split(',')
DEFAULT_ROLE_NAMES_FOR_INSTRUCTOR.extend(extra_roles)
return user_role.lower() in DEFAULT_ROLE_NAMES_FOR_INSTRUCTOR
6 changes: 3 additions & 3 deletions src/tests/illumidesk/apis/test_nbgrader_service_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def test_new_instance_calls_chown_method_for_gradebook_path(
def test_create_assignment_in_nbgrader_method_fixes_source_directory_permissions(
mock_gradebook, mock_path_mkdir, mock_makedirs
):
course_id = 'intro101'
course_id = 'intro101'
with patch.object(shutil, 'chown') as mock_chown:
sut = NbGraderServiceHelper(course_id)
sut.create_assignment_in_nbgrader('lab-abc')
Expand All @@ -95,9 +95,9 @@ def test_create_assignment_in_nbgrader_method_fixes_source_directory_permissions
def test_create_assignment_in_nbgrader_method_fixes_assignment_directory_permissions(
mock_gradebook, mock_path_mkdir, mock_makedirs
):
course_id = 'intro101'
course_id = 'intro101'
with patch.object(shutil, 'chown') as mock_chown:
sut = NbGraderServiceHelper(course_id)
sut.create_assignment_in_nbgrader('lab-abc')
assignment_dir = os.path.abspath(Path(sut.course_dir, 'source', 'lab-abc'))
mock_chown.assert_any_call(assignment_dir, user=10001, group=100)
mock_chown.assert_any_call(assignment_dir, user=10001, group=100)
Loading

0 comments on commit beadb21

Please sign in to comment.