Interaktive "Live" -Protokolle: Visualisierung der Protokolle im Voximplant Kit



Wir weiterhin Voximplant Kit mit aktualisieren JointJS . Und wir freuen uns, das Erscheinen von "Live" -Protokollen von Anrufen bekannt zu geben. Wie viel sie leben und ob sie für normale Benutzer gefährlich sind, lesen Sie unter dem Schnitt.



Bisher standen Benutzern nur Anrufaufzeichnungen zur Analyse von Anrufen im Voximplant Kit zur Verfügung. Zusätzlich zu Audio wollten wir nicht nur ein Textprotokoll erstellen, sondern auch ein bequemeres Tool zum Anzeigen von Anrufdetails und zum Analysieren von Fehlern. Und da es sich um ein Produkt mit niedrigem Code / ohne Code handelt, kam die Idee auf, Protokolle zu visualisieren.



Was ist das Salz? / Neues Konzept



Alle Anrufergebnisse sind jetzt als Kette abgeschlossener Blöcke verfügbar, die analog zum Demomodus animiert sind. Nur der Pfad hier wird im Voraus hervorgehoben: Sie können visuell verfolgen, wie sich der Client gemäß dem Skript bewegt hat.





Wechseln Sie dazu zur Registerkarte des Verlaufs ausgehender oder eingehender Anrufe oder im Bericht der ausgewählten Kampagne und klicken Sie auf "Protokoll anzeigen". Und dann zeigt der Editor Schritt für Schritt, was im Aufruf passiert ist.





Steuerung



Die Stopp / Start-Steuerung (1) stoppt / setzt die Wiedergabe fort und zurück \ weiter (2) bewegt den Benutzer punktweise zum Anfang des vorherigen / nächsten Blocks. Sie können auch einfach auf die Timeline klicken, um die Wiedergabe ab einem bestimmten Zeitpunkt zu starten, wie beim Abspielen eines Songs.



Wenn das Szenario eine Aufzeichnung eines Gesprächs enthält, wird es parallel zum Bewegen durch die Blöcke abgespielt. Die Timeline-Audioaufnahme wird in einer separaten Farbe hervorgehoben (3).





Für den Benutzer ist auch eine Liste der übergebenen Blöcke mit Zeitstempeln verfügbar ("Protokoll"):





Spoiler:

Auf der Registerkarte "Log" wollen wir die Details der Blöcke anzeigen. Sie helfen uns zu verstehen, warum der Block an einem bestimmten Port beendet wurde und ob Fehler aufgetreten sind. Für einen Erkennungsblock werden beispielsweise Ergebnisse und Erkennungsfehler angezeigt.

Komplexe Blöcke wie DialogFlowConnector, IVR, ASR usw. sind hier von größtem Interesse.




Variablen



Geänderte Variablen werden links in Form eines Benachrichtigungs-Popups angezeigt, das der Chronologie entspricht. Wenn wir also zum Block "Daten ändern" wechseln, werden die dort geänderten Variablen angezeigt. Gehen wir weit davon entfernt (mehr als 4 Sekunden auf der Zeitachse) - die Variablen verschwinden, bis wir uns wieder in dem Intervall befinden, in dem die Änderung stattgefunden hat:







Life Hack



Anrufprotokolle bleiben auch nach dem Ändern oder Löschen eines Skripts in ihrer ursprünglichen Form erhalten. Dies bedeutet, dass beim Wiederherstellen der Skriptlogik keine Probleme auftreten: Bei Bedarf können Sie sich jederzeit an das Protokoll wenden.



Sie können die Protokolle selbst auf dem Voximplant Kit fühlen .



Also, was ist drin?



Mal sehen, wie dynamische Protokolle im Code implementiert werden. Nehmen wir gleich an, wir haben von Joint JS nur Animation und Blockzuweisung übernommen, wie im Demo-Modus. Der Rest (was auf dieser Grundlage getan werden kann) ist unsere Fantasie.



Um mehr über die Interna des Demo-Modus zu erfahren, lesen Sie übrigens unseren vorherigen Artikel .


