<commit_before>package org.innovateuk.ifs.project.queries.transactional;

import org.innovateuk.ifs.activitylog.resource.ActivityType;
import org.innovateuk.ifs.activitylog.transactional.ActivityLogService;
import org.innovateuk.ifs.application.domain.Application;
import org.innovateuk.ifs.commons.service.ServiceResult;
import org.innovateuk.ifs.finance.domain.ProjectFinance;
import org.innovateuk.ifs.finance.repository.ProjectFinanceRepository;
import org.innovateuk.ifs.notifications.resource.*;
import org.innovateuk.ifs.notifications.service.NotificationService;
import org.innovateuk.ifs.project.core.domain.Project;
import org.innovateuk.ifs.project.core.domain.ProjectUser;
import org.innovateuk.ifs.project.core.transactional.AbstractProjectServiceImpl;
import org.innovateuk.ifs.threads.mapper.PostMapper;
import org.innovateuk.ifs.threads.mapper.QueryMapper;
import org.innovateuk.ifs.threads.repository.QueryRepository;
import org.innovateuk.ifs.threads.resource.PostResource;
import org.innovateuk.ifs.threads.resource.QueryResource;
import org.innovateuk.ifs.threads.service.MappingMessageThreadService;
import org.innovateuk.ifs.threads.service.MessageThreadService;
import org.innovateuk.ifs.user.domain.User;
import org.innovateuk.ifs.util.AuthenticationHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static org.innovateuk.ifs.commons.error.CommonErrors.forbiddenError;
import static org.innovateuk.ifs.commons.error.CommonErrors.notFoundError;
import static org.innovateuk.ifs.commons.error.CommonFailureKeys.NOTIFICATIONS_UNABLE_TO_SEND_SINGLE;
import static org.innovateuk.ifs.commons.error.CommonFailureKeys.QUERIES_CANNOT_BE_SENT_AS_FINANCE_CONTACT_NOT_SUBMITTED;
import static org.innovateuk.ifs.commons.service.ServiceResult.serviceFailure;
import static org.innovateuk.ifs.notifications.resource.NotificationMedium.EMAIL;
import static org.innovateuk.ifs.user.resource.Role.PROJECT_FINANCE;
import static org.innovateuk.ifs.util.CollectionFunctions.simpleFilter;
import static org.innovateuk.ifs.util.EntityLookupCallbacks.find;

@Service
public class FinanceCheckQueriesServiceImpl extends AbstractProjectServiceImpl implements FinanceCheckQueriesService {

    private final MessageThreadService<QueryResource, PostResource> service;

    @Autowired
    private SystemNotificationSource systemNotificationSource;

    @Autowired
    private NotificationService notificationService;

    @Autowired
    private ProjectFinanceRepository projectFinanceRepository;

    @Autowired
    private ActivityLogService activityLogService;

    @Value("${ifs.web.baseURL}")
    private String webBaseUrl;

    public enum Notifications {
        NEW_FINANCE_CHECK_QUERY,
        NEW_FINANCE_CHECK_QUERY_RESPONSE
    }

    @Autowired
    public FinanceCheckQueriesServiceImpl(QueryRepository queryRepository, AuthenticationHelper authenticationHelper, QueryMapper queryMapper, PostMapper postMapper) {
        service = new MappingMessageThreadService<>(queryRepository, authenticationHelper, queryMapper, postMapper, ProjectFinance.class);
    }

    @Override
    public ServiceResult<QueryResource> findOne(Long id) {
        return service.findOne(id);
    }

    @Override
    public ServiceResult<List<QueryResource>> findAll(Long classContextId) {
        return service.findAll(classContextId);
    }

    @Override
    @Transactional
    public ServiceResult<Void> addPost(PostResource post, Long threadId) {
        return findOne(threadId).andOnSuccess(query -> {
            ProjectFinance projectFinance = projectFinanceRepository.findById(query.contextClassPk).get();
            Optional<ProjectUser> financeContact = getFinanceContact(projectFinance.getProject(), projectFinance.getOrganisation());
            if (financeContact.isPresent()) {
                ServiceResult<Void> result = service.addPost(post, threadId);
                if (result.isSuccess() && post.author.hasRole(PROJECT_FINANCE)) {
                    Project project = projectFinance.getProject();
                    return sendResponseNotification(financeContact.get().getUser(), project)
                            .andOnSuccessReturn(() -> query);
                }
                return result.andOnSuccessReturn(() -> query);
            } else {
                return serviceFailure(forbiddenError(QUERIES_CANNOT_BE_SENT_AS_FINANCE_CONTACT_NOT_SUBMITTED));
            }
        }).andOnSuccessReturnVoid(query ->
            activityLogService.recordQueryActivityByProjectFinanceId(query.contextClassPk,
                    ActivityType.FINANCE_QUERY_RESPONDED, threadId)
        );
    }

