Skip to content

Commit

Permalink
Fix part of oppia#18470: Improve the user experience for adding topic…
Browse files Browse the repository at this point in the history
…s to the list of topics in the classroom admin page. (oppia#19000)
  • Loading branch information
DorianBD authored Nov 14, 2023
1 parent 57607c2 commit d807eaa
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 0 deletions.
28 changes: 28 additions & 0 deletions core/controllers/classroom.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,34 @@ def get(self) -> None:
self.render_json(self.values)


class UnusedTopicsHandler(
base.BaseHandler[Dict[str, str], Dict[str, str]]
):
"""Handler for fetching topics not associated with any classroom."""

GET_HANDLER_ERROR_RETURN_TYPE = feconf.HANDLER_TYPE_JSON
URL_PATH_ARGS_SCHEMAS: Dict[str, str] = {}
HANDLER_ARGS_SCHEMAS: Dict[str, Dict[str, str]] = {'GET': {}}

@acl_decorators.can_access_classroom_admin_page
def get(self) -> None:
"""Retrieves topics not associated with any classroom."""
all_topics = topic_fetchers.get_all_topics()
all_classrooms = classroom_config_services.get_all_classrooms()

topics_not_in_classroom = [
topic.to_dict() for topic in all_topics
if not any(
topic.id in classroom.topic_id_to_prerequisite_topic_ids
for classroom in all_classrooms
)
]
self.values.update({
'unused_topics': topics_not_in_classroom
})
self.render_json(self.values)


class NewClassroomIdHandler(
base.BaseHandler[Dict[str, str], Dict[str, str]]
):
Expand Down
131 changes: 131 additions & 0 deletions core/controllers/classroom_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,134 @@ def test_get_classroom_id_from_url_fragment_works_correctly(

json_response = self.get_json(
non_existent_classroom_url, expected_status_int=404)


class UnusedTopicsHandlerTests(test_utils.GenericTestBase):

def setUp(self) -> None:
super().setUp()
self.signup(self.CURRICULUM_ADMIN_EMAIL, self.CURRICULUM_ADMIN_USERNAME)
self.set_curriculum_admins([self.CURRICULUM_ADMIN_USERNAME])

self.owner_id = self.get_user_id_from_email(self.CURRICULUM_ADMIN_EMAIL)
self.used_topic1 = topic_domain.Topic.create_default_topic(
'used_topic_1', 'used_topic1_name',
'frag-used-topic-one', 'description', 'fragm')
topic_services.save_new_topic(self.owner_id, self.used_topic1)

self.physics_classroom_id = (
classroom_config_services.get_new_classroom_id())
self.physics_classroom_dict: classroom_config_domain.ClassroomDict = {
'classroom_id': self.physics_classroom_id,
'name': 'physics',
'url_fragment': 'physics',
'course_details': 'Curated physics foundations course.',
'topic_list_intro': 'Start from the basics with our first topic.',
'topic_id_to_prerequisite_topic_ids': {
'topic_id_1': ['topic_id_2', 'topic_id_3'],
'topic_id_2': [],
'topic_id_3': [],
'used_topic_1': []
}
}
self.physics_classroom = classroom_config_domain.Classroom.from_dict(
self.physics_classroom_dict)
classroom_config_services.update_or_create_classroom_model(
self.physics_classroom)

def test_returns_newly_added_unused_topics(self) -> None:
self.login(self.CURRICULUM_ADMIN_EMAIL, is_super_admin=True)

unused_topic1 = topic_domain.Topic.create_default_topic(
'unused_topic1', 'unused_topic1_name',
'frag-topic-one', 'description', 'fragm')
topic_services.save_new_topic(self.owner_id, unused_topic1)
unused_topics = [unused_topic1.to_dict()]
json_response = self.get_json(feconf.UNUSED_TOPICS_HANDLER_URL)
self.assertEqual(
json_response['unused_topics'],
unused_topics
)

unused_topic2 = topic_domain.Topic.create_default_topic(
'unused_topic2', 'unused_topic2_name',
'frag-topic-two', 'description', 'fragm')
topic_services.save_new_topic(self.owner_id, unused_topic2)
unused_topics = [unused_topic1.to_dict(), unused_topic2.to_dict()]
json_response = self.get_json(feconf.UNUSED_TOPICS_HANDLER_URL)
self.assertEqual(
json_response['unused_topics'],
unused_topics
)

self.logout()

def test_does_not_return_deleted_topic(self) -> None:
self.login(self.CURRICULUM_ADMIN_EMAIL, is_super_admin=True)

unused_topic1 = topic_domain.Topic.create_default_topic(
'unused_topic1', 'unused_topic1_name',
'frag-topic-one', 'description', 'fragm')
topic_services.save_new_topic(self.owner_id, unused_topic1)

unused_topic2 = topic_domain.Topic.create_default_topic(
'unused_topic2', 'unused_topic2_name',
'frag-topic-two', 'description', 'fragm')
topic_services.save_new_topic(self.owner_id, unused_topic2)

unused_topics = [unused_topic1.to_dict(), unused_topic2.to_dict()]
json_response = self.get_json(feconf.UNUSED_TOPICS_HANDLER_URL)
self.assertEqual(
json_response['unused_topics'],
unused_topics
)

topic_services.delete_topic(self.owner_id, unused_topic2.id, True)
unused_topics = [unused_topic1.to_dict()]
json_response = self.get_json(feconf.UNUSED_TOPICS_HANDLER_URL)
self.assertEqual(
json_response['unused_topics'],
unused_topics
)

self.logout()

def test_returns_topic_if_unused_in_classroom(self) -> None:
self.login(self.CURRICULUM_ADMIN_EMAIL, is_super_admin=True)
json_response = self.get_json(feconf.UNUSED_TOPICS_HANDLER_URL)
self.assertEqual(
json_response['unused_topics'],
[]
)

self.physics_classroom.topic_id_to_prerequisite_topic_ids.pop(
self.used_topic1.id
)
classroom_config_services.update_or_create_classroom_model(
self.physics_classroom)
json_response = self.get_json(feconf.UNUSED_TOPICS_HANDLER_URL)
self.assertEqual(
json_response['unused_topics'],
[self.used_topic1.to_dict()]
)

self.logout()

def test_returns_no_topics_if_no_unused_topics(self) -> None:
self.login(self.CURRICULUM_ADMIN_EMAIL, is_super_admin=True)
json_response = self.get_json(feconf.UNUSED_TOPICS_HANDLER_URL)
self.assertEqual(
json_response['unused_topics'],
[]
)

self.logout()

def test_not_able_to_get_unused_topics_when_user_is_not_admin(
self
) -> None:
self.signup(self.VIEWER_EMAIL, self.VIEWER_USERNAME)
self.login(self.VIEWER_EMAIL)
self.get_json(
feconf.UNUSED_TOPICS_HANDLER_URL, expected_status_int=401)
self.logout()
1 change: 1 addition & 0 deletions core/feconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,7 @@ def get_empty_ratings() -> Dict[str, int]:
CREATE_LEARNER_GROUP_PAGE_URL = '/create-learner-group'
EDIT_LEARNER_GROUP_PAGE_URL = '/edit-learner-group'
CLASSROOM_ADMIN_DATA_HANDLER_URL = '/classroom_admin_data_handler'
UNUSED_TOPICS_HANDLER_URL = '/unused_topics'
NEW_CLASSROOM_ID_HANDLER_URL = '/new_classroom_id_handler'
CLASSROOM_HANDLER_URL = '/classroom'
CLASSROOM_URL_FRAGMENT_HANDLER = '/classroom_url_fragment_handler'
Expand Down
3 changes: 3 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,9 @@ def get_redirect_route(
get_redirect_route(
r'%s' % feconf.CLASSROOM_ADMIN_DATA_HANDLER_URL,
classroom.ClassroomAdminDataHandler),
get_redirect_route(
r'%s' % feconf.UNUSED_TOPICS_HANDLER_URL,
classroom.UnusedTopicsHandler),
get_redirect_route(
r'%s' % feconf.NEW_CLASSROOM_ID_HANDLER_URL,
classroom.NewClassroomIdHandler),
Expand Down

0 comments on commit d807eaa

Please sign in to comment.