Skip to content

Commit

Permalink
Integrated Channel: Moodle - Inactivate courses instead of permanentl…
Browse files Browse the repository at this point in the history
…y delete courses
  • Loading branch information
hamzawaleed01 committed Sep 22, 2023
1 parent 3284586 commit 66ce31a
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 6 deletions.
37 changes: 34 additions & 3 deletions integrated_channels/moodle/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,31 @@ def create_content_metadata(self, serialized_data):
# check to see if more than 1 course is being passed
more_than_one_course = serialized_data.get('courses[1][shortname]')
serialized_data['wsfunction'] = 'core_course_create_courses'
LOGGER.info(
generate_formatted_log("CREATING:", serialized_data)
)
try:
response = self._wrapped_post(serialized_data)
moodle_course_id = self._get_course_id(serialized_data['courses[0][idnumber]'])
# Course already exists but is hidden - make it visible
if(moodle_course_id):
LOGGER.info(
generate_formatted_log(
"Existing course found with id:",
moodle_course_id,
" - updating it"
)
)
serialized_data['courses[0][visible]'] = 1
return self.update_content_metadata(serialized_data)
else: # create a new course
LOGGER.info(
generate_formatted_log(
"No existing course found with id:",
moodle_course_id,
" - creating a new one"
)
)
response = self._wrapped_post(serialized_data)
except MoodleClientError as error:
# treat duplicate as successful, but only if its a single course
# set chunk size settings to 1 if youre seeing a lot of these errors
Expand All @@ -369,6 +392,10 @@ def create_content_metadata(self, serialized_data):

def update_content_metadata(self, serialized_data):
moodle_course_id = self._get_course_id(serialized_data['courses[0][idnumber]'])
LOGGER.info(
generate_formatted_log("UPDATING:", moodle_course_id, serialized_data)
)

