From a47c90c9c3675f62371e032adb11a36086f31f94 Mon Sep 17 00:00:00 2001 From: Jacob Dubin Date: Sat, 16 May 2026 10:16:34 -0500 Subject: [PATCH] Polish weather phrasing for current and forecast replies --- .../Services/JiboInteractionService.cs | 4 +- .../Weather/OpenWeatherReportProvider.cs | 17 +---- .../Infrastructure/ProviderCachingTests.cs | 65 ++----------------- .../WebSockets/JiboInteractionServiceTests.cs | 28 ++++---- 4 files changed, 26 insertions(+), 88 deletions(-) diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs index d1a210d..ec94393 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs @@ -988,10 +988,10 @@ public sealed class JiboInteractionService( var forecastLeadIn = string.IsNullOrWhiteSpace(weatherDate.ForecastLeadIn) ? "Tomorrow" : weatherDate.ForecastLeadIn; - return $"{forecastLeadIn} in {location}, expect {summary}{tempRange}."; + return $"{forecastLeadIn} in {location}, it looks {summary}{tempRange}."; } - return $"Right now in {location}, it is {summary} and {snapshot.Temperature} degrees {unit}."; + return $"Right now in {location}, it's {summary}, around {snapshot.Temperature} degrees {unit}."; } private static string BuildWeeklyForecastSpokenReply( diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Weather/OpenWeatherReportProvider.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Weather/OpenWeatherReportProvider.cs index 64a72bc..944d66e 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Weather/OpenWeatherReportProvider.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Weather/OpenWeatherReportProvider.cs @@ -173,21 +173,10 @@ public sealed class OpenWeatherReportProvider( var temperature = TryReadInt(main, "temp"); var high = TryReadInt(main, "temp_max"); var low = TryReadInt(main, "temp_min"); - try + if (temperature is not null) { - var enrichedBand = await TryResolveCurrentDayHighLowFromForecastAsync( - location, - useCelsius, - cancellationToken); - if (enrichedBand is not null) - { - high = enrichedBand.Value.High ?? high; - low = enrichedBand.Value.Low ?? low; - } - } - catch (Exception exception) when (!cancellationToken.IsCancellationRequested) - { - logger.LogDebug(exception, "OpenWeather forecast enrichment failed for current-day Hi/Lo."); + high = high is null ? temperature : Math.Max(high.Value, temperature.Value); + low = low is null ? temperature : Math.Min(low.Value, temperature.Value); } if (temperature is null && high is null && low is null) diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/Infrastructure/ProviderCachingTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/Infrastructure/ProviderCachingTests.cs index 49a74b7..094f2f7 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/Infrastructure/ProviderCachingTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/Infrastructure/ProviderCachingTests.cs @@ -44,17 +44,12 @@ public sealed class ProviderCachingTests Assert.NotNull(second); Assert.Equal(1, handler.GetCallCount("/geo/1.0/direct")); Assert.Equal(1, handler.GetCallCount("/data/2.5/weather")); - Assert.Equal(1, handler.GetCallCount("/data/2.5/forecast")); + Assert.Equal(0, handler.GetCallCount("/data/2.5/forecast")); } [Fact] - public async Task OpenWeatherReportProvider_EnrichesCurrentHiLoFromForecast_WhenCurrentBandIsFlat() + public async Task OpenWeatherReportProvider_UsesCurrentHiLoForCurrentDay_WhenCurrentBandDiffers() { - var utcStart = DateTimeOffset.UtcNow.UtcDateTime.Date; - var forecastWindowStart = new DateTimeOffset(utcStart, TimeSpan.Zero).ToUnixTimeSeconds(); - var forecastWindowMid = new DateTimeOffset(utcStart.AddHours(3), TimeSpan.Zero).ToUnixTimeSeconds(); - var forecastWindowLate = new DateTimeOffset(utcStart.AddHours(6), TimeSpan.Zero).ToUnixTimeSeconds(); - var handler = new CountingHttpMessageHandler(message => { var path = message.RequestUri?.AbsolutePath ?? string.Empty; @@ -63,9 +58,7 @@ public sealed class ProviderCachingTests "/geo/1.0/direct" => JsonResponse( """[{"name":"Lone Jack","country":"US","lat":38.8708,"lon":-94.1733}]"""), "/data/2.5/weather" => JsonResponse( - """{"name":"Lone Jack","weather":[{"main":"Clouds","description":"overcast clouds"}],"main":{"temp":77.0,"temp_max":77.0,"temp_min":77.0}}"""), - "/data/2.5/forecast" => JsonResponse( - $"{{\"city\":{{\"timezone\":0}},\"list\":[{{\"dt\":{forecastWindowStart},\"main\":{{\"temp\":76.0,\"temp_max\":81.0,\"temp_min\":70.0}}}},{{\"dt\":{forecastWindowMid},\"main\":{{\"temp\":80.0,\"temp_max\":84.0,\"temp_min\":69.0}}}},{{\"dt\":{forecastWindowLate},\"main\":{{\"temp\":78.0,\"temp_max\":79.0,\"temp_min\":67.0}}}}]}}"), + """{"name":"Lone Jack","weather":[{"main":"Clouds","description":"overcast clouds"}],"main":{"temp":76.0,"temp_max":82.0,"temp_min":78.0}}"""), _ => new HttpResponseMessage(HttpStatusCode.NotFound) }; }); @@ -84,55 +77,11 @@ public sealed class ProviderCachingTests var report = await provider.GetReportAsync(new WeatherReportRequest("Lone Jack,US", null, null, false, false, 0)); Assert.NotNull(report); - Assert.Equal(77, report!.Temperature); - Assert.Equal(84, report.HighTemperature); - Assert.Equal(67, report.LowTemperature); + Assert.Equal(76, report!.Temperature); + Assert.Equal(82, report.HighTemperature); + Assert.Equal(76, report.LowTemperature); Assert.Equal(1, handler.GetCallCount("/data/2.5/weather")); - Assert.Equal(1, handler.GetCallCount("/data/2.5/forecast")); - } - - [Fact] - public async Task OpenWeatherReportProvider_UsesForecastHiLoForCurrentDay_WhenCurrentBandDiffers() - { - var utcStart = DateTimeOffset.UtcNow.UtcDateTime.Date; - var forecastWindowStart = new DateTimeOffset(utcStart, TimeSpan.Zero).ToUnixTimeSeconds(); - var forecastWindowMid = new DateTimeOffset(utcStart.AddHours(3), TimeSpan.Zero).ToUnixTimeSeconds(); - var forecastWindowLate = new DateTimeOffset(utcStart.AddHours(6), TimeSpan.Zero).ToUnixTimeSeconds(); - - var handler = new CountingHttpMessageHandler(message => - { - var path = message.RequestUri?.AbsolutePath ?? string.Empty; - return path switch - { - "/geo/1.0/direct" => JsonResponse( - """[{"name":"Boston","country":"US","lat":42.3601,"lon":-71.0589}]"""), - "/data/2.5/weather" => JsonResponse( - """{"name":"Boston","weather":[{"main":"Clouds","description":"overcast clouds"}],"main":{"temp":70.0,"temp_max":72.0,"temp_min":66.0}}"""), - "/data/2.5/forecast" => JsonResponse( - $"{{\"city\":{{\"timezone\":0}},\"list\":[{{\"dt\":{forecastWindowStart},\"main\":{{\"temp\":76.0,\"temp_max\":81.0,\"temp_min\":70.0}}}},{{\"dt\":{forecastWindowMid},\"main\":{{\"temp\":80.0,\"temp_max\":84.0,\"temp_min\":69.0}}}},{{\"dt\":{forecastWindowLate},\"main\":{{\"temp\":78.0,\"temp_max\":79.0,\"temp_min\":67.0}}}}]}}"), - _ => new HttpResponseMessage(HttpStatusCode.NotFound) - }; - }); - var provider = new OpenWeatherReportProvider( - new HttpClient(handler), - new OpenWeatherOptions - { - ApiKey = "test-key", - CurrentCacheTtlSeconds = 300, - ForecastCacheTtlSeconds = 300, - GeocodeCacheTtlSeconds = 300, - FailureCacheTtlSeconds = 30 - }, - NullLogger.Instance); - - var report = await provider.GetReportAsync(new WeatherReportRequest("Boston,US", null, null, false, false, 0)); - - Assert.NotNull(report); - Assert.Equal(70, report!.Temperature); - Assert.Equal(84, report.HighTemperature); - Assert.Equal(67, report.LowTemperature); - Assert.Equal(1, handler.GetCallCount("/data/2.5/weather")); - Assert.Equal(1, handler.GetCallCount("/data/2.5/forecast")); + Assert.Equal(0, handler.GetCallCount("/data/2.5/forecast")); } [Fact] diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs index 04ffa5a..6275d6c 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs @@ -1725,7 +1725,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("personal_report_delivered", decision.IntentName); Assert.Contains("Great, alex. Here is your personal report.", decision.ReplyText, StringComparison.OrdinalIgnoreCase); - Assert.Contains("Right now in Boston, U.S., it is light rain and 61 degrees Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Right now in Boston, U.S., it's light rain, around 61 degrees Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase); Assert.Contains("That is your personal report.", decision.ReplyText, StringComparison.OrdinalIgnoreCase); Assert.NotNull(decision.ContextUpdates); Assert.Equal("idle", decision.ContextUpdates![PersonalReportStateKey]); @@ -2063,7 +2063,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal(54, decision.SkillPayload["weather_low"]); Assert.Equal("F", decision.SkillPayload["weather_unit"]); Assert.Equal("Normal", decision.SkillPayload["weather_theme"]); - Assert.Equal("Right now in Boston, U.S., it is light rain and 61 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("Right now in Boston, U.S., it's light rain, around 61 degrees Fahrenheit.", decision.ReplyText); Assert.NotNull(provider.LastRequest); Assert.False(provider.LastRequest!.IsTomorrow); Assert.Equal(0, provider.LastRequest.ForecastDayOffset); @@ -2088,7 +2088,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("Chicago", provider.LastRequest?.LocationQuery); Assert.True(provider.LastRequest?.IsTomorrow); Assert.Equal(1, provider.LastRequest?.ForecastDayOffset); - Assert.Equal("Tomorrow in Chicago, U.S., expect mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("Tomorrow in Chicago, U.S., it looks mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText); } [Fact] @@ -2110,7 +2110,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("Seattle", provider.LastRequest?.LocationQuery); Assert.False(provider.LastRequest?.IsTomorrow); Assert.Equal(0, provider.LastRequest?.ForecastDayOffset); - Assert.Equal("Right now in Seattle, U.S., it is light rain and 58 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("Right now in Seattle, U.S., it's light rain, around 58 degrees Fahrenheit.", decision.ReplyText); } [Fact] @@ -2132,7 +2132,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("Paris", provider.LastRequest?.LocationQuery); Assert.False(provider.LastRequest?.IsTomorrow); Assert.Equal(0, provider.LastRequest?.ForecastDayOffset); - Assert.Equal("Right now in Paris, FR, it is overcast clouds and 66 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("Right now in Paris, FR, it's overcast clouds, around 66 degrees Fahrenheit.", decision.ReplyText); } [Fact] @@ -2154,7 +2154,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("Redmond Oregon", provider.LastRequest?.LocationQuery); Assert.False(provider.LastRequest?.IsTomorrow); Assert.Equal(0, provider.LastRequest?.ForecastDayOffset); - Assert.Equal("Right now in Redmond, U.S., it is clear sky and 63 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("Right now in Redmond, U.S., it's clear sky, around 63 degrees Fahrenheit.", decision.ReplyText); } [Fact] @@ -2176,7 +2176,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("New York City", provider.LastRequest?.LocationQuery); Assert.True(provider.LastRequest?.IsTomorrow); Assert.Equal(1, provider.LastRequest?.ForecastDayOffset); - Assert.Equal("Tomorrow in New York, U.S., expect partly cloudy with a high near 76 degrees Fahrenheit and a low around 61 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("Tomorrow in New York, U.S., it looks partly cloudy with a high near 76 degrees Fahrenheit and a low around 61 degrees Fahrenheit.", decision.ReplyText); } [Fact] @@ -2231,7 +2231,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("Chicago", provider.LastRequest?.LocationQuery); Assert.Null(provider.LastRequest?.Latitude); Assert.Null(provider.LastRequest?.Longitude); - Assert.Equal("Right now in Chicago, U.S., it is mostly cloudy and 70 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("Right now in Chicago, U.S., it's mostly cloudy, around 70 degrees Fahrenheit.", decision.ReplyText); } [Fact] @@ -2261,7 +2261,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("Chicago", provider.LastRequest?.LocationQuery); Assert.Equal(0, provider.LastRequest?.ForecastDayOffset); Assert.False(provider.LastRequest?.IsTomorrow); - Assert.Equal("Right now in Chicago, U.S., it is mostly cloudy and 70 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("Right now in Chicago, U.S., it's mostly cloudy, around 70 degrees Fahrenheit.", decision.ReplyText); } [Fact] @@ -2291,7 +2291,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("Chicago", provider.LastRequest?.LocationQuery); Assert.Equal(1, provider.LastRequest?.ForecastDayOffset); Assert.True(provider.LastRequest?.IsTomorrow); - Assert.Equal("Tomorrow in Chicago, U.S., expect mostly cloudy with a high near 75 degrees Fahrenheit and a low around 62 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("Tomorrow in Chicago, U.S., it looks mostly cloudy with a high near 75 degrees Fahrenheit and a low around 62 degrees Fahrenheit.", decision.ReplyText); } [Theory] @@ -2361,7 +2361,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("weather", decision.IntentName); Assert.Equal(2, provider.LastRequest?.ForecastDayOffset); Assert.False(provider.LastRequest?.IsTomorrow); - Assert.Equal("On Monday in Portland, U.S., expect scattered clouds with a high near 68 degrees Fahrenheit and a low around 53 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("On Monday in Portland, U.S., it looks scattered clouds with a high near 68 degrees Fahrenheit and a low around 53 degrees Fahrenheit.", decision.ReplyText); } [Fact] @@ -2386,7 +2386,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("weather", decision.IntentName); Assert.Equal("Chicago", provider.LastRequest?.LocationQuery); Assert.Equal(1, provider.LastRequest?.ForecastDayOffset); - Assert.Equal("On Tuesday in Chicago, U.S., expect light rain with a high near 63 degrees Fahrenheit and a low around 51 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("On Tuesday in Chicago, U.S., it looks light rain with a high near 63 degrees Fahrenheit and a low around 51 degrees Fahrenheit.", decision.ReplyText); } [Fact] @@ -2435,7 +2435,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("weather", decision.IntentName); Assert.Equal("Paris", provider.LastRequest?.LocationQuery); Assert.Equal(5, provider.LastRequest?.ForecastDayOffset); - Assert.Equal("This weekend in Paris, FR, expect overcast clouds with a high near 70 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("This weekend in Paris, FR, it looks overcast clouds with a high near 70 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText); } [Fact] @@ -2557,7 +2557,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("weather", decision.IntentName); Assert.Equal("Chicago", provider.LastRequest?.LocationQuery); Assert.Equal(2, provider.LastRequest?.ForecastDayOffset); - Assert.Equal("The day after tomorrow in Chicago, U.S., expect mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText); + Assert.Equal("The day after tomorrow in Chicago, U.S., it looks mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText); } [Fact]