Esempio n. 1
0
def update_stats():
    while True:
        log.debug('Start sending stats')
        while not stop_event.is_set():
            # update stats periodically
            data['pending'] = sf.pending_calls()
            data['answered'] = sf.answered_calls()
            data['abandoned'] = sf.abandoned_calls()
            data['rejected'] = sf.rejected_calls()
            data['total'] = sf.total_calls()
            socketio.emit(
                'stats', {
                    'pending': data['pending'],
                    'answered': data['answered'],
                    'abandoned': data['abandoned'],
                    'rejected': data['rejected'],
                    'total': data['total']
                })
            if 0 == data['pending']:
                log.info('No more pending calls')
                # force exit loop
                stop_event.set()
            socketio.sleep(1)
        log.debug('Received stop event')
        socketio.emit('stopped')  # will emulate a click on Stop button
        # wait until the stop event is cleared
        while stop_event.is_set():
            socketio.sleep(1)
Esempio n. 2
0
def input_data():
    global data
    global destno_list
    global cid_list
    global already_logged
    global parser
    global currentProviderId
    global start_timer
    global stopped
    global update_stats_thread

    errors = ''
    if 'data' in session:
        data = session['data']

    if not already_logged:
        return redirect(url_for('login'))

    if 'GET' == request.method:
        if request.values.get('provider'):
            currentProviderId = request.values.get('provider')
            log.info('Current provider ID %s', currentProviderId)
            acc = parser.getSipAccount(currentProviderId)
            if acc is not None:
                data['currentProviderId'] = currentProviderId
                data['callDuration'] = acc['callDurationSec']
                data['callerIdsFile'] = acc['callerIdsFile']
                data['destPhoneNumFile'] = acc['destNumsFile']
                data['phoneNumberPrefix'] = acc['phoneNumberPrefix']
                data['cdr_filename'] = ''
                ui_load_cid_dest_num()
            else:
                data['callerIdsFile'] = ''
                data['callerIds'] = ''
                data['destPhoneNumFile'] = ''
                data['destPhoneNum'] = ''
                log.critical('Invalid account for ID %s', currentProviderId)
            data['accountIds'] = parser.getSipAccountIds()
            # make sure that the account is registered
            currentProviderId = ''
            # clear stats
            sf.clear_stats()
            filepath = os.path.join(app.config['DOWNLOAD_FOLDER'],
                                    data['destPhoneNumFile'])
            dest_nums = jp.JsonParser.loadList(filepath)
            sf.set_pending_calls(len(dest_nums))
            data['pending'] = sf.pending_calls()
            data['answered'] = sf.answered_calls()
            data['abandoned'] = sf.abandoned_calls()
            data['rejected'] = sf.rejected_calls()
            data['total'] = sf.total_calls()
            socketio.emit(
                'stats', {
                    'pending': data['pending'],
                    'answered': data['answered'],
                    'abandoned': data['abandoned'],
                    'rejected': data['rejected'],
                    'total': data['total']
                })
        elif not currentProviderId:
            log.warning(
                'Provider cannot be found in request and current provider ID is empty'
            )
            data['cdr_filename'] = ''
            # use the first account as the default one
            accIds = parser.getSipAccountIds()
            data['accountIds'] = accIds
            if accIds:
                currentProviderId = accIds[0]
                log.info('Current provider ID %s', currentProviderId)
                acc = parser.getSipAccount(currentProviderId)
                if acc is not None:
                    data['currentProviderId'] = currentProviderId
                    data['callDuration'] = acc['callDurationSec']
                    data['callerIdsFile'] = acc['callerIdsFile']
                    data['destPhoneNumFile'] = acc['destNumsFile']
                    data['phoneNumberPrefix'] = acc['phoneNumberPrefix']
                    ui_load_cid_dest_num()
                    currentProviderId = ''
                    sf.clear_stats()
                    filepath = os.path.join(app.config['DOWNLOAD_FOLDER'],
                                            data['destPhoneNumFile'])
                    dest_nums = jp.JsonParser.loadList(filepath)
                    sf.set_pending_calls(len(dest_nums))
                    data['pending'] = sf.pending_calls()
                    data['answered'] = sf.answered_calls()
                    data['abandoned'] = sf.abandoned_calls()
                    data['rejected'] = sf.rejected_calls()
                    data['total'] = sf.total_calls()
                else:
                    log.critical('Invalid account for ID %s',
                                 currentProviderId)
            else:
                data['callerIdsFile'] = ''
                data['callerIds'] = ''
                data['destPhoneNumFile'] = ''
                data['destPhoneNum'] = ''
                log.warning('No provider found')
        else:
            log.info('Updating only stats')
            data['pending'] = sf.pending_calls()
            data['answered'] = sf.answered_calls()
            data['abandoned'] = sf.abandoned_calls()
            data['rejected'] = sf.rejected_calls()
            data['total'] = sf.total_calls()

        data['readonly'] = 'false'
        return render_template('main.html', errors=errors, data=data)
    elif 'POST' == request.method:
        # stop button pressed
        if 'Stop' == request.form['button']:
            on_stop()
            data['readonly'] = 'false'
            session['data'] = data
            errors = sf.error()
            return render_template('main.html',
                                   errors=errors,
                                   data=data,
                                   started=False)

        # clear button pressed
        if 'Clear' == request.form['button']:
            data['callerIdsFile'] = ''
            data['callerIds'] = ''
            cid_list = []
            data['destPhoneNumFile'] = ''
            data['destPhoneNum'] = ''
            destno_list = []
            session['data'] = data
            if not parser.clearCidDestNums(data['currentProviderId']):
                log.critical('Cannot clear dest nums for provider %s',
                             data['currentProviderId'])
            return render_template('main.html',
                                   errors=errors,
                                   data=data,
                                   started=False)

        # new provider button pressed
        if 'New' == request.form['button']:
            return render_template('new_provider.html')

        # remove provider button pressed
        if 'Remove' == request.form['button']:
            accId = request.form['provider']
            rc = sf.remove_account(accId)
            if not rc:
                errors = sf.error()
                return render_template('main.html', errors=errors, data=data)
            rc = parser.removeSipAccount(accId)
            if not rc:
                errors = 'Cannot remove account'
                return render_template('main.html', errors=errors, data=data)
            data['accountIds'] = parser.getSipAccountIds()
            # select the first account in the list as the default one
            if 0 < len(data['accountIds']):
                currentProviderId = data['accountIds'][0]
                log.info('Current provider ID %s', currentProviderId)
                acc = parser.getSipAccount(currentProviderId)
                if acc is not None:
                    data['currentProviderId'] = currentProviderId
                    data['callDuration'] = acc['callDurationSec']
                    data['callerIdsFile'] = acc['callerIdsFile']
                    data['destPhoneNumFile'] = acc['destNumsFile']
                    data['phoneNumberPrefix'] = acc['phoneNumberPrefix']
                    ui_load_cid_dest_num()
                    currentProviderId = ''
                    sf.clear_stats()
                    filepath = os.path.join(app.config['DOWNLOAD_FOLDER'],
                                            data['destPhoneNumFile'])
                    dest_nums = jp.JsonParser.loadList(filepath)
                    sf.set_pending_calls(len(dest_nums))
                    data['pending'] = sf.pending_calls()
                    data['answered'] = sf.answered_calls()
                    data['abandoned'] = sf.abandoned_calls()
                    data['rejected'] = sf.rejected_calls()
                    data['total'] = sf.total_calls()
                else:
                    log.warning('Cannot get SIP account for ID %s',
                                currentProviderId)
                    data['callDuration'] = ''
                    data['destPhoneNumFile'] = ''
                    data['phoneNumberPrefix'] = ''
            else:
                log.debug('Empty account list')
                data['callDuration'] = ''
                data['destPhoneNumFile'] = ''
                data['phoneNumberPrefix'] = ''
            return render_template('main.html', data=data, started=False)

        # add button pressed from add new provider page
        if 'Add' == request.form['button']:
            # validate form
            if not request.form['sipServer']:
                errors = 'SIP server address cannot be empty'
                return render_template('new_provider.html', errors=errors)
            try:
                if request.form['port']:
                    sipPort = int(request.form['port'])
                    if 0 > sipPort or 65535 < sipPort:
                        raise ValueError('Invalid port number')
                else:
                    raise ValueError('Port number is empty')
            except:
                errors = 'Invalid port number'
                return render_template('new_provider.html', errors=errors)
            if not request.form['userName']:
                errors = 'Username cannot be empty'
                return render_template('new_provider.html', errors=errors)
            if not request.form['password']:
                errors = 'Password cannot be empty'
                return render_template('new_provider.html', errors=errors)
            try:
                if request.form['concurrentCalls']:
                    concurrentCalls = int(request.form['concurrentCalls'])
                    if 1 > concurrentCalls:
                        raise ValueError('Invalid concurrent calls number')
                else:
                    raise ValueError('Concurrent calls is empty')
            except:
                errors = 'Invalid concurrent calls number'
                return render_template('new_provider.html', errors=errors)
            # add account to config file
            rc = parser.addSipAccount(request.form['sipServer'],
                                      request.form['port'],
                                      request.form['transportType'],
                                      request.form['concurrentCalls'],
                                      request.form['userName'],
                                      request.form['password'])
            if not rc:
                errors = 'Cannot add account'
                return render_template('main.html', errors=errors, data=data)
            data['accountIds'] = parser.getSipAccountIds()
            return render_template('main.html', data=data, started=False)

        # cancel button pressed from add new provider page
        if 'Cancel' == request.form['button']:
            return render_template('main.html', data=data, started=False)

        # get provider
        if 'provider' not in request.form:
            errors = 'You must add at least one provider'
            session['data'] = data
            return render_template('main.html', errors=errors, data=data)
        if '' != currentProviderId and currentProviderId != request.form[
                'provider'] or '' == currentProviderId:
            registerAccount = True
            currentProviderId = request.form['provider']
            log.debug('Need to register provider %s', currentProviderId)
            acc = parser.getSipAccount(currentProviderId)
            if acc is None:
                errors = 'You must add at least one provider'
                session['data'] = data
                return render_template('main.html', errors=errors, data=data)
            data['currentProviderId'] = currentProviderId
            data['concurrentCalls'] = int(acc['concurrentCalls'])
            data['callerIdsFile'] = acc['callerIdsFile']
            data['destPhoneNumFile'] = acc['destNumsFile']
        else:
            log.debug('Do not register provider %s', currentProviderId)
            registerAccount = False
            # need to get the number of concurrent calls
            acc = parser.getSipAccount(currentProviderId)
            if acc is None:
                errors = 'You must add at least one provider'
                session['data'] = data
                return render_template('main.html', errors=errors, data=data)
            data['currentProviderId'] = currentProviderId
            data['concurrentCalls'] = int(acc['concurrentCalls'])
            data['callerIdsFile'] = acc['callerIdsFile']
            data['destPhoneNumFile'] = acc['destNumsFile']

        # get call duration
        try:
            callDuration = int(request.form['callDuration'])
            # negative call durations are allowed to disable the timer
        except:
            errors = 'Invalid call duration'
            session['data'] = data
            return render_template('main.html', errors=errors, data=data)
        data['callDuration'] = callDuration

        # get caller IDs
        cid_list = []
        if not data['callerIdsFile']:
            log.debug('No CID file found')
            callerIds = request.form['callerIds']
            if callerIds:
                tok = callerIds.split(',')
                for t in tok:
                    cid_list.append(str(t.strip()))
                if 0 < len(cid_list):
                    filename = secure_filename(currentProviderId + '_cid.txt')
                    filepath = os.path.join(app.config['DOWNLOAD_FOLDER'],
                                            filename)
                    jp.JsonParser.saveList(cid_list, filepath)
                    data['callerIdsFile'] = filename
        # use either the edit box or the provided file
        if 0 == len(cid_list):
            file = request.files['callerIdsFile']
            if file:
                filename = secure_filename(currentProviderId + '_cid.txt')
                filepath = os.path.join(app.config['DOWNLOAD_FOLDER'],
                                        filename)
                try:
                    file.save(filepath)
                    data['callerIdsFile'] = filename
                except:
                    errors = 'Cannot save ' + filepath
                    session['data'] = data
                    return render_template('main.html',
                                           errors=errors,
                                           data=data)
                try:
                    cid_list = jp.JsonParser.loadList(filepath)
                    if 0 == len(cid_list):
                        raise ValueError('Empty CID list')
                except:
                    errors = 'Cannot open ' + filepath
                    session['data'] = data
                    return render_template('main.html',
                                           errors=errors,
                                           data=data)
            elif data['callerIdsFile']:
                filepath = os.path.join(app.config['DOWNLOAD_FOLDER'],
                                        data['callerIdsFile'])
                cid_list = jp.JsonParser.loadList(filepath)
            else:
                log.warning('No CLI file is used')
        log.info('Using %d caller IDs', len(cid_list))
        data['callerIds'] = ui_list(cid_list, ', ')

        # get destination phone numbers
        destno_list = []
        if not data['destPhoneNumFile']:
            log.debug('No dest. phone num. file')
            destPhoneNum = request.form['destPhoneNum']
            if destPhoneNum:
                tok = destPhoneNum.split()
                for t in tok:
                    destno_list.append(t.strip())
                if 0 < len(destno_list):
                    filename = secure_filename(currentProviderId +
                                               '_destno.txt')
                    filepath = os.path.join(app.config['DOWNLOAD_FOLDER'],
                                            filename)
                    jp.JsonParser.saveList(destno_list, filepath)
                    data['destPhoneNumFile'] = filename
        if 0 == len(destno_list):
            file = request.files['destPhoneNumFile']
            if file:
                filename = secure_filename(currentProviderId + '_destno.txt')
                filepath = os.path.join(app.config['DOWNLOAD_FOLDER'],
                                        filename)
                try:
                    file.save(filepath)
                    data['destPhoneNumFile'] = filename
                except:
                    errors = 'Cannot save ' + filepath
                    session['data'] = data
                    return render_template('main.html',
                                           errors=errors,
                                           data=data)
                try:
                    destno_list = jp.JsonParser.loadList(filepath)
                    if 0 == len(destno_list):
                        raise ValueError(
                            'Empty destination phone numbers list')
                except:
                    errors = 'Cannot open' + filepath
                    session['data'] = data
                    return render_template('main.html',
                                           errors=errors,
                                           data=data)
            elif data['destPhoneNumFile']:
                filepath = os.path.join(app.config['DOWNLOAD_FOLDER'],
                                        data['destPhoneNumFile'])
                destno_list = jp.JsonParser.loadList(filepath)
            else:
                log.warning('No dest. num. file is used')
        if 0 == len(destno_list):
            errors = 'No destination phone numbers'
            session['data'] = data
            return render_template('main.html', errors=errors, data=data)
        log.info('Using %d dest. nums.', len(destno_list))
        data['destPhoneNum'] = ui_list(destno_list, '\n')

        # get phone number prefix
        data['phoneNumberPrefix'] = request.form['phoneNumberPrefix']

        # save account params
        if not parser.addSipAccountParams(
                currentProviderId, data['callDuration'], data['callerIdsFile'],
                data['destPhoneNumFile'], data['phoneNumberPrefix']):
            log.critical('Cannot save account parameters for account ID %s',
                         currentProviderId)

        if registerAccount:
            log.info('Registering account ' + currentProviderId)
            sf.thread_register('input_data')
            rc = sf.register_account(acc['address'], acc['port'],
                                     acc['transport'], acc['username'],
                                     acc['password'])
            if not rc:
                errors = 'Cannot register account'
                err = sf.error()
                if err:
                    errors += ' (' + err + ')'
                currentProviderId = ''  # force provider registration
                data['accountIds'] = parser.getSipAccountIds()
                session['data'] = data
                return render_template('main.html', errors=errors, data=data)
            sf.set_null_sound_devices()
        else:
            log.info('Account already registered')

        sf.clear_stats()
        data['pending'] = sf.pending_calls()
        data['answered'] = sf.answered_calls()
        data['abandoned'] = sf.abandoned_calls()
        data['rejected'] = sf.rejected_calls()
        data['total'] = sf.total_calls()
        socketio.emit(
            'stats', {
                'pending': data['pending'],
                'answered': data['answered'],
                'abandoned': data['abandoned'],
                'rejected': data['rejected'],
                'total': data['total']
            })

        stopped = False

        # start thread for generating calls
        stop_event.clear()
        start_timer = Timer(0, on_start, [
            data['concurrentCalls'], data['callDuration'],
            data['phoneNumberPrefix']
        ])
        log.info(
            "********************* Start generating calls *********************"
        )
        start_timer.start()

        data['readonly'] = 'true'
        data['cdr_filename'] = ''
        session['data'] = data
        sf.error()  # reset previous errors
        return render_template('main.html', data=data, started=True)
    else:
        log.debug('Method %s', request.method)
