Log interaktif "Langsung": visualisasi log dalam Voximplant Kit



Kami terus memperbarui Kit Voximplant dengan JointJS . Dan kami senang mengumumkan penampilan log panggilan langsung. Betapa hidup dan berbahaya bagi pengguna biasa, baca di bawah potongan.



Sebelumnya, hanya rekaman panggilan yang tersedia bagi pengguna untuk menganalisis panggilan dalam Voximplant Kit. Selain audio, kami ingin membuat tidak hanya log teks, tetapi alat yang lebih nyaman untuk melihat detail panggilan dan menganalisis kesalahan. Dan karena kita berhadapan dengan produk kode rendah / tanpa kode, ide untuk memvisualisasikan log muncul.



Apa garamnya? / Konsep baru



Semua hasil panggilan sekarang tersedia sebagai rangkaian blok yang dilewati, dianimasikan mirip dengan mode demo. Hanya jalur yang disorot di sini sebelumnya: Anda dapat melacak secara visual bagaimana klien bergerak melalui skrip.





Untuk melakukan ini, buka tab riwayat panggilan keluar atau masuk atau dalam laporan kampanye yang dipilih dan klik "Lihat log". Dan kemudian editor akan menunjukkan apa yang terjadi dalam panggilan tersebut langkah demi langkah.





Kontrol



Mengontrol berhenti / mulai (1) berhenti / melanjutkan pemutaran, dan kembali / berikutnya (2) memindahkan pengguna secara searah ke awal blok sebelumnya / berikutnya. Anda juga dapat mengeklik timeline untuk memulai pemutaran dari titik waktu tertentu, seperti halnya memainkan lagu.



Jika skrip menyertakan rekaman percakapan, maka skrip itu akan diputar secara paralel dengan bergerak melintasi blok. Rekaman audio pada timeline disorot dalam warna terpisah (3).





Untuk kenyamanan pengguna, daftar blok yang dilewati dengan stempel waktu ("Log") juga tersedia:





Spoiler:

Di tab "Log", kami berencana untuk menampilkan detail blok. Mereka akan membantu kami memahami mengapa blok keluar pada port tertentu dan apakah ada kesalahan. Misalnya, untuk unit pengenalan, kita akan melihat hasil dan kesalahan pengenalan.

Blok kompleks seperti DialogFlowConnector, IVR, ASR, dll. Akan menjadi yang paling menarik di sini.




Variabel



Variabel yang diubah ditampilkan di sebelah kiri sebagai pemberitahuan yang muncul menurut kronologi. Artinya, jika kita pindah ke blok "Ubah data", variabel yang berubah di sana akan muncul. Mari kita pergi jauh dari itu (lebih dari 4d di timeline) - variabel akan menghilang sampai kita menemukan diri kita lagi dalam interval di mana perubahan terjadi:







Retas seumur hidup



Log panggilan disimpan dalam bentuk aslinya bahkan setelah skrip diubah atau dihapus. Dan ini berarti bahwa tidak akan ada masalah dengan mengembalikan logika skrip: jika perlu, Anda selalu dapat membuka log.



Anda dapat merasakan log sendiri di Kit Voximplant .



Jadi, apa yang ada di dalamnya?



Mari kita lihat bagaimana log dinamis diterapkan dalam kode. Katakanlah segera, dari Joint JS kami hanya mengambil animasi dan alokasi blok, seperti dalam mode demo. Selebihnya (apa yang bisa dilakukan atas dasar ini) adalah imajinasi kita.



Omong-omong, untuk mempelajari lebih lanjut tentang mode demo internal, baca artikel kami sebelumnya .


Kami mendapat titik waktu



Ketika Anda pergi untuk melihat log, server mengirim data, yang berisi daftar semua blok yang dilewati, tanggal dan waktu masuk ke dalamnya, dan daftar variabel yang berubah selama panggilan. Dengan kata lain, di depan kita mendapatkan dua array objek: log_path dan log_variables.



Juga, respons server berisi tautan ke audio dan durasinya, jika percakapan direkam.





Berdasarkan waktu masuk ke dalam blok, kami menghitung titik waktu dalam milidetik dan menuliskannya untuk setiap blok dan variabel. Titik referensi (0 ms) adalah waktu untuk memasuki blok pertama. Jadi, jika kita memasuki blok kedua 5 detik setelah dimulainya panggilan, maka titik waktu dari blok kedua = 5000 ms. Menggunakan titik waktu ini, kami menghitung total durasi log.



Memperbarui garis waktu



Setelah menekan tombol play, timeline mulai memperbarui setiap 10 ms. Selama setiap pembaruan, kami memeriksa apakah waktu saat ini bertepatan dengan salah satu titik waktu:



const found = this.timePoints.find((item) => item === this.playTime);


Jika ada kecocokan, kami akan mencari semua blok yang timepoint = waktu sekarang + 600 ms (waktu di mana animasi gerakan antar blok terjadi).



Kode metode UpdatePlayTime ():



