Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve weather plugin results #198

Merged
merged 3 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Pinpoint.Plugin.Weather/Models/WeatherDayModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class WeatherDayModel
[JsonProperty("maxwind_kph")]
public float WindKph { get; set; }

public float WindMps => (float) Math.Round(WindKph * 0.27777777777846f, 2);
public float WindMps => (float)Math.Round(WindKph * 0.27777777777846f, 2);

[JsonProperty("totalprecip_mm")]
public float PrecipitationMm { get; set; }
Expand Down
93 changes: 57 additions & 36 deletions Pinpoint.Plugin.Weather/WeatherPlugin.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
Expand All @@ -21,19 +22,17 @@ public class WeatherPlugin : IPlugin
public PluginMeta Meta { get; set; } = new("Weather", Description, PluginPriority.Highest);

public PluginStorage Storage { get; set; } = new();


public TimeSpan DebounceTime => TimeSpan.FromMilliseconds(200);

public async Task<bool> TryLoad()
{
if (Storage.UserSettings.Count == 0)
{
Storage.UserSettings.Put(KeyDefaultCity, string.Empty);
}

return true;
}

public void Unload()
{
return true;
}

public async Task<bool> Activate(Query query)
Expand All @@ -44,51 +43,72 @@ public async Task<bool> Activate(Query query)
return false;
}

var defaultCity = Storage.UserSettings.Str(KeyDefaultCity);
return !string.IsNullOrEmpty(defaultCity) || query.Parts.Length >= 2;
return !string.IsNullOrEmpty(GetDefaultCity()) || query.Parts.Length >= 2;
}

public async IAsyncEnumerable<AbstractQueryResult> Process(Query query, [EnumeratorCancellation] CancellationToken ct)
{
string location;
if (query.Parts.Length == 1)
var location = GetLocation(query);
var isUS = IsUS();

if (_weatherCache.TryGetValue(location, out var weather))
{
location = Storage.UserSettings.Str(KeyDefaultCity);
foreach (var weatherDayModel in weather)
{
yield return new WeatherResult(weatherDayModel, isUS);
}
}
else
{
location = string.Join(" ", query.Parts[1..]).Trim();
}

if (_weatherCache.ContainsKey(location))
{
foreach (var weatherDayModel in _weatherCache[location])
weather = await GetWeatherAt(location);
if (weather == null)
{
yield return new WeatherResult(weatherDayModel);
yield break;
}

yield break;
}

var weather = await LookupWeather(location);
if (weather == null)
{
yield break;
_weatherCache[location] = weather;

foreach (var weatherDayModel in weather)
{
yield return new WeatherResult(weatherDayModel, isUS);
}
}
}

_weatherCache[location] = weather;
private bool IsUS()
{
var currentRegion = new RegionInfo(CultureInfo.CurrentCulture.LCID);
return !currentRegion.IsMetric;
}

foreach(var weatherDayModel in weather)
private string GetLocation(Query query)
{
if (query.Parts.Length == 1)
{
yield return new WeatherResult(weatherDayModel);
return GetDefaultCity();
}

return string.Join(" ", query.Parts[1..]).Trim();
}

private string GetDefaultCity()
{
return Storage.UserSettings.Str(KeyDefaultCity);
}

private async Task<List<WeatherDayModel>> LookupWeather(string location)
private async Task<List<WeatherDayModel>> GetWeatherAt(string location)
{
var url = $"https://usepinpoint.com/api/weather/{location}";
var result = await HttpHelper.SendGet(url,
s => s.Contains("error") ? null : JObject.Parse(s)["forecast"]["forecastday"]);
var url = $"https://usepinpoint.com/api/weather/{location.ToLower()}";

var result = await HttpHelper.SendGet(url, response =>
{
if (response.Contains("error"))
{
return null;
}

return JObject.Parse(response)["forecast"]["forecastday"];
});

if (result == null)
{
Expand All @@ -97,12 +117,13 @@ private async Task<List<WeatherDayModel>> LookupWeather(string location)

return result.Select(token =>
{
var weatherDayModel = JsonConvert.DeserializeObject<WeatherDayModel>(token["day"].ToString());
weatherDayModel.DayOfWeek = DateTime.Parse(token["date"].ToString()).ToString("ddd").Substring(0, 2);
weatherDayModel.Hours = token["hour"]
var dayModel = JsonConvert.DeserializeObject<WeatherDayModel>(token["day"].ToString());
dayModel.DayOfWeek = DateTime.Parse(token["date"].ToString()).ToString("ddd").Substring(0, 2);
dayModel.Hours = token["hour"]
.Select(t => JsonConvert.DeserializeObject<WeatherHourModel>(t.ToString()))
.ToArray();
return weatherDayModel;

return dayModel;
}).ToList();
}
}
Expand Down
114 changes: 94 additions & 20 deletions Pinpoint.Plugin.Weather/WeatherResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,113 @@
using Pinpoint.Core.Results;
using Pinpoint.Plugin.Weather.Models;