    @Override
    @Transactional
    public ServiceResult<Long> create(QueryResource query) {
        return find(projectFinanceRepository.findById(query.contextClassPk), notFoundError(ProjectFinance.class, query.contextClassPk)).
                andOnSuccess(projectFinance -> {
                    if (getFinanceContact(projectFinance.getProject(), projectFinance.getOrganisation()).isPresent()) {
                        ServiceResult<Long> result = service.create(query);
                        if (result.isSuccess()) {
                            Project project = projectFinance.getProject();
                            List<ProjectUser> projectUsers = project.getProjectUsers(u -> u.getRole().isFinanceContact() || u.getRole().isProjectManager());
                            List<ProjectUser> financeContacts = simpleFilter(projectUsers, pu -> Objects.equals(pu.getOrganisation().getId(), projectFinance.getOrganisation().getId()));
                            Set<User> usersToNotify = financeContacts.stream().map(ProjectUser::getUser).collect(Collectors.toSet());
                            for (User user: usersToNotify) {
                                ServiceResult<Void> notificationResult = sendNewQueryNotification(user, project);

                                if (!notificationResult.isSuccess()) {
                                    return serviceFailure(NOTIFICATIONS_UNABLE_TO_SEND_SINGLE);
                                }
                            }
                        }
                        return result;
                    } else {
                        return serviceFailure(forbiddenError(QUERIES_CANNOT_BE_SENT_AS_FINANCE_CONTACT_NOT_SUBMITTED));
                    }
                }).andOnSuccessReturn(threadId -> {
                    activityLogService.recordQueryActivityByProjectFinanceId(query.contextClassPk,
                            ActivityType.FINANCE_QUERY, threadId);
                    return threadId;
                });<commit_msg>IFS-8943 using cleaner method for filtering project users<commit_after>package org.innovateuk.ifs.project.queries.transactional;

import org.innovateuk.ifs.activitylog.resource.ActivityType;
import org.innovateuk.ifs.activitylog.transactional.ActivityLogService;
import org.innovateuk.ifs.application.domain.Application;
import org.innovateuk.ifs.commons.service.ServiceResult;
import org.innovateuk.ifs.finance.domain.ProjectFinance;
import org.innovateuk.ifs.finance.repository.ProjectFinanceRepository;
import org.innovateuk.ifs.notifications.resource.*;
import org.innovateuk.ifs.notifications.service.NotificationService;
import org.innovateuk.ifs.project.core.domain.Project;
import org.innovateuk.ifs.project.core.domain.ProjectParticipantRole;
import org.innovateuk.ifs.project.core.domain.ProjectUser;
import org.innovateuk.ifs.project.core.transactional.AbstractProjectServiceImpl;
import org.innovateuk.ifs.threads.mapper.PostMapper;
import org.innovateuk.ifs.threads.mapper.QueryMapper;
import org.innovateuk.ifs.threads.repository.QueryRepository;
import org.innovateuk.ifs.threads.resource.PostResource;
import org.innovateuk.ifs.threads.resource.QueryResource;
import org.innovateuk.ifs.threads.service.MappingMessageThreadService;
import org.innovateuk.ifs.threads.service.MessageThreadService;
import org.innovateuk.ifs.user.domain.User;
import org.innovateuk.ifs.util.AuthenticationHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static org.innovateuk.ifs.commons.error.CommonErrors.forbiddenError;
import static org.innovateuk.ifs.commons.error.CommonErrors.notFoundError;
import static org.innovateuk.ifs.commons.error.CommonFailureKeys.NOTIFICATIONS_UNABLE_TO_SEND_SINGLE;
import static org.innovateuk.ifs.commons.error.CommonFailureKeys.QUERIES_CANNOT_BE_SENT_AS_FINANCE_CONTACT_NOT_SUBMITTED;
import static org.innovateuk.ifs.commons.service.ServiceResult.serviceFailure;
import static org.innovateuk.ifs.notifications.resource.NotificationMedium.EMAIL;
import static org.innovateuk.ifs.project.core.domain.ProjectParticipantRole.PROJECT_FINANCE_CONTACT;
import static org.innovateuk.ifs.project.core.domain.ProjectParticipantRole.PROJECT_MANAGER;
import static org.innovateuk.ifs.user.resource.Role.PROJECT_FINANCE;
import static org.innovateuk.ifs.util.CollectionFunctions.simpleFilter;
import static org.innovateuk.ifs.util.EntityLookupCallbacks.find;

@Service
public class FinanceCheckQueriesServiceImpl extends AbstractProjectServiceImpl implements FinanceCheckQueriesService {

    private final MessageThreadService<QueryResource, PostResource> service;

    @Autowired
    private SystemNotificationSource systemNotificationSource;

    @Autowired
    private NotificationService notificationService;

    @Autowired
    private ProjectFinanceRepository projectFinanceRepository;

    @Autowired
    private ActivityLogService activityLogService;

    @Value("${ifs.web.baseURL}")
    private String webBaseUrl;

    public enum Notifications {
        NEW_FINANCE_CHECK_QUERY,
        NEW_FINANCE_CHECK_QUERY_RESPONSE
    }

    @Autowired
    public FinanceCheckQueriesServiceImpl(QueryRepository queryRepository, AuthenticationHelper authenticationHelper, QueryMapper queryMapper, PostMapper postMapper) {
        service = new MappingMessageThreadService<>(queryRepository, authenticationHelper, queryMapper, postMapper, ProjectFinance.class);
    }

    @Override
    public ServiceResult<QueryResource> findOne(Long id) {
        return service.findOne(id);
    }

    @Override
    public ServiceResult<List<QueryResource>> findAll(Long classContextId) {
        return service.findAll(classContextId);
    }

    @Override
    @Transactional
    public ServiceResult<Void> addPost(PostResource post, Long threadId) {
        return findOne(threadId).andOnSuccess(query -> {
            ProjectFinance projectFinance = projectFinanceRepository.findById(query.contextClassPk).get();
            Optional<ProjectUser> financeContact = getFinanceContact(projectFinance.getProject(), projectFinance.getOrganisation());
            if (financeContact.isPresent()) {
                ServiceResult<Void> result = service.addPost(post, threadId);
                if (result.isSuccess() && post.author.hasRole(PROJECT_FINANCE)) {
                    Project project = projectFinance.getProject();
                    return sendResponseNotification(financeContact.get().getUser(), project)
                            .andOnSuccessReturn(() -> query);
                }
                return result.andOnSuccessReturn(() -> query);
            } else {
                return serviceFailure(forbiddenError(QUERIES_CANNOT_BE_SENT_AS_FINANCE_CONTACT_NOT_SUBMITTED));
            }
        }).andOnSuccessReturnVoid(query ->
            activityLogService.recordQueryActivityByProjectFinanceId(query.contextClassPk,
                    ActivityType.FINANCE_QUERY_RESPONDED, threadId)
        );
    }

    @Override
    @Transactional
    public ServiceResult<Long> create(QueryResource query) {
        return find(projectFinanceRepository.findById(query.contextClassPk), notFoundError(ProjectFinance.class, query.contextClassPk)).
                andOnSuccess(projectFinance -> {
                    if (getFinanceContact(projectFinance.getProject(), projectFinance.getOrganisation()).isPresent()) {
                        ServiceResult<Long> result = service.create(query);
                        if (result.isSuccess()) {
                            Project project = projectFinance.getProject();
                            List<ProjectUser> projectUsers = project.getProjectUsersWithRole(PROJECT_FINANCE_CONTACT, PROJECT_MANAGER);
                            List<ProjectUser> financeContacts = simpleFilter(projectUsers, pu -> Objects.equals(pu.getOrganisation().getId(), projectFinance.getOrganisation().getId()));
                            Set<User> usersToNotify = financeContacts.stream().map(ProjectUser::getUser).collect(Collectors.toSet());
                            for (User user: usersToNotify) {
                                ServiceResult<Void> notificationResult = sendNewQueryNotification(user, project);

                                if (!notificationResult.isSuccess()) {
                                    return serviceFailure(NOTIFICATIONS_UNABLE_TO_SEND_SINGLE);
                                }
                            }
                        }
                        return result;
                    } else {
                        return serviceFailure(forbiddenError(QUERIES_CANNOT_BE_SENT_AS_FINANCE_CONTACT_NOT_SUBMITTED));
                    }
                }).andOnSuccessReturn(threadId -> {
                    activityLogService.recordQueryActivityByProjectFinanceId(query.contextClassPk,
                            ActivityType.FINANCE_QUERY, threadId);
                    return threadId;
                });
{'fraction_non_alphanumeric': 0.08412280076928556, 'fraction_numerical': 0.00028492057838877413, 'mean_word_length': 3.3724696356275303, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1572441', 'n_tokens_mistral': 3725, 'n_tokens_neox': 3683, 'n_words': 520}