<commit_before>package org.motechproject.nms.kilkari.service.impl;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.motechproject.alerts.contract.AlertService;
import org.motechproject.alerts.domain.AlertStatus;
import org.motechproject.alerts.domain.AlertType;
import org.motechproject.event.MotechEvent;
import org.motechproject.event.listener.annotations.MotechListener;
import org.motechproject.metrics.service.Timer;
import org.motechproject.nms.kilkari.domain.CallRetry;
import org.motechproject.nms.kilkari.domain.CallStage;
import org.motechproject.nms.kilkari.domain.DeactivationReason;
import org.motechproject.nms.kilkari.domain.Subscription;
import org.motechproject.nms.kilkari.domain.SubscriptionOrigin;
import org.motechproject.nms.kilkari.domain.SubscriptionStatus;
import org.motechproject.nms.kilkari.dto.CallSummaryRecordDto;
import org.motechproject.nms.kilkari.exception.InvalidCallRecordDataException;
import org.motechproject.nms.kilkari.exception.NoSuchSubscriptionException;
import org.motechproject.nms.kilkari.repository.CallRetryDataService;
import org.motechproject.nms.kilkari.repository.SubscriptionDataService;
import org.motechproject.nms.kilkari.service.CsrService;
import org.motechproject.nms.kilkari.service.CsrVerifierService;
import org.motechproject.nms.props.domain.FinalCallStatus;
import org.motechproject.nms.props.domain.StatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static java.lang.Math.min;


@Service("csrService")
public class CsrServiceImpl implements CsrService {

    private static final String NMS_IMI_KK_PROCESS_CSR = "nms.imi.kk.process_csr";
    private static final int MAX_CHAR_ALERT = 4900;

    private static final Logger LOGGER = LoggerFactory.getLogger(CsrServiceImpl.class);

    private SubscriptionDataService subscriptionDataService;
    private CallRetryDataService callRetryDataService;
    private AlertService alertService;
    private CsrVerifierService csrVerifierService;

    @Autowired
    public CsrServiceImpl(SubscriptionDataService subscriptionDataService, CallRetryDataService callRetryDataService,
                          AlertService alertService, CsrVerifierService csrVerifierService) {
        this.subscriptionDataService = subscriptionDataService;
        this.callRetryDataService = callRetryDataService;
        this.alertService = alertService;
        this.csrVerifierService = csrVerifierService;
    }


    private void completeSubscriptionIfNeeded(Subscription subscription, String contentFileName) {

        resetWelcomeFlagInSubscription(subscription);

        if (!subscription.isLastPackMessage(contentFileName)) {
            // This subscription has not completed, do nothing
            return;
        }

        // Mark the subscription completed
        subscription.setStatus(SubscriptionStatus.COMPLETED);
        subscriptionDataService.update(subscription);

    }


    private void deleteCallRetryRecordIfNeeded(CallRetry callRetry) {
        if (callRetry == null) {
            return;
        }
        callRetryDataService.delete(callRetry);
    }


    private void deactivateSubscription(Subscription subscription, CallRetry callRetry) {

        if (subscription.getOrigin() == SubscriptionOrigin.IVR) {
            String error = String.format("Subscription %s was rejected (DND) but its origin is IVR, not MCTS!",
                    subscription.getSubscriptionId());
            LOGGER.error(error);
            alertService.create(subscription.getSubscriptionId(), "subscription", error, AlertType.CRITICAL,
                    AlertStatus.NEW, 0, null);
            return;
        }

        deleteCallRetryRecordIfNeeded(callRetry);

        //Deactivate the subscription
        subscription.setStatus(SubscriptionStatus.DEACTIVATED);
        subscription.setDeactivationReason(DeactivationReason.DO_NOT_DISTURB);
        subscriptionDataService.update(subscription);
    }


