from patch.include import *
from tqdm import tqdm
from collections import defaultdict


@app_context(commit=True)
def fix_resource_assignments():
    """
    Для тестирования патча: ( cd /opt/eva-app; python3 -m patch.202502281718_fix_resource_assignments )
    BCRM ~ 1.5 минута
    """
    print('Запуск патча fix_resource_assignments')
    _filter = [['cmf_created_at', '>', 'now() - 1y 6m'],
               ['OR', ['responsible', '!=', None],
                      ['executors', 'EXISTS', '']]]
    _fields = ['executors.cmf_deleted', 'responsible.cmf_deleted', 'executors.calendar', 'responsible.calendar',
               'op_gantt_task', 'op_gantt_task.sched_start_date']

    to_be_fixed_ids = []

    for t in models.CmfTask.slist(filter=_filter, fields=['executors', 'responsible', 'parent']):
        executors_count = len([e for e in t.executors if e.class_name == 'CmfPerson']) if t.executors else 0

        if t.responsible and t.responsible.class_name == 'CmfPerson':
            executors_count += 1

        assignments = models.CmfTaskResAssign.slist(filter=['parent_id', '==', t.id])

        if len(assignments) > 0 and executors_count != len(assignments):
            to_be_fixed_ids.append(t.id)

    errors = []

    i = 0
    step = 100
    while True:
        batch =  models.CmfTask.list(filter=['id', 'IN', to_be_fixed_ids], fields=_fields, slice=[i, i + step])

        if not batch:
            break

        for task in batch:

            persons = set()
            if task.responsible and isinstance(task.responsible.value, models.CmfPerson) and not task.responsible.cmf_deleted:
                persons.add(task.responsible.value)
            if task.executors:
                persons.update({executor for executor in task.executors if isinstance(executor, models.CmfPerson) and not executor.cmf_deleted})

            cur_assignments = models.CmfTaskResAssign.list(filter=['parent', '==', task],
                                                           fields=['person', 'parent', 'calendar'])
            cur_persons = {assignment.person for assignment in cur_assignments}
            to_be_removed_persons = cur_persons - persons

            # Лишние ресурсы удаляем
            if to_be_removed_persons:
                with cmfutil.disable_acl():
                    models.CmfTaskResAssign.remove_task_resources(task, list(to_be_removed_persons))

            # Пытаемся пересчитать план
            try:
                models.CmfTaskResAssign.sync_task_resources(task)
            except CmfAbortError as e:
                errors.append((task.code, task.name, str(e)))

        i += step

        cmf_commit()

    for task_code, task_name, error_text in errors:
        print()
        print('Возникла ошибка при расчете ресурсов: ')
        print('Код задачи:      ', task_code)
        print('Название задачи: ', task_name)
        print()
        print(error_text)
        print()

if __name__ == "__main__":
    fix_resource_assignments()
