clawed 2.5.1__tar.gz → 2.5.2__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.
- {clawed-2.5.1 → clawed-2.5.2}/PKG-INFO +1 -1
- {clawed-2.5.1 → clawed-2.5.2}/clawed/__init__.py +1 -1
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/generate.py +6 -2
- {clawed-2.5.1 → clawed-2.5.2}/clawed/compile_game.py +197 -19
- {clawed-2.5.1 → clawed-2.5.2}/clawed/export_docx.py +17 -3
- {clawed-2.5.1 → clawed-2.5.2}/clawed/export_pptx.py +369 -118
- {clawed-2.5.1 → clawed-2.5.2}/clawed/model_router.py +12 -13
- {clawed-2.5.1 → clawed-2.5.2}/clawed/multi_agent.py +2 -2
- {clawed-2.5.1 → clawed-2.5.2}/clawed/slide_images.py +285 -52
- {clawed-2.5.1 → clawed-2.5.2}/pyproject.toml +1 -1
- {clawed-2.5.1 → clawed-2.5.2}/.gitignore +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/LICENSE +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/README.md +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/__main__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/_legacy_gateway.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/approvals.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/autonomy.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/context.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/core.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/custom_tools.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/drive/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/drive/auth.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/drive/client.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/fake_llm.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/loop.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/curriculum.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/curriculum_kb.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/embeddings.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/episodes.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/identity.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/loader.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/preferences.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/planner.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/prompt.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/scheduler.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/base.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/configure_profile.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/curriculum_map.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_create_doc.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_create_slides.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_list.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_organize.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_read.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_upload.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/export_document.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/gap_analysis.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/generate_assessment.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/generate_lesson.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/generate_lesson_bundle.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/generate_materials.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/generate_unit.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/ingest_materials.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/parent_comm.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/read_heartbeat.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/read_workspace.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/request_approval.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/schedule_task.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/search_lessons.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/search_my_materials.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/search_standards.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/student_insights.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/sub_packet.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/switch_model.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/update_soul.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/analytics.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/deps.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/chat.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/export.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/feedback.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/gateway_chat.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/generate.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/ingest.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/lessons.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/school.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/settings.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/tools.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/server.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/static/app.js +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/static/style.css +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/static/widget.js +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/analytics.html +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/base.html +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/dashboard.html +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/generate.html +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/index.html +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/lesson.html +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/profile.html +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/settings.html +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/stats.html +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/students.html +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/assessment.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/asset_registry.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/async_utils.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/auth/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/auth/google_auth.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/bot_state.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/chat.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/cli.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/cli_chat.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/_helpers.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/bot.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/config.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/config_llm.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/config_profile.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/export.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/game.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/generate_assessment.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/generate_unit.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/queue.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/schedule_cmd.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/sub.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/train.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/workspace_cmd.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/compile_slides.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/compile_student.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/compile_teacher.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/config.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/corpus.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/curriculum_map.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/database.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_assessment.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_formative_assessment.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_lesson_materials.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_lesson_science_g6.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_lesson_social_studies_g8.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_master_content.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_pacing_guide.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_quiz.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_rubric.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_unit_plan.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_year_map.json +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/differentiation.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/doc_export.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/drive.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/evaluation.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/export_handout.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/export_markdown.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/export_pdf.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/export_templates.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/export_theme.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/exporter.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/failure_codes.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/feedback.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/formats/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/formats/flipchart.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/formats/notebook.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/formats/xbk.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/gateway.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/gateway_response.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/generation.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/generation_report.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/export.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/feedback.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/gaps.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/generate.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/ingest.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/misc.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/onboard.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/schedule.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/standards.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/hermes_plugin.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/image_pipeline.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/improver.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/ingestor.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/io.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/lesson.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/llm.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/master_content.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/materials.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/mcp_server.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/memory_engine.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/models.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/onboarding.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/openclaw_plugin.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/parent_comm.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/persona.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/persona_evolution.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/planner.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/504_accommodations.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/admin_lesson_plan.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/assessment.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/curriculum_gaps.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/dbq_assessment.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/differentiation.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/formative_assessment.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/iep_modification.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/lesson_plan.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/master_content.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/multi_agent_researcher.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/multi_agent_reviewer.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/pacing_guide.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/parent_note.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/persona_extract.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/quiz.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/rubric.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/student_packet.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/sub_packet.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/summative_assessment.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/tiered_assignments.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/unit_plan.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/worksheet.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/year_map.txt +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/quality.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/reading_report.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/router.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/sanitize.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/scheduler.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/school.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/search.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/art.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/base.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/computer_science.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/ela.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/foreign_language.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/history.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/library.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/math.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/music.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/physical_education.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/science.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/social_studies.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/special_education.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/standards.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/state.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/state_standards.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/student_bot.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/student_cli.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/student_telegram_bot.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/sub_packet.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/task_queue.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/templates_lib.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/tools.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/cli.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/hermes.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/openclaw.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/student_telegram.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/telegram.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/web.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/tui.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/tui_chat.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/validation.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/voice.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/voice_check.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/clawed/workspace.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/eduagent/__init__.py +0 -0
- {clawed-2.5.1 → clawed-2.5.2}/eduagent/_compat.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clawed
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.2
|
|
4
4
|
Summary: Free AI lesson planner for teachers. Learns your teaching voice from your curriculum files and generates complete lesson packages. Open source, runs locally, no subscription.
|
|
5
5
|
Project-URL: Homepage, https://sirhanmacx.github.io/Claw-ED
|
|
6
6
|
Project-URL: Documentation, https://github.com/SirhanMacx/Claw-ED#readme
|
|
@@ -17,7 +17,7 @@ if hasattr(sys.stderr, "reconfigure"):
|
|
|
17
17
|
except Exception:
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
|
-
__version__ = "2.5.
|
|
20
|
+
__version__ = "2.5.2"
|
|
21
21
|
__author__ = "Jon Maccarello & Claw-ED contributors"
|
|
22
22
|
__description__ = "Personal AI teaching agent. Learns your voice, works while you sleep."
|
|
23
23
|
|
|
@@ -306,7 +306,10 @@ def lesson(
|
|
|
306
306
|
False, "--game/--no-game",
|
|
307
307
|
help="Also generate an interactive HTML learning game",
|
|
308
308
|
),
|
|
309
|
-
fmt: str = typer.Option(
|
|
309
|
+
fmt: str = typer.Option(
|
|
310
|
+
"handout", "--format", "-f",
|
|
311
|
+
help="Export: handout, docx, pptx, pdf, markdown",
|
|
312
|
+
),
|
|
310
313
|
):
|
|
311
314
|
"""Generate a detailed daily lesson plan.
|
|
312
315
|
|
|
@@ -408,13 +411,14 @@ def lesson(
|
|
|
408
411
|
from clawed.compile_teacher import compile_teacher_view
|
|
409
412
|
from clawed.multi_agent import multi_agent_generate_master_content
|
|
410
413
|
console.print("[dim]Using multi-agent pipeline (researcher→writer→reviewer)...[/dim]")
|
|
414
|
+
_ma_config = AppConfig.load()
|
|
411
415
|
master = _run_async(
|
|
412
416
|
multi_agent_generate_master_content(
|
|
413
417
|
topic=topic,
|
|
414
418
|
grade=grade,
|
|
415
419
|
subject=subject,
|
|
416
420
|
persona=persona,
|
|
417
|
-
config=
|
|
421
|
+
config=_ma_config,
|
|
418
422
|
unit_context=kb_prompt_section,
|
|
419
423
|
)
|
|
420
424
|
)
|
|
@@ -20,6 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
import logging
|
|
21
21
|
import re
|
|
22
22
|
from pathlib import Path
|
|
23
|
+
from typing import Any
|
|
23
24
|
|
|
24
25
|
from clawed.io import safe_filename
|
|
25
26
|
from clawed.llm import LLMClient
|
|
@@ -36,6 +37,11 @@ mechanic, different visual style, different interaction pattern.
|
|
|
36
37
|
|
|
37
38
|
RULES:
|
|
38
39
|
- Output ONLY the complete HTML file. No explanation, no markdown fencing.
|
|
40
|
+
- The file MUST start with <!DOCTYPE html> then <html>, then <head>, then <body>. \
|
|
41
|
+
Always use this exact structure — never put CSS or text directly inside <html> \
|
|
42
|
+
without a <head> or <body> wrapper.
|
|
43
|
+
- The <head> MUST contain: <meta charset="UTF-8">, a <title> tag, and a <style> block.
|
|
44
|
+
- The <body> contains all visible elements and <script> tags.
|
|
39
45
|
- The file must be self-contained: all CSS and JS inline.
|
|
40
46
|
- You may use a Three.js CDN link for 3D: \
|
|
41
47
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
|
@@ -59,12 +65,15 @@ WHAT MAKES A GREAT LEARNING GAME:
|
|
|
59
65
|
|
|
60
66
|
|
|
61
67
|
def _extract_game_content(master: MasterContent) -> str:
|
|
62
|
-
"""Extract educational content from MasterContent for game generation.
|
|
68
|
+
"""Extract educational content from MasterContent for game generation.
|
|
69
|
+
|
|
70
|
+
Accepts MasterContent or DailyLesson (single-agent path lacks subject/grade_level).
|
|
71
|
+
"""
|
|
63
72
|
parts = [
|
|
64
73
|
f"LESSON: {master.title}",
|
|
65
|
-
f"SUBJECT: {master
|
|
66
|
-
f"GRADE: {master
|
|
67
|
-
f"TOPIC: {master.
|
|
74
|
+
f"SUBJECT: {getattr(master, 'subject', 'Social Studies')}",
|
|
75
|
+
f"GRADE: {getattr(master, 'grade_level', '')}",
|
|
76
|
+
f"TOPIC: {getattr(master, 'topic', master.title)}",
|
|
68
77
|
f"OBJECTIVE: {master.objective}",
|
|
69
78
|
]
|
|
70
79
|
|
|
@@ -76,36 +85,187 @@ def _extract_game_content(master: MasterContent) -> str:
|
|
|
76
85
|
+ (f" (context: {v.context_sentence})" if v.context_sentence else "")
|
|
77
86
|
)
|
|
78
87
|
|
|
79
|
-
|
|
88
|
+
guided_notes = getattr(master, "guided_notes", None)
|
|
89
|
+
if guided_notes:
|
|
80
90
|
parts.append("\nKEY FACTS (fill-in-the-blank):")
|
|
81
|
-
for note in
|
|
91
|
+
for note in guided_notes:
|
|
82
92
|
parts.append(f" - Q: {note.prompt} → A: {note.answer}")
|
|
83
93
|
|
|
84
|
-
|
|
94
|
+
exit_ticket = getattr(master, "exit_ticket", None)
|
|
95
|
+
if exit_ticket:
|
|
85
96
|
parts.append("\nQUIZ QUESTIONS:")
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
# DailyLesson exit_ticket is a list of ExitTicketQuestion (has .question)
|
|
98
|
+
# MasterContent exit_ticket may differ — handle both
|
|
99
|
+
for i, q in enumerate(exit_ticket if isinstance(exit_ticket, list) else [], 1):
|
|
100
|
+
question_text = getattr(q, "question", str(q))
|
|
101
|
+
parts.append(f" {i}. {question_text}")
|
|
88
102
|
if hasattr(q, "expected_answer") and q.expected_answer:
|
|
89
103
|
parts.append(f" Answer: {q.expected_answer}")
|
|
90
104
|
|
|
91
|
-
|
|
105
|
+
primary_sources = getattr(master, "primary_sources", None)
|
|
106
|
+
if primary_sources:
|
|
92
107
|
parts.append("\nPRIMARY SOURCES:")
|
|
93
|
-
for src in
|
|
94
|
-
|
|
108
|
+
for src in primary_sources:
|
|
109
|
+
src_title = getattr(src, "title", str(src))
|
|
110
|
+
src_type = getattr(src, "source_type", "")
|
|
111
|
+
parts.append(f" - {src_title} ({src_type})")
|
|
95
112
|
if hasattr(src, "content_text") and src.content_text:
|
|
96
113
|
parts.append(f" Text: {src.content_text[:300]}...")
|
|
97
114
|
|
|
98
|
-
|
|
115
|
+
direct_instruction = getattr(master, "direct_instruction", None)
|
|
116
|
+
if direct_instruction and not isinstance(direct_instruction, str):
|
|
99
117
|
parts.append("\nKEY CONCEPTS:")
|
|
100
|
-
for section in
|
|
101
|
-
parts.append(f" - {section
|
|
118
|
+
for section in direct_instruction:
|
|
119
|
+
parts.append(f" - {getattr(section, 'heading', str(section))}")
|
|
102
120
|
if hasattr(section, "key_points") and section.key_points:
|
|
103
121
|
for pt in section.key_points[:3]:
|
|
104
122
|
parts.append(f" • {pt}")
|
|
123
|
+
elif isinstance(direct_instruction, str) and direct_instruction:
|
|
124
|
+
parts.append(f"\nKEY CONCEPTS:\n{direct_instruction[:500]}")
|
|
105
125
|
|
|
106
126
|
return "\n".join(parts)
|
|
107
127
|
|
|
108
128
|
|
|
129
|
+
def _repair_html_structure(html: str) -> str:
|
|
130
|
+
"""Repair common LLM HTML generation failures.
|
|
131
|
+
|
|
132
|
+
LLMs sometimes emit:
|
|
133
|
+
- CSS rules directly after <!DOCTYPE html> with no <head> or <body>
|
|
134
|
+
- <title> text as bare text instead of inside a <title> tag
|
|
135
|
+
- <style> content without the wrapping <style> tag
|
|
136
|
+
- Missing </html> closer
|
|
137
|
+
|
|
138
|
+
This function detects those patterns and wraps the content into a
|
|
139
|
+
valid HTML skeleton, preserving all the CSS and JS the LLM generated.
|
|
140
|
+
"""
|
|
141
|
+
html_lower = html.lower()
|
|
142
|
+
|
|
143
|
+
# Strip duplicate DOCTYPE/html tags (LLM sometimes nests two documents)
|
|
144
|
+
if html.count("<!DOCTYPE") > 1:
|
|
145
|
+
# Keep only the content between the LAST <!DOCTYPE and end
|
|
146
|
+
# Actually: strip all but the first DOCTYPE
|
|
147
|
+
parts = html.split("<!DOCTYPE")
|
|
148
|
+
html = "<!DOCTYPE" + parts[1] # keep first occurrence + content
|
|
149
|
+
for extra in parts[2:]:
|
|
150
|
+
# Append content after stripping the duplicate preamble
|
|
151
|
+
extra_content = re.sub(
|
|
152
|
+
r"^[^>]*>\s*<html[^>]*>\s*", "", extra, flags=re.IGNORECASE
|
|
153
|
+
)
|
|
154
|
+
html += extra_content
|
|
155
|
+
html_lower = html.lower()
|
|
156
|
+
|
|
157
|
+
# Check for bare JS not wrapped in <script> tags
|
|
158
|
+
has_script_tags = "<script" in html_lower
|
|
159
|
+
has_js_code = bool(re.search(
|
|
160
|
+
r"(?:function\s+\w+|const\s+\w+\s*=|let\s+\w+\s*=|document\.|addEventListener)",
|
|
161
|
+
html
|
|
162
|
+
))
|
|
163
|
+
if not has_script_tags and has_js_code:
|
|
164
|
+
# Find where JS starts (after the last </div> or after CSS)
|
|
165
|
+
# Look for first function/const/let/document line
|
|
166
|
+
js_start = re.search(
|
|
167
|
+
r"\n((?:function |const |let |var |document\.|//\s*[-=]|class\s+\w+\s*\{))",
|
|
168
|
+
html
|
|
169
|
+
)
|
|
170
|
+
if js_start:
|
|
171
|
+
before_js = html[:js_start.start()]
|
|
172
|
+
js_code = html[js_start.start():]
|
|
173
|
+
# Strip trailing </body></html> from JS if present
|
|
174
|
+
js_code = re.sub(r"\s*</body>\s*</html>\s*$", "", js_code, flags=re.IGNORECASE)
|
|
175
|
+
html = before_js + "\n<script>\n" + js_code + "\n</script>\n</body>\n</html>"
|
|
176
|
+
html_lower = html.lower()
|
|
177
|
+
|
|
178
|
+
# Already well-formed — has <head>, <body>, and <script>
|
|
179
|
+
if "<head>" in html_lower and "<body>" in html_lower and (has_script_tags or "<script" in html_lower):
|
|
180
|
+
return html
|
|
181
|
+
|
|
182
|
+
# Has <head> but no <body> — unusual, leave it
|
|
183
|
+
if "<head>" in html_lower:
|
|
184
|
+
return html
|
|
185
|
+
|
|
186
|
+
# Missing <head>/<body> — LLM dumped CSS/JS straight into <html>
|
|
187
|
+
# Strategy: find the first <script or first element tag to split on,
|
|
188
|
+
# put everything before the first block element into <head>,
|
|
189
|
+
# everything else into <body>.
|
|
190
|
+
|
|
191
|
+
# Collect lines after <!DOCTYPE html><html ...>
|
|
192
|
+
# Find where the html tag ends
|
|
193
|
+
html_tag_end = html_lower.find(">", html_lower.find("<html"))
|
|
194
|
+
if html_tag_end == -1:
|
|
195
|
+
# No <html> tag at all — wrap everything
|
|
196
|
+
title_match = re.search(r"^([^\n<@{]+)", html.strip())
|
|
197
|
+
title_text = title_match.group(1).strip() if title_match else "Learning Game"
|
|
198
|
+
return (
|
|
199
|
+
f"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n"
|
|
200
|
+
f"<meta charset=\"UTF-8\">\n"
|
|
201
|
+
f"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
|
|
202
|
+
f"<title>{title_text}</title>\n"
|
|
203
|
+
f"<style>\n{html}\n</style>\n</head>\n<body></body>\n</html>"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
_preamble = html[:html_tag_end + 1] # noqa: F841 # everything up to and including <html ...>
|
|
207
|
+
rest = html[html_tag_end + 1:].strip()
|
|
208
|
+
|
|
209
|
+
# Extract bare title text (first non-tag, non-CSS line after <html>)
|
|
210
|
+
# This catches lines like "Age of Exploration: Chart Your Course" emitted before the CSS
|
|
211
|
+
title_text = "Learning Game"
|
|
212
|
+
title_match = re.match(r"^\s*([A-Za-z][^\n@{<]{3,120})\n", rest)
|
|
213
|
+
if title_match:
|
|
214
|
+
candidate = title_match.group(1).strip()
|
|
215
|
+
# Accept as title if it looks like a human-readable title (not CSS)
|
|
216
|
+
if not candidate.startswith(("@", "{", ".", "#", "*", ":", "/")):
|
|
217
|
+
title_text = candidate
|
|
218
|
+
rest = rest[title_match.end():]
|
|
219
|
+
|
|
220
|
+
# Split: CSS/meta goes in <head>, script/div/etc goes in <body>
|
|
221
|
+
head_parts: list[str] = []
|
|
222
|
+
body_parts: list[str] = [] # noqa: F841
|
|
223
|
+
|
|
224
|
+
# Collect <meta> tags floating outside <head>
|
|
225
|
+
meta_tags = re.findall(r"<meta[^>]*>", rest, re.IGNORECASE)
|
|
226
|
+
for m in meta_tags:
|
|
227
|
+
head_parts.append(m)
|
|
228
|
+
rest = rest.replace(m, "", 1)
|
|
229
|
+
|
|
230
|
+
# Wrap bare CSS (starts with @import, :root, or selector{) in <style>
|
|
231
|
+
# Find contiguous CSS blocks before the first <script or <div
|
|
232
|
+
first_script = re.search(r"<script|<div|<section|<main|<canvas", rest, re.IGNORECASE)
|
|
233
|
+
split_at = first_script.start() if first_script else len(rest)
|
|
234
|
+
|
|
235
|
+
css_block = rest[:split_at].strip()
|
|
236
|
+
body_block = rest[split_at:].strip()
|
|
237
|
+
|
|
238
|
+
if css_block:
|
|
239
|
+
# Check if it's raw CSS (no surrounding <style> tags)
|
|
240
|
+
if not re.search(r"<style", css_block, re.IGNORECASE):
|
|
241
|
+
head_parts.append(f"<style>\n{css_block}\n</style>")
|
|
242
|
+
else:
|
|
243
|
+
head_parts.append(css_block)
|
|
244
|
+
|
|
245
|
+
head_html = "\n".join(head_parts)
|
|
246
|
+
body_html = body_block
|
|
247
|
+
|
|
248
|
+
# Ensure </html> at end
|
|
249
|
+
if not body_html.rstrip().endswith("</html>"):
|
|
250
|
+
body_html = body_html.rstrip()
|
|
251
|
+
if not body_html.endswith("</body>"):
|
|
252
|
+
body_html += "\n</body>"
|
|
253
|
+
body_html += "\n</html>"
|
|
254
|
+
|
|
255
|
+
repaired = (
|
|
256
|
+
f"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n"
|
|
257
|
+
f"<meta charset=\"UTF-8\">\n"
|
|
258
|
+
f"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n"
|
|
259
|
+
f"<title>{title_text}</title>\n"
|
|
260
|
+
f"{head_html}\n"
|
|
261
|
+
f"</head>\n<body>\n"
|
|
262
|
+
f"{body_html}"
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
logger.info("Repaired HTML structure (was missing <head>/<body>)")
|
|
266
|
+
return repaired
|
|
267
|
+
|
|
268
|
+
|
|
109
269
|
def _validate_game_html(html: str, master: MasterContent) -> list[str]:
|
|
110
270
|
"""Validate that the generated HTML is a working game."""
|
|
111
271
|
issues = []
|
|
@@ -116,6 +276,12 @@ def _validate_game_html(html: str, master: MasterContent) -> list[str]:
|
|
|
116
276
|
if "<html" not in html.lower():
|
|
117
277
|
issues.append("Missing <html> tag")
|
|
118
278
|
|
|
279
|
+
if "<head>" not in html.lower():
|
|
280
|
+
issues.append("Missing <head> tag — CSS may render broken")
|
|
281
|
+
|
|
282
|
+
if "<body>" not in html.lower():
|
|
283
|
+
issues.append("Missing <body> tag — content structure invalid")
|
|
284
|
+
|
|
119
285
|
if "<script" not in html.lower():
|
|
120
286
|
issues.append("Missing <script> tag — no JavaScript")
|
|
121
287
|
|
|
@@ -123,7 +289,7 @@ def _validate_game_html(html: str, master: MasterContent) -> list[str]:
|
|
|
123
289
|
issues.append("No JavaScript functions found")
|
|
124
290
|
|
|
125
291
|
# Check that educational content is embedded
|
|
126
|
-
topic_words = master
|
|
292
|
+
topic_words = getattr(master, "topic", master.title).lower().split()[:3]
|
|
127
293
|
html_lower = html.lower()
|
|
128
294
|
found_topic = any(w in html_lower for w in topic_words if len(w) > 3)
|
|
129
295
|
if not found_topic:
|
|
@@ -142,7 +308,7 @@ def _validate_game_html(html: str, master: MasterContent) -> list[str]:
|
|
|
142
308
|
|
|
143
309
|
|
|
144
310
|
async def compile_game(
|
|
145
|
-
master: MasterContent,
|
|
311
|
+
master: "MasterContent | Any",
|
|
146
312
|
persona: TeacherPersona | None = None,
|
|
147
313
|
output_dir: Path | None = None,
|
|
148
314
|
config: AppConfig | None = None,
|
|
@@ -172,7 +338,15 @@ async def compile_game(
|
|
|
172
338
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
173
339
|
|
|
174
340
|
config = config or AppConfig.load()
|
|
175
|
-
|
|
341
|
+
# Games require maximum intelligence — always Opus.
|
|
342
|
+
from clawed.config import get_api_key
|
|
343
|
+
from clawed.models import LLMProvider
|
|
344
|
+
anthropic_key = get_api_key("anthropic")
|
|
345
|
+
if anthropic_key:
|
|
346
|
+
config.provider = LLMProvider.ANTHROPIC
|
|
347
|
+
config.anthropic_model = "claude-opus-4-6"
|
|
348
|
+
else:
|
|
349
|
+
config = route_model("game_generate", config)
|
|
176
350
|
client = LLMClient(config)
|
|
177
351
|
|
|
178
352
|
# Build the game generation prompt
|
|
@@ -209,7 +383,7 @@ async def compile_game(
|
|
|
209
383
|
if persona:
|
|
210
384
|
prompt_parts.append(
|
|
211
385
|
f"\nTEACHER'S STYLE: {persona.tone or 'engaging'}\n"
|
|
212
|
-
f"Grade level: {master
|
|
386
|
+
f"Grade level: {getattr(master, 'grade_level', '')}\n"
|
|
213
387
|
"Match the difficulty and humor to this teacher and grade."
|
|
214
388
|
)
|
|
215
389
|
|
|
@@ -233,6 +407,10 @@ async def compile_game(
|
|
|
233
407
|
html = re.sub(r"\n?```$", "", html)
|
|
234
408
|
html = html.strip()
|
|
235
409
|
|
|
410
|
+
# Structural repair: LLMs sometimes emit CSS/content outside <head>/<body>.
|
|
411
|
+
# Detect and wrap into a valid HTML skeleton if needed.
|
|
412
|
+
html = _repair_html_structure(html)
|
|
413
|
+
|
|
236
414
|
# Validate
|
|
237
415
|
issues = _validate_game_html(html, master)
|
|
238
416
|
if issues and attempt < max_attempts - 1:
|
|
@@ -457,6 +457,8 @@ def export_student_handout(
|
|
|
457
457
|
|
|
458
458
|
Returns the path to the saved .docx file.
|
|
459
459
|
"""
|
|
460
|
+
import re as _re
|
|
461
|
+
|
|
460
462
|
from docx import Document
|
|
461
463
|
from docx.enum.table import WD_TABLE_ALIGNMENT
|
|
462
464
|
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
@@ -464,15 +466,27 @@ def export_student_handout(
|
|
|
464
466
|
|
|
465
467
|
from clawed.sanitize import sanitize_text
|
|
466
468
|
|
|
469
|
+
def _strip_answers(text: str) -> str:
|
|
470
|
+
"""Remove answer keys from student-facing content."""
|
|
471
|
+
# Strip (Answer: ...) patterns
|
|
472
|
+
text = _re.sub(r"\s*\(Answer:\s*[^)]*\)", "", text)
|
|
473
|
+
# Strip (answer: ...) lowercase variant
|
|
474
|
+
text = _re.sub(r"\s*\(answer:\s*[^)]*\)", "", text)
|
|
475
|
+
# Strip "Answer: ..." at end of lines
|
|
476
|
+
text = _re.sub(r"\s*Answer:\s*.+$", "", text, flags=_re.MULTILINE)
|
|
477
|
+
# Strip "Expected: ..." patterns
|
|
478
|
+
text = _re.sub(r"\s*\*?Expected:?\*?\s*.+$", "", text, flags=_re.MULTILINE)
|
|
479
|
+
return text.strip()
|
|
480
|
+
|
|
467
481
|
doc = Document()
|
|
468
482
|
|
|
469
|
-
# Sanitize
|
|
483
|
+
# Sanitize AND strip answers from all student-facing text
|
|
470
484
|
lesson.title = sanitize_text(lesson.title)
|
|
471
485
|
lesson.objective = sanitize_text(lesson.objective)
|
|
472
486
|
lesson.do_now = sanitize_text(lesson.do_now) if lesson.do_now else ""
|
|
473
487
|
lesson.direct_instruction = sanitize_text(lesson.direct_instruction) if lesson.direct_instruction else ""
|
|
474
|
-
lesson.guided_practice = sanitize_text(lesson.guided_practice) if lesson.guided_practice else ""
|
|
475
|
-
lesson.independent_work = sanitize_text(lesson.independent_work) if lesson.independent_work else ""
|
|
488
|
+
lesson.guided_practice = _strip_answers(sanitize_text(lesson.guided_practice)) if lesson.guided_practice else ""
|
|
489
|
+
lesson.independent_work = _strip_answers(sanitize_text(lesson.independent_work)) if lesson.independent_work else ""
|
|
476
490
|
for q in lesson.exit_ticket:
|
|
477
491
|
q.question = sanitize_text(q.question)
|
|
478
492
|
|