namespace Pinpoint.Plugin.Weather
namespace Pinpoint.Plugin.Weather;

public class WeatherResult : AbstractQueryResult
{
public class WeatherResult : AbstractQueryResult
public WeatherResult(WeatherDayModel m, bool isUS) : base(
BuildDayResultString(m, isUS),
BuildDayResultDetailsString(m, isUS))
{
Icon = WeatherIconRepository.Get(m.Condition.IconUrl);

foreach (var weatherHourModel in m.Hours)
{
Options.Add(new WeatherResult(weatherHourModel, isUS));
}
}

public WeatherResult(WeatherHourModel m, bool isUS) : base(
BuildHourResultString(m, isUS),
BuildHourResultDetailsString(m, isUS))
{
Icon = WeatherIconRepository.Get(m.Condition.IconUrl);
}

private static string BuildDayResultString(WeatherDayModel m, bool isUS)
{
public WeatherResult(WeatherDayModel m) : base(
m.DayOfWeek + ": " + m.Condition.Text + " | " + m.MinTempC + "-" + m.MaxTempC + " C | " + m.MinTempF + "-" + m.MaxTempF + " F",
"Rain " + m.ChanceOfRain + "% | Snow " + m.ChanceOfSnow + "% "
+ (m.PrecipitationMm > 0 ? "| Precipitation: " + m.PrecipitationMm + "mm/" + m.PrecipitationIn + "in " : "| ")
+ "| Max wind: " + m.WindMps + "m/s"
)
var temperatureUnit = !isUS ? "C" : "F";
var minTemp = !isUS ? m.MinTempC : m.MinTempF;
var maxTemp = !isUS ? m.MaxTempC : m.MaxTempF;
var title = $"{m.DayOfWeek}: {m.Condition.Text} with {minTemp}-{maxTemp} {temperatureUnit}";
return title;
}

private static string BuildDayResultDetailsString(WeatherDayModel m, bool isUS)
{
var details = "";
if (m.ChanceOfRain > 0)
{
Icon = WeatherIconRepository.Get(m.Condition.IconUrl);
details += $"Rain {m.ChanceOfRain}% ";
}

foreach (var weatherHourModel in m.Hours)
if (m.ChanceOfSnow > 0)
{
if (details.Length > 0)
{
Options.Add(new WeatherResult(weatherHourModel));
details += "| ";
}
details += "Snow {m.ChanceOfSnow}% ";
}

if (m.PrecipitationMm > 0)
{
var precipitationUnit = isUS ? "in" : "mm";
var precipitation = isUS ? m.PrecipitationIn : m.PrecipitationMm;
details += $"| Precipitation: {precipitation}{precipitationUnit} ";
}

public WeatherResult(WeatherHourModel m) : base(
m.Time + ": " + m.Condition.Text + " | " + m.TempC + " (feels " + m.FeelsLikeC + ") C | " + m.TempF + " (feels " + m.FeelsLikeF + ") F",
"Rain " + m.ChanceOfRain + "% | Snow " + m.ChanceOfSnow + "% "
+ (m.PrecipitationMm > 0 ? "| Precipitation: " + m.PrecipitationMm + "mm/" + m.PrecipitationIn + "in | " : "| ")
+ "Wind " + m.WindMps + " (gust " + m.GustMps + ") m/s"
)
details += $"| Max wind: {m.WindMps}m/s";

return details;
}

private static string BuildHourResultString(WeatherHourModel m, bool isUS)
{
var temperatureUnit = !isUS ? "C" : "F";
var temp = !isUS ? m.TempC : m.TempF;
var feelsLike = !isUS ? m.FeelsLikeC : m.FeelsLikeF;
// Extract hour of time (last 5 letters)
var last5Letters = m.Time.Substring(m.Time.Length - 5);
var title = $"{last5Letters}: {m.Condition.Text} | {temp} {temperatureUnit} (feels like {feelsLike} {temperatureUnit})";
return title;
}

private static string BuildHourResultDetailsString(WeatherHourModel m, bool isUS)
{
var details = "";
if (m.ChanceOfRain > 0)
{
Icon = WeatherIconRepository.Get(m.Condition.IconUrl);
details += $"Rain {m.ChanceOfRain}% ";
}

public override Bitmap Icon { get; }
if (m.ChanceOfSnow > 0)
{
if (details.Length > 0)
{
details += "| ";
}
details += $"Snow {m.ChanceOfSnow}% ";
}

public override void OnSelect()
if (m.PrecipitationMm > 0)
{
var precipitationUnit = isUS ? "in" : "mm";
var precipitation = isUS ? m.PrecipitationIn : m.PrecipitationMm;
if (details.Length > 0)
{
details += "| ";
}
details += $"Precipitation: {precipitation}{precipitationUnit} ";
}

details += $"Wind {m.WindMps} (gust {m.GustMps}) m/s";
return details;
}

public override Bitmap Icon { get; }

public override void OnSelect()
{
}
}