     private void doReschedule(Subscription subscription, CallRetry existingCallRetry, CallSummaryRecordDto csrDto) {

        boolean invalidNr = StatusCode.fromInt(csrDto.getStatusCode()).equals(StatusCode.OBD_FAILED_INVALIDNUMBER);

        if (existingCallRetry == null) {
            // We've never retried this call, let's do it
            callRetryDataService.create(new CallRetry(
                    subscription.getSubscriptionId(),
                    subscription.getSubscriber().getCallingNumber(),
                    CallStage.RETRY_1,
                    csrDto.getContentFileName(),
                    csrDto.getWeekId(),
                    csrDto.getLanguageCode(),
                    csrDto.getCircleName(),
                    subscription.getOrigin(),
                    csrDto.getTargetFileTimeStamp(),
                    invalidNr ? 1 : 0)
            );
            return;
        }

        // We're dealing with a re-reschedule

        //From the same targetFile?
        if (csrDto.getTargetFileTimeStamp().equals(existingCallRetry.getTargetFiletimestamp())) {
            // We've already processed the call from this particular targetFile, this is probably a
            // resubmit, let's ignore it.
            return;
        }

        // From a different targetFile

        if ((subscription.getSubscriptionPack().retryCount() == 1) ||
                (existingCallRetry.getCallStage() == CallStage.RETRY_LAST)) {

            // This call should not be retried

            // Deactivate subscription for persistent invalid numbers
            // See https://github.com/motech-implementations/mim/issues/169
            if (existingCallRetry.getInvalidNumberCount() == subscription.getSubscriptionPack().retryCount()) {
                subscription.setStatus(SubscriptionStatus.DEACTIVATED);
                subscription.setDeactivationReason(DeactivationReason.INVALID_NUMBER);
                subscriptionDataService.update(subscription);
            }

            callRetryDataService.delete(existingCallRetry);

            // Does subscription need to be marked complete, even if we failed to send the last message?
            completeSubscriptionIfNeeded(subscription, csrDto.getContentFileName());
<commit_msg>One more NPE<commit_after>package org.motechproject.nms.kilkari.service.impl;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.motechproject.alerts.contract.AlertService;
import org.motechproject.alerts.domain.AlertStatus;
import org.motechproject.alerts.domain.AlertType;
import org.motechproject.event.MotechEvent;
import org.motechproject.event.listener.annotations.MotechListener;
import org.motechproject.metrics.service.Timer;
import org.motechproject.nms.kilkari.domain.CallRetry;
import org.motechproject.nms.kilkari.domain.CallStage;
import org.motechproject.nms.kilkari.domain.DeactivationReason;
import org.motechproject.nms.kilkari.domain.Subscription;
import org.motechproject.nms.kilkari.domain.SubscriptionOrigin;
import org.motechproject.nms.kilkari.domain.SubscriptionStatus;
import org.motechproject.nms.kilkari.dto.CallSummaryRecordDto;
import org.motechproject.nms.kilkari.exception.InvalidCallRecordDataException;
import org.motechproject.nms.kilkari.exception.NoSuchSubscriptionException;
import org.motechproject.nms.kilkari.repository.CallRetryDataService;
import org.motechproject.nms.kilkari.repository.SubscriptionDataService;
import org.motechproject.nms.kilkari.service.CsrService;
import org.motechproject.nms.kilkari.service.CsrVerifierService;
import org.motechproject.nms.props.domain.FinalCallStatus;
import org.motechproject.nms.props.domain.StatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static java.lang.Math.min;


@Service("csrService")
public class CsrServiceImpl implements CsrService {

    private static final String NMS_IMI_KK_PROCESS_CSR = "nms.imi.kk.process_csr";
    private static final int MAX_CHAR_ALERT = 4900;

    private static final Logger LOGGER = LoggerFactory.getLogger(CsrServiceImpl.class);

    private SubscriptionDataService subscriptionDataService;
    private CallRetryDataService callRetryDataService;
    private AlertService alertService;
    private CsrVerifierService csrVerifierService;

    @Autowired
    public CsrServiceImpl(SubscriptionDataService subscriptionDataService, CallRetryDataService callRetryDataService,
                          AlertService alertService, CsrVerifierService csrVerifierService) {
        this.subscriptionDataService = subscriptionDataService;
        this.callRetryDataService = callRetryDataService;
        this.alertService = alertService;
        this.csrVerifierService = csrVerifierService;
    }


    private void completeSubscriptionIfNeeded(Subscription subscription, String contentFileName) {

        resetWelcomeFlagInSubscription(subscription);

        if (!subscription.isLastPackMessage(contentFileName)) {
            // This subscription has not completed, do nothing
            return;
        }

        // Mark the subscription completed
        subscription.setStatus(SubscriptionStatus.COMPLETED);
        subscriptionDataService.update(subscription);

    }


    private void deleteCallRetryRecordIfNeeded(CallRetry callRetry) {
        if (callRetry == null) {
            return;
        }
        callRetryDataService.delete(callRetry);
    }


    private void deactivateSubscription(Subscription subscription, CallRetry callRetry) {

        if (subscription.getOrigin() == SubscriptionOrigin.IVR) {
            String error = String.format("Subscription %s was rejected (DND) but its origin is IVR, not MCTS!",
                    subscription.getSubscriptionId());
            LOGGER.error(error);
            alertService.create(subscription.getSubscriptionId(), "subscription", error, AlertType.CRITICAL,
                    AlertStatus.NEW, 0, null);
            return;
        }

        deleteCallRetryRecordIfNeeded(callRetry);

        //Deactivate the subscription
        subscription.setStatus(SubscriptionStatus.DEACTIVATED);
        subscription.setDeactivationReason(DeactivationReason.DO_NOT_DISTURB);
        subscriptionDataService.update(subscription);
    }


     private void doReschedule(Subscription subscription, CallRetry existingCallRetry, CallSummaryRecordDto csrDto) {

        boolean invalidNr = StatusCode.fromInt(csrDto.getStatusCode()).equals(StatusCode.OBD_FAILED_INVALIDNUMBER);

        if (existingCallRetry == null) {
            // We've never retried this call, let's do it
            callRetryDataService.create(new CallRetry(
                    subscription.getSubscriptionId(),
                    subscription.getSubscriber().getCallingNumber(),
                    CallStage.RETRY_1,
                    csrDto.getContentFileName(),
                    csrDto.getWeekId(),
                    csrDto.getLanguageCode(),
                    csrDto.getCircleName(),
                    subscription.getOrigin(),
                    csrDto.getTargetFileTimeStamp(),
                    invalidNr ? 1 : 0)
            );
            return;
        }

        // We're dealing with a re-reschedule

        //From the same targetFile?
        if (csrDto.getTargetFileTimeStamp().equals(existingCallRetry.getTargetFiletimestamp())) {
            // We've already processed the call from this particular targetFile, this is probably a
            // resubmit, let's ignore it.
            return;
        }

        // From a different targetFile

        if ((subscription.getSubscriptionPack().retryCount() == 1) ||
                (existingCallRetry.getCallStage() == CallStage.RETRY_LAST)) {

            // This call should not be retried

            // Deactivate subscription for persistent invalid numbers
            // See https://github.com/motech-implementations/mim/issues/169
            if (existingCallRetry.getInvalidNumberCount() != null &&
                    existingCallRetry.getInvalidNumberCount() == subscription.getSubscriptionPack().retryCount()) {
                subscription.setStatus(SubscriptionStatus.DEACTIVATED);
                subscription.setDeactivationReason(DeactivationReason.INVALID_NUMBER);
                subscriptionDataService.update(subscription);
            }

            callRetryDataService.delete(existingCallRetry);

            // Does subscription need to be marked complete, even if we failed to send the last message?
            completeSubscriptionIfNeeded(subscription, csrDto.getContentFileName());

{'fraction_non_alphanumeric': 0.07995337995337995, 'fraction_numerical': 0.0021756021756021756, 'mean_word_length': 3.239459815546772, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2773743', 'n_tokens_mistral': 3427, 'n_tokens_neox': 3428, 'n_words': 602}