# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_course_teaching():
    """Optimize the assignment of teachers to courses to maximize teaching quality."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="course_teaching_optimization")
    
    # Example data (replace with actual data from database)
    teachers = ['T1', 'T2', 'T3']
    courses = ['C1', 'C2', 'C3', 'C4', 'C5']
    
    # Teaching quality grades (g_ij)
    grades = {
        ('T1', 'C1'): 4.5,
        ('T1', 'C2'): 3.7,
        ('T1', 'C3'): 4.0,
        ('T1', 'C4'): 3.5,
        ('T1', 'C5'): 4.2,
        ('T2', 'C1'): 3.8,
        ('T2', 'C2'): 4.1,
        ('T2', 'C3'): 3.9,
        ('T2', 'C4'): 4.3,
        ('T2', 'C5'): 3.6,
        ('T3', 'C1'): 4.0,
        ('T3', 'C2'): 3.5,
        ('T3', 'C3'): 4.2,
        ('T3', 'C4'): 3.8,
        ('T3', 'C5'): 4.1,
    }
    
    # Maximum number of courses per teacher (c_i)
    max_courses = {
        'T1': 4,
        'T2': 3,
        'T3': 5,
    }
    
    # 2. VARIABLES
    # Binary decision variables x_ij
    x = {(i, j): mdl.binary_var(name=f"x_{i}_{j}") for i in teachers for j in courses}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total teaching quality
    objective = mdl.sum(grades[(i, j)] * x[(i, j)] for i in teachers for j in courses)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Course Assignment Constraint: Each course must be assigned exactly one teacher
    for j in courses:
        mdl.add_constraint(mdl.sum(x[(i, j)] for i in teachers) == 1, ctname=f"course_assignment_{j}")
    
    # Teacher Capacity Constraint: Each teacher can be assigned no more than their maximum allowable number of courses
    for i in teachers:
        mdl.add_constraint(mdl.sum(x[(i, j)] for j in courses) <= max_courses[i], ctname=f"teacher_capacity_{i}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in teachers:
            for j in courses:
                if solution.get_value(x[(i, j)]) > 0.5:
                    print(f"Teacher {i} is assigned to course {j}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
optimize_course_teaching()