Esempio n. 3
0
def on_stop():
    global data
    global stopped
    global start_timer
    global cur_save_path

    log.info('stop calls')
    start_timer.cancel()
    sf.thread_register('on_stop')
    sf.hangup_all()
    stopped = True
    stop_event.set()
    data['pending'] = sf.pending_calls()
    data['answered'] = sf.answered_calls()
    data['abandoned'] = sf.abandoned_calls()
    data['rejected'] = sf.rejected_calls()
    data['total'] = sf.total_calls()
    socketio.emit(
        'stats', {
            'pending': data['pending'],
            'answered': data['answered'],
            'abandoned': data['abandoned'],
            'rejected': data['rejected'],
            'total': data['total']
        })
    # save CDR
    csv_filename = 'call_details_record.csv'
    csv_filename_path = os.path.join(cur_save_path, csv_filename)
    cur_folder = os.path.basename(os.path.normpath(cur_save_path))
    if sf.save_cdr(csv_filename_path):
        data['cdr_filename'] = str(
            os.path.join('download', cur_folder, csv_filename))
    else:
        data['cdr_filename'] = ''
    session['data'] = data
    # merge all transcript files into a single file
    time.sleep(1)  # allow to save all wave files
    transcript_filename = os.path.join(cur_save_path, 'transcripts.txt')
    with open(transcript_filename, 'w', os.O_NONBLOCK) as transcript_file:
        # test if transcript is available
        from google.cloud import speech
        maxTrials = 40  # 2 min
        try:
            speech.SpeechClient()
        except:
            maxTrials = 0  # no wait is needed

        wave_path = os.path.join(cur_save_path, '*.wav')
        for wave_filename in glob.glob(wave_path):
            log.debug('Found wave file %s', wave_filename)
            temp_transcript_filename = wave_filename + '.txt'
            if os.path.isfile(temp_transcript_filename):
                with open(temp_transcript_filename, 'r',
                          os.O_NONBLOCK) as temp_transcript_file:
                    for line in temp_transcript_file:
                        transcript_file.write(line)
                os.remove(temp_transcript_filename)
            else:
                log.debug('Wait for transcript to finish')
                numTrials = 0
                while not os.path.isfile(temp_transcript_filename):
                    if numTrials < maxTrials:
                        time.sleep(3)
                        numTrials += 1
                    else:
                        log.warning('Number of trials exceeded for %s',
                                    wave_filename)
                        break
                if numTrials < maxTrials:
                    log.debug('Transcript file is ready to be processed')
                    with open(temp_transcript_filename, 'r',
                              os.O_NONBLOCK) as temp_transcript_file:
                        for line in temp_transcript_file:
                            transcript_file.write(line)
                    os.remove(temp_transcript_filename)
            # convert wave file to amr
            convert_wav2amr(wave_filename)

    data['transcripts_filename'] = str(
        os.path.join('download', cur_folder, 'transcripts.txt'))
    log.debug('Finished to merge all transcript files')