'Update the goals to recomputes values and change of states
If a manual goal is not updated for enough time, the user will be
reminded to do so (done only once, in \'inprogress\' state).
If a goal reaches the target value, the status is set to reached
If the end date is passed (at least +1 day, time not considered) without
the target value being reached, the goal is set as failed.'

@api.multi
def update_goal(self):

    goals_by_definition = {}
    for goal in self:
        goals_by_definition.setdefault(goal.definition_id, []).append(goal)
    for (definition, goals) in goals_by_definition.items():
        goals_to_write = {}
        if (definition.computation_mode == 'manually'):
            for goal in goals:
                goals_to_write[goal] = goal._check_remind_delay()
        elif (definition.computation_mode == 'python'):
            for goal in goals:
                cxt = {'object': goal, 'env': self.env, 'date': date, 'datetime': datetime, 'timedelta': timedelta, 'time': time}
                code = definition.compute_code.strip()
                safe_eval(code, cxt, mode='exec', nocopy=True)
                result = cxt.get('result')
                if ((result is not None) and isinstance(result, (float, int, long))):
                    goals_to_write.update(goal._get_write_values(result))
                else:
                    _logger.error("Invalid    return    content    '%r'    from    the    evaluation    of    code    for    definition    %s,    expected    a    number", result, definition.name)
        else:
            Obj = self.env[definition.model_id.model]
            field_date_name = definition.field_date_id.name
            if ((definition.computation_mode == 'count') and definition.batch_mode):
                general_domain = safe_eval(definition.domain)
                field_name = definition.batch_distinctive_field.name
                subqueries = {}
                for goal in goals:
                    start_date = ((field_date_name and goal.start_date) or False)
                    end_date = ((field_date_name and goal.end_date) or False)
                    subqueries.setdefault((start_date, end_date), {}).update({goal.id: safe_eval(definition.batch_user_expression, {'user': goal.user_id})})
                for ((start_date, end_date), query_goals) in subqueries.items():
                    subquery_domain = list(general_domain)
                    subquery_domain.append((field_name, 'in', list(set(query_goals.values()))))
                    if start_date:
                        subquery_domain.append((field_date_name, '>=', start_date))
                    if end_date:
                        subquery_domain.append((field_date_name, '<=', end_date))
                    if (field_name == 'id'):
                        users = Obj.search(subquery_domain)
                        user_values = [{'id': user.id, 'id_count': 1} for user in users]
                    else:
                        user_values = Obj.read_group(subquery_domain, fields=[field_name], groupby=[field_name])
                    for goal in [g for g in goals if (g.id in query_goals)]:
                        for user_value in user_values:
                            queried_value = (((field_name in user_value) and user_value[field_name]) or False)
                            if (isinstance(queried_value, tuple) and (len(queried_value) == 2) and isinstance(queried_value[0], (int, long))):
                                queried_value = queried_value[0]
                            if (queried_value == query_goals[goal.id]):
                                new_value = user_value.get((field_name + '_count'), goal.current)
                                goals_to_write.update(goal._get_write_values(new_value))
            else:
                for goal in goals:
                    domain = safe_eval(definition.domain, {'user': goal.user_id})
                    if (goal.start_date and field_date_name):
                        domain.append((field_date_name, '>=', goal.start_date))
                    if (goal.end_date and field_date_name):
                        domain.append((field_date_name, '<=', goal.end_date))
                    if (definition.computation_mode == 'sum'):
                        field_name = definition.field_id.name
                        res = Obj.read_group(domain, [field_name], [])
                        new_value = ((res and res[0][field_name]) or 0.0)
                    else:
                        new_value = Obj.search_count(domain)
                    goals_to_write.update(goal._get_write_values(new_value))
        for (goal, values) in goals_to_write.iteritems():
            if (not values):
                continue
            goal.write(values)
        if self.env.context.get('commit_gamification'):
            self.env.cr.commit()
    return True
