Version, authenticate and fully trace ecosystem calls #84
@@ -214,6 +214,9 @@ func (h *reactiveHandler) queryHabits(ctx context.Context, t *queryTurn) (string
|
||||
if q.HasWeekday {
|
||||
return profile.FormatWeekdayRU(q.Weekday), true
|
||||
}
|
||||
if q.Weekend {
|
||||
return profile.FormatWeekendRU(), true
|
||||
}
|
||||
return profile.FormatOverallRU(), true
|
||||
}
|
||||
|
||||
|
||||
@@ -250,3 +250,20 @@ func TestQueryHabitsReadsSelfFactsOnly(t *testing.T) {
|
||||
t.Errorf("profile read kind %q, want %q", api.kind, store.KindSelf)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHabitQueryWithPlanWordReachesHabits — the whole chain, not just the
|
||||
// matchers: a habit question carrying "планы" used to be answered by the day
|
||||
// plan with today's calendar, because day-plan sits above habits.
|
||||
func TestHabitQueryWithPlanWordReachesHabits(t *testing.T) {
|
||||
now := planDay()
|
||||
api := &habitAPI{facts: tuesdayFacts("workout", 19, 4, now)}
|
||||
h := &reactiveHandler{api: api, now: func() time.Time { return now }}
|
||||
|
||||
reply := h.actionQuery(context.Background(), router.Decision{
|
||||
Intent: router.IntentQuery,
|
||||
Utterance: "какие у меня обычно планы по вторникам?",
|
||||
})
|
||||
if want := "по вторникам ты обычно тренируешься около 19:00."; reply != want {
|
||||
t.Errorf("reply = %q, want %q", reply, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,6 +346,30 @@ func (p Profile) FormatWeekdayRU(wd time.Weekday) string {
|
||||
return fmt.Sprintf("по %s я пока не вижу у тебя ничего постоянного.", day)
|
||||
}
|
||||
|
||||
// FormatWeekendRU reads back what distinguishes Saturday and Sunday.
|
||||
//
|
||||
// The two days are answered separately rather than pooled: "по выходным" is a
|
||||
// question about both, and a habit he has on Saturdays only is the interesting
|
||||
// half of the answer, not noise to average away.
|
||||
func (p Profile) FormatWeekendRU() string {
|
||||
sat, sun := p.Weekly[time.Saturday], p.Weekly[time.Sunday]
|
||||
switch {
|
||||
case len(sat) > 0 && len(sun) > 0:
|
||||
return fmt.Sprintf("по субботам ты обычно %s, по воскресеньям — %s.",
|
||||
joinActivities(sat), joinActivities(sun))
|
||||
case len(sat) > 0:
|
||||
return fmt.Sprintf("по субботам ты обычно %s, а по воскресеньям ничего постоянного.",
|
||||
joinActivities(sat))
|
||||
case len(sun) > 0:
|
||||
return fmt.Sprintf("по воскресеньям ты обычно %s, а по субботам ничего постоянного.",
|
||||
joinActivities(sun))
|
||||
case len(p.Everyday) > 0:
|
||||
return fmt.Sprintf("по выходным у тебя нет ничего особенного — то же, что и в остальные дни: %s.",
|
||||
joinActivities(p.Everyday))
|
||||
}
|
||||
return "по выходным я пока не вижу у тебя ничего постоянного."
|
||||
}
|
||||
|
||||
// FormatOverallRU reads back the habits that hold across the whole week, and
|
||||
// says over what stretch of records it is claiming them.
|
||||
//
|
||||
|
||||
@@ -350,3 +350,20 @@ func TestPluralDaysRU(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "по выходным" is a question about two days, answered as two days.
|
||||
func TestFormatWeekendRU(t *testing.T) {
|
||||
now := behaviorNow()
|
||||
obs := append(
|
||||
habitHistory("workout", time.Saturday, 11, 0, 3, now),
|
||||
habitHistory("walk", time.Sunday, 15, 0, 3, now)...,
|
||||
)
|
||||
got := BuildProfile(obs, now).FormatWeekendRU()
|
||||
if !strings.Contains(got, "по субботам") || !strings.Contains(got, "по воскресеньям") {
|
||||
t.Errorf("got %q, want both weekend days named", got)
|
||||
}
|
||||
empty := BuildProfile(nil, now).FormatWeekendRU()
|
||||
if strings.Contains(empty, "у меня") {
|
||||
t.Errorf("got %q", empty)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,15 @@ var otherDayWords = []string{
|
||||
// сегодня?" and a plan that hijacks every date-bearing question would bury the
|
||||
// events under checklist lines. Only a plan-shaped ask, and only about today.
|
||||
func IsDayPlanQuery(text string) bool {
|
||||
// A habit question is never a day plan, whatever words it shares with one.
|
||||
// "какие у меня обычно планы по вторникам?" carries "планы", so the plan
|
||||
// source claimed it and answered today's calendar stamped with today's
|
||||
// date, and the habit source never ran. Deciding it here rather than by
|
||||
// reordering the source table keeps one matcher from depending on the
|
||||
// other's position in a slice.
|
||||
if _, ok := ParseHabitQuery(text); ok {
|
||||
return false
|
||||
}
|
||||
toks := planTokens(text)
|
||||
for _, t := range toks {
|
||||
for _, w := range otherDayWords {
|
||||
|
||||
@@ -11,9 +11,11 @@ import "time"
|
||||
|
||||
// HabitQuery — a parsed "what do I usually do" question. Weekday is set only
|
||||
// when the utterance names one; otherwise the answer covers the whole week.
|
||||
// Weekend is set for "по выходным", which names two days rather than one.
|
||||
type HabitQuery struct {
|
||||
Weekday time.Weekday
|
||||
HasWeekday bool
|
||||
Weekend bool
|
||||
}
|
||||
|
||||
// habitMarkers — the words that make a question about habit rather than about
|
||||
@@ -35,6 +37,8 @@ var weekdayWords = map[string]time.Weekday{
|
||||
"пятница": time.Friday, "пятницу": time.Friday, "пятницам": time.Friday,
|
||||
"суббота": time.Saturday, "субботу": time.Saturday, "субботам": time.Saturday,
|
||||
"воскресенье": time.Sunday, "воскресеньям": time.Sunday,
|
||||
"воскресенья": time.Sunday, "воскресенью": time.Sunday,
|
||||
"воскресеньем": time.Sunday, "воскресеньях": time.Sunday,
|
||||
"monday": time.Monday, "mondays": time.Monday,
|
||||
"tuesday": time.Tuesday, "tuesdays": time.Tuesday,
|
||||
"wednesday": time.Wednesday, "wednesdays": time.Wednesday,
|
||||
@@ -44,6 +48,14 @@ var weekdayWords = map[string]time.Weekday{
|
||||
"sunday": time.Sunday, "sundays": time.Sunday,
|
||||
}
|
||||
|
||||
// weekendWords — the weekend as one unit. "что я обычно делаю по выходным?"
|
||||
// has a habit marker and names days, but no weekday name is in it, so it used
|
||||
// to fall through to the whole-week profile and answer about Tuesdays too.
|
||||
var weekendWords = map[string]bool{
|
||||
"выходным": true, "выходные": true, "выходных": true, "выходной": true,
|
||||
"weekend": true, "weekends": true,
|
||||
}
|
||||
|
||||
// ParseHabitQuery reports whether an utterance asks what the owner usually
|
||||
// does, and on which weekday if it names one.
|
||||
//
|
||||
@@ -68,6 +80,9 @@ func ParseHabitQuery(text string) (HabitQuery, bool) {
|
||||
if wd, ok := weekdayWords[t]; ok {
|
||||
return HabitQuery{Weekday: wd, HasWeekday: true}, true
|
||||
}
|
||||
if weekendWords[t] {
|
||||
return HabitQuery{Weekend: true}, true
|
||||
}
|
||||
}
|
||||
return HabitQuery{}, true
|
||||
}
|
||||
|
||||
@@ -43,3 +43,48 @@ func TestParseHabitQuery(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestHabitQueryBeatsDayPlan — "какие у меня обычно планы по вторникам?" is a
|
||||
// habit question that happens to carry a plan word. The day plan claimed it
|
||||
// first and answered today's calendar stamped with today's date, and the habit
|
||||
// source never ran.
|
||||
func TestHabitQueryBeatsDayPlan(t *testing.T) {
|
||||
for _, q := range []string{
|
||||
"какие у меня обычно планы по вторникам?",
|
||||
"что обычно по плану в среду?",
|
||||
"какие планы обычно по выходным?",
|
||||
} {
|
||||
if IsDayPlanQuery(q) {
|
||||
t.Errorf("%q was claimed as a day plan", q)
|
||||
}
|
||||
if _, ok := ParseHabitQuery(q); !ok {
|
||||
t.Errorf("%q is not parsed as a habit question", q)
|
||||
}
|
||||
}
|
||||
// A plan question without a habit marker still belongs to the day plan.
|
||||
for _, q := range []string{"какие планы на сегодня?", "что у меня по плану?"} {
|
||||
if !IsDayPlanQuery(q) {
|
||||
t.Errorf("%q must still be a day plan", q)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestHabitQueryWeekendAndSundayForms — "по выходным" names days but no
|
||||
// weekday, so it used to be answered with the whole-week profile. Sunday had
|
||||
// only its dative plural listed.
|
||||
func TestHabitQueryWeekendAndSundayForms(t *testing.T) {
|
||||
q, ok := ParseHabitQuery("что я обычно делаю по выходным?")
|
||||
if !ok || !q.Weekend || q.HasWeekday {
|
||||
t.Errorf("weekend query parsed as %+v (ok=%v)", q, ok)
|
||||
}
|
||||
for _, s := range []string{
|
||||
"что я обычно делаю в воскресенье?",
|
||||
"чем я обычно занят по воскресеньям?",
|
||||
"что обычно бывает в воскресенья?",
|
||||
} {
|
||||
q, ok := ParseHabitQuery(s)
|
||||
if !ok || !q.HasWeekday || q.Weekday != time.Sunday {
|
||||
t.Errorf("%q parsed as %+v (ok=%v)", s, q, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user