| |
| """ |
| wire_eduintel.py |
| Wires EduIntel into the Morutabana teacher dashboard. |
| Edits 3 files: views_teacher.py, urls.py, teacher_hub.html |
| |
| Usage: |
| cd ~/leotsha_project |
| python3 wire_eduintel.py |
| """ |
|
|
| import re |
| from pathlib import Path |
|
|
| BASE = Path("/home/sediba/leotsha_project") |
|
|
| |
| VIEWS_FILE = BASE / "leotsha/views_teacher.py" |
| views_content = VIEWS_FILE.read_text() |
|
|
| EDUINTEL_VIEW = ''' |
| |
| # โโ EduIntel API โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ |
| def teacher_eduintel(request): |
| """API endpoint: teacher asks EduIntel a curriculum question.""" |
| from django.http import JsonResponse |
| import subprocess, os, re as _re |
| |
| teacher = get_teacher(request) |
| if not teacher: |
| return JsonResponse({"error": "not authenticated"}, status=401) |
| |
| query = request.GET.get("q", "").strip() |
| if not query: |
| return JsonResponse({"error": "no query"}) |
| |
| try: |
| result = subprocess.run( |
| ["python3", "eduintel_generate.py", query], |
| capture_output=True, |
| text=True, |
| timeout=120, |
| cwd="/home/sediba/leotsha_project", |
| env={**os.environ}, |
| ) |
| output = result.stdout |
| |
| # Extract just the answer (between โโโ separator and final โโโ) |
| sep = "โ" * 20 |
| end = "โ" * 20 |
| if sep in output: |
| answer = output.split(sep)[-1] |
| answer = answer.split(end)[0].strip() |
| else: |
| answer = output.strip() |
| |
| if not answer: |
| answer = "EduIntel could not find relevant content. Try rephrasing." |
| |
| except subprocess.TimeoutExpired: |
| answer = "EduIntel is processing. Please try a shorter question." |
| except Exception as e: |
| answer = f"Error: {str(e)}" |
| |
| return JsonResponse({"answer": answer, "query": query}) |
| ''' |
|
|
| if "def teacher_eduintel" in views_content: |
| print(" โญ teacher_eduintel already in views_teacher.py") |
| else: |
| VIEWS_FILE.write_text(views_content + EDUINTEL_VIEW) |
| print(" โ teacher_eduintel added to views_teacher.py") |
|
|
| |
| URLS_FILE = BASE / "leotsha/urls.py" |
| urls_content = URLS_FILE.read_text() |
|
|
| OLD_URL = ' path("leave/", t.learner_leave, name="learner_leave"),' |
| NEW_URL = ''' path("leave/", t.learner_leave, name="learner_leave"), |
| path("teacher/eduintel/", t.teacher_eduintel, name="teacher_eduintel"),''' |
|
|
| if "teacher_eduintel" in urls_content: |
| print(" โญ teacher_eduintel already in urls.py") |
| elif OLD_URL in urls_content: |
| URLS_FILE.write_text(urls_content.replace(OLD_URL, NEW_URL)) |
| print(" โ URL route added to leotsha/urls.py") |
| else: |
| print(" โ Could not find anchor in urls.py โ adding at end") |
| URLS_FILE.write_text(urls_content.rstrip() + "\n # EduIntel\n path(\"teacher/eduintel/\", t.teacher_eduintel, name=\"teacher_eduintel\"),\n]\n") |
| print(" โ URL route added to end of urls.py") |
|
|
| |
| TEMPLATE_FILE = BASE / "templates/teacher_hub.html" |
| template_content = TEMPLATE_FILE.read_text() |
|
|
| EDUINTEL_HTML = ''' |
| <!-- EDUINTEL --> |
| <div class="section-lbl" style="margin-top:8px">Ask EduIntel ๐ค</div> |
| <div class="card" id="ei-card"> |
| <div style="font-size:13px;color:#555;margin-bottom:10px"> |
| Ask anything about your Sepedi curriculum, Grade 10-12 content, or lesson planning. |
| </div> |
| <div style="display:flex;gap:8px;margin-bottom:10px"> |
| <input |
| id="ei-input" |
| type="text" |
| placeholder="e.g. What should I teach Grade 11 HL this term?" |
| style="flex:1;padding:10px 12px;border:1.5px solid #e5e5e0;border-radius:10px;font-size:14px;outline:none" |
| onkeydown="if(event.key==='Enter')askEduIntel()" |
| > |
| <button |
| onclick="askEduIntel()" |
| style="background:#1D9E75;color:#fff;border:none;border-radius:10px;padding:10px 16px;font-size:14px;font-weight:600;cursor:pointer;white-space:nowrap" |
| >Ask</button> |
| </div> |
| <div id="ei-loading" style="display:none;text-align:center;padding:12px;color:#999;font-size:13px"> |
| โณ EduIntel is thinking... |
| </div> |
| <div id="ei-result" style="display:none;background:#f8f9f6;border-radius:10px;padding:14px;font-size:13px;line-height:1.7;color:#222;white-space:pre-wrap;max-height:400px;overflow-y:auto"></div> |
| <div id="ei-suggestions" style="margin-top:8px;display:flex;flex-wrap:wrap;gap:6px"> |
| <button onclick="askSuggestion(this)" class="ei-pill">What to teach Grade 11 HL this term?</button> |
| <button onclick="askSuggestion(this)" class="ei-pill">Explain ancestors theme in Badimo</button> |
| <button onclick="askSuggestion(this)" class="ei-pill">Grade 12 prescribed poems?</button> |
| <button onclick="askSuggestion(this)" class="ei-pill">How to teach oral assessment?</button> |
| </div> |
| </div> |
| |
| <style> |
| .ei-pill{background:#E1F5EE;color:#0F6E56;border:none;border-radius:99px;padding:6px 12px;font-size:12px;cursor:pointer} |
| .ei-pill:active{background:#c5ece0} |
| </style> |
| |
| <script> |
| function askEduIntel() { |
| const input = document.getElementById('ei-input'); |
| const loading = document.getElementById('ei-loading'); |
| const result = document.getElementById('ei-result'); |
| const sugg = document.getElementById('ei-suggestions'); |
| const query = input.value.trim(); |
| if (!query) return; |
| |
| loading.style.display = 'block'; |
| result.style.display = 'none'; |
| sugg.style.display = 'none'; |
| |
| fetch('/teacher/eduintel/?q=' + encodeURIComponent(query)) |
| .then(r => r.json()) |
| .then(data => { |
| loading.style.display = 'none'; |
| result.style.display = 'block'; |
| // Basic markdown: **bold** and bullet points |
| let html = (data.answer || 'No answer returned.') |
| .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>') |
| .replace(/^- /gm, 'โข '); |
| result.innerHTML = html; |
| }) |
| .catch(err => { |
| loading.style.display = 'none'; |
| result.style.display = 'block'; |
| result.textContent = 'Error: ' + err.message; |
| }); |
| } |
| |
| function askSuggestion(btn) { |
| document.getElementById('ei-input').value = btn.textContent; |
| askEduIntel(); |
| } |
| </script> |
| |
| ''' |
|
|
| ANCHOR = "<!-- NAV -->" |
|
|
| if "Ask EduIntel" in template_content: |
| print(" โญ EduIntel section already in teacher_hub.html") |
| elif ANCHOR in template_content: |
| TEMPLATE_FILE.write_text(template_content.replace(ANCHOR, EDUINTEL_HTML + ANCHOR)) |
| print(" โ EduIntel section added to teacher_hub.html") |
| else: |
| print(" โ Nav anchor not found โ adding before </body>") |
| TEMPLATE_FILE.write_text(template_content.replace("</body>", EDUINTEL_HTML + "</body>")) |
| print(" โ EduIntel section added to teacher_hub.html") |
|
|
| print("\nโ
All three files updated.") |
| print("\nNow run:") |
| print(" cd ~/leotsha_project && python3 manage.py runserver") |
| print(" Open: http://localhost:8000/teacher/dashboard/") |
|
|