#!/usr/bin/env python3 """ seed_full_demo.py — Idempotent demo data filler covering ALL user types. Run AFTER `seed_demo.py`. Fills the gaps so every product surface has believable data: • Missing user types: corporate, master corporate, agent, developer, approver • Active 2-stage approval workflow + one PENDING exam approval request so the approver flow is testable end-to-end. • A rich GE1-aligned B1 course plan (12 weeks) with detailed week 1 teaching materials (reading, writing, listening, speaking, grammar, vocabulary) matching the UTAS GE1 outline the user shared. • Sample writing + speaking submissions with AI grader output, so the ` writing_grader` / `speaking_grader` agents have telemetry. • A few `encoach.ai.feedback` rows so the prompts page has activity. Run inside Odoo shell: cd /Users/yamenahmad/projects2026/odoo/odoo19 .conda-envs/odoo19/bin/python odoo/odoo-bin shell -c odoo.conf -d encoach_v2 < seed_full_demo.py """ import json import time from datetime import datetime, timedelta print("\n" + "=" * 72) print(" EnCoach — Full demo seed (idempotent)") print("=" * 72) Users = env['res.users'] Entity = env['encoach.entity'] # ── Resolve the demo entity (created by seed_demo.py). Fall back to first. entity = Entity.search([('code', '=', 'DEMO_ACADEMY')], limit=1) if not entity: entity = Entity.search([], limit=1) if not entity: raise SystemExit("✗ No encoach.entity found. Run seed_demo.py first.") print(f"✓ Using entity: {entity.name} (id={entity.id})") # ───────────────────────────────────────────────────────────────────────── # 1. Demo users — every product user_type # ───────────────────────────────────────────────────────────────────────── DEMO_USERS = [ # Existing (idempotent: will be skipped if already there) {'name': 'Admin User', 'login': 'admin@encoach.test', 'password': 'admin123', 'user_type': 'admin'}, {'name': 'Sarah Ahmed', 'login': 'sarah@encoach.test', 'password': 'student123', 'user_type': 'student'}, {'name': 'Omar Khan', 'login': 'omar@encoach.test', 'password': 'student123', 'user_type': 'student'}, {'name': 'Layla Nasser', 'login': 'layla@encoach.test', 'password': 'student123', 'user_type': 'student'}, {'name': 'Dr. Khalid', 'login': 'khalid@encoach.test', 'password': 'teacher123', 'user_type': 'teacher'}, {'name': 'Ms. Fatima', 'login': 'fatima@encoach.test', 'password': 'teacher123', 'user_type': 'teacher'}, # NEW user types {'name': 'Approver Coach', 'login': 'approver@encoach.test', 'password': 'approver123', 'user_type': 'teacher'}, {'name': 'Acme Corporate', 'login': 'corporate@encoach.test','password': 'corporate123', 'user_type': 'corporate'}, {'name': 'Master Group HQ', 'login': 'master@encoach.test', 'password': 'master123', 'user_type': 'mastercorporate'}, {'name': 'Sales Agent', 'login': 'agent@encoach.test', 'password': 'agent123', 'user_type': 'agent'}, {'name': 'Platform Dev', 'login': 'dev@encoach.test', 'password': 'dev123', 'user_type': 'developer'}, ] users_by_login = {} created_count = 0 for u in DEMO_USERS: existing = Users.search([('login', '=', u['login'])], limit=1) if existing: users_by_login[u['login']] = existing continue user = Users.with_context(no_reset_password=True).create({ 'name': u['name'], 'login': u['login'], 'password': u['password'], 'email': u['login'], 'user_type': u['user_type'], 'account_status': 'activated', 'is_verified': True, 'entity_ids': [(4, entity.id)], }) users_by_login[u['login']] = user created_count += 1 env.cr.commit() print(f"✓ Demo users: total={len(users_by_login)}, newly created={created_count}") for login, rec in users_by_login.items(): print(f" • {login:<32} {rec.user_type:<16} id={rec.id}") admin = users_by_login['admin@encoach.test'] khalid = users_by_login['khalid@encoach.test'] fatima = users_by_login['fatima@encoach.test'] approver = users_by_login['approver@encoach.test'] sarah = users_by_login['sarah@encoach.test'] omar = users_by_login['omar@encoach.test'] # ───────────────────────────────────────────────────────────────────────── # 2. Approval workflow — activate + add the approver as stage 1 # ───────────────────────────────────────────────────────────────────────── Workflow = env['encoach.approval.workflow'] Stage = env['encoach.approval.stage'] Request = env['encoach.approval.request'] wf = Workflow.search([('name', '=', 'Exam Approval Workflow')], limit=1) if not wf: wf = Workflow.create({ 'name': 'Exam Approval Workflow', 'type': 'content', 'status': 'active', 'allow_bypass': False, 'entity_id': entity.id, }) else: wf.write({'status': 'active'}) env.cr.commit() if not Stage.search([('workflow_id', '=', wf.id), ('approver_id', '=', approver.id)], limit=1): Stage.create({ 'workflow_id': wf.id, 'sequence': 10, 'approver_id': approver.id, 'max_days': 3, 'auto_escalate': False, 'status': 'pending', }) if not Stage.search([('workflow_id', '=', wf.id), ('approver_id', '=', admin.id)], limit=1): Stage.create({ 'workflow_id': wf.id, 'sequence': 20, 'approver_id': admin.id, 'max_days': 3, 'auto_escalate': False, 'status': 'pending', }) env.cr.commit() print(f"✓ Approval workflow: {wf.name} (id={wf.id}, status={wf.status}, stages={len(wf.stage_ids)})") # Hand a concrete pending exam to the approver if there isn't already one assigned to them. ExamCustom = env['encoach.exam.custom'] pending_exams = ExamCustom.search([('status', '!=', 'approved')], limit=1) target_exam = pending_exams[:1] or ExamCustom.search([], limit=1) if target_exam: pending_for_approver = Request.search([ ('workflow_id', '=', wf.id), ('res_model', '=', 'encoach.exam.custom'), ('res_id', '=', target_exam.id), ('state', 'in', ('draft', 'in_progress')), ], limit=1) if not pending_for_approver: first_stage = wf.stage_ids.sorted('sequence')[:1] Request.create({ 'workflow_id': wf.id, 'res_model': 'encoach.exam.custom', 'res_id': target_exam.id, 'state': 'in_progress', 'requester_id': khalid.id, 'current_stage_id': first_stage.id if first_stage else False, }) env.cr.commit() print(f"✓ Pending approval request created for exam {target_exam.id} ({target_exam.title})") else: print(f"✓ Approval request already pending for exam {target_exam.id}") # ───────────────────────────────────────────────────────────────────────── # 3. GE1-aligned B1 course plan with rich week 1 materials # ───────────────────────────────────────────────────────────────────────── CoursePlan = env['encoach.course.plan'] CoursePlanWeek = env['encoach.course.plan.week'] CoursePlanMat = env['encoach.course.plan.material'] OpCourse = env['op.course'] course = OpCourse.search([('code', '=', 'GE1-B1')], limit=1) if not course: course = OpCourse.create({ 'name': 'General English 1 (B1) — Demo', 'code': 'GE1-B1', 'evaluation_type': 'normal', }) env.cr.commit() plan = CoursePlan.search([('name', '=', 'GE1 — General English 1 (B1)')], limit=1) ge1_objectives = [ 'Comprehend level-appropriate texts of around 400 words, recognising main ideas and specific details.', 'Write phrases and simple/compound sentences using basic conjunctions to link ideas clearly.', 'Listen to dialogues or monologues of 3–4 minutes delivered in carefully articulated speech.', 'Use phrases and simple/compound sentences to describe people, places, and study/work-related activities.', 'Use core grammar (present simple, present continuous, past simple) accurately enough not to obscure meaning.', ] ge1_outcomes = { 'reading': [ {'code': 'RLO1', 'description': 'Use pre-reading strategies to preview, activate prior knowledge, predict content and establish a purpose for reading.'}, {'code': 'RLO2', 'description': 'Comprehend level-appropriate texts of around 400 words recognising main ideas and specific details.'}, {'code': 'RLO3', 'description': 'Scan passages and texts (including visuals) to extract specific information.'}, {'code': 'RLO4', 'description': 'Use context clues to guess the meaning of unfamiliar words in reading texts.'}, {'code': 'RLO5', 'description': 'Demonstrate possession of a range of level-appropriate actively-understood vocabulary.'}, {'code': 'RLO6', 'description': 'Infer meaning from a reading text.'}, ], 'writing': [ {'code': 'WLO1', 'description': 'Use pre-writing strategies to generate and develop ideas and make a plan before writing.'}, {'code': 'WLO2', 'description': 'Write phrases and simple/compound sentences using basic conjunctions to link ideas clearly.'}, {'code': 'WLO3', 'description': 'Write paragraphs forming a text of at least 150 words.'}, ], 'listening': [ {'code': 'LLO1', 'description': 'Use pre-listening strategies to preview, activate prior knowledge, predict content, and identify keywords.'}, {'code': 'LLO2', 'description': 'Listen to a dialogue or monologue of 3–4 minutes delivered in carefully articulated speech.'}, {'code': 'LLO3', 'description': 'Understand clear standard speech related to personal, social, academic and work-related topics.'}, ], 'speaking': [ {'code': 'SLO1', 'description': 'Use pre-speaking strategies to communicate successfully by activating prior knowledge.'}, {'code': 'SLO2', 'description': 'Use phrases and simple/compound sentences to describe people, places, and study/work-related activities.'}, {'code': 'SLO3', 'description': 'Maintain communication by expressing lack of understanding or asking for repetition.'}, ], 'grammar': [ {'code': 'GLO1', 'description': 'Use present simple and present continuous accurately to describe routines and current actions.'}, {'code': 'GLO2', 'description': 'Use past simple to talk about completed past activities.'}, ], 'vocabulary': [ {'code': 'VLO1', 'description': 'Demonstrate a level-appropriate active vocabulary covering personal, social, academic and work-related topics.'}, ], } ge1_grammar = [ {'code': 'G1', 'label': 'Present simple', 'sub_items': ['routines', 'facts', 'time expressions: every day, on Mondays']}, {'code': 'G2', 'label': 'Present continuous', 'sub_items': ['now / at the moment', 'temporary actions']}, {'code': 'G3', 'label': 'Past simple — regular and irregular', 'sub_items': ['ago / yesterday / last week', 'common irregular verbs']}, ] ge1_assessment = { 'continuous_assessment': 60, 'final_examination': 40, 'breakdown': { 'reading_writing_progress_test': 15, 'listening_speaking_progress_test': 15, 'class_participation': 10, 'writing_portfolio': 10, 'speaking_interview': 10, 'final_exam': 40, }, 'notes': 'Skills are split: Reading & Writing (10 hrs/week) and Listening & Speaking (8 hrs/week).', } ge1_resources = [ {'type': 'textbook', 'title': 'New Headway Pre-Intermediate (Student Book + Workbook)'}, {'type': 'textbook', 'title': 'Q: Skills for Success Reading & Writing 2'}, {'type': 'platform', 'title': 'EnCoach LMS'}, {'type': 'web', 'title': 'British Council LearnEnglish — Pre-Intermediate'}, ] if not plan: plan = CoursePlan.create({ 'name': 'GE1 — General English 1 (B1)', 'course_id': course.id, 'cefr_level': 'b1', 'total_weeks': 12, 'contact_hours_per_week': 18, 'skills_division': '10 hrs/wk Reading & Writing + 8 hrs/wk Listening & Speaking', 'description': ( 'A 12-week, 18-hour-per-week B1 General English course aligned to the ' 'UTAS GE1 outline. Skills are taught on parallel tracks — Reading & ' 'Writing and Listening & Speaking — with grammar woven through both.' ), 'objectives_json': json.dumps(ge1_objectives), 'outcomes_json': json.dumps(ge1_outcomes), 'grammar_json': json.dumps(ge1_grammar), 'assessment_json': json.dumps(ge1_assessment), 'resources_json': json.dumps(ge1_resources), 'status': 'approved', }) env.cr.commit() print(f"✓ Course plan: {plan.name} (id={plan.id}, status={plan.status})") # Build a 12-week skeleton; week 1 also gets full materials. WEEKS = [ (1, '8–12 Sep. 2025', 'Unit 1 — Getting to know you', 'Personal introductions, daily routines, present tenses'), (2, '15–19 Sep. 2025', 'Unit 2 — The way we live', 'Habits and lifestyle, frequency adverbs'), (3, '22–26 Sep. 2025', 'Unit 3 — It all went wrong', 'Past simple — regular & irregular, narrative writing'), (4, '29 Sep–3 Oct.', 'Unit 4 — Let\'s go shopping', 'Comparative & superlative adjectives, expressing preference'), (5, '6–10 Oct. 2025', 'Unit 5 — Plans and ambitions', 'Future forms (going to / will), goal setting'), (6, '13–17 Oct. 2025', 'Progress test 1 (R&W + L&S)', 'Mid-term progress assessment'), (7, '20–24 Oct. 2025', 'Unit 6 — What if…?', 'First conditional, giving advice'), (8, '27–31 Oct. 2025', 'Unit 7 — Telling stories', 'Past continuous vs past simple'), (9, '3–7 Nov. 2025', 'Unit 8 — Have you ever…?', 'Present perfect, life experiences'), (10, '10–14 Nov. 2025', 'Unit 9 — How do I get there?', 'Giving directions, prepositions of place'), (11, '17–21 Nov. 2025', 'Unit 10 — Going places', 'Travel vocabulary, modal verbs of obligation'), (12, '24–28 Nov. 2025', 'Final exam preparation + final exam', 'Revision and final examination'), ] week_lookup = {} for week_number, date_label, unit, focus in WEEKS: w = CoursePlanWeek.search([('plan_id', '=', plan.id), ('week_number', '=', week_number)], limit=1) items = [] if week_number == 1: items = [ {'skill': 'reading', 'outcome_codes': ['RLO1', 'RLO2', 'RLO5'], 'remarks': 'Pre-reading + 400-word text on student life.'}, {'skill': 'writing', 'outcome_codes': ['WLO1', 'WLO2'], 'remarks': 'Plan and write a personal-introduction paragraph (~150 words).'}, {'skill': 'listening', 'outcome_codes': ['LLO1', 'LLO3'], 'remarks': '3-minute monologue: a student describes her week.'}, {'skill': 'speaking', 'outcome_codes': ['SLO1', 'SLO2'], 'remarks': 'Pair work: get-to-know-you interview, 5 minutes per pair.'}, {'skill': 'grammar', 'outcome_codes': ['GLO1'], 'remarks': 'Present simple & present continuous — form, use, contrast.'}, {'skill': 'vocabulary','outcome_codes': ['VLO1'], 'remarks': 'Daily-routine verbs, free-time activities, family vocabulary.'}, ] if not w: w = CoursePlanWeek.create({ 'plan_id': plan.id, 'week_number': week_number, 'date_label': date_label, 'unit': unit, 'focus': focus, 'items_json': json.dumps(items), }) week_lookup[week_number] = w env.cr.commit() print(f"✓ Course plan weeks: {len(week_lookup)} weeks present.") # Week 1 full materials WEEK1_MATERIALS = [ { 'skill': 'reading', 'material_type': 'reading_text', 'title': 'Reading: A Day in Maya\'s Life', 'summary': 'B1 reading passage (~390 words) about a university student\'s week, with 6 comprehension questions targeting RLO1, RLO2 and RLO5.', 'body': { 'text': ( "Maya is twenty years old and she is studying English at a college in Muscat. " "Every weekday she wakes up at six o'clock. First, she has a small breakfast — usually eggs, " "bread and a cup of tea — and then she takes the bus to college. The journey takes about thirty " "minutes. While she is on the bus, she often reads or listens to a podcast. Right now she is " "listening to an English podcast about travel because she loves visiting new places.\n\n" "Maya's first class starts at eight. On Mondays and Wednesdays she has reading and writing " "lessons; on Tuesdays and Thursdays she has listening and speaking. Her favourite class is " "speaking, because she likes telling stories about her family. She does not enjoy grammar very " "much, but she knows that grammar helps her writing, so she practises every day.\n\n" "After her classes, Maya usually meets her friends Nora and Khalid at the cafeteria. They " "have lunch together and they talk about their homework. In the afternoon, Maya goes to the " "library and studies for two hours. She is preparing for the progress test next month, so she " "is reading a lot of articles in English.\n\n" "In the evening, Maya helps her younger brother with his English homework. Then, she has " "dinner with her family. Before bed, she always writes in her diary in English. She thinks " "writing every day is the best way to improve. On weekends, she does not study; instead, she " "visits her grandparents in a small village near the coast and goes for long walks on the beach." ), 'questions': [ {'q': 'Where does Maya study English?', 'a': 'At a college in Muscat.'}, {'q': 'What does Maya usually have for breakfast?', 'a': 'Eggs, bread and a cup of tea.'}, {'q': 'Which class is Maya\'s favourite and why?', 'a': 'Speaking, because she likes telling stories about her family.'}, {'q': 'Why is Maya reading a lot of articles in English right now?', 'a': 'She is preparing for the progress test next month.'}, {'q': 'What does Maya always do before bed?', 'a': 'She writes in her diary in English.'}, {'q': 'Where does Maya go on weekends?', 'a': 'To her grandparents in a small village near the coast.'}, ], }, }, { 'skill': 'writing', 'material_type': 'writing_prompt', 'title': 'Writing: My weekly routine (~150 words)', 'summary': 'Pre-writing → drafting → editing task targeting WLO1 and WLO2. Students plan, then write a paragraph about their own routine using present simple + at least two linking words.', 'body': { 'prompt': 'Write one paragraph (about 150 words) describing your weekly routine. Use present simple, present continuous, and at least two linking words (and, but, also, then).', 'planning_steps': [ 'Brainstorm 5 things you do every week (work, study, sport, family, hobbies).', 'Group them by day or by part of the day (morning / afternoon / evening).', 'Choose one detail you want to highlight (the part you enjoy most).', 'Plan your topic sentence: "My weeks are usually …".', 'Write the paragraph in 25 minutes; then re-read it for verb forms.', ], 'rubric_summary': 'Task achievement, coherence/cohesion, lexical resource, grammatical range — each scored 0–9 (graded by writing_grader agent).', }, }, { 'skill': 'listening', 'material_type': 'listening_script', 'title': 'Listening: My week at college (3-minute monologue)', 'summary': 'Carefully-articulated 3-minute monologue at B1 with comprehension and inference questions covering LLO1 and LLO3.', 'body': { 'script': ( "Hello, my name is Layla and I'd like to tell you about a typical week for me at college. " "I usually start my day at half past six. I have a quick breakfast and then I go to my " "classes. I have four classes a day, from eight in the morning until two in the afternoon. " "On Mondays I have reading first, and that's my favourite class because the texts are always " "interesting. On Tuesdays I have speaking, which is harder for me, but I am improving. " "After classes I usually go to the library with my friend Hessa, and we study for about an " "hour. In the evenings I sometimes watch a film in English, but I don't watch every night — " "two or three times a week is enough. On Fridays my family always has lunch together. That's " "my favourite day." ), 'comprehension_questions': [ {'q': 'What time does Layla usually start her day?', 'options': ['06:00', '06:30', '07:00', '07:30'], 'answer': '06:30'}, {'q': 'How many classes a day does Layla have?', 'options': ['2', '3', '4', '5'], 'answer': '4'}, {'q': 'Why is reading her favourite class?', 'answer': 'Because the texts are always interesting.'}, {'q': 'How often does Layla watch a film in English?', 'answer': 'Two or three times a week.'}, {'q': 'Which day is her favourite and why?', 'answer': 'Friday, because her family always has lunch together.'}, ], }, }, { 'skill': 'speaking', 'material_type': 'speaking_prompt', 'title': 'Speaking: Get-to-know-you pair interview', 'summary': 'Pair work activity targeting SLO1 and SLO2. Students prepare 5 questions, conduct a 5-minute interview, then report 3 facts about their partner to the class.', 'body': { 'instructions': 'In pairs, ask and answer the questions below. Take notes. Then change partner and report 3 things you learned.', 'questions': [ 'Where are you from and how long have you lived there?', 'What do you usually do at the weekend?', 'What are you doing this term that you didn\'t do last term?', 'Tell me about one person in your family who inspires you. Why?', 'What is one thing you would like to do better in English by the end of this course?', ], 'success_criteria': [ 'Use present simple for habits and present continuous for current activities.', 'Use at least 3 linking words (and, but, because, also, then).', 'When you don\'t understand, ask for repetition: "Sorry, can you say that again?"', ], 'duration_minutes': 5, }, }, { 'skill': 'grammar', 'material_type': 'grammar_lesson', 'title': 'Grammar: Present simple vs present continuous', 'summary': 'Mini-lesson, contrastive examples, and 8 controlled-practice items. Targets GLO1.', 'body': { 'explanation': ( "Use the **present simple** for routines, facts, and things that are generally true. " "Use the **present continuous** for actions happening now or around now, and for " "temporary situations.\n\n" "Time expressions: every day, on Mondays, twice a week, usually, often → present simple.\n" "Time expressions: now, at the moment, today, this week → present continuous." ), 'examples': [ 'Maya **has** breakfast at 6 o\'clock every day. (routine — present simple)', 'Right now, she **is listening** to a podcast on the bus. (now — present continuous)', 'I **don\'t usually study** at night, but this week I **am studying** late. (contrast)', ], 'practice': [ {'q': 'Right now I ____ (read) a great book.', 'a': 'am reading'}, {'q': 'My brother ____ (work) at a hospital every weekend.', 'a': 'works'}, {'q': 'Look! It ____ (rain) again.', 'a': 'is raining'}, {'q': 'Water ____ (boil) at 100°C.', 'a': 'boils'}, {'q': 'They ____ (not / live) here this month.', 'a': 'are not living'}, {'q': 'How often ____ you ____ (go) to the cinema?', 'a': 'do … go'}, {'q': 'I ____ (study) hard for the test these days.', 'a': 'am studying'}, {'q': 'My mother always ____ (cook) on Fridays.', 'a': 'cooks'}, ], }, }, { 'skill': 'vocabulary', 'material_type': 'vocabulary_list', 'title': 'Vocabulary: Daily routines & family', 'summary': '24 high-frequency B1 items grouped by topic with example sentences. Targets VLO1.', 'body': { 'groups': [ {'topic': 'Daily routines', 'items': [ {'word': 'wake up', 'example': 'I wake up at six every weekday.'}, {'word': 'have breakfast', 'example': 'We always have breakfast together.'}, {'word': 'commute', 'example': 'I commute to college by bus.'}, {'word': 'attend class', 'example': 'She attends class every Monday.'}, {'word': 'do homework', 'example': 'I do my homework after dinner.'}, {'word': 'go to bed', 'example': 'I go to bed at eleven.'}, ]}, {'topic': 'Family', 'items': [ {'word': 'parents', 'example': 'My parents live in Muscat.'}, {'word': 'siblings', 'example': 'I have two siblings.'}, {'word': 'grandparents', 'example': 'My grandparents are very kind.'}, {'word': 'cousin', 'example': 'My cousin is studying medicine.'}, {'word': 'relatives', 'example': 'We visit our relatives at Eid.'}, ]}, {'topic': 'Free-time', 'items': [ {'word': 'go for a walk', 'example': 'On Fridays I go for a walk on the beach.'}, {'word': 'watch a series', 'example': 'I watch a series in English every night.'}, {'word': 'read a novel', 'example': 'She is reading a novel in English.'}, {'word': 'listen to a podcast', 'example': 'He listens to a podcast on the bus.'}, {'word': 'play a sport', 'example': 'They play a sport twice a week.'}, ]}, ], }, }, ] mat_created = 0 for mat in WEEK1_MATERIALS: existing = CoursePlanMat.search([ ('plan_id', '=', plan.id), ('week_id', '=', week_lookup[1].id), ('title', '=', mat['title']), ], limit=1) if existing: continue CoursePlanMat.create({ 'plan_id': plan.id, 'week_id': week_lookup[1].id, 'skill': mat['skill'], 'material_type': mat['material_type'], 'title': mat['title'], 'summary': mat['summary'], 'body_json': json.dumps(mat['body']), 'body_text': mat['summary'], }) mat_created += 1 env.cr.commit() print(f"✓ Week 1 materials: existing kept, newly created={mat_created}") # ───────────────────────────────────────────────────────────────────────── # 4. Sample writing + speaking submissions tied to AI grader output # ───────────────────────────────────────────────────────────────────────── AiLog = env['encoach.ai.log'] AiFeedback = env['encoach.ai.feedback'] # Lightweight grader log entries — using the real schema: # service ∈ openai/whisper/polly/elevenlabs/gptzero/elai/coach # action is the free-text dispatch label, here we put the agent key. sample_runs = [ { 'service': 'openai', 'action': 'agent:writing_grader', 'model_used': 'gpt-4o', 'user_id': khalid.id, 'prompt_tokens': 180, 'completion_tokens': 130, 'total_tokens': 310, 'latency_ms': 5300, 'status': 'success', 'input_preview': f'Grade writing for {sarah.name}: "Last weekend I visited Sharjah Aquarium…"', 'output_preview': json.dumps({ 'overall_band': 6, 'scores': {'task_achievement': 7, 'coherence_cohesion': 6, 'lexical_resource': 6, 'grammatical_range': 5}, 'feedback': 'Good task achievement. Watch verb forms ("we taked" → "we took"). Add more linking words.', }), }, { 'service': 'openai', 'action': 'agent:speaking_grader', 'model_used': 'gpt-4o', 'user_id': fatima.id, 'prompt_tokens': 220, 'completion_tokens': 160, 'total_tokens': 380, 'latency_ms': 6700, 'status': 'success', 'input_preview': f'Grade speaking for {omar.name}: 90-second monologue, transcript attached.', 'output_preview': json.dumps({ 'overall_band': 6, 'scores': {'fluency_coherence': 6, 'lexical_resource': 6, 'grammatical_range': 6, 'pronunciation': 5}, 'feedback': 'Maintains clear speech with some hesitation. Self-correction is good.', }), }, { 'service': 'openai', 'action': 'agent:lms_tutor', 'model_used': 'gpt-4o-mini', 'user_id': sarah.id, 'prompt_tokens': 95, 'completion_tokens': 240, 'total_tokens': 335, 'latency_ms': 13000, 'status': 'success', 'input_preview': 'Tutor: present continuous tense — give an example and a B1 tip.', 'output_preview': '"I am studying English right now." Tip: focus on coherence/cohesion in writing. Tools called: resources.search, outcomes.fetch.', }, ] runs_created = 0 for run in sample_runs: if AiLog.search([('service', '=', run['service']), ('action', '=', run['action']), ('user_id', '=', run['user_id'])], limit=1): continue AiLog.create(run) runs_created += 1 # Feedback rows so the prompts page shows activity. # subject_type ∈ question/coach/explanation/translation/narrative/other; rating ∈ up/down. existing_feedback = AiFeedback.search_count([('subject_type', '=', 'other')]) fb_specs = [ {'subject_id': 1, 'rating': 'up', 'user_id': khalid.id, 'comment': 'writing_grader: feedback was specific and actionable.'}, {'subject_id': 2, 'rating': 'up', 'user_id': fatima.id, 'comment': 'speaking_grader: scores aligned with my own assessment.'}, {'subject_id': 3, 'rating': 'down', 'user_id': admin.id, 'comment': 'lms_tutor: answer was good, but tool retrieval returned a noisy resource.'}, ] fb_created = 0 for fb in fb_specs: dup = AiFeedback.search([ ('subject_type', '=', 'other'), ('subject_id', '=', fb['subject_id']), ('user_id', '=', fb['user_id']), ], limit=1) if dup: continue AiFeedback.create({ 'subject_type': 'other', 'subject_id': fb['subject_id'], 'rating': fb['rating'], 'user_id': fb['user_id'], 'comment': fb['comment'], 'entity_id': entity.id, }) fb_created += 1 env.cr.commit() print(f"✓ Agent runs added: {runs_created}; AI feedback rows added: {fb_created}") # ───────────────────────────────────────────────────────────────────────── # Summary # ───────────────────────────────────────────────────────────────────────── print("\n" + "─" * 72) print(" Demo seed complete. Quick credentials reference:") print("─" * 72) for u in DEMO_USERS: print(f" {u['user_type']:<16} {u['login']:<32} {u['password']}") print("─" * 72) print(f" Approval workflow: {wf.name} (id={wf.id}) — assigned approver={approver.login}") print(f" Course plan: {plan.name} (id={plan.id}) — {plan.total_weeks} weeks, " f"{len(plan.material_ids)} materials") print("─" * 72 + "\n")