2
from datetime import datetime
7
from tools.translate import _
9
class school_teacher(osv.osv):
10
_name = 'school.teacher'
11
_inherit = 'school.teacher'
13
def _compute_assigned_hours(self, cr, uid, ids, field_name, arg, context=None):
15
for teacher in self.browse(cr, uid, ids):
17
for teacher_data in teacher.teacher_data_ids:
18
ret[teacher.id] += teacher_data.iwl_id.duration
21
def _get_teachers_from_iwls(self, cr, uid, ids, context=None):
23
for iwl in self.browse(cr, uid, ids):
24
for teacher_data in iwl.teachers:
25
ret.add(teacher_data.teacher_id.id)
28
def _get_teachers_from_tds(self, cr, uid, ids, context=None):
30
for teacher_data in self.browse(cr, uid, ids):
31
ret.add(teacher_data.teacher_id.id)
35
'max_week_hours': fields.float('Max hours', help="Max. week hours to help assign "),
36
'min_week_hours': fields.float('Min hours', help="Min. week hours to help assign "),
37
'teacher_data_ids': fields.one2many('school.teacher_data', 'teacher_id', string='Teacher datas', ),
38
'assigned_hours': fields.function(_compute_assigned_hours, type='float', method=True, string='Assigned Hours',
39
store={'school.impartition_week_line': (_get_teachers_from_iwls, ['duration'], 10),
40
'school.teacher_data': (_get_teachers_from_tds, ['iwl_id', 'teacher_id'], 15)}),
44
('max_greater_or_equal_than_min', '(max_week_hours >= min_week_hours)', "Max hours should be greater or equal than min hours"),
45
('min_hours_not_negative', '(min_hours >= 0)', "Max hours should be not negative"),
50
class school_teacher_course_suitability(osv.osv):
51
_name = 'school.teacher_course_suitability'
53
def _compute_assigned_hours(self, cr, uid, ids, field_name, arg, context=None):
55
for teacher_cs in self.browse(cr, uid, ids):
56
ret[teacher_cs.id] = 0
57
teacher = teacher_cs.teacher_id
58
for teacher_data in teacher.teacher_data_ids:
59
iwl = teacher_data.iwl_id
60
if iwl.classe_id.course_id.id == teacher_cs.course_id.id:
61
ret[teacher_cs.id] += teacher_data.iwl_id.duration
64
def _get_teacher_cs_from_iwls(self, cr, uid, ids, context=None):
66
for iwl in self.browse(cr, uid, ids):
67
course_id = iwl.classe_id.course_id.id
68
for teacher_data in iwl.teachers:
69
ret.add((teacher_data.teacher_id.id, course_id))
71
for (teacher_id, course_id) in ret:
72
ret2 += self.pool.get('school.teacher_course_suitability').search(cr, uid, [('teacher_id', '=', teacher_id), ('course_id', '=', course_id)])
75
def _get_teacher_cs_from_tds(self, cr, uid, ids, context=None):
77
for teacher_data in self.browse(cr, uid, ids):
78
course_id = teacher_data.iwl_id.classe_id.course_id.id
79
teacher_id = teacher_data.teacher_id.id
80
ret.add((teacher_id, course_id))
82
for (teacher_id, course_id) in ret:
83
ret2 += self.pool.get('school.teacher_course_suitability').search(cr, uid, [('teacher_id', '=', teacher_id), ('course_id', '=', course_id)])
87
'teacher_id': fields.many2one('school.teacher', 'Teacher', ondelete='cascade', ),
88
'course_id': fields.many2one('school.course', 'Course', ondelete='cascade', ),
89
'percentage': fields.float('Percentage', help='Percentage suitability', ),
90
'max_week_hours': fields.float('Max hours', help="Max. week hours to help assign "),
91
'min_week_hours': fields.float('Min hours', help="Min. week hours to help assign "),
92
'assigned_hours': fields.function(_compute_assigned_hours, type='float', method=True, string='Assigned Hours',
93
store={'school.impartition_week_line': (_get_teacher_cs_from_iwls, ['duration'], 10),
94
'school.teacher_data': (_get_teacher_cs_from_tds, ['iwl_id', 'teacher_id'], 15)}),
95
'total_max_hours': fields.related('teacher_id', 'max_week_hours', type='float', string="Total max", ),
96
'total_min_hours': fields.related('teacher_id', 'min_week_hours', type='float', string="Total min", ),
97
'total_assigned_hours': fields.related('teacher_id', 'assigned_hours', type='float', string="Total assigned", ),
101
('teacher_course_unique', 'UNIQUE(teacher_id,course_id)', 'Only one register for the same teacher and course'),
102
('max_greater_or_equal_than_min', 'CHECK(max_week_hours >= min_week_hours)', "Max hours should be greater or equal than min hours"),
103
('min_hours_not_negative', 'CHECK(min_hours >= 0)', "Max hours should be not negative"),
104
('percentage_between_0_and_100', 'CHECK(percentage BETWEEN 0 AND 100)', "Percentage should be between 0 and 100"),
107
school_teacher_course_suitability()
109
class school_impartition_week_line(osv.osv):
110
_name = 'school.impartition_week_line'
111
_inherit = 'school.impartition_week_line'
113
def _compute_blocked(self, cr, uid, ids, field_name, arg, context=None):
115
for iwl in self.browse(cr, uid, ids):
117
ret[iwl.id] = {'needed_complete': (iwl.teachers_needed <= len(iwl.teachers)), }
118
for td in iwl.teachers:
119
blocked &= td.blocked
120
if not blocked: break
121
ret[iwl.id]['teachers_blocked'] = blocked
124
def _get_iwl_ids_from_td_ids(self, cr, uid, ids, context=None):
126
for td in self.browse(cr, uid, ids):
127
ret.add(td.iwl_id.id)
132
'teachers_blocked': fields.function(_compute_blocked, type='boolean', method=True, string='Blocked', multi='teachers', help='No teachers changes allowed',
133
store={'school.teacher_data': (_get_iwl_ids_from_td_ids, ['blocked'], 10)},
135
'iwl_group': fields.char('IWL group', size=30, help="Group name for IWL's with same teacher", ),
136
'teachers_needed': fields.integer('Teachers needed', ),
137
'teachers_recommended': fields.integer('Teachers recommended', ),
138
'needed_completed': fields.function(_compute_blocked, type='boolean', method=True, string='Teachers completed', multi='teachers',
139
store={'school.teacher_data': (_get_iwl_ids_from_td_ids, ['iwl_id', ], 10)},
144
'teachers_needed': lambda * a: 1,
145
'teachers_recommended': lambda * a: 1,
149
('needed_and_recommended_check', '(teachers_needed <= teachers_recommended)', "The number required should be smaller than the recommended"),
150
('needed_not_negative', '(teachers_needed >= 0)', "The number required should be not negative"),
153
school_impartition_week_line()
155
class school_teacher_data(osv.osv):
156
_name = 'school.teacher_data'
157
_inherit = 'school.teacher_data'
160
'blocked': fields.boolean('Blocked', help='No teachers changes allowed',),
163
school_teacher_data()
165
class school_teachers_solution(osv.osv):
166
_name = 'school.teachers_solution'
168
school_teachers_solution()
170
class school_teacher_iwl_solution(osv.osv):
171
_name = 'school.teacher_iwl_solution'
174
'solution_id': fields.many2one('school.teachers_solution', 'Solution', ondelete='cascade', required=True, ),
175
'iwl_id': fields.many2one('school.impartition_week_line', 'IWL', ondelete='cascade', required=True, ),
176
'teacher_id': fields.many2one('school.teacher', 'Teacher', ondelete='cascade', required=True, ),
177
'value': fields.float('Value', ),
180
school_teacher_iwl_solution()
182
class school_teachers_solution(osv.osv):
183
_name = 'school.teachers_solution'
185
def _compute(self, cr, uid, ids, field_name, arg, context=None):
186
return sum([x.percentage for x in self.browse(cr, uid, ids, context=context)]) / len(ids)
189
'name': fields.char('Name', size=100, ),
190
'created': fields.datetime('Created', ),
191
'total_hours': fields.float('Total hours', ),
192
'total_assigned': fields.float('Total assigned', ),
193
'teacher_iwl_ids': fields.one2many('school.teacher_iwl_solution', 'solution_id', 'Solution', ),
194
'value': fields.float('Value', ),
198
'created': lambda * a: datetime.today().strftime('%Y-%m-%d %H:%M:%S'),
200
school_teachers_solution()
202
def agrupa_cursos_i_professors(teacher_data_by_course):
207
for (curs, _dicci) in teacher_data_by_course.items():
208
for _dicci2 in _dicci['list']:
209
professor = _dicci2['id']
210
if curs not in cursos:
211
if professor not in professors:
212
dicci = {'courses': set([curs]), 'professors': set([professor]), }
213
cursos[curs] = len(ret)
214
professors[professor] = len(ret)
217
dicci = ret[professors[professor]]
218
dicci['courses'].add(curs)
219
cursos[curs] = professors[professor]
221
if professor not in professors:
222
dicci = ret[cursos[curs]]
223
dicci['professors'].add(professor)
224
professors[professor] = cursos[curs]
226
if cursos[curs] == professors[professor]:
228
dicci = ret[cursos[curs]]
229
dicci2 = ret[professors[professor]]
230
dicci['courses'].update(dicci2['courses'])
231
dicci['professors'].update(dicci2['professors'])
232
for curs2 in dicci2['courses']:
233
cursos[curs2] = cursos[curs]
234
for professor2 in dicci2['professors']:
235
professors[professor2] = cursos[curs]
236
dicci2['courses'] = False
237
dicci2['professors'] = False
238
return [x for x in ret if x['courses']]
240
class school_solution_data_by_course(osv.osv_memory):
241
_name = 'school.solution.data_by_course'
244
'solution_id' : fields.many2one('school.create_solutions','Solution'),
245
'course_id' : fields.many2one('school.course','Course',),
246
'classes_hours' : fields.float('Classes Hours'),
247
'max_hours' : fields.float('Max hours'),
248
'min_hours' : fields.float('Min hours'),
251
school_solution_data_by_course()
253
class school_create_solutions_wizard(osv.osv_memory):
254
_name = 'school.create_solutions'
257
'number_max_solutions': fields.integer('Max. solutions'),
258
'total_classes_hours': fields.float('Total classes hours', readonly=True, ),
259
'total_max_hours': fields.float('Max teachers hours', readonly=True, ),
260
'total_min_hours': fields.float('Min teachers hours', readonly=True, ),
261
'data_by_course' : fields.one2many('school.solution.data_by_course', 'solution_id', readonly=True, )
264
def _default_total_classes_hours(self, cr, uid, context=None):
265
(total_classes_hours, classes_data_by_course) = self.get_classes_data_by_course(cr, uid)
266
return total_classes_hours
268
def _default_total_max_hours(self, cr, uid, context=None):
269
(total_classes_hours, classes_data_by_course) = self.get_classes_data_by_course(cr, uid)
270
(total_teachers_hours, data_by_teacher, teachers_data_by_course) = self.get_teachers_data_by_course(cr, uid, classes_data_by_course.keys())
271
return total_teachers_hours['pool_max']
273
def _default_total_min_hours(self, cr, uid, context=None):
274
(total_classes_hours, classes_data_by_course) = self.get_classes_data_by_course(cr, uid)
275
(total_teachers_hours, data_by_teacher, teachers_data_by_course) = self.get_teachers_data_by_course(cr, uid, classes_data_by_course.keys())
276
return total_teachers_hours['pool_min']
278
def _default_data_by_course(self, cr, uid, context=None):
279
(total_classes_hours, classes_data_by_course) = self.get_classes_data_by_course(cr, uid)
280
(total_teachers_hours, data_by_teacher, teachers_data_by_course) = self.get_teachers_data_by_course(cr, uid, classes_data_by_course.keys())
282
for (course_id, classes_data) in classes_data_by_course.items():
283
ret.append({0: -1,'course_id': course_id,
284
'classes_hours': classes_data['duration'],
285
'max_hours': teachers_data_by_course[course_id]['pool_max'],
286
'min_hours': teachers_data_by_course[course_id]['pool_min'],})
291
'total_classes_hours': _default_total_classes_hours,
292
'total_max_hours': _default_total_max_hours,
293
'total_min_hours': _default_total_min_hours,
294
'data_by_course' : _default_data_by_course,
297
def get_teachers_hours_blocked(self, cr, uid):
298
obj_td = self.pool.get('school.teacher_data')
300
hours_by_teacher = {}
301
hours_by_course_and_teacher = {}
303
td_ids = obj_td.search(cr, uid, [('blocked', '=', True)])
304
for td in obj_td.browse(cr, uid, td_ids):
305
total_hours += td.iwl_id.duration
306
if td.teacher_id.id not in hours_by_teacher:
307
hours_by_teacher[td.teacher_id.id] = 0
308
hours_by_teacher[td.teacher_id.id] += td.iwl_id.duration
309
course_id = td.iwl_id.classe_id.course_id.id
310
key = (course_id, td.teacher_id.id)
311
if key not in hours_by_course_and_teacher:
312
hours_by_course_and_teacher[key] = 0
313
hours_by_course_and_teacher[key] += td.iwl_id.duration
314
return (total_hours, hours_by_teacher, hours_by_course_and_teacher)
318
def get_classes_data_by_course(self, cr, uid):
319
obj_iwl = self.pool.get('school.impartition_week_line')
320
iwl_ids = obj_iwl.search(cr, uid, [('teachers_blocked', '=', False), ])
324
for iwl in obj_iwl.browse(cr, uid, iwl_ids):
325
classe_id = iwl.classe_id.id
326
course_id = iwl.classe_id.course_id.id
327
if not course_id in ret:
328
ret[course_id] = {'duration': 0, 'classes': []}
329
classes[course_id] = {}
330
if (classe_id, iwl.iwl_group) not in classes[course_id]:
331
classes[course_id][(classe_id, iwl.iwl_group)] = {'duration': 0, 'iwls': []}
332
classes[course_id][(classe_id, iwl.iwl_group)]['duration'] += iwl.duration
333
classes[course_id][(classe_id, iwl.iwl_group)]['iwls'].append(iwl.id)
334
ret[course_id]['duration'] += iwl.duration
335
total_hours += iwl.duration
336
for course_id in ret.keys():
337
ret[course_id]['classes'] = list(classes[course_id].values())
338
return (total_hours, ret)
342
def get_teachers_data_by_course(self, cr, uid, courses):
343
total_hours = {'pool_max': 0, 'pool_min': 0}
344
(total_blocked_hours, blocked_hours_by_teacher, blocked_hours_by_course_and_teacher) = self.get_teachers_hours_blocked(cr, uid)
346
obj_tcs = self.pool.get('school.teacher_course_suitability')
347
teachers_suitability_ids = obj_tcs.search(cr, uid, [('course_id', 'in', list(courses))])
350
teachers_data_by_course = {}
351
for item in obj_tcs.browse(cr, uid, teachers_suitability_ids):
352
teacher_ids.add(item.teacher_id.id)
353
key = (item.course_id.id, item.teacher_id.id)
354
if item.course_id.id not in teachers_data_by_course:
355
teachers_data_by_course[item.course_id.id] = {'pool_max': 0, 'pool_min': 0, 'list': [], }
356
max_hours = max(0, item.max_week_hours - blocked_hours_by_course_and_teacher.get(key, 0))
357
min_hours = max(0, item.min_week_hours - blocked_hours_by_course_and_teacher.get(key, 0))
358
teachers_data_by_course[item.course_id.id]['list'].append ({
359
'percentage': item.percentage,
360
'id': item.teacher_id.id,
361
'pool_max': max_hours,
362
'pool_min': min_hours, })
363
teachers_data_by_course[item.course_id.id]['pool_max'] += max_hours
364
teachers_data_by_course[item.course_id.id]['pool_min'] += min_hours
366
obj_t = self.pool.get('school.teacher')
368
for x in obj_t.browse(cr, uid, obj_t.search(cr, uid, [('id','in',list(teacher_ids))])):
369
max_hours = max(0, x.max_week_hours - blocked_hours_by_teacher.get(x.id, 0))
370
min_hours = max(0, x.min_week_hours - blocked_hours_by_teacher.get(x.id, 0))
372
data_by_teacher[x.id] = {
373
'pool_max': max_hours,
374
'pool_min': min_hours,
376
total_hours['pool_max'] += max_hours
377
total_hours['pool_min'] += min_hours
379
return (total_hours, data_by_teacher, teachers_data_by_course)
381
def act_cancel(self, cr, uid, ids, context=None):
382
return {'type': 'ir.actions.act_window_close'}
384
def create_solutions(self, cr, uid, ids, context=None):
386
sols_from_wizard = []
387
for create_solutions_wizard in self.browse(cr, uid, ids):
388
number_max_solutions = create_solutions_wizard.number_max_solutions
389
(total_classes_hours, classes_data_by_course) = self.get_classes_data_by_course(cr, uid)
390
(total_teachers_hours, data_by_teacher, teachers_data_by_course) = self.get_teachers_data_by_course(cr, uid, classes_data_by_course.keys())
393
if total_classes_hours > total_teachers_hours['pool_max']: errors.append( _('Total hours to assign exceeds teachers disponibility.') )
394
if total_classes_hours < total_teachers_hours['pool_min']: errors.append( _('No classe hours for minimum requires') )
395
for (course_id, classe_data) in classes_data_by_course.items():
396
if classe_data['duration'] > teachers_data_by_course[course_id]['pool_max']:
397
errors.append( _('Total hours to assign exceeds teachers disponibility by course with id %s.') % (course_id,) )
398
if classe_data['duration'] < teachers_data_by_course[course_id]['pool_min']:
399
errors.append( _('No classe hours for minimum requires by course with id %s.') % (course_id,) )
401
raise orm.except_orm('Error!', '\n'.join(errors))
403
for agrupacio in agrupa_cursos_i_professors(teachers_data_by_course):
404
def assigna_classes(classes, data_by_teacher, teacher_course_data_list, total_hours, total_hours2, total_min_hours, total_min_hours2, max_solutions, classe_cursor=0, afinament = 2):
405
llista_de_solucions_a_retornar = []
406
classe_duration = classes[classe_cursor]['duration']
408
tcd_list_ordered = sorted(teacher_course_data_list, key=lambda x: (x['pool_min'],data_by_teacher[x['id']]['pool_min'],x['percentage'],x['pool_max']), reverse = True)
411
for tcd in tcd_list_ordered:
412
# Reduim la llista de professors
413
# Deixem els que tenen un minim d'hores per aquest curs
414
# i els que necessiten aquest curs per cumplir el minim d'hores generals
415
# completem amb els restants fins a tenir un nombre d'hores mes que suficient
416
if tcd['pool_min'] > 0 or max_hours < total_hours * afinament:
418
max_hours += tcd['pool_max']
421
for punter_teacher in range(len(new_list)):
422
teacher = new_list[punter_teacher]
423
if teacher['id'] not in data_by_teacher: continue
424
check_max_hours = teacher['pool_max'] > classe_duration
425
check_max_hours2 = data_by_teacher[teacher['id']]['pool_max'] > classe_duration
426
check_min_hours = (total_min_hours - teacher['pool_min'] <= total_hours - classe_duration)
427
check_min_hours2 = (total_min_hours2 - data_by_teacher[teacher['id']]['pool_min'] <= total_hours2 - classe_duration)
428
if check_max_hours and check_min_hours and check_max_hours2 and check_min_hours2:
429
# TODO : Mirar en FET
430
# classes[classe_cursor]['teacher'] = teacher['id']
433
# Apuntem la solucio actual
434
if len(llista_de_solucions_a_retornar) < max_solutions or llista_de_solucions_a_retornar[-1] < teacher['percentage']:
435
new_max = (teacher['percentage'], [teacher['id']])
436
bisect.insort(llista_de_solucions_a_retornar, new_max)
437
if len(llista_de_solucions_a_retornar) > max_solutions:
438
llista_de_solucions_a_retornar = llista_de_solucions_a_retornar[0:max_solutions]
440
# Si es la darrera classe no cal fer mes recursivitat
441
if classe_cursor + 1 >= len(classes):
444
# Actualitzem les variables de les estructures de dades
445
teacher['pool_max'] -= classe_duration
446
data_by_teacher[teacher['id']]['pool_max'] -= classe_duration
447
min_to_reduce = min(classe_duration, teacher['pool_min'])
448
teacher['pool_min'] -= min_to_reduce
449
min_to_reduce2 = min(classe_duration, data_by_teacher[teacher['id']]['pool_min'])
450
data_by_teacher[teacher['id']]['pool_min'] -= min_to_reduce2
452
# Executem la recursio
453
value_assignacio_list = assigna_classes(classes, data_by_teacher, teacher_course_data_list, total_hours - classe_duration, total_hours2 - classe_duration, total_min_hours - min_to_reduce, total_min_hours2 - min_to_reduce2, max_solutions, classe_cursor = classe_cursor + 1)
455
# Deixem les variables de les estructures de dades com estaven
456
teacher['pool_max'] += classe_duration
457
teacher['pool_min'] += min_to_reduce
458
data_by_teacher[teacher['id']]['pool_max'] += classe_duration
459
data_by_teacher[teacher['id']]['pool_min'] += min_to_reduce2
461
# Apuntem els resultats de la recursio
462
for (value, assignacio) in value_assignacio_list:
463
if len(llista_de_solucions_a_retornar) < max_solutions or llista_de_solucions_a_retornar[max_solutions-1][0] < value + teacher['percentage']:
464
new_max = (value + teacher['percentage'], [teacher['id']] + assignacio)
465
llista_de_solucions_a_retornar.reverse()
466
bisect.insort(llista_de_solucions_a_retornar, new_max)
467
llista_de_solucions_a_retornar.reverse()
468
if len(llista_de_solucions_a_retornar) > max_solutions:
469
llista_de_solucions_a_retornar = llista_de_solucions_a_retornar[0:max_solutions]
471
return llista_de_solucions_a_retornar
473
def assigna_cursos(cursos_list_orig, total_classes_hours, classes_data_by_course, total_teachers_hours, data_by_teacher, teachers_data_by_course, max_solutions):
474
llista_de_solucions_a_retornar = []
475
for courses_punter in range(len(cursos_list_orig)):
476
cursos_list = cursos_list_orig[:courses_punter] + cursos_list_orig[courses_punter + 1:]
477
course_id = cursos_list_orig[courses_punter]
479
classes = classes_data_by_course[course_id]['classes']
480
total_course_duration = classes_data_by_course[course_id]['duration']
481
teacher_course_data = teachers_data_by_course[course_id]
482
teacher_course_data_list = teacher_course_data['list']
484
sols = assigna_classes(classes, data_by_teacher, teacher_course_data_list, total_course_duration, total_classes_hours, teacher_course_data['pool_min'], total_teachers_hours['pool_min'], max_solutions)
486
if not sols: continue
489
for (value, assignacio) in sols:
490
llista_de_solucions_a_retornar.append((value, {course_id: assignacio, }))
494
for (value, assignacio) in sols:
495
# Contem les hores per reduir el necessari en l'estructura
496
# de dades d'hores maximes que passarem per assignar els seguents cursos
497
hours_by_teacher = {}
499
for i in range(len(assignacio)):
500
teacher_id = assignacio[i]
502
if teacher_id not in hours_by_teacher:
503
hours_by_teacher[teacher_id] = 0
504
hours_by_teacher[teacher_id] += classe['duration']
505
total_hours += classe['duration']
507
# Apliquem la reduccio i contabilitzem les hores minimes
508
total_teachers_hours['pool_max'] -= total_hours
510
min_to_reduce_by_teacher = {}
511
for (teacher_id, hours) in hours_by_teacher.items():
512
data_by_teacher[teacher_id]['pool_max'] -= hours
513
min_to_reduce_by_teacher['teacher_id'] = min(hours, data_by_teacher[teacher_id]['pool_min'])
514
data_by_teacher[teacher_id]['pool_min'] -= min_to_reduce_by_teacher['teacher_id']
515
min_to_reduce += min_to_reduce_by_teacher['teacher_id']
516
total_teachers_hours['pool_min'] -= min_to_reduce
518
# Fem recursivitat per
519
value_assignacio_list = assigna_cursos(cursos_list, total_classes_hours - total_hours, classes_data_by_course, total_teachers_hours, data_by_teacher, teachers_data_by_course, max_solutions)
520
total_teachers_hours['pool_max'] += total_hours
521
total_teachers_hours['pool_min'] += min_to_reduce
522
for (teacher_id, hours) in hours_by_teacher.items():
523
data_by_teacher[teacher_id]['pool_max'] += hours
524
data_by_teacher[teacher_id]['pool_min'] += min_to_reduce_by_teacher['teacher_id']
526
for (value2, assignacio_by_course) in value_assignacio_list:
527
if len(llista_de_solucions_a_retornar) < max_solutions or llista_de_solucions_a_retornar[max_solutions-1][0] < value2 + value:
528
assignacio_by_course[course_id] = assignacio
529
new_max = (value2 + value, assignacio_by_course)
530
llista_de_solucions_a_retornar.reverse()
531
bisect.insort(llista_de_solucions_a_retornar, new_max)
532
llista_de_solucions_a_retornar.reverse()
533
if len(llista_de_solucions_a_retornar) > max_solutions:
534
llista_de_solucions_a_retornar = llista_de_solucions_a_retornar[0:max_solutions]
535
return llista_de_solucions_a_retornar
537
# TODO: fer una llista de cursos ordenada comencant per el mes dificil d'assignar
538
# professors. Es provaran totes pero s'ha de mirar de comencar a
539
# permutar per on hi ha mes probabilitat de solucio
540
sols = assigna_cursos(list(agrupacio['courses']), total_classes_hours, classes_data_by_course, total_teachers_hours, data_by_teacher, teachers_data_by_course, number_max_solutions)
542
# Passar solucions a base de dades
543
for (value, assignacio_by_course) in sols:
544
sol_id = self.pool.get('school.teachers_solution').create(cr, uid, {'value': value, 'total_hours': total_classes_hours, })
545
sols_from_wizard.append(sol_id)
547
for (course_id, assignacio) in assignacio_by_course.items():
548
for i in range(len(assignacio)):
549
classe = classes_data_by_course[course_id]['classes'][i]
550
total_assigned += classe['duration']
551
for iwl_id in classe['iwls']:
552
value = [x['percentage'] for x in teachers_data_by_course[course_id]['list'] if x['id'] == assignacio[i]][0]
553
self.pool.get('school.teacher_iwl_solution').create(cr, uid, {'solution_id': sol_id, 'iwl_id': iwl_id, 'teacher_id': assignacio[i], 'value': value})
554
self.pool.get('school.teachers_solution').write(cr, uid, [sol_id], {'total_assigned': total_assigned, })
556
cr.execute('select id,name from ir_ui_view where model=%s and type=%s', ('school.teachers_solution', 'tree'))
557
view_res = cr.fetchone()
560
'domain': "[('id','in',%s),]" % (tuple(sols_from_wizard), ),
561
'name': _("Solutions from wizard"),
563
'view_mode': 'tree,form',
564
'res_model': 'school.teachers_solution',
567
'type': 'ir.actions.act_window',
571
school_create_solutions_wizard()
573
class school_block_iwl_wizard(osv.osv_memory):
574
_name = 'school.block_iwl_wizard'
577
'iwl_ids': fields.many2many('school.impartition_week_line', 'iwl_block_wizard_rel', 'wizard_id', 'iwl_id', string='IWLs', ),
578
'block': fields.boolean('Block', help='Block, yes or no',),
579
'teachers_needed': fields.integer('Teachers needed', ),
580
'teachers_recommended': fields.integer('Teachers recommended', ),
585
'iwl_ids': lambda self, cr, uid, context = {}: [(6,0,context.get('active_ids', []))],
586
'teachers_needed': lambda * a: 1,
587
'teachers_recommended': lambda * a: 1,
590
def act_cancel(self, cr, uid, ids, context=None):
591
return {'type': 'ir.actions.act_window_close'}
593
def action_block(self, cr, uid, ids, context=None):
594
for item in self.browse(cr, uid, ids):
595
iwl_ids = [x.id for x in item.iwl_ids]
596
self.pool.get('school.impartition_week_line').write(cr, uid, iwl_ids, {'teachers_needed': item.teachers_needed, 'teachers_recommended': item.teachers_recommended})
597
td_ids = self.pool.get('school.teacher_data').search(cr, uid, [('iwl_id', 'in', iwl_ids)])
598
self.pool.get('school.teacher_data').write(cr, uid, td_ids, {'blocked': item.block, })
600
school_block_iwl_wizard()
602
class school_block_td_wizard(osv.osv_memory):
603
_name = 'school.block_td_wizard'
606
'td_ids': fields.many2many('school.impartition_week_line', 'iwl_block_wizard_rel', 'wizard_id', 'iwl_id', string='IWLs', ),
607
'block': fields.boolean('Block', help='Block, yes or no',),
612
'td_ids': lambda self, cr, uid, context = {}: [(6,0,context.get('active_ids', []))],
615
def act_cancel(self, cr, uid, ids, context=None):
616
return {'type': 'ir.actions.act_window_close'}
618
def action_block(self, cr, uid, ids, context=None):
619
for item in self.browse(cr, uid, ids):
620
td_ids = [x.id for x in item.td_ids]
621
self.pool.get('school.teacher_data').write(cr, uid, td_ids, {'blocked': item.block, })
623
school_block_td_wizard()
625
class school_teacher_change_hours(osv.osv_memory):
626
_name = 'school.teacher_change_hours'
629
'teacher_ids': fields.many2many('school.teacher', 'teacher_change_hours_wizard_rel', 'wizard_id', 'teacher_id', string='Teachers', ),
630
'max_hours': fields.float('Max Hours', help='Max hours',),
631
'min_hours': fields.float('Min Hours', help='Min hours',),
635
'teacher_ids': lambda self, cr, uid, context = {}: context.get('active_ids', []),
638
def act_cancel(self, cr, uid, ids, context=None):
639
return {'type': 'ir.actions.act_window_close'}
641
def action_change(self, cr, uid, ids, context=None):
642
for item in self.browse(cr, uid, ids):
643
teacher_ids = [x.id for x in item.teacher_ids]
644
self.pool.get('school.teacher').write(cr, uid, teacher_ids, {'max_week_hours': item.max_hours, 'min_week_hours': item.min_hours})
645
return {'type': 'ir.actions.act_window_close'}
647
school_teacher_change_hours()
649
class school_teacher_suitability_change(osv.osv_memory):
650
_name = 'school.teacher_suitability_change'
653
'teacher_ids': fields.many2many('school.teacher', 'teacher_suitability_change_teacher_rel', 'wizard_id', 'teacher_id', string='Teachers', ),
654
'course_ids': fields.many2many('school.course', 'teacher_suitability_change_course_rel', 'wizard_id', 'course_id', string='Courses', ),
655
'percentage': fields.float('Percentage', ),
656
'max_hours': fields.float('Max Hours', help='Max hours',),
657
'min_hours': fields.float('Min Hours', help='Min hours',),
661
'teacher_ids': lambda self, cr, uid, context = {}: context.get('active_ids', []),
664
def act_cancel(self, cr, uid, ids, context=None):
665
return {'type': 'ir.actions.act_window_close'}
667
def action_change(self, cr, uid, ids, context=None):
668
obj = self.pool.get('school.teacher_course_suitability')
669
for item in self.browse(cr, uid, ids):
670
dicci = {'max_week_hours': item.max_hours, 'min_week_hours': item.min_hours, 'percentage': item.percentage,}
672
for teacher in item.teacher_ids:
673
for course in item.course_ids:
674
tcs_ids = obj.search(cr, uid, [('teacher_id', '=', teacher.id), ('course_id', '=', course.id)])
676
ids_to_write += tcs_ids
679
dicci2.update({'teacher_id': teacher.id, 'course_id': course.id,})
680
obj.create(cr, uid, dicci2, context = context)
682
obj.write(cr, uid, ids_to_write, dicci, context = context)
683
return {'type': 'ir.actions.act_window_close'}
685
school_teacher_suitability_change()
687
class school_apply_teacher_solution(osv.osv_memory):
688
_name = 'school.apply_teacher_solution'
691
'solution_ids': fields.many2many('school.teachers_solution', 'teacher_solution_apply_rel', 'wizard_id', 'solution_id', string='Solutions', ),
695
'solution_ids': lambda self, cr, uid, context = {}: context.get('active_ids', []),
698
def act_cancel(self, cr, uid, ids, context=None):
699
return {'type': 'ir.actions.act_window_close'}
701
def action_change(self, cr, uid, ids, context=None):
702
obj_td = self.pool.get('school.teacher_data')
705
for item in self.browse(cr, uid, ids):
707
for solution in item.solution_ids:
708
for teacher_iwl in solution.teacher_iwl_ids:
709
iwl_id = teacher_iwl.iwl_id.id
710
if iwl_id not in iwls_cleaned:
711
iwls_cleaned.add(iwl_id)
712
ids_to_unlink += obj_td.search(cr, uid, [('iwl_id','=',iwl_id),('blocked','=',False)])
713
ids_td_created.append( obj_td.create(cr, uid, {'iwl_id': iwl_id, 'teacher_id': teacher_iwl.teacher_id.id, 'title': 'needed'}) )
714
obj_td.unlink(cr, uid, ids_to_unlink)
716
cr.execute('select id,name from ir_ui_view where model=%s and type=%s', ('school.teacher_data', 'tree'))
717
view_res = cr.fetchone()
720
'domain': "[('id','in',%s),]" % (tuple(ids_td_created), ),
721
'name': _("Assignations created by the apply solutions wizard"),
723
'view_mode': 'tree,form',
724
'res_model': 'school.teacher_data',
727
'type': 'ir.actions.act_window',
729
school_apply_teacher_solution()
732
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: