Includes: - Odoo 19 framework (odoo/) - 27 custom EnCoach addons (new_project/custom_addons/) - encoach_core, encoach_api, encoach_lms_api, encoach_adaptive_api - encoach_exam, encoach_taxonomy, encoach_adaptive, encoach_assignment - encoach_ai, encoach_ai_grading, encoach_ai_generation, encoach_ai_media - encoach_courseware, encoach_communication, encoach_subscription - encoach_notification, encoach_approval, encoach_branding - encoach_classroom, encoach_registration, encoach_stats - encoach_faq, encoach_ticket, encoach_training, encoach_resources - encoach_adaptive_ai, encoach_sis - 21 OpenEduCat Enterprise modules (new_project/enterprise-19/) - 14 OpenEduCat Community modules (new_project/openeducat_erp-19.0/) - Configuration: odoo.conf, requirements.txt, scripts - 200+ REST API endpoints with JWT authentication - SRS and test documentation Made-with: Cursor
1417 lines
85 KiB
Python
1417 lines
85 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
EnCoach LMS – FULL Demo Data Seeder
|
||
=====================================
|
||
Covers every module end-to-end across 3 subjects (English, Math, IT):
|
||
|
||
Academic – departments, years, terms, courses, batches, subjects,
|
||
classrooms, timings, timetable, admissions
|
||
LMS / Courseware – chapters, materials, student progress
|
||
Exams – structures, rubrics, exams, sessions, evaluations, stats
|
||
Assignments – grading types, assignments (OP + EnCoach), extensions
|
||
Adaptive – diagnostics, proficiency, learning plans/items, content cache
|
||
Institutional – entities, invite codes, roles, user-role links, branding
|
||
Management – roles, permissions, user permissions
|
||
Communication – announcements, discussion boards/posts, direct messages
|
||
Approvals – workflows, stages, requests
|
||
Notifications – rules, preferences, notifications
|
||
Resources – learning resources
|
||
Subscription – packages, discounts, payments
|
||
Registration – registration batches
|
||
Training – tips, walkthroughs
|
||
Support – tickets, FAQ
|
||
Reports/Stats – exam session stats
|
||
Classroom Groups
|
||
|
||
Run: python new_project/seed_demo_data.py
|
||
"""
|
||
|
||
import psycopg2
|
||
import json
|
||
from datetime import date, datetime, timedelta
|
||
import random
|
||
|
||
DB = dict(dbname='encoach_v2', user='yamenahmad', host='localhost')
|
||
random.seed(42)
|
||
|
||
# ═══════════════════════════════════════════════════
|
||
# Helpers
|
||
# ═══════════════════════════════════════════════════
|
||
|
||
def j(val):
|
||
"""Odoo jsonb translatable field."""
|
||
return json.dumps({'en_US': val})
|
||
|
||
def insert(cur, table, vals):
|
||
cols = list(vals.keys())
|
||
sql = f"INSERT INTO {table} ({', '.join(cols)}) VALUES ({', '.join(['%s']*len(cols))}) RETURNING id"
|
||
cur.execute(sql, list(vals.values()))
|
||
return cur.fetchone()[0]
|
||
|
||
def find_or_create(cur, table, match_col, vals):
|
||
cur.execute(f"SELECT id FROM {table} WHERE {match_col} = %s", (vals[match_col],))
|
||
row = cur.fetchone()
|
||
if row:
|
||
return row[0]
|
||
return insert(cur, table, vals)
|
||
|
||
def m2m(cur, rel, c1, c2, v1, v2):
|
||
cur.execute(f"INSERT INTO {rel} ({c1},{c2}) VALUES (%s,%s) ON CONFLICT DO NOTHING", (v1, v2))
|
||
|
||
def ensure_partner(cur, name, email, company_id=1):
|
||
cur.execute("SELECT id FROM res_partner WHERE email = %s", (email,))
|
||
row = cur.fetchone()
|
||
if row:
|
||
return row[0]
|
||
return insert(cur, 'res_partner', {
|
||
'name': name, 'email': email, 'active': True,
|
||
'company_id': company_id, 'type': 'contact',
|
||
'autopost_bills': 'no', 'create_uid': 2, 'write_uid': 2,
|
||
})
|
||
|
||
def ensure_user(cur, name, email, company_id=1):
|
||
cur.execute("SELECT id FROM res_users WHERE login = %s", (email,))
|
||
row = cur.fetchone()
|
||
if row:
|
||
return row[0]
|
||
pid = ensure_partner(cur, name, email, company_id)
|
||
return insert(cur, 'res_users', {
|
||
'login': email, 'password': '$pbkdf2-sha512$25000$admin',
|
||
'active': True, 'company_id': company_id, 'partner_id': pid,
|
||
'notification_type': 'email', 'create_uid': 2, 'write_uid': 2,
|
||
})
|
||
|
||
AU = {'create_uid': 2, 'write_uid': 2}
|
||
|
||
# ═══════════════════════════════════════════════════
|
||
# Static data definitions
|
||
# ═══════════════════════════════════════════════════
|
||
|
||
DEPARTMENTS = [
|
||
{'code': 'LANG', 'name': 'Languages & Linguistics'},
|
||
{'code': 'MATH', 'name': 'Mathematics & Statistics'},
|
||
{'code': 'IT', 'name': 'Information Technology'},
|
||
]
|
||
|
||
ACADEMIC_YEARS = [
|
||
{'name': '2025-2026', 'start': date(2025, 9, 1), 'end': date(2026, 6, 30)},
|
||
{'name': '2026-2027', 'start': date(2026, 9, 1), 'end': date(2027, 6, 30)},
|
||
]
|
||
|
||
TERMS = {
|
||
'2025-2026': [
|
||
('Fall 2025', date(2025, 9, 1), date(2025, 12, 20)),
|
||
('Spring 2026', date(2026, 1, 15), date(2026, 5, 30)),
|
||
('Summer 2026', date(2026, 6, 1), date(2026, 8, 15)),
|
||
],
|
||
'2026-2027': [
|
||
('Fall 2026', date(2026, 9, 1), date(2026, 12, 20)),
|
||
('Spring 2027', date(2027, 1, 15), date(2027, 5, 30)),
|
||
],
|
||
}
|
||
|
||
OP_SUBJECTS = [
|
||
('ENG-101','English Grammar','theory','compulsory','LANG'),
|
||
('ENG-102','English Writing','theory','compulsory','LANG'),
|
||
('ENG-103','English Reading','theory','compulsory','LANG'),
|
||
('ENG-104','English Speaking','practical','compulsory','LANG'),
|
||
('ENG-105','IELTS Listening','practical','elective','LANG'),
|
||
('MTH-101','Calculus I','theory','compulsory','MATH'),
|
||
('MTH-102','Linear Algebra','theory','compulsory','MATH'),
|
||
('MTH-103','Probability & Stats','theory','compulsory','MATH'),
|
||
('MTH-104','Discrete Mathematics','theory','elective','MATH'),
|
||
('MTH-105','Numerical Methods','both','elective','MATH'),
|
||
('IT-101','Programming Basics','both','compulsory','IT'),
|
||
('IT-102','Database Systems','both','compulsory','IT'),
|
||
('IT-103','Web Development','practical','compulsory','IT'),
|
||
('IT-104','Networking Basics','theory','compulsory','IT'),
|
||
('IT-105','Cybersecurity Intro','theory','elective','IT'),
|
||
]
|
||
|
||
COURSES = [
|
||
('ENG-IELTS','IELTS & English Mastery','LANG','Comprehensive IELTS preparation: Reading, Writing, Listening, Speaking.',['ENG-101','ENG-102','ENG-103','ENG-104','ENG-105']),
|
||
('MATH-FND','Mathematics Foundations','MATH','Core mathematics from calculus through statistics.',['MTH-101','MTH-102','MTH-103','MTH-104','MTH-105']),
|
||
('IT-CORE','IT & Computer Science Core','IT','Essential IT: programming, databases, web dev, security.',['IT-101','IT-102','IT-103','IT-104','IT-105']),
|
||
]
|
||
|
||
BATCHES = [
|
||
('ENG-F26A','English Fall 2026 Morning','ENG-IELTS',date(2026,9,1),date(2026,12,20),30),
|
||
('ENG-F26B','English Fall 2026 Evening','ENG-IELTS',date(2026,9,1),date(2026,12,20),25),
|
||
('MTH-F26A','Math Fall 2026 Group A','MATH-FND',date(2026,9,1),date(2026,12,20),35),
|
||
('MTH-F26B','Math Fall 2026 Group B','MATH-FND',date(2026,9,1),date(2026,12,20),35),
|
||
('IT-F26A','IT Fall 2026 Lab A','IT-CORE',date(2026,9,1),date(2026,12,20),28),
|
||
('IT-F26B','IT Fall 2026 Lab B','IT-CORE',date(2026,9,1),date(2026,12,20),28),
|
||
]
|
||
|
||
TEACHERS = [
|
||
('Dr. Sarah Mitchell','sarah.mitchell@encoach.com','Sarah','Mitchell','female','LANG','IELTS & Applied Linguistics'),
|
||
('Prof. James Carter','james.carter@encoach.com','James','Carter','male','LANG','English Literature'),
|
||
('Dr. Elena Popov','elena.popov@encoach.com','Elena','Popov','female','MATH','Calculus & Analysis'),
|
||
('Prof. Ahmed Hassan','ahmed.hassan@encoach.com','Ahmed','Hassan','male','MATH','Statistics & Probability'),
|
||
('Dr. Lisa Wang','lisa.wang@encoach.com','Lisa','Wang','female','IT','Software Engineering'),
|
||
('Prof. David Kim','david.kim@encoach.com','David','Kim','male','IT','Databases & Cybersecurity'),
|
||
]
|
||
|
||
STUDENTS = [
|
||
('Emma Johnson','emma.johnson@student.encoach.com','Emma','Johnson','f',date(2004,3,15)),
|
||
('Liam Brown','liam.brown@student.encoach.com','Liam','Brown','m',date(2003,7,22)),
|
||
('Sophia Davis','sophia.davis@student.encoach.com','Sophia','Davis','f',date(2004,1,8)),
|
||
('Noah Wilson','noah.wilson@student.encoach.com','Noah','Wilson','m',date(2003,11,30)),
|
||
('Olivia Martinez','olivia.martinez@student.encoach.com','Olivia','Martinez','f',date(2004,5,19)),
|
||
('Ethan Anderson','ethan.anderson@student.encoach.com','Ethan','Anderson','m',date(2003,9,12)),
|
||
('Ava Thomas','ava.thomas@student.encoach.com','Ava','Thomas','f',date(2004,8,3)),
|
||
('Mason Jackson','mason.jackson@student.encoach.com','Mason','Jackson','m',date(2003,4,25)),
|
||
('Isabella White','isabella.white@student.encoach.com','Isabella','White','f',date(2004,12,7)),
|
||
('Lucas Harris','lucas.harris@student.encoach.com','Lucas','Harris','m',date(2003,6,18)),
|
||
('Mia Clark','mia.clark@student.encoach.com','Mia','Clark','f',date(2004,2,28)),
|
||
('Alexander Lewis','alex.lewis@student.encoach.com','Alexander','Lewis','m',date(2003,10,14)),
|
||
('Charlotte Lee','charlotte.lee@student.encoach.com','Charlotte','Lee','f',date(2004,7,9)),
|
||
('William Young','william.young@student.encoach.com','William','Young','m',date(2003,1,21)),
|
||
('Amelia Walker','amelia.walker@student.encoach.com','Amelia','Walker','f',date(2004,11,5)),
|
||
('James Hall','james.hall@student.encoach.com','James','Hall','m',date(2003,8,27)),
|
||
('Harper Allen','harper.allen@student.encoach.com','Harper','Allen','f',date(2004,4,16)),
|
||
('Benjamin King','benjamin.king@student.encoach.com','Benjamin','King','m',date(2003,12,2)),
|
||
('Evelyn Wright','evelyn.wright@student.encoach.com','Evelyn','Wright','f',date(2004,6,24)),
|
||
('Daniel Scott','daniel.scott@student.encoach.com','Daniel','Scott','m',date(2003,3,11)),
|
||
]
|
||
|
||
CLASSROOMS = [
|
||
('LNG101','Lang Lab 1',30),('LNG102','Lang Lab 2',25),
|
||
('MTH201','Math Hall A',40),('MTH202','Math Room B',20),
|
||
('IT301','Comp Lab A',30),('IT302','Comp Lab B',28),
|
||
('GEN101','Auditorium',100),('GEN102','Meeting Rm 1',15),
|
||
]
|
||
|
||
TIMINGS = [
|
||
('08:00 AM',8,0,1.5,'am',1),('09:30 AM',9,30,1.5,'am',2),
|
||
('11:00 AM',11,0,1.5,'am',3),('01:00 PM',1,0,1.5,'pm',4),
|
||
('02:30 PM',2,30,1.5,'pm',5),('04:00 PM',4,0,1.5,'pm',6),
|
||
]
|
||
|
||
DOMAINS_TOPICS = {
|
||
'english': {
|
||
'Reading': [('Skimming & Scanning','intermediate'),('Passage Comprehension','intermediate'),('True/False/Not Given','beginner'),('Matching Headings','advanced'),('Summary Completion','intermediate')],
|
||
'Writing': [('Task 1 - Data Description','intermediate'),('Task 1 - Process Diagrams','advanced'),('Task 2 - Opinion Essays','intermediate'),('Task 2 - Discussion Essays','advanced'),('Coherence & Cohesion','beginner')],
|
||
'Listening': [('Section 1 - Social Contexts','beginner'),('Section 2 - Monologues','intermediate'),('Section 3 - Academic Discussion','advanced'),('Section 4 - Academic Lecture','advanced'),('Note Completion','intermediate')],
|
||
'Speaking': [('Part 1 - Introduction','beginner'),('Part 2 - Long Turn','intermediate'),('Part 3 - Discussion','advanced'),('Pronunciation & Fluency','intermediate'),('Vocabulary Range','intermediate')],
|
||
'Grammar': [('Tenses & Aspects','beginner'),('Conditionals','intermediate'),('Relative Clauses','intermediate'),('Passive Voice','beginner'),('Complex Sentences','advanced')],
|
||
},
|
||
'math': {
|
||
'Algebra': [('Linear Equations','beginner'),('Quadratic Equations','intermediate'),('Systems of Equations','intermediate'),('Polynomials','advanced'),('Inequalities','beginner')],
|
||
'Calculus': [('Limits & Continuity','intermediate'),('Derivatives','intermediate'),('Integration Techniques','advanced'),('Applications of Integrals','advanced'),('Series & Sequences','advanced')],
|
||
'Statistics': [('Descriptive Statistics','beginner'),('Probability Distributions','intermediate'),('Hypothesis Testing','advanced'),('Regression Analysis','advanced'),('Sampling Methods','intermediate')],
|
||
'Geometry': [('Euclidean Geometry','beginner'),('Coordinate Geometry','intermediate'),('Trigonometry','intermediate'),('Vectors in 2D/3D','advanced'),('Conic Sections','advanced')],
|
||
'Logic': [('Propositional Logic','beginner'),('Set Theory','intermediate'),('Graph Theory','intermediate'),('Combinatorics','advanced'),('Mathematical Proofs','advanced')],
|
||
},
|
||
'it': {
|
||
'Programming': [('Variables & Data Types','beginner'),('Control Structures','beginner'),('Functions & Modules','intermediate'),('OOP Concepts','intermediate'),('Algorithms & Complexity','advanced')],
|
||
'Databases': [('SQL Fundamentals','beginner'),('Table Design & Normalization','intermediate'),('Joins & Subqueries','intermediate'),('Indexing & Optimization','advanced'),('NoSQL Concepts','advanced')],
|
||
'Web Development': [('HTML & CSS Basics','beginner'),('JavaScript Fundamentals','intermediate'),('React / Frontend Frameworks','advanced'),('REST APIs','intermediate'),('Responsive Design','intermediate')],
|
||
'Networking': [('OSI Model','beginner'),('TCP/IP Protocol Suite','intermediate'),('DNS & HTTP','intermediate'),('Firewalls & VPNs','advanced'),('Cloud Networking','advanced')],
|
||
'Security': [('Cybersecurity Fundamentals','beginner'),('Encryption & Hashing','intermediate'),('Auth & Authorization','intermediate'),('Vulnerability Assessment','advanced'),('Ethical Hacking Basics','advanced')],
|
||
},
|
||
}
|
||
|
||
ENTITIES = [
|
||
('MAIN-UNI','EnCoach Academy','university'),
|
||
('CORP-TECH','TechCorp Training Center','corporate'),
|
||
('FREE-TUTOR','Independent Tutors Hub','freelance'),
|
||
]
|
||
|
||
TICKETS = [
|
||
('Cannot access IELTS Writing module','bug','open','When clicking on the Writing module, the page returns a 500 error.'),
|
||
('Add dark mode to exam interface','feature','open','Students request dark mode for exam-taking.'),
|
||
('Math formula rendering broken on mobile','bug','in_progress','LaTeX formulas do not render on mobile browsers.'),
|
||
('Request: Export grades to CSV','feature','open','Teachers want CSV export for gradebook data.'),
|
||
('IT lab booking conflicts','bug','resolved','Double-booking on Computer Lab A Tuesdays.'),
|
||
('Improve attendance tracking UX','feature','in_progress','Batch check-in needed for large classes.'),
|
||
('Student portal login timeout too short','bug','open','Session expires after only 5 minutes.'),
|
||
('Add video conferencing for Speaking tests','feature','open','Integration with Zoom/Meet for remote sessions.'),
|
||
('Broken link in Chapter 3 materials','bug','open','PDF link 404 in English Writing Chapter 3.'),
|
||
('Request: Parent portal access','feature','open','Parents want read-only access to student progress.'),
|
||
]
|
||
|
||
FAQ_DATA = [
|
||
('Getting Started', [
|
||
('How do I enroll in a course?', 'Navigate to Courses, select your desired course, click "Enroll". Your advisor approves within 24 hours.'),
|
||
('What are system requirements?', 'Modern browser (Chrome, Firefox, Safari, Edge) with JS enabled. For IT labs, laptop with 8GB+ RAM.'),
|
||
('How do I reset my password?', 'Click "Forgot Password" on login, enter your email. Reset link arrives in minutes.'),
|
||
]),
|
||
('Exams & Assessments', [
|
||
('How are IELTS mock exams scored?', 'Official 9-band scoring. AI grading gives immediate feedback; teacher review within 48h.'),
|
||
('Can I retake a failed exam?', 'Yes, up to 3 retakes per semester, at least 1 week apart.'),
|
||
('What if internet drops during timed exam?', 'Auto-saves every 30s. Resume within 15 minutes of disconnection.'),
|
||
]),
|
||
('Technical Support', [
|
||
('Platform loading slowly', 'Clear cache, disable extensions, or switch to Chrome. Report browser version if issue persists.'),
|
||
('Cannot upload assignment', 'Supported: PDF, DOCX, TXT, ZIP (max 50MB). For code, use ZIP.'),
|
||
]),
|
||
('Billing & Payments', [
|
||
('What payment methods are accepted?', 'Visa, MasterCard, PayPal, bank transfers. Installment plans available.'),
|
||
('How do I get a refund?', 'Submit within 14 days of enrollment. Contact billing@encoach.com with enrollment ID.'),
|
||
]),
|
||
]
|
||
|
||
# ═══════════════════════════════════════════════════
|
||
# Main seed function
|
||
# ═══════════════════════════════════════════════════
|
||
def seed():
|
||
conn = psycopg2.connect(**DB)
|
||
conn.autocommit = False
|
||
cur = conn.cursor()
|
||
step = [0]
|
||
total_steps = 30
|
||
|
||
def log(msg):
|
||
step[0] += 1
|
||
print(f"[{step[0]:2d}/{total_steps}] {msg}")
|
||
|
||
print("=" * 65)
|
||
print(" EnCoach LMS – Full Demo Data Seeder")
|
||
print("=" * 65)
|
||
|
||
try:
|
||
# ══════════════════════════════════════════
|
||
# SECTION A – ACADEMIC FOUNDATION
|
||
# ══════════════════════════════════════════
|
||
|
||
# 1. Departments
|
||
log("Departments...")
|
||
dept_ids = {}
|
||
for d in DEPARTMENTS:
|
||
dept_ids[d['code']] = find_or_create(cur, 'op_department', 'code', {'code': d['code'], 'name': d['name'], **AU})
|
||
print(f" {len(dept_ids)} departments")
|
||
|
||
# 2. Academic years & terms
|
||
log("Academic years & terms...")
|
||
ay_ids = {}
|
||
for ay in ACADEMIC_YEARS:
|
||
ay_ids[ay['name']] = find_or_create(cur, 'op_academic_year', 'name', {
|
||
'name': ay['name'], 'start_date': ay['start'], 'end_date': ay['end'],
|
||
'term_structure': 'semester', 'company_id': 1, **AU,
|
||
})
|
||
term_ids = {}
|
||
for ay_name, terms in TERMS.items():
|
||
for n, s, e in terms:
|
||
term_ids[n] = find_or_create(cur, 'op_academic_term', 'name', {
|
||
'name': n, 'term_start_date': s, 'term_end_date': e,
|
||
'academic_year_id': ay_ids[ay_name], 'company_id': 1, **AU,
|
||
})
|
||
print(f" {len(ay_ids)} years, {len(term_ids)} terms")
|
||
|
||
# 3. Op Subjects
|
||
log("Academic subjects...")
|
||
dept_map = {'ENG': 'LANG', 'MTH': 'MATH', 'IT': 'IT'}
|
||
subj_ids = {}
|
||
for code, name, typ, stype, dept in OP_SUBJECTS:
|
||
subj_ids[code] = find_or_create(cur, 'op_subject', 'code', {
|
||
'code': code, 'name': name, 'type': typ, 'subject_type': stype,
|
||
'active': True, 'department_id': dept_ids[dept], **AU,
|
||
})
|
||
print(f" {len(subj_ids)} subjects")
|
||
|
||
# 4. Courses + subject links
|
||
log("Courses...")
|
||
course_ids = {}
|
||
for code, name, dept, desc, subs in COURSES:
|
||
cid = find_or_create(cur, 'op_course', 'code', {
|
||
'code': code, 'name': j(name), 'active': True,
|
||
'department_id': dept_ids[dept], 'evaluation_type': 'normal',
|
||
'description': desc, 'status': 'active', 'company_id': 1, **AU,
|
||
})
|
||
course_ids[code] = cid
|
||
for sc in subs:
|
||
if sc in subj_ids:
|
||
m2m(cur, 'op_course_op_subject_rel', 'op_course_id', 'op_subject_id', cid, subj_ids[sc])
|
||
print(f" {len(course_ids)} courses")
|
||
|
||
# 5. Batches
|
||
log("Batches...")
|
||
batch_ids = {}
|
||
batch_course = {}
|
||
for code, name, crs, s, e, mx in BATCHES:
|
||
batch_ids[code] = find_or_create(cur, 'op_batch', 'code', {
|
||
'code': code, 'name': name, 'active': True,
|
||
'course_id': course_ids[crs], 'start_date': s, 'end_date': e,
|
||
'max_students': mx, 'status': 'active', 'company_id': 1, **AU,
|
||
})
|
||
batch_course[code] = crs
|
||
print(f" {len(batch_ids)} batches")
|
||
|
||
# 6. Faculty
|
||
log("Faculty...")
|
||
faculty_ids = {}
|
||
faculty_uids = {}
|
||
for name, email, first, last, gender, dept, spec in TEACHERS:
|
||
uid = ensure_user(cur, name, email)
|
||
cur.execute("SELECT partner_id FROM res_users WHERE id=%s", (uid,))
|
||
pid = cur.fetchone()[0]
|
||
cur.execute("SELECT id FROM op_faculty WHERE partner_id=%s", (pid,))
|
||
frow = cur.fetchone()
|
||
fid = frow[0] if frow else insert(cur, 'op_faculty', {
|
||
'partner_id': pid, 'first_name': j(first), 'last_name': last,
|
||
'gender': gender, 'birth_date': date(1985, 6, 15), 'active': True,
|
||
'department_id': dept_ids[dept], 'specialization': spec, 'company_id': 1, **AU,
|
||
})
|
||
faculty_ids[email] = fid
|
||
faculty_uids[email] = uid
|
||
print(f" {len(faculty_ids)} faculty")
|
||
|
||
# 7. Students
|
||
log("Students...")
|
||
student_ids = {}
|
||
student_uids = {}
|
||
batch_list = list(batch_ids.keys())
|
||
for i, (name, email, first, last, gender, dob) in enumerate(STUDENTS):
|
||
uid = ensure_user(cur, name, email)
|
||
cur.execute("SELECT partner_id FROM res_users WHERE id=%s", (uid,))
|
||
pid = cur.fetchone()[0]
|
||
cur.execute("SELECT id FROM op_student WHERE partner_id=%s", (pid,))
|
||
srow = cur.fetchone()
|
||
sid = srow[0] if srow else insert(cur, 'op_student', {
|
||
'partner_id': pid, 'user_id': uid, 'first_name': j(first), 'last_name': j(last),
|
||
'gender': gender, 'birth_date': dob, 'gr_no': f'STU-2026{i+1:04d}',
|
||
'active': True, 'status': 'active', 'enrollment_date': date(2026, 8, 15),
|
||
'company_id': 1, **AU,
|
||
})
|
||
student_ids[email] = sid
|
||
student_uids[email] = uid
|
||
b = batch_list[i % len(batch_list)]
|
||
crs = batch_course[b]
|
||
cur.execute("INSERT INTO op_student_course (student_id,course_id,batch_id,roll_number,state,create_uid,write_uid) VALUES(%s,%s,%s,%s,'running',2,2) ON CONFLICT DO NOTHING",
|
||
(sid, course_ids[crs], batch_ids[b], f'R-{i+1:04d}'))
|
||
print(f" {len(student_ids)} students")
|
||
|
||
# 8. Classrooms
|
||
log("Classrooms...")
|
||
cls_ids = {}
|
||
for code, name, cap in CLASSROOMS:
|
||
cls_ids[code] = find_or_create(cur, 'op_classroom', 'code', {
|
||
'code': code, 'name': name, 'capacity': cap, 'active': True, 'company_id': 1, **AU,
|
||
})
|
||
print(f" {len(cls_ids)} classrooms")
|
||
|
||
# 9. Timings
|
||
log("Timings...")
|
||
timing_ids = {}
|
||
for name, h, m, dur, ampm, seq in TIMINGS:
|
||
timing_ids[name] = find_or_create(cur, 'op_timing', 'name', {
|
||
'name': name, 'hour': h, 'minute': m, 'duration': dur, 'am_pm': ampm, 'sequence': seq, **AU,
|
||
})
|
||
print(f" {len(timing_ids)} timings")
|
||
|
||
# 10. Timetable sessions
|
||
log("Timetable sessions...")
|
||
sess_defs = [
|
||
('ENG-F26A','ENG-101','sarah.mitchell@encoach.com','LNG101','08:00 AM',0),
|
||
('ENG-F26A','ENG-102','james.carter@encoach.com','LNG101','09:30 AM',1),
|
||
('ENG-F26A','ENG-103','sarah.mitchell@encoach.com','LNG102','11:00 AM',2),
|
||
('ENG-F26B','ENG-104','james.carter@encoach.com','LNG102','01:00 PM',0),
|
||
('ENG-F26B','ENG-105','sarah.mitchell@encoach.com','LNG101','02:30 PM',1),
|
||
('MTH-F26A','MTH-101','elena.popov@encoach.com','MTH201','08:00 AM',2),
|
||
('MTH-F26A','MTH-102','ahmed.hassan@encoach.com','MTH201','09:30 AM',3),
|
||
('MTH-F26A','MTH-103','elena.popov@encoach.com','MTH202','11:00 AM',4),
|
||
('MTH-F26B','MTH-104','ahmed.hassan@encoach.com','MTH202','01:00 PM',0),
|
||
('MTH-F26B','MTH-105','elena.popov@encoach.com','MTH201','02:30 PM',1),
|
||
('IT-F26A','IT-101','lisa.wang@encoach.com','IT301','08:00 AM',2),
|
||
('IT-F26A','IT-102','david.kim@encoach.com','IT301','09:30 AM',3),
|
||
('IT-F26A','IT-103','lisa.wang@encoach.com','IT302','11:00 AM',4),
|
||
('IT-F26B','IT-104','david.kim@encoach.com','IT302','01:00 PM',0),
|
||
('IT-F26B','IT-105','lisa.wang@encoach.com','IT301','02:30 PM',3),
|
||
]
|
||
sess_count = 0
|
||
base_dt = datetime(2026, 9, 7)
|
||
for bc, sc, te, rm, tn, day in sess_defs:
|
||
t_info = next(t for t in TIMINGS if t[0] == tn)
|
||
h = t_info[1] + (12 if t_info[4] == 'pm' and t_info[1] != 12 else 0)
|
||
sdt = base_dt + timedelta(days=day, hours=h, minutes=t_info[2])
|
||
insert(cur, 'op_session', {
|
||
'course_id': course_ids[batch_course[bc]], 'batch_id': batch_ids[bc],
|
||
'subject_id': subj_ids[sc], 'faculty_id': faculty_ids[te],
|
||
'classroom_id': cls_ids.get(rm), 'timing_id': timing_ids.get(tn),
|
||
'start_datetime': sdt, 'end_datetime': sdt + timedelta(hours=1, minutes=30),
|
||
'state': 'confirm', 'active': True, 'color': random.randint(1, 10), 'company_id': 1, **AU,
|
||
})
|
||
sess_count += 1
|
||
print(f" {sess_count} timetable sessions")
|
||
|
||
# 11. Taxonomy
|
||
log("Taxonomy (domains, topics, learning objectives)...")
|
||
cur.execute("DELETE FROM encoach_learning_objective")
|
||
cur.execute("DELETE FROM encoach_content_cache")
|
||
cur.execute("DELETE FROM encoach_proficiency")
|
||
cur.execute("DELETE FROM encoach_topic")
|
||
cur.execute("DELETE FROM encoach_domain")
|
||
cur.execute("SELECT id, code FROM encoach_subject")
|
||
ec_subj = {r[1]: r[0] for r in cur.fetchall()}
|
||
dom_ids = {}
|
||
topic_ids_list = []
|
||
topic_by_subj = {}
|
||
bloom = ['remember','understand','apply','analyze','evaluate','create']
|
||
dom_c, top_c, lo_c = 0, 0, 0
|
||
for subj_code, domains in DOMAINS_TOPICS.items():
|
||
sid = ec_subj.get(subj_code)
|
||
if not sid:
|
||
continue
|
||
topic_by_subj[subj_code] = []
|
||
for dom_name, topics in domains.items():
|
||
did = insert(cur, 'encoach_domain', {
|
||
'subject_id': sid, 'name': dom_name,
|
||
'description': f'{dom_name} domain for {subj_code}', **AU,
|
||
})
|
||
dom_ids[f'{subj_code}_{dom_name}'] = did
|
||
dom_c += 1
|
||
for topic_name, diff in topics:
|
||
tid = insert(cur, 'encoach_topic', {
|
||
'domain_id': did, 'subject_id': sid,
|
||
'name': topic_name, 'difficulty': diff,
|
||
'description': f'{topic_name} - {diff} level', **AU,
|
||
})
|
||
topic_ids_list.append(tid)
|
||
topic_by_subj[subj_code].append(tid)
|
||
top_c += 1
|
||
lo_id = insert(cur, 'encoach_learning_objective', {
|
||
'topic_id': tid, 'name': f'Understand {topic_name}',
|
||
'bloom_level': random.choice(bloom),
|
||
'description': f'Student can demonstrate understanding of {topic_name}', **AU,
|
||
})
|
||
lo_c += 1
|
||
print(f" {dom_c} domains, {top_c} topics, {lo_c} learning objectives")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION B – INSTITUTIONAL
|
||
# ══════════════════════════════════════════
|
||
|
||
# 12. Entities + branding + invite codes
|
||
log("Entities, branding, invite codes...")
|
||
ent_ids = {}
|
||
for code, name, etype in ENTITIES:
|
||
ent_ids[code] = find_or_create(cur, 'encoach_entity', 'code', {
|
||
'code': code, 'name': name, 'type': etype, 'active': True, **AU,
|
||
})
|
||
main_ent = ent_ids['MAIN-UNI']
|
||
brandings = [
|
||
(ent_ids['MAIN-UNI'], '#1a56db', '#7c3aed', 'EnCoach Academy'),
|
||
(ent_ids['CORP-TECH'], '#059669', '#0891b2', 'TechCorp LMS'),
|
||
(ent_ids['FREE-TUTOR'], '#dc2626', '#f59e0b', 'TutorHub'),
|
||
]
|
||
for eid, pc, sc, an in brandings:
|
||
cur.execute("SELECT id FROM encoach_branding WHERE entity_id=%s", (eid,))
|
||
if not cur.fetchone():
|
||
insert(cur, 'encoach_branding', {
|
||
'entity_id': eid, 'primary_color': pc, 'secondary_color': sc, 'app_name': an, **AU,
|
||
})
|
||
invite_codes = [
|
||
('STUDENT-2026', 'student', main_ent, 100), ('TEACHER-2026', 'teacher', main_ent, 20),
|
||
('ADMIN-JOIN', 'admin', main_ent, 5), ('CORP-ACCESS', 'corporate', ent_ids['CORP-TECH'], 50),
|
||
]
|
||
for code, utype, eid, mx in invite_codes:
|
||
find_or_create(cur, 'encoach_invite_code', 'code', {
|
||
'code': code, 'user_type': utype, 'entity_id': eid,
|
||
'max_uses': mx, 'current_uses': 0, 'active': True, **AU,
|
||
})
|
||
# Link users to entity
|
||
all_uids = list(student_uids.values()) + list(faculty_uids.values()) + [2]
|
||
for uid in all_uids:
|
||
m2m(cur, 'encoach_entity_user_rel', 'entity_id', 'user_id', main_ent, uid)
|
||
print(f" {len(ent_ids)} entities, 3 brandings, {len(invite_codes)} invite codes")
|
||
|
||
# 13. Roles & user-role links
|
||
log("Roles & user-role assignments...")
|
||
cur.execute("SELECT id, name FROM encoach_role")
|
||
existing_roles = {r[1]: r[0] for r in cur.fetchall()}
|
||
role_names = {
|
||
'Exam Manager': 'Manages exam creation, scheduling and grading workflows',
|
||
'Student Advisor': 'Advises students on course selection and academic progress',
|
||
'Report Viewer': 'Read-only access to reports and analytics dashboards',
|
||
'Course Admin': 'Full control over course content, chapters, and materials',
|
||
'IT Support': 'Handles technical support tickets and system configuration',
|
||
}
|
||
for rn, desc in role_names.items():
|
||
if rn not in existing_roles:
|
||
existing_roles[rn] = insert(cur, 'encoach_role', {
|
||
'name': rn, 'description': desc, 'entity_id': main_ent, **AU,
|
||
})
|
||
teachers_list = list(faculty_uids.values())
|
||
m2m(cur, 'encoach_role_user_rel', 'role_id', 'user_id', existing_roles['Exam Manager'], teachers_list[0])
|
||
m2m(cur, 'encoach_role_user_rel', 'role_id', 'user_id', existing_roles['Exam Manager'], teachers_list[2])
|
||
m2m(cur, 'encoach_role_user_rel', 'role_id', 'user_id', existing_roles['Student Advisor'], teachers_list[1])
|
||
m2m(cur, 'encoach_role_user_rel', 'role_id', 'user_id', existing_roles['Student Advisor'], teachers_list[3])
|
||
m2m(cur, 'encoach_role_user_rel', 'role_id', 'user_id', existing_roles['Course Admin'], teachers_list[4])
|
||
m2m(cur, 'encoach_role_user_rel', 'role_id', 'user_id', existing_roles['IT Support'], teachers_list[5])
|
||
m2m(cur, 'encoach_role_user_rel', 'role_id', 'user_id', existing_roles['Report Viewer'], 2)
|
||
cur.execute("SELECT id FROM encoach_permission LIMIT 20")
|
||
perm_sample = [r[0] for r in cur.fetchall()]
|
||
for rid in existing_roles.values():
|
||
for pid in random.sample(perm_sample, min(8, len(perm_sample))):
|
||
m2m(cur, 'encoach_role_permission_rel', 'role_id', 'permission_id', rid, pid)
|
||
print(f" {len(existing_roles)} roles, user-role + role-permission links set")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION C – LMS / COURSEWARE
|
||
# ══════════════════════════════════════════
|
||
|
||
# 14. Course chapters & materials
|
||
log("Course chapters & materials...")
|
||
chapter_data = {
|
||
'ENG-IELTS': [
|
||
('Introduction to IELTS', 'Overview of the IELTS exam format, scoring, and strategies.', [
|
||
('IELTS Overview Guide', 'pdf', 'https://example.com/ielts-guide.pdf'),
|
||
('Band Score Explained (Video)', 'video', 'https://example.com/band-score.mp4'),
|
||
('Practice Test Walkthrough', 'link', 'https://example.com/practice-test'),
|
||
]),
|
||
('Reading Skills', 'Master IELTS reading techniques for Academic and General modules.', [
|
||
('Skimming Techniques PDF', 'pdf', 'https://example.com/skimming.pdf'),
|
||
('Reading Practice Set 1', 'document', 'https://example.com/reading-set1.docx'),
|
||
('Speed Reading Video', 'video', 'https://example.com/speed-reading.mp4'),
|
||
]),
|
||
('Writing Task 1', 'Describing charts, graphs, tables and processes.', [
|
||
('Task 1 Writing Guide', 'pdf', 'https://example.com/task1-guide.pdf'),
|
||
('Sample Answers Collection', 'document', 'https://example.com/samples.docx'),
|
||
('Data Description Workshop', 'video', 'https://example.com/workshop.mp4'),
|
||
('Practice Templates', 'document', 'https://example.com/templates.docx'),
|
||
]),
|
||
('Writing Task 2', 'Essay writing: opinion, discussion, problem-solution types.', [
|
||
('Essay Structure Guide', 'pdf', 'https://example.com/essay-guide.pdf'),
|
||
('Argumentative Writing Tips', 'article', 'https://example.com/arg-tips'),
|
||
('Vocabulary for Essays', 'pdf', 'https://example.com/vocab-essays.pdf'),
|
||
]),
|
||
('Listening Skills', 'Practice all four sections of the IELTS Listening test.', [
|
||
('Listening Section Overview', 'pdf', 'https://example.com/listening-overview.pdf'),
|
||
('Audio Practice Set 1', 'audio', 'https://example.com/listen-set1.mp3'),
|
||
('Note-taking Strategies', 'video', 'https://example.com/note-taking.mp4'),
|
||
]),
|
||
('Speaking Practice', 'Prepare for all three parts of the IELTS Speaking test.', [
|
||
('Speaking Part 1 Common Topics', 'pdf', 'https://example.com/speaking-p1.pdf'),
|
||
('Cue Card Examples', 'document', 'https://example.com/cue-cards.docx'),
|
||
('Fluency Drills (Video)', 'video', 'https://example.com/fluency.mp4'),
|
||
]),
|
||
],
|
||
'MATH-FND': [
|
||
('Pre-Calculus Review', 'Algebra and function foundations before diving into calculus.', [
|
||
('Algebra Refresher', 'pdf', 'https://example.com/algebra-refresh.pdf'),
|
||
('Functions & Graphs', 'video', 'https://example.com/functions.mp4'),
|
||
('Practice Problems Set', 'document', 'https://example.com/precalc-problems.docx'),
|
||
]),
|
||
('Limits & Derivatives', 'Understanding limits, continuity, and basic derivatives.', [
|
||
('Limits Explained', 'pdf', 'https://example.com/limits.pdf'),
|
||
('Derivative Rules Cheatsheet', 'pdf', 'https://example.com/deriv-rules.pdf'),
|
||
('Video: Chain Rule', 'video', 'https://example.com/chain-rule.mp4'),
|
||
]),
|
||
('Integration', 'Definite and indefinite integrals, techniques of integration.', [
|
||
('Integration Fundamentals', 'pdf', 'https://example.com/integrals.pdf'),
|
||
('Practice: Substitution Method', 'document', 'https://example.com/substitution.docx'),
|
||
('Integration by Parts (Video)', 'video', 'https://example.com/by-parts.mp4'),
|
||
]),
|
||
('Linear Algebra', 'Vectors, matrices, determinants, and eigenvalues.', [
|
||
('Matrix Operations Guide', 'pdf', 'https://example.com/matrices.pdf'),
|
||
('Eigenvalues Explained', 'video', 'https://example.com/eigenvalues.mp4'),
|
||
('LA Problem Set', 'document', 'https://example.com/la-problems.docx'),
|
||
]),
|
||
('Probability & Statistics', 'Distributions, hypothesis testing, regression.', [
|
||
('Probability Basics', 'pdf', 'https://example.com/prob-basics.pdf'),
|
||
('Hypothesis Testing Guide', 'pdf', 'https://example.com/hyp-test.pdf'),
|
||
('Stats with Python (Notebook)', 'link', 'https://example.com/stats-python'),
|
||
('Regression Analysis Video', 'video', 'https://example.com/regression.mp4'),
|
||
]),
|
||
],
|
||
'IT-CORE': [
|
||
('Programming Fundamentals', 'Variables, control flow, functions in Python.', [
|
||
('Python Basics Guide', 'pdf', 'https://example.com/python-basics.pdf'),
|
||
('Coding Exercises Set 1', 'document', 'https://example.com/exercises-1.docx'),
|
||
('Intro to Python (Video)', 'video', 'https://example.com/python-intro.mp4'),
|
||
]),
|
||
('Object-Oriented Programming', 'Classes, inheritance, polymorphism, design patterns.', [
|
||
('OOP Concepts PDF', 'pdf', 'https://example.com/oop.pdf'),
|
||
('Design Patterns Overview', 'pdf', 'https://example.com/patterns.pdf'),
|
||
('OOP Lab Exercises', 'document', 'https://example.com/oop-lab.docx'),
|
||
]),
|
||
('Database Design', 'Relational databases, SQL, normalization, indexing.', [
|
||
('SQL Crash Course', 'pdf', 'https://example.com/sql-crash.pdf'),
|
||
('Normalization Tutorial', 'video', 'https://example.com/normalization.mp4'),
|
||
('Database Lab: Build a Schema', 'link', 'https://example.com/db-lab'),
|
||
]),
|
||
('Web Development', 'HTML, CSS, JavaScript, React, REST APIs.', [
|
||
('HTML & CSS Fundamentals', 'pdf', 'https://example.com/html-css.pdf'),
|
||
('JavaScript ES6+ Guide', 'pdf', 'https://example.com/js-guide.pdf'),
|
||
('Build a React App (Video)', 'video', 'https://example.com/react-app.mp4'),
|
||
('REST API Design Best Practices', 'article', 'https://example.com/rest-api'),
|
||
]),
|
||
('Networking & Security', 'OSI model, TCP/IP, firewalls, encryption basics.', [
|
||
('Networking Fundamentals', 'pdf', 'https://example.com/networking.pdf'),
|
||
('Security Best Practices', 'pdf', 'https://example.com/security.pdf'),
|
||
('Wireshark Lab Tutorial', 'video', 'https://example.com/wireshark.mp4'),
|
||
]),
|
||
('Capstone Project', 'Full-stack application development project.', [
|
||
('Project Requirements Doc', 'pdf', 'https://example.com/capstone-req.pdf'),
|
||
('Architecture Template', 'document', 'https://example.com/arch-template.docx'),
|
||
('Deployment Guide', 'link', 'https://example.com/deploy-guide'),
|
||
]),
|
||
],
|
||
}
|
||
chap_count, mat_count = 0, 0
|
||
chapter_ids_by_course = {}
|
||
for crs_code, chapters in chapter_data.items():
|
||
cid = course_ids.get(crs_code)
|
||
if not cid:
|
||
continue
|
||
chapter_ids_by_course[crs_code] = []
|
||
for seq, (ch_name, ch_desc, materials) in enumerate(chapters, 1):
|
||
ch_id = insert(cur, 'encoach_course_chapter', {
|
||
'course_id': cid, 'name': ch_name, 'sequence': seq * 10,
|
||
'description': ch_desc, 'unlock_mode': 'manual', 'is_unlocked': seq <= 2,
|
||
'active': True, **AU,
|
||
})
|
||
chapter_ids_by_course[crs_code].append(ch_id)
|
||
chap_count += 1
|
||
for mseq, (m_name, m_type, m_url) in enumerate(materials, 1):
|
||
insert(cur, 'encoach_chapter_material', {
|
||
'chapter_id': ch_id, 'name': m_name, 'type': m_type,
|
||
'url': m_url, 'sequence': mseq * 10, 'allow_download': True,
|
||
'active': True, **AU,
|
||
})
|
||
mat_count += 1
|
||
print(f" {chap_count} chapters, {mat_count} materials")
|
||
|
||
# 15. Student progress
|
||
log("Student chapter progress...")
|
||
prog_c = 0
|
||
statuses = ['completed', 'in_progress', 'not_started']
|
||
student_list = list(student_uids.values())
|
||
for crs_code, ch_list in chapter_ids_by_course.items():
|
||
for uid in student_list[:10]:
|
||
for ci, ch_id in enumerate(ch_list):
|
||
st = statuses[0] if ci < 2 else (statuses[1] if ci == 2 else statuses[2])
|
||
cur.execute("INSERT INTO encoach_chapter_progress (student_id,chapter_id,status,materials_completed,started_at,completed_at,create_uid,write_uid) VALUES(%s,%s,%s,%s,%s,%s,2,2) ON CONFLICT DO NOTHING",
|
||
(uid, ch_id, st,
|
||
3 if st == 'completed' else (1 if st == 'in_progress' else 0),
|
||
datetime.now() - timedelta(days=30-ci*5) if st != 'not_started' else None,
|
||
datetime.now() - timedelta(days=20-ci*5) if st == 'completed' else None))
|
||
prog_c += 1
|
||
print(f" {prog_c} progress records")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION D – EXAMS (EnCoach)
|
||
# ══════════════════════════════════════════
|
||
|
||
# 16. Exam structures, rubrics, exams
|
||
log("Exam structures, rubrics & exams...")
|
||
cur.execute("DELETE FROM encoach_evaluation_job")
|
||
cur.execute("DELETE FROM encoach_exam_session_stat")
|
||
cur.execute("DELETE FROM encoach_exam_session")
|
||
cur.execute("DELETE FROM encoach_exam_topic_rel")
|
||
cur.execute("DELETE FROM encoach_course_chapter_encoach_exam_rel")
|
||
cur.execute("DELETE FROM encoach_exam WHERE id > 0")
|
||
cur.execute("DELETE FROM encoach_exam_rubric")
|
||
cur.execute("DELETE FROM encoach_rubric_group")
|
||
cur.execute("DELETE FROM encoach_exam_structure")
|
||
|
||
modules = ['reading','writing','listening','speaking','math','it']
|
||
struct_ids = {}
|
||
for mod in modules:
|
||
struct_ids[mod] = insert(cur, 'encoach_exam_structure', {
|
||
'name': f'{mod.title()} Exam Structure', 'module': mod,
|
||
'entity_id': main_ent, 'creator_id': 2,
|
||
'config': json.dumps({'sections': 4, 'time_per_section': 15}),
|
||
'is_default': True, **AU,
|
||
})
|
||
|
||
rg_id = insert(cur, 'encoach_rubric_group', {'name': 'Standard Assessment Rubrics', **AU})
|
||
rubric_ids = {}
|
||
for mod in modules:
|
||
rubric_ids[mod] = insert(cur, 'encoach_exam_rubric', {
|
||
'name': f'{mod.title()} Rubric', 'module': mod,
|
||
'group_id': rg_id, 'entity_id': main_ent,
|
||
'criteria': json.dumps([
|
||
{'name': 'Accuracy', 'weight': 40, 'levels': ['Poor','Fair','Good','Excellent']},
|
||
{'name': 'Completeness', 'weight': 30, 'levels': ['Incomplete','Partial','Mostly','Full']},
|
||
{'name': 'Presentation', 'weight': 30, 'levels': ['Weak','Adequate','Strong','Outstanding']},
|
||
]),
|
||
'max_score': 100, **AU,
|
||
})
|
||
|
||
exam_defs = [
|
||
('IELTS Reading Mock Test 1','reading','english','published','medium',60),
|
||
('IELTS Writing Practice Exam','writing','english','published','hard',60),
|
||
('IELTS Listening Simulation','listening','english','published','medium',30),
|
||
('IELTS Speaking Assessment','speaking','english','draft','medium',15),
|
||
('Calculus Midterm Exam','math','math','published','medium',90),
|
||
('Linear Algebra Quiz','math','math','published','easy',30),
|
||
('Statistics Final Exam','math','math','published','hard',120),
|
||
('Programming Fundamentals Test','it','it','published','easy',45),
|
||
('Database Design Exam','it','it','published','medium',60),
|
||
('Web Development Project Eval','it','it','draft','hard',90),
|
||
('Cybersecurity Challenge','it','it','published','hard',60),
|
||
('Grammar Mastery Test','writing','english','published','easy',30),
|
||
]
|
||
exam_ids_map = {}
|
||
for ename, mod, subj, status, diff, time_limit in exam_defs:
|
||
eid = insert(cur, 'encoach_exam', {
|
||
'name': ename, 'module': mod, 'subject_id': ec_subj[subj],
|
||
'entity_id': main_ent, 'creator_id': 2,
|
||
'structure_id': struct_ids[mod], 'rubric_id': rubric_ids[mod],
|
||
'status': status, 'difficulty': diff, 'time_limit': time_limit,
|
||
'is_public': True, 'approval_status': 'approved' if status == 'published' else 'none',
|
||
'exercises': json.dumps([{'q': f'Question {i+1}', 'type': 'mcq', 'options': ['A','B','C','D'], 'answer': random.choice(['A','B','C','D'])} for i in range(10)]),
|
||
**AU,
|
||
})
|
||
exam_ids_map[ename] = eid
|
||
topics = topic_by_subj.get(subj, [])
|
||
for tid in random.sample(topics, min(3, len(topics))):
|
||
m2m(cur, 'encoach_exam_topic_rel', 'exam_id', 'topic_id', eid, tid)
|
||
for crs, chs in chapter_ids_by_course.items():
|
||
exams_for_course = [eid for en, eid in exam_ids_map.items()
|
||
if ('IELTS' in en and crs == 'ENG-IELTS') or
|
||
('Calculus' in en and crs == 'MATH-FND') or ('Linear' in en and crs == 'MATH-FND') or
|
||
('Programming' in en and crs == 'IT-CORE') or ('Database' in en and crs == 'IT-CORE')]
|
||
for i, eid in enumerate(exams_for_course[:len(chs)]):
|
||
m2m(cur, 'encoach_course_chapter_encoach_exam_rel', 'encoach_course_chapter_id', 'encoach_exam_id', chs[i], eid)
|
||
print(f" {len(struct_ids)} structures, {len(rubric_ids)} rubrics, {len(exam_ids_map)} exams")
|
||
|
||
# 17. Exam sessions, evaluations & stats
|
||
log("Exam sessions, evaluations & stats...")
|
||
sess_c, eval_c, stat_c = 0, 0, 0
|
||
published_exams = [(en, eid) for en, eid in exam_ids_map.items() if 'draft' not in en.lower()]
|
||
for ename, eid in published_exams[:8]:
|
||
for uid in student_list[:8]:
|
||
score = round(random.uniform(40, 98), 1)
|
||
band = round(score / 11.1, 1)
|
||
ts = random.randint(600, 3600)
|
||
started = datetime.now() - timedelta(days=random.randint(5, 30))
|
||
completed = started + timedelta(seconds=ts)
|
||
es_id = insert(cur, 'encoach_exam_session', {
|
||
'user_id': uid, 'exam_id': eid, 'status': 'graded',
|
||
'score': score, 'band_score': band, 'time_spent': ts,
|
||
'started_at': started, 'completed_at': completed,
|
||
'answers': json.dumps([{'q': i, 'answer': random.choice(['A','B','C','D'])} for i in range(10)]),
|
||
'feedback': json.dumps({'overall': 'Good effort', 'areas_to_improve': ['time management']}),
|
||
**AU,
|
||
})
|
||
sess_c += 1
|
||
insert(cur, 'encoach_evaluation_job', {
|
||
'session_id': es_id, 'eval_type': 'multiple_choice',
|
||
'status': 'completed',
|
||
'input_data': json.dumps({'session_id': es_id}),
|
||
'result_data': json.dumps({'score': score, 'band': band}),
|
||
'created_at': started, 'completed_at': completed, **AU,
|
||
})
|
||
eval_c += 1
|
||
insert(cur, 'encoach_exam_session_stat', {
|
||
'user_id': uid, 'exam_id': eid, 'entity_id': main_ent,
|
||
'module': ename.split()[0].lower()[:10],
|
||
'score': score, 'band_score': band, 'time_spent': ts,
|
||
'started_at': started, 'completed_at': completed, **AU,
|
||
})
|
||
stat_c += 1
|
||
print(f" {sess_c} sessions, {eval_c} evaluations, {stat_c} stats")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION E – OP EXAMS (OpenEduCat)
|
||
# ══════════════════════════════════════════
|
||
|
||
# 18. Op exam types, sessions, exams, attendees
|
||
log("OpenEduCat exams (types, sessions, exams, attendees)...")
|
||
op_et = {}
|
||
for code, name in [('MID','Midterm'),('FIN','Final'),('QZ','Quiz')]:
|
||
op_et[code] = find_or_create(cur, 'op_exam_type', 'code', {'code': code, 'name': name, 'company_id': 1, **AU})
|
||
|
||
op_es_data = [
|
||
('ENG-MID-F26','English Midterm Fall 2026','ENG-IELTS','ENG-F26A','MID',date(2026,10,15),date(2026,10,18)),
|
||
('MTH-MID-F26','Math Midterm Fall 2026','MATH-FND','MTH-F26A','MID',date(2026,10,20),date(2026,10,22)),
|
||
('IT-FIN-F26','IT Final Fall 2026','IT-CORE','IT-F26A','FIN',date(2026,12,10),date(2026,12,15)),
|
||
]
|
||
op_exam_sess = {}
|
||
for ecode, ename, crs, batch, etype, sd, ed in op_es_data:
|
||
op_exam_sess[ecode] = find_or_create(cur, 'op_exam_session', 'exam_code', {
|
||
'exam_code': ecode, 'name': ename,
|
||
'course_id': course_ids[crs], 'batch_id': batch_ids[batch],
|
||
'exam_type': op_et[etype], 'evaluation_type': 'normal',
|
||
'start_date': sd, 'end_date': ed, 'state': 'schedule',
|
||
'active': True, 'company_id': 1, **AU,
|
||
})
|
||
|
||
op_exam_data = [
|
||
('ENG-R-MID','English Reading Midterm','ENG-MID-F26','ENG-101',datetime(2026,10,15,9,0),datetime(2026,10,15,11,0),100,40),
|
||
('ENG-W-MID','English Writing Midterm','ENG-MID-F26','ENG-102',datetime(2026,10,16,9,0),datetime(2026,10,16,11,0),100,40),
|
||
('MTH-C-MID','Calculus Midterm','MTH-MID-F26','MTH-101',datetime(2026,10,20,9,0),datetime(2026,10,20,11,0),100,50),
|
||
('MTH-LA-MID','Linear Algebra Midterm','MTH-MID-F26','MTH-102',datetime(2026,10,21,9,0),datetime(2026,10,21,11,0),100,50),
|
||
('IT-P-FIN','Programming Final','IT-FIN-F26','IT-101',datetime(2026,12,10,9,0),datetime(2026,12,10,12,0),150,60),
|
||
('IT-DB-FIN','Database Final','IT-FIN-F26','IT-102',datetime(2026,12,12,9,0),datetime(2026,12,12,12,0),150,60),
|
||
]
|
||
op_exam_ids = {}
|
||
for ecode, ename, sess_code, subj_code, st, et, tm, mm in op_exam_data:
|
||
op_exam_ids[ecode] = find_or_create(cur, 'op_exam', 'exam_code', {
|
||
'exam_code': ecode, 'name': ename,
|
||
'session_id': op_exam_sess[sess_code],
|
||
'subject_id': subj_ids[subj_code],
|
||
'start_time': st, 'end_time': et,
|
||
'total_marks': tm, 'min_marks': mm,
|
||
'state': 'schedule', 'active': True, 'company_id': 1, **AU,
|
||
})
|
||
att_c = 0
|
||
for ecode, eid in op_exam_ids.items():
|
||
for sid in list(student_ids.values())[:10]:
|
||
cur.execute("INSERT INTO op_exam_attendees (student_id,exam_id,status,marks,company_id,create_uid,write_uid) VALUES(%s,%s,%s,%s,1,2,2) ON CONFLICT DO NOTHING",
|
||
(sid, eid, random.choice(['present','present','present','absent']), round(random.uniform(30, 95), 1)))
|
||
att_c += 1
|
||
print(f" {len(op_exam_sess)} sessions, {len(op_exam_ids)} exams, {att_c} attendees")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION F – ASSIGNMENTS
|
||
# ══════════════════════════════════════════
|
||
|
||
# 19. Grading types + OP assignments + EnCoach assignments
|
||
log("Assignments (OP + EnCoach)...")
|
||
ga_types = {}
|
||
for code, name in [('HW','Homework'),('PRJ','Project'),('LAB','Lab Work'),('ESS','Essay')]:
|
||
ga_types[code] = find_or_create(cur, 'grading_assignment_type', 'code', {
|
||
'code': code, 'name': name, 'assign_type': 'sub', 'company_id': 1, **AU,
|
||
})
|
||
|
||
op_assign_data = [
|
||
('ENG Grammar Essay','ENG-IELTS','ENG-101','ENG-F26A','sarah.mitchell@encoach.com','ESS','Write a 500-word essay on conditional sentences.',date(2026,9,20),50),
|
||
('Math Problem Set 1','MATH-FND','MTH-101','MTH-F26A','elena.popov@encoach.com','HW','Solve 20 calculus problems on limits.',date(2026,9,25),100),
|
||
('IT Lab: Build a Database','IT-CORE','IT-102','IT-F26A','david.kim@encoach.com','LAB','Design and implement a normalized database schema.',date(2026,10,5),100),
|
||
('IELTS Writing Task 2 Practice','ENG-IELTS','ENG-102','ENG-F26B','james.carter@encoach.com','ESS','Write an opinion essay on education funding.',date(2026,10,10),50),
|
||
('Statistics Lab Report','MATH-FND','MTH-103','MTH-F26B','ahmed.hassan@encoach.com','LAB','Analyze dataset using hypothesis testing.',date(2026,10,15),100),
|
||
('Web App Project','IT-CORE','IT-103','IT-F26B','lisa.wang@encoach.com','PRJ','Build a full-stack web application with React + API.',date(2026,11,1),200),
|
||
]
|
||
op_a_c = 0
|
||
for aname, crs, subj, batch, teacher, atype, desc, sub_date, marks in op_assign_data:
|
||
ga_id = insert(cur, 'grading_assignment', {
|
||
'name': aname, 'course_id': course_ids[crs], 'subject_id': subj_ids[subj],
|
||
'assignment_type': ga_types[atype], 'faculty_id': faculty_ids[teacher],
|
||
'issued_date': sub_date - timedelta(days=14), 'point': marks,
|
||
'user_id': 2, 'sequence': op_a_c + 1, 'state': 'publish',
|
||
'active': True, 'company_id': 1, **AU,
|
||
})
|
||
oa_id = insert(cur, 'op_assignment', {
|
||
'grading_assignment_id': ga_id, 'batch_id': batch_ids[batch],
|
||
'description': desc, 'state': 'publish',
|
||
'submission_date': sub_date, 'marks': marks,
|
||
'active': True, 'attempt_type': 'once', 'evaluation_type': 'normal',
|
||
'company_id': 1, **AU,
|
||
})
|
||
for sid in list(student_ids.values())[:8]:
|
||
m2m(cur, 'op_assignment_op_student_rel', 'op_assignment_id', 'op_student_id', oa_id, sid)
|
||
op_a_c += 1
|
||
|
||
en_assign_data = [
|
||
('IELTS Mock Writing Assignment','reading','english',date(2026,9,15),date(2026,10,1),date(2026,10,3)),
|
||
('Math Weekly Problem Set','math','math',date(2026,9,20),date(2026,9,27),date(2026,9,30)),
|
||
('Programming Challenge: Algorithms','it','it',date(2026,10,1),date(2026,10,15),date(2026,10,18)),
|
||
('Database Design Project','it','it',date(2026,10,10),date(2026,11,10),date(2026,11,15)),
|
||
('Listening Comprehension Task','listening','english',date(2026,10,5),date(2026,10,19),date(2026,10,22)),
|
||
('Statistics Research Paper','math','math',date(2026,10,15),date(2026,11,15),date(2026,11,20)),
|
||
]
|
||
en_assign_ids = []
|
||
for aname, mod, subj, start, deadline, late in en_assign_data:
|
||
aid = insert(cur, 'encoach_assignment', {
|
||
'name': aname, 'entity_id': main_ent, 'creator_id': 2,
|
||
'status': 'active', 'start_date': start, 'deadline': deadline,
|
||
'late_deadline': late, 'penalty_type': 'percentage', 'penalty_value': 10.0,
|
||
'max_submissions': 3, 'allow_extensions': True,
|
||
'plagiarism_enabled': True, 'plagiarism_max_checks': 2,
|
||
'reminder_count': 2, 'reminder_frequency': 'daily',
|
||
'description': f'Complete the {aname} within the deadline.',
|
||
**AU,
|
||
})
|
||
en_assign_ids.append(aid)
|
||
for uid in student_list[:10]:
|
||
m2m(cur, 'encoach_assignment_user_rel', 'assignment_id', 'user_id', aid, uid)
|
||
|
||
ext_reqs = [
|
||
(student_list[0], en_assign_ids[0], 'Medical emergency', 'approved', 2, 'Approved with documentation'),
|
||
(student_list[1], en_assign_ids[2], 'Family event', 'pending', None, None),
|
||
(student_list[3], en_assign_ids[3], 'Technical difficulties with lab', 'approved', 2, '3-day extension granted'),
|
||
(student_list[5], en_assign_ids[1], 'Competing exam schedule', 'rejected', 2, 'Deadline already extended once'),
|
||
]
|
||
for sid, aid, reason, status, approver, note in ext_reqs:
|
||
insert(cur, 'encoach_extension_request', {
|
||
'student_id': sid, 'assignment_id': aid, 'reason': reason,
|
||
'status': status, 'approved_by': approver, 'response_note': note,
|
||
'requested_date': datetime.now() - timedelta(days=random.randint(1, 15)), **AU,
|
||
})
|
||
print(f" {op_a_c} OP assignments, {len(en_assign_ids)} EnCoach assignments, {len(ext_reqs)} extension requests")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION G – ADAPTIVE LEARNING
|
||
# ══════════════════════════════════════════
|
||
|
||
# 20. Diagnostics, proficiency, learning plans
|
||
log("Adaptive learning (diagnostics, proficiency, plans)...")
|
||
diag_c, prof_c, plan_c, item_c = 0, 0, 0, 0
|
||
for subj_code, topics in topic_by_subj.items():
|
||
sid = ec_subj[subj_code]
|
||
for uid in student_list[:6]:
|
||
ds_id = insert(cur, 'encoach_diagnostic_session', {
|
||
'student_id': uid, 'subject_id': sid,
|
||
'status': 'completed', 'questions_answered': random.randint(15, 30),
|
||
'result_data': json.dumps({'level': random.choice(['beginner','intermediate','advanced']), 'strengths': ['reading'], 'weaknesses': ['writing']}),
|
||
'started_at': datetime.now() - timedelta(days=45),
|
||
'completed_at': datetime.now() - timedelta(days=45) + timedelta(minutes=30),
|
||
**AU,
|
||
})
|
||
diag_c += 1
|
||
for tid in topics:
|
||
mscore = round(random.uniform(0.2, 0.95), 2)
|
||
insert(cur, 'encoach_proficiency', {
|
||
'student_id': uid, 'subject_id': sid, 'topic_id': tid,
|
||
'mastery_score': mscore, 'confidence': round(random.uniform(0.3, 0.9), 2),
|
||
'assessment_count': random.randint(1, 10),
|
||
'last_assessed': datetime.now() - timedelta(days=random.randint(1, 30)), **AU,
|
||
})
|
||
prof_c += 1
|
||
lp_id = insert(cur, 'encoach_learning_plan', {
|
||
'student_id': uid, 'subject_id': sid,
|
||
'status': 'active', 'generated_by': 'ai',
|
||
'created_at': datetime.now() - timedelta(days=40),
|
||
'progress': round(random.uniform(0.1, 0.8), 2), **AU,
|
||
})
|
||
plan_c += 1
|
||
plan_statuses = ['completed','completed','in_progress','available','locked']
|
||
for seq, tid in enumerate(topics[:5], 1):
|
||
st = plan_statuses[min(seq-1, len(plan_statuses)-1)]
|
||
insert(cur, 'encoach_learning_plan_item', {
|
||
'plan_id': lp_id, 'topic_id': tid, 'sequence': seq * 10,
|
||
'status': st, 'estimated_hours': round(random.uniform(1, 5), 1),
|
||
'mastery_threshold': 0.7,
|
||
'completed_at': datetime.now() - timedelta(days=30-seq*5) if st == 'completed' else None,
|
||
**AU,
|
||
})
|
||
item_c += 1
|
||
print(f" {diag_c} diagnostics, {prof_c} proficiencies, {plan_c} plans, {item_c} items")
|
||
|
||
# 21. Content cache
|
||
log("Content cache...")
|
||
cc_c = 0
|
||
for tid in topic_ids_list[:20]:
|
||
for ctype in ['explanation', 'practice', 'quiz']:
|
||
insert(cur, 'encoach_content_cache', {
|
||
'topic_id': tid, 'content_type': ctype,
|
||
'content': json.dumps({'text': f'Cached {ctype} content for topic', 'version': 1}),
|
||
'generated_at': datetime.now() - timedelta(hours=random.randint(1, 200)),
|
||
'ttl_hours': 168, **AU,
|
||
})
|
||
cc_c += 1
|
||
print(f" {cc_c} content cache entries")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION H – COMMUNICATION
|
||
# ══════════════════════════════════════════
|
||
|
||
# 22. Announcements
|
||
log("Announcements...")
|
||
announcements = [
|
||
('Welcome to Fall 2026 Semester!', 'all', 'high', 'Welcome all students and faculty to the new semester. Please check your schedules.'),
|
||
('IELTS Mock Test Schedule Released', 'course', 'normal', 'Mock test dates for IELTS preparation are now available. Check the exam calendar.'),
|
||
('System Maintenance Notice', 'all', 'urgent', 'The platform will undergo maintenance on Oct 5th, 10 PM - 2 AM. Please save your work.'),
|
||
('New Math Resources Available', 'course', 'normal', 'Additional calculus practice sets and video tutorials have been uploaded.'),
|
||
('IT Lab Hours Extended', 'entity', 'normal', 'Computer labs will remain open until 9 PM during exam preparation weeks.'),
|
||
('Scholarship Applications Open', 'all', 'high', 'Applications for the 2027 academic excellence scholarship are now open.'),
|
||
]
|
||
for title, target, priority, content in announcements:
|
||
insert(cur, 'encoach_announcement', {
|
||
'author_id': 2, 'title': title, 'content': content,
|
||
'target_type': target, 'priority': priority,
|
||
'entity_id': main_ent, 'active': True,
|
||
'published_at': datetime.now() - timedelta(days=random.randint(1, 30)),
|
||
**AU,
|
||
})
|
||
print(f" {len(announcements)} announcements")
|
||
|
||
# 23. Discussion boards & posts
|
||
log("Discussion boards & posts...")
|
||
boards = [
|
||
('ENG-IELTS', 'IELTS Preparation Discussion', 'Discuss strategies, share tips, and ask questions about IELTS prep.'),
|
||
('MATH-FND', 'Math Study Group', 'Collaborate on problem sets and discuss mathematical concepts.'),
|
||
('IT-CORE', 'IT & Programming Forum', 'Share code, debug together, discuss tech trends.'),
|
||
]
|
||
board_ids = {}
|
||
for crs, bname, bdesc in boards:
|
||
bid = insert(cur, 'encoach_discussion_board', {
|
||
'course_id': course_ids[crs], 'entity_id': main_ent,
|
||
'name': bname, 'description': bdesc, 'active': True, **AU,
|
||
})
|
||
board_ids[crs] = bid
|
||
|
||
posts = [
|
||
('ENG-IELTS', 'Tips for Writing Task 2', 'I found that structuring paragraphs with topic sentences really helps. Anyone else have strategies?', student_list[0]),
|
||
('ENG-IELTS', None, 'Absolutely! I also recommend using linking words like "Furthermore" and "However" for coherence.', student_list[2]),
|
||
('ENG-IELTS', 'Best resources for Listening practice?', 'Looking for good podcast recommendations for IELTS listening prep.', student_list[1]),
|
||
('MATH-FND', 'Integration by parts confusion', 'Can someone explain when to use integration by parts vs. substitution?', student_list[4]),
|
||
('MATH-FND', None, 'Use by-parts when you have a product of two different types of functions (e.g., x*sin(x)).', teachers_list[2]),
|
||
('MATH-FND', 'Study group for Stats final?', 'Anyone want to form a study group for the statistics final?', student_list[6]),
|
||
('IT-CORE', 'React vs Vue for our project?', 'Our team is debating React vs Vue. What does everyone recommend?', student_list[8]),
|
||
('IT-CORE', None, 'React has a larger ecosystem. For a capstone project, I would recommend React.', teachers_list[4]),
|
||
('IT-CORE', 'SQL optimization tips', 'My queries are running slow. Any tips for optimizing SQL joins?', student_list[10]),
|
||
]
|
||
post_c = 0
|
||
last_post_id = {}
|
||
for crs, title, content, author in posts:
|
||
pid_val = last_post_id.get(crs) if title is None else None
|
||
p_id = insert(cur, 'encoach_discussion_post', {
|
||
'board_id': board_ids[crs], 'author_id': author,
|
||
'title': title or '', 'content': content,
|
||
'parent_id': pid_val, 'is_pinned': False,
|
||
'created_at': datetime.now() - timedelta(days=random.randint(1, 20)), **AU,
|
||
})
|
||
if title:
|
||
last_post_id[crs] = p_id
|
||
post_c += 1
|
||
print(f" {len(board_ids)} boards, {post_c} posts")
|
||
|
||
# 24. Direct messages
|
||
log("Direct messages...")
|
||
dm_data = [
|
||
(student_list[0], teachers_list[0], 'Hi Dr. Mitchell, could you review my Writing Task 2 draft?'),
|
||
(teachers_list[0], student_list[0], 'Of course! Please submit it through the assignment portal and I will review it by Friday.'),
|
||
(student_list[4], teachers_list[2], 'Prof. Popov, I am struggling with the chain rule. Can we schedule office hours?'),
|
||
(teachers_list[2], student_list[4], 'Sure! I have office hours Tuesday 3-5 PM. You can also check the video in Chapter 2.'),
|
||
(student_list[8], teachers_list[4], 'Dr. Wang, is it okay to use TypeScript instead of JavaScript for the project?'),
|
||
(teachers_list[4], student_list[8], 'Yes, TypeScript is perfectly fine and actually encouraged for larger projects.'),
|
||
(student_list[1], student_list[3], 'Hey Noah, want to study for the IELTS listening test together?'),
|
||
(student_list[3], student_list[1], 'Sure! Let us meet at the library tomorrow at 2 PM.'),
|
||
]
|
||
for sender, recip, content in dm_data:
|
||
insert(cur, 'encoach_direct_message', {
|
||
'sender_id': sender, 'recipient_id': recip, 'content': content,
|
||
'is_read': random.choice([True, False]),
|
||
'sent_at': datetime.now() - timedelta(hours=random.randint(1, 200)), **AU,
|
||
})
|
||
print(f" {len(dm_data)} direct messages")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION I – APPROVALS
|
||
# ══════════════════════════════════════════
|
||
|
||
# 25. Approval workflows, stages, requests
|
||
log("Approval workflows & requests...")
|
||
wf_data = [
|
||
('Exam Publication Approval', 'exam_publish', [
|
||
('Department Review', teachers_list[0]), ('Admin Final Approval', 2),
|
||
]),
|
||
('Assignment Publication', 'assignment_publish', [
|
||
('Faculty Review', teachers_list[2]), ('Admin Approval', 2),
|
||
]),
|
||
('Content Publication', 'content_publish', [
|
||
('Content Review', teachers_list[4]), ('Quality Check', 2),
|
||
]),
|
||
]
|
||
wf_ids = {}
|
||
for wname, wtype, stages in wf_data:
|
||
wid = insert(cur, 'encoach_approval_workflow', {
|
||
'name': wname, 'type': wtype, 'entity_id': main_ent,
|
||
'allow_bypass': False, 'active': True, **AU,
|
||
})
|
||
wf_ids[wname] = wid
|
||
for seq, (sname, approver) in enumerate(stages, 1):
|
||
insert(cur, 'encoach_approval_stage', {
|
||
'workflow_id': wid, 'sequence': seq * 10,
|
||
'approver_id': approver, 'max_days': 3,
|
||
'auto_escalate': True, 'status': 'pending', **AU,
|
||
})
|
||
|
||
req_data = [
|
||
('Exam Publication Approval', 'encoach.exam', list(exam_ids_map.values())[0], teachers_list[0], 'approved'),
|
||
('Exam Publication Approval', 'encoach.exam', list(exam_ids_map.values())[3], teachers_list[1], 'in_progress'),
|
||
('Assignment Publication', 'encoach.assignment', en_assign_ids[0], teachers_list[2], 'approved'),
|
||
('Content Publication', 'encoach.exam', list(exam_ids_map.values())[5], teachers_list[4], 'in_progress'),
|
||
]
|
||
for wname, res_model, res_id, requester, state in req_data:
|
||
insert(cur, 'encoach_approval_request', {
|
||
'workflow_id': wf_ids[wname], 'res_model': res_model, 'res_id': res_id,
|
||
'requester_id': requester, 'state': state,
|
||
'created_at': datetime.now() - timedelta(days=random.randint(1, 15)), **AU,
|
||
})
|
||
print(f" {len(wf_data)} workflows, {len(req_data)} requests")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION J – NOTIFICATIONS
|
||
# ══════════════════════════════════════════
|
||
|
||
# 26. Notification rules, preferences, notifications
|
||
log("Notifications (rules, preferences, alerts)...")
|
||
rules = [
|
||
('Exam Published Alert', 'exam_published', 'both'),
|
||
('Assignment Due Reminder', 'assignment_due', 'email'),
|
||
('Grade Posted', 'grade_posted', 'in_app'),
|
||
('New Announcement', 'announcement_created', 'both'),
|
||
('Extension Request Status', 'extension_status', 'email'),
|
||
]
|
||
for rname, event, channel in rules:
|
||
insert(cur, 'encoach_notification_rule', {
|
||
'name': rname, 'event': event, 'channel': channel,
|
||
'entity_id': main_ent, 'active': True, 'template': f'{event} notification template', **AU,
|
||
})
|
||
for uid in student_list[:10]:
|
||
for cat in ['exam','assignment','announcement','grade']:
|
||
cur.execute("INSERT INTO encoach_notification_preference (user_id,category,email_enabled,in_app_enabled,create_uid,write_uid) VALUES(%s,%s,%s,%s,2,2) ON CONFLICT DO NOTHING",
|
||
(uid, cat, True, True))
|
||
|
||
notif_data = [
|
||
('Exam Published: IELTS Reading Mock Test 1', 'success', 'exam', '/exams'),
|
||
('Assignment Due Tomorrow: IELTS Mock Writing', 'warning', 'assignment', '/assignments'),
|
||
('Your Math Midterm grade is available', 'info', 'grade', '/grades'),
|
||
('New Announcement: System Maintenance', 'warning', 'announcement', '/announcements'),
|
||
('Extension Request Approved', 'success', 'extension', '/assignments'),
|
||
('New resource uploaded: Calculus Video', 'info', 'resource', '/resources'),
|
||
('Diagnostic session completed', 'success', 'adaptive', '/learning-plan'),
|
||
('Reminder: Statistics Lab Report due in 3 days', 'warning', 'assignment', '/assignments'),
|
||
]
|
||
notif_c = 0
|
||
for title, ntype, cat, link in notif_data:
|
||
for uid in student_list[:5]:
|
||
insert(cur, 'encoach_notification', {
|
||
'user_id': uid, 'title': title, 'type': ntype,
|
||
'category': cat, 'link': link,
|
||
'message': f'Details: {title}', 'is_read': random.choice([True, False]),
|
||
'created_at': datetime.now() - timedelta(hours=random.randint(1, 200)), **AU,
|
||
})
|
||
notif_c += 1
|
||
print(f" {len(rules)} rules, {notif_c} notifications")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION K – RESOURCES
|
||
# ══════════════════════════════════════════
|
||
|
||
# 27. Learning resources
|
||
log("Learning resources...")
|
||
res_data = [
|
||
('IELTS Band 7+ Reading Strategies','english','video','https://example.com/ielts-reading.mp4','intermediate',30),
|
||
('Academic Writing Guide','english','pdf','https://example.com/writing-guide.pdf','beginner',None),
|
||
('Calculus Visual Explanations','math','video','https://example.com/calc-visual.mp4','intermediate',45),
|
||
('Linear Algebra Interactive Tool','math','interactive','https://example.com/la-tool','advanced',None),
|
||
('Python for Beginners Tutorial','it','video','https://example.com/python-tut.mp4','beginner',60),
|
||
('SQL Query Optimizer Guide','it','pdf','https://example.com/sql-opt.pdf','advanced',None),
|
||
('Statistics with Real Data','math','document','https://example.com/stats-data.docx','intermediate',None),
|
||
('Web Dev Best Practices 2026','it','link','https://example.com/webdev-2026','intermediate',None),
|
||
('Pronunciation Practice Audio','english','video','https://example.com/pronunciation.mp4','beginner',20),
|
||
('Cybersecurity Case Studies','it','pdf','https://example.com/cyber-cases.pdf','advanced',None),
|
||
]
|
||
cur.execute("DELETE FROM encoach_resource_encoach_topic_rel")
|
||
cur.execute("DELETE FROM encoach_resource")
|
||
for rname, subj, rtype, url, diff, dur in res_data:
|
||
rid = insert(cur, 'encoach_resource', {
|
||
'name': rname, 'subject_id': ec_subj[subj], 'type': rtype,
|
||
'url': url, 'difficulty': diff, 'duration_minutes': dur,
|
||
'creator_id': 2, 'active': True, 'review_status': 'approved', **AU,
|
||
})
|
||
topics = topic_by_subj.get(subj, [])
|
||
for tid in random.sample(topics, min(3, len(topics))):
|
||
m2m(cur, 'encoach_resource_encoach_topic_rel', 'encoach_resource_id', 'encoach_topic_id', rid, tid)
|
||
print(f" {len(res_data)} resources")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION L – SUBSCRIPTION & PAYMENTS
|
||
# ══════════════════════════════════════════
|
||
|
||
# 28. Packages, discounts, payments
|
||
log("Packages, discounts & payments...")
|
||
pkgs = [
|
||
('Basic Plan', 29.99, 30, 'Access to one subject with basic resources', '["1 Subject","Basic Resources","Email Support"]'),
|
||
('Standard Plan', 79.99, 90, 'Access to two subjects with full resources', '["2 Subjects","Full Resources","Priority Support","Mock Exams"]'),
|
||
('Premium Plan', 149.99, 180, 'Full access to all subjects and features', '["All Subjects","Full Resources","1-on-1 Tutoring","Unlimited Mock Exams","Certificate"]'),
|
||
('Corporate Bundle', 499.99, 365, 'Team plan for corporate training programs', '["Unlimited Users","All Subjects","Admin Dashboard","Custom Branding","API Access"]'),
|
||
]
|
||
pkg_ids = {}
|
||
for pname, price, dur, desc, feats in pkgs:
|
||
pid = insert(cur, 'encoach_package', {
|
||
'name': pname, 'price': price, 'duration_days': dur,
|
||
'currency': 'USD', 'description': desc, 'features': feats,
|
||
'active': True, **AU,
|
||
})
|
||
pkg_ids[pname] = pid
|
||
for sid in ec_subj.values():
|
||
m2m(cur, 'encoach_package_encoach_subject_rel', 'encoach_package_id', 'encoach_subject_id', pid, sid)
|
||
|
||
discounts = [
|
||
('EARLY2026', 15.0, date(2026,6,1), date(2026,9,1), 200),
|
||
('STUDENT10', 10.0, date(2026,1,1), date(2026,12,31), 500),
|
||
('CORPVIP', 25.0, date(2026,1,1), date(2027,6,30), 50),
|
||
]
|
||
for code, pct, vf, vt, mx in discounts:
|
||
find_or_create(cur, 'encoach_discount', 'code', {
|
||
'code': code, 'percentage': pct, 'valid_from': vf, 'valid_to': vt,
|
||
'max_uses': mx, 'current_uses': random.randint(0, 20), **AU,
|
||
})
|
||
|
||
pay_c = 0
|
||
providers = ['stripe', 'paypal', 'paymob']
|
||
for uid in student_list[:12]:
|
||
pkg = random.choice(list(pkg_ids.items()))
|
||
insert(cur, 'encoach_payment', {
|
||
'user_id': uid, 'entity_id': main_ent,
|
||
'package_id': pkg[1], 'amount': pkg[0].split()[0] == 'Basic' and 29.99 or 79.99,
|
||
'currency': 'USD', 'provider': random.choice(providers),
|
||
'status': random.choice(['completed','completed','completed','pending']),
|
||
'provider_ref': f'PAY-{random.randint(100000,999999)}',
|
||
'created_at': datetime.now() - timedelta(days=random.randint(1, 60)), **AU,
|
||
})
|
||
pay_c += 1
|
||
print(f" {len(pkgs)} packages, {len(discounts)} discounts, {pay_c} payments")
|
||
|
||
# ══════════════════════════════════════════
|
||
# SECTION M – CLASSROOM GROUPS
|
||
# ══════════════════════════════════════════
|
||
|
||
# 29. Classroom groups
|
||
log("Classroom groups, registration, training, support...")
|
||
groups = [
|
||
('IELTS Morning Study Group', teachers_list[0]),
|
||
('Math Olympiad Prep', teachers_list[2]),
|
||
('IT Hackathon Team', teachers_list[4]),
|
||
('English Book Club', teachers_list[1]),
|
||
]
|
||
for gname, admin_uid in groups:
|
||
gid = insert(cur, 'encoach_classroom_group', {
|
||
'name': gname, 'entity_id': main_ent, 'admin_id': admin_uid,
|
||
'description': f'{gname} - collaborative learning group',
|
||
'active': True, **AU,
|
||
})
|
||
for uid in random.sample(student_list, min(8, len(student_list))):
|
||
m2m(cur, 'encoach_group_member_rel', 'group_id', 'user_id', gid, uid)
|
||
|
||
# Registration batches
|
||
reg_batches = [
|
||
('Fall 2026 Batch Import', 'completed', 50, 50),
|
||
('Corporate Training Import', 'completed', 25, 25),
|
||
('Spring 2027 Pre-Registration', 'processing', 100, 45),
|
||
]
|
||
for rname, status, total, processed in reg_batches:
|
||
insert(cur, 'encoach_registration_batch', {
|
||
'name': rname, 'entity_id': main_ent, 'created_by': 2,
|
||
'status': status, 'total_rows': total, 'processed_rows': processed,
|
||
'error_log': None if status == 'completed' else json.dumps([{'row': 46, 'error': 'Duplicate email'}]),
|
||
'created_at': datetime.now() - timedelta(days=random.randint(5, 60)), **AU,
|
||
})
|
||
|
||
# Training tips & walkthroughs
|
||
tips = [
|
||
('Quick IELTS Reading Tip', 'english', 'reading', 'Always read the questions before the passage to know what to look for.'),
|
||
('Math Problem Solving', 'math', 'calculus', 'Draw diagrams for word problems - visualization makes integration easier.'),
|
||
('Debug Like a Pro', 'it', 'programming', 'Use console.log strategically and check the browser DevTools Network tab.'),
|
||
('Writing Coherence', 'english', 'writing', 'Use transition words between paragraphs to maintain flow.'),
|
||
('SQL Performance', 'it', 'databases', 'Always use EXPLAIN ANALYZE to understand your query execution plan.'),
|
||
]
|
||
for title, subj, module, content in tips:
|
||
insert(cur, 'encoach_training_tip', {
|
||
'title': title, 'subject_id': ec_subj.get(subj), 'module': module,
|
||
'content': content, **AU,
|
||
})
|
||
|
||
walkthroughs = [
|
||
('Getting Started with EnCoach', 'onboarding', [
|
||
{'step': 1, 'title': 'Create Account', 'description': 'Sign up with your email or invite code'},
|
||
{'step': 2, 'title': 'Choose Your Subject', 'description': 'Select English, Math, or IT'},
|
||
{'step': 3, 'title': 'Take Diagnostic Test', 'description': 'Complete the placement assessment'},
|
||
{'step': 4, 'title': 'Start Learning', 'description': 'Follow your personalized learning plan'},
|
||
]),
|
||
('How to Take an Exam', 'exams', [
|
||
{'step': 1, 'title': 'Navigate to Exams', 'description': 'Click the Exams tab in the sidebar'},
|
||
{'step': 2, 'title': 'Select Your Exam', 'description': 'Choose from available exams'},
|
||
{'step': 3, 'title': 'Review Instructions', 'description': 'Read time limits and rules carefully'},
|
||
{'step': 4, 'title': 'Submit & Review', 'description': 'Submit answers and check feedback'},
|
||
]),
|
||
('Using the Learning Plan', 'adaptive', [
|
||
{'step': 1, 'title': 'View Your Plan', 'description': 'Check your AI-generated learning plan'},
|
||
{'step': 2, 'title': 'Complete Topics', 'description': 'Work through topics in sequence'},
|
||
{'step': 3, 'title': 'Track Progress', 'description': 'Monitor mastery scores per topic'},
|
||
]),
|
||
]
|
||
for title, module, steps in walkthroughs:
|
||
insert(cur, 'encoach_training_walkthrough', {
|
||
'title': title, 'module': module, 'steps': json.dumps(steps), **AU,
|
||
})
|
||
|
||
print(f" {len(groups)} groups, {len(reg_batches)} registrations, {len(tips)} tips, {len(walkthroughs)} walkthroughs")
|
||
|
||
# 30. Tickets & FAQ
|
||
log("Support tickets & FAQ...")
|
||
for t_subj, t_type, t_status, t_desc in TICKETS:
|
||
insert(cur, 'encoach_ticket', {
|
||
'subject': t_subj, 'description': t_desc, 'type': t_type,
|
||
'status': t_status, 'reporter_id': 2, 'source': 'web',
|
||
'created_at': datetime.now() - timedelta(days=random.randint(1, 30)), **AU,
|
||
})
|
||
for cat_name, items in FAQ_DATA:
|
||
cat_id = insert(cur, 'encoach_faq_category', {
|
||
'name': cat_name, 'sequence': 1, 'active': True, **AU,
|
||
})
|
||
for q, a in items:
|
||
insert(cur, 'encoach_faq_item', {
|
||
'category_id': cat_id, 'question': q, 'answer': a, 'active': True, **AU,
|
||
})
|
||
print(f" {len(TICKETS)} tickets, {len(FAQ_DATA)} FAQ categories")
|
||
|
||
# ═══════════════════════════════════════════
|
||
# COMMIT
|
||
# ═══════════════════════════════════════════
|
||
conn.commit()
|
||
|
||
# Final counts
|
||
cur2 = conn.cursor()
|
||
summary_tables = [
|
||
('op_department','Departments'),('op_academic_year','Academic Years'),('op_academic_term','Academic Terms'),
|
||
('op_subject','Op Subjects'),('op_course','Courses'),('op_batch','Batches'),
|
||
('op_faculty','Faculty'),('op_student','Students'),('op_student_course','Enrollments'),
|
||
('op_classroom','Classrooms'),('op_timing','Timings'),('op_session','Timetable Sessions'),
|
||
('encoach_subject','Taxonomy Subjects'),('encoach_domain','Taxonomy Domains'),
|
||
('encoach_topic','Taxonomy Topics'),('encoach_learning_objective','Learning Objectives'),
|
||
('encoach_entity','Entities'),('encoach_branding','Brandings'),
|
||
('encoach_invite_code','Invite Codes'),('encoach_role','Roles'),('encoach_permission','Permissions'),
|
||
('encoach_course_chapter','Course Chapters'),('encoach_chapter_material','Chapter Materials'),
|
||
('encoach_chapter_progress','Chapter Progress'),
|
||
('encoach_exam_structure','Exam Structures'),('encoach_rubric_group','Rubric Groups'),
|
||
('encoach_exam_rubric','Exam Rubrics'),('encoach_exam','EnCoach Exams'),
|
||
('encoach_exam_session','Exam Sessions'),('encoach_evaluation_job','Evaluation Jobs'),
|
||
('encoach_exam_session_stat','Exam Stats'),
|
||
('op_exam_type','OP Exam Types'),('op_exam_session','OP Exam Sessions'),
|
||
('op_exam','OP Exams'),('op_exam_attendees','OP Exam Attendees'),
|
||
('grading_assignment_type','Grading Types'),('grading_assignment','Grading Assignments'),
|
||
('op_assignment','OP Assignments'),
|
||
('encoach_assignment','EnCoach Assignments'),('encoach_extension_request','Extension Requests'),
|
||
('encoach_diagnostic_session','Diagnostic Sessions'),('encoach_proficiency','Proficiencies'),
|
||
('encoach_learning_plan','Learning Plans'),('encoach_learning_plan_item','Learning Plan Items'),
|
||
('encoach_content_cache','Content Cache'),
|
||
('encoach_announcement','Announcements'),('encoach_discussion_board','Discussion Boards'),
|
||
('encoach_discussion_post','Discussion Posts'),('encoach_direct_message','Direct Messages'),
|
||
('encoach_approval_workflow','Approval Workflows'),('encoach_approval_request','Approval Requests'),
|
||
('encoach_notification_rule','Notification Rules'),('encoach_notification','Notifications'),
|
||
('encoach_resource','Resources'),
|
||
('encoach_package','Packages'),('encoach_discount','Discounts'),('encoach_payment','Payments'),
|
||
('encoach_classroom_group','Classroom Groups'),('encoach_registration_batch','Reg Batches'),
|
||
('encoach_training_tip','Training Tips'),('encoach_training_walkthrough','Walkthroughs'),
|
||
('encoach_ticket','Tickets'),('encoach_faq_category','FAQ Categories'),('encoach_faq_item','FAQ Items'),
|
||
('res_users','Users'),
|
||
]
|
||
print("\n" + "=" * 65)
|
||
print(" SEED COMPLETE – Full demo data created!")
|
||
print("=" * 65)
|
||
print(f"\n {'Table':<30} {'Count':>6}")
|
||
print(" " + "-" * 38)
|
||
total = 0
|
||
for tbl, label in summary_tables:
|
||
cur2.execute(f"SELECT COUNT(*) FROM {tbl}")
|
||
c = cur2.fetchone()[0]
|
||
total += c
|
||
print(f" {label:<30} {c:>6}")
|
||
print(" " + "-" * 38)
|
||
print(f" {'TOTAL RECORDS':<30} {total:>6}")
|
||
print()
|
||
cur2.close()
|
||
|
||
except Exception as e:
|
||
conn.rollback()
|
||
print(f"\nERROR: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
raise
|
||
finally:
|
||
cur.close()
|
||
conn.close()
|
||
|
||
|
||
if __name__ == '__main__':
|
||
seed()
|