Wir bekommen Zeitpunkte



Wenn Sie das Protokoll anzeigen, sendet der Server Daten, die eine Liste aller übergebenen Blöcke, das Datum und die Uhrzeit der Eingabe sowie eine Liste der Variablen enthalten, die sich während des Aufrufs geändert haben. Mit anderen Worten, auf der Vorderseite erhalten wir zwei Arrays von Objekten: log_path und log_variables.



Außerdem enthält die Serverantwort einen Link zum Audio und seiner Dauer, wenn das Gespräch aufgezeichnet wurde.





Basierend auf den Blockeintrittszeiten berechnen wir Zeitpunkte in Millisekunden und zeichnen sie für jeden Block und jede Variable auf. Der Referenzpunkt (0 ms) ist die Zeit, um den ersten Block einzugeben. Wenn wir also 5 Sekunden nach Beginn des Aufrufs den zweiten Block betreten, ist der Zeitpunkt des zweiten Blocks = 5000 ms. Mit diesen Zeitpunkten berechnen wir die Gesamtdauer des Protokolls.



Aktualisieren der Timeline



Nach dem Drücken der Wiedergabetaste wird die Timeline alle 10 ms aktualisiert. Bei jedem Update prüfen wir, ob die aktuelle Zeit mit einem der Zeitpunkte übereinstimmt:



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


Wenn es eine Übereinstimmung gibt, suchen wir nach allen Blöcken, für die Zeitpunkt = aktuelle Zeit + 600 ms (die Zeit, während der die Animation der Bewegung zwischen den Blöcken stattfindet).



Methodencode von 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);
}


Außerdem überprüfen wir alle 90 ms die Übereinstimmungen für die aktuelle Zeit und die Zeitpunkte für die geänderten Variablen + 4000 ms (die Zeit, während der die Benachrichtigung über die Änderung der Variablen hängt).



Blöcke auswählen



Nachdem alle Übereinstimmungen gefunden wurden, fügen Sie der Warteschlange Blöcke zur Auswahl hinzu und starten Sie die Animation der Links.



Wenn es mehrere Blöcke mit Zeitpunkt = aktuelle Zeit + 600 ms gibt, wird der Übergang nur zum letzten animiert:



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


Dies ist notwendig, da es Blöcke gibt, die sehr schnell verarbeitet werden. Zum Beispiel "Datenüberprüfung", "Daten ändern" usw. - In 1 Sekunde können mehrere Blöcke durchlaufen werden. Wenn Sie sie nacheinander animieren, tritt eine Verzögerung von der Zeitachse auf.



OnUpdateTimeline-Methodencode:



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);
            }
        }
    }
}


Und so gibt es in einem Kreis einen Zufall - wir wählen Blöcke aus, wenn sich ein Block bereits in der Warteschlange befindet - wir tun nichts.



Die Methode selectBlock () hilft uns dabei:



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();
}


Zurückspulen



Beim Zurückspulen gilt das gleiche Prinzip: Wenn die Zeitachse verschoben wird, erhalten wir die Zeit, für die wir zurückspulen und aus den ausgewählten Blöcken entfernen müssen, wobei die Zeitpunkte länger als die aktuelle Zeit sind:



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


Wir machen einen animierten Übergang zum letzten.



OnRewind () -Methodencode:



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();
}


Audio abspielen



Das Ein- und Ausschalten von Audio ist noch einfacher. Wenn die Timeline mit dem Beginn der Aufnahme übereinstimmt, beginnt sie mit der Wiedergabe und die Zeit wird synchronisiert. Die updatePlayer () -Methode ist dafür verantwortlich:



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();
    }
}


Das ist alles! Basierend auf den Joint JS-Methoden und der Kreativität unserer Entwickler wurden Live-Protokolle angezeigt. Testen Sie sie unbedingt selbst, wenn Sie es noch nicht getan haben :)



Großartig, wenn Ihnen unsere Artikelserie über Keith Updates gefällt. Wir werden weiterhin die frischesten und interessantesten mit Ihnen teilen!



All Articles