tutorflow-content 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tutorflow_content-0.1.0/PKG-INFO +151 -0
- tutorflow_content-0.1.0/README.md +130 -0
- tutorflow_content-0.1.0/pyproject.toml +39 -0
- tutorflow_content-0.1.0/setup.cfg +4 -0
- tutorflow_content-0.1.0/tests/test_errors.py +56 -0
- tutorflow_content-0.1.0/tests/test_pagination.py +102 -0
- tutorflow_content-0.1.0/tests/test_retry.py +110 -0
- tutorflow_content-0.1.0/tests/test_runs.py +136 -0
- tutorflow_content-0.1.0/tests/test_webhooks.py +125 -0
- tutorflow_content-0.1.0/tutorflow_content/__init__.py +42 -0
- tutorflow_content-0.1.0/tutorflow_content/client.py +71 -0
- tutorflow_content-0.1.0/tutorflow_content/errors.py +87 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/__init__.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_api_keys/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_api_keys/content_integration_api_key_controller_create_api_key.py +243 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_api_keys/content_integration_api_key_controller_delete_api_key.py +251 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_api_keys/content_integration_api_key_controller_list_api_keys.py +228 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_api_keys/content_integration_api_key_controller_revoke_api_key.py +232 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_api_keys/content_integration_api_key_controller_rotate_api_key.py +256 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_api_keys/content_integration_api_key_controller_update_api_key.py +283 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_api_keys/content_organization_activity_controller_api_key_labels.py +240 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_assets/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_assets/content_asset_upload_controller_complete.py +266 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_assets/content_asset_upload_controller_create.py +350 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_audit_log/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_audit_log/content_api_audit_log_controller_list.py +383 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_audit_log/content_organization_activity_controller_audit_log.py +384 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_classrooms/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_classrooms/content_integration_classroom_controller_list_classrooms.py +206 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/content_resource_course_curriculum_controller_create_chapter.py +291 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/content_resource_course_curriculum_controller_create_lesson.py +304 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/content_resource_course_curriculum_controller_delete_chapter.py +283 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/content_resource_course_curriculum_controller_delete_lesson.py +283 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/content_resource_course_curriculum_controller_get_lesson.py +280 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/content_resource_course_curriculum_controller_list_chapters.py +267 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/content_resource_course_curriculum_controller_reorder_chapters.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/content_resource_course_curriculum_controller_reorder_lessons.py +300 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/content_resource_course_curriculum_controller_update_chapter.py +300 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_course_curriculum/content_resource_course_curriculum_controller_update_lesson.py +300 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_courses/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_courses/content_resource_course_controller_create.py +278 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_courses/content_resource_course_controller_delete.py +270 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_courses/content_resource_course_controller_get.py +315 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_courses/content_resource_course_controller_list.py +379 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_courses/content_resource_course_controller_update.py +295 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_credits/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_credits/content_integration_credit_controller_get_balance.py +209 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_credits/content_integration_credit_controller_get_history.py +366 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_credits/content_organization_activity_controller_credit_history.py +371 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_evaluations/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_evaluations/content_evaluation_controller_create.py +323 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_evaluations/content_evaluation_controller_download_file.py +277 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_evaluations/content_evaluation_controller_get.py +247 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_evaluations/content_evaluation_controller_list.py +330 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_evaluations/content_learning_controller_get_mastery.py +247 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_evaluations/content_learning_controller_list_concepts.py +234 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/content_resource_game_controller_build_game.py +325 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/content_resource_game_controller_create.py +278 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/content_resource_game_controller_delete.py +274 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/content_resource_game_controller_generate_brief.py +338 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/content_resource_game_controller_get.py +267 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/content_resource_game_controller_get_run_status.py +243 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/content_resource_game_controller_list.py +364 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/content_resource_game_controller_restore_version.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/content_resource_game_controller_revise_game.py +342 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_games/content_resource_game_controller_update.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_generation/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_generation/content_generation_controller_generate_course.py +337 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_generation/content_generation_controller_generate_module.py +329 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_generation/content_generation_controller_generate_slides.py +353 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_generation/content_generation_controller_generate_test.py +321 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_generation/content_generation_controller_get_run.py +243 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_generation/content_generation_controller_list_runs.py +311 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_generation/content_generation_controller_resume_run.py +341 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_integrations/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_integrations/content_integration_controller_create_expansion.py +280 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_integrations/content_integration_controller_get_expansion.py +230 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_integrations/content_integration_controller_get_expansion_result.py +230 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/content_learner_controller_get.py +251 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/content_learner_controller_invite.py +317 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/content_learner_controller_list.py +318 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/content_learner_controller_progress.py +288 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/content_learner_controller_remove.py +255 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/content_learner_enrollment_controller_enroll.py +303 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/content_learner_enrollment_controller_list.py +288 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/content_learner_enrollment_controller_unenroll.py +264 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/content_test_result_controller_get.py +295 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_learners/content_test_result_controller_list.py +314 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_modules/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_modules/content_resource_module_controller_copy_to_course.py +291 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_modules/content_resource_module_controller_create.py +278 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_modules/content_resource_module_controller_delete.py +270 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_modules/content_resource_module_controller_get.py +267 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_modules/content_resource_module_controller_list.py +364 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_modules/content_resource_module_controller_update.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_organizations/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_organizations/content_integration_organization_controller_list_organizations.py +199 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/content_resource_simulation_controller_build_simulation.py +325 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/content_resource_simulation_controller_create.py +278 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/content_resource_simulation_controller_delete.py +274 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/content_resource_simulation_controller_generate_brief.py +338 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/content_resource_simulation_controller_get.py +267 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/content_resource_simulation_controller_get_run_status.py +243 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/content_resource_simulation_controller_list.py +384 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/content_resource_simulation_controller_restore_version.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/content_resource_simulation_controller_revise_simulation.py +342 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_simulations/content_resource_simulation_controller_update.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_slides/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_slides/content_resource_slide_controller_create.py +278 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_slides/content_resource_slide_controller_delete.py +270 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_slides/content_resource_slide_controller_get.py +267 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_slides/content_resource_slide_controller_list.py +364 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_slides/content_resource_slide_controller_update.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_tests/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_tests/content_resource_test_controller_create.py +278 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_tests/content_resource_test_controller_delete.py +270 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_tests/content_resource_test_controller_get.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_tests/content_resource_test_controller_list.py +379 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_tests/content_resource_test_controller_update.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_controller_add_scene.py +291 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_controller_create.py +286 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_controller_delete.py +270 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_controller_delete_scene.py +283 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_controller_get.py +267 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_controller_list.py +364 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_controller_reorder_scenes.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_controller_update.py +287 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_controller_update_scene.py +300 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_narration_controller_narrate_missing.py +289 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_narration_controller_narrate_scene.py +294 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_render_controller_cancel.py +250 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_render_controller_get_status.py +243 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_videos/content_resource_video_render_controller_trigger.py +328 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_integration_webhook_controller_create_webhook.py +243 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_integration_webhook_controller_delete_webhook.py +236 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_integration_webhook_controller_get_webhook.py +236 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_integration_webhook_controller_list_deliveries.py +317 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_integration_webhook_controller_list_webhooks.py +228 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_integration_webhook_controller_redeliver.py +249 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_integration_webhook_controller_rotate_secret.py +280 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_integration_webhook_controller_send_test_event.py +236 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_integration_webhook_controller_update_webhook.py +284 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_webhook_key_controller_create.py +236 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_webhook_key_controller_delete.py +230 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_webhook_key_controller_get.py +230 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_webhook_key_controller_list.py +206 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_webhook_key_controller_list_deliveries.py +311 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_webhook_key_controller_redeliver.py +243 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_webhook_key_controller_rotate_secret.py +270 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_webhook_key_controller_send_test_event.py +230 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/content_webhooks/content_webhook_key_controller_update.py +278 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/v_1_contentclassroomsclassroom_idintegrationsexports/__init__.py +1 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/api/v_1_contentclassroomsclassroom_idintegrationsexports/content_integration_export_controller_export_standalone_lesson.py +243 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/client.py +271 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/errors.py +14 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/__init__.py +693 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/add_scene_dto.py +104 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/chat_course_quiz_dto.py +257 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/classroom_test_level_type.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/classroom_test_visibility_type.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_audit_log_controller_list_source.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_error.py +85 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_error_error.py +221 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_error_error_details_item.py +87 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_key_created_res_dto.py +277 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_key_created_res_dto_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_key_expiring_event.py +135 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_key_filter_label.py +118 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_key_filter_label_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_key_label_type_0.py +109 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_key_label_type_0_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_api_scope.py +12 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_asset_upload.py +169 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_asset_upload_purpose.py +13 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_asset_upload_status.py +11 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_asset_upload_ticket.py +186 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_asset_upload_ticket_headers.py +85 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_asset_upload_ticket_method.py +7 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_asset_upload_ticket_purpose.py +13 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_asset_upload_ticket_status.py +11 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_audit_log_admin_item.py +330 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_audit_log_admin_page.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_audit_log_item.py +307 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_audit_log_page.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_build_history_entry.py +124 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_chapter.py +148 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_classroom_res_dto.py +113 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_concept.py +123 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_concept_list.py +94 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course.py +343 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course_chapter.py +146 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course_deleted.py +121 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course_lesson.py +337 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course_lesson_stats.py +84 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course_list.py +137 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course_progress.py +208 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course_stats.py +221 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course_stats_daily_learning_time_item.py +84 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course_stats_most_behind_learner_type_0.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_course_stats_top_learners_item.py +119 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_credit_history_item.py +227 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_credit_history_item_kind.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_credit_history_page.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_curriculum.py +118 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_cursor_page_meta.py +100 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_deleted.py +101 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_deprecated_success.py +80 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_enrollment.py +177 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_enrollment_change.py +94 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_enrollment_page.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation.py +584 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_controller_create_files_body.py +413 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_controller_download_file_field.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_controller_list_evaluation_type.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_controller_list_status.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_create.py +254 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_create_evaluation_type.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_create_feedback_options.py +105 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_create_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_create_rubric.py +95 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_create_rubric_criteria_item.py +92 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_create_submitter.py +87 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_event.py +241 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_event_status.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_file.py +100 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_learner_type_0.py +92 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_page.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_rubric_breakdown_type_0_item.py +105 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_evaluation_status.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_game.py +372 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_game_brief_type_0.py +66 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_game_list.py +137 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_game_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_course_request.py +146 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_course_request_level.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_module_request.py +120 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_module_request_type.py +19 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_slides_request.py +150 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_slides_request_text_length.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_slides_request_theme_id.py +15 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_test_request.py +196 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_test_request_item_types_item.py +11 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_test_request_level.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generate_test_request_passing_value_type.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_accepted.py +151 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_accepted_kind.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_accepted_phase.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_controller_list_runs_kind.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_controller_list_runs_status.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_event.py +213 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_event_phase.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_event_resource_type.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_event_status.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run.py +296 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_accepted.py +121 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_accepted_kind.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_accepted_status.py +7 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_event.py +241 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_event_resource_type.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_event_status.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_kind.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_page.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_progress.py +93 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_state.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_status.py +135 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_status_active_type_0.py +116 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_status_active_type_0_phase.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_status_last_type_0.py +148 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_status_last_type_0_phase.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_generation_run_status_last_type_0_status.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_api_key_controller_delete_api_key_response_200.py +88 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_api_key_dto.py +377 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_api_key_dto_issuer_status.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_api_key_dto_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_credit_balance_res_dto.py +117 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_credit_controller_get_history_source.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_credit_controller_get_history_type.py +54 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_job_res_dto.py +334 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_job_res_dto_error_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_organization_res_dto.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_organization_res_dto_role.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_output_res_dto.py +302 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_output_res_dto_error_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_output_res_dto_manifest_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_output_type.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_result_res_dto.py +122 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_result_res_dto_manifest.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_source_type.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_webhook_delivery_status.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_webhook_event_type.py +35 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_integration_webhook_status.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_job_event.py +178 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner.py +132 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_controller_list_status.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_course_completed_event.py +141 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_enrolled_event.py +129 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_invitation_result.py +143 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_invitation_result_invitation_type_1.py +104 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_invitation_result_status.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_mastery.py +131 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_mastery_concepts_item.py +145 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_mastery_learner.py +92 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_page.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_progress.py +106 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_removed.py +91 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_status.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_learner_test_submitted_event.py +152 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_lesson_detail.py +265 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_lesson_detail_submission.py +70 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_lesson_progress.py +154 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_lesson_summary.py +205 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_module.py +426 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_module_copy_result.py +99 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_module_list.py +137 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_module_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_module_quiz.py +237 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_organization_activity_controller_audit_log_source.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_organization_activity_controller_credit_history_source.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_organization_activity_controller_credit_history_type.py +54 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_page_meta.py +116 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_course_controller_get_days.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_course_controller_list_field.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_event.py +328 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_event_resource_type.py +13 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_event_source.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_event_subresource.py +107 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_event_subresource_op.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_event_subresource_type.py +11 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_event_subresources_item.py +106 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_event_subresources_item_op.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_event_subresources_item_type.py +11 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_game_controller_list_field.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_module_controller_list_field.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_module_controller_update_response_200.py +437 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_simulation_controller_list_field.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_slide_controller_list_field.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_slide_controller_update_response_200.py +311 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_test_controller_list_field.py +11 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_test_controller_update_response_200.py +441 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_video_controller_list_field.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_video_narration_controller_narrate_missing_response_200.py +110 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_video_narration_controller_narrate_scene_response_200.py +110 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_resource_video_render_controller_trigger_body.py +62 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_scene_subtitle.py +92 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_simulation.py +390 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_simulation_brief_type_0.py +66 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_simulation_list.py +137 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_simulation_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_slide.py +300 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_slide_list.py +137 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_slide_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_success.py +79 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test.py +429 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_item.py +280 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_item_stats.py +92 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_list.py +137 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_result.py +250 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_result_controller_get_include.py +7 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_result_detail.py +274 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_result_item.py +116 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_result_item_status.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_result_page.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_result_status.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_test_result_taker_type_0.py +93 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_unenrolled.py +102 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video.py +431 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video_list.py +116 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video_render_accepted.py +145 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video_render_cancelled.py +79 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video_render_event.py +195 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video_render_event_render_status.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video_render_state.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video_render_status.py +198 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video_scene.py +623 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_video_scene_visual_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_visual_adjustments.py +181 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_visual_overlay.py +311 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_visual_overlay_animation.py +105 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_visual_overlay_frame_animation.py +87 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_visual_overlay_frames_item.py +87 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_visual_overlay_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_created_res_dto.py +344 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_created_res_dto_disabled_reason_type_1.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_created_res_dto_disabled_reason_type_2_type_1.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_created_res_dto_disabled_reason_type_3_type_1.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_created_res_dto_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_deleted.py +91 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_delivery_res_dto.py +325 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_delivery_res_dto_payload_type_0.py +66 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_event_envelope.py +102 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_ping_event.py +115 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_res_dto.py +336 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_res_dto_disabled_reason_type_1.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_res_dto_disabled_reason_type_2_type_1.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_res_dto_disabled_reason_type_3_type_1.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_res_dto_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_updated_res_dto.py +351 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_updated_res_dto_disabled_reason_type_1.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_updated_res_dto_disabled_reason_type_2_type_1.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_updated_res_dto_disabled_reason_type_3_type_1.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/content_webhook_updated_res_dto_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/copy_standalone_lesson_to_course_req_dto.py +118 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/course_celebration_trigger.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/course_content_type.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_classroom_course_req_dto.py +176 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_classroom_game_dto.py +150 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_classroom_game_dto_metadata_type_0.py +67 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_classroom_simulation_dto.py +150 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_classroom_simulation_dto_metadata_type_0.py +67 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_classroom_slide_dto.py +190 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_classroom_slide_dto_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_classroom_test_dto.py +162 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_classroom_video_dto.py +108 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_api_key_req_dto.py +207 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_api_key_req_dto_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_asset_upload_req_dto.py +121 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_asset_upload_req_dto_purpose.py +13 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_chapter_req_dto.py +86 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_expansion_req_dto.py +155 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_expansion_req_dto_payload.py +66 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_lesson_req_dto.py +143 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_lesson_req_dto_submission.py +70 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_module_req_dto.py +281 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_module_req_dto_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_module_req_dto_type.py +19 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_webhook_req_dto.py +132 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_content_webhook_req_dto_mode.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_update_classroom_test_dto.py +449 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_update_classroom_test_dto_metadata_type_0.py +66 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/create_update_classroom_test_item_dto.py +248 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/enroll_content_learners_request.py +91 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/export_classroom_content_req_dto.py +90 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/game_visibility.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/gamesortfields.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/generate_game_brief_dto.py +78 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/generate_simulation_brief_dto.py +78 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/invite_content_learner_request.py +148 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/order.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/passing_value_type.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/problem_type.py +11 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/quiz_type.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/remotion_template_type.py +13 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/reorder_content_chapters_req_dto.py +91 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/reorder_content_lessons_req_dto.py +91 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/reorder_scenes_dto.py +91 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/revise_game_dto.py +76 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/revise_simulation_dto.py +76 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/rotate_content_api_key_req_dto.py +79 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/rotate_content_webhook_secret_req_dto.py +88 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/simulation_visibility.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/simulationsortfields.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/simulationsubjects.py +13 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/slide_visibility.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/test_layout_type.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/test_period_type.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_course_settings_dto.py +114 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_course_settings_dto_ai_tutor_defaults.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_game_dto.py +205 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_game_dto_brief_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_game_dto_metadata_type_0.py +67 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_simulation_dto.py +196 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_simulation_dto_brief_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_simulation_dto_metadata_type_0.py +67 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_slide_dto.py +187 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_slide_dto_metadata_type_0.py +66 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_video_dto.py +235 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_classroom_video_dto_metadata_type_0.py +66 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_content_api_key_req_dto.py +217 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_content_chapter_req_dto.py +87 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_content_course_req_dto.py +186 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_content_course_req_dto_visibility.py +11 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_content_lesson_req_dto.py +196 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_content_lesson_req_dto_submission.py +70 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_content_webhook_req_dto.py +150 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_standalone_lesson_dto.py +274 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_standalone_lesson_dto_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_standalone_lesson_dto_type.py +19 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto.py +758 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_transition_out_type_1.py +12 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_transition_out_type_2_type_1.py +12 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_transition_out_type_3_type_1.py +12 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_video_effect_type_1.py +14 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_video_effect_type_2_type_1.py +14 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_video_effect_type_3_type_1.py +14 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_effect_type_1.py +14 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_effect_type_2_type_1.py +14 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_effect_type_3_type_1.py +14 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_fit.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_metadata_type_0.py +64 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_source_type_1.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_source_type_2_type_1.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_source_type_3_type_1.py +10 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_type_type_1.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_type_type_2_type_1.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/update_video_scene_dto_visual_type_type_3_type_1.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/video_clip_source.py +8 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/models/video_visibility.py +9 -0
- tutorflow_content-0.1.0/tutorflow_content/generated/types.py +53 -0
- tutorflow_content-0.1.0/tutorflow_content/pagination.py +48 -0
- tutorflow_content-0.1.0/tutorflow_content/retry.py +129 -0
- tutorflow_content-0.1.0/tutorflow_content/runs.py +140 -0
- tutorflow_content-0.1.0/tutorflow_content/webhooks.py +97 -0
- tutorflow_content-0.1.0/tutorflow_content.egg-info/PKG-INFO +151 -0
- tutorflow_content-0.1.0/tutorflow_content.egg-info/SOURCES.txt +515 -0
- tutorflow_content-0.1.0/tutorflow_content.egg-info/dependency_links.txt +1 -0
- tutorflow_content-0.1.0/tutorflow_content.egg-info/requires.txt +7 -0
- tutorflow_content-0.1.0/tutorflow_content.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tutorflow-content
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for the TutorFlow Content API: generated routes and models, retries, pagination, run polling and webhook verification.
|
|
5
|
+
License: Proprietary
|
|
6
|
+
Project-URL: Homepage, https://tutorflow.io/resources/integrations/content-api-sdks
|
|
7
|
+
Project-URL: Documentation, https://tutorflow.io/resources/integrations/content-api-sdks
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
12
|
+
Classifier: Typing :: Typed
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: httpx<1,>=0.27
|
|
16
|
+
Requires-Dist: attrs>=22.2
|
|
17
|
+
Requires-Dist: python-dateutil>=2.8
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
20
|
+
Requires-Dist: openapi-python-client==0.29.1; extra == "dev"
|
|
21
|
+
|
|
22
|
+
# tutorflow-content
|
|
23
|
+
|
|
24
|
+
A Python client for the TutorFlow Content API (`/v1/content`). Routes and models are generated from the published OpenAPI document with [openapi-python-client](https://github.com/openapi-generators/openapi-python-client), and the package adds what an integration otherwise writes by hand: retries, pagination, waiting for runs, typed errors, and webhook verification. It is built on httpx.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
Requires Python 3.11 or later.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install tutorflow-content
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Documentation is at [tutorflow.io/resources/integrations/content-api-sdks](https://tutorflow.io/resources/integrations/content-api-sdks).
|
|
35
|
+
|
|
36
|
+
## Create a client
|
|
37
|
+
|
|
38
|
+
Keep the key on your server.
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import os
|
|
42
|
+
|
|
43
|
+
from tutorflow_content import create_client, unwrap
|
|
44
|
+
from tutorflow_content.generated.api.content_classrooms import (
|
|
45
|
+
content_integration_classroom_controller_list_classrooms as list_classrooms,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
client = create_client(os.environ["TUTORFLOW_CONTENT_API_KEY"])
|
|
49
|
+
|
|
50
|
+
for classroom in unwrap(list_classrooms.sync_detailed(client=client)):
|
|
51
|
+
print(classroom.id, classroom.name, classroom.is_sandbox)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Every route is a module under `tutorflow_content.generated.api.<tag>` with `sync`, `sync_detailed`, `asyncio` and `asyncio_detailed`. `unwrap()` takes a `*_detailed` response and returns the parsed model, or raises `ContentApiError` with `code`, `status`, `request_id`, `retry_after_seconds`, `current_etag` and `details` from the error envelope. A 412 raises its subclass `ContentPreconditionFailedError`.
|
|
55
|
+
|
|
56
|
+
A test key (`tf_content_test_...`) works the same way against the sandbox classroom; `is_test_key(api_key)` tells them apart.
|
|
57
|
+
|
|
58
|
+
## Retries and idempotency
|
|
59
|
+
|
|
60
|
+
`create_client()` installs `RetryTransport` (and `AsyncRetryTransport` for the async calls):
|
|
61
|
+
|
|
62
|
+
- `429`: always retried, after `Retry-After` (up to 60 seconds; a longer wait comes back to you as the 429).
|
|
63
|
+
- `500`, `502`, `503`, `504` and connection failures: retried only for `GET`, `PUT`, `DELETE` and requests that carry an `Idempotency-Key`, so a change is never applied twice.
|
|
64
|
+
|
|
65
|
+
Backoff starts at 0.5 s and doubles with jitter, two retries by default. Tune it with `create_client(key, retry=RetryConfig(max_retries=4))` or turn it off with `retry=None`.
|
|
66
|
+
|
|
67
|
+
Routes that accept an `Idempotency-Key` take it as the `idempotency_key=` argument. Every retry sends the same key, and the API replays the first answer:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from tutorflow_content import new_idempotency_key
|
|
71
|
+
from tutorflow_content.generated.api.content_generation import content_generation_controller_generate_course as generate_course
|
|
72
|
+
from tutorflow_content.generated.models import ContentGenerateCourseRequest
|
|
73
|
+
|
|
74
|
+
accepted = unwrap(
|
|
75
|
+
generate_course.sync_detailed(
|
|
76
|
+
classroom_id,
|
|
77
|
+
client=client,
|
|
78
|
+
body=ContentGenerateCourseRequest(topic="Fractions for Grade 5", lesson_count=6, language="en"),
|
|
79
|
+
idempotency_key=new_idempotency_key(),
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Pagination
|
|
85
|
+
|
|
86
|
+
`paginate()` yields every item of a list and reads both envelopes: page-numbered lists get the next `page=`, cursor lists get `cursor=` from `meta.nextCursor`. Pass a function that takes those keyword arguments:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from tutorflow_content import paginate
|
|
90
|
+
from tutorflow_content.generated.api.content_courses import content_resource_course_controller_list as list_courses
|
|
91
|
+
|
|
92
|
+
for course in paginate(lambda **next: list_courses.sync_detailed(classroom_id, client=client, limit=100, **next)):
|
|
93
|
+
print(course.id, course.title)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Waiting for runs
|
|
97
|
+
|
|
98
|
+
These poll until the run settles and return its final status. They do not raise when the run failed; check the status.
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from tutorflow_content import wait_for_build, wait_for_render, wait_for_run
|
|
102
|
+
|
|
103
|
+
run = wait_for_run(client, classroom_id=classroom_id, run_id=accepted.run_id) # tests, modules, courses, slides
|
|
104
|
+
if str(run.status) == "failed":
|
|
105
|
+
raise RuntimeError(run.error)
|
|
106
|
+
|
|
107
|
+
build = wait_for_build(client, kind="game", classroom_id=classroom_id, resource_id=game_id, run_id=run_id)
|
|
108
|
+
render = wait_for_render(client, classroom_id=classroom_id, video_id=video_id)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Options: `interval` (default 3 s), `timeout` (default 30 minutes), `on_poll`. The build status route only knows runs of the server that answers since its last deploy; a run it no longer tracks raises `ContentWaitError` with `reason == "run_not_tracked"`, and the run's webhook is the signal to rely on.
|
|
112
|
+
|
|
113
|
+
## Webhooks
|
|
114
|
+
|
|
115
|
+
`verify_webhook()` checks the `X-Content-Integration-Signature-V2` header against the raw body and returns the event as a dict, with `livemode` always present:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from flask import Flask, abort, request
|
|
119
|
+
|
|
120
|
+
from tutorflow_content import WebhookVerificationError, verify_webhook
|
|
121
|
+
|
|
122
|
+
app = Flask(__name__)
|
|
123
|
+
SECRETS = [s for s in (os.environ.get("TUTORFLOW_WEBHOOK_SECRET"), os.environ.get("TUTORFLOW_WEBHOOK_SECRET_PREVIOUS")) if s]
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@app.post("/tutorflow/webhooks")
|
|
127
|
+
def tutorflow_webhook():
|
|
128
|
+
try:
|
|
129
|
+
event = verify_webhook(request.get_data(), request.headers.get("X-Content-Integration-Signature-V2"), SECRETS)
|
|
130
|
+
except WebhookVerificationError as error:
|
|
131
|
+
abort(400, error.reason)
|
|
132
|
+
|
|
133
|
+
if event["event"]["type"] == "course.generation.completed" and event["livemode"]:
|
|
134
|
+
queue_course_import(event["event"]["id"], event["resourceId"])
|
|
135
|
+
return "", 204
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
It accepts the request when `t` is within `tolerance_seconds` (default 300) of now and any `v1=` signature matches any secret you pass. During TutorFlow's secret rotation grace period the header carries a signature for the new and the previous secret. Deliveries are signed again on every retry.
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
cd sdks/python
|
|
144
|
+
python -m venv .venv && . .venv/bin/activate
|
|
145
|
+
pip install -e ".[dev]"
|
|
146
|
+
pytest # httpx.MockTransport, no network
|
|
147
|
+
python scripts/generate.py # rewrite tutorflow_content/generated
|
|
148
|
+
python scripts/generate.py --check # exit 1 when it is stale against openapi.json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`tutorflow_content/generated` comes from `apps/frontend-admin/public/resources/integrations/openapi.json`, which the backend writes (`pnpm run openapi:content -- <path>` in tutorflow-backend). Regenerate after that file changes and commit both. Do not edit the generated code by hand.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# tutorflow-content
|
|
2
|
+
|
|
3
|
+
A Python client for the TutorFlow Content API (`/v1/content`). Routes and models are generated from the published OpenAPI document with [openapi-python-client](https://github.com/openapi-generators/openapi-python-client), and the package adds what an integration otherwise writes by hand: retries, pagination, waiting for runs, typed errors, and webhook verification. It is built on httpx.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Requires Python 3.11 or later.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install tutorflow-content
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Documentation is at [tutorflow.io/resources/integrations/content-api-sdks](https://tutorflow.io/resources/integrations/content-api-sdks).
|
|
14
|
+
|
|
15
|
+
## Create a client
|
|
16
|
+
|
|
17
|
+
Keep the key on your server.
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import os
|
|
21
|
+
|
|
22
|
+
from tutorflow_content import create_client, unwrap
|
|
23
|
+
from tutorflow_content.generated.api.content_classrooms import (
|
|
24
|
+
content_integration_classroom_controller_list_classrooms as list_classrooms,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
client = create_client(os.environ["TUTORFLOW_CONTENT_API_KEY"])
|
|
28
|
+
|
|
29
|
+
for classroom in unwrap(list_classrooms.sync_detailed(client=client)):
|
|
30
|
+
print(classroom.id, classroom.name, classroom.is_sandbox)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Every route is a module under `tutorflow_content.generated.api.<tag>` with `sync`, `sync_detailed`, `asyncio` and `asyncio_detailed`. `unwrap()` takes a `*_detailed` response and returns the parsed model, or raises `ContentApiError` with `code`, `status`, `request_id`, `retry_after_seconds`, `current_etag` and `details` from the error envelope. A 412 raises its subclass `ContentPreconditionFailedError`.
|
|
34
|
+
|
|
35
|
+
A test key (`tf_content_test_...`) works the same way against the sandbox classroom; `is_test_key(api_key)` tells them apart.
|
|
36
|
+
|
|
37
|
+
## Retries and idempotency
|
|
38
|
+
|
|
39
|
+
`create_client()` installs `RetryTransport` (and `AsyncRetryTransport` for the async calls):
|
|
40
|
+
|
|
41
|
+
- `429`: always retried, after `Retry-After` (up to 60 seconds; a longer wait comes back to you as the 429).
|
|
42
|
+
- `500`, `502`, `503`, `504` and connection failures: retried only for `GET`, `PUT`, `DELETE` and requests that carry an `Idempotency-Key`, so a change is never applied twice.
|
|
43
|
+
|
|
44
|
+
Backoff starts at 0.5 s and doubles with jitter, two retries by default. Tune it with `create_client(key, retry=RetryConfig(max_retries=4))` or turn it off with `retry=None`.
|
|
45
|
+
|
|
46
|
+
Routes that accept an `Idempotency-Key` take it as the `idempotency_key=` argument. Every retry sends the same key, and the API replays the first answer:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from tutorflow_content import new_idempotency_key
|
|
50
|
+
from tutorflow_content.generated.api.content_generation import content_generation_controller_generate_course as generate_course
|
|
51
|
+
from tutorflow_content.generated.models import ContentGenerateCourseRequest
|
|
52
|
+
|
|
53
|
+
accepted = unwrap(
|
|
54
|
+
generate_course.sync_detailed(
|
|
55
|
+
classroom_id,
|
|
56
|
+
client=client,
|
|
57
|
+
body=ContentGenerateCourseRequest(topic="Fractions for Grade 5", lesson_count=6, language="en"),
|
|
58
|
+
idempotency_key=new_idempotency_key(),
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Pagination
|
|
64
|
+
|
|
65
|
+
`paginate()` yields every item of a list and reads both envelopes: page-numbered lists get the next `page=`, cursor lists get `cursor=` from `meta.nextCursor`. Pass a function that takes those keyword arguments:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from tutorflow_content import paginate
|
|
69
|
+
from tutorflow_content.generated.api.content_courses import content_resource_course_controller_list as list_courses
|
|
70
|
+
|
|
71
|
+
for course in paginate(lambda **next: list_courses.sync_detailed(classroom_id, client=client, limit=100, **next)):
|
|
72
|
+
print(course.id, course.title)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Waiting for runs
|
|
76
|
+
|
|
77
|
+
These poll until the run settles and return its final status. They do not raise when the run failed; check the status.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from tutorflow_content import wait_for_build, wait_for_render, wait_for_run
|
|
81
|
+
|
|
82
|
+
run = wait_for_run(client, classroom_id=classroom_id, run_id=accepted.run_id) # tests, modules, courses, slides
|
|
83
|
+
if str(run.status) == "failed":
|
|
84
|
+
raise RuntimeError(run.error)
|
|
85
|
+
|
|
86
|
+
build = wait_for_build(client, kind="game", classroom_id=classroom_id, resource_id=game_id, run_id=run_id)
|
|
87
|
+
render = wait_for_render(client, classroom_id=classroom_id, video_id=video_id)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Options: `interval` (default 3 s), `timeout` (default 30 minutes), `on_poll`. The build status route only knows runs of the server that answers since its last deploy; a run it no longer tracks raises `ContentWaitError` with `reason == "run_not_tracked"`, and the run's webhook is the signal to rely on.
|
|
91
|
+
|
|
92
|
+
## Webhooks
|
|
93
|
+
|
|
94
|
+
`verify_webhook()` checks the `X-Content-Integration-Signature-V2` header against the raw body and returns the event as a dict, with `livemode` always present:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from flask import Flask, abort, request
|
|
98
|
+
|
|
99
|
+
from tutorflow_content import WebhookVerificationError, verify_webhook
|
|
100
|
+
|
|
101
|
+
app = Flask(__name__)
|
|
102
|
+
SECRETS = [s for s in (os.environ.get("TUTORFLOW_WEBHOOK_SECRET"), os.environ.get("TUTORFLOW_WEBHOOK_SECRET_PREVIOUS")) if s]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@app.post("/tutorflow/webhooks")
|
|
106
|
+
def tutorflow_webhook():
|
|
107
|
+
try:
|
|
108
|
+
event = verify_webhook(request.get_data(), request.headers.get("X-Content-Integration-Signature-V2"), SECRETS)
|
|
109
|
+
except WebhookVerificationError as error:
|
|
110
|
+
abort(400, error.reason)
|
|
111
|
+
|
|
112
|
+
if event["event"]["type"] == "course.generation.completed" and event["livemode"]:
|
|
113
|
+
queue_course_import(event["event"]["id"], event["resourceId"])
|
|
114
|
+
return "", 204
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
It accepts the request when `t` is within `tolerance_seconds` (default 300) of now and any `v1=` signature matches any secret you pass. During TutorFlow's secret rotation grace period the header carries a signature for the new and the previous secret. Deliveries are signed again on every retry.
|
|
118
|
+
|
|
119
|
+
## Development
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
cd sdks/python
|
|
123
|
+
python -m venv .venv && . .venv/bin/activate
|
|
124
|
+
pip install -e ".[dev]"
|
|
125
|
+
pytest # httpx.MockTransport, no network
|
|
126
|
+
python scripts/generate.py # rewrite tutorflow_content/generated
|
|
127
|
+
python scripts/generate.py --check # exit 1 when it is stale against openapi.json
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`tutorflow_content/generated` comes from `apps/frontend-admin/public/resources/integrations/openapi.json`, which the backend writes (`pnpm run openapi:content -- <path>` in tutorflow-backend). Regenerate after that file changes and commit both. Do not edit the generated code by hand.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tutorflow-content"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python client for the TutorFlow Content API: generated routes and models, retries, pagination, run polling and webhook verification."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "Proprietary" }
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Topic :: Software Development :: Libraries",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"httpx>=0.27,<1",
|
|
21
|
+
"attrs>=22.2",
|
|
22
|
+
"python-dateutil>=2.8",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://tutorflow.io/resources/integrations/content-api-sdks"
|
|
27
|
+
Documentation = "https://tutorflow.io/resources/integrations/content-api-sdks"
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = [
|
|
31
|
+
"pytest>=8",
|
|
32
|
+
"openapi-python-client==0.29.1",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.packages.find]
|
|
36
|
+
include = ["tutorflow_content*"]
|
|
37
|
+
|
|
38
|
+
[tool.pytest.ini_options]
|
|
39
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from tutorflow_content import ContentApiError, ContentPreconditionFailedError, error_from_response
|
|
6
|
+
|
|
7
|
+
from .conftest import error_response, json_response
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_reads_every_field_of_the_error_envelope():
|
|
11
|
+
# Arrange
|
|
12
|
+
response = error_response(429, "content_rate_limit_exceeded", {"retryAfterSeconds": 30}, {"Retry-After": "30"})
|
|
13
|
+
|
|
14
|
+
# Act
|
|
15
|
+
error = error_from_response(response)
|
|
16
|
+
|
|
17
|
+
# Assert
|
|
18
|
+
assert isinstance(error, ContentApiError)
|
|
19
|
+
assert (error.code, error.status, error.request_id, error.retry_after_seconds) == ("content_rate_limit_exceeded", 429, "req-err", 30.0)
|
|
20
|
+
assert str(error) == "content_rate_limit_exceeded happened"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_a_412_is_a_precondition_failure_with_the_current_etag():
|
|
24
|
+
# Act
|
|
25
|
+
error = error_from_response(error_response(412, "content_precondition_failed", {"currentETag": '"v13"'}))
|
|
26
|
+
|
|
27
|
+
# Assert
|
|
28
|
+
assert isinstance(error, ContentPreconditionFailedError)
|
|
29
|
+
assert error.current_etag == '"v13"'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_falls_back_to_status_and_headers_without_an_envelope():
|
|
33
|
+
# Arrange
|
|
34
|
+
response = json_response(502, "<html>Bad gateway</html>", {"Request-Id": "edge-9"})
|
|
35
|
+
|
|
36
|
+
# Act
|
|
37
|
+
error = error_from_response(response)
|
|
38
|
+
|
|
39
|
+
# Assert
|
|
40
|
+
assert (error.code, error.status, error.request_id) == ("content_http_error", 502, "edge-9")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_unwrap_raises_for_a_generated_error_response():
|
|
44
|
+
# Arrange
|
|
45
|
+
from tutorflow_content import unwrap
|
|
46
|
+
|
|
47
|
+
class Detailed:
|
|
48
|
+
status_code = 404
|
|
49
|
+
content = b'{"error":{"code":"content_not_found","message":"Not found","status":404,"requestId":"r"}}'
|
|
50
|
+
headers = {}
|
|
51
|
+
parsed = None
|
|
52
|
+
|
|
53
|
+
# Act and Assert
|
|
54
|
+
with pytest.raises(ContentApiError) as raised:
|
|
55
|
+
unwrap(Detailed())
|
|
56
|
+
assert raised.value.code == "content_not_found"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from urllib.parse import parse_qs
|
|
4
|
+
|
|
5
|
+
from tutorflow_content import create_client, paginate
|
|
6
|
+
from tutorflow_content.generated.api.content_courses import content_resource_course_controller_list as list_courses
|
|
7
|
+
from tutorflow_content.generated.api.content_generation import content_generation_controller_list_runs as list_runs
|
|
8
|
+
|
|
9
|
+
from .conftest import API_KEY, BASE_URL, CLASSROOM_ID, ScriptedTransport, json_response
|
|
10
|
+
|
|
11
|
+
COURSE = {
|
|
12
|
+
"classroomId": CLASSROOM_ID,
|
|
13
|
+
"title": "Fractions",
|
|
14
|
+
"description": None,
|
|
15
|
+
"thumbnail": None,
|
|
16
|
+
"slug": "fractions",
|
|
17
|
+
"level": None,
|
|
18
|
+
"visibility": "PRIVATE",
|
|
19
|
+
"createdAt": "2026-09-01T09:00:00.000Z",
|
|
20
|
+
"updatedAt": "2026-09-02T14:30:00.000Z",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def numbered_page(page: int, ids: list[str], has_next: bool) -> dict:
|
|
25
|
+
data = [{**COURSE, "id": course_id} for course_id in ids]
|
|
26
|
+
meta = {"page": page, "take": 2, "itemCount": 3, "pageCount": 2, "hasPreviousPage": page > 1, "hasNextPage": has_next}
|
|
27
|
+
return {"data": data, "meta": meta, "items": data, "totalCount": 3}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def run(run_id: str) -> dict:
|
|
31
|
+
return {
|
|
32
|
+
"runId": run_id,
|
|
33
|
+
"kind": "course",
|
|
34
|
+
"classroomId": CLASSROOM_ID,
|
|
35
|
+
"status": "completed",
|
|
36
|
+
"stage": None,
|
|
37
|
+
"progress": {"completed": 1, "total": 1, "unit": "lesson"},
|
|
38
|
+
"resourceId": None,
|
|
39
|
+
"creditsCharged": 3,
|
|
40
|
+
"estimatedCredits": 3,
|
|
41
|
+
"error": None,
|
|
42
|
+
"resumedFromRunId": None,
|
|
43
|
+
"createdAt": "2026-09-01T09:00:00.000Z",
|
|
44
|
+
"finishedAt": None,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def query(request) -> dict:
|
|
49
|
+
return {key: values[0] for key, values in parse_qs(request.url.query.decode()).items()}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_walks_a_page_numbered_list_with_the_next_page_number():
|
|
53
|
+
# Arrange
|
|
54
|
+
ids = ["3f6c2a1e-7b4d-4c8e-9a10-00000000000" + str(index) for index in range(1, 4)]
|
|
55
|
+
scripted = ScriptedTransport(
|
|
56
|
+
[json_response(200, numbered_page(1, ids[:2], True)), json_response(200, numbered_page(2, ids[2:], False))]
|
|
57
|
+
)
|
|
58
|
+
client = create_client(API_KEY, base_url=BASE_URL, transport=scripted.transport)
|
|
59
|
+
|
|
60
|
+
# Act
|
|
61
|
+
courses = list(
|
|
62
|
+
paginate(lambda **next: list_courses.sync_detailed(CLASSROOM_ID, client=client, limit=2, **next))
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Assert
|
|
66
|
+
assert [str(course.id) for course in courses] == ids
|
|
67
|
+
assert [query(request).get("page") for request in scripted.requests] == ["1", "2"]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_walks_a_cursor_list_by_sending_next_cursor_back():
|
|
71
|
+
# Arrange
|
|
72
|
+
first = {"data": [run("3f6c2a1e-7b4d-4c8e-9a10-000000000001")], "meta": {"limit": 1, "hasNextPage": True, "nextCursor": "opaque-1"}}
|
|
73
|
+
last = {"data": [run("3f6c2a1e-7b4d-4c8e-9a10-000000000002")], "meta": {"limit": 1, "hasNextPage": False, "nextCursor": None}}
|
|
74
|
+
scripted = ScriptedTransport([json_response(200, first), json_response(200, last)])
|
|
75
|
+
client = create_client(API_KEY, base_url=BASE_URL, transport=scripted.transport)
|
|
76
|
+
|
|
77
|
+
# Act
|
|
78
|
+
runs = list(paginate(lambda **next: list_runs.sync_detailed(CLASSROOM_ID, client=client, limit=1, **next)))
|
|
79
|
+
|
|
80
|
+
# Assert
|
|
81
|
+
assert len(runs) == 2
|
|
82
|
+
assert "cursor" not in query(scripted.requests[0])
|
|
83
|
+
assert query(scripted.requests[1])["cursor"] == "opaque-1"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_reads_plain_dict_pages_too():
|
|
87
|
+
# Arrange
|
|
88
|
+
pages = iter([{"data": [1, 2], "meta": {"hasNextPage": True, "nextCursor": "c"}}, {"data": [3], "meta": {"hasNextPage": False, "nextCursor": None}}])
|
|
89
|
+
seen: list[dict] = []
|
|
90
|
+
|
|
91
|
+
def fetch(**next):
|
|
92
|
+
seen.append(next)
|
|
93
|
+
return next_page()
|
|
94
|
+
|
|
95
|
+
next_page = lambda: next(pages) # noqa: E731
|
|
96
|
+
|
|
97
|
+
# Act
|
|
98
|
+
items = list(paginate(fetch))
|
|
99
|
+
|
|
100
|
+
# Assert
|
|
101
|
+
assert items == [1, 2, 3]
|
|
102
|
+
assert seen == [{}, {"cursor": "c"}]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from tutorflow_content import RetryConfig, RetryTransport, create_client, new_idempotency_key, unwrap
|
|
7
|
+
from tutorflow_content.generated.api.content_classrooms import content_integration_classroom_controller_list_classrooms as list_classrooms
|
|
8
|
+
|
|
9
|
+
from .conftest import API_KEY, BASE_URL, ScriptedTransport, error_response, json_response
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def client_with(scripted: ScriptedTransport, sleep, **config):
|
|
13
|
+
transport = RetryTransport(scripted.transport, RetryConfig(random=lambda: 0.5, **config), sleep=sleep)
|
|
14
|
+
return httpx.Client(base_url=BASE_URL, transport=transport)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_waits_out_retry_after_on_429_and_sends_again(sleep):
|
|
18
|
+
# Arrange
|
|
19
|
+
scripted = ScriptedTransport([error_response(429, "content_rate_limit_exceeded", headers={"Retry-After": "2"}), json_response(200, [])])
|
|
20
|
+
client = client_with(scripted, sleep)
|
|
21
|
+
|
|
22
|
+
# Act
|
|
23
|
+
response = client.get("/v1/content/classrooms")
|
|
24
|
+
|
|
25
|
+
# Assert
|
|
26
|
+
assert response.status_code == 200
|
|
27
|
+
assert sleep.delays == [2.0]
|
|
28
|
+
assert len(scripted.requests) == 2
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_reuses_the_idempotency_key_and_body_after_a_503(sleep):
|
|
32
|
+
# Arrange
|
|
33
|
+
scripted = ScriptedTransport([error_response(503, "content_internal_error"), json_response(202, {"runId": "r1"})])
|
|
34
|
+
client = client_with(scripted, sleep)
|
|
35
|
+
|
|
36
|
+
# Act
|
|
37
|
+
response = client.post("/v1/content/x", json={"topic": "Fractions"}, headers={"Idempotency-Key": "key-1"})
|
|
38
|
+
|
|
39
|
+
# Assert
|
|
40
|
+
assert response.status_code == 202
|
|
41
|
+
assert [request.headers["Idempotency-Key"] for request in scripted.requests] == ["key-1", "key-1"]
|
|
42
|
+
assert scripted.requests[1].content == b'{"topic":"Fractions"}'
|
|
43
|
+
assert sleep.delays == [0.25]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_does_not_repeat_a_post_without_a_key_after_a_500(sleep):
|
|
47
|
+
# Arrange
|
|
48
|
+
scripted = ScriptedTransport([error_response(500, "content_internal_error")])
|
|
49
|
+
client = client_with(scripted, sleep)
|
|
50
|
+
|
|
51
|
+
# Act
|
|
52
|
+
response = client.post("/v1/content/x", json={})
|
|
53
|
+
|
|
54
|
+
# Assert
|
|
55
|
+
assert response.status_code == 500
|
|
56
|
+
assert len(scripted.requests) == 1
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_retries_a_get_after_a_network_failure_then_gives_up(sleep):
|
|
60
|
+
# Arrange
|
|
61
|
+
failure = httpx.ConnectError("refused")
|
|
62
|
+
scripted = ScriptedTransport([failure, failure, failure])
|
|
63
|
+
client = client_with(scripted, sleep, max_retries=2)
|
|
64
|
+
|
|
65
|
+
# Act and Assert
|
|
66
|
+
with pytest.raises(httpx.ConnectError):
|
|
67
|
+
client.get("/v1/content/classrooms")
|
|
68
|
+
assert len(scripted.requests) == 3
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_hands_back_a_429_that_asks_for_too_long_a_wait(sleep):
|
|
72
|
+
# Arrange
|
|
73
|
+
scripted = ScriptedTransport([error_response(429, "content_rate_limit_exceeded", headers={"Retry-After": "120"})])
|
|
74
|
+
client = client_with(scripted, sleep)
|
|
75
|
+
|
|
76
|
+
# Act
|
|
77
|
+
response = client.get("/v1/content/classrooms")
|
|
78
|
+
|
|
79
|
+
# Assert
|
|
80
|
+
assert response.status_code == 429
|
|
81
|
+
assert sleep.delays == []
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_create_client_sends_the_bearer_key_through_the_generated_calls():
|
|
85
|
+
# Arrange
|
|
86
|
+
scripted = ScriptedTransport([json_response(200, [{"id": "3f6c2a1e-7b4d-4c8e-9a10-0000000c0001", "name": "Grade 5 maths", "slug": "grade-5-maths", "locale": "en", "isSandbox": True}])])
|
|
87
|
+
client = create_client(API_KEY, base_url=f"{BASE_URL}/", transport=scripted.transport, retry=None)
|
|
88
|
+
|
|
89
|
+
# Act
|
|
90
|
+
classrooms = unwrap(list_classrooms.sync_detailed(client=client))
|
|
91
|
+
|
|
92
|
+
# Assert
|
|
93
|
+
assert classrooms[0].name == "Grade 5 maths"
|
|
94
|
+
assert scripted.requests[0].headers["Authorization"] == f"Bearer {API_KEY}"
|
|
95
|
+
assert str(scripted.requests[0].url) == f"{BASE_URL}/v1/content/classrooms"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_create_client_refuses_a_value_that_is_not_a_content_api_key():
|
|
99
|
+
# Act and Assert
|
|
100
|
+
with pytest.raises(ValueError):
|
|
101
|
+
create_client("sk_live_wrong")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_new_idempotency_key_is_a_fresh_uuid():
|
|
105
|
+
# Act
|
|
106
|
+
first, second = new_idempotency_key(), new_idempotency_key()
|
|
107
|
+
|
|
108
|
+
# Assert
|
|
109
|
+
assert first != second
|
|
110
|
+
assert len(first) == 36
|