Skip to content

Commit adbc9f6

Browse files
authored
Fix oppia#18305, oppia#19851: Study guide model generation button (oppia#23518)
* Created Generate Study Guides Model on the Admin page * Added study guide model deletion button on admin page, new subtopic page creation logic when study guide is created and audit study guide models beam job * Added verification button; Fixed workedexample flag off behaviour; Fixed subtopic changes when study guide is edited * Fixed some tests * Fixed lint and type errors; Added frontend test coverage * Lint fix * Lint fix * Fixed backend test * Added backend test coverage * Made requested changes * Added more context for admin misc tab cards; Removed populate study guides job * Fixed test
1 parent 1f20066 commit adbc9f6

19 files changed

+1056
-833
lines changed

core/controllers/admin.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3317,6 +3317,91 @@ def put(self) -> None:
33173317
self.render_json({})
33183318

33193319

3320+
class GenerateStudyGuideModelsHandler(
3321+
base.BaseHandler[Dict[str, str], Dict[str, str]]
3322+
):
3323+
"""Handler to generate study guide models for all subtopic pages."""
3324+
3325+
URL_PATH_ARGS_SCHEMAS: Dict[str, str] = {}
3326+
HANDLER_ARGS_SCHEMAS: Dict[str, Dict[str, str]] = {'POST': {}}
3327+
3328+
@acl_decorators.can_access_admin_page
3329+
def post(self) -> None:
3330+
"""Generates study guide models for all subtopic pages."""
3331+
3332+
# Fetched topics are sorted only to make the backend tests pass.
3333+
topics = sorted(
3334+
topic_fetchers.get_all_topics(),
3335+
key=operator.attrgetter('created_on'),
3336+
)
3337+
3338+
for topic in topics:
3339+
study_guide_services.generate_study_guide_models(
3340+
topic.id, topic.subtopics
3341+
)
3342+
3343+
self.render_json({})
3344+
3345+
3346+
class DeleteStudyGuideModelsHandler(
3347+
base.BaseHandler[Dict[str, str], Dict[str, str]]
3348+
):
3349+
"""Handler to delete all study guide models."""
3350+
3351+
URL_PATH_ARGS_SCHEMAS: Dict[str, str] = {}
3352+
HANDLER_ARGS_SCHEMAS: Dict[str, Dict[str, str]] = {'DELETE': {}}
3353+
3354+
@acl_decorators.can_access_admin_page
3355+
def delete(self) -> None:
3356+
"""Deletes all study guide models."""
3357+
3358+
# Fetched topics are sorted only to make the backend tests pass.
3359+
topics = sorted(
3360+
topic_fetchers.get_all_topics(),
3361+
key=operator.attrgetter('created_on'),
3362+
)
3363+
3364+
for topic in topics:
3365+
study_guide_services.delete_study_guide_models(
3366+
topic.id, topic.subtopics
3367+
)
3368+
3369+
self.render_json({})
3370+
3371+
3372+
class VerifyStudyGuideModelsHandler(
3373+
base.BaseHandler[Dict[str, str], Dict[str, str]]
3374+
):
3375+
"""Handler to verify all study guide models have the correct
3376+
corresponding snapshot and commitlog models."""
3377+
3378+
GET_HANDLER_ERROR_RETURN_TYPE = feconf.HANDLER_TYPE_JSON
3379+
URL_PATH_ARGS_SCHEMAS: Dict[str, str] = {}
3380+
HANDLER_ARGS_SCHEMAS: Dict[str, Dict[str, str]] = {'GET': {}}
3381+
3382+
@acl_decorators.can_access_admin_page
3383+
def get(self) -> None:
3384+
"""Verifies all study guide models have the correct snapshot and
3385+
commitlog models.
3386+
"""
3387+
3388+
# Fetched topics are sorted only to make the backend tests pass.
3389+
topics = sorted(
3390+
topic_fetchers.get_all_topics(),
3391+
key=operator.attrgetter('created_on'),
3392+
)
3393+
3394+
issues = []
3395+
for topic in topics:
3396+
issues.append(
3397+
study_guide_services.verify_study_guide_models(
3398+
topic.id, topic.subtopics
3399+
)
3400+
)
3401+
3402+
self.render_json({'issues': issues})
3403+
3404+
33203405
class TranslationCoordinatorRoleHandlerNormalizedPayloadDict(TypedDict):
33213406
"""Dict representation of TranslationCoordinatorRoleHandler's
33223407
normalized_payload dictionary.

core/controllers/admin_test.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2862,6 +2862,157 @@ def test_regenerate_topic_summaries(self) -> None:
28622862
)
28632863

28642864

2865+
class GenerateStudyGuideModelsHandlerTest(test_utils.GenericTestBase):
2866+
"""Tests for GenerateStudyGuideModelsHandler."""
2867+
2868+
def setUp(self) -> None:
2869+
super().setUp()
2870+
self.admin_id = self.get_user_id_from_email(self.SUPER_ADMIN_EMAIL)
2871+
2872+
def test_generate_study_guide_models(self) -> None:
2873+
topic_id_1 = topic_fetchers.get_new_topic_id()
2874+
subtopic_1 = topic_domain.Subtopic.create_default_subtopic(
2875+
1, 'Subtopic Title 1', 'url-frag-one'
2876+
)
2877+
subtopic_1.skill_ids = ['skill_id_1']
2878+
subtopic_1.url_fragment = 'sub-one-frag'
2879+
self.save_new_subtopic(
2880+
1,
2881+
self.admin_id,
2882+
topic_id_1,
2883+
)
2884+
self.save_new_topic(
2885+
topic_id_1,
2886+
self.admin_id,
2887+
name='Topic 1',
2888+
abbreviated_name='T1',
2889+
url_fragment='url-frag-one',
2890+
description='Description',
2891+
canonical_story_ids=[],
2892+
additional_story_ids=[],
2893+
uncategorized_skill_ids=[],
2894+
subtopics=[subtopic_1],
2895+
next_subtopic_id=2,
2896+
)
2897+
2898+
self.login(self.SUPER_ADMIN_EMAIL, is_super_admin=True)
2899+
csrf_token = self.get_new_csrf_token()
2900+
2901+
# Order of function calls in expected_args should not
2902+
# matter for this test.
2903+
with self.swap(
2904+
study_guide_services,
2905+
'generate_study_guide_models',
2906+
study_guide_services.generate_study_guide_models,
2907+
):
2908+
self.post_json(
2909+
feconf.GENERATE_STUDY_GUIDE_MODELS_URL,
2910+
{},
2911+
csrf_token=csrf_token,
2912+
expected_status_int=200,
2913+
)
2914+
2915+
2916+
class DeleteStudyGuideModelsHandlerTest(test_utils.GenericTestBase):
2917+
"""Tests for DeleteStudyGuideModelsHandler."""
2918+
2919+
def setUp(self) -> None:
2920+
super().setUp()
2921+
self.admin_id = self.get_user_id_from_email(self.SUPER_ADMIN_EMAIL)
2922+
2923+
def test_delete_study_guide_models(self) -> None:
2924+
topic_id_1 = topic_fetchers.get_new_topic_id()
2925+
subtopic_1 = topic_domain.Subtopic.create_default_subtopic(
2926+
1, 'Subtopic Title 1', 'url-frag-one'
2927+
)
2928+
subtopic_1.skill_ids = ['skill_id_1']
2929+
subtopic_1.url_fragment = 'sub-one-frag'
2930+
self.save_new_subtopic(
2931+
1,
2932+
self.admin_id,
2933+
topic_id_1,
2934+
)
2935+
self.save_new_study_guide(1, self.admin_id, topic_id_1)
2936+
self.save_new_topic(
2937+
topic_id_1,
2938+
self.admin_id,
2939+
name='Topic 1',
2940+
abbreviated_name='T1',
2941+
url_fragment='url-frag-one',
2942+
description='Description',
2943+
canonical_story_ids=[],
2944+
additional_story_ids=[],
2945+
uncategorized_skill_ids=[],
2946+
subtopics=[subtopic_1],
2947+
next_subtopic_id=2,
2948+
)
2949+
2950+
self.login(self.SUPER_ADMIN_EMAIL, is_super_admin=True)
2951+
2952+
# Order of function calls in expected_args should not
2953+
# matter for this test.
2954+
with self.swap(
2955+
study_guide_services,
2956+
'delete_study_guide_models',
2957+
study_guide_services.delete_study_guide_models,
2958+
):
2959+
self.delete_json(
2960+
feconf.DELETE_STUDY_GUIDE_MODELS_URL,
2961+
{},
2962+
expected_status_int=200,
2963+
)
2964+
2965+
2966+
class VerifyStudyGuideModelsHandlerTest(test_utils.GenericTestBase):
2967+
"""Tests for VerifyStudyGuideModelsHandler."""
2968+
2969+
def setUp(self) -> None:
2970+
super().setUp()
2971+
self.admin_id = self.get_user_id_from_email(self.SUPER_ADMIN_EMAIL)
2972+
2973+
def test_verify_study_guide_models(self) -> None:
2974+
topic_id_1 = topic_fetchers.get_new_topic_id()
2975+
subtopic_1 = topic_domain.Subtopic.create_default_subtopic(
2976+
1, 'Subtopic Title 1', 'url-frag-one'
2977+
)
2978+
subtopic_1.skill_ids = ['skill_id_1']
2979+
subtopic_1.url_fragment = 'sub-one-frag'
2980+
self.save_new_subtopic(
2981+
1,
2982+
self.admin_id,
2983+
topic_id_1,
2984+
)
2985+
self.save_new_study_guide(1, self.admin_id, topic_id_1)
2986+
self.save_new_topic(
2987+
topic_id_1,
2988+
self.admin_id,
2989+
name='Topic 1',
2990+
abbreviated_name='T1',
2991+
url_fragment='url-frag-one',
2992+
description='Description',
2993+
canonical_story_ids=[],
2994+
additional_story_ids=[],
2995+
uncategorized_skill_ids=[],
2996+
subtopics=[subtopic_1],
2997+
next_subtopic_id=2,
2998+
)
2999+
3000+
self.login(self.SUPER_ADMIN_EMAIL, is_super_admin=True)
3001+
3002+
# Order of function calls in expected_args should not
3003+
# matter for this test.
3004+
with self.swap(
3005+
study_guide_services,
3006+
'verify_study_guide_models',
3007+
study_guide_services.verify_study_guide_models,
3008+
):
3009+
self.get_json(
3010+
feconf.VERIFY_STUDY_GUIDE_MODELS_URL,
3011+
{},
3012+
expected_status_int=200,
3013+
)
3014+
3015+
28653016
class TopicManagerRoleHandlerTest(test_utils.GenericTestBase):
28663017
"""Tests for TopicManagerRoleHandler."""
28673018

core/controllers/topic_editor_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,7 @@ def test_editable_topic_handler_put(self) -> None:
10611061
self.assertEqual(
10621062
{
10631063
'subtitled_html': {
1064-
'html': '<p>New Data</p>',
1064+
'html': '<p><strong>new heading</strong></p>\n\n<p>New Data</p>',
10651065
'content_id': 'content',
10661066
},
10671067
'recorded_voiceovers': {'voiceovers_mapping': {'content': {}}},

0 commit comments

Comments
 (0)