def test_illegal_name(self): with self.assertRaisesRegexp(ValueError, 'name "A" must contain only'): analytics.Visualization('A', 'Foo', 'foo.html') with self.assertRaisesRegexp(ValueError, 'name " " must contain only'): analytics.Visualization(' ', 'Foo', 'foo.html') with self.assertRaisesRegexp(ValueError, 'name "#" must contain only'): analytics.Visualization('#', 'Foo', 'foo.html')
def register_on_enable(): data_sources.Registry.register(ExamsDataSource) data_sources.Registry.register(PupilsDataSource) data_sources.Registry.register(AnswersDataSource) exams = analytics.Visualization('exams', 'Exams', 'fake_visualizations.html', [ExamsDataSource]) pupils = analytics.Visualization('pupils', 'Pupils', 'fake_visualizations.html', [PupilsDataSource]) scoring = analytics.Visualization( 'scoring', 'Scoring', 'fake_visualizations.html', [ExamsDataSource, PupilsDataSource, AnswersDataSource]) dashboard.DashboardHandler.add_sub_nav_mapping( 'analytics', 'exams', 'Exams', action='analytics_exams', contents=analytics.TabRenderer([exams])) dashboard.DashboardHandler.add_sub_nav_mapping( 'analytics', 'pupils', 'Pupils', action='analytics_pupils', contents=analytics.TabRenderer([pupils])) dashboard.DashboardHandler.add_sub_nav_mapping( 'analytics', 'scoring', 'Scoring', action='analytics_scoring', contents=analytics.TabRenderer([scoring]))
def register_analytic(): data_sources.Registry.register(PeerReviewStatsSource) name = 'peer_review' title = 'Peer Review' peer_review = analytics.Visualization( name, title, 'stats.html', data_source_classes=[PeerReviewStatsSource]) tabs.Registry.register('analytics', name, title, [peer_review])
def register_callbacks(): # Update enrollments counters when a student enrolls, unenrolls. models.StudentLifecycleObserver.EVENT_CALLBACKS[ models.StudentLifecycleObserver.EVENT_ADD][ MODULE_NAME] = _count_add models.StudentLifecycleObserver.EVENT_CALLBACKS[ models.StudentLifecycleObserver.EVENT_REENROLL][ MODULE_NAME] = _count_add models.StudentLifecycleObserver.EVENT_CALLBACKS[ models.StudentLifecycleObserver.EVENT_UNENROLL][ MODULE_NAME] = _count_drop models.StudentLifecycleObserver.EVENT_CALLBACKS[ models.StudentLifecycleObserver.EVENT_UNENROLL_COMMANDED][ MODULE_NAME] = _count_drop # Set counters for newly-created courses initially to zero (to avoid # extraneous enrollments MapReduce runs). config.CoursesItemRESTHandler.NEW_COURSE_ADDED_HOOKS[ MODULE_NAME] = _new_course_counts # Delete the corresponding enrollments counters when a course is deleted. config.CourseDeleteHandler.COURSE_DELETED_HOOKS[ MODULE_NAME] = delete_counters # Register analytic to show nice zoomable graph of enroll/unenroll rates. data_sources.Registry.register(EnrollmentsDataSource) visualization = analytics.Visualization( 'enrollments', 'Enrollments', 'templates/enrollments.html', data_source_classes=[EnrollmentsDataSource]) dashboard.DashboardHandler.add_sub_nav_mapping( 'analytics', 'enrollments', 'Enrollments', action='analytics_enrollments', contents=analytics.TabRenderer([visualization]))
def register_module(): """Registers this module in the registry.""" data_sources.Registry.register(panalytics.WatchTimeStatsDataSource) tab_name = 'watch_time_stats' stats = analytics.Visualization( tab_name, MODULE_NAME, 'templates/watch_time_stats.html', data_source_classes=[panalytics.WatchTimeStatsDataSource]) tabs.Registry.register('analytics', tab_name, MODULE_NAME, [stats]) global_routes = [] watch_time_routes = [('/modules/watch_time/video_watchtime', record_watchtime.RecordWatchTime), ('/modules/nptel/video_watchtime', record_watchtime.RecordWatchTime)] global custom_module custom_module = custom_modules.Module( MODULE_NAME, 'Provides library to register video watch time related assets/code', global_routes, watch_time_routes) return custom_module
def register_analytic(): data_sources.Registry.register(NotificationsDataSource) name = 'notifications' title = 'Notifications' visualization = analytics.Visualization( name, title, 'stats.html', data_source_classes=[NotificationsDataSource]) tabs.Registry.register('analytics', name, title, [visualization])
def register_analytic(): data_sources.Registry.register(CertificatesEarnedDataSource) name = 'certificates_earned' title = 'Certificates Earned' certificates_earned = analytics.Visualization( name, title, 'certificates_earned.html', data_source_classes=[CertificatesEarnedDataSource]) tabs.Registry.register('analytics', name, title, analytics.TabRenderer([certificates_earned]))
def register_on_enable(): data_sources.Registry.register(ExamsDataSource) data_sources.Registry.register(PupilsDataSource) data_sources.Registry.register(AnswersDataSource) exams = analytics.Visualization('exams', 'Exams', 'fake_visualizations.html', [ExamsDataSource]) pupils = analytics.Visualization('pupils', 'Pupils', 'fake_visualizations.html', [PupilsDataSource]) scoring = analytics.Visualization( 'scoring', 'Scoring', 'fake_visualizations.html', [ExamsDataSource, PupilsDataSource, AnswersDataSource]) tabs.Registry.register('analytics', 'exams', 'Exams', [exams]) tabs.Registry.register('analytics', 'pupils', 'Pupils', [pupils]) tabs.Registry.register('analytics', 'scoring', 'Scoring', [scoring])
def register_analytic(): data_sources.Registry.register(CertificatesEarnedDataSource) name = 'certificates_earned' title = 'Certificates' certificates_earned = analytics.Visualization( name, title, 'certificates_earned.html', data_source_classes=[CertificatesEarnedDataSource]) dashboard.DashboardHandler.add_sub_nav_mapping( 'analytics', name, title, action='analytics_certificates_earned', contents=analytics.TabRenderer([certificates_earned]))
def test_analytics_warns_when_no_gcs_bucket(self): name = 'foo' analytic = analytics.Visualization( name, name, 'models_analytics_section.html', [OneGenSource]) result = self._generate_analytics_page([analytic]) self.assertNotIn('enable Google Cloud Storage', result) self._mock_identity_module.set_default_gcs_bucket_name(None) result = self._generate_analytics_page([analytic]) self.assertIn('enable Google Cloud Storage', result)
def test_no_generator_display(self): name = 'no_generator' analytic = analytics.Visualization( name, name, 'models_analytics_section.html', [NoGenSource]) result = self._generate_analytics_page([analytic]) # Statistic reports result to page self.assertIn('no_generator_no_gen_source: "no_gen_value"', result) # Statistic does not have a run/cancel button; it has no generators # which depend on jobs. self.assertNotIn('gdb-run-analytic-simple', result) self.assertNotIn('gdb-cancel-analytic-simple', result)
def register_tabs(): clusters_visualization = analytics.Visualization( 'clusters', 'Cluster Manager', 'clustering.html', data_source_classes=[clustering.ClusterDataSource]) student_vectors_visualization = analytics.Visualization( 'student_vectors', 'Student Vectors', 'student_vectors.html', data_source_classes=[clustering.TentpoleStudentVectorDataSource]) stats_visualization = analytics.Visualization( 'clustering_stats', 'Clustering Statistics', 'cluster_stats.html', data_source_classes=[clustering.ClusterStatisticsDataSource]) tabs.Registry.register('analytics', 'clustering', 'Clustering', [ clusters_visualization, student_vectors_visualization, stats_visualization ])
def register_analytic(): data_sources.Registry.register(PeerReviewStatsSource) name = 'peer_review' title = 'Peer review' peer_review = analytics.Visualization( name, title, 'stats.html', data_source_classes=[PeerReviewStatsSource]) dashboard.DashboardHandler.add_sub_nav_mapping( 'analytics', name, title, action='analytics_peer_review', contents=analytics.TabRenderer([peer_review]), placement=7000)
def register_analytic(): """This isn't exactly an analytic, but we register that way to be included with the other analytics sub-tabs on the Dashboard.""" name = 'sample_data' title = 'Generate Sample Data' sample_data = analytics.Visualization( name, title, os.path.join('modules', 'gen_sample_data', 'templates', 'sample_data.html')) dashboard.DashboardHandler.add_sub_nav_mapping( 'analytics', name, title, action='analytics_sample_data', contents=analytics.TabRenderer([sample_data]))
def register_analytic(): data_sources.Registry.register(NotificationsDataSource) name = 'notifications' title = 'Notifications' visualization = analytics.Visualization( name, title, 'stats.html', data_source_classes=[NotificationsDataSource]) dashboard.DashboardHandler.add_sub_nav_mapping( 'analytics', name, title, action='analytics_notifications', contents=analytics.TabRenderer([visualization]))
def test_generator_run_cancel_state_display(self): name = 'foo' analytic = analytics.Visualization(name, name, 'models_analytics_section.html', [OneGenSource]) result = self._generate_analytics_page([analytic]) self.assertIn('Statistics for gen one have not been', result) self.assertIn('Update Statistic', result) self.assertIn('action=run_visualizations', result) self._run_generators_for_visualizations(self._mock_app_context, [analytic]) result = self._generate_analytics_page([analytic]) self.assertIn('Job for gen one statistics started at', result) self.assertIn('Cancel Statistic Calculation', result) self.assertIn('action=cancel_visualizations', result) self._cancel_generators_for_visualizations(self._mock_app_context, [analytic]) result = self._generate_analytics_page([analytic]) self.assertIn('There was an error updating gen one statistics', result) self.assertIn('<pre>Canceled</pre>', result) self.assertIn('Update Statistic', result) self.assertIn('action=run_visualizations', result) self._run_generators_for_visualizations(self._mock_app_context, [analytic]) result = self._generate_analytics_page([analytic]) self.assertIn('Job for gen one statistics started at', result) self.assertIn('Cancel Statistic Calculation', result) self.assertIn('action=cancel_visualizations', result) GenOne(self._mock_app_context).load().complete('run_state_display') result = self._generate_analytics_page([analytic]) self.assertIn('Statistics for gen one were last updated at', result) self.assertIn('in about 0 sec', result) self.assertIn('Update Statistic', result) self.assertIn('action=run_visualizations', result) self.assertIn('foo_one_gen_source_gen_one: "run_state_display"', result)
def register_tabs(): multiple_choice_question = analytics.Visualization( 'multiple_choice_question', 'Multiple Choice Question', 'multiple_choice_question.html', data_source_classes=[synchronous_providers.QuestionStatsSource]) student_progress = analytics.Visualization( 'student_progress', 'Student Progress', 'student_progress.html', data_source_classes=[synchronous_providers.StudentProgressStatsSource]) enrollment_assessment = analytics.Visualization( 'enrollment_assessment', 'Enrollment/Assessment', 'enrollment_assessment.html', data_source_classes=[ synchronous_providers.StudentEnrollmentAndScoresSource ]) assessment_difficulty = analytics.Visualization( 'assessment_difficulty', 'Assessment Difficulty', 'assessment_difficulty.html', data_source_classes=[rest_providers.StudentAssessmentScoresDataSource]) labels_on_students = analytics.Visualization( 'labels_on_students', 'Labels on Students', 'labels_on_students.html', data_source_classes=[rest_providers.LabelsOnStudentsDataSource]) question_answers = analytics.Visualization( 'question_answers', 'Question Answers', 'question_answers.html', data_source_classes=[ student_answers.QuestionAnswersDataSource, student_answers.CourseQuestionsDataSource, student_answers.CourseUnitsDataSource ]) gradebook = analytics.Visualization( 'gradebook', 'Gradebook', 'gradebook.html', data_source_classes=[ student_answers.RawAnswersDataSource, student_answers.OrderedQuestionsDataSource ]) clusters_visualization = analytics.Visualization( 'clusters', 'Cluster Manager', 'clustering.html', data_source_classes=[clustering.ClusterDataSource]) student_vectors_visualization = analytics.Visualization( 'student_vectors', 'Student Vectors', 'student_vectors.html', data_source_classes=[clustering.TentpoleStudentVectorDataSource]) stats_visualization = analytics.Visualization( 'clustering_stats', 'Clustering Statistics', 'cluster_stats.html', data_source_classes=[clustering.ClusterStatisticsDataSource]) dashboard.DashboardHandler.add_sub_nav_mapping( ANALYTICS, 'students', 'Students', action='analytics_students', contents=analytics.TabRenderer( [labels_on_students, student_progress, enrollment_assessment]), placement=1000) dashboard.DashboardHandler.add_sub_nav_mapping( ANALYTICS, 'questions', 'Questions', action='analytics_questions', contents=analytics.TabRenderer( [multiple_choice_question, question_answers]), placement=2000) dashboard.DashboardHandler.add_sub_nav_mapping( ANALYTICS, 'assessments', 'Assessments', action='analytics_assessments', contents=analytics.TabRenderer([assessment_difficulty]), placement=3000) dashboard.DashboardHandler.add_sub_nav_mapping( ANALYTICS, 'gradebook', 'Gradebook', action='analytics_gradebook', contents=analytics.TabRenderer([gradebook]), placement=5000) dashboard.DashboardHandler.add_sub_nav_mapping( ANALYTICS, 'clustering', 'Clustering', action='analytics_clustering', contents=analytics.TabRenderer([ clusters_visualization, student_vectors_visualization, stats_visualization ]), placement=8000)
def register_module(): """Registers this module in the registry.""" data_sources.Registry.register(panalytics.ProgrammingStatsDataSource) tab_name = 'programming_assignment_stats' stats = analytics.Visualization( tab_name, base.ProgAssignment.NAME, 'templates/prog_assignment_stats.html', data_source_classes=[panalytics.ProgrammingStatsDataSource]) tabs.Registry.register('analytics', tab_name, base.ProgAssignment.NAME, [stats]) settings.ProgrammingAssignmentSettings.register() tabs.Registry.register(scoring_base.ScoringBase.DASHBOARD_NAV, base.ProgAssignment.DASHBOARD_REEVALUATION_TAB, 'Programming Assignments', pdashboard.ProgAssignmentDashboardHandler) tabs.Registry.register(base.ProgAssignment.DASHBOARD_NAV, base.ProgAssignment.DASHBOARD_TEST_RUN_TAB, 'Test Run Details', pdashboard.ProgAssignmentTestRunHandler) tabs.Registry.register(base.ProgAssignment.DASHBOARD_NAV, base.ProgAssignment.DASHBOARD_DOWNLOAD_TAB, 'Download', pdashboard.ProgAssignmentDownloadDashboardHandler) dashboard.DashboardHandler.add_custom_get_action( base.ProgAssignment.DASHBOARD_NAV, None) dashboard.DashboardHandler.add_custom_post_action( base.ProgAssignment.REEVAL_ACTION, pdashboard.ProgAssignmentDashboardHandler.confirm_reevaluation_page) dashboard.DashboardHandler.add_custom_post_action( base.ProgAssignment.REEVAL_CONFIRMED_ACTION, pdashboard.ProgAssignmentDashboardHandler.reevalaute_assignment) dashboard.DashboardHandler.add_custom_post_action( base.ProgAssignment.DOWNLOAD_ACTION, pdashboard.ProgAssignmentDownloadDashboardHandler.download_assignment) dashboard.DashboardHandler.add_custom_get_action( base.ProgAssignment.TEST_RUN_ACTION, pdashboard.ProgAssignmentTestRunHandler.get_show_results) dashboard.DashboardHandler.add_nav_mapping( base.ProgAssignment.DASHBOARD_NAV, base.ProgAssignment.NAME) associated_js_files_handlers = [ ('/static/edit_area/(.*)', sites.make_zip_handler( os.path.join(appengine_config.BUNDLE_ROOT, 'lib/edit-area.zip'))), ('/static/prettify/(.*)', sites.make_zip_handler( os.path.join(appengine_config.BUNDLE_ROOT, 'lib/google-code-prettify.zip'))), ('/cron/compute_programming_test_case_stats', panalytics.ComputeProgrammingStatsHandler), ('/cron/reevaluate_programming_submissions', reevaluator.ReevaulateSubmissionHandler), ('/modules/programming_assignments/assets/.*', tags.ResourcesHandler) ] prog_assignment_handlers = [(base.ProgAssignment.UNIT_URL, assignment.ProgAssignmentHandler), (question.ProgAssignmentRESTHandler.URI, question.ProgAssignmentRESTHandler)] global custom_module # pylint: disable=global-statement custom_module = custom_modules.Module( base.ProgAssignment.NAME, 'A set pages to manage programming assignments.', associated_js_files_handlers, prog_assignment_handlers) global custom_unit # pylint: disable=global-statement custom_unit = custom_units.CustomUnit( base.ProgAssignment.UNIT_TYPE_ID, base.ProgAssignment.NAME, question.ProgAssignmentRESTHandler, base.ProgAssignment.get_public_url, create_helper=question.create_assignment, cleanup_helper=base.ProgAssignment.delete_content, import_helper=question.import_assignment, is_graded=True) return custom_module
def test_illegal_generator(self): with self.assertRaisesRegexp( ValueError, 'All data source classes used in visualizations must be'): analytics.Visualization('foo', 'foo', 'foo', [MockHandler])
def test_multiple_visualizationsmultiple_generators_multiple_sources(self): visualizations = [] visualizations.append( analytics.Visualization('trivial', 'Trivial Statistics', 'models_analytics_section.html', [NoGenSource])) visualizations.append( analytics.Visualization('simple', 'Simple Statistics', 'models_analytics_section.html', [OneGenSource])) visualizations.append( analytics.Visualization( 'complex', 'Complex Statistics', 'models_analytics_section.html', [NoGenSource, OneGenSource, TwoGenSource, ThreeGenSource])) self._run_generators_for_visualizations(self._mock_app_context, visualizations) # Verify that not-all generators are running, but that 'complex' # is still not reporting data, as the generator it's relying on # (GenThree) is still running. GenOne(self._mock_app_context).load().complete('gen_one_data') GenTwo(self._mock_app_context).load().complete('gen_two_data') result = self._generate_analytics_page(visualizations) self.assertIn('simple_one_gen_source_gen_one: "gen_one_data"', result) self.assertIn('Statistics for gen one were last updated', result) self.assertIn('Statistics for gen two were last updated', result) self.assertIn('Job for gen three statistics started at', result) self.assertNotIn('complex_three_gen_source', result) # Finish last generator; should now have all data from all sources. GenThree(self._mock_app_context).load().complete('gen_three_data') result = self._generate_analytics_page(visualizations) self.assertIn('trivial_no_gen_source: "no_gen_value"', result) self.assertIn('simple_one_gen_source_gen_one: "gen_one_data"', result) self.assertIn('complex_no_gen_source: "no_gen_value"', result) self.assertIn('complex_one_gen_source_gen_one: "gen_one_data"', result) self.assertIn('complex_two_gen_source_gen_one: "gen_one_data"', result) self.assertIn('complex_two_gen_source_gen_two: "gen_two_data"', result) self.assertIn('complex_three_gen_source_gen_one: "gen_one_data"', result) self.assertIn('complex_three_gen_source_gen_two: "gen_two_data"', result) self.assertIn('complex_three_gen_source_gen_three: "gen_three_data"', result) # Verify that we _don't_ have data for sections that didn't specify # that source. self.assertIn('trivial_one_gen_source_gen_one: ""', result) self.assertIn('simple_no_gen_source: ""', result) # We should have all headers self.assertIn('<h3>Trivial Statistics</h3>', result) self.assertIn('<h3>Simple Statistics</h3>', result) self.assertIn('<h3>Complex Statistics</h3>', result) # And submission forms for analytics w/ generators self.assertNotIn( '<input type="hidden" name="visualization" value="trivial"', result) self.assertIn( '<input type="hidden" name="visualization" value="simple"', result) self.assertIn( '<input type="hidden" name="visualization" value="complex"', result)