Move catalog, dossier sync, and SearXNG to private oo-workspace; keep generic CRM name/title helpers and anonymized docs/examples.
171 lines
4.6 KiB
Go
171 lines
4.6 KiB
Go
package onlyoffice
|
||
|
||
import (
|
||
"regexp"
|
||
"strings"
|
||
)
|
||
|
||
var multiSpace = regexp.MustCompile(`\s+`)
|
||
|
||
// sloganSeparators split a company name from a trailing tagline/slogan.
|
||
var sloganSeparators = []string{" — ", " – ", " - ", "—", "–"}
|
||
|
||
// companyLegalSuffix matches trailing legal-form tokens (GmbH, & Co. KG, …).
|
||
var companyLegalSuffix = regexp.MustCompile(`(?i)[\s,]*(&\s*co\.?\s*kg\.?|co\.?\s*kg\.?|gmbh|ag|ltd\.?|llc\.?|inc\.?|plc\.?|s\.?l\.?|sarl|b\.?v\.?)\s*$`)
|
||
|
||
// StripSloganSuffix returns the part before an em/en dash tagline, e.g.
|
||
// "Affirm — Fraud Engineering" → "Affirm".
|
||
func StripSloganSuffix(s string) string {
|
||
s = strings.TrimSpace(s)
|
||
for _, sep := range sloganSeparators {
|
||
if i := strings.Index(s, sep); i > 0 {
|
||
return strings.TrimSpace(s[:i])
|
||
}
|
||
}
|
||
return s
|
||
}
|
||
|
||
// StripCompanyLegalSuffix removes trailing GmbH / & Co. KG / Ltd / … tokens.
|
||
func StripCompanyLegalSuffix(s string) string {
|
||
s = strings.TrimSpace(s)
|
||
s = strings.TrimRight(s, ",; ")
|
||
for {
|
||
next := companyLegalSuffix.ReplaceAllString(s, "")
|
||
next = strings.TrimRight(strings.TrimSpace(next), ",; ")
|
||
if next == s || next == "" {
|
||
return s
|
||
}
|
||
s = next
|
||
}
|
||
}
|
||
|
||
// CompanyGroupingKey normalizes a company name for dedupe (ignores slogans and legal suffixes).
|
||
func CompanyGroupingKey(s string) string {
|
||
return NormalizeCompanyName(StripCompanyLegalSuffix(StripSloganSuffix(s)))
|
||
}
|
||
|
||
// NormalizeCompanyName lowercases and collapses whitespace for grouping.
|
||
func NormalizeCompanyName(s string) string {
|
||
return collapseKey(s)
|
||
}
|
||
|
||
// NormalizePersonKey builds a grouping key from first and last name.
|
||
func NormalizePersonKey(first, last string) string {
|
||
return collapseKey(strings.TrimSpace(first) + " " + strings.TrimSpace(last))
|
||
}
|
||
|
||
// NormalizeOpportunityTitle lowercases and trims a deal title for exact dedupe.
|
||
func NormalizeOpportunityTitle(s string) string {
|
||
return collapseKey(s)
|
||
}
|
||
|
||
// StripCompanySuffix removes a trailing " @ Company" segment when present.
|
||
func StripCompanySuffix(title string) string {
|
||
title = strings.TrimSpace(title)
|
||
if i := strings.LastIndex(title, " @ "); i >= 0 {
|
||
return strings.TrimSpace(title[:i])
|
||
}
|
||
return title
|
||
}
|
||
|
||
// FixDealTitle strips a leading @, normalizes separator spacing, and collapses
|
||
// empty-position titles like " @ Acme" to "Acme".
|
||
func FixDealTitle(s string) string {
|
||
s = strings.TrimSpace(s)
|
||
for strings.HasPrefix(s, "@") {
|
||
s = strings.TrimSpace(strings.TrimPrefix(s, "@"))
|
||
}
|
||
if s == "" {
|
||
return ""
|
||
}
|
||
if i := strings.Index(s, "@"); i >= 0 {
|
||
left := strings.TrimSpace(s[:i])
|
||
right := strings.TrimSpace(s[i+1:])
|
||
if left == "" && right != "" {
|
||
return right
|
||
}
|
||
if left != "" && right != "" {
|
||
return left + " @ " + right
|
||
}
|
||
}
|
||
return s
|
||
}
|
||
|
||
// ContactInfoKey groups contact info rows by type and normalized value.
|
||
// ContactInfoKey builds a dedupe key for a contact-info row.
|
||
func ContactInfoKey(infoType, value string) string {
|
||
return NormalizeContactInfoType(infoType) + "|" + strings.ToLower(strings.TrimSpace(value))
|
||
}
|
||
|
||
// NormalizeContactInfoType maps OO numeric infoType codes and string labels to
|
||
// a canonical lowercase label (email, phone, …).
|
||
func NormalizeContactInfoType(infoType string) string {
|
||
t := strings.ToLower(strings.TrimSpace(infoType))
|
||
switch t {
|
||
case "1", "email":
|
||
return "email"
|
||
case "2", "phone":
|
||
return "phone"
|
||
case "3", "website":
|
||
return "website"
|
||
case "4", "skype":
|
||
return "skype"
|
||
case "5", "twitter":
|
||
return "twitter"
|
||
case "6", "linkedin":
|
||
return "linkedin"
|
||
case "7", "facebook":
|
||
return "facebook"
|
||
case "8", "address":
|
||
return "address"
|
||
case "9", "livejournal":
|
||
return "livejournal"
|
||
case "10", "myspace":
|
||
return "myspace"
|
||
case "11", "gmail":
|
||
return "gmail"
|
||
case "12", "blogger":
|
||
return "blogger"
|
||
case "13", "yahoo":
|
||
return "yahoo"
|
||
case "14", "msn":
|
||
return "msn"
|
||
case "15", "icq":
|
||
return "icq"
|
||
case "16", "jabber":
|
||
return "jabber"
|
||
case "17", "aim":
|
||
return "aim"
|
||
default:
|
||
return t
|
||
}
|
||
}
|
||
|
||
// MemberDisplayKey normalizes a member displayName for duplicate detection.
|
||
func MemberDisplayKey(displayName string) string {
|
||
return CompanyGroupingKey(displayName)
|
||
}
|
||
|
||
func collapseKey(s string) string {
|
||
s = strings.TrimSpace(s)
|
||
s = multiSpace.ReplaceAllString(s, " ")
|
||
return strings.ToLower(s)
|
||
}
|
||
|
||
// OpportunityTitlesMatch reports whether two deal titles refer to the same role+company.
|
||
func OpportunityTitlesMatch(a, b string) bool {
|
||
return DealTitleKey(a, false) == DealTitleKey(b, false)
|
||
}
|
||
|
||
func DealTitleForApplication(position, company string) string {
|
||
position = strings.TrimSpace(position)
|
||
company = strings.TrimSpace(company)
|
||
if company == "" {
|
||
return position
|
||
}
|
||
if position == "" {
|
||
return company
|
||
}
|
||
return position + " @ " + company
|
||
}
|