-
Notifications
You must be signed in to change notification settings - Fork 20
/
environment.go
138 lines (112 loc) · 4.19 KB
/
environment.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package flows
import (
"regexp"
"slices"
"strings"
"time"
"github.com/nyaruka/gocommon/i18n"
"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/envs"
)
type assetsEnvironment struct {
envs.Environment
locationResolver envs.LocationResolver
}
// NewAssetsEnvironment creates a new environment from a base environment and adds support for location resolving using
// location assets.
func NewAssetsEnvironment(e envs.Environment, la *LocationAssets) envs.Environment {
var locationResolver envs.LocationResolver
hierarchies := la.Hierarchies()
if len(hierarchies) > 0 {
locationResolver = &assetLocationResolver{hierarchies[0]}
}
return &assetsEnvironment{Environment: e, locationResolver: locationResolver}
}
func (e *assetsEnvironment) LocationResolver() envs.LocationResolver {
return e.locationResolver
}
type assetLocationResolver struct {
locations assets.LocationHierarchy
}
// FindLocations returns locations with the matching name (case-insensitive), level and parent (optional)
func (r *assetLocationResolver) FindLocations(env envs.Environment, name string, level envs.LocationLevel, parent *envs.Location) []*envs.Location {
return r.locations.FindByName(env, name, level, parent)
}
// FindLocationsFuzzy returns matching locations like FindLocations but attempts the following strategies
// to find locations:
// 1. Exact match
// 2. Match with punctuation removed
// 3. Split input into words and try to match each word
// 4. Try to match pairs of words
func (r *assetLocationResolver) FindLocationsFuzzy(env envs.Environment, text string, level envs.LocationLevel, parent *envs.Location) []*envs.Location {
// try matching name exactly
if locations := r.FindLocations(env, text, level, parent); len(locations) > 0 {
return locations
}
// try with punctuation removed
stripped := strings.TrimSpace(regexp.MustCompile(`[\s\p{P}]+`).ReplaceAllString(text, ""))
if locations := r.FindLocations(env, stripped, level, parent); len(locations) > 0 {
return locations
}
// try on each tokenized word
re := regexp.MustCompile(`[\p{L}\d]+(-[\p{L}\d]+)*`)
words := re.FindAllString(text, -1)
for _, word := range words {
if locations := r.FindLocations(env, word, level, parent); len(locations) > 0 {
return locations
}
}
// try with each pair of words
for i := 0; i < len(words)-1; i++ {
wordPair := strings.Join(words[i:i+2], " ")
if locations := r.FindLocations(env, wordPair, level, parent); len(locations) > 0 {
return locations
}
}
return []*envs.Location{}
}
func (r *assetLocationResolver) LookupLocation(path envs.LocationPath) *envs.Location {
return r.locations.FindByPath(path)
}
type sessionEnvironment struct {
envs.Environment
session Session
}
// NewSessionEnvironment creates a new environment from a session's base environment that merges some properties with
// those from the contact.
func NewSessionEnvironment(s Session) envs.Environment {
return &sessionEnvironment{
Environment: NewAssetsEnvironment(s.Environment(), s.Assets().Locations()),
session: s,
}
}
func (e *sessionEnvironment) Timezone() *time.Location {
contact := e.session.Contact()
// if we have a contact and they have a timezone that overrides the base enviroment's timezone
if contact != nil && contact.Timezone() != nil {
return contact.Timezone()
}
return e.Environment.Timezone()
}
func (e *sessionEnvironment) DefaultLanguage() i18n.Language {
contact := e.session.Contact()
// if we have a contact and they have a language and it's an allowed language that overrides the base environment's languuage
if contact != nil && contact.Language() != i18n.NilLanguage && slices.Contains(e.AllowedLanguages(), contact.Language()) {
return contact.Language()
}
return e.Environment.DefaultLanguage()
}
func (e *sessionEnvironment) DefaultCountry() i18n.Country {
contact := e.session.Contact()
// if we have a contact and they have a preferred channel with a country that overrides the base environment's country
if contact != nil {
cc := contact.Country()
if cc != i18n.NilCountry {
return cc
}
}
return e.Environment.DefaultCountry()
}
func (e *sessionEnvironment) DefaultLocale() i18n.Locale {
return i18n.NewLocale(e.DefaultLanguage(), e.DefaultCountry())
}