updatePlayTime(): void {
    const interval = 10;
    let expected = Date.now() + interval;

    const tick = () => {
        const drift = Date.now() - expected;
        const found = this.timePoints.find((item) => item === this.playTime);
        this.$emit('update', {
            time: this.playTime,
            found: found !== undefined
        });

        if (this.playTime >= this.duration) {
            this.isPlay = false;
            this.playTime = this.duration;
            clearTimeout(this.playInterval);
            this.$emit('end', this.playTime);
            return;
        }

        expected += interval;

        this.playTime += 0.01;
        this.playTime = +this.playTime.toFixed(2);

        this.updateProgress();

        this.playInterval = window.setTimeout(tick, Math.max(0, interval - drift));
    };

    this.playInterval = window.setTimeout(tick, 10);
}


Juga, setiap 90 ms kami memeriksa kebetulan untuk waktu saat ini dan titik waktu untuk variabel yang diubah + 4000 ms (waktu selama pemberitahuan tentang perubahan variabel hang).



Memilih blok



Setelah semua kecocokan ditemukan, tambahkan blok ke antrian untuk memilih dan memulai animasi tautan.



Jika ada beberapa blok dengan timepoint = waktu sekarang + 600 ms, transisi dianimasikan hanya ke yang terakhir:



if (i === blocks.length - 1) {
    await this.selectBlock(blocks[i], 600, true, true);
}


Ini diperlukan karena ada blok yang diproses sangat cepat. Misalnya, "Memeriksa data", "Mengubah data", dll. - beberapa blok dapat dilalui dalam 1 detik. Jika Anda menganimasinya secara berurutan, akan ada kelambatan dari timeline.



Kode metode OnUpdateTimeline:



async onUpdateTimeline({
    time,
    found
}) {
    this.logTimer = time * 1000; //   
    this.checkHistoryNotify();

    if (!found) return;

    //        + 600
    const blocks = this.callHistory.log_path.filter((item) => {
        return item.timepoint >= this.logTimer && item.timepoint < this.logTimer + 600;
    });

    if (blocks.length) {
        this.editor.unselectAll();

        for (let i = 0; i < blocks.length; i++) {

            if (i === blocks.length - 1) {
                await this.selectBlock(blocks[i], 600, true, true);

                const cell = this.editor.getCellById(blocks[i].idTarget);
                this.editor.select(cell);
            } else {
                await this.selectBlock(blocks[i], 0, false, true);
            }
        }
    }
}


Jadi dalam lingkaran, ada kebetulan - kita memilih blok, jika blok sudah ada dalam antrian - kita tidak melakukan apa-apa.



Metode selectBlock () membantu kami dalam hal ini:



async selectBlock(voxHistory, timeout = 700, animate = true, animateLink = true) {
    const inQueue = this.selectQueue.find((item) => item[0].targetId === voxHistory.idTarget);

    if (!inQueue) this.selectQueue.push(arguments);

    return this.exeQueue();
}


Putar balik



Saat memundurkan, prinsipnya sama: ketika garis waktu dipindahkan, kami mendapat waktu untuk mundur dan menghapus dari blok yang dipilih dengan titik waktu lebih lama dari waktu saat ini:



const forSelect = this.callHistory.log_path.filter((item) => {
        const time = accurate ? item.accurateTime : item.timepoint;
        return time <= this.logTimer;
    });


Kami membuat transisi animasi ke yang terakhir.



Kode metode OnRewind ():



async onRewind({
    time,
    accurate
}, animation = true) {
    this.editor.unselectAll();
    this.stopLinksAnimation();
    this.checkHistoryNotify(true);

    const forSelect = this.callHistory.log_path.filter((item) => {
        const time = accurate ? item.accurateTime : item.timepoint;
        return time <= this.logTimer;
    });

    for (let i = 0; i < forSelect.length; i++) {
        if (i === forSelect.length - 1) {
            await this.selectBlock(forSelect[i], 600, animation, false);
            const cell = this.editor.getCellById(forSelect[i].idTarget);
            this.editor.select(cell);
        } else {
            await this.selectBlock(forSelect[i], 0, false, false);
        }
    }

    if (this.isPlay) this.restartAnimateLink();

    this.onEndTimeline();
}


Putar audio



Menghidupkan / mematikan audio bahkan lebih mudah. Jika timeline bertepatan dengan dimulainya rekaman, itu mulai diputar dan kemudian waktu disinkronkan. Metode updatePlayer () bertanggung jawab untuk ini:



updatePlayer() {
    if (this.playTime >= this.recordStart && this.player.paused && !this.isEndAudio) {
        this.player.play();
        this.player.currentTime = this.playTime - this.recordStart;
    } else if (this.playTime < this.recordStart && !this.player.paused) {
        this.player.pause();
    }
}


Itu saja! Jadi, berdasarkan metode JS Bersama dan kreativitas pengembang kami, log langsung muncul. Pastikan untuk mengujinya sendiri jika Anda belum :)



Hebat jika Anda menikmati serangkaian artikel kami di Keith Updates. Kami akan terus berbagi dengan Anda yang paling segar dan paling menarik!



All Articles