# if we cannot find the course, lets create it
if moodle_course_id:
serialized_data['courses[0][id]'] = moodle_course_id
Expand All @@ -381,6 +408,9 @@ def update_content_metadata(self, serialized_data):
def delete_content_metadata(self, serialized_data):
response = self._get_courses(serialized_data['courses[0][idnumber]'])
parsed_response = json.loads(response.text)
LOGGER.info(
generate_formatted_log("DELETING:", serialized_data, parsed_response)
)
if not parsed_response.get('courses'):
LOGGER.info(
generate_formatted_log(
Expand All @@ -398,8 +428,9 @@ def delete_content_metadata(self, serialized_data):
return rsp
moodle_course_id = parsed_response['courses'][0]['id']
params = {
'wsfunction': 'core_course_delete_courses',
'courseids[]': moodle_course_id
'wsfunction': 'core_course_update_courses',
'courses[0][id]': moodle_course_id,
'courses[0][visible]': 0 # hide a course rather than doing a true delete
}
response = self._wrapped_post(params)
return response.status_code, response.text
Expand Down
56 changes: 53 additions & 3 deletions tests/test_integrated_channels/test_moodle/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def setUp(self):
self.user_email = '[email protected]'
self.moodle_course_id = random.randint(1, 1000)
self._get_courses_response = bytearray('{{"courses": [{{"id": {}}}]}}'.format(self.moodle_course_id), 'utf-8')
self._get_courses_response_empty = bytearray('{}', 'utf-8')
self.empty_get_courses_response = bytearray('{"courses": []}', 'utf-8')
self.moodle_module_id = random.randint(1, 1000)
self.moodle_module_name = 'module'
Expand Down Expand Up @@ -129,6 +130,11 @@ def test_successful_create_content_metadata(self):

client = MoodleAPIClient(self.enterprise_config)
client._post = unittest.mock.MagicMock(name='_post', return_value=SUCCESSFUL_RESPONSE) # pylint: disable=protected-access
client._get_courses = unittest.mock.MagicMock(name='_get_courses') # pylint: disable=protected-access
mock_response = Response()
mock_response.status_code = 200
mock_response._content = self._get_courses_response_empty
client._get_courses.return_value = mock_response # pylint: disable=protected-access
client.create_content_metadata(SERIALIZED_DATA)
client._post.assert_called_once_with(expected_data) # pylint: disable=protected-access

Expand All @@ -142,6 +148,11 @@ def test_duplicate_shortname_create_content_metadata(self):

client = MoodleAPIClient(self.enterprise_config)
client._post = unittest.mock.MagicMock(name='_post', return_value=SHORTNAMETAKEN_RESPONSE) # pylint: disable=protected-access
client._get_courses = unittest.mock.MagicMock(name='_get_courses') # pylint: disable=protected-access
mock_response = Response()
mock_response.status_code = 200
mock_response._content = self._get_courses_response_empty
client._get_courses.return_value = mock_response # pylint: disable=protected-access
client.create_content_metadata(SERIALIZED_DATA)
client._post.assert_called_once_with(expected_data) # pylint: disable=protected-access

Expand All @@ -152,9 +163,13 @@ def test_duplicate_courseidnumber_create_content_metadata(self):
"""
expected_data = SERIALIZED_DATA.copy()
expected_data['wsfunction'] = 'core_course_create_courses'

client = MoodleAPIClient(self.enterprise_config)
client._post = unittest.mock.MagicMock(name='_post', return_value=COURSEIDNUMBERTAKEN_RESPONSE) # pylint: disable=protected-access
client._get_courses = unittest.mock.MagicMock(name='_get_courses') # pylint: disable=protected-access
mock_response = Response()
mock_response.status_code = 200
mock_response._content = self._get_courses_response_empty
client._get_courses.return_value = mock_response # pylint: disable=protected-access
client.create_content_metadata(SERIALIZED_DATA)
client._post.assert_called_once_with(expected_data) # pylint: disable=protected-access

Expand All @@ -168,6 +183,11 @@ def test_multi_duplicate_create_content_metadata(self):

client = MoodleAPIClient(self.enterprise_config)
client._post = unittest.mock.MagicMock(name='_post', return_value=SHORTNAMETAKEN_RESPONSE) # pylint: disable=protected-access
client._get_courses = unittest.mock.MagicMock(name='_get_courses') # pylint: disable=protected-access
mock_response = Response()
mock_response.status_code = 200
mock_response._content = self._get_courses_response_empty
client._get_courses.return_value = mock_response # pylint: disable=protected-access
with self.assertRaises(MoodleClientError):
client.create_content_metadata(MULTI_SERIALIZED_DATA)
client._post.assert_called_once_with(expected_data) # pylint: disable=protected-access
Expand All @@ -179,9 +199,14 @@ def test_multi_duplicate_courseidnumber_create_content_metadata(self):
"""
expected_data = MULTI_SERIALIZED_DATA.copy()
expected_data['wsfunction'] = 'core_course_create_courses'

client = MoodleAPIClient(self.enterprise_config)
client._post = unittest.mock.MagicMock(name='_post', return_value=COURSEIDNUMBERTAKEN_RESPONSE) # pylint: disable=protected-access
client._get_courses = unittest.mock.MagicMock(name='_get_courses') # pylint: disable=protected-access
mock_response = Response()
mock_response.status_code = 200
mock_response._content = self._get_courses_response_empty
client._get_courses.return_value = mock_response # pylint: disable=protected-access
with self.assertRaises(MoodleClientError):
client.create_content_metadata(MULTI_SERIALIZED_DATA)
client._post.assert_called_once_with(expected_data) # pylint: disable=protected-access
Expand All @@ -205,8 +230,9 @@ def test_update_content_metadata(self):
def test_delete_content_metadata(self):
"""
Test core logic for formatting a delete request to Moodle.
Mark a course visible:0 rather than doing a true delete
"""
expected_data = {'wsfunction': 'core_course_delete_courses', 'courseids[]': self.moodle_course_id}
expected_data = {'wsfunction': 'core_course_update_courses', 'courses[0][id]': self.moodle_course_id, 'courses[0][visible]': 0}

client = MoodleAPIClient(self.enterprise_config)
client._post = unittest.mock.MagicMock(name='_post', return_value=SUCCESSFUL_RESPONSE) # pylint: disable=protected-access
Expand Down Expand Up @@ -352,3 +378,27 @@ def test_get_course_final_grade_module_custom_name(self):

# The base transmitter expects the create course completion response to be a tuple of (code, body)
assert client.get_course_final_grade_module(2) == (1337, 'foobar')

def test_successful_update_existing_content_metadata(self):
"""
Test core logic of create_content_metadata to ensure
if a course already exists(hidden) then client sets its
visibility: 1 instead of creating a new course
"""
expected_data = SERIALIZED_DATA.copy()
expected_data['wsfunction'] = 'core_course_update_courses'
expected_data['courses[0][visible]'] = 1
expected_data['courses[0][id]'] = self.moodle_course_id

client = MoodleAPIClient(self.enterprise_config)
client._post = unittest.mock.MagicMock(name='_post', return_value=SUCCESSFUL_RESPONSE) # pylint: disable=protected-access
client._get_courses = unittest.mock.MagicMock(name='_get_courses') # pylint: disable=protected-access
mock_response = Response()
mock_response.status_code = 200
mock_response._content = self._get_courses_response

client._get_course_id = unittest.mock.MagicMock(name='_get_course_id') # pylint: disable=protected-access
client._get_course_id.return_value = self.moodle_course_id # pylint: disable=protected-access
client._get_courses.return_value = mock_response # pylint: disable=protected-access
client.create_content_metadata(SERIALIZED_DATA)
client._post.assert_called_once_with(expected_data) # pylint: disable=protected-access

0 comments on commit 66ce31a

Please sign in to comment.