mirror of
https://github.com/alexhopeoconnor/bom-local-service.git
synced 2026-10-04 03:28:11 +10:00
Initial commit: BOM Local Service
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
namespace BomLocalService.Utilities;
|
||||
|
||||
public static class FilePathHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the cache directory path from configuration or returns default
|
||||
/// </summary>
|
||||
public static string GetCacheDirectory(IConfiguration configuration)
|
||||
{
|
||||
return configuration.GetValue<string>("CacheDirectory")
|
||||
?? Path.Combine(AppContext.BaseDirectory, "cache");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metadata file path for a given screenshot path
|
||||
/// </summary>
|
||||
public static string GetMetadataFilePath(string screenshotPath)
|
||||
{
|
||||
return Path.ChangeExtension(screenshotPath, ".json");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cache file pattern for a location key (e.g., "Pomona_QLD_*.png")
|
||||
/// </summary>
|
||||
public static string GetCacheFilePattern(string locationKey)
|
||||
{
|
||||
var safeLocationKey = LocationHelper.SanitizeFileName(locationKey);
|
||||
return $"{safeLocationKey}_*.png";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cache file pattern for suburb and state
|
||||
/// </summary>
|
||||
public static string GetCacheFilePattern(string suburb, string state)
|
||||
{
|
||||
var locationKey = LocationHelper.GetLocationKey(suburb, state);
|
||||
return GetCacheFilePattern(locationKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace BomLocalService.Utilities;
|
||||
|
||||
public static class LocationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a location key from suburb and state (e.g., "Pomona_QLD")
|
||||
/// </summary>
|
||||
public static string GetLocationKey(string suburb, string state)
|
||||
{
|
||||
return $"{suburb}_{state}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sanitizes a filename by removing invalid characters and replacing spaces/commas
|
||||
/// </summary>
|
||||
public static string SanitizeFileName(string fileName)
|
||||
{
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
return string.Join("_", fileName.Split(invalidChars, StringSplitOptions.RemoveEmptyEntries))
|
||||
.Replace(" ", "_")
|
||||
.Replace(",", "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses suburb and state from a cache filename (format: Suburb_State_YYYYMMDD_HHMMSS.png)
|
||||
/// Returns null if parsing fails
|
||||
/// </summary>
|
||||
public static (string suburb, string state)? ParseLocationFromFilename(string fileName)
|
||||
{
|
||||
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
|
||||
var parts = fileNameWithoutExtension.Split('_');
|
||||
|
||||
if (parts.Length >= 2)
|
||||
{
|
||||
return (parts[0], parts[1]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses timestamp from a cache filename (format: Suburb_State_YYYYMMDD_HHMMSS.png)
|
||||
/// Returns null if parsing fails
|
||||
/// </summary>
|
||||
public static DateTime? ParseTimestampFromFilename(string fileName)
|
||||
{
|
||||
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
|
||||
var parts = fileNameWithoutExtension.Split('_');
|
||||
|
||||
if (parts.Length >= 3)
|
||||
{
|
||||
// Last two parts should be date and time
|
||||
var dateTimeStr = $"{parts[^2]}_{parts[^1]}";
|
||||
if (DateTime.TryParseExact(dateTimeStr, "yyyyMMdd_HHmmss", null, System.Globalization.DateTimeStyles.None, out var fileTime))
|
||||
{
|
||||
return fileTime;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using BomLocalService.Models;
|
||||
|
||||
namespace BomLocalService.Utilities;
|
||||
|
||||
public static class ResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a RadarScreenshotResponse from a file path and metadata
|
||||
/// </summary>
|
||||
public static RadarScreenshotResponse CreateRadarScreenshotResponse(
|
||||
string imagePath,
|
||||
LastUpdatedInfo? metadata = null)
|
||||
{
|
||||
var lastWriteTime = File.Exists(imagePath)
|
||||
? File.GetLastWriteTime(imagePath)
|
||||
: DateTime.UtcNow;
|
||||
|
||||
if (metadata == null)
|
||||
{
|
||||
return new RadarScreenshotResponse
|
||||
{
|
||||
ImagePath = imagePath,
|
||||
LastUpdated = lastWriteTime,
|
||||
ObservationTime = DateTime.UtcNow,
|
||||
ForecastTime = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
return new RadarScreenshotResponse
|
||||
{
|
||||
ImagePath = imagePath,
|
||||
LastUpdated = lastWriteTime,
|
||||
ObservationTime = metadata.ObservationTime,
|
||||
ForecastTime = metadata.ForecastTime,
|
||||
WeatherStation = metadata.WeatherStation,
|
||||
Distance = metadata.Distance
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using Microsoft.Playwright;
|
||||
|
||||
namespace BomLocalService.Utilities;
|
||||
|
||||
public static class SelectorHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to find a locator by trying multiple selectors in order
|
||||
/// Returns the first visible locator found, or null if none found
|
||||
/// </summary>
|
||||
public static async Task<ILocator?> FindLocatorBySelectorsAsync(
|
||||
IPage page,
|
||||
string[] selectors,
|
||||
ILogger? logger = null)
|
||||
{
|
||||
foreach (var selector in selectors)
|
||||
{
|
||||
try
|
||||
{
|
||||
var locator = page.Locator(selector).First;
|
||||
if (await locator.IsVisibleAsync())
|
||||
{
|
||||
logger?.LogInformation("Found element with selector: {Selector}", selector);
|
||||
return locator;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Continue to next selector
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
namespace BomLocalService.Utilities;
|
||||
|
||||
public static class StateAbbreviationHelper
|
||||
{
|
||||
private static readonly Dictionary<string, string[]> StateMappings = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "qld", new[] { "queensland" } },
|
||||
{ "nsw", new[] { "new south wales" } },
|
||||
{ "vic", new[] { "victoria" } },
|
||||
{ "sa", new[] { "south australia" } },
|
||||
{ "wa", new[] { "western australia" } },
|
||||
{ "tas", new[] { "tasmania" } },
|
||||
{ "nt", new[] { "northern territory" } },
|
||||
{ "act", new[] { "australian capital territory" } }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the text matches the state abbreviation or full state name
|
||||
/// </summary>
|
||||
public static bool MatchesState(string text, string stateAbbreviation)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(stateAbbreviation))
|
||||
{
|
||||
return true; // No state specified, match any
|
||||
}
|
||||
|
||||
var textLower = text.ToLower();
|
||||
var stateLower = stateAbbreviation.ToLower().Trim();
|
||||
|
||||
// Direct match
|
||||
if (textLower.Contains(stateLower))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check abbreviation mappings
|
||||
if (StateMappings.TryGetValue(stateLower, out var fullNames))
|
||||
{
|
||||
return fullNames.Any(fullName => textLower.Contains(fullName));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace BomLocalService.Utilities;
|
||||
|
||||
public static class ValidationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates suburb and state parameters
|
||||
/// Returns error message if validation fails, null if valid
|
||||
/// </summary>
|
||||
public static string? ValidateLocation(string suburb, string state)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(suburb))
|
||||
{
|
||||
return "Suburb is required";
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(state))
|
||||
{
|
||||
return "State is required";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user