feat(GODT-3121): adds KB suggestion scoring.

This commit is contained in:
Xavier Michelon 2023-12-06 16:17:53 +01:00
parent 3309137b80
commit b93c10ad47
4 changed files with 72 additions and 23 deletions

View File

@ -69,7 +69,7 @@ func NewShowMainWindowEvent() *StreamEvent {
func NewRequestKnowledgeBaseSuggestionsEvent(suggestions kb.ArticleList) *StreamEvent {
s := xslices.Map(
suggestions,
func(article kb.Article) *KnowledgeBaseSuggestion {
func(article *kb.Article) *KnowledgeBaseSuggestion {
return &KnowledgeBaseSuggestion{Url: article.URL, Title: article.Title}
},
)

View File

@ -6,7 +6,10 @@
"keywords": [
"start",
"automatically",
"login"
"login",
"startup",
"start-up",
"boot"
]
},
{
@ -14,7 +17,11 @@
"url": "https://proton.me/support/bridge-automatic-update",
"title": "Automatic Update and Bridge",
"keywords": [
"TBD"
"update",
"upgrade",
"restart",
"automatic",
"manual"
]
},
{
@ -22,7 +29,12 @@
"url": "https://proton.me/support/messages-encrypted-via-bridge",
"title": "Are my messages encrypted via Proton Mail Bridge?",
"keywords": [
"TBD"
"encrypted",
"privacy",
"message",
"security",
"gpg",
"pgp"
]
},
{
@ -70,7 +82,7 @@
"url": "https://proton.me/support/update-required",
"title": "Update required",
"keywords": [
"TBD"
"update", "upgrade", "restart", "reboot"
]
},
{
@ -78,7 +90,7 @@
"url": "https://proton.me/support/port-already-occupied-error",
"title": "Port already occupied error",
"keywords": [
"TBD"
"Port", "occupied", "1143", "1025", "SMTP", "IMAP", "error"
]
},
{
@ -86,7 +98,7 @@
"url": "https://proton.me/support/clients-supported-bridge",
"title": "Email clients supported by Proton Mail Bridge",
"keywords": [
"TBD"
"client", "Outlook", "Thunderbird", "Apple Mail", "EM Client", "The Bat", "Eudora", "Postbox"
]
},
{
@ -94,7 +106,7 @@
"url": "https://proton.me/support/imap-smtp-and-pop3-setup",
"title": "IMAP, SMTP, and POP3 setup",
"keywords": [
"TBD"
"IMAP", "SMTP", "setup"
]
},
{
@ -102,7 +114,7 @@
"url": "https://proton.me/support/protonmail-bridge-install",
"title": "How to install Proton Mail Bridge",
"keywords": [
"TBD"
"install", "setup", "installer"
]
},
{
@ -110,7 +122,7 @@
"url": "https://proton.me/support/bridge-for-linux",
"title": "Proton Mail Bridge for Linux",
"keywords": [
"TBD"
"Linux", "Ubuntu", "Fedora", "Debian", "Unix", "deb", "rpm"
]
},
{
@ -118,7 +130,8 @@
"url": "https://proton.me/support/operating-systems-supported-bridge",
"title": "System requirements for Proton Mail Bridge",
"keywords": [
"TBD"
"requirement", "cpu", "memory", "Windows 7", "Windows XP", "Windows", "Windows 10", "Windows 11","Catalina", "Sonoma", "Ventura",
"Debian", "Ubuntu", "Fedora", "Redhat", "Big Sur", "Monterey"
]
},
{
@ -126,7 +139,7 @@
"url": "https://proton.me/support/protonmail-bridge-configure-client",
"title": "How to configure your email client for Proton Mail Bridge",
"keywords": [
"TBD"
"Client", "Outlook", "configure", "setup", "IMAP", "SMTP"
]
},
{
@ -134,7 +147,7 @@
"url": "https://proton.me/support/invalid-password-error-setting-email-client",
"title": "Invalid password error while setting up email client",
"keywords": [
"TBD"
"password", "invalid", "error"
]
},
{
@ -142,7 +155,7 @@
"url": "https://proton.me/support/protonmail-bridge-clients-windows-outlook-2019",
"title": "Proton Mail Bridge Microsoft Outlook for Windows 2019 setup guide",
"keywords": [
"TBD"
"Outlook", "2019", "setup", "configuration"
]
},
{

View File

@ -20,6 +20,11 @@ package kb
import (
_ "embed"
"encoding/json"
"regexp"
"strings"
"github.com/bradenaw/juniper/xslices"
"golang.org/x/exp/slices"
)
//go:embed kbArticleList.json
@ -31,9 +36,10 @@ type Article struct {
URL string `json:"url"`
Title string `json:"title"`
Keywords []string `json:"keywords"`
Score int
}
type ArticleList []Article
type ArticleList []*Article
// GetArticleList returns the list of KB articles.
func GetArticleList() (ArticleList, error) {
@ -44,17 +50,35 @@ func GetArticleList() (ArticleList, error) {
}
// GetSuggestions return a list of up to 3 suggestions for KB articles matching the given user input.
func GetSuggestions(_ string) (ArticleList, error) {
func GetSuggestions(userInput string) (ArticleList, error) {
userInput = strings.ToUpper(userInput)
articles, err := GetArticleList()
if err != nil {
return ArticleList{}, err
}
// note starting with go 1.21, we will be able to do:
// return articles[:min(3, len(articles))]
l := len(articles)
if l > 3 {
l = 3
for _, article := range articles {
for _, keyword := range article.Keywords {
if strings.Contains(userInput, strings.ToUpper(keyword)) {
article.Score++
}
}
}
return articles[:l], nil
articles = xslices.Filter(articles, func(article *Article) bool { return article.Score > 0 })
slices.SortFunc(articles, func(lhs, rhs *Article) bool { return lhs.Score > rhs.Score })
if len(articles) > 3 {
return articles[:3], nil
}
return articles, nil
}
func simplifyUserInput(input string) string {
// replace any sequence not matching of the following with a single space:
// - letters in any language (accentuated or not)
// - numbers
// - the apostrophe character '
return strings.TrimSpace(regexp.MustCompile(`[^\p{L}\p{N}']+`).ReplaceAllString(input, " "))
}

View File

@ -18,6 +18,7 @@
package kb
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
@ -39,7 +40,18 @@ func Test_ArticleList(t *testing.T) {
}
func Test_GetSuggestions(t *testing.T) {
suggestions, err := GetSuggestions("")
suggestions, err := GetSuggestions("Thunderbird is not working, error during password")
require.NoError(t, err)
require.True(t, len(suggestions) <= 3)
for _, article := range suggestions {
fmt.Printf("Score: %v - %#v\n", article.Score, article.Title)
}
suggestions, err = GetSuggestions("Supercalifragilisticexpialidocious Sesquipedalian Worcestershire")
require.NoError(t, err)
require.Empty(t, suggestions)
}
func Test_simplifyUserInput(t *testing.T) {
require.Equal(t, "word1 ñóÄ don't déjà 33 pizza", simplifyUserInput(" \nword1 \n\tñóÄ don't\n\n\ndéjà, 33 pizza=🍕\n,\n"))
}